cms建站模板app,seo网络优化是什么工作,做体育赛事网站公司,全国企业信息网查询平台官网一、概述 关于 RN6752V1 这个芯片这里就不做介绍了#xff0c;看到这篇笔记的小伙伴应该都明白#xff0c;虽然说 RN6752V1 芯片是 AHD 信号的解码芯片#xff0c;但是也可以把芯片当做是一个 YUV 信号的 MIPI 摄像头#xff0c;所以驱动的编写和 MIPI 摄像头无太大的区别。…一、概述 关于 RN6752V1 这个芯片这里就不做介绍了看到这篇笔记的小伙伴应该都明白虽然说 RN6752V1 芯片是 AHD 信号的解码芯片但是也可以把芯片当做是一个 YUV 信号的 MIPI 摄像头所以驱动的编写和 MIPI 摄像头无太大的区别。这里主要是介绍具体的函数关于 MIPI 驱动的框架程序看我之前的笔记Linux MIPI 摄像头驱动框架编写RN6752解码芯片 二、RN6752 帧格式 RN6752 支持 DVP 和 MIPI 信号这里我主要是对 MIPI 信号的使用当然 DVP 通信的操作也可以做参考。 寄存地配置通过代理商提供的头文件中可以获取到相关寄存器的配置如下所示 static const struct sensor_register rn6752_fhd_1080P25_video[] {{ 0x19, 0x0A }, // 视频格式检测滞后控制{ 0x81, 0x01 }, // 打开视频解码器{ 0xDF, 0xFE }, // 启用HD格式{ 0xF0, 0xC0 }, // 使能 FIFO 和 144 MHz 解码器输出{ 0xA3, 0x04 }, // 启用 HD 输出{ 0x88, 0x40 }, // 禁用 SCLK1 输出{ 0xF6, 0x40 }, // 禁用 SCLK3A 输出/* 切换到ch0默认可选 */{ 0xFF, 0x00 }, // 寄存器集选择{ 0x33, 0x10 }, // 检测中的视频{ 0x4A, 0xA8 }, // 检测中的视频{ 0x00, 0x20 }, // internal use*{ 0x06, 0x08 }, // internal use*{ 0x07, 0x63 }, // 高清格式{ 0x2A, 0x01 }, // 滤波器控制{ 0x3A, 0x24 }, // 在SAV/EAV代码中插入通道ID{ 0x3F, 0x10 }, // 通道ID{ 0x4C, 0x37 }, // 均衡器{ 0x4F, 0x03 }, // 同步控制{ 0x50, 0x03 }, // 1080p分辨率{ 0x56, 0x02 }, // 144M 和 BT656模式{ 0x5F, 0x44 }, // 消隐电平{ 0x63, 0xF8 }, // 滤波器控制{ 0x59, 0x00 }, // 扩展寄存器存取{ 0x5A, 0x48 }, // 扩展寄存器的数据{ 0x58, 0x01 }, // 启用扩展寄存器写入{ 0x59, 0x33 }, // 扩展寄存器存取{ 0x5A, 0x23 }, // 扩展寄存器的数据{ 0x58, 0x01 }, // 启用扩展寄存器写入{ 0x51, 0xF4 }, // 比例因子1{ 0x52, 0x29 }, // 比例因子2{ 0x53, 0x15 }, // 比例因子3{ 0x5B, 0x01 }, // H-标度控制{ 0x5E, 0x08 }, // 启用H缩放控制{ 0x6A, 0x87 }, // H-标度控制{ 0x28, 0x92 }, // 剪裁{ 0x03, 0x80 }, // 饱和{ 0x04, 0x80 }, // 颜色{ 0x05, 0x04 }, // 尖锐{ 0x57, 0x23 }, // 黑色/白色拉伸{ 0x68, 0x00 }, // coring{ 0x37, 0x33 }, // { 0x61, 0x6C }, //
#ifdef USE_BLUE_SCREEN{ 0x3A, 0x24 }, // AHD 断开链接时屏幕为蓝色
#else{ 0x3A, 0x2C }, // AHD 断开链接时屏幕为黑色{ 0x3B, 0x00 }, //{ 0x3C, 0x80 }, //{ 0x3D, 0x80 }, //
#endif{ 0x2E, 0x30 }, // 强制不播放视频{ 0x2E, 0x00 }, // 回归平常/* mipi 连接 */{ 0xFF, 0x09 }, // 切换到 mipi tx1{ 0x00, 0x03 }, // enable bias{ 0xFF, 0x08 }, // 切换到 mipi csi1{ 0x04, 0x03 }, // csi1 和 tx1 重置{ 0x6C, 0x11 }, // 禁用 ch 输出打开 ch0
#ifdef USE_MIPI_4LANES{ 0x06, 0x7C }, // mipi 4 线
#else{ 0x06, 0x4C }, // mipi 2 线
#endif{ 0x21, 0x01 }, // 启用 hs 时钟{ 0x34, 0x06 }, //{ 0x35, 0x0B }, // { 0x78, 0xC0 }, // ch0 的 Y/C 计数{ 0x79, 0x03 }, // ch0 的 Y/C 计数{ 0x6C, 0x01 }, // 启用 ch 输出{ 0x04, 0x00 }, // csi1 和 tx1 重置完成{ 0x20, 0xAA }, //
#ifdef USE_MIPI_NON_CONTINUOUS_CLOCK{ 0x07, 0x05 }, // 启用非连续时钟
#else{ 0x07, 0x04 }, // 启用连续时钟
#endif{ 0xFF, 0x0A }, // 切换到 mipi csi3{ 0x6C, 0x10 }, // 禁用 ch 输出关闭 ch0~3{REG_NULL, 0x00},
}; 注意 其他格式的寄存器我这里就不附上了可以参考代理商提供的头文件 将配置信息存入帧列表中 static const struct rn6752_framesize rn6752_mipi_framesizes[] {{.width 1280,.height 720,.max_fps {.numerator 10000,.denominator 250000,},.regs rn6752_fhd_720P25_video,}, {.width 1280,.height 720,.max_fps {.numerator 10000,.denominator 300000,},.regs rn6752_fhd_720P30_video,}, {.width 1920,.height 1080,.max_fps {.numerator 10000,.denominator 250000,},.regs rn6752_fhd_1080P25_video,}, {.width 1920,.height 1080,.max_fps {.numerator 10000,.denominator 300000,},.regs rn6752_fhd_1080P30_video,}, {.width 1280,.height 960,.max_fps {.numerator 10000,.denominator 250000,},.regs rn6752_fhd_960P25_video,}, {.width 1280,.height 960,.max_fps {.numerator 10000,.denominator 300000,},.regs rn6752_fhd_960P30_video,}
}; 配置默认帧在 rn6752_probe 函数中存入默认支持的帧列表如下所示 static void rn6752_get_default_format(struct rn6752 *rn6752,struct v4l2_mbus_framefmt *format)
{format-width rn6752-framesize_cfg[2].width; /* 设置默认宽度 */format-height rn6752-framesize_cfg[2].height; /* 设置默认高度 */format-colorspace V4L2_COLORSPACE_SRGB; /* 设置默认色彩空间为标准的 sRGB 色彩空间 */format-code rn6752_formats[0].code; /* 设置默认编码格式 */format-field V4L2_FIELD_NONE; /* 设置默认场模式 */
}/* rn6752_mipi_framesizes 是 rn6752 mipi 通信支持的所有帧格式 */
rn6752-framesize_cfg rn6752_mipi_framesizes;
rn6752-cfg_num ARRAY_SIZE(rn6752_mipi_framesizes);
/* 获取摄像头传感器支持的图像帧格式 */
rn6752_get_default_format(rn6752, rn6752-format);
rn6752-frame_size rn6752-framesize_cfg[2]; /* 设置帧大小 */
rn6752-format.width rn6752-framesize_cfg[2].width; /* 设置宽度 */
rn6752-format.height rn6752-framesize_cfg[2].height; /* 设置高度 */
rn6752-fps DIV_ROUND_CLOSEST(rn6752-framesize_cfg[2].max_fps.denominator,rn6752-framesize_cfg[2].max_fps.numerator); /* 设置最大帧速率 */ 注意 首先将所有支持的帧列表存入了 rn6752-framesize_cfg 中 将支持的列表数量存入 rn6752-cfg_num 中 将默认支持的帧格式和大小存入 rn6752-format 中这个在用户空间可以查看 将默认支持的帧大小存入 rn6752-frame_size 中 将默认支持的帧率存入 rn6752-fps 中 以上这些默认变量将在后面的函数中经常用到所以需要特别注意一下不然很难理解数据从哪里来的 三、Media 设备节点 之前在 Media 子系统中提到过模块之间的关系查看命令media-ctl -p -d /dev/mediaX通过命令可以得到驱动中的一些信息如下图所示 Media 帧大小Media 帧大小是在驱动初始化时通过 rn6752_get_fmt 函数获取的程序如下 static int rn6752_get_fmt(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_format *fmt)
{struct i2c_client *client v4l2_get_subdevdata(sd); /* 获取i2c_client指针 */struct rn6752 *rn6752 to_rn6752(sd);/* 使用dev_dbg打印日志显示当前函数进入 */// dev_info(client-dev, %s enter\n, __func__);/* 条件成立时表示要获取正在尝试的格式 */if (fmt-which V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_APIstruct v4l2_mbus_framefmt *mf;/* 获取正在尝试的格式 */mf v4l2_subdev_get_try_format(sd, cfg, 0);mutex_lock(rn6752-lock);fmt-format *mf;mutex_unlock(rn6752-lock);return 0;
#elsereturn -ENOTTY;
#endif}/* 条件不成立时表示要获取当前的格式 */mutex_lock(rn6752-lock);fmt-format rn6752-format;mutex_unlock(rn6752-lock);/* 使用dev_dbg打印日志显示当前格式的代码值、宽度和高度 */dev_dbg(client-dev, %s: %x %dx%d\n, __func__, rn6752-format.code,rn6752-format.width, rn6752-format.height);return 0;
} 帧格式判断 Media 设备是通过 rn6752_enum_frame_sizes 和 rn6752_enum_frame_interval 函数枚举了帧大小和帧率这两个函数主要起到判断的作用确实当前帧率是否是驱动支持的程序如下 static int rn6752_enum_frame_sizes(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_frame_size_enum *fse)
{struct rn6752 *rn6752 to_rn6752(sd);struct i2c_client *client v4l2_get_subdevdata(sd);int i ARRAY_SIZE(rn6752_formats);printk(KERN_INFOrn6752_enum_frame_sizes................................................\n);dev_dbg(client-dev, %s:\n, __func__);if (fse-index rn6752-cfg_num)return -EINVAL;while (--i)if (fse-code rn6752_formats[i].code)break;fse-code rn6752_formats[i].code;fse-min_width rn6752-framesize_cfg[fse-index].width;fse-max_width fse-min_width;fse-max_height rn6752-framesize_cfg[fse-index].height;fse-min_height fse-max_height;return 0;
}static int rn6752_enum_frame_interval(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_frame_interval_enum *fie)
{struct rn6752 *rn6752 to_rn6752(sd);printk(KERN_INFOrn6752_enum_frame_interval index: %d....................\n, fie-index );/* 检查传入的 fie 结构体中的 index 字段是否超出了 rn6752 所支持的帧间隔配置数量cfg_num */if (fie-index rn6752-cfg_num)return -EINVAL;/* 检查传入的 fie 结构体中的 code 字段是否与期望的媒体总线格式MEDIA_BUS_FMT_UYVY8_2X8匹配 */if (fie-code ! MEDIA_BUS_FMT_UYVY8_2X8)return -EINVAL;fie-width rn6752-framesize_cfg[fie-index].width; /* 宽 */fie-height rn6752-framesize_cfg[fie-index].height; /* 高 */fie-interval rn6752-framesize_cfg[fie-index].max_fps; /* 最大帧率 */return 0;
} 帧大小设置 可以通过 rn6752_set_fmt 函数设置帧的大小程序如下 tatic int rn6752_set_fmt(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_format *fmt)
{struct i2c_client *client v4l2_get_subdevdata(sd);int index ARRAY_SIZE(rn6752_formats);struct v4l2_mbus_framefmt *mf fmt-format;const struct rn6752_framesize *size NULL;struct rn6752 *rn6752 to_rn6752(sd);printk(KERN_INFOrn6752_set_fmt................................................\n);dev_info(client-dev, %s enter\n, __func__);/* 根据传入的参数调整帧大小和帧速率并返回适合的帧大小和帧速率 */__rn6752_try_frame_size_fps(rn6752, mf, size, rn6752-fps);/* 遍历rn6752_formats数组 */while (--index 0)if (rn6752_formats[index].code mf-code)break;if (index 0)return -EINVAL;/* 色彩空间为sRGB场为无 */mf-colorspace V4L2_COLORSPACE_SRGB;mf-code rn6752_formats[index].code;mf-field V4L2_FIELD_NONE;mutex_lock(rn6752-lock);if (fmt-which V4L2_SUBDEV_FORMAT_TRY) {
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_APImf v4l2_subdev_get_try_format(sd, cfg,fmt-pad); /* 使用v4l2_subdev_get_try_format函数获取正在尝试的格式 */*mf fmt-format;
#elsereturn -ENOTTY;
#endif} else {if (rn6752-streaming) {mutex_unlock(rn6752-lock);return -EBUSY;}/* 分别设置为获取到的帧大小和传入的格式 */rn6752-frame_size size;rn6752-format fmt-format;}mutex_unlock(rn6752-lock);return 0;
} 帧间隔获取 static int rn6752_g_frame_interval(struct v4l2_subdev *sd,struct v4l2_subdev_frame_interval *fi)
{struct rn6752 *rn6752 to_rn6752(sd);printk(KERN_INFOrn6752_g_frame_interval................................................\n);mutex_lock(rn6752-lock);fi-interval rn6752-frame_size-max_fps;mutex_unlock(rn6752-lock);return 0;
} 四、总线编码格式 之前有提到过RN6752 支持 DVP 和 MIPI 总线格式所以可以在一个驱动中实现两个功能这里我就是写了 MIPI 的通信方式我目前对 DVP 也不了解以后在补上。 刚好驱动中提供了两个函数可以获取驱动总线的格式如下所示 获取当前媒体总线配置的函数 static int rn6752_g_mbus_config(struct v4l2_subdev *sd,struct v4l2_mbus_config *config)
{printk(KERN_INFOrn6752_g_mbus_config................................................\n);/* 总线类型是CSI-2 */config-type V4L2_MBUS_CSI2;config-flags V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_CHANNEL_0 |V4L2_MBUS_CSI2_CHANNEL_1 |V4L2_MBUS_CSI2_CONTINUOUS_CLOCK;return 0;
} 枚举所有支持的媒体总线编码和格式 static int rn6752_enum_mbus_code(struct v4l2_subdev *sd,struct v4l2_subdev_pad_config *cfg,struct v4l2_subdev_mbus_code_enum *code)
{struct i2c_client *client v4l2_get_subdevdata(sd);printk(KERN_INFOrn6752_enum_mbus_code................................................\n);dev_dbg(client-dev, %s:\n, __func__);if (code-index ARRAY_SIZE(rn6752_formats))return -EINVAL;code-code rn6752_formats[code-index].code;return 0;
} 五、电源管理 摄像头每次开启和关闭时都需要通过电源管理函数配置摄像头电源 static int rn6752_power(struct v4l2_subdev *sd, int on)
{struct rn6752 *rn6752 to_rn6752(sd);struct i2c_client *client rn6752-client;int ret 0;/* 使用dev_info打印日志显示当前函数和行号并打印on参数的值 */dev_dbg(client-dev, %s(%d) on(%d)\n, __func__, __LINE__, on);mutex_lock(rn6752-lock);if (rn6752-power_on !! on)goto unlock_and_return;if (on) {ret pm_runtime_get_sync(client-dev);if (ret 0) {pm_runtime_put_noidle(client-dev);goto unlock_and_return;}rn6752-power_on true;} else {pm_runtime_put(client-dev);rn6752-power_on false;}unlock_and_return:mutex_unlock(rn6752-lock);return ret;
} 六、摄像头控制 由于这里我没有实现太多的控制功能所以只实现了必要的两个控制最主要的是复位时执行的 RKMODULE_SET_QUICK_STREAM 功能 static long rn6752_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{struct rn6752 *rn6752 to_rn6752(sd);// struct rkmodule_hdr_cfg *hdr;long ret 0;u32 stream 0;// dev_dbg(KERN_INFO rn6752_ioctl 0x%x..........\n, cmd);switch (cmd) {case RKMODULE_GET_MODULE_INFO:rn6752_get_module_info(rn6752, (struct rkmodule_inf *)arg);break;case RKMODULE_SET_QUICK_STREAM:stream *((u32 *)arg);rn6752_set_streaming(rn6752, !!stream);break;default:ret -ENOIOCTLCMD;break;}return ret;
}#ifdef CONFIG_COMPAT
static long rn6752_compat_ioctl32(struct v4l2_subdev *sd, unsigned int cmd,unsigned long arg)
{void __user *up compat_ptr(arg);struct rkmodule_inf *inf;struct rkmodule_awb_cfg *cfg;long ret;u32 stream 0;// dev_dbg(KERN_INFO rn6752_compat_ioctl32..........\n);switch (cmd) {case RKMODULE_GET_MODULE_INFO:inf kzalloc(sizeof(*inf), GFP_KERNEL);if (!inf) {ret -ENOMEM;return ret;}ret rn6752_ioctl(sd, cmd, inf);if (!ret)ret copy_to_user(up, inf, sizeof(*inf));kfree(inf);break;case RKMODULE_AWB_CFG:cfg kzalloc(sizeof(*cfg), GFP_KERNEL);if (!cfg) {ret -ENOMEM;return ret;}ret copy_from_user(cfg, up, sizeof(*cfg));if (!ret)ret rn6752_ioctl(sd, cmd, cfg);kfree(cfg);break;case RKMODULE_SET_QUICK_STREAM:ret copy_from_user(stream, up, sizeof(u32));if (!ret)ret rn6752_ioctl(sd, cmd, stream);break;default:ret -ENOIOCTLCMD;break;}return 0;
}
#endif 七、数据流控制 整个驱动最重要的便是流控制函数通过此函数完成了摄像头的启动和停止 static int rn6752_set_streaming(struct rn6752 *rn6752, int on)
{struct i2c_client *client rn6752-client;int ret 0;dev_info(client-dev, %s: on: %d\n, __func__, on);if (on){ret rn6752_write(client, 0x80, 0x31);usleep_range(200, 500);ret | rn6752_write(client, 0x80, 0x30);if (ret){dev_err(client-dev, rn6752 soft reset failed\n);return ret;}ret rn6752_write_array(client, rn6752-frame_size-regs);if (ret)dev_err(client-dev, rn6752 start initialization failed\n);}else{ret rn6752_write(client, 0x80, 0x00);if (ret)dev_err(client-dev, rn6752 soft standby failed\n);}return ret;
}static int rn6752_s_stream(struct v4l2_subdev *sd, int on)
{struct i2c_client *client v4l2_get_subdevdata(sd);struct rn6752 *rn6752 to_rn6752(sd);int ret 0;unsigned int fps;/* 计算帧率和延迟时间 */fps DIV_ROUND_CLOSEST(rn6752-frame_size-max_fps.denominator,rn6752-frame_size-max_fps.numerator);dev_info(client-dev, %s: on: %d, %dx%d%d\n, __func__, on,rn6752-frame_size-width, rn6752-frame_size-height,DIV_ROUND_CLOSEST(rn6752-frame_size-max_fps.denominator,rn6752-frame_size-max_fps.numerator));mutex_lock(rn6752-lock);on !!on;if (rn6752-streaming on)goto unlock;if (on) {ret pm_runtime_get_sync(client-dev);if (ret 0){pm_runtime_put_noidle(client-dev);goto unlock;}}rn6752-streaming on;ret rn6752_set_streaming(rn6752, on);if (ret)rn6752-streaming !on;pm_runtime_put(client-dev);unlock:mutex_unlock(rn6752-lock);return ret;
} 注意 摄像头驱动中并没有图像接收之类的关系而数据流操作函数主要的作用是对芯片进行初始化使摄像头进入工作模式。从上面的驱动程序可以看出整个驱动并没有其他特别的功能就是一个 I2C 控制功能所以摄像头的驱动其实就是一个 I2C 驱动程序。 由于笔记内容有点多这里我就不附上完成的驱动程序了其次是驱动程序也比较简单看完的小伙伴应该都能明白。主要的难度都在调试摄像头驱动上面我也折腾了很久有需要的小伙变可以看我后面的笔记 参考资料 gc2145.c 和 imx335.c 驱动程序 文章转载自浇筑菜鸟
原文链接https://www.cnblogs.com/jzcn/p/17825502.html