From 30902a052277cce4601eef4c2e39bcec37e22053 Mon Sep 17 00:00:00 2001 From: "Guoniu.Zhou" Date: Tue, 30 Oct 2018 14:58:48 +0800 Subject: [PATCH] MLK-20123-1: camera: add automatic selection mode for mipi ov5640 There is two methods to select different mode for camera sensor, one is setting mode by VIDIOC_S_PARM ioctl and the other is automatic selection through resolution. If resolution match one of camera sensor supported, driver will select the corresponding mode. If not, driver will select the max resolution supported by sensor and use ISI to resize to the target resolution. This patch is for mipi interface of ov5640 sensor Signed-off-by: Guoniu.Zhou (cherry picked from commit 4c560890906f7df33e68a65372f60778cb1406b7) --- drivers/media/platform/imx8/ov5640_mipi_v3.c | 171 ++++++++++++++----- 1 file changed, 129 insertions(+), 42 deletions(-) diff --git a/drivers/media/platform/imx8/ov5640_mipi_v3.c b/drivers/media/platform/imx8/ov5640_mipi_v3.c index 7aed344bf7d7..a3a2d2e198e4 100644 --- a/drivers/media/platform/imx8/ov5640_mipi_v3.c +++ b/drivers/media/platform/imx8/ov5640_mipi_v3.c @@ -44,12 +44,12 @@ enum ov5640_mode { ov5640_mode_MIN = 0, - ov5640_mode_VGA_640_480 = 0, - ov5640_mode_NTSC_720_480 = 1, - ov5640_mode_720P_1280_720 = 2, - ov5640_mode_1080P_1920_1080 = 3, + ov5640_mode_1080P_1920_1080 = 0, + ov5640_mode_720P_1280_720 = 1, + ov5640_mode_NTSC_720_480 = 2, + ov5640_mode_VGA_640_480 = 3, ov5640_mode_QSXGA_2592_1944 = 4, - ov5640_mode_MAX = 5, + ov5640_mode_MAX = 4, ov5640_mode_INIT = 0xff, /*only for sensor init*/ }; @@ -355,27 +355,27 @@ static struct reg_value ov5640_setting_15fps_QSXGA_2592_1944[] = { static struct ov5640_mode_info ov5640_mode_info_data[2][ov5640_mode_MAX + 1] = { { - {ov5640_mode_VGA_640_480, -1, 0, 0, NULL, 0}, - {ov5640_mode_NTSC_720_480, -1, 0, 0, NULL, 0}, - {ov5640_mode_720P_1280_720, -1, 0, 0, NULL, 0}, {ov5640_mode_1080P_1920_1080, -1, 0, 0, NULL, 0}, + {ov5640_mode_720P_1280_720, -1, 0, 0, NULL, 0}, + {ov5640_mode_NTSC_720_480, -1, 0, 0, NULL, 0}, + {ov5640_mode_VGA_640_480, -1, 0, 0, NULL, 0}, {ov5640_mode_QSXGA_2592_1944, SCALING, 2592, 1944, ov5640_setting_15fps_QSXGA_2592_1944, ARRAY_SIZE(ov5640_setting_15fps_QSXGA_2592_1944)}, }, { - {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480, - ov5640_setting_30fps_VGA_640_480, - ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, - {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480, - ov5640_setting_30fps_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, - {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720, - ov5640_setting_30fps_720P_1280_720, - ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, {ov5640_mode_1080P_1920_1080, SCALING, 1920, 1080, ov5640_setting_30fps_1080P_1920_1080, ARRAY_SIZE(ov5640_setting_30fps_1080P_1920_1080)}, + {ov5640_mode_720P_1280_720, SUBSAMPLING, 1280, 720, + ov5640_setting_30fps_720P_1280_720, + ARRAY_SIZE(ov5640_setting_30fps_720P_1280_720)}, + {ov5640_mode_NTSC_720_480, SUBSAMPLING, 720, 480, + ov5640_setting_30fps_NTSC_720_480, + ARRAY_SIZE(ov5640_setting_30fps_NTSC_720_480)}, + {ov5640_mode_VGA_640_480, SUBSAMPLING, 640, 480, + ov5640_setting_30fps_VGA_640_480, + ARRAY_SIZE(ov5640_setting_30fps_VGA_640_480)}, {ov5640_mode_QSXGA_2592_1944, -1, 0, 0, NULL, 0}, }, }; @@ -438,6 +438,23 @@ static struct ov5640 *to_ov5640(const struct i2c_client *client) return container_of(i2c_get_clientdata(client), struct ov5640, subdev); } +static enum ov5640_frame_rate to_ov5640_frame_rate(struct v4l2_fract *timeperframe) +{ + enum ov5640_frame_rate rate; + u32 tgt_fps; /* target frames per secound */ + + tgt_fps = timeperframe->denominator / timeperframe->numerator; + + if (tgt_fps == 30) + rate = ov5640_30_fps; + else if (tgt_fps == 15) + rate = ov5640_15_fps; + else + rate = -EINVAL; + + return rate; +} + static uint16_t find_hs_configure(struct ov5640 *sensor) { struct device *dev = &sensor->i2c_client->dev; @@ -683,10 +700,12 @@ static void ov5640_start(struct ov5640 *sensor) udelay(1000); } -static int ov5640_change_mode(struct ov5640 *sensor, - enum ov5640_frame_rate frame_rate, enum ov5640_mode mode) +static int ov5640_change_mode(struct ov5640 *sensor) { struct reg_value *pModeSetting = NULL; + enum ov5640_mode mode = sensor->streamcap.capturemode; + enum ov5640_frame_rate frame_rate = + to_ov5640_frame_rate(&sensor->streamcap.timeperframe); int ArySize = 0, retval = 0; if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) { @@ -813,10 +832,10 @@ static int ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) struct ov5640 *sensor = to_ov5640(client); struct v4l2_fract *timeperframe = &a->parm.capture.timeperframe; u32 tgt_fps; /* target frames per secound */ - enum ov5640_frame_rate frame_rate; enum ov5640_mode mode = a->parm.capture.capturemode; int ret = 0; + switch (a->type) { /* This is the only case currently handled. */ case V4L2_BUF_TYPE_VIDEO_CAPTURE: @@ -839,28 +858,13 @@ static int ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) timeperframe->numerator = 1; } - /* Actual frame rate we use */ - tgt_fps = timeperframe->denominator / - timeperframe->numerator; - - if (tgt_fps == 30) - frame_rate = ov5640_30_fps; - else if (tgt_fps == 15) - frame_rate = ov5640_15_fps; - else { - pr_err(" The camera frame rate is not supported!\n"); - goto error; + if (mode > ov5640_mode_MAX || mode < ov5640_mode_MIN) { + pr_err("The camera mode[%d] is not supported!\n", mode); + return -EINVAL; } - ret = ov5640_change_mode(sensor, frame_rate, mode); - if (ret < 0) - goto error; - - sensor->streamcap.timeperframe = *timeperframe; sensor->streamcap.capturemode = mode; - sensor->pix.width = ov5640_mode_info_data[frame_rate][mode].width; - sensor->pix.height = ov5640_mode_info_data[frame_rate][mode].height; - + sensor->streamcap.timeperframe = *timeperframe; break; /* These are all the possible cases. */ @@ -881,7 +885,6 @@ static int ov5640_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a) break; } -error: return ret; } @@ -899,6 +902,88 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) return 0; } +static struct ov5640_mode_info *get_max_resolution(enum ov5640_frame_rate rate) +{ + u32 max_width; + enum ov5640_mode mode; + int i; + + mode = 0; + max_width = ov5640_mode_info_data[rate][0].width; + + for (i = 0; i < (ov5640_mode_MAX + 1); i++) { + if (ov5640_mode_info_data[rate][i].width > max_width) { + max_width = ov5640_mode_info_data[rate][i].width; + mode = i; + } + } + return &ov5640_mode_info_data[rate][mode]; +} + +static struct ov5640_mode_info *match(struct v4l2_mbus_framefmt *fmt, + enum ov5640_frame_rate rate) +{ + struct ov5640_mode_info *info; + int i; + + for (i = 0; i < (ov5640_mode_MAX + 1); i++) { + if (fmt->width == ov5640_mode_info_data[rate][i].width && + fmt->height == ov5640_mode_info_data[rate][i].height) { + info = &ov5640_mode_info_data[rate][i]; + break; + } + } + if (i == ov5640_mode_MAX + 1) + info = NULL; + + return info; +} + +static void try_to_find_resolution(struct ov5640 *sensor, + struct v4l2_mbus_framefmt *mf) +{ + enum ov5640_mode mode = sensor->streamcap.capturemode; + struct v4l2_fract *timeperframe = &sensor->streamcap.timeperframe; + enum ov5640_frame_rate frame_rate = to_ov5640_frame_rate(timeperframe); + struct device *dev = &sensor->i2c_client->dev; + struct ov5640_mode_info *info; + bool found = false; + + if ((mf->width == ov5640_mode_info_data[frame_rate][mode].width) && + (mf->height == ov5640_mode_info_data[frame_rate][mode].height)) { + info = &ov5640_mode_info_data[frame_rate][mode]; + found = true; + } else { + /* get mode info according to frame user's width and height */ + info = match(mf, frame_rate); + if (info == NULL) { + frame_rate ^= 0x1; + info = match(mf, frame_rate); + if (info) { + sensor->streamcap.capturemode = -1; + dev_err(dev, "%s %dx%d only support %s(fps)\n", __func__, + info->width, info->height, + (frame_rate == 0) ? "15fps" : "30fps"); + return; + } + goto max_resolution; + } + found = true; + } + + /* get max resolution to resize */ +max_resolution: + if (!found) { + frame_rate ^= 0x1; + info = get_max_resolution(frame_rate); + } + + sensor->streamcap.capturemode = info->mode; + sensor->streamcap.timeperframe.denominator = (frame_rate) ? 30 : 15; + sensor->pix.width = info->width; + sensor->pix.height = info->height; +} + static int ov5640_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_format *format) @@ -907,6 +992,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, const struct ov5640_datafmt *fmt = ov5640_find_datafmt(mf->code); struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5640 *sensor = to_ov5640(client); + int ret; if (format->pad) return -EINVAL; @@ -917,13 +1003,15 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, } mf->field = V4L2_FIELD_NONE; + try_to_find_resolution(sensor, mf); if (format->which == V4L2_SUBDEV_FORMAT_TRY) return 0; + ret = ov5640_change_mode(sensor); sensor->fmt = fmt; - return 0; + return ret; } static int ov5640_get_fmt(struct v4l2_subdev *sd, @@ -933,7 +1021,6 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *mf = &format->format; struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5640 *sensor = to_ov5640(client); - /*const struct ov5640_datafmt *fmt = sensor->fmt;*/ if (format->pad) return -EINVAL; -- 2.17.1