@@ -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;
};
@@ -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,18 @@ 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;
+ u32 vblank_def;
+
+ target_fps = (width * height <= VIMC_PIXELS_THRESHOLD_30FPS) ? 30 : 10;
+ vblank_def = (pixel_rate / (target_fps * hts)) - height;
+ return clamp(vblank_def, VIMC_VBLANK_MIN, VIMC_VBLANK_MAX);
+}
+
static int vimc_sensor_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_state *sd_state,
struct v4l2_subdev_format *fmt)
@@ -137,6 +174,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 +273,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 +346,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 +439,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;
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: Faizel K B <faizel.kb@gmail.com> --- drivers/media/test-drivers/vimc/vimc-common.h | 13 +++ drivers/media/test-drivers/vimc/vimc-sensor.c | 82 +++++++++++++++++++ 2 files changed, 95 insertions(+)