Hello, we're trying to connect OV2640 camera to CSI0 in parallel mode. What we have achieved till now:
From dmesg:
Linux video capture interface: v2.00
mxc_v4l2_output mxc_v4l2_output.0: V4L2 device registered as video16
mxc_v4l2_output mxc_v4l2_output.0: V4L2 device registered as video17
usbcore: registered new interface driver uvcvideo
USB Video Class driver (v1.1.0)
And finally when we open Camera2 apk we see error - Can't connetc to camera. It all looks like ov2640 driver is not working properly. It allocates i2c bus resources, but something is wrong. Did we miss anything in the configuration?
Hi Krzysztof,
see following steps, please !
-----------------
/* the following codes are based on MCIMX6Q-SabreSDP board, see board-mx6q_sabresd.c & board-mx6q_sabresd.c file */
Step 1: /* IOMUX csi0 port : adding them to .h file*/
static iomux_v3_cfg_t mx6q_sabresd_csi0_sensor_pads[] = {
/* IPU1 Camera */
MX6Q_PAD_CSI0_DAT12__IPU1_CSI0_D_12,
MX6Q_PAD_CSI0_DAT13__IPU1_CSI0_D_13,
MX6Q_PAD_CSI0_DAT14__IPU1_CSI0_D_14,
MX6Q_PAD_CSI0_DAT15__IPU1_CSI0_D_15,
MX6Q_PAD_CSI0_DAT16__IPU1_CSI0_D_16,
MX6Q_PAD_CSI0_DAT17__IPU1_CSI0_D_17,
MX6Q_PAD_CSI0_DAT18__IPU1_CSI0_D_18,
MX6Q_PAD_CSI0_DAT19__IPU1_CSI0_D_19,
MX6Q_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC,
MX6Q_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK,
MX6Q_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC,
MX6Q_PAD_GPIO_3__CCM_CLKO2, /* using CLKO2 camera clk */
MX6Q_PAD_GPIO_7__GPIO_1_7, /* camera PWDN */
MX6Q_PAD_GPIO_8__GPIO_1_8, /* camera RESET */
};
Step 2:/* add the following code to board-mx6q_sabresd.c */
...
#define SABRESD_CSI0_RST IMX_GPIO_NR(1, 8) /* assume reset pin is GPIO1_8 */
#define SABRESD_CSI0_PWN IMX_GPIO_NR(1, 7) /* assume pwdn pin is GPIO1_7 */
...
static void mx6q_csi0_io_init(void)
{
/* cpu is i.MX6q */
mxc_iomux_v3_setup_multiple_pads(mx6q_sabresd_csi0_sensor_pads,ARRAY_SIZE(mx6q_sabresd_csi0_sensor_pads));
/* Camera reset */
gpio_request(SABRESD_CSI0_RST, "cam-reset");
gpio_direction_output(SABRESD_CSI0_RST, 1);
/* Camera power down */
gpio_request(SABRESD_CSI0_PWN, "cam-pwdn");
gpio_direction_output(SABRESD_CSI0_PWN, 1);
msleep(5);
gpio_set_value(SABRESD_CSI0_PWN, 0);
msleep(5);
gpio_set_value(SABRESD_CSI0_RST, 0);
msleep(1);
gpio_set_value(SABRESD_CSI0_RST, 1);
msleep(5);
gpio_set_value(SABRESD_CSI0_PWN, 1);
/* CPU is i.MX6Q*/
mxc_iomux_set_gpr_register(1, 19, 1, 1);
}
static void mx6q_csi0_cam_powerdown(int powerdown)
{
if (powerdown)
gpio_set_value(SABRESD_CSI0_PWN, 1);
else
gpio_set_value(SABRESD_CSI0_PWN, 0);
msleep(2);
}
static struct fsl_mxc_camera_platform_data camera_data = {
.mclk = 24000000,
.mclk_source = 0,
.csi = 0,
.io_init = mx6q_csi0_io_init,
.pwdn = mx6q_csi0_cam_powerdown,
};
Step 3: /* add ov2640 to i2c3 bus */
static struct i2c_board_info mxc_i2c2_board_info[] __initdata = {
{
I2C_BOARD_INFO("ov2640", 0x30),
.platform_data = (void *)&camera_data,
},
};
Step 4: /* add ov2640 to board init */
static void __init mx6_sabresd_board_init(void)
{
......
imx6q_add_imx_i2c(2, &mx6q_sabresd_i2c_data);
i2c_register_board_info(2, mxc_i2c2_board_info,ARRAY_SIZE(mxc_i2c2_board_info));
......
/* enable CLKO2 for ov2640*/
clko2 = clk_get(NULL, "clko2_clk");
if (IS_ERR(clko2))
pr_err("can't get CLKO2 clock.\n");
new_parent = clk_get(NULL, "osc_clk");
if (!IS_ERR(new_parent)) {
clk_set_parent(clko2, new_parent);
clk_put(new_parent);
}
rate = clk_round_rate(clko2, 24000000);
clk_set_rate(clko2, rate);
clk_enable(clko2);
......
}
step 5: /* add the following code to ov2640.c */
static int ov2640_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
int retval;
u8 chip_id_high, chip_id_low;
...
if (plat_data->gpo_regulator) {
gpo_regulator =
regulator_get(&client->dev, plat_data->gpo_regulator);
if (!IS_ERR(gpo_regulator)) {
if (regulator_enable(gpo_regulator) != 0) {
pr_err("%s:gpo3 set voltage error\n", __func__);
goto err4;
} else {
dev_dbg(&client->dev,
"%s:gpo3 set voltage ok\n", __func__);
}
} else
gpo_regulator = NULL;
}
/*------------------ and the following code--------------------- */
if (plat_data->io_init)
plat_data->io_init();
if (plat_data->pwdn)
plat_data->pwdn(0); /* ov2640 power down */
/* read product ID */
chip_id_high = i2c_smbus_read_byte_data(client,0x0a); /* read product ID MSB */
if (chip_id_high != 0x26) {
pr_warning("camera ov2640 is not found\n");
retval = -ENODEV;
goto err4;
}
chip_id_low = i2c_smbus_read_byte_data(client,0x0b); /* read product ID LSB */
if (chip_id_low != 0x41) {
pr_warning("camera ov2640 is not found\n");
retval = -ENODEV;
goto err4;
}
if (plat_data->pwdn)
plat_data->pwdn(1); /* ov2640 power on */
/*--------- extra code ended--------------------- */
/* This function attaches this structure to the /dev/video0 device.
* The pointer in priv points to the ov2640_data structure here.*/
ov2640_int_device.priv = &ov2640_data;
...
}
step 6: /* when running make menuconfig, select ov2640 dirver module to linux image */
step 7: /* modify init.rc */
setprop back_camera_name ov2640
step 8: /* modify camera HAL */
in android BSP, camera HAL source code is at path ~/myandroid/hardware/imx/mx6/libcamera2
customer can add ov2640 device to here by referring to other example devices , such as ov5642 etc.
-----------------------------------
Regards,
Weidong
Hello Weidong Sun!
I appreciate your help, there were some thing that I probably won't ever get to (like extedning Android's libcamera2). Unfortunatelly still i have no luck to connect to camera.
One thing was that my camera is using different ID. In your example there is 0x2641, while my camera has 0x2642. I've changed it and driver is allocating camera address (I'm checking this by using i2cdetect on specifis bus - if Kernel driver allocates some address, there is UU written on this address position, if it's not allocated - then it's address (0x30 in my case) itself).
I've created Ov2640Csi files from Ov5642Csi in libcamera2 folder. I suppose there will be some diffrences in resolutions supported, but as a beggining it should work without any change.
I'm wondering about two things:
- are you sure about powerdown function? In original board-mx6q_var_som.c file 0 and 1 state are replaced, like this:
static void mx6q_csi0_cam_powerdown(int powerdown)
{
if (powerdown)
gpio_set_value(SABRESD_CSI0_PWN, 0);
else
gpio_set_value(SABRESD_CSI0_PWN, 1);
msleep(2);
}
- shouldn't I add in fsl_mxc_camera_platform_data value .ipu = 0?
Hi, krzysztofsza?ach
>>shouldn't I add in fsl_mxc_camera_platform_data value .ipu = 0?
you know two cameras are supported on i.mx6 sabreSDP EVK board, one is based on CSI0 parallel, the other is based on MIPI-CSI2 port, if the camera you are using is parallel, for your application, you don't need to modify settings in BSP.
>>I've created Ov2640Csi files from Ov5642Csi in libcamera2 folder. I suppose there will be some diffrences in resolutions supported, but as a beggining it should work without any change.
Yes, the way is correct, but you shoud do some ajustment on your source code in your Camera HAL, for example, initialization is different from ov5642,etc.
>> other reminder about data flow:
up to low : Camer HAL configuration--->mxc_v4l2_caputure(get capbilities of camera from camera driver)----->camera driver.
low to up : camera driver(start video stream according to configuration from HAL)---->mxc_v4l2_caputure----->IPU CSI interface--->IDMA--->IPU display.
Hope above suggestions can help you !
good luck !
Regards,
Weidong