XS9922B 是一款四通道多合一模拟高清解码器,支持HDcctv高清协议和CVBS标清协议。它最大支持1080p @ 30fps的视频分辨率,具有以下亮点:
- 四通道视频输入
- MIPI CSI-2 4-Lane视频输出
- 支持音频传输
- 封装:QFN88,尺寸为10mm x 10mm 1.
此芯片将接收到的高清模拟复合视频信号经过模数转化、视频解码以及2D图像处理之后,转化为YCbCr,并以MIPI CSI接口输出。
此外,XS9922A/B 是另一款四通道模拟复合视频解码芯片方案,同样支持HDcctv高清协议和CVBS标清协议,最高支持4路2M@30fps。它也将接收到的高清模拟复合视频信号转化为YCbCr并以MIPI CSI接口输出
硬件原理图连接:
设备树配置
RK3568 MIPI CSI RX端配置
&csi2_dphy_hw {status = "okay";
};&csi2_dphy0 {status = "okay";ports {#address-cells = <1>;#size-cells = <0>;port@0 {reg = <0>;#address-cells = <1>;#size-cells = <0>;mipi_in_ucam0: endpoint@1 {reg = <1>;remote-endpoint = <&ucam_out0>;data-lanes = <1 2 3 4>;}; };port@1 {reg = <1>;#address-cells = <1>;#size-cells = <0>;csidphy_out: endpoint@0 {reg = <0>;remote-endpoint = <&mipi_csi2_input>;};};};
};&mipi_csi2 {status = "okay";ports {#address-cells = <1>;#size-cells = <0>;port@0 {reg = <0>;#address-cells = <1>;#size-cells = <0>;mipi_csi2_input: endpoint@1 {reg = <1>;remote-endpoint = <&csidphy_out>;data-lanes = <1 2 3 4>;};};port@1 {reg = <1>;#address-cells = <1>;#size-cells = <0>;mipi_csi2_output: endpoint@0 {reg = <0>;remote-endpoint = <&cif_mipi_in>;};};};};&rkcif_mipi_lvds {status = "okay";port {cif_mipi_in: endpoint {remote-endpoint = <&mipi_csi2_output>;data-lanes = <1 2 3 4>;};};
};
xs9922设备树配置
xs9922: xs9922@31 {compatible = "xs9922";status = "okay";reg = <0x31>;clocks = <&cru CLK_CAM0_OUT>; clock-names = "xvclk";pinctrl-names = "rockchip,camera_default";//使用CAM CLK0引脚pinctrl-0 = <&cam_clkout0>;power-domains = <&power RK3568_PD_VI>;//复位GPIOreset-gpios = <&gpio4 RK_PB7 GPIO_ACTIVE_HIGH>;//外接摄像头电源GPIOpower2-gpios = <&gpio4 RK_PD0 GPIO_ACTIVE_HIGH>;rockchip,camera-module-index = <0>;rockchip,camera-module-facing = "front";rockchip,camera-module-name = "default";rockchip,camera-module-lens-name = "default";port {ucam_out0: endpoint {remote-endpoint = <&mipi_in_ucam0>;data-lanes = <1 2 3 4>;};};};
RK3568 SDK默认支持XS9922驱动:
drivers\media\i2c\xs9922\xs9922_reg_cfg.h
xs9922_init_cfg
typedef struct regval {unsigned short addr; //芯片数据地址unsigned char val; //i2c数据unsigned char nDelay; // 延时
}REG_VAL;xs9922 i2c初始化配置
REG_VAL xs9922_init_cfg[] = {{0x4200, 0x3f, 0x00},{0x4210, 0x3f, 0x00},{0x4220, 0x3f, 0x00},
...//图像调优参数//{0x0106, 0x80}, // contrast //{0x0107, 0x00}, // brightness//{0x0108, 0x80}, // staturation//{0x0109, 0x00}, // hue
...xs9922MIPI 输出速率,默认1.2G{0x511b, 0x36, 0x00},//0x78=1.5G,0x36=1.2G,0x34=1G,0x32=800M
...};
//1080p 配置
REG_VAL xs9922_1080p_4lanes_25fps[] = {
...
};//720p配置
REG_VAL xs9922_720p_4lanes_25fps[] = {
...
};
//xs9922音频配置参数
REG_VAL xs9922_audio_codec[] = {...
};
xs9922.c
static const struct xs9922_mode supported_modes[] = {{//supported_modes[0] 720p配置.bus_fmt = MEDIA_BUS_FMT_UYVY8_2X8, // MEDIA_BUS_FMT_UYVY8_2X8,.width = 1280,.height = 720,.max_fps = {.numerator = 10000,.denominator = 150000,},.global_reg_list = xs9922_init_cfg,.reg_list = xs9922_720p_4lanes_25fps,...},{//supported_modes[1] 1920p配置.bus_fmt = MEDIA_BUS_FMT_UYVY8_2X8, // MEDIA_BUS_FMT_UYVY8_2X8,.width = 1920,.height = 1080,.max_fps = {.numerator = 10000,.denominator = 150000,},.global_reg_list = xs9922_init_cfg,.reg_list = xs9922_1080p_4lanes_25fps,...}};驱动配置函数
static int xs9922_probe(struct i2c_client *client,const struct i2c_device_id *id)
{...//走1080p,supported_modes[0]为720pxs9922->cur_mode = &supported_modes[1]; ...//上电时许 RK3568 CLK时钟初始化ret = __xs9922_power_on(xs9922);if (ret) {dev_err(dev, "Failed to power on xs9922\n");goto err_free_handler;}//读芯片ID 比较ret = check_chip_id(client);if (ret) {dev_err(dev, "Failed to check senosr id\n");goto err_free_handler;}//热插拔事件初始化 一般没啥用xs9922->input_dev = devm_input_allocate_device(dev);if (xs9922->input_dev == NULL) {dev_err(dev, "failed to allocate xs9922 input device\n");return -ENOMEM;}xs9922->input_dev->name = "xs9922_input_event";set_bit(EV_MSC, xs9922->input_dev->evbit);set_bit(MSC_RAW, xs9922->input_dev->mscbit);...//PM电源管理初始化pm_runtime_set_active(dev);pm_runtime_enable(dev);pm_runtime_idle(dev);//初始化I2C数据,启动热拔插线程read_config(xs9922);
}
static int read_config(void *data) //初始化I2C数据,启动热拔插线程
static int read_config(void *data)
{...//I2C 写入初始化配置xs9922_write_array(xs9922->client, xs9922->cur_mode->global_reg_list);....//打开驱动文件/etc/board.conf 获取走的分辨率配置,没有文件则走软件默认配置do {fp = filp_open(config_file, O_RDONLY, 0);...} while (tries > 0);if (IS_ERR(fp)) {printk(KERN_ERR "open " config_file " fail!!!\n");// return -1;} else {.....}//写入分辨率配置 数据switch_mode(xs9922);xs9922_write_array(xs9922->client, xs9922_lastInit);热拔插线程detect_thread_start(xs9922);printk("__xs9922_init out\n");return 0;
}
xs9922_ioctl一般由RK热拔插capture.c调用
static long xs9922_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{...//RK热拔插驱动获取热拔插状态case RKMODULE_GET_VICAP_RST_INFO:xs9922_get_vicap_rst_inf(xs9922, (struct rkmodule_vicap_reset_info *)arg);// printk("[chad] Get vicap reset info --->>> is_reset : %d\n", xs9922->is_reset);break;case RKMODULE_SET_VICAP_RST_INFO:xs9922_set_vicap_rst_inf(xs9922, *(struct rkmodule_vicap_reset_info *)arg);// printk("[chad] Set vicap reset info --->>> is_reset : %d\n", xs9922->is_reset);break;//由RK热拔插驱动调用,复位MIPI数据case RKMODULE_SET_QUICK_STREAM:stream = *((u32 *)arg);if (stream) {dev_info(&xs9922->client->dev,"[chad]======== quick stream on: do xs9922 mipi reset start =======\n");ret = xs9922_write_reg(xs9922->client, 0x5004,XS9922_REG_VALUE_08BIT, 0x00);...dev_info(&xs9922->client->dev,"[chad]======== quick stream on: do xs9922 mipi reset end =======\n");} else {xs9922_write_reg(xs9922->client, 0x0e08,XS9922_REG_VALUE_08BIT, 0x00);....dev_info(&xs9922->client->dev,"[chad]======== quick stream off:xs9922 mipi Disabled =======\n");usleep_range(100 * 1000, 110 * 1000);}// mutex_unlock(&xs9922->mutex);break;...return ret;
}
MIPI输出 关闭
static int __xs9922_start_stream(struct xs9922 *xs9922)
{struct i2c_client *client = xs9922->client;
#ifdef OPEN_XS9922_INITxs9922_write_array(xs9922->client, xs9922->cur_mode->global_reg_list);switch_mode(xs9922);xs9922_write_array(xs9922->client, xs9922_lastInit);
#endifxs9922_write_reg(client, 0x5004, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x5005, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x5006, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x5007, XS9922_REG_VALUE_08BIT, 0x01);usleep_range(220 * 1000, 230 * 1000);xs9922_write_reg(client, 0x0e08, XS9922_REG_VALUE_08BIT, 0x01);xs9922_write_reg(client, 0x1e08, XS9922_REG_VALUE_08BIT, 0x01);xs9922_write_reg(client, 0x2e08, XS9922_REG_VALUE_08BIT, 0x01);xs9922_write_reg(client, 0x3e08, XS9922_REG_VALUE_08BIT, 0x01);dev_dbg(&client->dev, "%s OUT---<<<\n", __func__);return 0;
}static int __xs9922_stop_stream(struct xs9922 *xs9922)
{struct i2c_client *client = xs9922->client;dev_dbg(&client->dev, "%s In---<<<\n", __func__);// usleep_range(50 * 1000, 60 * 1000);xs9922_write_reg(client, 0x0e08, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x1e08, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x2e08, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x3e08, XS9922_REG_VALUE_08BIT, 0x00);// usleep_range(150 * 1000, 160 * 1000);xs9922_write_reg(client, 0x5004, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x5005, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x5006, XS9922_REG_VALUE_08BIT, 0x00);xs9922_write_reg(client, 0x5007, XS9922_REG_VALUE_08BIT, 0x00);
#if __CLOSE_SENSOR__detect_thread_stop(xs9922);
#endifdev_dbg(&client->dev, "%s OUT---<<<\n", __func__);return 0;
}
BUG:
1.720P 也可用 xs9922_1080p_4lanes_25fps 配置,V4L2下发获取分辨率720P即可
2.图像有绿条,测试配置1.5G速率 {0x511b, 0x78, 0x00},//0x78=1.5G,0x36=1.2G,0x34=1G,0x32=800M 是否改善
3.I2C 数据无法写入,使用外部晶振确认是否起振,使用RK3568 CameraCLK引脚确认有无时钟
4.可以获取寄存器地址 1<<12 ,2<<12,3<<12,4<<12 ,获取接入摄像头的 分辨率
for (i = 0; i < PAD_MAX; i++) {xs9922_read_reg(xs9922->client, 0x0001 | (i << 12), 1, &value);if (value == 64) {printk("i= %d,AutoPareVideoSize 720p\n",i);} else if (value == 68) {printk("i = %d 1080p\n",i);}
}
5.打开摄像头 预览画面出现图像断层,请尝试延长__xs9922_start_stream 里的延时