/* Frame height */
unsigned int height;
+ unsigned int fps;
+
/* Analog crop rectangle. */
struct v4l2_rect crop;
/* 8MPix 15fps mode */
.width = 3280,
.height = 2464,
+ .fps = 15,
.crop = {
.left = IMX219_PIXEL_ARRAY_LEFT,
.top = IMX219_PIXEL_ARRAY_TOP,
/* 1080P 30fps cropped */
.width = 1920,
.height = 1080,
+ .fps = 30,
.crop = {
.left = 688,
.top = 700,
/* 2x2 binned 30fps mode */
.width = 1640,
.height = 1232,
+ .fps = 30,
.crop = {
.left = IMX219_PIXEL_ARRAY_LEFT,
.top = IMX219_PIXEL_ARRAY_TOP,
/* 640x480 30fps mode */
.width = 640,
.height = 480,
+ .fps = 30,
.crop = {
.left = 1008,
.top = 760,
},
},
};
-#define MODE_COUNT_MAX ARRAY_SIZE(supported_modes)
-static int imx219_fps[MODE_COUNT_MAX] = {15, 30, 30, 30};
struct imx219 {
struct v4l2_subdev sd;
/* Current mode */
const struct imx219_mode *mode;
+ struct v4l2_fract frame_interval;
/*
* Mutex for serialized access:
return 0;
}
+
+static int imx219_try_frame_interval(struct imx219 *imx219,
+ struct v4l2_fract *fi,
+ u32 w, u32 h)
+{
+ const struct imx219_mode *mode;
+
+ mode = v4l2_find_nearest_size(supported_modes, ARRAY_SIZE(supported_modes),
+ width, height, w, h);
+ if (!mode || (mode->width != w || mode->height != h))
+ return -EINVAL;
+
+ fi->numerator = 1;
+ fi->denominator = mode->fps;
+
+ return mode->fps;
+}
+
static int imx219_enum_frame_interval(struct v4l2_subdev *sd,
struct v4l2_subdev_state *state,
struct v4l2_subdev_frame_interval_enum *fie)
{
struct imx219 *imx219 = to_imx219(sd);
+ struct v4l2_fract tpf;
u32 code;
- int i = 0;
+ int ret;
if (fie->index >= ARRAY_SIZE(supported_modes) || fie->index)
return -EINVAL;
mutex_lock(&imx219->mutex);
code = imx219_get_format_code(imx219, fie->code);
+ if (fie->code != code) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = imx219_try_frame_interval(imx219, &tpf,
+ fie->width, fie->height);
+ if (ret < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ mutex_unlock(&imx219->mutex);
+ fie->interval = tpf;
+
+ return 0;
+
+out:
+ mutex_unlock(&imx219->mutex);
+ return ret;
+}
+
+static int imx219_g_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct imx219 *imx219 = to_imx219(sd);
+
+ mutex_lock(&imx219->mutex);
+ fi->interval = imx219->frame_interval;
mutex_unlock(&imx219->mutex);
- if (fie->code != code)
+
+ return 0;
+}
+
+static int imx219_s_frame_interval(struct v4l2_subdev *sd,
+ struct v4l2_subdev_frame_interval *fi)
+{
+ struct imx219 *imx219 = to_imx219(sd);
+ const struct imx219_mode *mode = imx219->mode;
+ int frame_rate, ret = 0;
+
+ if (fi->pad != 0)
return -EINVAL;
- pr_debug("fie->width = %d, fie->height = %d\n", fie->width, fie->height);
- for (i = 0; i < MODE_COUNT_MAX; i++) {
- if (fie->width == supported_modes[i].width &&
- fie->height == supported_modes[i].height)
- break;
+ mutex_lock(&imx219->mutex);
+
+ if (imx219->streaming) {
+ ret = -EBUSY;
+ goto out;
}
- if (i == MODE_COUNT_MAX)
- return -ENOTTY;
- fie->interval.denominator = imx219_fps[i];
- fie->interval.numerator = 1;
- fie->code = code;
- fie->width = supported_modes[i].width;
- fie->height = supported_modes[i].height;
+ frame_rate = imx219_try_frame_interval(imx219, &fi->interval,
+ mode->width, mode->height);
+ if (frame_rate < 0) {
+ ret = -EINVAL;
+ goto out;
+ }
- return 0;
+ imx219->frame_interval = fi->interval;
+
+out:
+ mutex_unlock(&imx219->mutex);
+ return ret;
}
static void imx219_reset_colorspace(struct v4l2_mbus_framefmt *fmt)
};
static const struct v4l2_subdev_video_ops imx219_video_ops = {
+ .g_frame_interval = imx219_g_frame_interval,
+ .s_frame_interval = imx219_s_frame_interval,
.s_stream = imx219_set_stream,
};
/* Set default mode to max resolution */
imx219->mode = &supported_modes[0];
+ imx219->frame_interval.numerator = 1;
+ imx219->frame_interval.denominator = supported_modes[0].fps;
/* sensor doesn't enter LP-11 state upon power up until and unless
* streaming is started, so upon power up switch the modes to:
struct media_entity *entity = &vdev->entity;
struct media_entity *sensor;
struct v4l2_subdev *subdev;
+ struct media_pad *pad;
+ bool support_selection = false;
int i;
int ret;
if (i == video->nformats)
return -EINVAL;
- sensor = stfcamss_find_sensor(entity);
- if (sensor) {
+ entity = &vdev->entity;
+ while (1) {
+ pad = &entity->pads[0];
+ if (!(pad->flags & MEDIA_PAD_FL_SINK))
+ break;
+
+ pad = media_entity_remote_pad(pad);
+ if (!pad || !is_media_entity_v4l2_subdev(pad->entity))
+ break;
+
+ entity = pad->entity;
+ subdev = media_entity_to_v4l2_subdev(entity);
+
+ if (subdev->ops->pad->set_selection) {
+ support_selection = true;
+ break;
+ }
+ }
+
+ if (support_selection) {
+ if (fsize->index)
+ return -ENOTTY;
+ fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
+ fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH;
+ fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH;
+ fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT;
+ fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT;
+ fsize->stepwise.step_width = 1;
+ fsize->stepwise.step_height = 1;
+ } else {
+ entity = &vdev->entity;
+ sensor = stfcamss_find_sensor(entity);
+ if (!sensor)
+ return -ENOTTY;
+
subdev = media_entity_to_v4l2_subdev(sensor);
code.index = 0;
code.which = V4L2_SUBDEV_FORMAT_ACTIVE;
fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
fsize->discrete.width = fse.min_width;
fsize->discrete.height = fse.min_height;
- } else {
- if (fsize->index)
- return -EINVAL;
- fsize->type = V4L2_FRMSIZE_TYPE_CONTINUOUS;
- fsize->stepwise.min_width = STFCAMSS_FRAME_MIN_WIDTH;
- fsize->stepwise.max_width = STFCAMSS_FRAME_MAX_WIDTH;
- fsize->stepwise.min_height = STFCAMSS_FRAME_MIN_HEIGHT;
- fsize->stepwise.max_height = STFCAMSS_FRAME_MAX_HEIGHT;
- fsize->stepwise.step_width = 1;
- fsize->stepwise.step_height = 1;
}
return 0;
}
+
static int video_enum_frameintervals(struct file *file, void *fh,
struct v4l2_frmivalenum *fival)
{