| From c673ce1338b69c5c8c6572e361be02356af93a72 Mon Sep 17 00:00:00 2001 |
| From: Dave Stevenson <dave.stevenson@raspberrypi.com> |
| Date: Tue, 7 Jul 2020 18:29:10 +0100 |
| Subject: [PATCH] media: i2c: ov9281: Add support for 8 bit readout |
| |
| The sensor supports 8 bit mode as well as 10bit, so add the |
| relevant code to allow selection of this. |
| |
| Signed-off-by: Dave Stevenson <dave.stevenson@raspberrypi.com> |
| --- |
| drivers/media/i2c/ov9281.c | 66 ++++++++++++++++++++++++++++++-------- |
| 1 file changed, 52 insertions(+), 14 deletions(-) |
| |
| --- a/drivers/media/i2c/ov9281.c |
| +++ b/drivers/media/i2c/ov9281.c |
| @@ -29,11 +29,12 @@ |
| |
| #define OV9281_LINK_FREQ_400MHZ 400000000 |
| #define OV9281_LANES 2 |
| -#define OV9281_BITS_PER_SAMPLE 10 |
| |
| /* pixel rate = link frequency * 2 * lanes / BITS_PER_SAMPLE */ |
| -#define OV9281_PIXEL_RATE (OV9281_LINK_FREQ_400MHZ * 2 * \ |
| - OV9281_LANES / OV9281_BITS_PER_SAMPLE) |
| +#define OV9281_PIXEL_RATE_10BIT (OV9281_LINK_FREQ_400MHZ * 2 * \ |
| + OV9281_LANES / 10) |
| +#define OV9281_PIXEL_RATE_8BIT (OV9281_LINK_FREQ_400MHZ * 2 * \ |
| + OV9281_LANES / 8) |
| #define OV9281_XVCLK_FREQ 24000000 |
| |
| #define CHIP_ID 0x9281 |
| @@ -122,24 +123,25 @@ struct ov9281 { |
| struct v4l2_ctrl *digi_gain; |
| struct v4l2_ctrl *hblank; |
| struct v4l2_ctrl *vblank; |
| + struct v4l2_ctrl *pixel_rate; |
| struct v4l2_ctrl *test_pattern; |
| struct mutex mutex; |
| bool streaming; |
| bool power_on; |
| const struct ov9281_mode *cur_mode; |
| + u32 code; |
| }; |
| |
| #define to_ov9281(sd) container_of(sd, struct ov9281, subdev) |
| |
| /* |
| * Xclk 24Mhz |
| - * max_framerate 120fps |
| + * max_framerate 120fps for 10 bit, 144fps for 8 bit. |
| * mipi_datarate per lane 800Mbps |
| */ |
| static const struct regval ov9281_1280x800_regs[] = { |
| {0x0103, 0x01}, |
| {0x0302, 0x32}, |
| - {0x030d, 0x50}, |
| {0x030e, 0x02}, |
| {0x3001, 0x00}, |
| {0x3004, 0x00}, |
| @@ -168,7 +170,6 @@ static const struct regval ov9281_1280x8 |
| {0x3620, 0x6f}, |
| {0x3632, 0x56}, |
| {0x3633, 0x78}, |
| - {0x3662, 0x05}, |
| {0x3666, 0x00}, |
| {0x366f, 0x5a}, |
| {0x3680, 0x84}, |
| @@ -235,6 +236,18 @@ static const struct regval ov9281_1280x8 |
| {REG_NULL, 0x00}, |
| }; |
| |
| +static const struct regval op_10bit[] = { |
| + {0x030d, 0x50}, |
| + {0x3662, 0x05}, |
| + {REG_NULL, 0x00}, |
| +}; |
| + |
| +static const struct regval op_8bit[] = { |
| + {0x030d, 0x60}, |
| + {0x3662, 0x07}, |
| + {REG_NULL, 0x00}, |
| +}; |
| + |
| static const struct ov9281_mode supported_modes[] = { |
| { |
| .width = 1280, |
| @@ -374,12 +387,13 @@ static int ov9281_set_fmt(struct v4l2_su |
| { |
| struct ov9281 *ov9281 = to_ov9281(sd); |
| const struct ov9281_mode *mode; |
| - s64 h_blank, vblank_def; |
| + s64 h_blank, vblank_def, pixel_rate; |
| |
| mutex_lock(&ov9281->mutex); |
| |
| mode = ov9281_find_best_fit(fmt); |
| - fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; |
| + if (fmt->format.code != MEDIA_BUS_FMT_Y8_1X8) |
| + fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; |
| fmt->format.width = mode->width; |
| fmt->format.height = mode->height; |
| fmt->format.field = V4L2_FIELD_NONE; |
| @@ -396,6 +410,7 @@ static int ov9281_set_fmt(struct v4l2_su |
| *v4l2_subdev_get_try_format(sd, cfg, fmt->pad) = fmt->format; |
| } else { |
| ov9281->cur_mode = mode; |
| + ov9281->code = fmt->format.code; |
| h_blank = mode->hts_def - mode->width; |
| __v4l2_ctrl_modify_range(ov9281->hblank, h_blank, |
| h_blank, 1, h_blank); |
| @@ -405,6 +420,11 @@ static int ov9281_set_fmt(struct v4l2_su |
| OV9281_VTS_MAX - mode->height, |
| 1, vblank_def); |
| __v4l2_ctrl_s_ctrl(ov9281->vblank, vblank_def); |
| + |
| + pixel_rate = (fmt->format.code == MEDIA_BUS_FMT_Y10_1X10) ? |
| + OV9281_PIXEL_RATE_10BIT : OV9281_PIXEL_RATE_8BIT; |
| + __v4l2_ctrl_modify_range(ov9281->pixel_rate, pixel_rate, |
| + pixel_rate, 1, pixel_rate); |
| } |
| |
| mutex_unlock(&ov9281->mutex); |
| @@ -425,7 +445,7 @@ static int ov9281_get_fmt(struct v4l2_su |
| } else { |
| fmt->format.width = mode->width; |
| fmt->format.height = mode->height; |
| - fmt->format.code = MEDIA_BUS_FMT_Y10_1X10; |
| + fmt->format.code = ov9281->code; |
| fmt->format.field = V4L2_FIELD_NONE; |
| fmt->format.colorspace = V4L2_COLORSPACE_SRGB; |
| fmt->format.ycbcr_enc = |
| @@ -446,9 +466,16 @@ static int ov9281_enum_mbus_code(struct |
| struct v4l2_subdev_pad_config *cfg, |
| struct v4l2_subdev_mbus_code_enum *code) |
| { |
| - if (code->index) |
| + switch (code->index) { |
| + default: |
| return -EINVAL; |
| - code->code = MEDIA_BUS_FMT_Y10_1X10; |
| + case 0: |
| + code->code = MEDIA_BUS_FMT_Y10_1X10; |
| + break; |
| + case 1: |
| + code->code = MEDIA_BUS_FMT_Y8_1X8; |
| + break; |
| + } |
| |
| return 0; |
| } |
| @@ -460,7 +487,8 @@ static int ov9281_enum_frame_sizes(struc |
| if (fse->index >= ARRAY_SIZE(supported_modes)) |
| return -EINVAL; |
| |
| - if (fse->code != MEDIA_BUS_FMT_Y10_1X10) |
| + if (fse->code != MEDIA_BUS_FMT_Y10_1X10 && |
| + fse->code != MEDIA_BUS_FMT_Y8_1X8) |
| return -EINVAL; |
| |
| fse->min_width = supported_modes[fse->index].width; |
| @@ -543,6 +571,13 @@ static int __ov9281_start_stream(struct |
| if (ret) |
| return ret; |
| |
| + if (ov9281->code == MEDIA_BUS_FMT_Y10_1X10) |
| + ret = ov9281_write_array(ov9281->client, op_10bit); |
| + else |
| + ret = ov9281_write_array(ov9281->client, op_8bit); |
| + if (ret) |
| + return ret; |
| + |
| /* In case these controls are set before streaming */ |
| mutex_unlock(&ov9281->mutex); |
| ret = v4l2_ctrl_handler_setup(&ov9281->ctrl_handler); |
| @@ -849,8 +884,11 @@ static int ov9281_initialize_controls(st |
| if (ctrl) |
| ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; |
| |
| - v4l2_ctrl_new_std(handler, NULL, V4L2_CID_PIXEL_RATE, |
| - 0, OV9281_PIXEL_RATE, 1, OV9281_PIXEL_RATE); |
| + ov9281->pixel_rate = v4l2_ctrl_new_std(handler, NULL, |
| + V4L2_CID_PIXEL_RATE, |
| + OV9281_PIXEL_RATE_10BIT, |
| + OV9281_PIXEL_RATE_10BIT, 1, |
| + OV9281_PIXEL_RATE_10BIT); |
| |
| h_blank = mode->hts_def - mode->width; |
| ov9281->hblank = v4l2_ctrl_new_std(handler, NULL, V4L2_CID_HBLANK, |