This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DS90UB964-Q1: Display abnormal

Part Number: DS90UB964-Q1

Hi Team,

Now I am checking customer's urgent issue.

Customer now use T7+ 964 and 933 to do 360 surrounding view.

They have made 600 pces board, and 20 of them can't display normally as the following picture shows. Moreover, this kind of flickering phenomenon occurs as soon as the power is turned on, and cannot be restored by restarting, and it can be reproduced 100%.

PCLK:99MHz       960p*25 fps         YUV422 8 bit 

I asked customer to enable 964's PATTERN GENERATION.

The normal board:

The abnormal board:

Here are two questions I would like to check:

1. It seems the abnormal and normal board have the same color bar. Could it prove 964+T7 have no problem? The problem is on the camera module 933?

2.Is the color bar ok? Why the color is so strange?

 Based on the PATTERN result, I think the problem is on the camera module. but I really doubt whether my judgement is right. because the color bar is strange...

/*
 * A V4L2 driver for *_mipi Raw cameras.
 *
 * Copyright (c) 2017 by Allwinnertech Co., Ltd.  http://www.allwinnertech.com
 *
 * Authors:  Chen Liang <michaelchen@allwinnertech.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/videodev2.h>
#include <linux/clk.h>
#include <media/v4l2-device.h>
#include <media/v4l2-mediabus.h>
#include <linux/io.h>
#include "camera.h"
#include "sensor_helper.h"
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/workqueue.h>

#include <asm/gpio.h>    
#include <linux/interrupt.h>
#include <linux/irq.h>

#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/pinctrl/consumer.h>
#include <linux/of_gpio.h>
#include <linux/delay.h>


MODULE_AUTHOR("chenliang");
MODULE_DESCRIPTION("A low-level driver for ds90ub964 mipi chip for yuv sensor");
MODULE_LICENSE("GPL");

#define MCLK              (25*1000*1000)
#define SENSOR_NAME "mipi0"
 
struct v4l2_subdev *mipi0_sd;
static int is_sensor_initialized = 0;

struct cfg_array {	
	struct regval_list *regs;
	int size;
};

typedef struct {
    u8 addr;
    u8 val;
    u16 delay;
} BspUtils_I2cParams;

static BspUtils_I2cParams gCfg_reg_list[] =
{

	{0x01, 0x01, 0xFFF}, 
	{0x1F, 0x00, 0x4FF}, 
	  
	{0x4C, 0x01, 0x0},
	{0x58, 0x58, 0x0},
	{0x5c, 0xb0, 0x0},
	{0x5D, 0x34, 0x0},
	{0x65, 0x58, 0x0},
	{0x7C, 0xC0, 0x0}, 
	{0x70, 0x1E, 0x0},  
	{0x6d, 0x7f, 0x0}, 
	
	{0x4C, 0x12, 0x0},
	{0x58, 0x58, 0x0},
	{0x5c, 0xb2, 0x0},
	{0x5D, 0x34, 0x0},
	{0x65, 0x62, 0x0},
	{0x7C, 0xC0, 0x0}, 
	{0x70, 0x5E, 0x0},  
	{0x6d, 0x7f, 0x0}, 
	
	{0x4C, 0x24, 0x0},
	{0x58, 0x58, 0x0},
	{0x5c, 0xb4, 0x0},
	{0x5D, 0x34, 0x0},
	{0x65, 0x64, 0x0},
	{0x7C, 0xC0, 0x0}, 
	{0x70, 0x9E, 0x0},  
	{0x6d, 0x7f, 0x0},
		
	{0x4C, 0x38, 0x0},
	{0x58, 0x58, 0x0},
	{0x5c, 0xba, 0x0},
	{0x5D, 0x34, 0x0},
	{0x65, 0x66, 0x0},
	{0x7C, 0xC0, 0x0}, 
	{0x70, 0xdE, 0x0},  
	{0x6d, 0x7f, 0x0}, 
	
	
	
	
				
	{0x4C, 0x01, 0x0},
	{0xB9, 0x1F, 0x0},
	{0xD4, 0x61, 0x0},
	{0x6E, 0xAA, 0x0},
	
	{0x4C, 0x12, 0x0},
	{0xB9, 0x1F, 0x0},
	{0xD4, 0x61, 0x0},
	{0x6E, 0xAA, 0x0},
	
	{0x4C, 0x24, 0x0},
	{0xB9, 0x1F, 0x0},
	{0xD4, 0x61, 0x0},
	{0x6E, 0xAA, 0x0},
	
	{0x4C, 0x38, 0x0},
	{0xB9, 0x1F, 0x0},
	{0xD4, 0x61, 0x0},
	{0x6E, 0xAA, 0x0},
	
	
	{0x11, 0x91, 0x0},
	{0x58, 0x58, 0x0},
	{0x19, 0x01, 0x0},
	{0x1A, 0x4c, 0x0},
	{0x1B, 0x0b, 0x0},
	{0x1C, 0xb7, 0x0},
	
	{0x18, 0x01, 0xFFF},
	{0x32, 0x01, 0x00}, 
	{0x33, 0x01, 0x00}, 
	{0x20, 0x00, 0xFFF},

};


static struct regval_list sensor_default_regs[] = {

};

static int sensor_g_exp(struct v4l2_subdev *sd, __s32 *value)
{
	struct sensor_info *info = to_state(sd);

	*value = info->exp;
	sensor_dbg("sensor_get_exposure = %d\n", info->exp);
	return 0;
}

static int sensor_g_gain(struct v4l2_subdev *sd, __s32 *value)
{
	struct sensor_info *info = to_state(sd);

	*value = info->gain;
	sensor_dbg("sensor_get_gain = %d\n", info->gain);
	return 0;
}


static int sensor_s_exp_gain(struct v4l2_subdev *sd,struct sensor_exp_gain *exp_gain)
{
	int exp_val, gain_val;
	struct sensor_info *info = to_state(sd);

	exp_val = exp_gain->exp_val;
	gain_val = exp_gain->gain_val;

	info->exp = exp_val;
	info->gain = gain_val;
	return 0;
}

static int sensor_s_sw_stby(struct v4l2_subdev *sd, int on_off)
{
	int ret = 0;
	return ret;
}

static int sensor_power(struct v4l2_subdev *sd, int on)
{
	int ret;
	ret = 0;
	switch (on) {
	case STBY_ON:
		sensor_dbg("STBY_ON!\n");
		cci_lock(sd);
		vin_gpio_write(sd, PWDN, CSI_GPIO_HIGH);
		vin_set_mclk(sd, OFF);
		cci_unlock(sd);
		break;
	case STBY_OFF:
		sensor_dbg("STBY_OFF!\n");
		cci_lock(sd);
		vin_set_mclk_freq(sd, MCLK);
		vin_set_mclk(sd, ON);
		usleep_range(10000, 12000);
		vin_gpio_write(sd, PWDN, CSI_GPIO_LOW);
		usleep_range(10000, 12000);
		cci_unlock(sd);
		ret = sensor_s_sw_stby(sd, CSI_GPIO_LOW);
		if (ret < 0)
			sensor_err("soft stby off falied!\n");
		usleep_range(10000, 12000);

		break;
	case PWR_ON:
		sensor_print("PWR_ON!\n");
		cci_lock(sd);
		vin_set_mclk_freq(sd, MCLK);
		vin_set_mclk(sd, ON);
		vin_set_pmu_channel(sd, IOVDD, ON);
		vin_set_pmu_channel(sd, DVDD, ON);
		usleep_range(10000, 12000);
		vin_gpio_set_status(sd, VDD3V3_EN, 1);
		vin_gpio_set_status(sd, V5_EN, 1);
		vin_gpio_set_status(sd, CCDVDD_EN, 1);
		vin_gpio_set_status(sd, RESET, 1);
		vin_gpio_set_status(sd, POWER_EN, 1);
		vin_gpio_write(sd, VDD3V3_EN, CSI_GPIO_HIGH);
		vin_gpio_write(sd, V5_EN, CSI_GPIO_HIGH);
		vin_gpio_write(sd, CCDVDD_EN, CSI_GPIO_HIGH);
		vin_gpio_write(sd, POWER_EN, CSI_GPIO_LOW);
		usleep_range(10000, 12000);
		vin_gpio_write(sd, POWER_EN, CSI_GPIO_HIGH);
		usleep_range(10000, 12000);
		vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
		usleep_range(10000, 12000);
		vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
		usleep_range(5000, 10000);
		vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
		cci_unlock(sd);
		break;

	case PWR_OFF:
		sensor_print("PWR_OFF!\n");
		cci_lock(sd);
		vin_set_mclk(sd, OFF);
		usleep_range(10000, 12000);
		vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
		vin_gpio_write(sd, POWER_EN, CSI_GPIO_LOW);
		usleep_range(10000, 12000);
		vin_gpio_write(sd, VDD3V3_EN, CSI_GPIO_LOW);
		vin_gpio_write(sd, V5_EN, CSI_GPIO_LOW);
		vin_gpio_write(sd, CCDVDD_EN, CSI_GPIO_LOW);
		vin_set_pmu_channel(sd, IOVDD, OFF);
		vin_set_pmu_channel(sd, DVDD, OFF);
		cci_unlock(sd);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int sensor_reset(struct v4l2_subdev *sd, u32 val)
{
	switch (val) {
	case 0:
		vin_gpio_write(sd, RESET, CSI_GPIO_HIGH);
		usleep_range(10000, 12000);
		break;
	case 1:
		vin_gpio_write(sd, RESET, CSI_GPIO_LOW);
		usleep_range(10000, 12000);
		break;
	default:
		return -EINVAL;
	}

	return 0;
}

static int sensor_detect(struct v4l2_subdev *sd)
{
#if 1
	data_type rdval;
	sensor_read(sd, 0x00, &rdval);
	sensor_print("%s read addr 0x00 value 0x%x\n", __func__, rdval);
	sensor_read(sd, 0x5D, &rdval);
	sensor_print("%s read addr 0x5D value 0x%x\n", __func__, rdval);
	sensor_read(sd, 0x5E, &rdval);
	sensor_print("%s read addr 0x5E value 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF0, &rdval);
	sensor_print("%s read addr 0xF0 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF1, &rdval);
	sensor_print("%s read addr 0xF1 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF2, &rdval);
	sensor_print("%s read addr 0xF2 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF3, &rdval);
	sensor_print("%s read addr 0xF3 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF4, &rdval);
	sensor_print("%s read addr 0xF4 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF5, &rdval);
	sensor_print("%s read addr 0xF5 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF8, &rdval);
	sensor_print("%s read addr 0xF8 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xF9, &rdval);
	sensor_print("%s read addr 0xF9 value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xFA, &rdval);
	sensor_print("%s read addr 0xFA value is 0x%x\n", __func__, rdval);
	sensor_read(sd, 0xFB, &rdval);
	sensor_print("%s read addr 0xFB value is 0x%x\n", __func__, rdval);
#endif

	return 0;
}

static int sensor_init(struct v4l2_subdev *sd, u32 val)
{
	int ret;
	struct sensor_info *info = to_state(sd);
	info->focus_status = 0;
	info->low_speed = 0;
	info->width  = 1280;
	info->height = 960;
	info->hflip  = 0;
	info->vflip  = 0;
	info->gain   = 0;

	info->tpf.numerator = 1;
	info->tpf.denominator = 25;

	return 0;
}

static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
	int ret = 0;
	struct sensor_info *info = to_state(sd);
	switch (cmd) {
	case GET_CURRENT_WIN_CFG:
		if (info->current_wins != NULL) {
			memcpy(arg, info->current_wins,
			       sizeof(struct sensor_win_size));
			ret = 0;
		} else {
			sensor_err("empty wins!\n");
			ret = -1;
		}
		break;
	case SET_FPS:
		break;
	case VIDIOC_VIN_SENSOR_EXP_GAIN:
		sensor_s_exp_gain(sd, (struct sensor_exp_gain *)arg);
		break;
	case VIDIOC_VIN_SENSOR_CFG_REQ:
		sensor_cfg_req(sd, (struct sensor_config *)arg);
		break;
	default:
		return -EINVAL;
	}
	return ret;
}

static struct sensor_format_struct sensor_formats[] = {
	{
		.desc		= "YUYV 4:2:2",
		.mbus_code	= V4L2_MBUS_FMT_VYUY8_2X8,
		.regs 		= sensor_default_regs,
		.regs_size  = ARRAY_SIZE(sensor_default_regs),
		.bpp		= 2,
	}
};
#define N_FMTS ARRAY_SIZE(sensor_formats)

static struct sensor_win_size sensor_win_sizes[] = {
	{
	 .width  = 1280,
	 .height = 960,
	 .hoffset = 0,
	 .voffset = 0,
	 .hts = 0,
	 .vts = 0,
	 .pclk = 99*1000*1000,
	 .mipi_bps = 1000*1000*1000,
	 .fps_fixed = 25,
	 .bin_factor = 1,
	 .intg_min = 4 << 4,
	 .intg_max = (2310 - 12) << 4,
	 .gain_min = 1 << 4,
	 .gain_max = 1400 << 4,
	 .regs = sensor_default_regs,
	 .regs_size = ARRAY_SIZE(sensor_default_regs),
	 .set_size = NULL,
	 },
};

#define N_WIN_SIZES (ARRAY_SIZE(sensor_win_sizes))

static int sensor_g_mbus_config(struct v4l2_subdev *sd,struct v4l2_mbus_config *cfg)
{
	struct sensor_info *info = to_state(sd);
	cfg->type = V4L2_MBUS_CSI2;
	cfg->flags = V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CHANNEL_1 | V4L2_MBUS_CSI2_CHANNEL_2 | V4L2_MBUS_CSI2_CHANNEL_3;
	return 0;
}


int mipi0_init(struct v4l2_subdev *sd)
{
	int retval = 0;

	int reg_len = sizeof(gCfg_reg_list)/sizeof(gCfg_reg_list[0]);
	int i;

    for (i = 0; i < reg_len; i++) 
    {
        if (sensor_write(sd, gCfg_reg_list[i].addr, gCfg_reg_list[i].val) < 0) {
		printk(KERN_ERR"<<<<<<<<< write 964 fail. >>>>>>>>\n");
            return -1;
        }

        if(0 == i) {
            msleep(30);
        }

        if(gCfg_reg_list[i].delay) 
	{
            usleep_range(gCfg_reg_list[i].delay, gCfg_reg_list[i].delay + 10);
        } 
	else 
	{
            usleep_range(1000,1010);
        }
    }
	return retval;
}

static int sensor_reg_init(struct sensor_info *info)
{
	int ret;
	struct v4l2_subdev *sd = &info->sd;
	struct sensor_format_struct *sensor_fmt = info->fmt;
	struct sensor_win_size *wsize = info->current_wins;

	mipi0_init(sd);

	info->width = wsize->width;
	info->height = wsize->height;
	return 0;
}

static int sensor_s_stream(struct v4l2_subdev *sd, int enable)
{
	struct sensor_info *info = to_state(sd);
	if (!enable)
		return 0;

	return sensor_reg_init(info);
}

static const struct v4l2_subdev_core_ops sensor_core_ops = {
	.reset = sensor_reset,
	.init = sensor_init,
	.s_power = sensor_power,
	.ioctl = sensor_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl32 = sensor_compat_ioctl32,
#endif
};

static const struct v4l2_subdev_video_ops sensor_video_ops = {
	.s_parm = sensor_s_parm,
	.g_parm = sensor_g_parm,
	.s_stream = sensor_s_stream,
	.g_mbus_config = sensor_g_mbus_config,
};

static const struct v4l2_subdev_pad_ops sensor_pad_ops = {
	.enum_mbus_code = sensor_enum_mbus_code,
	.enum_frame_size = sensor_enum_frame_size,
	.get_fmt = sensor_get_fmt,
	.set_fmt = sensor_set_fmt,
};

static const struct v4l2_subdev_ops sensor_ops = {
	.core = &sensor_core_ops,
	.video = &sensor_video_ops,
	.pad = &sensor_pad_ops,
};

static struct cci_driver cci_drv = {
	.name = SENSOR_NAME,
	.addr_width = CCI_BITS_8,
	.data_width = CCI_BITS_8,
};

static int sensor_probe(struct i2c_client *client,
			const struct i2c_device_id *id)
{
	struct v4l2_subdev *sd;
	struct sensor_info *info;
	info = kzalloc(sizeof(struct sensor_info), GFP_KERNEL);
	if (info == NULL)
		return -ENOMEM;
	sd = &info->sd;

	cci_dev_probe_helper(sd, client, &sensor_ops, &cci_drv);
	mutex_init(&info->lock);

	info->fmt = &sensor_formats[0];
	info->fmt_pt = &sensor_formats[0];
	info->win_pt = &sensor_win_sizes[0];
	info->fmt_num = N_FMTS;
	info->win_size_num = N_WIN_SIZES;
	info->sensor_field = V4L2_FIELD_NONE;
	info->combo_mode = CMB_TERMINAL_RES | CMB_PHYA_OFFSET1 | MIPI_NORMAL_MODE;
	info->stream_seq = MIPI_BEFORE_SENSOR;
	info->af_first_flag = 1;
	info->exp = 0;
	info->gain = 0;

	mipi0_sd = sd;
	return 0;
}

static int sensor_remove(struct i2c_client *client)
{
	struct v4l2_subdev *sd;
	sd = cci_dev_remove_helper(client, &cci_drv);
	kfree(to_state(sd));
	return 0;
}

static const struct i2c_device_id sensor_id[] = {
	{SENSOR_NAME, 0},
	{}
};

MODULE_DEVICE_TABLE(i2c, sensor_id);

static struct i2c_driver sensor_driver = {
	.driver = {
		   .owner = THIS_MODULE,
		   .name = SENSOR_NAME,
		   },
	.probe = sensor_probe,
	.remove = sensor_remove,
	.id_table = sensor_id,
};

static __init int init_sensor(void)
{

	return cci_dev_init_helper(&sensor_driver);
}

static __exit void exit_sensor(void)
{

	cci_dev_exit_helper(&sensor_driver);
}

#ifdef CONFIG_ARCH_SUN8IW17P1
subsys_initcall(init_sensor);
#else
module_init(init_sensor);
#endif
module_exit(exit_sensor);