diff --git a/drivers/media/i2c/imx283.c b/drivers/media/i2c/imx283.c index da618c8cbadc3b..667da30bed2d90 100644 --- a/drivers/media/i2c/imx283.c +++ b/drivers/media/i2c/imx283.c @@ -175,7 +175,7 @@ #define IMX283_XCLR_MIN_DELAY_US (1 * USEC_PER_MSEC) #define IMX283_XCLR_DELAY_RANGE_US (1 * USEC_PER_MSEC) -/* IMX283 native and active pixel array size. */ +/* IMX283 crop regions and positions */ static const struct v4l2_rect imx283_native_area = { .top = 0, .left = 0, @@ -183,9 +183,24 @@ static const struct v4l2_rect imx283_native_area = { .height = 3710, }; +static const struct v4l2_rect imx283_effective_area = { + .top = 16 + 12, /* Clamp + Ignored area*/ + .left = 96, + .width = 5496, + .height = 3672, +}; + static const struct v4l2_rect imx283_active_area = { - .top = 40, - .left = 108, + .top = 16, + .left = 96, + .width = 5496, + .height = 3694, +}; + +/* Datasheet recommended recording pixels */ +static const struct v4l2_rect imx283_recommended_area = { + .top = 16 + 12 + 12, /* Clamp, Ignored area, Color margin */ + .left = 96 + 12, /* Horizontal black, Color margin */ .width = 5472, .height = 3648, }; @@ -195,65 +210,6 @@ struct imx283_reg_list { const struct cci_reg_sequence *regs; }; -/* Mode : resolution and related config values */ -struct imx283_mode { - unsigned int mode; - - /* Bits per pixel */ - unsigned int bpp; - - /* Frame width */ - unsigned int width; - - /* Frame height */ - unsigned int height; - - /* - * Minimum horizontal timing in pixel-units - * - * Note that HMAX is written in 72MHz units, and the datasheet assumes a - * 720MHz link frequency. Convert datasheet values with the following: - * - * For 12 bpp modes (480Mbps) convert with: - * hmax = [hmax in 72MHz units] * 480 / 72 - * - * For 10 bpp modes (576Mbps) convert with: - * hmax = [hmax in 72MHz units] * 576 / 72 - */ - u32 min_hmax; - - /* minimum V-timing in lines */ - u32 min_vmax; - - /* default H-timing */ - u32 default_hmax; - - /* default V-timing */ - u32 default_vmax; - - /* minimum SHR */ - u32 min_shr; - - /* - * Per-mode vertical crop constants used to calculate values - * of IMX283REG_WIDCUT and IMX283_REG_VWINPOS. - */ - u32 veff; - u32 vst; - u32 vct; - - /* Horizontal and vertical binning ratio */ - u8 hbin_ratio; - u8 vbin_ratio; - - /* Optical Blanking */ - u32 horizontal_ob; - u32 vertical_ob; - - /* Analog crop rectangle. */ - struct v4l2_rect crop; -}; - struct imx283_input_frequency { unsigned int mhz; unsigned int reg_count; @@ -323,28 +279,137 @@ struct imx283_readout_mode { u8 mdsel4; }; -static const struct imx283_readout_mode imx283_readout_modes[] = { +struct imx283_scanout { + u8 bpp; + struct imx283_readout_mode readout; + + /* Optical Blanking */ + u8 vertical_ob; + + /* vertical binning ratio */ + u8 vbin_ratio; + + /* Vertical Arbitrary Cropping Function */ + s16 vst; + u16 vct; + u16 veff; + + /* minimum SHR */ + u32 min_shr; +}; + +static const struct imx283_scanout imx283_scan_modes[] = { /* All pixel scan modes */ - [IMX283_MODE_0] = { 0x04, 0x03, 0x10, 0x00 }, /* 12 bit */ - [IMX283_MODE_1] = { 0x04, 0x01, 0x00, 0x00 }, /* 10 bit */ - [IMX283_MODE_1A] = { 0x04, 0x01, 0x20, 0x50 }, /* 10 bit */ - [IMX283_MODE_1S] = { 0x04, 0x41, 0x20, 0x50 }, /* 10 bit */ + [IMX283_MODE_0] = { + .bpp = 12, + .readout = { 0x04, 0x03, 0x10, 0x00 }, + .vertical_ob = 16, + .vbin_ratio = 1, + .vst = -1, /* Align to Mode 2/3 */ + .vct = 0, + .veff = 3694, + .min_shr = 11, + }, + [IMX283_MODE_1] = { + .bpp = 10, + .readout = { 0x04, 0x01, 0x00, 0x00 }, + .vertical_ob = 16, + .vbin_ratio = 1, + .vst = -1, /* Align to Mode 2/3 */ + .vct = 0, + .veff = 3694, + .min_shr = 10, + }, + [IMX283_MODE_1A] = { + .bpp = 10, + .readout = { 0x04, 0x01, 0x20, 0x50 }, + .vertical_ob = 16, + .vbin_ratio = 1, + .vst = 146, + .vct = 291, + .veff = 3112, + .min_shr = 10, + }, + [IMX283_MODE_1S] = { + .bpp = 10, + .readout = { 0x04, 0x41, 0x20, 0x50 }, + .vertical_ob = 16, + .vbin_ratio = 1, + .vst = 162, + .vct = 324, + .veff = 3046, + .min_shr = 10, + }, /* Horizontal / Vertical 2/2-line binning */ - [IMX283_MODE_2] = { 0x0d, 0x11, 0x50, 0x00 }, /* 12 bit */ - [IMX283_MODE_2A] = { 0x0d, 0x11, 0x70, 0x50 }, /* 12 bit */ + [IMX283_MODE_2] = { + .bpp = 12, + .readout = { 0x0d, 0x11, 0x50, 0x00 }, + .vertical_ob = 4, + .vbin_ratio = 2, + .vst = -2, /* Provides alignment to Mode 0/1 */ + .vct = 0, + .veff = 1824, + .min_shr = 12, + }, + [IMX283_MODE_2A] = { + .bpp = 12, + .readout = { 0x0d, 0x11, 0x70, 0x50 }, + .vertical_ob = 4, + .vbin_ratio = 2, + .vst = 71, + .vct = 143, + .veff = 1556, + .min_shr = 12, + }, /* Horizontal / Vertical 3/3-line binning */ - [IMX283_MODE_3] = { 0x1e, 0x18, 0x10, 0x00 }, /* 12 bit */ + [IMX283_MODE_3] = { + .bpp = 12, + .readout = { 0x1e, 0x18, 0x10, 0x00 }, + .vertical_ob = 4, + .vbin_ratio = 3, + .vst = 1, /* Provides alignment to Mode 0/1 */ + .vct = 0, + .veff = 1234, + .min_shr = 16, + }, /* Vertical 2/9 subsampling, horizontal 3 binning cropping */ - [IMX283_MODE_4] = { 0x29, 0x18, 0x30, 0x50 }, /* 12 bit */ + [IMX283_MODE_4] = { + .bpp = 12, + .readout = { 0x29, 0x18, 0x30, 0x50 }, + .vertical_ob = 4, + .vbin_ratio = 1, /* SUBSAMPLING UNDEFINED */ + .vst = 9, + .vct = 17, + .veff = 378, + .min_shr = 4, + }, /* Vertical 2/19 subsampling binning, horizontal 3 binning */ - [IMX283_MODE_5] = { 0x2d, 0x18, 0x10, 0x00 }, /* 12 bit */ + [IMX283_MODE_5] = { + .bpp = 12, + .readout = { 0x2d, 0x18, 0x10, 0x00 }, + .vertical_ob = 4, + .vbin_ratio = 1, /* SUBSAMPLING UNDEFINED */ + .vst = 0, + .vct = 0, + .veff = 198, + .min_shr = 4, + }, /* Vertical 2 binning horizontal 2/4, subsampling 16:9 cropping */ - [IMX283_MODE_6] = { 0x18, 0x21, 0x00, 0x09 }, /* 10 bit */ + [IMX283_MODE_6] = { + .bpp = 10, + .readout = { 0x18, 0x21, 0x00, 0x09 }, + .vertical_ob = 4, + .vbin_ratio = 2, /* SUBSAMPLING UNDEFINED */ + .vst = 0, + .vct = 0, + .veff = 1556, + .min_shr = 12, + }, /* * New modes should make sure the offset period is complied. @@ -352,6 +417,48 @@ static const struct imx283_readout_mode imx283_readout_modes[] = { */ }; +static bool scan_mode(const struct imx283_scanout *scan, enum imx283_modes mode) +{ + return scan == &imx283_scan_modes[mode]; +} + +/* Mode : resolution and related config values */ +struct imx283_mode { + const struct imx283_scanout *scan; + + /* Frame width */ + unsigned int width; + + /* Frame height */ + unsigned int height; + + /* + * Minimum horizontal timing in pixel-units + * + * Note that HMAX is written in 72MHz units, and the datasheet assumes a + * 720MHz link frequency. Convert datasheet values with the following: + * + * For 12 bpp modes (480Mbps) convert with: + * hmax = [hmax in 72MHz units] * 480 / 72 + * + * For 10 bpp modes (576Mbps) convert with: + * hmax = [hmax in 72MHz units] * 576 / 72 + */ + u32 min_hmax; + + /* minimum V-timing in lines */ + u32 min_vmax; + + /* default H-timing */ + u32 default_hmax; + + /* default V-timing */ + u32 default_vmax; + + /* Analog crop rectangle. */ + struct v4l2_rect crop; +}; + static const struct cci_reg_sequence mipi_data_rate_1440Mbps[] = { /* The default register settings provide the 1440Mbps rate */ { CCI_REG8(0x36c5), 0x00 }, /* Undocumented */ @@ -408,42 +515,71 @@ static const struct imx283_reg_list link_freq_reglist[] = { /* Mode configs */ static const struct imx283_mode supported_modes_12bit[] = { + { + /* Full Native pixel array, including HOB/VOB. 5592x3710 */ + .scan = &imx283_scan_modes[IMX283_MODE_0], + + .width = 5592, + .height = 3710, /* 3694 + 16 additional lines for VOB */ + .min_hmax = 5914, /* 887 @ 480MHz/72MHz */ + .min_vmax = 3793, /* Lines */ + + /* 20.00 FPS */ + .default_hmax = 6000, /* 900 @ 480MHz/72MHz */ + .default_vmax = 4000, + + .crop = imx283_native_area, + }, + { + /* All illuminated pixels : 5496x3694 */ + .scan = &imx283_scan_modes[IMX283_MODE_0], + + .width = 5496, + .height = 3694, + .min_hmax = 5914, /* 887 @ 480MHz/72MHz */ + .min_vmax = 3793, /* Lines */ + + /* 20.00 FPS */ + .default_hmax = 6000, /* 900 @ 480MHz/72MHz */ + .default_vmax = 4000, + + .crop = imx283_active_area, + }, + { + /* Effective Pixel Mode : 5496x3672 */ + .scan = &imx283_scan_modes[IMX283_MODE_0], + + .width = 5496, + .height = 3672, + .min_hmax = 5914, /* 887 @ 480MHz/72MHz */ + .min_vmax = 3793, /* Lines */ + + /* 20.00 FPS */ + .default_hmax = 6000, /* 900 @ 480MHz/72MHz */ + .default_vmax = 4000, + + .crop = imx283_effective_area, + }, { /* 20MPix 21.40 fps readout mode 0 */ - .mode = IMX283_MODE_0, - .bpp = 12, + .scan = &imx283_scan_modes[IMX283_MODE_0], + .width = 5472, .height = 3648, .min_hmax = 5914, /* 887 @ 480MHz/72MHz */ .min_vmax = 3793, /* Lines */ - .veff = 3694, - .vst = 0, - .vct = 0, - - .hbin_ratio = 1, - .vbin_ratio = 1, - /* 20.00 FPS */ .default_hmax = 6000, /* 900 @ 480MHz/72MHz */ .default_vmax = 4000, - .min_shr = 11, - .horizontal_ob = 96, - .vertical_ob = 16, - .crop = { - .top = 40, - .left = 108, - .width = 5472, - .height = 3648, - }, + .crop = imx283_recommended_area, }, { /* * Readout mode 2 : 2/2 binned mode (2736x1824) */ - .mode = IMX283_MODE_2, - .bpp = 12, + .scan = &imx283_scan_modes[IMX283_MODE_2], .width = 2736, .height = 1824, .min_hmax = 2414, /* Pixels (362 * 480MHz/72MHz + padding) */ @@ -453,30 +589,13 @@ static const struct imx283_mode supported_modes_12bit[] = { .default_hmax = 2500, /* 375 @ 480MHz/72Mhz */ .default_vmax = 3840, - .veff = 1824, - .vst = 0, - .vct = 0, - - .hbin_ratio = 2, - .vbin_ratio = 2, - - .min_shr = 12, - .horizontal_ob = 48, - .vertical_ob = 4, - - .crop = { - .top = 40, - .left = 108, - .width = 5472, - .height = 3648, - }, + .crop = imx283_recommended_area, }, { /* * Readout mode 3 : 3/3 binned mode (1824x1216) */ - .mode = IMX283_MODE_3, - .bpp = 12, + .scan = &imx283_scan_modes[IMX283_MODE_3], .width = 1824, .height = 1216, .min_hmax = 1894, /* Pixels (284 * 480MHz/72MHz + padding) */ @@ -486,31 +605,14 @@ static const struct imx283_mode supported_modes_12bit[] = { .default_hmax = 1900, /* 285 @ 480MHz/72Mhz */ .default_vmax = 4200, - .veff = 1234, - .vst = 0, - .vct = 0, - - .hbin_ratio = 3, - .vbin_ratio = 3, - - .min_shr = 16, - .horizontal_ob = 32, - .vertical_ob = 4, - - .crop = { - .top = 40, - .left = 108, - .width = 5472, - .height = 3648, - }, + .crop = imx283_recommended_area, }, }; static const struct imx283_mode supported_modes_10bit[] = { { /* 20MPix 25.48 fps readout mode 1 */ - .mode = IMX283_MODE_1, - .bpp = 10, + .scan = &imx283_scan_modes[IMX283_MODE_1], .width = 5472, .height = 3648, .min_hmax = 5960, /* 745 @ 576MHz / 72MHz */ @@ -520,15 +622,7 @@ static const struct imx283_mode supported_modes_10bit[] = { .default_hmax = 6000, /* 750 @ 576MHz / 72MHz */ .default_vmax = 3840, - .min_shr = 10, - .horizontal_ob = 96, - .vertical_ob = 16, - .crop = { - .top = 40, - .left = 108, - .width = 5472, - .height = 3648, - }, + .crop = imx283_recommended_area, }, }; @@ -576,23 +670,31 @@ static inline struct imx283 *to_imx283(struct v4l2_subdev *sd) return container_of_const(sd, struct imx283, sd); } +static inline int get_format_code(unsigned int code) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(imx283_mbus_codes); i++) + if (imx283_mbus_codes[i] == code) + break; + + if (i >= ARRAY_SIZE(imx283_mbus_codes)) + i = 0; + + return imx283_mbus_codes[i]; +} + static inline void get_mode_table(unsigned int code, const struct imx283_mode **mode_list, unsigned int *num_modes) { switch (code) { case MEDIA_BUS_FMT_SRGGB12_1X12: - case MEDIA_BUS_FMT_SGRBG12_1X12: - case MEDIA_BUS_FMT_SGBRG12_1X12: - case MEDIA_BUS_FMT_SBGGR12_1X12: *mode_list = supported_modes_12bit; *num_modes = ARRAY_SIZE(supported_modes_12bit); break; case MEDIA_BUS_FMT_SRGGB10_1X10: - case MEDIA_BUS_FMT_SGRBG10_1X10: - case MEDIA_BUS_FMT_SGBRG10_1X10: - case MEDIA_BUS_FMT_SBGGR10_1X10: *mode_list = supported_modes_10bit; *num_modes = ARRAY_SIZE(supported_modes_10bit); break; @@ -608,7 +710,7 @@ static u64 imx283_pixel_rate(struct imx283 *imx283, const struct imx283_mode *mode) { u64 link_frequency = link_frequencies[__ffs(imx283->link_freq_bitmap)]; - unsigned int bpp = mode->bpp; + unsigned int bpp = mode->scan->bpp; const unsigned int ddr = 2; /* Double Data Rate */ const unsigned int lanes = 4; /* Only 4 lane support */ u64 numerator = link_frequency * ddr * lanes; @@ -665,7 +767,7 @@ static u32 imx283_exposure(struct imx283 *imx283, u64 numerator; /* Number of clocks per internal offset period */ - offset = mode->mode == IMX283_MODE_0 ? 209 : 157; + offset = scan_mode(mode->scan, IMX283_MODE_0) ? 209 : 157; numerator = (imx283->vmax * (svr + 1) - shr) * imx283->hmax + offset; do_div(numerator, imx283->hmax); @@ -678,7 +780,7 @@ static void imx283_exposure_limits(struct imx283 *imx283, s64 *min_exposure, s64 *max_exposure) { u32 svr = 0; /* SVR feature is not currently supported */ - u64 min_shr = mode->min_shr; + u64 min_shr = mode->scan->min_shr; /* Global Shutter is not supported */ u64 max_shr = (svr + 1) * imx283->vmax - 4; @@ -700,7 +802,7 @@ static u32 imx283_shr(struct imx283 *imx283, const struct imx283_mode *mode, u64 temp; /* Number of clocks per internal offset period */ - offset = mode->mode == IMX283_MODE_0 ? 209 : 157; + offset = scan_mode(mode->scan, IMX283_MODE_0) ? 209 : 157; temp = ((u64)exposure * imx283->hmax - offset); do_div(temp, imx283->hmax); @@ -755,7 +857,7 @@ static int imx283_set_ctrl(struct v4l2_ctrl *ctrl) const struct imx283_mode *mode_list; struct v4l2_subdev_state *state; unsigned int num_modes; - u64 shr, pixel_rate; + u64 shr; int ret = 0; state = v4l2_subdev_get_locked_active_state(&imx283->sd); @@ -766,15 +868,24 @@ static int imx283_set_ctrl(struct v4l2_ctrl *ctrl) fmt->width, fmt->height); /* - * The VBLANK control may change the limits of usable exposure, so check - * and adjust if necessary. + * The VBLANK/HBLANK controls change the limits of usable exposure, + * so check and adjust if necessary. */ - if (ctrl->id == V4L2_CID_VBLANK) { + if (ctrl->id == V4L2_CID_HBLANK) { + u64 pixel_rate = imx283_pixel_rate(imx283, mode); + u32 max_width = mode->crop.width + ctrl->val; + + imx283->hmax = imx283_internal_clock(pixel_rate, max_width); + } + + if (ctrl->id == V4L2_CID_VBLANK) + imx283->vmax = mode->crop.height + ctrl->val; + + if (ctrl->id == V4L2_CID_HBLANK || + ctrl->id == V4L2_CID_VBLANK) { /* Honour the VBLANK limits when setting exposure. */ s64 current_exposure, max_exposure, min_exposure; - imx283->vmax = mode->height + ctrl->val; - imx283_exposure_limits(imx283, mode, &min_exposure, &max_exposure); @@ -802,18 +913,26 @@ static int imx283_set_ctrl(struct v4l2_ctrl *ctrl) break; case V4L2_CID_HBLANK: - pixel_rate = imx283_pixel_rate(imx283, mode); - imx283->hmax = imx283_internal_clock(pixel_rate, mode->width + ctrl->val); dev_dbg(imx283->dev, "V4L2_CID_HBLANK : %d HMAX : %u\n", ctrl->val, imx283->hmax); ret = cci_write(imx283->cci, IMX283_REG_HMAX, imx283->hmax, NULL); + + /* Recompute the SHR based on the new timings */ + shr = imx283_shr(imx283, mode, imx283->exposure->val); + cci_write(imx283->cci, IMX283_REG_SHR, shr, &ret); + break; case V4L2_CID_VBLANK: - imx283->vmax = mode->height + ctrl->val; + imx283->vmax = mode->crop.height + ctrl->val; dev_dbg(imx283->dev, "V4L2_CID_VBLANK : %d VMAX : %u\n", ctrl->val, imx283->vmax); ret = cci_write(imx283->cci, IMX283_REG_VMAX, imx283->vmax, NULL); + + /* Recompute the SHR based on the new timings */ + shr = imx283_shr(imx283, mode, imx283->exposure->val); + cci_write(imx283->cci, IMX283_REG_SHR, shr, &ret); + break; case V4L2_CID_ANALOGUE_GAIN: @@ -829,13 +948,11 @@ static int imx283_set_ctrl(struct v4l2_ctrl *ctrl) * VFLIP is managed by BIT(0) of IMX283_REG_HTRIMMING address, hence * both need to be set simultaneously. */ - if (ctrl->val) { - cci_write(imx283->cci, IMX283_REG_HTRIMMING, - IMX283_HTRIMMING_EN | IMX283_MDVREV, &ret); - } else { - cci_write(imx283->cci, IMX283_REG_HTRIMMING, - IMX283_HTRIMMING_EN, &ret); - } + u8 trim = IMX283_HTRIMMING_EN; + + trim |= ctrl->val ? IMX283_MDVREV : 0; + cci_write(imx283->cci, IMX283_REG_HTRIMMING, trim, &ret); + break; case V4L2_CID_TEST_PATTERN: @@ -930,6 +1047,9 @@ static void imx283_set_framing_limits(struct imx283 *imx283, u64 pixel_rate = imx283_pixel_rate(imx283, mode); u64 min_hblank, max_hblank, def_hblank; + /* Use crop for timings from native sensor units */ + const struct v4l2_rect *crop = &mode->crop; + /* Initialise hmax and vmax for exposure calculations */ imx283->hmax = imx283_internal_clock(pixel_rate, mode->default_hmax); imx283->vmax = mode->default_vmax; @@ -938,30 +1058,33 @@ static void imx283_set_framing_limits(struct imx283 *imx283, * Horizontal Blanking * Convert the HMAX_MAX (72MHz) to Pixel rate values for HBLANK_MAX */ - min_hblank = mode->min_hmax - mode->width; - max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->width; - def_hblank = mode->default_hmax - mode->width; + min_hblank = mode->min_hmax - crop->width; + max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - crop->width; + def_hblank = mode->default_hmax - crop->width; __v4l2_ctrl_modify_range(imx283->hblank, min_hblank, max_hblank, 1, def_hblank); __v4l2_ctrl_s_ctrl(imx283->hblank, def_hblank); /* Vertical Blanking */ - __v4l2_ctrl_modify_range(imx283->vblank, mode->min_vmax - mode->height, - IMX283_VMAX_MAX - mode->height, 1, + __v4l2_ctrl_modify_range(imx283->vblank, mode->min_vmax - crop->height, + IMX283_VMAX_MAX - crop->height, 1, mode->default_vmax - mode->height); - __v4l2_ctrl_s_ctrl(imx283->vblank, mode->default_vmax - mode->height); + __v4l2_ctrl_s_ctrl(imx283->vblank, mode->default_vmax - crop->height); } static int imx283_set_pad_format(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *fmt) { + struct v4l2_rect *crop; struct v4l2_mbus_framefmt *format; const struct imx283_mode *mode; struct imx283 *imx283 = to_imx283(sd); const struct imx283_mode *mode_list; unsigned int num_modes; + fmt->format.code = get_format_code(fmt->format.code); + get_mode_table(fmt->format.code, &mode_list, &num_modes); mode = v4l2_find_nearest_size(mode_list, num_modes, width, height, @@ -982,6 +1105,9 @@ static int imx283_set_pad_format(struct v4l2_subdev *sd, *format = fmt->format; + crop = v4l2_subdev_state_get_crop(sd_state, IMAGE_PAD); + *crop = mode->crop; + return 0; } @@ -1038,10 +1164,7 @@ static int imx283_start_streaming(struct imx283 *imx283, const struct v4l2_mbus_framefmt *fmt; const struct imx283_mode *mode_list; unsigned int num_modes; - u32 v_widcut; - s32 v_pos; - u32 write_v_size; - u32 y_out_size; + int ret = 0; fmt = v4l2_subdev_state_get_format(state, 0); @@ -1059,7 +1182,7 @@ static int imx283_start_streaming(struct imx283 *imx283, * Set the readout mode registers. * MDSEL3 and MDSEL4 are updated to enable Arbitrary Vertical Cropping. */ - readout = &imx283_readout_modes[mode->mode]; + readout = &mode->scan->readout; cci_write(imx283->cci, IMX283_REG_MDSEL1, readout->mdsel1, &ret); cci_write(imx283->cci, IMX283_REG_MDSEL2, readout->mdsel2, &ret); cci_write(imx283->cci, IMX283_REG_MDSEL3, @@ -1068,7 +1191,7 @@ static int imx283_start_streaming(struct imx283 *imx283, readout->mdsel4 | IMX283_MDSEL4_VCROP_EN, &ret); /* Mode 1S specific entries from the Readout Drive Mode Tables */ - if (mode->mode == IMX283_MODE_1S) { + if (scan_mode(mode->scan, IMX283_MODE_1S)) { cci_write(imx283->cci, IMX283_REG_MDSEL7, 0x01, &ret); cci_write(imx283->cci, IMX283_REG_MDSEL18, 0x1098, &ret); } @@ -1088,28 +1211,93 @@ static int imx283_start_streaming(struct imx283 *imx283, mode->crop.width, mode->crop.height); - y_out_size = mode->crop.height / mode->vbin_ratio; - write_v_size = y_out_size + mode->vertical_ob; - /* - * cropping start position = (VWINPOS – Vst) × 2 - * cropping width = Veff – (VWIDCUT – Vct) × 2 - */ - v_pos = imx283->vflip->val ? - ((-mode->crop.top / mode->vbin_ratio) / 2) + mode->vst : - ((mode->crop.top / mode->vbin_ratio) / 2) + mode->vst; - v_widcut = ((mode->veff - y_out_size) / 2) + mode->vct; + /* Vertical Configuration */ + { + u32 y_out_size = mode->crop.height / mode->scan->vbin_ratio; + + /* + * The CLAMP region contains the Vertical Optical Black (VOB) + * lines, which are not included in the effective image height + * and 0 of the VWIDPOS corresponds to the first line after. + * + * This puts any request to view VOB as negative positions. + */ + s16 top = mode->crop.top - 16; + u16 veff = mode->scan->veff; + + /* + * Lots of empirical testing shows that we need to cut one less + * line than expected or we get corruption in the last line. + */ + u16 cut = veff - min(veff, y_out_size + 1); + + u32 v_widcut; + s32 v_pos; + + /* + * Reduce the v_ob when the requested crop position is below + * zero to output the VOB on image data. + */ + u8 v_ob = mode->scan->vertical_ob + min_t(s16, 0, top); + u32 write_v_size = y_out_size + v_ob; - cci_write(imx283->cci, IMX283_REG_Y_OUT_SIZE, y_out_size, &ret); - cci_write(imx283->cci, IMX283_REG_WRITE_VSIZE, write_v_size, &ret); - cci_write(imx283->cci, IMX283_REG_VWIDCUT, v_widcut, &ret); - cci_write(imx283->cci, IMX283_REG_VWINPOS, v_pos, &ret); + /* Clamp our top position now that VOB is handled */ + top = max_t(s16, 0, top); - cci_write(imx283->cci, IMX283_REG_OB_SIZE_V, mode->vertical_ob, &ret); + /* + * The all-pixel scan modes introduce an 'extra line' at the + * start of the image when not flipped. This is understood to be + * a mechanism to preserve bayer ordering regardless of vflip + * however it introduces an undesirable line of black pixels. + * + * Crop this by adding two extra lines and moving them into the + * OB data type without impacting the effective image height or + * positioning. + * + * It's ok for top to go negative here as the sensor does in + * fact have extra 'hidden' lines in this region and the sensor + * supports negative vertical cropping positions. (estimated + * about 48 extra lines) + */ + if ((scan_mode(mode->scan, IMX283_MODE_0) || + scan_mode(mode->scan, IMX283_MODE_1)) && + imx283->vflip->val == 0) { + top -= 2; + v_ob += 2; + write_v_size += 2; + } + + if (imx283->vflip->val) + top = -top; + + /* + * cropping start position = (VWINPOS – Vst) × 2 + * cropping width = Veff – (VWIDCUT – Vct) × 2 + */ + v_pos = (top / mode->scan->vbin_ratio / 2) + mode->scan->vst; + v_widcut = (cut / 2) + mode->scan->vct; - /* TODO: Validate mode->crop is fully contained within imx283_native_area */ - cci_write(imx283->cci, IMX283_REG_HTRIMMING_START, mode->crop.left, &ret); - cci_write(imx283->cci, IMX283_REG_HTRIMMING_END, - mode->crop.left + mode->crop.width, &ret); + cci_write(imx283->cci, IMX283_REG_Y_OUT_SIZE, y_out_size, &ret); + cci_write(imx283->cci, IMX283_REG_WRITE_VSIZE, write_v_size, &ret); + cci_write(imx283->cci, IMX283_REG_VWIDCUT, v_widcut, &ret); + cci_write(imx283->cci, IMX283_REG_VWINPOS, v_pos, &ret); + + cci_write(imx283->cci, IMX283_REG_OB_SIZE_V, v_ob, &ret); + } + + /* Horizontal Configuration */ + { + /* + * While Vertical OB is excluded from the sensor positions, + * the Horizontal OB is included within the whole HTRIMMING + * calculation. + */ + u32 left = mode->crop.left; + u32 right = left + mode->crop.width; + + cci_write(imx283->cci, IMX283_REG_HTRIMMING_START, left, &ret); + cci_write(imx283->cci, IMX283_REG_HTRIMMING_END, right, &ret); + } /* Disable embedded data */ cci_write(imx283->cci, IMX283_REG_EBD_X_OUT_SIZE, 0, &ret); @@ -1331,13 +1519,13 @@ static int imx283_init_controls(struct imx283 *imx283) /* Initialise vblank/hblank/exposure based on the current mode. */ imx283->vblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_VBLANK, - mode->min_vmax - mode->height, + mode->min_vmax - mode->crop.height, IMX283_VMAX_MAX, 1, - mode->default_vmax - mode->height); + mode->default_vmax - mode->crop.height); - min_hblank = mode->min_hmax - mode->width; - max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->width; - def_hblank = mode->default_hmax - mode->width; + min_hblank = mode->min_hmax - mode->crop.width; + max_hblank = imx283_iclk_to_pix(pixel_rate, IMX283_HMAX_MAX) - mode->crop.width; + def_hblank = mode->default_hmax - mode->crop.width; imx283->hblank = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_HBLANK, min_hblank, max_hblank, 1, def_hblank); @@ -1359,8 +1547,6 @@ static int imx283_init_controls(struct imx283 *imx283) imx283->vflip = v4l2_ctrl_new_std(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); - if (imx283->vflip) - imx283->vflip->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT; v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &imx283_ctrl_ops, V4L2_CID_TEST_PATTERN,