基于RV1126移植Sony imx585
查看RV1126SDK内的sensor驱动,发现可以直接用的型号并不多,在实际项目实现的过程中,还是需要调试新的sensor,因此记录一下调试过程,之后的Sony系列都可以套用这个过程来实现。
首先确保硬件供电正常,另外i2c地址、时钟、上拉电阻等都要检查好,确保硬件没问题
在SDK/kernel/driver/media/i2c/ 下有相机sensor的驱动,都是在Linux框架下的,所以可以随便找一个索尼的驱动,在此基础上改。
在此之前要先修改设备树,设备树里主要定义了sensor的i2c地址,时钟频率,供电,复位的gpio、相机模组名、port节点的链接关系、date-lanes等
这里要按照自己原理图的定义去修改,另外ucam0_out节点链接到了mipi_in_ucam0,又链接到了rkcif->isp,因为对camera流的链接要求各不相同,此处不详细写出
&i2c1 {
status = "okay";
clock-frequency = <400000>;
imx585: imx585@37 {
compatible = "sony,imx585";
reg = <0x37>;
clocks = <&cru 103>;
clock-names = "xvclk";
power-domains = <&power 9>;
pinctrl-names = "rockchip,camera_default";
pinctrl-0 = <&mipicsi_clk0>;
avdd-supply = <&vcc3v3_sys>;
dovdd-supply = <&vcc_1v8>;
dvdd-supply = <&vcc_dvdd>;
reset-gpios = <&gpio1 28 1>;
rockchip,camera-module-index = <1>;
rockchip,camera-module-facing = "front";
rockchip,camera-module-name = "YT10092";
rockchip,camera-module-lens-name = "IR0147-60IRC-8M-F20";
ir-cut = <&cam_ircut0>;
port {
ucam_out0: endpoint {
remote-endpoint = <&mipi_in_ucam0>;
data-lanes = <1 2 3 4>;
};
};
};
};
在~/workspace/RV1126SDK/kernel/drivers/media/i2c下编辑Makefile,添加编译输出
obj-$(CONFIG_VIDEO_IMX585) += imx585.o
在~/workspace/RV1126SDK/kernel/drivers/media/i2c下编辑Kconfig,添加编译配置
config VIDEO_IMX585
tristate "Sony IMX585 sensor support"
depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API
depends on MEDIA_CAMERA_SUPPORT
help
This is a Video4Linux2 sensor driver for the Sony
IMX585 camera.
To compile this driver as a module, choose M here: the
module will be called imx585.
编辑~/workspace/RV1126SDK/kernel/arch/arm/configs/rv1126_defconfig,打开IMX585编译选项
CONFIG_VIDEO_IMX585=y
以上配置完成后,连接SoC与sensor,i2c应该是通的,但是用i2c工具测试,设备并没有挂载上,重复多次检查硬件确保硬件正常
1、分析驱动源码
首先进入probe函数
通过of_property_read_u32获取设备树节点信息,以下代码为获取设备树内的相机模组名、hdr参数等
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
&imx585->module_index);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
&imx585->module_facing);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
&imx585->module_name);
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
&imx585->len_name);
ret = of_property_read_u32(node, OF_CAMERA_HDR_MODE, &hdr_mode);
获取imx585的工作模式,寄存器值可以通过Sony的手册获取,保密问题此处省略
imx585->client = client;
imx585->cfg_num = ARRAY_SIZE(supported_modes);
for (i = 0; i < imx585->cfg_num; i++) {
if (hdr_mode == supported_modes[i].hdr_mode) {
imx585->cur_mode = &supported_modes[i];
break;
}
}
在非HDR模式时,默认匹配为supported_modes[0]
{
.bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10,
.width = 3864,
.height = 2192,
.max_fps = {
.numerator = 10000,
.denominator = 300000,
},
.exp_def = 0x08ca - 0x08,
.hts_def = 0x044c * IMX585_4LANES * 2,
.vts_def = 0x08ca,
.global_reg_list = imx585_global_10bit_3864x2192_regs,
.reg_list = imx585_linear_10bit_3864x2192_891M_regs,
.hdr_mode = NO_HDR,
.mipi_freq_idx = 1,
.bpp = 10,
}
因此需要设置imx585_global_10bit_3864x2192_regs和imx585_linear_10bit_3864x2192_891M_regs这两个寄存器
寄存器值可以通过Sony寄存器手册获取,参照寄存器表,以设置不同的工作模式
static __maybe_unused const struct regval imx585_global_10bit_3864x2192_regs[] = {
***************
{REG_NULL, 0x00},
};
static __maybe_unused const struct regval imx585_linear_10bit_3864x2192_891M_regs[] = {
**************
{REG_NULL, 0x00},
};
获取设备树内reset、power、pinctrl信息
imx585->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_ASIS);
if (IS_ERR(imx585->reset_gpio))
dev_warn(dev, "Failed to get reset-gpios\n");
imx585->power_gpio = devm_gpiod_get(dev, "power", GPIOD_ASIS);
if (IS_ERR(imx585->power_gpio))
dev_warn(dev, "Failed to get power-gpios\n");
imx585->pinctrl = devm_pinctrl_get(dev);
获取power信息
ret = imx585_configure_regulators(imx585);
if (ret) {
dev_err(dev, "Failed to get power regulators\n");
return ret;
}
查看imx585_configure_regulators函数最后可以定位到下面的结构体的power信息
static const char * const imx585_supply_names[] = {
"dvdd", /* Digital core power */
"dovdd", /* Digital I/O power */
"avdd", /* Analog power */
};
imx585_initialize_controls用于创建v4l2的控件,进入可以看到新建了exposure_max,、vblank_def、pixel_rate、h_blank等内容
__imx585_power_on用于使能设备,进入函数可以看到在里面设置了power_gpio和reset引脚的输出方向
imx585_check_sensor_id用于检测sensor,在此函数内进行了复位后寄存器值的检查,用于确认连接的sensor是否正确
sd = &imx585->subdev;
v4l2_i2c_subdev_init(sd, client, &imx585_subdev_ops);
ret = imx585_initialize_controls(imx585);
if (ret)
goto err_destroy_mutex;
ret = __imx585_power_on(imx585);
if (ret)
goto err_free_handler;
ret = imx585_check_sensor_id(imx585, client);
if (ret)
goto err_power_off;
通过内核log,最后将问题确认到了imx585_check_sensor_id函数,在imx415和imx715中,检验的寄存器和寄存器值都是一样的,
改成imx585后,因为其与imx415、imx715的寄存器不一样,因此在check_sensor的时候就会报错,导致sensor不能启动,i2c就不通。
通过imx585_read_reg获取IMX585_REG_CHIP_ID寄存器的值,id即为读取的IMX585_REG_CHIP_ID寄存器内的值,使其与CHIP_ID比较,如果正确,则确定sensor是imx585,因此在选择此寄存器时,要选择寄存器表中最能体现为imx585的寄存器。
static int imx585_check_sensor_id(struct imx585 *imx585,
struct i2c_client *client)
{
struct device *dev = &imx585->client->dev;
u32 id = 0;
int ret;
if (imx585->is_thunderboot) {
dev_info(dev, "Enable thunderboot mode, skip sensor id check\n");
return 0;
}
ret = imx585_read_reg(client, IMX585_REG_CHIP_ID,
IMX585_REG_VALUE_08BIT, &id);
if (id != CHIP_ID) {
dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
return -ENODEV;
}
dev_info(dev, "Detected imx585 id %06x\n", CHIP_ID);
return 0;
}
i2c读寄存器时,要创建两个msg,第一个msg用于存要读的寄存器信息,第二个msg用于存获取的寄存器信息
cpu_to_be16将i2c地址由小端存储改为大端存储。
/* Read registers up to 4 at a time */
static int imx585_read_reg(struct i2c_client *client, u16 reg, unsigned int len,
u32 *val)
{
struct i2c_msg msgs[2];
u8 *data_be_p;
__be32 data_be = 0;
__be16 reg_addr_be = cpu_to_be16(reg);
int ret;
if (len > 4 || !len)
return -EINVAL;
data_be_p = (u8 *)&data_be;
/* Write register address */
msgs[0].addr = client->addr;
msgs[0].flags = 0;
msgs[0].len = 2;
msgs[0].buf = (u8 *)®_addr_be;
/* Read data from register */
msgs[1].addr = client->addr;
msgs[1].flags = I2C_M_RD;
msgs[1].len = len;
msgs[1].buf = &data_be_p[4 - len];
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret != ARRAY_SIZE(msgs))
return -EIO;
*val = be32_to_cpu(data_be);
return 0;
}
设置的校验寄存器及复位后的初始化值
/* TODO: Get the real chip id from reg */
#define CHIP_ID 0x32
#define IMX585_REG_CHIP_ID 0x30DC
修改完后重新编译驱动烧录测试,使用i2c工具检测,可以看到设备0x37处为UU,设备成功挂载
修改宏定义部分寄存器值
#define IMX585_LF_GAIN_REG_H 0x306D
#define IMX585_LF_GAIN_REG_L 0x306C
#define IMX585_VTS_REG_L 0x3028
#define IMX585_VTS_REG_M 0x3029
#define IMX585_VTS_REG_H 0x302A
在驱动内修改INCK和Data Rate
#define IMX585_XVCLK_FREQ_74M 74250000
usleep_range(10 * 1000, 20 * 1000);
ret = clk_set_rate(imx585->xvclk, IMX585_XVCLK_FREQ_74M);
static const s64 link_freq_items[] = {
MIPI_FREQ_297M,
MIPI_FREQ_446M,
MIPI_FREQ_743M,
MIPI_FREQ_891M,
};
接镜头测试,发现画面整体偏红,但黑色区域是正常的,因此考虑应该是Bayer格式的RGB排列问题,改为SRGGB10--> .bus_fmt = MEDIA_BUS_FMT_SGBRG10_1X10,画面正常。
2、总结
因此移植一个新sensor可以总结为:
1)确认硬件连接,确认io口及供电正确
2)修改设备树,确认sensor的i2c地址,时钟频率,供电,复位的gpio、相机模组名、port节点的链接关系、date-lanes等
3)修改驱动内初始化寄存器值,check_id,INCK、Data Rate等,要与实际参数对应上
4)检查sensor的Bayer排列格式,不同的sensor排列方式不一样,此处设置不对画面会出现异常