完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
v4l2_subdev_ops v4l2_subdev_ops 回调函数是 Sensor 驱动中逻辑控制的核心。回调函数包括丰富的接口,具体可以查看kernel 代码 include/media/v4l2-subdev.h 。建议 Sensor 驱动至少包括如下回调函数: static const struct v4l2_subdev_ops gc8034_subdev_ops = { .core = &gc8034_core_ops,//Define core ops callbacks for subdevs .video = &gc8034_video_ops, //Callbacks used when v4l device was opened in video mode. .pad = &gc8034_pad_ops,//v4l2-subdev pad level operations }; #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API //V4L2 subdev internal ops static const struct v4l2_subdev_internal_ops gc8034_internal_ops = { .open = gc8034_open, }; #endif static const struct v4l2_subdev_core_ops gc8034_core_ops = { .s_power = gc8034_s_power, .ioctl = gc8034_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = gc8034_compat_ioctl32, #endif }; static const struct v4l2_subdev_video_ops gc8034_video_ops = { .s_stream = gc8034_s_stream, .g_frame_interval = gc8034_g_frame_interval, .g_mbus_config = gc8034_g_mbus_config, }; static const struct v4l2_subdev_pad_ops gc8034_pad_ops = { .enum_mbus_code = gc8034_enum_mbus_code, .enum_frame_size = gc8034_enum_frame_sizes, .enum_frame_interval = gc8034_enum_frame_interval, .get_fmt = gc8034_get_fmt, .set_fmt = gc8034_set_fmt, }; .s_power(),包括power on和power off(上电或者下电)。 //puts subdevice in power saving mode (on == 0) or normal operation mode (on == 1). static int gc8034_s_power(struct v4l2_subdev *sd, int on) { struct gc8034 *gc8034 = to_gc8034(sd); struct i2c_client *client = gc8034->client; int ret = 0; dev_info(&client->dev, "%s(%d) on(%d) ", __func__, __LINE__, on); mutex_lock(&gc8034->mutex); /* If the power state is not modified - no work to do. */ if (gc8034->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; } ret = gc8034_write_array(gc8034->client, gc8034_global_regs); if (ret) { v4l2_err(sd, "could not set init registers "); pm_runtime_put_noidle(&client->dev); goto unlock_and_return; } gc8034->power_on = true; } else { pm_runtime_put(&client->dev); gc8034->power_on = false; } unlock_and_return: mutex_unlock(&gc8034->mutex); return ret; } ioctl //called at the end of ioctl() syscall handler at the V4L2 core. used to provide support for private ioctls used on the driver. static long gc8034_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) { struct gc8034 *gc8034 = to_gc8034(sd); long ret = 0; u32 stream = 0; switch (cmd) { case RKMODULE_GET_MODULE_INFO: gc8034_get_module_inf(gc8034, (struct rkmodule_inf *)arg); break; case RKMODULE_AWB_CFG: gc8034_set_module_inf(gc8034, (struct rkmodule_awb_cfg *)arg); break; case RKMODULE_SET_QUICK_STREAM: stream = *((u32 *)arg); if (stream) { ret = gc8034_write_reg(gc8034->client, GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); if (2 == gc8034->lane_num) { ret |= gc8034_write_reg(gc8034->client, GC8034_REG_CTRL_MODE, 0x91); } else { ret |= gc8034_write_reg(gc8034->client, GC8034_REG_CTRL_MODE, GC8034_MODE_STREAMING); } } else { ret = gc8034_write_reg(gc8034->client, GC8034_REG_SET_PAGE, GC8034_SET_PAGE_ZERO); ret |= gc8034_write_reg(gc8034->client, GC8034_REG_CTRL_MODE, GC8034_MODE_SW_STANDBY); } break; default: ret = -ENOTTY; break; } return ret; } 目前使用了如下的私有 ioctl 实现模组信息的查询和 OTP 信息的查询设置。 .s_stream(),即 set stream。包括 stream on 和 stream off。一般在这里配置寄存器,使其输出图像 //used to notify the driver that a video stream will start or has stopped. static int gc8034_s_stream(struct v4l2_subdev *sd, int on) { struct gc8034 *gc8034 = to_gc8034(sd); struct i2c_client *client = gc8034->client; int ret = 0; dev_info(&client->dev, "%s: on: %d, %dx%d@%d ", __func__, on, gc8034->cur_mode->width, gc8034->cur_mode->height, DIV_ROUND_CLOSEST(gc8034->cur_mode->max_fps.denominator, gc8034->cur_mode->max_fps.numerator)); mutex_lock(&gc8034->mutex); on = !!on; if (on == gc8034->streaming) 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; } ret = __gc8034_start_stream(gc8034); if (ret) { v4l2_err(sd, "start stream failed while write regs "); pm_runtime_put(&client->dev); goto unlock_and_return; } } else { __gc8034_stop_stream(gc8034); pm_runtime_put(&client->dev); } gc8034->streaming = on; unlock_and_return: mutex_unlock(&gc8034->mutex); return ret; } .g_frame_interval , 获取 sensor 输出 fps //callback for VIDIOC_SUBDEV_G_FRAME_INTERVAL() ioctl handler code. static int gc8034_g_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_frame_interval *fi) { struct gc8034 *gc8034 = to_gc8034(sd); const struct gc8034_mode *mode = gc8034->cur_mode; mutex_lock(&gc8034->mutex); fi->interval = mode->max_fps; mutex_unlock(&gc8034->mutex); return 0; } .g_mbus_config ,获取支持的媒体总线配置 //get supported mediabus configurations static int gc8034_g_mbus_config(struct v4l2_subdev *sd, struct v4l2_mbus_config *config) { struct gc8034 *sensor = to_gc8034(sd); struct device *dev = &sensor->client->dev; dev_info(dev, "%s(%d) enter! ", __func__, __LINE__); if (2 == sensor->lane_num) { config->type = V4L2_MBUS_CSI2; config->flags = V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; } else if (4 == sensor->lane_num) { config->type = V4L2_MBUS_CSI2; config->flags = V4L2_MBUS_CSI2_4_LANE | V4L2_MBUS_CSI2_CHANNEL_0 | V4L2_MBUS_CSI2_CONTINUOUS_CLOCK; } else { dev_err(&sensor->client->dev, "unsupported lane_num(%d) ", sensor->lane_num); } return 0; } .enum_mbus_code(),枚举 sensor 输出 bus format。 //callback for VIDIOC_SUBDEV_ENUM_MBUS_CODE() ioctl handler code. static int gc8034_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { if (code->index != 0) return -EINVAL; code->code = GC8034_MEDIA_BUS_FMT; return 0; } .enum_frame_size(),枚举 sensor 支持输出的分辨率大小。 //callback for VIDIOC_SUBDEV_ENUM_FRAME_SIZE() ioctl handler code. static int gc8034_enum_frame_sizes(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_size_enum *fse) { struct gc8034 *gc8034 = to_gc8034(sd); if (fse->index >= gc8034->cfg_num) return -EINVAL; if (fse->code != GC8034_MEDIA_BUS_FMT) return -EINVAL; fse->min_width = supported_modes[fse->index].width; fse->max_width = supported_modes[fse->index].width; fse->max_height = supported_modes[fse->index].height; fse->min_height = supported_modes[fse->index].height; return 0; } .enum_frame_interval, 枚举帧间隔 //allback for VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL() ioctl handler code. static int gc8034_enum_frame_interval(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_frame_interval_enum *fie) { struct gc8034 *gc8034 = to_gc8034(sd); if (fie->index >= gc8034->cfg_num) return -EINVAL; if (fie->code != GC8034_MEDIA_BUS_FMT) return -EINVAL; fie->width = supported_modes[fie->index].width; fie->height = supported_modes[fie->index].height; fie->interval = supported_modes[fie->index].max_fps; return 0; } .get_fmt(),获取 sensor 输出格式。如果.get_fmt() 缺失,media-ctl 工具无法查看 sensor entity 当前配置的format //callback for VIDIOC_SUBDEV_G_FMT() ioctl handler code. static int gc8034_get_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct gc8034 *gc8034 = to_gc8034(sd); const struct gc8034_mode *mode = gc8034->cur_mode; mutex_lock(&gc8034->mutex); if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API fmt->format = *v4l2_subdev_get_try_format(sd, cfg, fmt->pad); #else mutex_unlock(&gc8034->mutex); return -ENOTTY; #endif } else { fmt->format.width = mode->width; fmt->format.height = mode->height; fmt->format.code = GC8034_MEDIA_BUS_FMT; fmt->format.field = V4L2_FIELD_NONE; } mutex_unlock(&gc8034->mutex); return 0; } .set_fmt(),设置 sensor 输出格式 //callback for VIDIOC_SUBDEV_S_FMT() ioctl handler code. static int gc8034_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *fmt) { struct gc8034 *gc8034 = to_gc8034(sd); const struct gc8034_mode *mode; s64 h_blank, vblank_def; mutex_lock(&gc8034->mutex); mode = gc8034_find_best_fit(gc8034, fmt); fmt->format.code = GC8034_MEDIA_BUS_FMT; fmt->format.width = mode->width; fmt->format.height = mode->height; fmt->format.field = V4L2_FIELD_NONE; if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; #else mutex_unlock(&gc8034->mutex); return -ENOTTY; #endif } else { gc8034->cur_mode = mode; h_blank = mode->hts_def - mode->width; __v4l2_ctrl_modify_range(gc8034->hblank, h_blank, h_blank, 1, h_blank); vblank_def = mode->vts_def - mode->height; __v4l2_ctrl_modify_range(gc8034->vblank, vblank_def, GC8034_VTS_MAX - mode->height, 1, vblank_def); __v4l2_ctrl_s_ctrl(gc8034->link_freq, link_freq_menu_items[0]); } mutex_unlock(&gc8034->mutex); return 0; } .open(),Userspace通过在打开/dev/v4l-subdev节点时,会调用到该.open()函数。在上层需要单独对 sensor 设置 control 时.open()是必须实现的 //called when the subdev device node is opened by an application. #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API static int gc8034_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) { struct gc8034 *gc8034 = to_gc8034(sd); struct v4l2_mbus_framefmt *try_fmt = v4l2_subdev_get_try_format(sd, fh->pad, 0); const struct gc8034_mode *def_mode = &supported_modes[0]; mutex_lock(&gc8034->mutex); /* Initialize try_fmt */ try_fmt->width = def_mode->width; try_fmt->height = def_mode->height; try_fmt->code = GC8034_MEDIA_BUS_FMT; try_fmt->field = V4L2_FIELD_NONE; mutex_unlock(&gc8034->mutex); /* No crop or compose */ return 0; } #endif 原作者:悲伤的小强 |
|
相关推荐
1个回答
|
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
431 浏览 0 评论
524 浏览 1 评论
1789 浏览 1 评论
3043 浏览 1 评论
synopsys 的design ware:DW_fpv_div,浮点数除法器,默认32位下,想提升覆盖率(TMAX),如果用功能case去提升覆盖率呢?
3771 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-19 06:33 , Processed in 0.511643 second(s), Total 42, Slave 36 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号