提交 35c5f8f6 编写于 作者: D David Plowman 提交者: Zheng Zengkai

media: ov5647: Add basic support for multiple sensor modes.

raspberrypi inclusion
category: feature
bugzilla: 50432

--------------------------------

Specifically:

Added a structure ov5647_mode and a list of supported_modes (though no
actual new modes as yet). The state object points to the "current mode".

ov5647_enum_mbus_code, ov5647_enum_frame_size, ov5647_set_fmt and
ov5647_get_fmt all needed upgrading to cope with multiple modes.

__sensor_init (which writes all the registers) is now called by
ov5647_stream_on (once the mode is known) rather than by
ov5647_sensor_power.
Signed-off-by: NDavid Plowman <david.plowman@raspberrypi.com>
Signed-off-by: NNaushir Patuck <naush@raspberrypi.com>
Signed-off-by: NFang Yafen <yafen@iscas.ac.cn>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 b10493fd
...@@ -86,13 +86,17 @@ struct regval_list { ...@@ -86,13 +86,17 @@ struct regval_list {
u8 data; u8 data;
}; };
struct ov5647_mode {
struct v4l2_mbus_framefmt format;
struct regval_list *reg_list;
unsigned int num_regs;
};
struct ov5647 { struct ov5647 {
struct v4l2_subdev sd; struct v4l2_subdev sd;
struct media_pad pad; struct media_pad pad;
struct mutex lock; struct mutex lock;
struct v4l2_mbus_framefmt format; const struct ov5647_mode *mode;
unsigned int width;
unsigned int height;
int power_count; int power_count;
struct clk *xclk; struct clk *xclk;
struct gpio_desc *pwdn; struct gpio_desc *pwdn;
...@@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480[] = { ...@@ -207,6 +211,32 @@ static struct regval_list ov5647_640x480[] = {
{0x0100, 0x01}, {0x0100, 0x01},
}; };
static struct ov5647_mode supported_modes_8bit[] = {
/*
* Original 8-bit VGA mode
* Uncentred crop (top left quarter) from 2x2 binned 1296x972 image.
*/
{
{
.code = MEDIA_BUS_FMT_SBGGR8_1X8,
.colorspace = V4L2_COLORSPACE_SRGB,
.field = V4L2_FIELD_NONE,
.width = 640,
.height = 480
},
ov5647_640x480,
ARRAY_SIZE(ov5647_640x480)
},
/* more modes below here... */
};
static struct ov5647_mode supported_modes_10bit[] = {
/* no 10-bit modes yet */
};
/* Use original 8-bit VGA mode as default. */
#define OV5647_DEFAULT_MODE (&supported_modes_8bit[0])
static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val) static int ov5647_write(struct v4l2_subdev *sd, u16 reg, u8 val)
{ {
int ret; int ret;
...@@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel) ...@@ -293,12 +323,55 @@ static int ov5647_set_virtual_channel(struct v4l2_subdev *sd, int channel)
return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6)); return ov5647_write(sd, OV5647_REG_MIPI_CTRL14, channel_id | (channel << 6));
} }
static int __sensor_init(struct v4l2_subdev *sd)
{
int ret;
u8 resetval, rdval;
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5647 *state = to_state(sd);
ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
if (ret < 0)
return ret;
ret = ov5647_write_array(sd, state->mode->reg_list,
state->mode->num_regs);
if (ret < 0) {
dev_err(&client->dev, "write sensor default regs error\n");
return ret;
}
ret = ov5647_set_virtual_channel(sd, 0);
if (ret < 0)
return ret;
ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
if (ret < 0)
return ret;
if (!(resetval & 0x01)) {
dev_err(&client->dev, "Device was in SW standby");
ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
if (ret < 0)
return ret;
}
return 0;
}
static int ov5647_stream_on(struct v4l2_subdev *sd) static int ov5647_stream_on(struct v4l2_subdev *sd)
{ {
struct i2c_client *client = v4l2_get_subdevdata(sd);
struct ov5647 *ov5647 = to_state(sd); struct ov5647 *ov5647 = to_state(sd);
u8 val = MIPI_CTRL00_BUS_IDLE; u8 val = MIPI_CTRL00_BUS_IDLE;
int ret; int ret;
ret = __sensor_init(sd);
if (ret < 0) {
dev_err(&client->dev, "sensor_init failed\n");
return ret;
}
if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK) if (ov5647->flags & V4L2_MBUS_CSI2_NONCONTINUOUS_CLOCK)
val |= MIPI_CTRL00_CLOCK_LANE_GATE | val |= MIPI_CTRL00_CLOCK_LANE_GATE |
MIPI_CTRL00_LINE_SYNC_ENABLE; MIPI_CTRL00_LINE_SYNC_ENABLE;
...@@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_subdev *sd, bool standby) ...@@ -347,44 +420,6 @@ static int set_sw_standby(struct v4l2_subdev *sd, bool standby)
return ov5647_write(sd, OV5647_SW_STANDBY, rdval); return ov5647_write(sd, OV5647_SW_STANDBY, rdval);
} }
static int __sensor_init(struct v4l2_subdev *sd)
{
int ret;
u8 resetval, rdval;
struct i2c_client *client = v4l2_get_subdevdata(sd);
ret = ov5647_read(sd, OV5647_SW_STANDBY, &rdval);
if (ret < 0)
return ret;
ret = ov5647_write_array(sd, ov5647_640x480,
ARRAY_SIZE(ov5647_640x480));
if (ret < 0) {
dev_err(&client->dev, "write sensor default regs error\n");
return ret;
}
ret = ov5647_set_virtual_channel(sd, 0);
if (ret < 0)
return ret;
ret = ov5647_read(sd, OV5647_SW_STANDBY, &resetval);
if (ret < 0)
return ret;
if (!(resetval & 0x01)) {
dev_err(&client->dev, "Device was in SW standby");
ret = ov5647_write(sd, OV5647_SW_STANDBY, 0x01);
if (ret < 0)
return ret;
}
/*
* stream off to make the clock lane into LP-11 state.
*/
return ov5647_stream_off(sd);
}
static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
{ {
int ret = 0; int ret = 0;
...@@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on) ...@@ -416,7 +451,10 @@ static int ov5647_sensor_power(struct v4l2_subdev *sd, int on)
goto out; goto out;
} }
ret = __sensor_init(sd); /*
* Ensure streaming off to make clock lane go into LP-11 state.
*/
ret = ov5647_stream_off(sd);
if (ret < 0) { if (ret < 0) {
clk_disable_unprepare(ov5647->xclk); clk_disable_unprepare(ov5647->xclk);
dev_err(&client->dev, dev_err(&client->dev,
...@@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = { ...@@ -489,10 +527,19 @@ static const struct v4l2_subdev_core_ops ov5647_subdev_core_ops = {
static int ov5647_s_stream(struct v4l2_subdev *sd, int enable) static int ov5647_s_stream(struct v4l2_subdev *sd, int enable)
{ {
struct ov5647 *state = to_state(sd);
int ret = 0;
mutex_lock(&state->lock);
if (enable) if (enable)
return ov5647_stream_on(sd); ret = ov5647_stream_on(sd);
else else
return ov5647_stream_off(sd); ret = ov5647_stream_off(sd);
mutex_unlock(&state->lock);
return ret;
} }
static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = { static const struct v4l2_subdev_video_ops ov5647_subdev_video_ops = {
...@@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd, ...@@ -503,38 +550,127 @@ static int ov5647_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code) struct v4l2_subdev_mbus_code_enum *code)
{ {
if (code->index > 0) if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit))
code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
else if (code->index == 0 && ARRAY_SIZE(supported_modes_8bit) == 0 &&
ARRAY_SIZE(supported_modes_10bit))
code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
else if (code->index == 1 && ARRAY_SIZE(supported_modes_8bit) &&
ARRAY_SIZE(supported_modes_10bit))
code->code = MEDIA_BUS_FMT_SBGGR10_1X10;
else
return -EINVAL; return -EINVAL;
code->code = MEDIA_BUS_FMT_SBGGR8_1X8; return 0;
}
static int ov5647_enum_frame_size(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_frame_size_enum *fse)
{
struct ov5647_mode *mode = NULL;
if (fse->code == MEDIA_BUS_FMT_SBGGR8_1X8) {
if (fse->index >= ARRAY_SIZE(supported_modes_8bit))
return -EINVAL;
mode = &supported_modes_8bit[fse->index];
} else if (fse->code == MEDIA_BUS_FMT_SBGGR10_1X10) {
if (fse->index >= ARRAY_SIZE(supported_modes_10bit))
return -EINVAL;
mode = &supported_modes_10bit[fse->index];
} else {
return -EINVAL;
}
fse->min_width = mode->format.width;
fse->max_width = fse->min_width;
fse->min_height = mode->format.height;
fse->max_height = fse->min_height;
return 0;
}
static int ov5647_set_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format)
{
struct v4l2_mbus_framefmt *fmt = &format->format;
struct ov5647 *state = to_state(sd);
struct v4l2_mbus_framefmt *framefmt;
const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
if (format->pad != 0)
return -EINVAL;
mutex_lock(&state->lock);
/*
* Try to respect any given pixel format, otherwise try for a 10-bit
* mode.
*/
mode_8bit = v4l2_find_nearest_size(supported_modes_8bit,
ARRAY_SIZE(supported_modes_8bit),
format.width, format.height,
format->format.width,
format->format.height);
mode_10bit = v4l2_find_nearest_size(supported_modes_10bit,
ARRAY_SIZE(supported_modes_10bit),
format.width, format.height,
format->format.width,
format->format.height);
if (format->format.code == MEDIA_BUS_FMT_SBGGR8_1X8 && mode_8bit)
mode = mode_8bit;
else if (format->format.code == MEDIA_BUS_FMT_SBGGR10_1X10 &&
mode_10bit)
mode = mode_10bit;
else if (mode_10bit)
mode = mode_10bit;
else
mode = mode_8bit;
if (!mode)
return -EINVAL;
*fmt = mode->format;
if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
framefmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
*framefmt = format->format;
} else {
state->mode = mode;
}
mutex_unlock(&state->lock);
return 0; return 0;
} }
static int ov5647_set_get_fmt(struct v4l2_subdev *sd, static int ov5647_get_fmt(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg, struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_format *format) struct v4l2_subdev_format *format)
{ {
struct v4l2_mbus_framefmt *fmt = &format->format; struct v4l2_mbus_framefmt *fmt = &format->format;
struct ov5647 *state = to_state(sd);
if (format->pad != 0) if (format->pad != 0)
return -EINVAL; return -EINVAL;
/* Only one format is supported, so return that */ mutex_lock(&state->lock);
memset(fmt, 0, sizeof(*fmt));
fmt->code = MEDIA_BUS_FMT_SBGGR8_1X8; if (format->which == V4L2_SUBDEV_FORMAT_TRY)
fmt->colorspace = V4L2_COLORSPACE_SRGB; *fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
fmt->field = V4L2_FIELD_NONE; else
fmt->width = 640; *fmt = state->mode->format;
fmt->height = 480;
mutex_unlock(&state->lock);
return 0; return 0;
} }
static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = { static const struct v4l2_subdev_pad_ops ov5647_subdev_pad_ops = {
.enum_mbus_code = ov5647_enum_mbus_code, .enum_mbus_code = ov5647_enum_mbus_code,
.set_fmt = ov5647_set_get_fmt, .set_fmt = ov5647_set_fmt,
.get_fmt = ov5647_set_get_fmt, .get_fmt = ov5647_get_fmt,
.enum_frame_size = ov5647_enum_frame_size,
}; };
static const struct v4l2_subdev_ops ov5647_subdev_ops = { static const struct v4l2_subdev_ops ov5647_subdev_ops = {
...@@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) ...@@ -580,18 +716,15 @@ static int ov5647_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
v4l2_subdev_get_try_format(sd, fh->pad, 0); v4l2_subdev_get_try_format(sd, fh->pad, 0);
struct v4l2_rect *crop = struct v4l2_rect *crop =
v4l2_subdev_get_try_crop(sd, fh->pad, 0); v4l2_subdev_get_try_crop(sd, fh->pad, 0);
struct ov5647 *state = to_state(sd);
crop->left = OV5647_COLUMN_START_DEF; crop->left = OV5647_COLUMN_START_DEF;
crop->top = OV5647_ROW_START_DEF; crop->top = OV5647_ROW_START_DEF;
crop->width = OV5647_WINDOW_WIDTH_DEF; crop->width = OV5647_WINDOW_WIDTH_DEF;
crop->height = OV5647_WINDOW_HEIGHT_DEF; crop->height = OV5647_WINDOW_HEIGHT_DEF;
format->code = MEDIA_BUS_FMT_SBGGR8_1X8; /* Set the default format to the same as the sensor. */
*format = state->mode->format;
format->width = OV5647_WINDOW_WIDTH_DEF;
format->height = OV5647_WINDOW_HEIGHT_DEF;
format->field = V4L2_FIELD_NONE;
format->colorspace = V4L2_COLORSPACE_SRGB;
return 0; return 0;
} }
...@@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_client *client) ...@@ -660,6 +793,9 @@ static int ov5647_probe(struct i2c_client *client)
mutex_init(&sensor->lock); mutex_init(&sensor->lock);
/* Set the default mode before we init the subdev */
sensor->mode = OV5647_DEFAULT_MODE;
sd = &sensor->sd; sd = &sensor->sd;
v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops); v4l2_i2c_subdev_init(sd, client, &ov5647_subdev_ops);
sensor->sd.internal_ops = &ov5647_subdev_internal_ops; sensor->sd.internal_ops = &ov5647_subdev_internal_ops;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册