RK3568 Camera 使用
RK3568 Sensor驱动开发移植(1)
RK3568 Sensor驱动开发移植(2)
RK3568 Sensor驱动开发移植(3)

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)\n", __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\n");
			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\n", __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\n");
			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!\n", __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)\n", 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
Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