Key-Word:TC358840、MSM8953、Android9.0
简介 : HDMI 转 MIPI(CSI) 芯片
平台 : MSM8953
芯片 : TC358840XBG (最大支持4K 30fps)
Format : rgb 转 Ycbcr422-8bit
软件平台 :Android – P(9.0 )
信号输出源:电脑主机
目前调通的配置:1080p 30fps 4lane (预览30帧、录像30帧)
1. 前言:
TC358840 是一个 HDMI 转 CSI 信号的转换,信号由其他设备输入进来。对平台端msm8953 而言,可以当做一个摄像头处理。
在840(TC358840后面简称840)芯片初始化后,其他信号设备会经 HDMI 接口读取芯片的EDID 数据,探测芯片端支持的最大分辨率,探测成功后,
默认输出840芯片最大支持的分辨率。(一个芯片内可同时储存多种EDID数据,即同时支持多种分辨率。)
调试时用电脑主机接上初始化完的芯片(写EDID等数据),电脑上会显示识别到一个新的显示器。
具体的分辨率见电脑显示设置。
2. 遇到的问题
- 840芯片 寄存器读取ID失败。
- 840芯片 寄存器写入不起作用。
- 840芯片 识别到后,数据流异常。无法预览、拍照、录像。
- 数据出流后颜色异常,颜色发绿。
2.1 解决
- 840芯片的从机地址由INT 脚的电平决定,上拉为0x1F,下拉为0x0F。实际使用要两种都尝试一下。 我们实际调试中发现,硬件链接到普通IIC上(非CCI),在kernel中写 一个iic驱动,读取的从机地址为0F,用vendorcam 驱动的方式 slave_addr为1F。
- 写入大小端不一致,解决办法见 CCI 写问题。见附录。
- 通过底层添加 csid csi等驱动log后,发现并无异常 frame id 会递增说明接收到了帧。最后排查发现是初始化代码中没有设置输出流的帧格式,导致无法正确解析到帧。
- 颜色异常修改数据格式后解决。.filter_arrangement = SENSOR_UYVY,
后续存在问题:
- 1080P 60fps的画面输出调试,甚至更高的其他分辨率如4K 30fps。
- 外部信号输入需要动态调节的,比如同一个信号输入源分辨率、帧率的改变。不同信号源之间,HDMI 线的插拔切换。
3. 达到的功能点
- 840 芯片相对主控来说作为一个cam 设备,但信号是从外部信号源输入840后再转化为mipi信号。
- 相机要能够正常的打开使用。
功能点的验证和预期结果:
- 插入外部1080p、30fps的信号 (插到电脑主机上会自动输出匹配格式的信号)。
- 打开qcom相机,电脑能够识别到一个外部显示器(数据格式分辨率、帧率都可以看出来 这里是 RGB 1080p 30fps)
- 查看预览、拍照、录像、画面是否正常,颜色是否正常。
- 在相机设置中相片尺寸需要调节为 HD 1080P,这样拍照的照片才是 1920x1080的分辨率。
- 录像测试: 切到录像模式,录像后,退出。用adb pull sdcard/DCIM/camera 到本地电脑上,查看录像文件信息,可以看到帧率为30帧。
功能点实测:
1、 和预期一致。验证通过。
4. kernel drv 配置
首先是一个YUV camera的适配:
第一步就是配置好dts和vendor的驱动,保证 cam 读取ID 匹配正常。
需要注意,这是一个YUV的cam,因此不需要效果 chromatix文件的配置,所以一切和效果相关的配置都需要去掉。
供电:实际的电路供电修改过,因此实际的dts配置以实际应用的为准。
这里简单说明。有三路供电。1V2 1V8和3V3。
dts中把 1V2和1V8 配置上即可。
其他如rst、pwd脚、挂载位置、角度、cci 总线位置,根据硬件接线配置。
mclk配置:使用外置晶振提供clk时钟,不使用平台的mclk
4.1 CCI 读问题
经过加log实测发现,840芯片大小端和平台端相反。因此按照默认的写入方式,芯片的初始化是不正确的。需要做修改。
CCI 读取问题:
如840的识别ID寄存器地址是 0xx00,按照CCI 的默认读取方式读出是0x47(0x0047)。
但是按照IIC的读取方式和我们的一般认知应该是0x4700。
为了保证对比方便(后续会把写入的寄存器值再读出来做对比)和认知一致,我们在读取函数中作如下修改。
kernel\msm-4.9\drivers\media\platform\msm\
camera_v2\sensor\io\msm_camera_cci_i2c.c 54
int32_t msm_camera_cci_i2c_read
===>
rc = cci_ctrl.status;
if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
*data = buf[0];
else {
*data = buf[0] << 8 | buf[1];
===> 修改为
rc = cci_ctrl.status;
if (data_type == MSM_CAMERA_I2C_BYTE_DATA)
*data = buf[0];
else {
// *data = buf[0] << 8 | buf[1];
*data = buf[1] << 8 | buf[0];
}
这样修改后,读取出的 0x00 就是0x4700了。和平台大小端一致,而且是按照我们的阅读顺序,高位在左,低位在右。
4.2 CCI 写问题
写寄存器比较麻烦,需要修改多处。有以下几个问题。
- 840的初始化数组,有三种不同的类型,有8bit、16bit、32bit、的寄存器。
- 咨询芯片厂商,8bit的要按照8bit的去写,16bit的按照16bit的去写,32bit可以拆分为两个 16bit的去写入,且数据的写入顺序不能修改。因此至少要按照两种类型去写入。
- vendor的cam驱动中,初始化数组res0中,数据类型是固定的,只能设置为 byte(8bit)或者word(16bit)类型,不能为每个数据设置类型。
这里通过修改vemdor的cam驱动和底层的写函数来实现。
- 区分不同的数据类型。在cam的初始化数组中,通过添加延时,来区分8bit或者16bit的数据类型。通过观察,默认数据有三种 8bit无延时,16bit无延时,16bit有延时(这类保持默认延时,不做修改)。因此,在每个16bit的后面都加0x01(1us)的延时,通过判断延时时间是否>0就可以把8bit和16bit的数据类型区分开。默认设置初始化数据类型为 word 类型。
2. 修改底层驱动,根据延时判断数据类型。
查看驱动流程,所有的读写都是通过队列来完成的,最终都会调用到这个函数。
msm_cci.c kernel\msm-4.9\drivers\media\platform\msm\camera_v2\sensor\cci
int32_t msm_cci_data_queue(...)
默认写函数关键代码如下:
/* max of 10 data bytes */
do {
if (i2c_msg->data_type == MSM_CAMERA_I2C_BYTE_DATA) {
data[i++] = i2c_cmd->reg_data;
reg_addr++;
} else {
if ((i + 1) <= cci_dev->payload_size) {
data[i++] = (i2c_cmd->reg_data &
0xFF00) >> 8; /* MSB */
data[i++] = i2c_cmd->reg_data &
0x00FF; /* LSB */
reg_addr++;
} else
break;
}
i2c_cmd++;
--cmd_size;
} while (((c_ctrl->cmd == MSM_CCI_I2C_WRITE_SEQ) || pack--) &&
(cmd_size > 0) && (i <= cci_dev->payload_size));
可以看到这里判断了数据类型,并且若是word类型,data数组中分别放置高8bit和低8bit。
这里做两个修改:
- 通过延时区分8bit、16bit 数据类型。
添加如下内容
+#if 1
+ if (i2c_cmd->delay > 0)
+ i2c_msg->data_type = MSM_CAMERA_I2C_WORD_DATA;
+ else
+ i2c_msg->data_type = MSM_CAMERA_I2C_BYTE_DATA;
+#endif
i = 0;
data[i++] = CCI_I2C_WRITE_CMD;
2. 由于大小端相反,交换高低8bit数据。
data[i++] = (i2c_cmd->reg_data &
0xFF00) >> 8; /* MSB */
data[i++] = i2c_cmd->reg_data &
0x00FF; /* LSB */
reg_addr++;
===> 修改为
data[i++] = i2c_cmd->reg_data &
0x00FF; /* LSB */
data[i++] = (i2c_cmd->reg_data &
0xFF00) >> 8; /* MSB */
5. vendor drv 配置
TC358840的驱动是在 gc5025的驱动上修改的,只是名字还是用的gc5025,实际驱动和gc5025毫无关系。
1. 修改device-vendor.mk
\vendor\qcom\proprietary\common\config
由于是YUV cam,去掉所有效果相关的 lib。只保留 MM_CAMERA += libmmcamera_gc5025
2. 修改 cam config 配置文件。
msm8953_camera.xml vendor\qcom\proprietary\
mm-camera\mm-camera2\media-controller\modules\sensors\configs\
<CameraModuleConfig>
<CameraId>0</CameraId>
<SensorName>gc5025</SensorName>
<ModesSupported>1</ModesSupported>
<Position>BACK</Position>
<MountAngle>90</MountAngle>
<CSIInfo>
<CSIDCore>0</CSIDCore>
<LaneMask>0x1F</LaneMask>
<LaneAssign>0x4320</LaneAssign>
<ComboMode>0</ComboMode>
</CSIInfo>
<LensInfo>
<FocalLength>5.24</FocalLength>
<FNumber>1.05</FNumber>
<TotalFocusDistance>47</TotalFocusDistance>
<HorizontalViewAngle>94</HorizontalViewAngle>
<VerticalViewAngle>49.5</VerticalViewAngle>
<MinFocusDistance>0.1</MinFocusDistance>
</LensInfo>
</CameraModuleConfig>
3. 去除 charomatix 配置文件的链接
vendor/qcom/proprietary/mm-camera/
mm-camera2/mediacontroller/modules/sensors/configs/project.mk
去除 gc5025的效果文件的编译。
+# include $(CLEAR_VARS)
+# LOCAL_MODULE:= gc5025_chromatix.xml
+# LOCAL_MODULE_CLASS := EXECUTABLES
+# LOCAL_SRC_FILES := gc5025_chromatix.xml
+# LOCAL_MODULE_TAGS := optional
+# LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/camera
+# LOCAL_MODULE_OWNER := qti
+# include $(BUILD_PREBUILT)
4. 去除 charomatix 配置文件的编译
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/chromatix/0310/chromatix_gc5025/Android.mk
ifeq ($(call is-vendor-board-platform,QCOM),true)
- include $(call all-subdir-makefiles)
+ # include $(call all-subdir-makefiles)
endif
5. 延长帧检测时间。由于在840的初始化代码中,有很多必要且很长的延时,因此为了避免帧检测时间过短,导致还没初始化完成的时候就误判退出,加长了帧检测时间的设置。
vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/mct/bus/mct_bus.c
-#define MCT_BUS_NANOSECOND_SCALER 1000000000
+//#define MCT_BUS_NANOSECOND_SCALER 1000000000
+#define MCT_BUS_NANOSECOND_SCALER 3000000000 // 3s
#define MAX_MCT_BUS_QUEUE_LENGTH 1000
补充:
EDID :
EDID,Extended display identification data,中文名称扩展显示器识别数据,是VESA在制定DDC显示器数据通道通信协议时,制定的有关显示器识别数据的标准。EDID存储在显示器中的DDC存储器中,当电脑主机与显示器连接后,电脑主机会通道DDC通道读取显示器DDC存储器中的存储的EDID。
EDID其中包含有关显示器及其性能的参数,包括供应商信息、最大图像大小、颜色设置、厂商预设置、频率范围的限制以及显示器名和序列号的字符串等等。形象地说,EDID就是显示器的身份证、户口本、技能证书等证件的集合,目的就是告诉别人我是谁,我从哪来,我能干什么。
说人话:就是电脑主机输出端,读取到 EDID 信息,获取到芯片端最大支持的信息,然后是输出对应的数据流。
对我们来说,我们关心的,就是芯片中对自身支持的最大分辨率、帧率的一个信息。EDID 在成功写入芯片后,就代表了芯片对外界的一个回应信息,在电脑主机插入芯片的HDMI接口的时候,主机会读取芯片的EDID数据,从而明白主机本身应该输出多大分辨率和帧率的数据流。
840芯片同时可储存多组EDID信息,即同时支持多种分辨率。如同时支持1080P@60fps、1080P@30fps、720P@60fps、720P@60fps等等。
你再快,也永远有人比你快!Come On ~~