summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/gc2145.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/gc2145.c')
-rw-r--r--drivers/media/i2c/gc2145.c131
1 files changed, 84 insertions, 47 deletions
diff --git a/drivers/media/i2c/gc2145.c b/drivers/media/i2c/gc2145.c
index bef7b0e056a8..ba02161d46e7 100644
--- a/drivers/media/i2c/gc2145.c
+++ b/drivers/media/i2c/gc2145.c
@@ -21,7 +21,6 @@
#include <media/v4l2-cci.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
-#include <media/v4l2-event.h>
#include <media/v4l2-fwnode.h>
#include <media/v4l2-mediabus.h>
@@ -68,8 +67,7 @@
#define GC2145_DPHY_CLK_DELAY BIT(4)
#define GC2145_DPHY_LANE0_DELAY BIT(5)
#define GC2145_DPHY_LANE1_DELAY BIT(6)
-#define GC2145_REG_FIFO_FULL_LVL_LOW CCI_REG8(0x04)
-#define GC2145_REG_FIFO_FULL_LVL_HIGH CCI_REG8(0x05)
+#define GC2145_REG_FIFO_FULL_LVL CCI_REG16_LE(0x04)
#define GC2145_REG_FIFO_MODE CCI_REG8(0x06)
#define GC2145_FIFO_MODE_READ_GATE BIT(3)
#define GC2145_FIFO_MODE_MIPI_CLK_MODULE BIT(7)
@@ -79,8 +77,7 @@
#define GC2145_CSI2_MODE_MIPI_EN BIT(4)
#define GC2145_CSI2_MODE_EN BIT(7)
#define GC2145_REG_MIPI_DT CCI_REG8(0x11)
-#define GC2145_REG_LWC_LOW CCI_REG8(0x12)
-#define GC2145_REG_LWC_HIGH CCI_REG8(0x13)
+#define GC2145_REG_LWC CCI_REG16_LE(0x12)
#define GC2145_REG_DPHY_MODE CCI_REG8(0x15)
#define GC2145_DPHY_MODE_TRIGGER_PROG BIT(4)
#define GC2145_REG_FIFO_GATE_MODE CCI_REG8(0x17)
@@ -542,45 +539,82 @@ static const struct gc2145_mode supported_modes[] = {
/**
* struct gc2145_format - GC2145 pixel format description
* @code: media bus (MBUS) associated code
+ * @colorspace: V4L2 colorspace
* @datatype: MIPI CSI2 data type
* @output_fmt: GC2145 output format
* @switch_bit: GC2145 first/second switch
+ * @row_col_switch: GC2145 switch row and/or column
*/
struct gc2145_format {
unsigned int code;
+ unsigned int colorspace;
unsigned char datatype;
unsigned char output_fmt;
bool switch_bit;
+ unsigned char row_col_switch;
};
/* All supported formats */
static const struct gc2145_format supported_formats[] = {
{
.code = MEDIA_BUS_FMT_UYVY8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.datatype = MIPI_CSI2_DT_YUV422_8B,
.output_fmt = 0x00,
},
{
.code = MEDIA_BUS_FMT_VYUY8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.datatype = MIPI_CSI2_DT_YUV422_8B,
.output_fmt = 0x01,
},
{
.code = MEDIA_BUS_FMT_YUYV8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.datatype = MIPI_CSI2_DT_YUV422_8B,
.output_fmt = 0x02,
},
{
.code = MEDIA_BUS_FMT_YVYU8_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.datatype = MIPI_CSI2_DT_YUV422_8B,
.output_fmt = 0x03,
},
{
.code = MEDIA_BUS_FMT_RGB565_1X16,
+ .colorspace = V4L2_COLORSPACE_SRGB,
.datatype = MIPI_CSI2_DT_RGB565,
.output_fmt = 0x06,
.switch_bit = true,
},
+ {
+ .code = MEDIA_BUS_FMT_SGRBG8_1X8,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .output_fmt = 0x17,
+ .row_col_switch = GC2145_SYNC_MODE_COL_SWITCH,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SRGGB8_1X8,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .output_fmt = 0x17,
+ .row_col_switch = GC2145_SYNC_MODE_COL_SWITCH | GC2145_SYNC_MODE_ROW_SWITCH,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SBGGR8_1X8,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .output_fmt = 0x17,
+ .row_col_switch = 0,
+ },
+ {
+ .code = MEDIA_BUS_FMT_SGBRG8_1X8,
+ .colorspace = V4L2_COLORSPACE_RAW,
+ .datatype = MIPI_CSI2_DT_RAW8,
+ .output_fmt = 0x17,
+ .row_col_switch = GC2145_SYNC_MODE_ROW_SWITCH,
+ },
};
struct gc2145_ctrls {
@@ -641,7 +675,8 @@ gc2145_get_format_code(struct gc2145 *gc2145, u32 code)
static void gc2145_update_pad_format(struct gc2145 *gc2145,
const struct gc2145_mode *mode,
- struct v4l2_mbus_framefmt *fmt, u32 code)
+ struct v4l2_mbus_framefmt *fmt, u32 code,
+ u32 colorspace)
{
fmt->code = code;
fmt->width = mode->width;
@@ -663,7 +698,8 @@ static int gc2145_init_state(struct v4l2_subdev *sd,
/* Initialize pad format */
format = v4l2_subdev_state_get_format(state, 0);
gc2145_update_pad_format(gc2145, &supported_modes[0], format,
- MEDIA_BUS_FMT_RGB565_1X16);
+ MEDIA_BUS_FMT_RGB565_1X16,
+ V4L2_COLORSPACE_SRGB);
/* Initialize crop rectangle. */
crop = v4l2_subdev_state_get_crop(state, 0);
@@ -754,7 +790,13 @@ static int gc2145_set_pad_format(struct v4l2_subdev *sd,
width, height,
fmt->format.width, fmt->format.height);
- gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code);
+ /* In RAW mode, VGA is not possible so use 720p instead */
+ if (gc2145_fmt->colorspace == V4L2_COLORSPACE_RAW &&
+ mode == &supported_modes[GC2145_MODE_640X480])
+ mode = &supported_modes[GC2145_MODE_1280X720];
+
+ gc2145_update_pad_format(gc2145, mode, &fmt->format, gc2145_fmt->code,
+ gc2145_fmt->colorspace);
framefmt = v4l2_subdev_state_get_format(sd_state, fmt->pad);
if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) {
gc2145->mode = mode;
@@ -811,9 +853,12 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145,
* For RAW8, LWC = image width
* For RAW10, LWC = image width * 1.25
*/
- lwc = gc2145->mode->width * 2;
- cci_write(gc2145->regmap, GC2145_REG_LWC_HIGH, lwc >> 8, &ret);
- cci_write(gc2145->regmap, GC2145_REG_LWC_LOW, lwc & 0xff, &ret);
+ if (gc2145_format->colorspace != V4L2_COLORSPACE_RAW)
+ lwc = gc2145->mode->width * 2;
+ else
+ lwc = gc2145->mode->width;
+
+ cci_write(gc2145->regmap, GC2145_REG_LWC, lwc, &ret);
/*
* Adjust the MIPI FIFO Full Level
@@ -821,21 +866,25 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145,
* 1280x720 / 1600x1200 (aka no scaler) non RAW: 0x0001
* 1600x1200 RAW: 0x0190
*/
- if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600)
- fifo_full_lvl = 0x0001;
- else
+ if (gc2145_format->colorspace != V4L2_COLORSPACE_RAW) {
+ if (gc2145->mode->width == 1280 || gc2145->mode->width == 1600)
+ fifo_full_lvl = 0x0001;
+ else
+ fifo_full_lvl = 0x0190;
+ } else {
fifo_full_lvl = 0x0190;
+ }
- cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_HIGH,
- fifo_full_lvl >> 8, &ret);
- cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL_LOW,
- fifo_full_lvl & 0xff, &ret);
+ cci_write(gc2145->regmap, GC2145_REG_FIFO_FULL_LVL,
+ fifo_full_lvl, &ret);
/*
* Set the FIFO gate mode / MIPI wdiv set:
* 0xf1 in case of RAW mode and 0xf0 otherwise
*/
- cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE, 0xf0, &ret);
+ cci_write(gc2145->regmap, GC2145_REG_FIFO_GATE_MODE,
+ gc2145_format->colorspace == V4L2_COLORSPACE_RAW ?
+ 0xf1 : 0xf0, &ret);
/* Set the MIPI data type */
cci_write(gc2145->regmap, GC2145_REG_MIPI_DT,
@@ -849,9 +898,11 @@ static int gc2145_config_mipi_mode(struct gc2145 *gc2145,
return ret;
}
-static int gc2145_start_streaming(struct gc2145 *gc2145,
- struct v4l2_subdev_state *state)
+static int gc2145_enable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
+ struct gc2145 *gc2145 = to_gc2145(sd);
struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd);
const struct gc2145_format *gc2145_format;
struct v4l2_mbus_framefmt *fmt;
@@ -883,6 +934,10 @@ static int gc2145_start_streaming(struct gc2145 *gc2145,
GC2145_BYPASS_MODE_SWITCH,
gc2145_format->switch_bit ? GC2145_BYPASS_MODE_SWITCH
: 0, &ret);
+ cci_update_bits(gc2145->regmap, GC2145_REG_SYNC_MODE,
+ GC2145_SYNC_MODE_COL_SWITCH |
+ GC2145_SYNC_MODE_ROW_SWITCH,
+ gc2145_format->row_col_switch, &ret);
if (ret) {
dev_err(&client->dev, "%s failed to write regs\n", __func__);
goto err_rpm_put;
@@ -913,8 +968,11 @@ err_rpm_put:
return ret;
}
-static void gc2145_stop_streaming(struct gc2145 *gc2145)
+static int gc2145_disable_streams(struct v4l2_subdev *sd,
+ struct v4l2_subdev_state *state, u32 pad,
+ u64 streams_mask)
{
+ struct gc2145 *gc2145 = to_gc2145(sd);
struct i2c_client *client = v4l2_get_subdevdata(&gc2145->sd);
int ret = 0;
@@ -929,22 +987,6 @@ static void gc2145_stop_streaming(struct gc2145 *gc2145)
pm_runtime_mark_last_busy(&client->dev);
pm_runtime_put_autosuspend(&client->dev);
-}
-
-static int gc2145_set_stream(struct v4l2_subdev *sd, int enable)
-{
- struct gc2145 *gc2145 = to_gc2145(sd);
- struct v4l2_subdev_state *state;
- int ret = 0;
-
- state = v4l2_subdev_lock_and_get_active_state(sd);
-
- if (enable)
- ret = gc2145_start_streaming(gc2145, state);
- else
- gc2145_stop_streaming(gc2145);
-
- v4l2_subdev_unlock_state(state);
return ret;
}
@@ -1069,13 +1111,8 @@ static const u8 test_pattern_val[] = {
GC2145_TEST_UNIFORM | GC2145_TEST_BLACK,
};
-static const struct v4l2_subdev_core_ops gc2145_core_ops = {
- .subscribe_event = v4l2_ctrl_subdev_subscribe_event,
- .unsubscribe_event = v4l2_event_subdev_unsubscribe,
-};
-
static const struct v4l2_subdev_video_ops gc2145_video_ops = {
- .s_stream = gc2145_set_stream,
+ .s_stream = v4l2_subdev_s_stream_helper,
};
static const struct v4l2_subdev_pad_ops gc2145_pad_ops = {
@@ -1084,10 +1121,11 @@ static const struct v4l2_subdev_pad_ops gc2145_pad_ops = {
.set_fmt = gc2145_set_pad_format,
.get_selection = gc2145_get_selection,
.enum_frame_size = gc2145_enum_frame_size,
+ .enable_streams = gc2145_enable_streams,
+ .disable_streams = gc2145_disable_streams,
};
static const struct v4l2_subdev_ops gc2145_subdev_ops = {
- .core = &gc2145_core_ops,
.video = &gc2145_video_ops,
.pad = &gc2145_pad_ops,
};
@@ -1353,8 +1391,7 @@ static int gc2145_probe(struct i2c_client *client)
goto error_power_off;
/* Initialize subdev */
- gc2145->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE |
- V4L2_SUBDEV_FL_HAS_EVENTS;
+ gc2145->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
gc2145->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
/* Initialize source pad */