mirror of
https://github.com/raspberrypi/linux.git
synced 2025-12-06 18:09:56 +00:00
media: ov5647: Add basic support for multiple sensor modes.
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: David Plowman <david.plowman@raspberrypi.com> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
This commit is contained in:
committed by
popcornmix
parent
fb02f28576
commit
27fdbac26d
@@ -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[] = {
|
|||||||
{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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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 = {
|
|||||||
|
|
||||||
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,
|
|||||||
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))
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
code->code = MEDIA_BUS_FMT_SBGGR8_1X8;
|
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 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ov5647_set_get_fmt(struct v4l2_subdev *sd,
|
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_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);
|
||||||
|
struct v4l2_mbus_framefmt *framefmt;
|
||||||
|
const struct ov5647_mode *mode_8bit, *mode_10bit, *mode = NULL;
|
||||||
|
|
||||||
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;
|
/*
|
||||||
fmt->colorspace = V4L2_COLORSPACE_SRGB;
|
* Try to respect any given pixel format, otherwise try for a 10-bit
|
||||||
fmt->field = V4L2_FIELD_NONE;
|
* mode.
|
||||||
fmt->width = 640;
|
*/
|
||||||
fmt->height = 480;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ov5647_get_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);
|
||||||
|
|
||||||
|
if (format->pad != 0)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
mutex_lock(&state->lock);
|
||||||
|
|
||||||
|
if (format->which == V4L2_SUBDEV_FORMAT_TRY)
|
||||||
|
*fmt = *v4l2_subdev_get_try_format(sd, cfg, format->pad);
|
||||||
|
else
|
||||||
|
*fmt = state->mode->format;
|
||||||
|
|
||||||
|
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user