summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/ov5640.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/ov5640.c')
-rw-r--r--drivers/media/i2c/ov5640.c169
1 files changed, 136 insertions, 33 deletions
diff --git a/drivers/media/i2c/ov5640.c b/drivers/media/i2c/ov5640.c
index be6c882dd1d5..730133216c9f 100644
--- a/drivers/media/i2c/ov5640.c
+++ b/drivers/media/i2c/ov5640.c
@@ -110,7 +110,8 @@ enum ov5640_mode_id {
};
enum ov5640_frame_rate {
- OV5640_15_FPS = 0,
+ OV5640_08_FPS = 0,
+ OV5640_15_FPS,
OV5640_30_FPS,
OV5640_60_FPS,
OV5640_NUM_FRAMERATES,
@@ -152,6 +153,7 @@ MODULE_PARM_DESC(virtual_channel,
"MIPI CSI-2 virtual channel (0..3), default 0");
static const int ov5640_framerates[] = {
+ [OV5640_08_FPS] = 8,
[OV5640_15_FPS] = 15,
[OV5640_30_FPS] = 30,
[OV5640_60_FPS] = 60,
@@ -353,10 +355,12 @@ static const struct reg_value ov5640_init_setting_30fps_VGA[] = {
{0x583b, 0x28, 0, 0}, {0x583c, 0x42, 0, 0}, {0x583d, 0xce, 0, 0},
{0x5025, 0x00, 0, 0}, {0x3a0f, 0x30, 0, 0}, {0x3a10, 0x28, 0, 0},
{0x3a1b, 0x30, 0, 0}, {0x3a1e, 0x26, 0, 0}, {0x3a11, 0x60, 0, 0},
- {0x3a1f, 0x14, 0, 0}, {0x3008, 0x02, 0, 0}, {0x3c00, 0x04, 0, 300},
+ {0x3a1f, 0x14, 0, 0}, {0x3008, 0x42, 0, 0}, {0x3c00, 0x04, 0, 300},
+ {0x302c, 0xc2, 0, 0},
};
static const struct reg_value ov5640_setting_VGA_640_480[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -373,9 +377,11 @@ static const struct reg_value ov5640_setting_VGA_640_480[] = {
{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},
+ {0x3008, 0x02, 0, 10},
};
static const struct reg_value ov5640_setting_XGA_1024_768[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -390,11 +396,13 @@ static const struct reg_value ov5640_setting_XGA_1024_768[] = {
{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},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x20, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0xa3, 0, 0},
+ {0x3008, 0x02, 0, 10}
};
static const struct reg_value ov5640_setting_QVGA_320_240[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -411,9 +419,11 @@ static const struct reg_value ov5640_setting_QVGA_320_240[] = {
{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},
+ {0x3008, 0x02, 0, 10},
};
static const struct reg_value ov5640_setting_QCIF_176_144[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -430,9 +440,11 @@ static const struct reg_value ov5640_setting_QCIF_176_144[] = {
{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},
+ {0x3008, 0x02, 0, 10},
};
static const struct reg_value ov5640_setting_NTSC_720_480[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -449,9 +461,11 @@ static const struct reg_value ov5640_setting_NTSC_720_480[] = {
{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},
+ {0x3008, 0x02, 0, 10},
};
static const struct reg_value ov5640_setting_PAL_720_576[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -468,9 +482,11 @@ static const struct reg_value ov5640_setting_PAL_720_576[] = {
{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},
+ {0x3008, 0x02, 0, 10},
};
static const struct reg_value ov5640_setting_720P_1280_720[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x07, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x31, 0, 0},
@@ -487,6 +503,7 @@ static const struct reg_value ov5640_setting_720P_1280_720[] = {
{0x4001, 0x02, 0, 0}, {0x4004, 0x02, 0, 0},
{0x4407, 0x04, 0, 0}, {0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0},
{0x3824, 0x04, 0, 0}, {0x5001, 0x83, 0, 0},
+ {0x3008, 0x02, 0, 15},
};
static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
@@ -518,10 +535,11 @@ static const struct reg_value ov5640_setting_1080P_1920_1080[] = {
{0x3a0e, 0x03, 0, 0}, {0x3a0d, 0x04, 0, 0}, {0x3a14, 0x04, 0, 0},
{0x3a15, 0x60, 0, 0}, {0x4407, 0x04, 0, 0},
{0x460b, 0x37, 0, 0}, {0x460c, 0x20, 0, 0}, {0x3824, 0x04, 0, 0},
- {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 0},
+ {0x4005, 0x1a, 0, 0}, {0x3008, 0x02, 0, 10},
};
static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
+ {0x3008, 0x42, 0, 0},
{0x3c07, 0x08, 0, 0},
{0x3c09, 0x1c, 0, 0}, {0x3c0a, 0x9c, 0, 0}, {0x3c0b, 0x40, 0, 0},
{0x3814, 0x11, 0, 0},
@@ -536,8 +554,9 @@ static const struct reg_value ov5640_setting_QSXGA_2592_1944[] = {
{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, 0x06, 0, 0},
- {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x22, 0, 0},
+ {0x4407, 0x04, 0, 0}, {0x460b, 0x35, 0, 0}, {0x460c, 0x20, 0, 0},
{0x3824, 0x02, 0, 0}, {0x5001, 0x83, 0, 70},
+ {0x3008, 0x02, 0, 20},
};
/* power-on sensor init reg table */
@@ -904,6 +923,51 @@ out:
return best;
}
+static int ov5640_check_valid_mode(struct ov5640_dev *sensor,
+ const struct ov5640_mode_info *mode,
+ enum ov5640_frame_rate rate)
+{
+ struct i2c_client *client = sensor->i2c_client;
+ int ret = 0;
+
+ switch (mode->id) {
+ case OV5640_MODE_QCIF_176_144:
+ case OV5640_MODE_QVGA_320_240:
+ case OV5640_MODE_NTSC_720_480:
+ case OV5640_MODE_PAL_720_576 :
+ case OV5640_MODE_XGA_1024_768:
+ case OV5640_MODE_720P_1280_720:
+ if ((rate != OV5640_15_FPS) &&
+ (rate != OV5640_30_FPS))
+ ret = -EINVAL;
+ break;
+ case OV5640_MODE_VGA_640_480:
+ if ((rate != OV5640_15_FPS) &&
+ (rate != OV5640_30_FPS))
+ ret = -EINVAL;
+ break;
+ case OV5640_MODE_1080P_1920_1080:
+ if (sensor->ep.bus_type == V4L2_MBUS_CSI2_DPHY) {
+ if ((rate != OV5640_15_FPS) &&
+ (rate != OV5640_30_FPS))
+ ret = -EINVAL;
+ } else {
+ if ((rate != OV5640_15_FPS))
+ ret = -EINVAL;
+ }
+ break;
+ case OV5640_MODE_QSXGA_2592_1944:
+ if (rate != OV5640_08_FPS)
+ ret = -EINVAL;
+ break;
+ default:
+ dev_err(&client->dev, "Invalid mode (%d)\n", mode->id);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
/*
* ov5640_set_mipi_pclk() - Calculate the clock tree configuration values
* for the MIPI CSI-2 output.
@@ -1013,7 +1077,7 @@ static int ov5640_set_dvp_pclk(struct ov5640_dev *sensor, unsigned long rate)
&bit_div, &pclk_div);
if (bit_div == 2)
- bit_div = 8;
+ bit_div = 0xA;
ret = ov5640_mod_reg(sensor, OV5640_REG_SC_PLL_CTRL0,
0x0f, bit_div);
@@ -1267,7 +1331,7 @@ static int ov5640_set_stream_dvp(struct ov5640_dev *sensor, bool on)
* 2: MIPI enable
*/
ret = ov5640_write_reg(sensor,
- OV5640_REG_IO_MIPI_CTRL00, on ? 0x18 : 0);
+ OV5640_REG_IO_MIPI_CTRL00, on ? 0x58 : 0);
if (ret)
return ret;
@@ -1302,8 +1366,18 @@ static int ov5640_set_stream_mipi(struct ov5640_dev *sensor, bool on)
if (ret)
return ret;
- return ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
+ ret = ov5640_write_reg(sensor, OV5640_REG_FRAME_CTRL01,
on ? 0x00 : 0x0f);
+ if (ret)
+ return ret;
+
+ ret = ov5640_write_reg(sensor, OV5640_REG_SYS_CTRL0,
+ on ? 0x02 : 0x42);
+ if (ret)
+ return ret;
+
+ msleep(100);
+ return ret;
}
static int ov5640_get_sysclk(struct ov5640_dev *sensor)
@@ -1591,16 +1665,6 @@ ov5640_find_mode(struct ov5640_dev *sensor, enum ov5640_frame_rate fr,
(!nearest && (mode->hact != width || mode->vact != height)))
return NULL;
- /* Only 640x480 can operate at 60fps (for now) */
- if (fr == OV5640_60_FPS &&
- !(mode->hact == 640 && mode->vact == 480))
- return NULL;
-
- /* 2592x1944 only works at 15fps max */
- if ((mode->hact == 2592 && mode->vact == 1944) &&
- fr > OV5640_15_FPS)
- return NULL;
-
return mode;
}
@@ -1970,6 +2034,7 @@ static void ov5640_set_power_off(struct ov5640_dev *sensor)
ov5640_power(sensor, false);
regulator_bulk_disable(OV5640_NUM_SUPPLIES, sensor->supplies);
clk_disable_unprepare(sensor->xclk);
+ sensor->streaming = false;
}
static int ov5640_set_power_mipi(struct ov5640_dev *sensor, bool on)
@@ -2130,11 +2195,11 @@ static int ov5640_try_frame_interval(struct ov5640_dev *sensor,
u32 width, u32 height)
{
const struct ov5640_mode_info *mode;
- enum ov5640_frame_rate rate = OV5640_15_FPS;
+ enum ov5640_frame_rate rate = OV5640_08_FPS;
int minfps, maxfps, best_fps, fps;
int i;
- minfps = ov5640_framerates[OV5640_15_FPS];
+ minfps = ov5640_framerates[OV5640_08_FPS];
maxfps = ov5640_framerates[OV5640_60_FPS];
if (fi->numerator == 0) {
@@ -2183,10 +2248,10 @@ static int ov5640_get_fmt(struct v4l2_subdev *sd,
else
fmt = &sensor->fmt;
+ fmt->reserved[1] = (sensor->current_fr == OV5640_30_FPS) ? 30 : 15;
format->format = *fmt;
mutex_unlock(&sensor->lock);
-
return 0;
}
@@ -2204,6 +2269,7 @@ static int ov5640_try_fmt_internal(struct v4l2_subdev *sd,
return -EINVAL;
fmt->width = mode->hact;
fmt->height = mode->vact;
+ memset(fmt->reserved, 0, sizeof(fmt->reserved));
if (new_mode)
*new_mode = mode;
@@ -2262,6 +2328,8 @@ static int ov5640_set_fmt(struct v4l2_subdev *sd,
if (mbus_fmt->code != sensor->fmt.code)
sensor->pending_fmt_change = true;
+ if (sensor->pending_mode_change || sensor->pending_fmt_change)
+ sensor->fmt = *mbus_fmt;
out:
mutex_unlock(&sensor->lock);
return ret;
@@ -2773,24 +2841,36 @@ static int ov5640_enum_frame_interval(
struct v4l2_subdev_frame_interval_enum *fie)
{
struct ov5640_dev *sensor = to_ov5640_dev(sd);
- struct v4l2_fract tpf;
- int ret;
+ int i, j, count;
if (fie->pad != 0)
return -EINVAL;
if (fie->index >= OV5640_NUM_FRAMERATES)
return -EINVAL;
- tpf.numerator = 1;
- tpf.denominator = ov5640_framerates[fie->index];
-
- ret = ov5640_try_frame_interval(sensor, &tpf,
- fie->width, fie->height);
- if (ret < 0)
+ if (fie->width == 0 || fie->height == 0 || fie->code == 0) {
+ pr_warn("Please assign pixel format, width and height.\n");
return -EINVAL;
+ }
- fie->interval = tpf;
- return 0;
+ fie->interval.numerator = 1;
+
+ count = 0;
+ for (i = 0; i < OV5640_NUM_FRAMERATES; i++) {
+ for (j = 0; j < OV5640_NUM_MODES; j++) {
+ if (fie->width == ov5640_mode_data[j].hact &&
+ fie->height == ov5640_mode_data[j].vact &&
+ !ov5640_check_valid_mode(sensor, &ov5640_mode_data[j], i))
+ count++;
+
+ if (fie->index == (count - 1)) {
+ fie->interval.denominator = ov5640_framerates[i];
+ return 0;
+ }
+ }
+ }
+
+ return -EINVAL;
}
static int ov5640_g_frame_interval(struct v4l2_subdev *sd,
@@ -2867,11 +2947,23 @@ static int ov5640_enum_mbus_code(struct v4l2_subdev *sd,
static int ov5640_s_stream(struct v4l2_subdev *sd, int enable)
{
struct ov5640_dev *sensor = to_ov5640_dev(sd);
+ struct i2c_client *client = sensor->i2c_client;
int ret = 0;
mutex_lock(&sensor->lock);
if (sensor->streaming == !enable) {
+ ret = ov5640_check_valid_mode(sensor,
+ sensor->current_mode,
+ sensor->current_fr);
+ if (ret) {
+ dev_err(&client->dev, "Not support WxH@fps=%dx%d@%d\n",
+ sensor->current_mode->hact,
+ sensor->current_mode->vact,
+ ov5640_framerates[sensor->current_fr]);
+ goto out;
+ }
+
if (enable && sensor->pending_mode_change) {
ret = ov5640_set_mode(sensor);
if (ret)
@@ -2925,6 +3017,17 @@ static const struct v4l2_subdev_ops ov5640_subdev_ops = {
.pad = &ov5640_pad_ops,
};
+static int ov5640_link_setup(struct media_entity *entity,
+ const struct media_pad *local,
+ const struct media_pad *remote, u32 flags)
+{
+ return 0;
+}
+
+static const struct media_entity_operations ov5640_sd_media_ops = {
+ .link_setup = ov5640_link_setup,
+};
+
static int ov5640_get_regulators(struct ov5640_dev *sensor)
{
int i;
@@ -3061,9 +3164,9 @@ static int ov5640_probe(struct i2c_client *client)
v4l2_i2c_subdev_init(&sensor->sd, client, &ov5640_subdev_ops);
- sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
- V4L2_SUBDEV_FL_HAS_EVENTS;
+ sensor->sd.flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
sensor->pad.flags = MEDIA_PAD_FL_SOURCE;
+ sensor->sd.entity.ops = &ov5640_sd_media_ops;
sensor->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sensor->sd.entity, 1, &sensor->pad);
if (ret)