RK3588是瑞芯微推出的一款高性能处理器,采用8核设计,包含4个Cortex-A76和4个Cortex-A55核心,主频最高可达2.4GHz。这款芯片在多媒体处理方面表现突出,特别适合视频采集和处理应用。我在实际项目中经常用它来做4K视频处理,性能确实很稳。
LT6911UXC是龙迅半导体的一款HDMI转MIPI桥接芯片,支持HDMI 2.0输入和MIPI CSI-2输出。这个芯片有几个很实用的特性:
这两个器件组合起来,可以很好地解决HDMI摄像头接入嵌入式系统的问题。比如我们最近做的一个智能监控项目,就需要把HDMI摄像头的视频流接入RK3588平台进行处理。
要让LT6911UXC在RK3588上工作,首先得正确配置设备树。这里我分享一下实际项目中的配置经验。
LT6911UXC通过I2C进行控制,在RK3588上我们用的是I2C8接口。设备树配置如下:
dts复制&i2c8 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2c8m3_xfer>;
lt6911uxc: lt6911uxc@2b {
compatible = "lontium,lt6911uxc";
reg = <0x2b>;
status = "okay";
clocks = <&cru CLK_MIPI_CAMARAOUT_M1>;
clock-names = "xvclk";
interrupt-parent = <&gpio3>;
interrupts = <RK_PD5 IRQ_TYPE_EDGE_RISING>;
reset-gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_LOW>;
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
rockchip,camera-module-name = "lt6911uxc";
port {
lt6911uxc_out: endpoint {
remote-endpoint = <&mipi_in_ucam>;
data-lanes = <1 2 3 4>;
};
};
};
};
这里有几个关键点需要注意:
MIPI CSI接口的配置相对复杂一些,需要串联多个节点:
dts复制&csi2_dphy0 {
status = "okay";
ports {
port@0 {
mipi_in_ucam: endpoint@1 {
remote-endpoint = <<6911uxc_out>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
csidphy1_out: endpoint@0 {
remote-endpoint = <&mipi2_csi2_input>;
};
};
};
};
&mipi2_csi2 {
status = "okay";
ports {
port@0 {
mipi2_csi2_input: endpoint@1 {
remote-endpoint = <&csidphy1_out>;
};
};
port@1 {
mipi2_csi2_output: endpoint@0 {
remote-endpoint = <&cif_mipi_in2>;
};
};
};
};
&rkcif_mipi_lvds2 {
status = "okay";
port {
cif_mipi_in2: endpoint {
remote-endpoint = <&mipi2_csi2_output>;
};
};
};
这个配置建立了从LT6911UXC到RK3588 CSI接口的完整数据通路。在实际调试时,一定要检查每个端点的remote-endpoint是否正确对应。
LT6911UXC的驱动主要基于V4L2框架实现,下面我重点分析几个关键函数。
probe函数是驱动初始化的入口,主要完成以下工作:
c复制static int lt6911uxc_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lt6911uxc *lt6911uxc;
struct v4l2_subdev *sd;
int err;
// 1. 分配内存并初始化默认参数
lt6911uxc = devm_kzalloc(dev, sizeof(*lt6911uxc), GFP_KERNEL);
lt6911uxc->timings = V4L2_DV_BT_CEA_640X480P59_94;
lt6911uxc->cur_mode = &supported_modes[0];
// 2. 解析设备树配置
err = lt6911uxc_parse_of(lt6911uxc);
if (err)
return err;
// 3. 检查芯片ID
err = lt6911uxc_check_chip_id(lt6911uxc);
if (err)
return err;
// 4. 初始化V4L2控制
err = lt6911uxc_init_v4l2_ctrls(lt6911uxc);
if (err)
goto err_free_hdl;
// 5. 注册V4L2子设备
v4l2_i2c_subdev_init(sd, client, <6911uxc_ops);
// 6. 初始化工作队列
INIT_DELAYED_WORK(<6911uxc->delayed_work_res_change,
lt6911uxc_delayed_work_res_change);
// 7. 配置中断
if (client->irq) {
err = devm_request_threaded_irq(dev, client->irq, NULL,
lt6911uxc_res_change_irq_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"lt6911uxc", lt6911uxc);
}
return 0;
err_free_hdl:
v4l2_ctrl_handler_free(<6911uxc->hdl);
return err;
}
在实际调试中,最容易出问题的是芯片ID检测和中断配置部分。如果probe失败,建议先检查这两部分。
LT6911UXC会自动检测输入视频的分辨率,这是通过工作队列定期检测实现的:
c复制static void lt6911uxc_delayed_work_res_change(struct work_struct *work)
{
struct lt6911uxc *lt6911uxc = container_of(work, struct lt6911uxc,
delayed_work_res_change.work);
struct v4l2_subdev *sd = <6911uxc->sd;
struct v4l2_dv_timings timings;
// 1. 获取当前检测到的视频参数
if (lt6911uxc_get_detected_timings(sd, &timings)) {
v4l2_dbg(1, debug, sd, "No signal detected\n");
} else {
// 2. 如果分辨率发生变化,更新配置
if (!v4l2_match_dv_timings(<6911uxc->timings, &timings, 0, false)) {
lt6911uxc_s_dv_timings(sd, &timings);
v4l2_print_dv_timings(sd->name, "New format: ", &timings, false);
}
}
// 3. 重新调度工作队列
schedule_delayed_work(<6911uxc->delayed_work_res_change,
msecs_to_jiffies(1000));
}
这个工作队列会每隔1秒检测一次输入信号的状态。在实际项目中,我发现这个检测间隔可以根据需要调整,比如对响应速度要求高的场景可以设置为500ms。
LT6911UXC的一个很实用的功能是根据输入分辨率自动调整MIPI lane数量。这个功能是通过以下代码实现的:
c复制static int lt6911uxc_get_detected_timings(struct v4l2_subdev *sd,
struct v4l2_dv_timings *timings)
{
// 读取当前使用的lane数量
i2c_rd8(sd, MIPI_LANES, &lanes);
lt6911uxc->csi_lanes_in_use = lanes;
// 其他参数读取...
i2c_rd8(sd, HTOTAL_H, &val_h);
i2c_rd8(sd, HTOTAL_L, &val_l);
htotal = ((val_h << 8) | val_l) * 2;
// ...
}
读取到的lane数量会在g_mbus_config函数中被V4L2子系统使用:
c复制static int lt6911uxc_g_mbus_config(struct v4l2_subdev *sd, unsigned int pad,
struct v4l2_mbus_config *cfg)
{
struct lt6911uxc *lt6911uxc = to_state(sd);
cfg->type = V4L2_MBUS_CSI2_DPHY;
cfg->flags = V4L2_MBUS_CSI2_CONTINUOUS_CLOCK | V4L2_MBUS_CSI2_CHANNEL_0;
switch (lt6911uxc->csi_lanes_in_use) {
case 1:
cfg->flags |= V4L2_MBUS_CSI2_1_LANE;
break;
case 2:
cfg->flags |= V4L2_MBUS_CSI2_2_LANE;
break;
case 3:
cfg->flags |= V4L2_MBUS_CSI2_3_LANE;
break;
case 4:
cfg->flags |= V4L2_MBUS_CSI2_4_LANE;
break;
default:
return -EINVAL;
}
return 0;
}
在实际测试中,我发现1080p分辨率通常会使用2条lane,而4K分辨率则会使用4条lane。这种自适应机制大大简化了系统配置。
LT6911UXC支持多种视频格式的转换,这部分功能主要在驱动中通过以下结构体定义:
c复制static const struct v4l2_dv_timings_cap lt6911uxc_timings_cap = {
.type = V4L2_DV_BT_656_1120,
.bt = {
.min_width = 640,
.max_width = 3840,
.min_height = 480,
.max_height = 2160,
.min_pixelclock = 25000000,
.max_pixelclock = 148500000,
.standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT,
.capabilities = V4L2_DV_BT_CAP_PROGRESSIVE |
V4L2_DV_BT_CAP_REDUCED_BLANKING |
V4L2_DV_BT_CAP_CUSTOM,
},
};
static const struct lt6911uxc_mode supported_modes[] = {
{
.width = 1920,
.height = 1080,
.max_fps = 30,
.hts_def = 2200,
.vts_def = 1125,
.reg_list = lt6911uxc_1080p_regs,
},
{
.width = 3840,
.height = 2160,
.max_fps = 30,
.hts_def = 4400,
.vts_def = 2250,
.reg_list = lt6911uxc_4k_regs,
},
};
在调试过程中,如果需要支持新的视频格式,只需要在supported_modes数组中添加相应的配置即可。
在实际项目中,我们遇到过不少问题,这里分享几个典型的排查经验。
症状:驱动加载失败,dmesg显示无法读取芯片ID。
排查步骤:
有一次我们遇到这个问题,最后发现是电源滤波电容焊接不良导致的。用示波器测量电源发现有很大的噪声,更换电容后问题解决。
症状:驱动加载正常,但无法获取视频数据。
排查步骤:
我们在一个项目中遇到MIPI信号不稳定的问题,后来发现是PCB布线时MIPI差分对长度匹配不够严格,重新设计PCB后问题解决。
症状:视频能显示,但会出现闪烁或花屏现象。
排查步骤:
这类问题通常与信号完整性有关。我们曾通过调整RK3588的CSI phy参数解决了花屏问题,具体是在设备树中增加以下配置:
dts复制&csi2_dphy0 {
rockchip,phy-cfg = <0x01c4>;
};
经过多个项目的实践,我总结出以下几点优化建议:
电源设计:LT6911UXC对电源噪声比较敏感,建议使用LDO供电而不是DCDC,并在电源引脚附近放置足够的去耦电容。
PCB布局:
散热考虑:长时间工作在高分辨率模式下芯片会发热,建议添加适当的散热措施。
驱动优化:
信号质量测试:
在最近的一个项目中,我们通过优化电源设计和PCB布局,将MIPI信号的眼图张开度提高了30%,系统稳定性显著提升。