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>;
//复位GPIO
reset-gpios = <&gpio4 RK_PB7 GPIO_ACTIVE_HIGH>;
//外接摄像头电源GPIO
power2-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]为720p
xs9922->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_INIT
xs9922_write_array(xs9922->client, xs9922->cur_mode->global_reg_list);
switch_mode(xs9922);
xs9922_write_array(xs9922->client, xs9922_lastInit);
#endif
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, 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);
#endif
dev_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 里的延时