aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
diff options
context:
space:
mode:
authorÁlvaro Fernández Rojas <noltari@gmail.com>2021-02-18 18:04:33 +0100
committerÁlvaro Fernández Rojas <noltari@gmail.com>2021-02-18 23:42:32 +0100
commitf07e572f6447465d8938679533d604e402b0f066 (patch)
treecb333bd2a67e59e7c07659514850a0fd55fc825e /target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
parent5d3a6fd970619dfc55f8259035c3027d7613a2a6 (diff)
downloadupstream-f07e572f6447465d8938679533d604e402b0f066.tar.gz
upstream-f07e572f6447465d8938679533d604e402b0f066.tar.bz2
upstream-f07e572f6447465d8938679533d604e402b0f066.zip
bcm27xx: import latest patches from the RPi foundation
bcm2708: boot tested on RPi B+ v1.2 bcm2709: boot tested on RPi 3B v1.2 and RPi 4B v1.1 4G bcm2710: boot tested on RPi 3B v1.2 bcm2711: boot tested on RPi 4B v1.1 4G Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
Diffstat (limited to 'target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch407
1 files changed, 407 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch b/target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
new file mode 100644
index 0000000000..5846e96af8
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.4/950-0456-media-ov5647-Add-basic-support-for-multiple-sensor-m.patch
@@ -0,0 +1,407 @@
+From 9e584d9de3387588bf455d3c45ec6a092bfa4266 Mon Sep 17 00:00:00 2001
+From: David Plowman <david.plowman@raspberrypi.com>
+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 <david.plowman@raspberrypi.com>
+Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
+---
+ 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;