#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
+
#define OV5640_DEFAULT_SLAVE_ID 0x3c
#define OV5640_LINK_RATE_MAX 490000000U
u32 delay_ms;
};
-struct ov5640_mode_info {
- enum ov5640_mode_id id;
- enum ov5640_downsize_mode dn_mode;
- enum ov5640_pixel_rate_id pixel_rate;
+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: crop.width + fixed hblank. */
+ /* Total pixels per line: width + fixed hblank. */
u32 htot;
- /* Default vertical blanking: frame height = crop.height + vblank. */
+ /* 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;
+ 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;
- /* DVP only; ignored in MIPI mode. */
+
+ /* Used by s_frame_interval only. */
u32 max_fps;
};
.id = 0,
.dn_mode = SUBSAMPLING,
.pixel_rate = OV5640_PIXEL_RATE_96M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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 = 504,
},
- .crop = {
- .left = 16,
- .top = 6,
- .width = 640,
- .height = 480,
+ .csi2_timings = {
+ .analog_crop = {
+ .left = OV5640_PIXEL_ARRAY_LEFT,
+ .top = OV5640_PIXEL_ARRAY_TOP,
+ .width = OV5640_PIXEL_ARRAY_WIDTH,
+ .height = OV5640_PIXEL_ARRAY_HEIGHT,
+ },
+ .crop = {
+ .left = 2,
+ .top = 4,
+ .width = 640,
+ .height = 480,
+ },
+ .htot = 1896,
+ .vblank_def = 504,
},
- .htot = 1896,
- .vblank_def = 504,
.reg_data = ov5640_init_setting_30fps_VGA,
.reg_data_size = ARRAY_SIZE(ov5640_init_setting_30fps_VGA),
.max_fps = OV5640_30_FPS
};
-static const struct ov5640_mode_info
-ov5640_mode_data[OV5640_NUM_MODES] = {
+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,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 6,
- .width = 160,
- .height = 120,
+ .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 = 1896,
+ .vblank_def = 864,
},
- .htot = 1896,
- .vblank_def = 864,
.reg_data = ov5640_setting_QQVGA_160_120,
.reg_data_size = ARRAY_SIZE(ov5640_setting_QQVGA_160_120),
.max_fps = OV5640_30_FPS
.id = OV5640_MODE_QCIF_176_144,
.dn_mode = SUBSAMPLING,
.pixel_rate = OV5640_PIXEL_RATE_48M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 6,
- .width = 176,
- .height = 144,
+ .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 = 1896,
+ .vblank_def = 840,
},
- .htot = 1896,
- .vblank_def = 840,
.reg_data = ov5640_setting_QCIF_176_144,
.reg_data_size = ARRAY_SIZE(ov5640_setting_QCIF_176_144),
.max_fps = OV5640_30_FPS
/* 320x240 */
.id = OV5640_MODE_QVGA_320_240,
.dn_mode = SUBSAMPLING,
+ .width = 320,
+ .height = 240,
.pixel_rate = OV5640_PIXEL_RATE_48M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 6,
- .width = 320,
- .height = 240,
+ .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 = 1896,
+ .vblank_def = 744,
},
- .htot = 1896,
- .vblank_def = 744,
.reg_data = ov5640_setting_QVGA_320_240,
.reg_data_size = ARRAY_SIZE(ov5640_setting_QVGA_320_240),
.max_fps = OV5640_30_FPS
.id = OV5640_MODE_VGA_640_480,
.dn_mode = SUBSAMPLING,
.pixel_rate = OV5640_PIXEL_RATE_48M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 6,
- .width = 640,
- .height = 480,
+ .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 = 1896,
+ .vblank_def = 600,
},
- .htot = 1896,
- .vblank_def = 600,
.reg_data = ov5640_setting_VGA_640_480,
.reg_data_size = ARRAY_SIZE(ov5640_setting_VGA_640_480),
.max_fps = OV5640_60_FPS
/* 720x480 */
.id = OV5640_MODE_NTSC_720_480,
.dn_mode = SUBSAMPLING,
+ .width = 720,
+ .height = 480,
.pixel_rate = OV5640_PIXEL_RATE_96M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 56,
- .top = 60,
- .width = 720,
- .height = 480,
+ .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 = 504,
},
- .htot = 1896,
- .vblank_def = 504,
.reg_data = ov5640_setting_NTSC_720_480,
.reg_data_size = ARRAY_SIZE(ov5640_setting_NTSC_720_480),
.max_fps = OV5640_30_FPS
/* 720x576 */
.id = OV5640_MODE_PAL_720_576,
.dn_mode = SUBSAMPLING,
+ .width = 720,
+ .height = 576,
.pixel_rate = OV5640_PIXEL_RATE_96M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 56,
- .top = 6,
- .width = 720,
- .height = 576,
+ .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 = 408,
},
- .htot = 1896,
- .vblank_def = 408,
.reg_data = ov5640_setting_PAL_720_576,
.reg_data_size = ARRAY_SIZE(ov5640_setting_PAL_720_576),
.max_fps = OV5640_30_FPS
.id = OV5640_MODE_XGA_1024_768,
.dn_mode = SUBSAMPLING,
.pixel_rate = OV5640_PIXEL_RATE_96M,
- .analog_crop = {
- .left = 0,
- .top = 4,
- .width = 2624,
- .height = 1944,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 6,
- .width = 1024,
- .height = 768,
+ .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 = 312,
},
- .htot = 1896,
- .vblank_def = 312,
.reg_data = ov5640_setting_XGA_1024_768,
.reg_data_size = ARRAY_SIZE(ov5640_setting_XGA_1024_768),
.max_fps = OV5640_30_FPS
.id = OV5640_MODE_720P_1280_720,
.dn_mode = SUBSAMPLING,
.pixel_rate = OV5640_PIXEL_RATE_124M,
- .analog_crop = {
- .left = 0,
- .top = 250,
- .width = 2624,
- .height = 1456,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 4,
- .width = 1280,
- .height = 720,
+ .csi2_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,
},
- .htot = 1892,
- .vblank_def = 20,
.reg_data = ov5640_setting_720P_1280_720,
.reg_data_size = ARRAY_SIZE(ov5640_setting_720P_1280_720),
.max_fps = OV5640_30_FPS
.id = OV5640_MODE_1080P_1920_1080,
.dn_mode = SCALING,
.pixel_rate = OV5640_PIXEL_RATE_148M,
- .analog_crop = {
- .left = 336,
- .top = 434,
- .width = 1952,
- .height = 1088,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 4,
- .width = 1920,
- .height = 1080,
+ .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 = 2500,
+ .vblank_def = 40,
},
- .htot = 2500,
- .vblank_def = 40,
.reg_data = ov5640_setting_1080P_1920_1080,
.reg_data_size = ARRAY_SIZE(ov5640_setting_1080P_1920_1080),
.max_fps = OV5640_30_FPS
.id = OV5640_MODE_QSXGA_2592_1944,
.dn_mode = SCALING,
.pixel_rate = OV5640_PIXEL_RATE_168M,
- .analog_crop = {
- .left = 0,
- .top = 0,
- .width = 2624,
- .height = 1952,
+ .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,
},
- .crop = {
- .left = 16,
- .top = 4,
- .width = 2592,
- .height = 1944,
+ .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,
},
- .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
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 = mode->htot * (mode->crop.height + mode->vblank_def);
+ rate = timings->htot * (timings->crop.height + timings->vblank_def);
rate *= ov5640_framerates[sensor->current_fr];
return rate;
if (ret < 0)
return ret;
- ret = ov5640_write_reg16(sensor, OV5640_REG_VFIFO_HSIZE,
- mode->crop.width);
+ 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->crop.height);
+ 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 v4l2_rect *analog_crop = &mode->analog_crop;
- const struct v4l2_rect *crop = &mode->crop;
+ 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) {
return ret;
}
+ if (ov5640_is_csi2(sensor))
+ timings = &mode->csi2_timings;
+ else
+ timings = &mode->dvp_timings;
+
+ analog_crop = &timings->analog_crop;
+ crop = &timings->crop;
+
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HS,
analog_crop->left);
if (ret < 0)
if (ret < 0)
return ret;
- ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPHO, crop->width);
+ 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, crop->height);
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_DVPVO, mode->height);
if (ret < 0)
return ret;
- ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, mode->htot);
+ ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_HTS, timings->htot);
if (ret < 0)
return ret;
ret = ov5640_write_reg16(sensor, OV5640_REG_TIMING_VTS,
- crop->height + mode->vblank_def);
+ mode->height + timings->vblank_def);
if (ret < 0)
return ret;
mode = v4l2_find_nearest_size(ov5640_mode_data,
ARRAY_SIZE(ov5640_mode_data),
- crop.width, crop.height, width, height);
+ width, height, width, height);
if (!mode ||
(!nearest &&
- (mode->crop.width != width || mode->crop.height != height)))
+ (mode->width != width || mode->height != height)))
return NULL;
/* Check to see if the current mode exceeds the max frame rate */
mode = ov5640_find_mode(sensor, fr, fmt->width, fmt->height, true);
if (!mode)
return -EINVAL;
- fmt->width = mode->crop.width;
- fmt->height = mode->crop.height;
+ fmt->width = mode->width;
+ fmt->height = mode->height;
if (new_mode)
*new_mode = mode;
if (fse->index >= OV5640_NUM_MODES)
return -EINVAL;
- fse->min_width = ov5640_mode_data[fse->index].crop.width;
+ fse->min_width = ov5640_mode_data[fse->index].width;
fse->max_width = fse->min_width;
- fse->min_height = ov5640_mode_data[fse->index].crop.height;
+ fse->min_height = ov5640_mode_data[fse->index].height;
fse->max_height = fse->min_height;
return 0;
mode = sensor->current_mode;
frame_rate = ov5640_try_frame_interval(sensor, &fi->interval,
- mode->crop.width,
- mode->crop.height);
+ 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->crop.width,
- mode->crop.height, true);
+ mode = ov5640_find_mode(sensor, frame_rate, mode->width,
+ mode->height, true);
if (!mode) {
ret = -EINVAL;
goto out;