Commit ec1e620b authored by Faizel K B's avatar Faizel K B Committed by Hans Verkuil
Browse files

media: vimc: sensor: Add pixel_rate,vblank and hblank configuration



pixel_rate and hblank as read only parameter. vblank can be configured
to match the desired frame rate.

Default values are, pixel_rate - 160 MHz, hblank - 800.
vblank defaults to an equivalent value of 30 fps for resolutions less than
or equal to 1920x1080 and 10 fps for higher resolutions. For higher
resolutions, modify pixel_rate in the driver code.
fps = pixel_rate / ((width + hblank) * (height + vblank))
minimum vblank - 4, maximum vblank - 65535

The configured fps delay is pre-calculated into jiffies and
stored in the sensor's hw structure for efficient access by the
streamer thread.

Signed-off-by: default avatarFaizel K B <faizel.kb@gmail.com>
Signed-off-by: default avatarHans Verkuil <hverkuil+cisco@kernel.org>
parent 12cdc242
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -29,6 +29,15 @@
#define VIMC_FRAME_MIN_WIDTH 16
#define VIMC_FRAME_MIN_HEIGHT 16

#define VIMC_PIXEL_RATE_FIXED		160000000	/* 160 MHz */
#define VIMC_HBLANK_FIXED		800
/* VBLANK - vertical blanking (primary FPS control) */
#define VIMC_VBLANK_MIN			4
#define VIMC_VBLANK_MAX			65535
#define VIMC_VBLANK_STEP		1
#define VIMC_VBLANK_DEFAULT	        3223           /* 30fps vga */
#define VIMC_PIXELS_THRESHOLD_30FPS	(1920 * 1080) /* 2073600 pixels */

#define VIMC_FRAME_INDEX(lin, col, width, bpp) ((lin * width + col) * bpp)

/* Source and sink pad checks */
@@ -173,6 +182,9 @@ struct vimc_sensor_device {
	struct tpg_data tpg;
	struct v4l2_ctrl_handler hdl;
	struct media_pad pad;
	struct v4l2_ctrl *pixel_rate;
	struct v4l2_ctrl *hblank;
	struct v4l2_ctrl *vblank;

	u8 *frame;

@@ -184,6 +196,7 @@ struct vimc_sensor_device {
		struct v4l2_area size;
		enum vimc_sensor_osd_mode osd_value;
		u64 start_stream_ts;
		unsigned long fps_jiffies;
	} hw;
};

+88 −0
Original line number Diff line number Diff line
@@ -25,10 +25,15 @@ static const struct v4l2_mbus_framefmt fmt_default = {
static int vimc_sensor_init_state(struct v4l2_subdev *sd,
				  struct v4l2_subdev_state *sd_state)
{
	struct vimc_sensor_device *vsensor =
		container_of(sd, struct vimc_sensor_device, sd);

	struct v4l2_mbus_framefmt *mf;

	mf = v4l2_subdev_state_get_format(sd_state, 0);
	*mf = fmt_default;
	vsensor->hw.size.width = fmt_default.width;
	vsensor->hw.size.height = fmt_default.height;

	return 0;
}
@@ -87,6 +92,26 @@ static void vimc_sensor_tpg_s_format(struct vimc_sensor_device *vsensor,
	tpg_s_xfer_func(&vsensor->tpg, format->xfer_func);
}

static int vimc_sensor_update_frame_timing(struct v4l2_subdev *sd,
					   u32 width, u32 height)
{
	struct vimc_sensor_device *vsensor =
		container_of(sd, struct vimc_sensor_device, sd);
	u64 pixel_rate = vsensor->pixel_rate->val;
	u32 hts = width + vsensor->hblank->val;
	u32 vts = height + vsensor->vblank->val;
	u64 total_pixels = (u64)hts * vts;
	u64 frame_interval_ns;

	frame_interval_ns = total_pixels * NSEC_PER_SEC;
	do_div(frame_interval_ns, pixel_rate);
	vsensor->hw.fps_jiffies = nsecs_to_jiffies(frame_interval_ns);
	if (vsensor->hw.fps_jiffies == 0)
		vsensor->hw.fps_jiffies = 1;

	return 0;
}

static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
{
	const struct vimc_pix_map *vpix;
@@ -108,6 +133,24 @@ static void vimc_sensor_adjust_fmt(struct v4l2_mbus_framefmt *fmt)
	vimc_colorimetry_clamp(fmt);
}

static u32 vimc_calc_vblank(u32 width, u32 height,
			    s64 pixel_rate, s32 hblank)
{
	u32 hts = width + hblank;
	u32 target_fps;
	u64 vts;

	target_fps = (width * height <= VIMC_PIXELS_THRESHOLD_30FPS) ? 30 : 10;

	vts = (u64)pixel_rate;
	do_div(vts, target_fps * hts);

	if (vts > height)
		return clamp((u32)(vts - height), VIMC_VBLANK_MIN, VIMC_VBLANK_MAX);

	return VIMC_VBLANK_MIN;
}

static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
			       struct v4l2_subdev_state *sd_state,
			       struct v4l2_subdev_format *fmt)
