作为车载Camera领域的资深开发者,我最近完成了高通QCOM8397&8797 QCX平台上MAX96724解串器的驱动集成工作。这个项目涉及从编译系统适配到底层驱动调试的全流程,过程中积累了不少实战经验。今天我就来详细拆解整个集成过程,重点分享那些官方文档里不会写的"坑"和解决方案。
车载Camera系统与消费级产品最大的不同在于其严苛的可靠性要求和复杂的信号链路。以我们这次集成的MAX96724+IMX490方案为例,信号需要经过:传感器→解串器→串行器→车载以太网→SoC CSI接口,整个链路任何一个环节出问题都会导致成像异常。下面我就从实际项目角度,分模块讲解集成要点。
QCX平台采用CMake作为构建系统,我们需要将测试用例和驱动代码集成到编译体系中。关键修改点在cdk_qcx/build/lrh/CMakeLists.txt:
cmake复制# 原始基础配置
add_subdirectory(core/lib)
add_subdirectory(oem/chiusecase)
add_subdirectory(oem/sensor)
# 新增解串器配置目录
add_subdirectory(oem/qcom/cameraconfig)
add_subdirectory(oem/qcom/eeprom)
add_subdirectory(oem/qcom/sensor)
add_subdirectory(oem/***) # 新增解串器模块
这里有个容易踩坑的地方:目录添加顺序直接影响初始化流程。车载Camera需要严格遵循"先底层后上层"的加载顺序,建议按照:
测试XML的集成需要特别注意路径设置,我们在oem/***/CMakeLists.txt中配置:
cmake复制include (${PROJECT_SOURCE_DIR}/common.cmake)
set(
***_CONFIGS_PATH
"${CDK_PATH}/oem/***" # 必须使用绝对路径
)
经验分享:在高通平台开发时,所有路径配置建议都使用
${CDK_PATH}开头的绝对路径。我曾遇到过因为相对路径导致的编译时找不到配置文件的问题,调试了整整两天。
MAX96724通过CCI总线与SoC通信,设备树配置示例:
dts复制i2c@0 {
#address-cells = <1>;
#size-cells = <0>;
max96724@40 {
compatible = "maxim,max96724";
reg = <0x40>;
cci-device = <0>; // 使用CCI0
clocks = <&camcc CAM_CC_CPHY_RX_CLK_SRC>;
clock-names = "cphy_rx";
// 电源时序配置
vdd-supply = <&pm8998_l16>;
vddio-supply = <&pm8998_l22>;
reset-gpio = <&tlmm 76 0>;
};
};
关键参数说明:
cci-device:指定使用的CCI控制器编号cphy_rx时钟:必须与传感器端匹配CSI配置需要与解串器输出格式严格匹配:
dts复制csi_0: qcom,csi@0 {
qcom,csi-phy-sel = <0>; // 使用CSI0
qcom,phy-position = <0>;
qcom,phy-mode = <1>; // CPHY模式
qcom,interface-type = <1>; // CSI-2
ports {
port@0 {
reg = <0>;
max96724_csi_in: endpoint {
data-lanes = <1 2 3 4>;
clock-lanes = <0>;
link-frequencies = /bits/ 64 <800000000>;
remote-endpoint = <&max96724_out>;
};
};
};
};
避坑指南:我曾遇到过因为
link-frequencies配置错误导致的图像花屏问题。建议先用示波器测量实际链路时钟频率,再填入准确值。
MAX96724驱动需要实现以下核心功能:
c复制static const struct of_device_id max96724_dt_match[] = {
{ .compatible = "maxim,max96724" },
{}
};
static struct i2c_driver max96724_driver = {
.driver = {
.name = "max96724",
.of_match_table = max96724_dt_match,
},
.probe = max96724_probe,
.remove = max96724_remove,
.id_table = max96724_id,
};
module_i2c_driver(max96724_driver);
车载Camera对电源稳定性要求极高,我们的实现方案:
c复制static int max96724_power_on(struct max96724 *max96724)
{
int ret;
// 1. 使能数字电源
ret = regulator_set_load(max96724->vdd, 100000);
if (ret) {
dev_err(dev, "set vdd load failed\n");
return ret;
}
ret = regulator_enable(max96724->vdd);
if (ret) {
dev_err(dev, "vdd enable failed\n");
return ret;
}
udelay(50); // 必须的稳定时间
// 2. 使能IO电源
ret = regulator_enable(max96724->vddio);
if (ret) {
dev_err(dev, "vddio enable failed\n");
goto err_vdd;
}
udelay(10);
// 3. 复位操作
gpiod_set_value(max96724->reset_gpio, 1);
udelay(5);
gpiod_set_value(max96724->reset_gpio, 0);
msleep(20); // 复位后等待稳定
return 0;
err_vdd:
regulator_disable(max96724->vdd);
return ret;
}
实测经验:电源时序中的延时参数不能简单照搬spec,需要根据实际PCB布局调整。我们通过示波器测量发现,不同板子的电源稳定时间可能相差30%以上。
xml复制<PhysicalCamera name="max96724">
<SensorName>max96724</SensorName>
<MountAngle>0</MountAngle>
<Position>REAR</Position>
<SupportedHw>HW_VERSION_1</SupportedHw>
<LensInfo>
<FocalLength>4.3</FocalLength>
<FNumber>2.0</FNumber>
</LensInfo>
<SensorConfig>
<OutputFormat>RAW10</OutputFormat>
<FrameDurationRange min="33333" max="1000000"/>
<StreamConfig width="1920" height="1080" format="BAYER10"/>
</SensorConfig>
</PhysicalCamera>
xml复制<LogicalCamera name="camera">
<PhysicalCameraMap>
<PhysicalCamera name="max96724">
<ActuatorName>actuator0</ActuatorName>
<CSIPHYSlot>0</CSIPHYSlot>
<LensFacing>BACK</LensFacing>
</PhysicalCamera>
</PhysicalCameraMap>
<StreamConfig>
<Stream width="1920" height="1080" format="YCbCr420_888" usage="PREVIEW"/>
<Stream width="3840" height="2160" format="RAW10" usage="VIDEO"/>
</StreamConfig>
</LogicalCamera>
| 现象 | 可能原因 | 排查方法 |
|---|---|---|
| I2C通信失败 | 1. 电源未稳定 2. 上拉电阻不匹配 |
1. 测量电源纹波 2. 检查I2C波形 |
| 图像花屏 | 1. CSI时钟不匹配 2. 数据lane反序 |
1. 测量实际时钟频率 2. 交换lane顺序测试 |
| 帧率不稳 | 1. 电源噪声大 2. 散热不良 |
1. 增加电源滤波电容 2. 检查芯片温度 |
I2C调试:
bash复制i2cdetect -y 0 # 扫描I2C设备
i2cdump -f -y 0 0x40 # 读取寄存器
CSI信号质量检测:
bash复制cat /sys/kernel/debug/camera/cci/status
cat /sys/kernel/debug/camera/csi/0/status
功耗测量:
bash复制cat /sys/class/power_supply/battery/current_now
通过DMA缓冲区配置提升传输效率:
c复制static int max96724_config_dma(struct max96724 *max96724)
{
struct cam_buf_io_cfg io_cfg = {
.direction = CAM_BUF_OUTPUT,
.resource_type = CAM_ISP_IFE_OUT_RES_RDI_0,
.mem_handle = max96724->buf_handle,
};
int rc = cam_mem_get_cpu_buf(max96724->buf_handle, &max96724->kaddr);
if (rc) {
dev_err(dev, "get cpu buf failed\n");
return rc;
}
max96724->iova = cam_mem_get_io_buf(max96724->buf_handle);
if (!max96724->iova) {
dev_err(dev, "get iova failed\n");
return -ENOMEM;
}
return 0;
}
c复制static irqreturn_t max96724_irq_handler(int irq, void *data)
{
struct max96724 *max96724 = data;
u32 status;
status = max96724_read_reg(max96724, MAX96724_INT_STATUS_REG);
if (!status)
return IRQ_NONE;
// 关键中断处理路径禁用抢占
preempt_disable();
if (status & FRAME_START_INT)
handle_frame_start(max96724);
if (status & FRAME_END_INT)
handle_frame_end(max96724);
if (status & ERROR_INT)
handle_error(max96724);
preempt_enable();
return IRQ_HANDLED;
}
在车载Camera开发中,稳定性永远是第一位的。经过三个月的迭代优化,我们的方案最终实现了:
这些指标完全满足车规级要求,目前已经量产上车。整个开发过程中最深的体会是:车载电子开发必须建立完整的测试体系,包括温度循环测试、振动测试、EMC测试等,单纯的功能测试是远远不够的。