函数首先是获取mipi_csi2_info:
mipi_csi2_info = mipi_csi2_get_info();
这句代码返回mipi_csi2_info,那么这个mipi csi2 information是什么呢?
查看mipi_csi2_get_info()的原函数在Mxc_mipi_csi2.c中
struct mipi_csi2_info *mipi_csi2_get_info(void)
{
return gmipi_csi2;
}
可以看出该函数返回值为gmipi_csi2,那么gmipi_csi2又是什么呢。
gmipi_csi2的定义在Mxc_mipi_csi2.c文件的最前面
static struct mipi_csi2_info *gmipi_csi2;
其中struct mipi_csi2_info的数据结构为:
/* driver private data */
struct mipi_csi2_info {
bool mipi_en;
int ipu_id;
unsigned int csi_id;
unsigned int v_channel;
unsigned int lanes;
unsigned int datatype;
struct clk *dphy_clk;
struct clk *pixel_clk;
unsigned int *mipi_csi2_base;
struct platform_device *pdev;
struct mutex mutex_lock;
};
那么gmipi_csi2是干什么的呢,在Mxc_mipi_csi2.c的mipi_csi2_probe函数中,开辟了gmipi_csi2 的内存空间:
gmipi_csi2 = kmalloc(sizeof(struct mipi_csi2_info), GFP_KERNEL);
然后是配置gmipi_csi2结构体的值:
mutex_init(&gmipi_csi2->mutex_lock);
/* get mipi csi2 informaiton */
gmipi_csi2->pdev = pdev;
gmipi_csi2->mipi_en = false;
gmipi_csi2->ipu_id = plat_data->ipu_id;
gmipi_csi2->csi_id = plat_data->csi_id;
gmipi_csi2->v_channel = plat_data->v_channel;
gmipi_csi2->lanes = plat_data->lanes;
gmipi_csi2->dphy_clk = clk_get(&pdev->dev, plat_data->dphy_clk);
gmipi_csi2->pixel_clk = clk_get(&pdev->dev, plat_data->pixel_clk);
gmipi_csi2->mipi_csi2_base = ioremap(res->start, PAGE_SIZE);
然后通过gmipi_csi2去设置CSI2:
/* mipi dphy clk enable for register access */
clk_enable(gmipi_csi2->dphy_clk);
/* get mipi csi2 dphy version */
mipi_csi2_dphy_ver = mipi_csi2_read(gmipi_csi2, CSI2_VERSION);
clk_disable(gmipi_csi2->dphy_clk);
platform_set_drvdata(pdev, gmipi_csi2);
到这里就可以说明gmipi_csi2是控制CSI2的结构体。驱动中配置CSI2都是通过gmipi_csi2实现的。
下面回到ov5640_init_mode函数:
if (mipi_csi2_info) {
if (!mipi_csi2_get_status(mipi_csi2_info))
mipi_csi2_enable(mipi_csi2_info);
其中mipi_csi2_get_status函数为:
bool mipi_csi2_get_status(struct mipi_csi2_info *info)
{
bool status;
_mipi_csi2_lock(info);
status = info->mipi_en;
_mipi_csi2_unlock(info);
return status;
}
其实就是查看gmipi_csi2结构体中的mipi_en是否为1,为1就说明CSI2已经使能,如果mipi_en为0,就说明CSI2没有使能,就需要通过mipi_csi2_enable函数来进行使能了。
那么mipi_csi2_enable都干了啥工作呢?其实也就是将gmipi_csi2结构体中的mipi_en设置为1.并且使能CSI2的物理时钟gmipi_csi2->dphy_clk。
bool mipi_csi2_enable(struct mipi_csi2_info *info)
{
bool status;
_mipi_csi2_lock(info);
if (!info->mipi_en) {
info->mipi_en = true;
clk_enable(info->dphy_clk);
} else
mipi_dbg("mipi csi2 already enabled!\n");
status = info->mipi_en;
_mipi_csi2_unlock(info);
return status;
}
接下来就是设置MIPI的lane的数量:
if (mipi_csi2_get_status(mipi_csi2_info)) {
mipi_csi2_set_lanes(mipi_csi2_info);
unsigned int mipi_csi2_set_lanes(struct mipi_csi2_info *info)
{
unsigned int lanes;
_mipi_csi2_lock(info);
mipi_csi2_write(info, info->lanes - 1, CSI2_N_LANES);
lanes = mipi_csi2_read(info, CSI2_N_LANES);
_mipi_csi2_unlock(info);
return lanes;
}
那么这个lane的数量是怎么确定的呢?
该函数中info->lanes也就是gmipi_csi2->lanes即为lane的数量。该数量在mipi_csi2_probe函数中设置:gmipi_csi2->lanes = plat_data->lanes;那么plat_data->lanes是怎么来的呢?该函数在board-mx6q_sabresd.c文件中通过
imx6q_add_mipi_csi2(&mipi_csi2_pdata);进行注册。其中,mipi_csi2_pdata的设置为:
static struct mipi_csi2_platform_data mipi_csi2_pdata = {
.ipu_id = 0,
.csi_id = 1,
.v_channel = 0,
.lanes = 2,
.dphy_clk = "mipi_pllref_clk",
.pixel_clk = "emi_clk",
};
可以看到该MIPI是2lanes的。
再次回到ov5640_init_mode函数。设置完mipi的lane后,就要设置mipi传输的数据类型了:
if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_UYVY)
mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_YUV422);
else if (ov5640_data.pix.pixelformat == V4L2_PIX_FMT_RGB565)
mipi_csi2_set_datatype(mipi_csi2_info, MIPI_DT_RGB565);
else
pr_err("currently this sensor format can not be supported!\n");
其中mipi_csi2_set_datatype函数为:
unsigned int mipi_csi2_set_datatype(struct mipi_csi2_info *info,
unsigned int datatype)
{
unsigned int dtype;
_mipi_csi2_lock(info);
info->datatype = datatype;
dtype = info->datatype;
_mipi_csi2_unlock(info);
return dtype;
}
其实也就是设置gmipi_csi2->datatype的数据类型。再次看出,驱动中配置mipi其实就是配置结构体gmipi_csi2。
下面就是通过I2C来配置OV5640的寄存器:
dn_mode = ov5640_mode_info_data[frame_rate][mode].dn_mode;
orig_dn_mode = ov5640_mode_info_data[frame_rate][orig_mode].dn_mode;
if (mode == ov5640_mode_INIT) {
pModeSetting = ov5640_init_setting_30fps_VGA;
ArySize = ARRAY_SIZE(ov5640_init_setting_30fps_VGA);
ov5640_data.pix.width = 640;
ov5640_data.pix.height = 480;
retval = ov5640_download_firmware(pModeSetting, ArySize);
if (retval < 0)
goto err;
pModeSetting = ov5640_setting_30fps_VGA_640_480;
ArySize = ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480);
retval = ov5640_download_firmware(pModeSetting, ArySize);
} else if ((dn_mode == SUBSAMPLING && orig_dn_mode == SCALING) ||
(dn_mode == SCALING && orig_dn_mode == SUBSAMPLING)) {
/* change between subsampling and scaling
* go through exposure calucation */
retval = ov5640_change_mode_exposure_calc(frame_rate, mode);
} else {
/* change inside subsampling or scaling
* download firmware directly */
retval = ov5640_change_mode_direct(frame_rate, mode);
}
if (retval < 0)
goto err;
OV5640_set_AE_target(AE_Target);
OV5640_get_light_freq();
OV5640_set_bandingfilter();
ov5640_set_virtual_channel(ov5640_data.csi);
/* add delay to wait for sensor stable */
if (mode == ov5640_mode_QSXGA_2592_1944) {
/* dump the first two frames: 1/7.5*2
* the frame rate of QSXGA is 7.5fps */
msec_wait4stable = 267;
} else if (frame_rate == ov5640_15_fps) {
/* dump the first nine frames: 1/15*9 */
msec_wait4stable = 600;
} else if (frame_rate == ov5640_30_fps) {
/* dump the first nine frames: 1/30*9 */
msec_wait4stable = 300;
}
msleep(msec_wait4stable);
该部分不是此次分析的重点,下回有机会再细说。
接下来就是获取IMX6中MIPI模块的工作状态了:
mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info);
unsigned int mipi_csi2_dphy_status(struct mipi_csi2_info *info)
{
unsigned int status;
_mipi_csi2_lock(info);
status = mipi_csi2_read(info, CSI2_PHY_STATE);
_mipi_csi2_unlock(info);
return status;
}
该函数会返回mipi dphy的status.
如果mipi模块的状态不是ready状态的话,就会每个10ms询问一次,总共会询问10次。
while ((mipi_reg == 0x200) && (i < 10))
{
mipi_reg = mipi_csi2_dphy_status(mipi_csi2_info);
i++;
msleep(10);
}
if (i >= 10)
{
pr_err("mipi csi2 can not receive sensor clk!\n");
return -1;
}
如果询问10次mipi都不是ready的话,就说明mipi模块可能挂掉了(也许是内核阻塞。。。)此时,就要打印"mipi csi2 can not receive sensor clk!\n"告诉我们CSI2出问题啦,快点过来帮我解决。
接下来就是检查mipi CSI2模块是否出现错误:
mipi_reg = mipi_csi2_get_error1(mipi_csi2_info);
while ((mipi_reg != 0x0) && (i < 10))
{
mipi_reg = mipi_csi2_get_error1(mipi_csi2_info);
i++;
msleep(10);
}
if (i >= 10)
{
pr_err("mipi csi2 can not reveive data correctly!\n");
return -1;
}
其中mipi_csi2_get_error1函数为:
unsigned int mipi_csi2_get_error1(struct mipi_csi2_info *info)
{
unsigned int err1;
_mipi_csi2_lock(info);
err1 = mipi_csi2_read(info, CSI2_ERR1);
_mipi_csi2_unlock(info);
return err1;
}
当发现有错误时,再每隔10ms查询1次,看看IMX6芯片自己能解决不。如果再查询10次,仍然发现mipi CSI2模块有错误的话,就说明IMX6芯片不能自己解决该错误。这就很可能是配置的错误了。10次后还是会打印"mipi csi2 can not reveive data correctly!\n"错误来提示我们。