diff options
Diffstat (limited to 'drivers/media/i2c/ov5640.c')
-rw-r--r-- | drivers/media/i2c/ov5640.c | 1650 |
1 files changed, 1188 insertions, 462 deletions
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c index db5a19babe67..502f0b62e950 100644 --- a/drivers/media/i2c/ov5640.c +++ b/drivers/media/i2c/ov5640.c @@ -29,8 +29,21 @@ #define OV5640_XCLK_MIN 6000000 #define OV5640_XCLK_MAX 54000000 +#define OV5640_NATIVE_WIDTH 2624 +#define OV5640_NATIVE_HEIGHT 1964 +#define OV5640_PIXEL_ARRAY_TOP 14 +#define OV5640_PIXEL_ARRAY_LEFT 16 +#define OV5640_PIXEL_ARRAY_WIDTH 2592 +#define OV5640_PIXEL_ARRAY_HEIGHT 1944 + +/* FIXME: not documented. */ +#define OV5640_MIN_VBLANK 24 +#define OV5640_MAX_VTS 3375 + #define OV5640_DEFAULT_SLAVE_ID 0x3c +#define OV5640_LINK_RATE_MAX 490000000U + #define OV5640_REG_SYS_RESET02 0x3002 #define OV5640_REG_SYS_CLOCK_ENABLE02 0x3006 #define OV5640_REG_SYS_CTRL0 0x3008 @@ -59,10 +72,16 @@ #define OV5640_REG_AEC_PK_MANUAL 0x3503 #define OV5640_REG_AEC_PK_REAL_GAIN 0x350a #define OV5640_REG_AEC_PK_VTS 0x350c +#define OV5640_REG_TIMING_HS 0x3800 +#define OV5640_REG_TIMING_VS 0x3802 +#define OV5640_REG_TIMING_HW 0x3804 +#define OV5640_REG_TIMING_VH 0x3806 #define OV5640_REG_TIMING_DVPHO 0x3808 #define OV5640_REG_TIMING_DVPVO 0x380a #define OV5640_REG_TIMING_HTS 0x380c #define OV5640_REG_TIMING_VTS 0x380e +#define OV5640_REG_TIMING_HOFFS 0x3810 +#define OV5640_REG_TIMING_VOFFS 0x3812 #define OV5640_REG_TIMING_TC_REG20 0x3820 #define OV5640_REG_TIMING_TC_REG21 0x3821 #define OV5640_REG_AEC_CTRL00 0x3a00 @@ -88,6 +107,7 @@ #define OV5640_REG_POLARITY_CTRL00 0x4740 #define OV5640_REG_MIPI_CTRL00 0x4800 #define OV5640_REG_DEBUG_MODE 0x4814 +#define OV5640_REG_PCLK_PERIOD 0x4837 #define OV5640_REG_ISP_FORMAT_MUX_CTRL 0x501f #define OV5640_REG_PRE_ISP_TEST_SET1 0x503d #define OV5640_REG_SDE_CTRL0 0x5580 @@ -118,6 +138,47 @@ enum ov5640_frame_rate { OV5640_NUM_FRAMERATES, }; +enum ov5640_pixel_rate_id { + OV5640_PIXEL_RATE_168M, + OV5640_PIXEL_RATE_148M, + OV5640_PIXEL_RATE_124M, + OV5640_PIXEL_RATE_96M, + OV5640_PIXEL_RATE_48M, + OV5640_NUM_PIXEL_RATES, +}; + +/* + * The chip manual suggests 24/48/96/192 MHz pixel clocks. + * + * 192MHz exceeds the sysclk limits; use 168MHz as maximum pixel rate for + * full resolution mode @15 FPS. + */ +static const u32 ov5640_pixel_rates[] = { + [OV5640_PIXEL_RATE_168M] = 168000000, + [OV5640_PIXEL_RATE_148M] = 148000000, + [OV5640_PIXEL_RATE_124M] = 124000000, + [OV5640_PIXEL_RATE_96M] = 96000000, + [OV5640_PIXEL_RATE_48M] = 48000000, +}; + +/* + * MIPI CSI-2 link frequencies. + * + * Derived from the above defined pixel rate for bpp = (8, 16, 24) and + * data_lanes = (1, 2) + * + * link_freq = (pixel_rate * bpp) / (2 * data_lanes) + */ +static const s64 ov5640_csi2_link_freqs[] = { + 992000000, 888000000, 768000000, 744000000, 672000000, 672000000, + 592000000, 592000000, 576000000, 576000000, 496000000, 496000000, + 384000000, 384000000, 384000000, 336000000, 296000000, 288000000, + 248000000, 192000000, 192000000, 192000000, 96000000, +}; + +/* Link freq for default mode: UYVY 16 bpp, 2 data lanes. */ +#define OV5640_DEFAULT_LINK_FREQ 13 + enum ov5640_format_mux { OV5640_FMT_MUX_YUV422 = 0, OV5640_FMT_MUX_RGB, @@ -130,20 +191,145 @@ enum ov5640_format_mux { struct ov5640_pixfmt { u32 code; u32 colorspace; + u8 bpp; + u8 ctrl00; + enum ov5640_format_mux mux; +}; + +static const struct ov5640_pixfmt ov5640_dvp_formats[] = { + { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_JPEG_1X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, UYVY */ + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x3f, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_YUYV8_2X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ + .code = MEDIA_BUS_FMT_RGB565_2X8_LE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x6f, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ + .code = MEDIA_BUS_FMT_RGB565_2X8_BE, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x61, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* Raw, BGBG... / GRGR... */ + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x00, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GBGB... / RGRG... */ + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x01, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GRGR... / BGBG... */ + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x02, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, RGRG... / GBGB... */ + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x03, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, + { /* sentinel */ } }; -static const struct ov5640_pixfmt ov5640_formats[] = { - { MEDIA_BUS_FMT_JPEG_1X8, V4L2_COLORSPACE_JPEG, }, - { MEDIA_BUS_FMT_UYVY8_2X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_UYVY8_1X16, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_YUYV8_2X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_YUYV8_1X16, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_RGB565_2X8_LE, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_RGB565_2X8_BE, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SBGGR8_1X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SGBRG8_1X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SGRBG8_1X8, V4L2_COLORSPACE_SRGB, }, - { MEDIA_BUS_FMT_SRGGB8_1X8, V4L2_COLORSPACE_SRGB, }, +static const struct ov5640_pixfmt ov5640_csi2_formats[] = { + { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_JPEG_1X8, + .colorspace = V4L2_COLORSPACE_JPEG, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, UYVY */ + .code = MEDIA_BUS_FMT_UYVY8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x3f, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* YUV422, YUYV */ + .code = MEDIA_BUS_FMT_YUYV8_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x30, + .mux = OV5640_FMT_MUX_YUV422, + }, { + /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ + .code = MEDIA_BUS_FMT_RGB565_1X16, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 16, + .ctrl00 = 0x6f, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* BGR888: RGB */ + .code = MEDIA_BUS_FMT_BGR888_1X24, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 24, + .ctrl00 = 0x23, + .mux = OV5640_FMT_MUX_RGB, + }, { + /* Raw, BGBG... / GRGR... */ + .code = MEDIA_BUS_FMT_SBGGR8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x00, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GBGB... / RGRG... */ + .code = MEDIA_BUS_FMT_SGBRG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x01, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, GRGR... / BGBG... */ + .code = MEDIA_BUS_FMT_SGRBG8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x02, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, { + /* Raw bayer, RGRG... / GBGB... */ + .code = MEDIA_BUS_FMT_SRGGB8_1X8, + .colorspace = V4L2_COLORSPACE_SRGB, + .bpp = 8, + .ctrl00 = 0x03, + .mux = OV5640_FMT_MUX_RAW_DPC, + }, + { /* sentinel */ } }; /* @@ -186,21 +372,42 @@ struct reg_value { u32 delay_ms; }; +struct ov5640_timings { + /* Analog crop rectangle. */ + struct v4l2_rect analog_crop; + /* Visibile crop: from analog crop top-left corner. */ + struct v4l2_rect crop; + /* Total pixels per line: width + fixed hblank. */ + u32 htot; + /* Default vertical blanking: frame height = height + vblank. */ + u32 vblank_def; +}; + struct ov5640_mode_info { enum ov5640_mode_id id; enum ov5640_downsize_mode dn_mode; - u32 hact; - u32 htot; - u32 vact; - u32 vtot; + enum ov5640_pixel_rate_id pixel_rate; + + unsigned int width; + unsigned int height; + + struct ov5640_timings dvp_timings; + struct ov5640_timings csi2_timings; + const struct reg_value *reg_data; u32 reg_data_size; + + /* Used by s_frame_interval only. */ u32 max_fps; + u32 def_fps; }; struct ov5640_ctrls { struct v4l2_ctrl_handler handler; struct v4l2_ctrl *pixel_rate; + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *vblank; struct { struct v4l2_ctrl *auto_exp; struct v4l2_ctrl *exposure; @@ -249,6 +456,7 @@ struct ov5640_dev { const struct ov5640_mode_info *last_mode; enum ov5640_frame_rate current_fr; struct v4l2_fract frame_interval; + s64 current_link_freq; struct ov5640_ctrls ctrls; @@ -270,6 +478,40 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) ctrls.handler)->sd; } +static inline bool ov5640_is_csi2(const struct ov5640_dev *sensor) +{ + return sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY; +} + +static inline const struct ov5640_pixfmt * +ov5640_formats(struct ov5640_dev *sensor) +{ + return ov5640_is_csi2(sensor) ? ov5640_csi2_formats + : ov5640_dvp_formats; +} + +static const struct ov5640_pixfmt * +ov5640_code_to_pixfmt(struct ov5640_dev *sensor, u32 code) +{ + const struct ov5640_pixfmt *formats = ov5640_formats(sensor); + unsigned int i; + + for (i = 0; formats[i].code; ++i) { + if (formats[i].code == code) + return &formats[i]; + } + + return &formats[0]; +} + +static u32 ov5640_code_to_bpp(struct ov5640_dev *sensor, u32 code) +{ + const struct ov5640_pixfmt *format = ov5640_code_to_pixfmt(sensor, + code); + + return format->bpp; +} + /* * FIXME: all of these register tables are likely filled with * entries that set the register to their power-on default values, @@ -278,7 +520,19 @@ static inline struct v4l2_subdev *ctrl_to_sd(struct v4l2_ctrl *ctrl) * over i2c. */ /* YUV422 UYVY VGA@30fps */ -static const struct reg_value ov5640_init_setting_30fps_VGA[] = { + +static const struct v4l2_mbus_framefmt ov5640_default_fmt = { + .code = MEDIA_BUS_FMT_UYVY8_2X8, + .width = 640, + .height = 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(V4L2_COLORSPACE_SRGB), + .quantization = V4L2_QUANTIZATION_FULL_RANGE, + .xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(V4L2_COLORSPACE_SRGB), + .field = V4L2_FIELD_NONE, +}; + +static const struct reg_value ov5640_init_setting[] = { {0x3103, 0x11, 0, 0}, {0x3008, 0x82, 0, 5}, {0x3008, 0x42, 0, 0}, {0x3103, 0x03, 0, 0}, {0x3630, 0x36, 0, 0}, {0x3631, 0x0e, 0, 0}, {0x3632, 0xe2, 0, 0}, {0x3633, 0x12, 0, 0}, @@ -294,11 +548,7 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3c06, 0x00, 0, 0}, {0x3c07, 0x08, 0, 0}, {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3820, 0x41, 0, 0}, {0x3821, 0x07, 0, 0}, {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -362,72 +612,11 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = { {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300}, }; -static const struct reg_value ov5640_setting_VGA_640_480[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_XGA_1024_768[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_QVGA_320_240[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_QQVGA_160_120[] = { +static const struct reg_value ov5640_setting_low_res[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -437,72 +626,11 @@ static const struct reg_value ov5640_setting_QQVGA_160_120[] = { {0x4407, 0x04, 0, 0}, {0x5001, 0xa3, 0, 0}, }; -static const struct reg_value ov5640_setting_QCIF_176_144[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_NTSC_720_480[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x3c, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - -static const struct reg_value ov5640_setting_PAL_720_576[] = { - {0x3c07, 0x08, 0, 0}, - {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x04, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9b, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x38, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x06, 0, 0}, - {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, - {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x03, 0, 0}, - {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, - {0x3a0a, 0x00, 0, 0}, {0x3a0b, 0xf6, 0, 0}, {0x3a0e, 0x03, 0, 0}, - {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x03, 0, 0}, {0x3a15, 0xd8, 0, 0}, - {0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0}, - {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0}, - {0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0}, -}; - static const struct reg_value ov5640_setting_720P_1280_720[] = { {0x3c07, 0x07, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x31, 0, 0}, - {0x3815, 0x31, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0xfa, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x06, 0, 0}, {0x3807, 0xa9, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3815, 0x31, 0, 0}, {0x3618, 0x00, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3709, 0x52, 0, 0}, {0x370c, 0x03, 0, 0}, {0x3a02, 0x02, 0, 0}, {0x3a03, 0xe4, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0xbc, 0, 0}, @@ -517,11 +645,7 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, - {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -532,9 +656,6 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 0}, {0x3c07, 0x07, 0, 0}, {0x3c08, 0x00, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, - {0x3800, 0x01, 0, 0}, {0x3801, 0x50, 0, 0}, {0x3802, 0x01, 0, 0}, - {0x3803, 0xb2, 0, 0}, {0x3804, 0x08, 0, 0}, {0x3805, 0xef, 0, 0}, - {0x3806, 0x05, 0, 0}, {0x3807, 0xf1, 0, 0}, {0x3612, 0x2b, 0, 0}, {0x3708, 0x64, 0, 0}, {0x3a02, 0x04, 0, 0}, {0x3a03, 0x60, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x50, 0, 0}, {0x3a0a, 0x01, 0, 0}, {0x3a0b, 0x18, 0, 0}, @@ -548,11 +669,7 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { {0x3c07, 0x08, 0, 0}, {0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0}, {0x3814, 0x11, 0, 0}, - {0x3815, 0x11, 0, 0}, {0x3800, 0x00, 0, 0}, {0x3801, 0x00, 0, 0}, - {0x3802, 0x00, 0, 0}, {0x3803, 0x00, 0, 0}, {0x3804, 0x0a, 0, 0}, - {0x3805, 0x3f, 0, 0}, {0x3806, 0x07, 0, 0}, {0x3807, 0x9f, 0, 0}, - {0x3810, 0x00, 0, 0}, - {0x3811, 0x10, 0, 0}, {0x3812, 0x00, 0, 0}, {0x3813, 0x04, 0, 0}, + {0x3815, 0x11, 0, 0}, {0x3618, 0x04, 0, 0}, {0x3612, 0x29, 0, 0}, {0x3708, 0x21, 0, 0}, {0x3709, 0x12, 0, 0}, {0x370c, 0x00, 0, 0}, {0x3a02, 0x03, 0, 0}, {0x3a03, 0xd8, 0, 0}, {0x3a08, 0x01, 0, 0}, {0x3a09, 0x27, 0, 0}, @@ -563,67 +680,462 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = { {0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70}, }; -/* power-on sensor init reg table */ -static const struct ov5640_mode_info ov5640_mode_init_data = { - 0, SUBSAMPLING, 640, 1896, 480, 984, - ov5640_init_setting_30fps_VGA, - ARRAY_SIZE(ov5640_init_setting_30fps_VGA), - OV5640_30_FPS, +static const struct ov5640_mode_info ov5640_mode_data[OV5640_NUM_MODES] = { + { + /* 160x120 */ + .id = OV5640_MODE_QQVGA_160_120, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .width = 160, + .height = 120, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 160, + .height = 120, + }, + .htot = 1896, + .vblank_def = 864, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 160, + .height = 120, + }, + .htot = 1600, + .vblank_def = 878, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 176x144 */ + .id = OV5640_MODE_QCIF_176_144, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .width = 176, + .height = 144, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 176, + .height = 144, + }, + .htot = 1896, + .vblank_def = 840, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 176, + .height = 144, + }, + .htot = 1600, + .vblank_def = 854, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 320x240 */ + .id = OV5640_MODE_QVGA_320_240, + .dn_mode = SUBSAMPLING, + .width = 320, + .height = 240, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 320, + .height = 240, + }, + .htot = 1896, + .vblank_def = 744, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 320, + .height = 240, + }, + .htot = 1600, + .vblank_def = 760, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 640x480 */ + .id = OV5640_MODE_VGA_640_480, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_48M, + .width = 640, + .height = 480, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 640, + .height = 480, + }, + .htot = 1896, + .vblank_def = 600, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + /* Maintain a minimum processing margin. */ + .crop = { + .left = 2, + .top = 4, + .width = 640, + .height = 480, + }, + .htot = 1600, + .vblank_def = 520, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_60_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 720x480 */ + .id = OV5640_MODE_NTSC_720_480, + .dn_mode = SUBSAMPLING, + .width = 720, + .height = 480, + .pixel_rate = OV5640_PIXEL_RATE_96M, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 56, + .top = 60, + .width = 720, + .height = 480, + }, + .htot = 1896, + .vblank_def = 504, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + .crop = { + .left = 56, + .top = 60, + .width = 720, + .height = 480, + }, + .htot = 1896, + .vblank_def = 1206, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 720x576 */ + .id = OV5640_MODE_PAL_720_576, + .dn_mode = SUBSAMPLING, + .width = 720, + .height = 576, + .pixel_rate = OV5640_PIXEL_RATE_96M, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 56, + .top = 6, + .width = 720, + .height = 576, + }, + .htot = 1896, + .vblank_def = 408, + }, + .csi2_timings = { + /* Feed the full valid pixel array to the ISP. */ + .analog_crop = { + .left = OV5640_PIXEL_ARRAY_LEFT, + .top = OV5640_PIXEL_ARRAY_TOP, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + .crop = { + .left = 56, + .top = 6, + .width = 720, + .height = 576, + }, + .htot = 1896, + .vblank_def = 1110, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 1024x768 */ + .id = OV5640_MODE_XGA_1024_768, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_96M, + .width = 1024, + .height = 768, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = 2624, + .height = 1944, + }, + .crop = { + .left = 16, + .top = 6, + .width = 1024, + .height = 768, + }, + .htot = 1896, + .vblank_def = 312, + }, + .csi2_timings = { + .analog_crop = { + .left = 0, + .top = 4, + .width = OV5640_NATIVE_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + }, + .crop = { + .left = 16, + .top = 6, + .width = 1024, + .height = 768, + }, + .htot = 1896, + .vblank_def = 918, + }, + .reg_data = ov5640_setting_low_res, + .reg_data_size = ARRAY_SIZE(ov5640_setting_low_res), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 1280x720 */ + .id = OV5640_MODE_720P_1280_720, + .dn_mode = SUBSAMPLING, + .pixel_rate = OV5640_PIXEL_RATE_124M, + .width = 1280, + .height = 720, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 250, + .width = 2624, + .height = 1456, + }, + .crop = { + .left = 16, + .top = 4, + .width = 1280, + .height = 720, + }, + .htot = 1892, + .vblank_def = 20, + }, + .csi2_timings = { + .analog_crop = { + .left = 0, + .top = 250, + .width = 2624, + .height = 1456, + }, + .crop = { + .left = 16, + .top = 4, + .width = 1280, + .height = 720, + }, + .htot = 1600, + .vblank_def = 560, + }, + .reg_data = ov5640_setting_720P_1280_720, + .reg_data_size = ARRAY_SIZE(ov5640_setting_720P_1280_720), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 1920x1080 */ + .id = OV5640_MODE_1080P_1920_1080, + .dn_mode = SCALING, + .pixel_rate = OV5640_PIXEL_RATE_148M, + .width = 1920, + .height = 1080, + .dvp_timings = { + .analog_crop = { + .left = 336, + .top = 434, + .width = 1952, + .height = 1088, + }, + .crop = { + .left = 16, + .top = 4, + .width = 1920, + .height = 1080, + }, + .htot = 2500, + .vblank_def = 40, + }, + .csi2_timings = { + /* Crop the full valid pixel array in the center. */ + .analog_crop = { + .left = 336, + .top = 434, + .width = 1952, + .height = 1088, + }, + /* Maintain a larger processing margins. */ + .crop = { + .left = 16, + .top = 4, + .width = 1920, + .height = 1080, + }, + .htot = 2234, + .vblank_def = 24, + }, + .reg_data = ov5640_setting_1080P_1920_1080, + .reg_data_size = ARRAY_SIZE(ov5640_setting_1080P_1920_1080), + .max_fps = OV5640_30_FPS, + .def_fps = OV5640_30_FPS + }, { + /* 2592x1944 */ + .id = OV5640_MODE_QSXGA_2592_1944, + .dn_mode = SCALING, + .pixel_rate = OV5640_PIXEL_RATE_168M, + .width = OV5640_PIXEL_ARRAY_WIDTH, + .height = OV5640_PIXEL_ARRAY_HEIGHT, + .dvp_timings = { + .analog_crop = { + .left = 0, + .top = 0, + .width = 2624, + .height = 1952, + }, + .crop = { + .left = 16, + .top = 4, + .width = 2592, + .height = 1944, + }, + .htot = 2844, + .vblank_def = 24, + }, + .csi2_timings = { + /* Give more processing margin to full resolution. */ + .analog_crop = { + .left = 0, + .top = 0, + .width = OV5640_NATIVE_WIDTH, + .height = 1952, + }, + .crop = { + .left = 16, + .top = 4, + .width = 2592, + .height = 1944, + }, + .htot = 2844, + .vblank_def = 24, + }, + .reg_data = ov5640_setting_QSXGA_2592_1944, + .reg_data_size = ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944), + .max_fps = OV5640_15_FPS, + .def_fps = OV5640_15_FPS + }, }; -static const struct ov5640_mode_info -ov5640_mode_data[OV5640_NUM_MODES] = { - {OV5640_MODE_QQVGA_160_120, SUBSAMPLING, - 160, 1896, 120, 984, - ov5640_setting_QQVGA_160_120, - ARRAY_SIZE(ov5640_setting_QQVGA_160_120), - OV5640_30_FPS}, - {OV5640_MODE_QCIF_176_144, SUBSAMPLING, - 176, 1896, 144, 984, - ov5640_setting_QCIF_176_144, - ARRAY_SIZE(ov5640_setting_QCIF_176_144), - OV5640_30_FPS}, - {OV5640_MODE_QVGA_320_240, SUBSAMPLING, - 320, 1896, 240, 984, - ov5640_setting_QVGA_320_240, - ARRAY_SIZE(ov5640_setting_QVGA_320_240), - OV5640_30_FPS}, - {OV5640_MODE_VGA_640_480, SUBSAMPLING, - 640, 1896, 480, 1080, - ov5640_setting_VGA_640_480, - ARRAY_SIZE(ov5640_setting_VGA_640_480), - OV5640_60_FPS}, - {OV5640_MODE_NTSC_720_480, SUBSAMPLING, - 720, 1896, 480, 984, - ov5640_setting_NTSC_720_480, - ARRAY_SIZE(ov5640_setting_NTSC_720_480), - OV5640_30_FPS}, - {OV5640_MODE_PAL_720_576, SUBSAMPLING, - 720, 1896, 576, 984, - ov5640_setting_PAL_720_576, - ARRAY_SIZE(ov5640_setting_PAL_720_576), - OV5640_30_FPS}, - {OV5640_MODE_XGA_1024_768, SUBSAMPLING, - 1024, 1896, 768, 1080, - ov5640_setting_XGA_1024_768, - ARRAY_SIZE(ov5640_setting_XGA_1024_768), - OV5640_30_FPS}, - {OV5640_MODE_720P_1280_720, SUBSAMPLING, - 1280, 1892, 720, 740, - ov5640_setting_720P_1280_720, - ARRAY_SIZE(ov5640_setting_720P_1280_720), - OV5640_30_FPS}, - {OV5640_MODE_1080P_1920_1080, SCALING, - 1920, 2500, 1080, 1120, - ov5640_setting_1080P_1920_1080, - ARRAY_SIZE(ov5640_setting_1080P_1920_1080), - OV5640_30_FPS}, - {OV5640_MODE_QSXGA_2592_1944, SCALING, - 2592, 2844, 1944, 1968, - ov5640_setting_QSXGA_2592_1944, - ARRAY_SIZE(ov5640_setting_QSXGA_2592_1944), - OV5640_15_FPS}, -}; +static const struct ov5640_timings * +ov5640_timings(const struct ov5640_dev *sensor, + const struct ov5640_mode_info *mode) +{ + if (ov5640_is_csi2(sensor)) + return &mode->csi2_timings; + + return &mode->dvp_timings; +} static int ov5640_init_slave_id(struct ov5640_dev *sensor) { @@ -797,20 +1309,10 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, * +-----+-----+ * +------------> PCLK * - * This is deviating from the datasheet at least for the register - * 0x3108, since it's said here that the PCLK would be clocked from - * the PLL. - * - * There seems to be also (unverified) constraints: + * There seems to be also constraints: * - the PLL pre-divider output rate should be in the 4-27MHz range * - the PLL multiplier output rate should be in the 500-1000MHz range * - PCLK >= SCLK * 2 in YUV, >= SCLK in Raw or JPEG - * - * In the two latter cases, these constraints are met since our - * factors are hardcoded. If we were to change that, we would need to - * take this into account. The only varying parts are the PLL - * multiplier and the system clock divider, which are shared between - * all these clocks so won't cause any issue. */ /* @@ -830,13 +1332,6 @@ static int ov5640_mod_reg(struct ov5640_dev *sensor, u16 reg, #define OV5640_SYSDIV_MAX 16 /* - * Hardcode these values for scaler and non-scaler modes. - * FIXME: to be re-calcualted for 1 data lanes setups - */ -#define OV5640_MIPI_DIV_PCLK 2 -#define OV5640_MIPI_DIV_SCLK 1 - -/* * This is supposed to be ranging from 1 to 2, but the value is always * set to 2 in the vendor kernels. */ @@ -945,70 +1440,83 @@ out: /* * ov5640_set_mipi_pclk() - Calculate the clock tree configuration values * for the MIPI CSI-2 output. - * - * @rate: The requested bandwidth per lane in bytes per second. - * 'Bandwidth Per Lane' is calculated as: - * bpl = HTOT * VTOT * FPS * bpp / num_lanes; - * - * This function use the requested bandwidth to calculate: - * - sample_rate = bpl / (bpp / num_lanes); - * = bpl / (PLL_RDIV * BIT_DIV * PCLK_DIV * MIPI_DIV / num_lanes); - * - * - mipi_sclk = bpl / MIPI_DIV / 2; ( / 2 is for CSI-2 DDR) - * - * with these fixed parameters: - * PLL_RDIV = 2; - * BIT_DIVIDER = 2; (MIPI_BIT_MODE == 8 ? 2 : 2,5); - * PCLK_DIV = 1; - * - * The MIPI clock generation differs for modes that use the scaler and modes - * that do not. In case the scaler is in use, the MIPI_SCLK generates the MIPI - * BIT CLk, and thus: - * - * - mipi_sclk = bpl / MIPI_DIV / 2; - * MIPI_DIV = 1; - * - * For modes that do not go through the scaler, the MIPI BIT CLOCK is generated - * from the pixel clock, and thus: - * - * - sample_rate = bpl / (bpp / num_lanes); - * = bpl / (2 * 2 * 1 * MIPI_DIV / num_lanes); - * = bpl / (4 * MIPI_DIV / num_lanes); - * - MIPI_DIV = bpp / (4 * num_lanes); - * - * FIXME: this have been tested with 16bpp and 2 lanes setup only. - * MIPI_DIV is fixed to value 2, but it -might- be changed according to the - * above formula for setups with 1 lane or image formats with different bpp. - * - * FIXME: this deviates from the sensor manual documentation which is quite - * thin on the MIPI clock tree generation part. */ -static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, - unsigned long rate) +static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor) { - const struct ov5640_mode_info *mode = sensor->current_mode; + u8 bit_div, mipi_div, pclk_div, sclk_div, sclk2x_div, root_div; u8 prediv, mult, sysdiv; - u8 mipi_div; + unsigned long link_freq; + unsigned long sysclk; + u8 pclk_period; + u32 sample_rate; + u32 num_lanes; int ret; + /* Use the link freq computed at ov5640_update_pixel_rate() time. */ + link_freq = sensor->current_link_freq; + /* - * 1280x720 is reported to use 'SUBSAMPLING' only, - * but according to the sensor manual it goes through the - * scaler before subsampling. + * - mipi_div - Additional divider for the MIPI lane clock. + * + * Higher link frequencies would make sysclk > 1GHz. + * Keep the sysclk low and do not divide in the MIPI domain. */ - if (mode->dn_mode == SCALING || - (mode->id == OV5640_MODE_720P_1280_720)) - mipi_div = OV5640_MIPI_DIV_SCLK; + if (link_freq > OV5640_LINK_RATE_MAX) + mipi_div = 1; else - mipi_div = OV5640_MIPI_DIV_PCLK; + mipi_div = 2; - ov5640_calc_sys_clk(sensor, rate, &prediv, &mult, &sysdiv); + sysclk = link_freq * mipi_div; + ov5640_calc_sys_clk(sensor, sysclk, &prediv, &mult, &sysdiv); - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, - 0x0f, OV5640_PLL_CTRL0_MIPI_MODE_8BIT); + /* + * Adjust PLL parameters to maintain the MIPI_SCLK-to-PCLK ratio. + * + * - root_div = 2 (fixed) + * - bit_div : MIPI 8-bit = 2; MIPI 10-bit = 2.5 + * - pclk_div = 1 (fixed) + * - p_div = (2 lanes ? mipi_div : 2 * mipi_div) + * + * This results in the following MIPI_SCLK depending on the number + * of lanes: + * + * - 2 lanes: MIPI_SCLK = (4 or 5) * PCLK + * - 1 lanes: MIPI_SCLK = (8 or 10) * PCLK + */ + root_div = OV5640_PLL_CTRL3_PLL_ROOT_DIV_2; + bit_div = OV5640_PLL_CTRL0_MIPI_MODE_8BIT; + pclk_div = ilog2(OV5640_PCLK_ROOT_DIV); - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, - 0xff, sysdiv << 4 | mipi_div); + /* + * Scaler clock: + * - YUV: PCLK >= 2 * SCLK + * - RAW or JPEG: PCLK >= SCLK + * - sclk2x_div = sclk_div / 2 + */ + sclk_div = ilog2(OV5640_SCLK_ROOT_DIV); + sclk2x_div = ilog2(OV5640_SCLK2X_ROOT_DIV); + + /* + * Set the pixel clock period expressed in ns with 1-bit decimal + * (0x01=0.5ns). + * + * The register is very briefly documented. In the OV5645 datasheet it + * is described as (2 * pclk period), and from testing it seems the + * actual definition is 2 * 8-bit sample period. + * + * 2 * sample_period = (mipi_clk * 2 * num_lanes / bpp) * (bpp / 8) / 2 + */ + num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes; + sample_rate = (link_freq * mipi_div * num_lanes * 2) / 16; + pclk_period = 2000000000UL / sample_rate; + + /* Program the clock tree registers. */ + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0, 0x0f, bit_div); + if (ret) + return ret; + + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL1, 0xff, + (sysdiv << 4) | mipi_div); if (ret) return ret; @@ -1016,13 +1524,29 @@ static int ov5640_set_mipi_pclk(struct ov5640_dev *sensor, if (ret) return ret; - ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, - 0x1f, OV5640_PLL_CTRL3_PLL_ROOT_DIV_2 | prediv); + ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL3, 0x1f, + root_div | prediv); if (ret) return ret; - return ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, - 0x30, OV5640_PLL_SYS_ROOT_DIVIDER_BYPASS); + ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, + (pclk_div << 4) | (sclk2x_div << 2) | sclk_div); + if (ret) + return ret; + + return ov5640_write_reg(sensor, OV5640_REG_PCLK_PERIOD, pclk_period); +} + +static u32 ov5640_calc_pixel_rate(struct ov5640_dev *sensor) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_timings *timings = &mode->dvp_timings; + u32 rate; + + rate = timings->htot * (timings->crop.height + timings->vblank_def); + rate *= ov5640_framerates[sensor->current_fr]; + + return rate; } static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor, @@ -1042,11 +1566,16 @@ static unsigned long ov5640_calc_pclk(struct ov5640_dev *sensor, return _rate / *pll_rdiv / *bit_div / *pclk_div; } -static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate) +static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor) { u8 prediv, mult, sysdiv, pll_rdiv, bit_div, pclk_div; + u32 rate; int ret; + rate = ov5640_calc_pixel_rate(sensor); + rate *= ov5640_code_to_bpp(sensor, sensor->fmt.code); + rate /= sensor->ep.bus.parallel.bus_width; + ov5640_calc_pclk(sensor, rate, &prediv, &mult, &sysdiv, &pll_rdiv, &bit_div, &pclk_div); @@ -1098,17 +1627,20 @@ static int ov5640_set_jpeg_timings(struct ov5640_dev *sensor, if (ret < 0) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->hact); + ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE, mode->width); if (ret < 0) return ret; - return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->vact); + return ov5640_write_reg16(sensor, OV5640_REG_VFIFO_VSIZE, mode->height); } /* download ov5640 settings to sensor through i2c */ static int ov5640_set_timings(struct ov5640_dev *sensor, const struct ov5640_mode_info *mode) { + const struct ov5640_timings *timings; + const struct v4l2_rect *analog_crop; + const struct v4l2_rect *crop; int ret; if (sensor->fmt.code == MEDIA_BUS_FMT_JPEG_1X8) { @@ -1117,32 +1649,68 @@ static int ov5640_set_timings(struct ov5640_dev *sensor, return ret; } - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->hact); + timings = ov5640_timings(sensor, mode); + analog_crop = &timings->analog_crop; + crop = &timings->crop; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS, + analog_crop->left); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VS, + analog_crop->top); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HW, + analog_crop->left + analog_crop->width - 1); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VH, + analog_crop->top + analog_crop->height - 1); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HOFFS, crop->left); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VOFFS, crop->top); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, mode->width); + if (ret < 0) + return ret; + + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->height); if (ret < 0) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->vact); + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, timings->htot); if (ret < 0) return ret; - ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot); + ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, + mode->height + timings->vblank_def); if (ret < 0) return ret; - return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, mode->vtot); + return 0; } -static int ov5640_load_regs(struct ov5640_dev *sensor, - const struct ov5640_mode_info *mode) +static void ov5640_load_regs(struct ov5640_dev *sensor, + const struct reg_value *regs, unsigned int regnum) { - const struct reg_value *regs = mode->reg_data; unsigned int i; u32 delay_ms; u16 reg_addr; u8 mask, val; int ret = 0; - for (i = 0; i < mode->reg_data_size; ++i, ++regs) { + for (i = 0; i < regnum; ++i, ++regs) { delay_ms = regs->delay_ms; reg_addr = regs->reg_addr; val = regs->val; @@ -1151,7 +1719,7 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, /* remain in power down mode for DVP */ if (regs->reg_addr == OV5640_REG_SYS_CTRL0 && val == OV5640_REG_SYS_CTRL0_SW_PWUP && - sensor->ep.bus_type != V4L2_MBUS_CSI2_DPHY) + !ov5640_is_csi2(sensor)) continue; if (mask) @@ -1164,8 +1732,6 @@ static int ov5640_load_regs(struct ov5640_dev *sensor, if (delay_ms) usleep_range(1000 * delay_ms, 1000 * delay_ms + 100); } - - return ov5640_set_timings(sensor, mode); } static int ov5640_set_autoexposure(struct ov5640_dev *sensor, bool on) @@ -1550,37 +2116,22 @@ static int ov5640_set_virtual_channel(struct ov5640_dev *sensor) } static const struct ov5640_mode_info * -ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr, - int width, int height, bool nearest) +ov5640_find_mode(struct ov5640_dev *sensor, int width, int height, bool nearest) { const struct ov5640_mode_info *mode; mode = v4l2_find_nearest_size(ov5640_mode_data, ARRAY_SIZE(ov5640_mode_data), - hact, vact, - width, height); + width, height, width, height); if (!mode || - (!nearest && (mode->hact != width || mode->vact != height))) - return NULL; - - /* Check to see if the current mode exceeds the max frame rate */ - if (ov5640_framerates[fr] > ov5640_framerates[mode->max_fps]) + (!nearest && + (mode->width != width || mode->height != height))) return NULL; return mode; } -static u64 ov5640_calc_pixel_rate(struct ov5640_dev *sensor) -{ - u64 rate; - - rate = sensor->current_mode->vtot * sensor->current_mode->htot; - rate *= ov5640_framerates[sensor->current_fr]; - - return rate; -} - /* * sensor changes between scaling and subsampling, go through * exposure calculation @@ -1628,7 +2179,8 @@ static int ov5640_set_mode_exposure_calc(struct ov5640_dev *sensor, return ret; /* Write capture setting */ - ret = ov5640_load_regs(sensor, mode); + ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size); + ret = ov5640_set_timings(sensor, mode); if (ret < 0) return ret; @@ -1752,7 +2304,8 @@ static int ov5640_set_mode_direct(struct ov5640_dev *sensor, return -EINVAL; /* Write capture setting */ - return ov5640_load_regs(sensor, mode); + ov5640_load_regs(sensor, mode->reg_data, mode->reg_data_size); + return ov5640_set_timings(sensor, mode); } static int ov5640_set_mode(struct ov5640_dev *sensor) @@ -1762,7 +2315,6 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) enum ov5640_downsize_mode dn_mode, orig_dn_mode; bool auto_gain = sensor->ctrls.auto_gain->val == 1; bool auto_exp = sensor->ctrls.auto_exp->val == V4L2_EXPOSURE_AUTO; - unsigned long rate; int ret; dn_mode = mode->dn_mode; @@ -1781,19 +2333,10 @@ static int ov5640_set_mode(struct ov5640_dev *sensor) goto restore_auto_gain; } - /* - * All the formats we support have 16 bits per pixel, seems to require - * the same rate than YUV, so we can just use 16 bpp all the time. - */ - rate = ov5640_calc_pixel_rate(sensor) * 16; - if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) { - rate = rate / sensor->ep.bus.mipi_csi2.num_data_lanes; - ret = ov5640_set_mipi_pclk(sensor, rate); - } else { - rate = rate / sensor->ep.bus.parallel.bus_width; - ret = ov5640_set_dvp_pclk(sensor, rate); - } - + if (ov5640_is_csi2(sensor)) + ret = ov5640_set_mipi_pclk(sensor); + else + ret = ov5640_set_dvp_pclk(sensor); if (ret < 0) return 0; @@ -1860,10 +2403,8 @@ static int ov5640_restore_mode(struct ov5640_dev *sensor) int ret; /* first load the initial register values */ - ret = ov5640_load_regs(sensor, &ov5640_mode_init_data); - if (ret < 0) - return ret; - sensor->last_mode = &ov5640_mode_init_data; + ov5640_load_regs(sensor, ov5640_init_setting, + ARRAY_SIZE(ov5640_init_setting)); ret = ov5640_mod_reg(sensor, OV5640_REG_SYS_ROOT_DIVIDER, 0x3f, (ilog2(OV5640_SCLK2X_ROOT_DIV) << 2) | @@ -2224,7 +2765,7 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor, fi->denominator = best_fps; find_mode: - mode = ov5640_find_mode(sensor, rate, width, height, false); + mode = ov5640_find_mode(sensor, width, height, false); return mode ? rate : -EINVAL; } @@ -2260,25 +2801,34 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, { struct ov5640_dev *sensor = to_ov5640_dev(sd); const struct ov5640_mode_info *mode; - int i; + const struct ov5640_pixfmt *pixfmt; + unsigned int bpp; - mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true); + mode = ov5640_find_mode(sensor, fmt->width, fmt->height, true); if (!mode) return -EINVAL; - fmt->width = mode->hact; - fmt->height = mode->vact; + + pixfmt = ov5640_code_to_pixfmt(sensor, fmt->code); + bpp = pixfmt->bpp; + + /* + * Adjust mode according to bpp: + * - 8bpp modes work for resolution >= 1280x720 + * - 24bpp modes work resolution < 1280x720 + */ + if (bpp == 8 && mode->width < 1280) + mode = &ov5640_mode_data[OV5640_MODE_720P_1280_720]; + else if (bpp == 24 && mode->width > 1024) + mode = &ov5640_mode_data[OV5640_MODE_XGA_1024_768]; + + fmt->width = mode->width; + fmt->height = mode->height; if (new_mode) *new_mode = mode; - for (i = 0; i < ARRAY_SIZE(ov5640_formats); i++) - if (ov5640_formats[i].code == fmt->code) - break; - if (i >= ARRAY_SIZE(ov5640_formats)) - i = 0; - - fmt->code = ov5640_formats[i].code; - fmt->colorspace = ov5640_formats[i].colorspace; + fmt->code = pixfmt->code; + fmt->colorspace = pixfmt->colorspace; fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); @@ -2286,6 +2836,107 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd, return 0; } +static int ov5640_update_pixel_rate(struct ov5640_dev *sensor) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + enum ov5640_pixel_rate_id pixel_rate_id = mode->pixel_rate; + struct v4l2_mbus_framefmt *fmt = &sensor->fmt; + const struct ov5640_timings *timings; + s32 exposure_val, exposure_max; + unsigned int hblank; + unsigned int i = 0; + u32 pixel_rate; + s64 link_freq; + u32 num_lanes; + u32 vblank; + u32 bpp; + + /* + * Update the pixel rate control value. + * + * For DVP mode, maintain the pixel rate calculation using fixed FPS. + */ + if (!ov5640_is_csi2(sensor)) { + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, + ov5640_calc_pixel_rate(sensor)); + + return 0; + } + + /* + * The MIPI CSI-2 link frequency should comply with the CSI-2 + * specification and be lower than 1GHz. + * + * Start from the suggested pixel_rate for the current mode and + * progressively slow it down if it exceeds 1GHz. + */ + num_lanes = sensor->ep.bus.mipi_csi2.num_data_lanes; + bpp = ov5640_code_to_bpp(sensor, fmt->code); + do { + pixel_rate = ov5640_pixel_rates[pixel_rate_id]; + link_freq = pixel_rate * bpp / (2 * num_lanes); + } while (link_freq >= 1000000000U && + ++pixel_rate_id < OV5640_NUM_PIXEL_RATES); + + sensor->current_link_freq = link_freq; + + /* + * Higher link rates require the clock tree to be programmed with + * 'mipi_div' = 1; this has the effect of halving the actual output + * pixel rate in the MIPI domain. + * + * Adjust the pixel rate and link frequency control value to report it + * correctly to userspace. + */ + if (link_freq > OV5640_LINK_RATE_MAX) { + pixel_rate /= 2; + link_freq /= 2; + } + + for (i = 0; i < ARRAY_SIZE(ov5640_csi2_link_freqs); ++i) { + if (ov5640_csi2_link_freqs[i] == link_freq) + break; + } + WARN_ON(i == ARRAY_SIZE(ov5640_csi2_link_freqs)); + + __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, pixel_rate); + __v4l2_ctrl_s_ctrl(sensor->ctrls.link_freq, i); + + timings = ov5640_timings(sensor, mode); + hblank = timings->htot - mode->width; + __v4l2_ctrl_modify_range(sensor->ctrls.hblank, + hblank, hblank, 1, hblank); + + vblank = timings->vblank_def; + + if (sensor->current_fr != mode->def_fps) { + /* + * Compute the vertical blanking according to the framerate + * configured with s_frame_interval. + */ + int fie_num = sensor->frame_interval.numerator; + int fie_denom = sensor->frame_interval.denominator; + + vblank = ((fie_num * pixel_rate / fie_denom) / timings->htot) - + mode->height; + } + + __v4l2_ctrl_modify_range(sensor->ctrls.vblank, OV5640_MIN_VBLANK, + OV5640_MAX_VTS - mode->height, 1, vblank); + __v4l2_ctrl_s_ctrl(sensor->ctrls.vblank, vblank); + + exposure_max = timings->crop.height + vblank - 4; + exposure_val = clamp_t(s32, sensor->ctrls.exposure->val, + sensor->ctrls.exposure->minimum, + exposure_max); + + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, + sensor->ctrls.exposure->minimum, + exposure_max, 1, exposure_val); + + return 0; +} + static int ov5640_set_fmt(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_format *format) @@ -2316,6 +2967,7 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, } if (new_mode != sensor->current_mode) { + sensor->current_fr = new_mode->def_fps; sensor->current_mode = new_mode; sensor->pending_mode_change = true; } @@ -2325,80 +2977,70 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd, /* update format even if code is unchanged, resolution might change */ sensor->fmt = *mbus_fmt; - __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, - ov5640_calc_pixel_rate(sensor)); + ov5640_update_pixel_rate(sensor); + out: mutex_unlock(&sensor->lock); return ret; } +static int ov5640_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_state *sd_state, + struct v4l2_subdev_selection *sel) +{ + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_timings *timings; + + switch (sel->target) { + case V4L2_SEL_TGT_CROP: { + mutex_lock(&sensor->lock); + timings = ov5640_timings(sensor, mode); + sel->r = timings->analog_crop; + mutex_unlock(&sensor->lock); + + return 0; + } + + case V4L2_SEL_TGT_NATIVE_SIZE: + case V4L2_SEL_TGT_CROP_BOUNDS: + sel->r.top = 0; + sel->r.left = 0; + sel->r.width = OV5640_NATIVE_WIDTH; + sel->r.height = OV5640_NATIVE_HEIGHT; + + return 0; + + case V4L2_SEL_TGT_CROP_DEFAULT: + sel->r.top = OV5640_PIXEL_ARRAY_TOP; + sel->r.left = OV5640_PIXEL_ARRAY_LEFT; + sel->r.width = OV5640_PIXEL_ARRAY_WIDTH; + sel->r.height = OV5640_PIXEL_ARRAY_HEIGHT; + + return 0; + } + + return -EINVAL; +} + static int ov5640_set_framefmt(struct ov5640_dev *sensor, struct v4l2_mbus_framefmt *format) { + bool is_jpeg = format->code == MEDIA_BUS_FMT_JPEG_1X8; + const struct ov5640_pixfmt *pixfmt; int ret = 0; - bool is_jpeg = false; - u8 fmt, mux; - switch (format->code) { - case MEDIA_BUS_FMT_UYVY8_1X16: - case MEDIA_BUS_FMT_UYVY8_2X8: - /* YUV422, UYVY */ - fmt = 0x3f; - mux = OV5640_FMT_MUX_YUV422; - break; - case MEDIA_BUS_FMT_YUYV8_1X16: - case MEDIA_BUS_FMT_YUYV8_2X8: - /* YUV422, YUYV */ - fmt = 0x30; - mux = OV5640_FMT_MUX_YUV422; - break; - case MEDIA_BUS_FMT_RGB565_2X8_LE: - /* RGB565 {g[2:0],b[4:0]},{r[4:0],g[5:3]} */ - fmt = 0x6F; - mux = OV5640_FMT_MUX_RGB; - break; - case MEDIA_BUS_FMT_RGB565_2X8_BE: - /* RGB565 {r[4:0],g[5:3]},{g[2:0],b[4:0]} */ - fmt = 0x61; - mux = OV5640_FMT_MUX_RGB; - break; - case MEDIA_BUS_FMT_JPEG_1X8: - /* YUV422, YUYV */ - fmt = 0x30; - mux = OV5640_FMT_MUX_YUV422; - is_jpeg = true; - break; - case MEDIA_BUS_FMT_SBGGR8_1X8: - /* Raw, BGBG... / GRGR... */ - fmt = 0x00; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - case MEDIA_BUS_FMT_SGBRG8_1X8: - /* Raw bayer, GBGB... / RGRG... */ - fmt = 0x01; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - case MEDIA_BUS_FMT_SGRBG8_1X8: - /* Raw bayer, GRGR... / BGBG... */ - fmt = 0x02; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - case MEDIA_BUS_FMT_SRGGB8_1X8: - /* Raw bayer, RGRG... / GBGB... */ - fmt = 0x03; - mux = OV5640_FMT_MUX_RAW_DPC; - break; - default: - return -EINVAL; - } + pixfmt = ov5640_code_to_pixfmt(sensor, format->code); /* FORMAT CONTROL00: YUV and RGB formatting */ - ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, fmt); + ret = ov5640_write_reg(sensor, OV5640_REG_FORMAT_CONTROL00, + pixfmt->ctrl00); if (ret) return ret; /* FORMAT MUX CONTROL: ISP YUV or RGB */ - ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, mux); + ret = ov5640_write_reg(sensor, OV5640_REG_ISP_FORMAT_MUX_CTRL, + pixfmt->mux); if (ret) return ret; @@ -2655,6 +3297,15 @@ static int ov5640_set_ctrl_vflip(struct ov5640_dev *sensor, int value) (BIT(2) | BIT(1)) : 0); } +static int ov5640_set_ctrl_vblank(struct ov5640_dev *sensor, int value) +{ + const struct ov5640_mode_info *mode = sensor->current_mode; + + /* Update the VTOT timing register value. */ + return ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS, + mode->height + value); +} + static int ov5640_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); @@ -2685,10 +3336,25 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = ctrl_to_sd(ctrl); struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_mode_info *mode = sensor->current_mode; + const struct ov5640_timings *timings; + unsigned int exp_max; int ret; /* v4l2_ctrl_lock() locks our own mutex */ + switch (ctrl->id) { + case V4L2_CID_VBLANK: + /* Update the exposure range to the newly programmed vblank. */ + timings = ov5640_timings(sensor, mode); + exp_max = mode->height + ctrl->val - 4; + __v4l2_ctrl_modify_range(sensor->ctrls.exposure, + sensor->ctrls.exposure->minimum, + exp_max, sensor->ctrls.exposure->step, + timings->vblank_def); + break; + } + /* * If the device is not powered up by the host driver do * not apply any controls to H/W at this time. Instead @@ -2728,6 +3394,9 @@ static int ov5640_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_VFLIP: ret = ov5640_set_ctrl_vflip(sensor, ctrl->val); break; + case V4L2_CID_VBLANK: + ret = ov5640_set_ctrl_vblank(sensor, ctrl->val); + break; default: ret = -EINVAL; break; @@ -2743,9 +3412,14 @@ static const struct v4l2_ctrl_ops ov5640_ctrl_ops = { static int ov5640_init_controls(struct ov5640_dev *sensor) { + const struct ov5640_mode_info *mode = sensor->current_mode; const struct v4l2_ctrl_ops *ops = &ov5640_ctrl_ops; struct ov5640_ctrls *ctrls = &sensor->ctrls; struct v4l2_ctrl_handler *hdl = &ctrls->handler; + struct v4l2_fwnode_device_properties props; + const struct ov5640_timings *timings; + unsigned int max_vblank; + unsigned int hblank; int ret; v4l2_ctrl_handler_init(hdl, 32); @@ -2755,8 +3429,25 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) /* Clock related controls */ ctrls->pixel_rate = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_PIXEL_RATE, - 0, INT_MAX, 1, - ov5640_calc_pixel_rate(sensor)); + ov5640_pixel_rates[OV5640_NUM_PIXEL_RATES - 1], + ov5640_pixel_rates[0], 1, + ov5640_pixel_rates[mode->pixel_rate]); + + ctrls->link_freq = v4l2_ctrl_new_int_menu(hdl, ops, + V4L2_CID_LINK_FREQ, + ARRAY_SIZE(ov5640_csi2_link_freqs) - 1, + OV5640_DEFAULT_LINK_FREQ, + ov5640_csi2_link_freqs); + + timings = ov5640_timings(sensor, mode); + hblank = timings->htot - mode->width; + ctrls->hblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_HBLANK, hblank, + hblank, 1, hblank); + + max_vblank = OV5640_MAX_VTS - mode->height; + ctrls->vblank = v4l2_ctrl_new_std(hdl, ops, V4L2_CID_VBLANK, + OV5640_MIN_VBLANK, max_vblank, + 1, timings->vblank_def); /* Auto/manual white balance */ ctrls->auto_wb = v4l2_ctrl_new_std(hdl, ops, @@ -2805,7 +3496,20 @@ static int ov5640_init_controls(struct ov5640_dev *sensor) goto free_ctrls; } + ret = v4l2_fwnode_device_parse(&sensor->i2c_client->dev, &props); + if (ret) + goto free_ctrls; + + if (props.rotation == 180) + sensor->upside_down = true; + + ret = v4l2_ctrl_new_fwnode_properties(hdl, ops, &props); + if (ret) + goto free_ctrls; + ctrls->pixel_rate->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->link_freq->flags |= V4L2_CTRL_FLAG_READ_ONLY; + ctrls->hblank->flags |= V4L2_CTRL_FLAG_READ_ONLY; ctrls->gain->flags |= V4L2_CTRL_FLAG_VOLATILE; ctrls->exposure->flags |= V4L2_CTRL_FLAG_VOLATILE; @@ -2825,16 +3529,29 @@ static int ov5640_enum_frame_size(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_frame_size_enum *fse) { + struct ov5640_dev *sensor = to_ov5640_dev(sd); + u32 bpp = ov5640_code_to_bpp(sensor, fse->code); + unsigned int index = fse->index; + if (fse->pad != 0) return -EINVAL; - if (fse->index >= OV5640_NUM_MODES) + if (!bpp) + return -EINVAL; + + /* Only low-resolution modes are supported for 24bpp formats. */ + if (bpp == 24 && index >= OV5640_MODE_720P_1280_720) + return -EINVAL; + + /* FIXME: Low resolution modes don't work in 8bpp formats. */ + if (bpp == 8) + index += OV5640_MODE_720P_1280_720; + + if (index >= OV5640_NUM_MODES) return -EINVAL; - fse->min_width = - ov5640_mode_data[fse->index].hact; + fse->min_width = ov5640_mode_data[index].width; fse->max_width = fse->min_width; - fse->min_height = - ov5640_mode_data[fse->index].vact; + fse->min_height = ov5640_mode_data[index].height; fse->max_height = fse->min_height; return 0; @@ -2898,20 +3615,25 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, mode = sensor->current_mode; frame_rate = ov5640_try_frame_interval(sensor, &fi->interval, - mode->hact, mode->vact); + mode->width, + mode->height); if (frame_rate < 0) { /* Always return a valid frame interval value */ fi->interval = sensor->frame_interval; goto out; } - mode = ov5640_find_mode(sensor, frame_rate, mode->hact, - mode->vact, true); + mode = ov5640_find_mode(sensor, mode->width, mode->height, true); if (!mode) { ret = -EINVAL; goto out; } + if (ov5640_framerates[frame_rate] > ov5640_framerates[mode->max_fps]) { + ret = -EINVAL; + goto out; + } + if (mode != sensor->current_mode || frame_rate != sensor->current_fr) { sensor->current_fr = frame_rate; @@ -2919,8 +3641,7 @@ static int ov5640_s_frame_interval(struct v4l2_subdev *sd, sensor->current_mode = mode; sensor->pending_mode_change = true; - __v4l2_ctrl_s_ctrl_int64(sensor->ctrls.pixel_rate, - ov5640_calc_pixel_rate(sensor)); + ov5640_update_pixel_rate(sensor); } out: mutex_unlock(&sensor->lock); @@ -2931,12 +3652,23 @@ static int ov5640_enum_mbus_code(struct v4l2_subdev *sd, struct v4l2_subdev_state *sd_state, struct v4l2_subdev_mbus_code_enum *code) { - if (code->pad != 0) - return -EINVAL; - if (code->index >= ARRAY_SIZE(ov5640_formats)) + struct ov5640_dev *sensor = to_ov5640_dev(sd); + const struct ov5640_pixfmt *formats; + unsigned int num_formats; + + if (ov5640_is_csi2(sensor)) { + formats = ov5640_csi2_formats; + num_formats = ARRAY_SIZE(ov5640_csi2_formats) - 1; + } else { + formats = ov5640_dvp_formats; + num_formats = ARRAY_SIZE(ov5640_dvp_formats) - 1; + } + + if (code->index >= num_formats) return -EINVAL; - code->code = ov5640_formats[code->index].code; + code->code = formats[code->index].code; + return 0; } @@ -2961,7 +3693,7 @@ static int ov5640_s_stream(struct v4l2_subdev *sd, int enable) sensor->pending_fmt_change = false; } - if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) + if (ov5640_is_csi2(sensor)) ret = ov5640_set_stream_mipi(sensor, enable); else ret = ov5640_set_stream_dvp(sensor, enable); @@ -2974,6 +3706,23 @@ out: return ret; } +static int ov5640_init_cfg(struct v4l2_subdev *sd, + struct v4l2_subdev_state *state) +{ + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(sd, state, 0); + struct v4l2_rect *crop = v4l2_subdev_get_try_crop(sd, state, 0); + + *fmt = ov5640_default_fmt; + + crop->left = OV5640_PIXEL_ARRAY_LEFT; + crop->top = OV5640_PIXEL_ARRAY_TOP; + crop->width = OV5640_PIXEL_ARRAY_WIDTH; + crop->height = OV5640_PIXEL_ARRAY_HEIGHT; + + return 0; +} + static const struct v4l2_subdev_core_ops ov5640_core_ops = { .s_power = ov5640_s_power, .log_status = v4l2_ctrl_subdev_log_status, @@ -2988,9 +3737,11 @@ static const struct v4l2_subdev_video_ops ov5640_video_ops = { }; static const struct v4l2_subdev_pad_ops ov5640_pad_ops = { + .init_cfg = ov5640_init_cfg, .enum_mbus_code = ov5640_enum_mbus_code, .get_fmt = ov5640_get_fmt, .set_fmt = ov5640_set_fmt, + .get_selection = ov5640_get_selection, .enum_frame_size = ov5640_enum_frame_size, .enum_frame_interval = ov5640_enum_frame_interval, }; @@ -3046,8 +3797,6 @@ static int ov5640_probe(struct i2c_client *client) struct device *dev = &client->dev; struct fwnode_handle *endpoint; struct ov5640_dev *sensor; - struct v4l2_mbus_framefmt *fmt; - u32 rotation; int ret; sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL); @@ -3060,40 +3809,17 @@ static int ov5640_probe(struct i2c_client *client) * default init sequence initialize sensor to * YUV422 UYVY VGA@30fps */ - fmt = &sensor->fmt; - fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; - fmt->colorspace = V4L2_COLORSPACE_SRGB; - fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace); - fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE; - fmt->xfer_func = V4L2_MAP_XFER_FUNC_DEFAULT(fmt->colorspace); - fmt->width = 640; - fmt->height = 480; - fmt->field = V4L2_FIELD_NONE; + sensor->fmt = ov5640_default_fmt; sensor->frame_interval.numerator = 1; sensor->frame_interval.denominator = ov5640_framerates[OV5640_30_FPS]; sensor->current_fr = OV5640_30_FPS; sensor->current_mode = &ov5640_mode_data[OV5640_MODE_VGA_640_480]; sensor->last_mode = sensor->current_mode; + sensor->current_link_freq = OV5640_DEFAULT_LINK_FREQ; sensor->ae_target = 52; - /* optional indication of physical rotation of sensor */ - ret = fwnode_property_read_u32(dev_fwnode(&client->dev), "rotation", - &rotation); - if (!ret) { - switch (rotation) { - case 180: - sensor->upside_down = true; - fallthrough; - case 0: - break; - default: - dev_warn(dev, "%u degrees rotation is not supported, ignoring...\n", - rotation); - } - } - endpoint = fwnode_graph_get_next_endpoint(dev_fwnode(&client->dev), NULL); if (!endpoint) { |