@@ -137,6 +180,20 @@ static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
		fmt->format.xfer_func, fmt->format.ycbcr_enc);

	*mf = fmt->format;
	if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
		u32 vblank_def = vimc_calc_vblank(fmt->format.width,
						  fmt->format.height,
						  vsensor->pixel_rate->val,
						  vsensor->hblank->val);
		vsensor->hw.size.width = fmt->format.width;
		vsensor->hw.size.height = fmt->format.height;
		__v4l2_ctrl_modify_range(vsensor->vblank,
					 VIMC_VBLANK_MIN,
					 VIMC_VBLANK_MAX,
					 VIMC_VBLANK_STEP,
					 vblank_def);
		__v4l2_ctrl_s_ctrl(vsensor->vblank, vblank_def);
	}

	return 0;
}
@@ -222,6 +279,8 @@ static int vimc_sensor_s_stream(struct v4l2_subdev *sd, int enable)

		vsensor->hw.size.width = format->width;
		vsensor->hw.size.height = format->height;
		vimc_sensor_update_frame_timing(sd, format->width,
						format->height);

		v4l2_subdev_unlock_state(state);

@@ -293,6 +352,15 @@ static int vimc_sensor_s_ctrl(struct v4l2_ctrl *ctrl)
	case VIMC_CID_OSD_TEXT_MODE:
		vsensor->hw.osd_value = ctrl->val;
		break;
	case V4L2_CID_PIXEL_RATE:
		break;
	case V4L2_CID_HBLANK:
		break;
	case V4L2_CID_VBLANK:
		vimc_sensor_update_frame_timing(&vsensor->sd,
						vsensor->hw.size.width,
						vsensor->hw.size.height);
		break;
	default:
		return -EINVAL;
	}
@@ -377,6 +445,26 @@ static struct vimc_ent_device *vimc_sensor_add(struct vimc_device *vimc,
			  V4L2_CID_HUE, -128, 127, 1, 0);
	v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
			  V4L2_CID_SATURATION, 0, 255, 1, 128);
	/* Timing controls for frame interval configuration */
	vsensor->pixel_rate = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
						V4L2_CID_PIXEL_RATE,
						VIMC_PIXEL_RATE_FIXED, VIMC_PIXEL_RATE_FIXED,
						1, VIMC_PIXEL_RATE_FIXED);
	if (vsensor->pixel_rate)
		vsensor->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY;

	vsensor->hblank = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
					    V4L2_CID_HBLANK,
					    VIMC_HBLANK_FIXED, VIMC_HBLANK_FIXED,
					    1, VIMC_HBLANK_FIXED);
	if (vsensor->hblank)
		vsensor->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY;

	vsensor->vblank = v4l2_ctrl_new_std(&vsensor->hdl, &vimc_sensor_ctrl_ops,
					    V4L2_CID_VBLANK,
					    VIMC_VBLANK_MIN, VIMC_VBLANK_MAX,
					    VIMC_VBLANK_STEP, VIMC_VBLANK_DEFAULT);

	vsensor->sd.ctrl_handler = &vsensor->hdl;
	if (vsensor->hdl.error) {
		ret = vsensor->hdl.error;