From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001 From: David Plowman Date: Wed, 29 Jan 2020 15:30:53 +0000 Subject: [PATCH] media: ov5647: Add basic support for multiple sensor modes. Specifically: Added a structure ov5647_mode and a list of supported_modes (though no actual new modes as yet). The state object points to the "current mode". ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and ov5647_get_fmt all needed upgrading to cope with multiple modes. __sensor_init (which writes all the registers) is now called by ov5647_stream_on (once the mode is known) rather than by ov5647_sensor_power. Signed-off-by: David Plowman Signed-off-by: Naushir Patuck --- drivers/media/i2c/ov5647.c | 268 ++++++++++++++++++++++++++++--------- 1 file changed, 202 insertions(+), 66 deletions(-) --- a/drivers/media/i2c/ov5647.c +++ b/drivers/media/i2c/ov5647.c @@ -86,13 +86,17 @@ struct regval_list { u8 data; }; +struct ov5647_mode { + struct v4l2_mbus_framefmt format; + struct regval_list *reg_list; + unsigned int num_regs; +}; + struct ov5647 { struct v4l2_subdev sd; struct media_pad pad; struct mutex lock; - struct v4l2_mbus_framefmt format; - unsigned int width; - unsigned int height; + const struct ov5647_mode *mode; int power_count; struct clk *xclk; struct gpio_desc *pwdn; @@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480 {0x0100, 0x01}, }; +static struct ov5647_mode supported_modes_8bit[] = { + /* + * Original 8-bit VGA mode + * Uncentred crop (top left quarter) from 2x2 binned 1296x972 image. + */ + { + { + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .field = V4L2_FIELD_NONE, + .width = 640, + .height = 480 + }, + ov5647_640x480, + ARRAY_SIZE(ov5647_640x480) + }, + /* more modes below here... */ +}; + +static struct ov5647_mode supported_modes_10bit[] = { + /* no 10-bit modes yet */ +}; + +/* Use original 8-bit VGA mode as default. */ +#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0]) + static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) { int ret; @@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(st return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6)); } +static int __sensor_init(struct v4l2_subdev *sd) +{ + int ret; + u8 resetval, rdval; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov5647 *state = to_state(sd); + + ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); + if (ret < 0) + return ret; + + ret = ov5647_write_array(sd, state->mode->reg_list, + state->mode->num_regs); + if (ret < 0) { + dev_err(&client->dev, "write sensor default regs error\n"); + return ret; + } + + ret = ov5647_set_virtual_channel(sd, 0); + if (ret < 0) + return ret; + + ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); + if (ret < 0) + return ret; + + if (!(resetval & 0x01)) { + dev_err(&client->dev, "Device was in SW standby"); + ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); + if (ret < 0) + return ret; + } + + return 0; +} + static int ov5647_stream_on(struct v4l2_subdev *sd) { + struct i2c_client *client = v4l2_get_subdevdata(sd); struct ov5647 *ov5647 = to_state(sd); u8 val = MIPI_CTRL00_BUS_IDLE; int ret; + ret = __sensor_init(sd); + if (ret < 0) { + dev_err(&client->dev, "sensor_init failed\n"); + return ret; + } + if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) val |= MIPI_CTRL00_CLOCK_LANE_GATE | MIPI_CTRL00_LINE_SYNC_ENABLE; @@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_su return ov5647_write(sd, OV5647_SW_STANDBY, rdval); } -static int __sensor_init(struct v4l2_subdev *sd) -{ - int ret; - u8 resetval, rdval; - struct i2c_client *client = v4l2_get_subdevdata(sd); - - ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval); - if (ret < 0) - return ret; - - ret = ov5647_write_array(sd, ov5647_640x480, - ARRAY_SIZE(ov5647_640x480)); - if (ret < 0) { - dev_err(&client->dev, "write sensor default regs error\n"); - return ret; - } - - ret = ov5647_set_virtual_channel(sd, 0); - if (ret < 0) - return ret; - - ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval); - if (ret < 0) - return ret; - - if (!(resetval & 0x01)) { - dev_err(&client->dev, "Device was in SW standby"); - ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01); - if (ret < 0) - return ret; - } - - /* - * stream off to make the clock lane into LP-11 state. - */ - return ov5647_stream_off(sd); -} - static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) { int ret = 0; @@ -408,7 +443,7 @@ static int ov5647_sensor_power(struct v4 } ret = ov5647_write_array(sd, sensor_oe_enable_regs, - ARRAY_SIZE(sensor_oe_enable_regs)); + ARRAY_SIZE(sensor_oe_enable_regs)); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); dev_err(&client->dev, @@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4 goto out; } - ret = __sensor_init(sd); + /* + * Ensure streaming off to make clock lane go into LP-11 state. + */ + ret = ov5647_stream_off(sd); if (ret < 0) { clk_disable_unprepare(ov5647->xclk); dev_err(&client->dev, @@ -427,7 +465,7 @@ static int ov5647_sensor_power(struct v4 dev_dbg(&client->dev, "OV5647 power off\n"); ret = ov5647_write_array(sd, sensor_oe_disable_regs, - ARRAY_SIZE(sensor_oe_disable_regs)); + ARRAY_SIZE(sensor_oe_disable_regs)); if (ret < 0) dev_dbg(&client->dev, "disable oe failed\n"); @@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) { + struct ov5647 *state = to_state(sd); + int ret = 0; + + mutex_lock(&state->lock); + if (enable) - return ov5647_stream_on(sd); + ret = ov5647_stream_on(sd); else - return ov5647_stream_off(sd); + ret = ov5647_stream_off(sd); + + mutex_unlock(&state->lock); + + return ret; } static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { @@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_mbus_code_enum *code) { - if (code->index > 0) + if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit)) + code->code = MEDIA_BUS_FMT_SBGGR8_1X8; + else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 && + ARRAY_SIZE(supported_modes_10bit)) + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) && + ARRAY_SIZE(supported_modes_10bit)) + code->code = MEDIA_BUS_FMT_SBGGR10_1X10; + else return -EINVAL; - code->code = MEDIA_BUS_FMT_SBGGR8_1X8; + return 0; +} + +static int ov5647_enum_frame_size(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_frame_size_enum *fse) +{ + struct ov5647_mode *mode = NULL; + + if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) { + if (fse->index >= ARRAY_SIZE(supported_modes_8bit)) + return -EINVAL; + mode = &supported_modes_8bit[fse->index]; + } else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) { + if (fse->index >= ARRAY_SIZE(supported_modes_10bit)) + return -EINVAL; + mode = &supported_modes_10bit[fse->index]; + } else { + return -EINVAL; + } + + fse->min_width = mode->format.width; + fse->max_width = fse->min_width; + fse->min_height = mode->format.height; + fse->max_height = fse->min_height; + + return 0; +} + +static int ov5647_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) +{ + struct v4l2_mbus_framefmt *fmt = &format->format; + struct ov5647 *state = to_state(sd); + struct v4l2_mbus_framefmt *framefmt; + const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL; + + if (format->pad != 0) + return -EINVAL; + + mutex_lock(&state->lock); + + /* + * Try to respect any given pixel format, otherwise try for a 10-bit + * mode. + */ + mode_8bit = v4l2_find_nearest_size(supported_modes_8bit, + ARRAY_SIZE(supported_modes_8bit), + format.width, format.height, + format->format.width, + format->format.height); + mode_10bit = v4l2_find_nearest_size(supported_modes_10bit, + ARRAY_SIZE(supported_modes_10bit), + format.width, format.height, + format->format.width, + format->format.height); + if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit) + mode = mode_8bit; + else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 && + mode_10bit) + mode = mode_10bit; + else if (mode_10bit) + mode = mode_10bit; + else + mode = mode_8bit; + + if (!mode) + return -EINVAL; + + *fmt = mode->format; + if (format->which == V4L2_SUBDEV_FORMAT_TRY) { + framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad); + *framefmt = format->format; + } else { + state->mode = mode; + } + + mutex_unlock(&state->lock); return 0; } -static int ov5647_set_get_fmt(struct v4l2_subdev *sd, - struct v4l2_subdev_pad_config *cfg, - struct v4l2_subdev_format *format) +static int ov5647_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_pad_config *cfg, + struct v4l2_subdev_format *format) { struct v4l2_mbus_framefmt *fmt = &format->format; + struct ov5647 *state = to_state(sd); if (format->pad != 0) return -EINVAL; - /* Only one format is supported, so return that */ - memset(fmt, 0, sizeof(*fmt)); - fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->field = V4L2_FIELD_NONE; - fmt->width = 640; - fmt->height = 480; + mutex_lock(&state->lock); + + if (format->which == V4L2_SUBDEV_FORMAT_TRY) + *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad); + else + *fmt = state->mode->format; + + mutex_unlock(&state->lock); return 0; } static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { .enum_mbus_code = ov5647_enum_mbus_code, - .set_fmt = ov5647_set_get_fmt, - .get_fmt = ov5647_set_get_fmt, + .set_fmt = ov5647_set_fmt, + .get_fmt = ov5647_get_fmt, + .enum_frame_size = ov5647_enum_frame_size, }; static const struct v4l2_subdev_ops ov5647_subdev_ops = { @@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subde v4l2_subdev_get_try_format(sd, fh->pad, 0); struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, fh->pad, 0); + struct ov5647 *state = to_state(sd); crop->left = OV5647_COLUMN_START_DEF; crop->top = OV5647_ROW_START_DEF; crop->width = OV5647_WINDOW_WIDTH_DEF; crop->height = OV5647_WINDOW_HEIGHT_DEF; - format->code = MEDIA_BUS_FMT_SBGGR8_1X8; - - format->width = OV5647_WINDOW_WIDTH_DEF; - format->height = OV5647_WINDOW_HEIGHT_DEF; - format->field = V4L2_FIELD_NONE; - format->colorspace = V4L2_COLORSPACE_SRGB; + /* Set the default format to the same as the sensor. */ + *format = state->mode->format; return 0; } @@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_clien mutex_init(&sensor->lock); + /* Set the default mode before we init the subdev */ + sensor->mode = OV5647_DEFAULT_MODE; + sd = &sensor->sd; v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); sensor->sd.internal_ops = &ov5647_subdev_internal_ops;