summaryrefslogtreecommitdiff
path: root/drivers/media/i2c/adv7842.c
diff options
context:
space:
mode:
authorHans Verkuil <hans.verkuil@cisco.com>2015-05-01 14:31:30 (GMT)
committerMauro Carvalho Chehab <mchehab@osg.samsung.com>2015-05-20 16:37:36 (GMT)
commitf888ae7e6a9e7c0ddc2aa78adc1299747f202516 (patch)
treed22c2c574787a95332fcab69dd584f5ace47aff8 /drivers/media/i2c/adv7842.c
parent71df673132cd5eef9ae2d9c79e3a446249de4961 (diff)
downloadlinux-f888ae7e6a9e7c0ddc2aa78adc1299747f202516.tar.xz
[media] adv7842: Make output format configurable through pad format operations
Replace the dummy video format operations by pad format operations that configure the output format. Copied from the adv7604 driver. Note: while arch/blackfin/mach-bf609/boards/ezkit.c uses adv7842_platform_data this source has not been updated because it is broken since the very beginning. It depends on a struct adv7842_output_format that does not exist. And besides that gcc has no support for bf609 so nobody can compile it except by installing a toolchain from ADI. Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com> Cc: Scott Jiang <scott.jiang.linux@gmail.com> Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
Diffstat (limited to 'drivers/media/i2c/adv7842.c')
-rw-r--r--drivers/media/i2c/adv7842.c269
1 files changed, 239 insertions, 30 deletions
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c
index 86e65a8..dceabc2 100644
--- a/drivers/media/i2c/adv7842.c
+++ b/drivers/media/i2c/adv7842.c
@@ -56,6 +56,28 @@ MODULE_LICENSE("GPL");
/* ADV7842 system clock frequency */
#define ADV7842_fsc (28636360)
+#define ADV7842_RGB_OUT (1 << 1)
+
+#define ADV7842_OP_FORMAT_SEL_8BIT (0 << 0)
+#define ADV7842_OP_FORMAT_SEL_10BIT (1 << 0)
+#define ADV7842_OP_FORMAT_SEL_12BIT (2 << 0)
+
+#define ADV7842_OP_MODE_SEL_SDR_422 (0 << 5)
+#define ADV7842_OP_MODE_SEL_DDR_422 (1 << 5)
+#define ADV7842_OP_MODE_SEL_SDR_444 (2 << 5)
+#define ADV7842_OP_MODE_SEL_DDR_444 (3 << 5)
+#define ADV7842_OP_MODE_SEL_SDR_422_2X (4 << 5)
+#define ADV7842_OP_MODE_SEL_ADI_CM (5 << 5)
+
+#define ADV7842_OP_CH_SEL_GBR (0 << 5)
+#define ADV7842_OP_CH_SEL_GRB (1 << 5)
+#define ADV7842_OP_CH_SEL_BGR (2 << 5)
+#define ADV7842_OP_CH_SEL_RGB (3 << 5)
+#define ADV7842_OP_CH_SEL_BRG (4 << 5)
+#define ADV7842_OP_CH_SEL_RBG (5 << 5)
+
+#define ADV7842_OP_SWAP_CB_CR (1 << 0)
+
/*
**********************************************************************
*
@@ -64,6 +86,14 @@ MODULE_LICENSE("GPL");
**********************************************************************
*/
+struct adv7842_format_info {
+ u32 code;
+ u8 op_ch_sel;
+ bool rgb_out;
+ bool swap_cb_cr;
+ u8 op_format_sel;
+};
+
struct adv7842_state {
struct adv7842_platform_data pdata;
struct v4l2_subdev sd;
@@ -72,6 +102,9 @@ struct adv7842_state {
enum adv7842_mode mode;
struct v4l2_dv_timings timings;
enum adv7842_vid_std_select vid_std_select;
+
+ const struct adv7842_format_info *format;
+
v4l2_std_id norm;
struct {
u8 edid[256];
@@ -221,11 +254,21 @@ static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl)
return &container_of(ctrl->handler, struct adv7842_state, hdl)->sd;
}
+static inline unsigned hblanking(const struct v4l2_bt_timings *t)
+{
+ return V4L2_DV_BT_BLANKING_WIDTH(t);
+}
+
static inline unsigned htotal(const struct v4l2_bt_timings *t)
{
return V4L2_DV_BT_FRAME_WIDTH(t);
}
+static inline unsigned vblanking(const struct v4l2_bt_timings *t)
+{
+ return V4L2_DV_BT_BLANKING_HEIGHT(t);
+}
+
static inline unsigned vtotal(const struct v4l2_bt_timings *t)
{
return V4L2_DV_BT_FRAME_HEIGHT(t);
@@ -335,6 +378,12 @@ static inline int io_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 va
return io_write(sd, reg, (io_read(sd, reg) & mask) | val);
}
+static inline int io_write_clr_set(struct v4l2_subdev *sd,
+ u8 reg, u8 mask, u8 val)
+{
+ return io_write(sd, reg, (io_read(sd, reg) & ~mask) | val);
+}
+
static inline int avlink_read(struct v4l2_subdev *sd, u8 reg)
{
struct adv7842_state *state = to_state(sd);
@@ -535,6 +584,64 @@ static void main_reset(struct v4l2_subdev *sd)
mdelay(5);
}
+/* -----------------------------------------------------------------------------
+ * Format helpers
+ */
+
+static const struct adv7842_format_info adv7842_formats[] = {
+ { MEDIA_BUS_FMT_RGB888_1X24, ADV7842_OP_CH_SEL_RGB, true, false,
+ ADV7842_OP_MODE_SEL_SDR_444 | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YUYV8_2X8, ADV7842_OP_CH_SEL_RGB, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YVYU8_2X8, ADV7842_OP_CH_SEL_RGB, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YUYV10_2X10, ADV7842_OP_CH_SEL_RGB, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_10BIT },
+ { MEDIA_BUS_FMT_YVYU10_2X10, ADV7842_OP_CH_SEL_RGB, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_10BIT },
+ { MEDIA_BUS_FMT_YUYV12_2X12, ADV7842_OP_CH_SEL_RGB, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_12BIT },
+ { MEDIA_BUS_FMT_YVYU12_2X12, ADV7842_OP_CH_SEL_RGB, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422 | ADV7842_OP_FORMAT_SEL_12BIT },
+ { MEDIA_BUS_FMT_UYVY8_1X16, ADV7842_OP_CH_SEL_RBG, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_VYUY8_1X16, ADV7842_OP_CH_SEL_RBG, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YUYV8_1X16, ADV7842_OP_CH_SEL_RGB, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_YVYU8_1X16, ADV7842_OP_CH_SEL_RGB, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_8BIT },
+ { MEDIA_BUS_FMT_UYVY10_1X20, ADV7842_OP_CH_SEL_RBG, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT },
+ { MEDIA_BUS_FMT_VYUY10_1X20, ADV7842_OP_CH_SEL_RBG, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT },
+ { MEDIA_BUS_FMT_YUYV10_1X20, ADV7842_OP_CH_SEL_RGB, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT },
+ { MEDIA_BUS_FMT_YVYU10_1X20, ADV7842_OP_CH_SEL_RGB, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_10BIT },
+ { MEDIA_BUS_FMT_UYVY12_1X24, ADV7842_OP_CH_SEL_RBG, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT },
+ { MEDIA_BUS_FMT_VYUY12_1X24, ADV7842_OP_CH_SEL_RBG, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT },
+ { MEDIA_BUS_FMT_YUYV12_1X24, ADV7842_OP_CH_SEL_RGB, false, false,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT },
+ { MEDIA_BUS_FMT_YVYU12_1X24, ADV7842_OP_CH_SEL_RGB, false, true,
+ ADV7842_OP_MODE_SEL_SDR_422_2X | ADV7842_OP_FORMAT_SEL_12BIT },
+};
+
+static const struct adv7842_format_info *
+adv7842_format_info(struct adv7842_state *state, u32 code)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(adv7842_formats); ++i) {
+ if (adv7842_formats[i].code == code)
+ return &adv7842_formats[i];
+ }
+
+ return NULL;
+}
+
/* ----------------------------------------------------------------------- */
static inline bool is_analog_input(struct v4l2_subdev *sd)
@@ -1440,6 +1547,8 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
}
bt->interlaced = stdi.interlaced ?
V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE;
+ bt->standards = V4L2_DV_BT_STD_CEA861 | V4L2_DV_BT_STD_DMT |
+ V4L2_DV_BT_STD_GTF | V4L2_DV_BT_STD_CVT;
if (is_digital_input(sd)) {
uint32_t freq;
@@ -1478,6 +1587,10 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd,
hdmi_read(sd, 0x31)) / 2;
bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 +
hdmi_read(sd, 0x35)) / 2;
+ } else {
+ bt->il_vfrontporch = 0;
+ bt->il_vsync = 0;
+ bt->il_vbackporch = 0;
}
adv7842_fill_optional_dv_timings_fields(sd, timings);
} else {
@@ -1871,47 +1984,145 @@ static int adv7842_enum_mbus_code(struct v4l2_subdev *sd,
struct v4l2_subdev_pad_config *cfg,
struct v4l2_subdev_mbus_code_enum *code)
{
- if (code->pad || code->index)
+ if (code->index >= ARRAY_SIZE(adv7842_formats))
return -EINVAL;
- /* Good enough for now */
- code->code = MEDIA_BUS_FMT_FIXED;
+ code->code = adv7842_formats[code->index].code;
return 0;
}
-static int adv7842_fill_fmt(struct v4l2_subdev *sd,
- struct v4l2_subdev_pad_config *cfg,
- struct v4l2_subdev_format *format)
+static void adv7842_fill_format(struct adv7842_state *state,
+ struct v4l2_mbus_framefmt *format)
+{
+ memset(format, 0, sizeof(*format));
+
+ format->width = state->timings.bt.width;
+ format->height = state->timings.bt.height;
+ format->field = V4L2_FIELD_NONE;
+ format->colorspace = V4L2_COLORSPACE_SRGB;
+
+ if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO)
+ format->colorspace = (state->timings.bt.height <= 576) ?
+ V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
+}
+
+/*
+ * Compute the op_ch_sel value required to obtain on the bus the component order
+ * corresponding to the selected format taking into account bus reordering
+ * applied by the board at the output of the device.
+ *
+ * The following table gives the op_ch_value from the format component order
+ * (expressed as op_ch_sel value in column) and the bus reordering (expressed as
+ * adv7842_bus_order value in row).
+ *
+ * | GBR(0) GRB(1) BGR(2) RGB(3) BRG(4) RBG(5)
+ * ----------+-------------------------------------------------
+ * RGB (NOP) | GBR GRB BGR RGB BRG RBG
+ * GRB (1-2) | BGR RGB GBR GRB RBG BRG
+ * RBG (2-3) | GRB GBR BRG RBG BGR RGB
+ * BGR (1-3) | RBG BRG RGB BGR GRB GBR
+ * BRG (ROR) | BRG RBG GRB GBR RGB BGR
+ * GBR (ROL) | RGB BGR RBG BRG GBR GRB
+ */
+static unsigned int adv7842_op_ch_sel(struct adv7842_state *state)
+{
+#define _SEL(a, b, c, d, e, f) { \
+ ADV7842_OP_CH_SEL_##a, ADV7842_OP_CH_SEL_##b, ADV7842_OP_CH_SEL_##c, \
+ ADV7842_OP_CH_SEL_##d, ADV7842_OP_CH_SEL_##e, ADV7842_OP_CH_SEL_##f }
+#define _BUS(x) [ADV7842_BUS_ORDER_##x]
+
+ static const unsigned int op_ch_sel[6][6] = {
+ _BUS(RGB) /* NOP */ = _SEL(GBR, GRB, BGR, RGB, BRG, RBG),
+ _BUS(GRB) /* 1-2 */ = _SEL(BGR, RGB, GBR, GRB, RBG, BRG),
+ _BUS(RBG) /* 2-3 */ = _SEL(GRB, GBR, BRG, RBG, BGR, RGB),
+ _BUS(BGR) /* 1-3 */ = _SEL(RBG, BRG, RGB, BGR, GRB, GBR),
+ _BUS(BRG) /* ROR */ = _SEL(BRG, RBG, GRB, GBR, RGB, BGR),
+ _BUS(GBR) /* ROL */ = _SEL(RGB, BGR, RBG, BRG, GBR, GRB),
+ };
+
+ return op_ch_sel[state->pdata.bus_order][state->format->op_ch_sel >> 5];
+}
+
+static void adv7842_setup_format(struct adv7842_state *state)
+{
+ struct v4l2_subdev *sd = &state->sd;
+
+ io_write_clr_set(sd, 0x02, 0x02,
+ state->format->rgb_out ? ADV7842_RGB_OUT : 0);
+ io_write(sd, 0x03, state->format->op_format_sel |
+ state->pdata.op_format_mode_sel);
+ io_write_clr_set(sd, 0x04, 0xe0, adv7842_op_ch_sel(state));
+ io_write_clr_set(sd, 0x05, 0x01,
+ state->format->swap_cb_cr ? ADV7842_OP_SWAP_CB_CR : 0);
+}
+
+static int adv7842_get_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
{
- struct v4l2_mbus_framefmt *fmt = &format->format;
struct adv7842_state *state = to_state(sd);
- if (format->pad)
+ if (format->pad != ADV7842_PAD_SOURCE)
return -EINVAL;
- fmt->width = state->timings.bt.width;
- fmt->height = state->timings.bt.height;
- fmt->code = MEDIA_BUS_FMT_FIXED;
- fmt->field = V4L2_FIELD_NONE;
-
if (state->mode == ADV7842_MODE_SDP) {
/* SPD block */
- if (!(sdp_read(sd, 0x5A) & 0x01))
+ if (!(sdp_read(sd, 0x5a) & 0x01))
return -EINVAL;
- fmt->width = 720;
+ format->format.code = MEDIA_BUS_FMT_YUYV8_2X8;
+ format->format.width = 720;
/* valid signal */
if (state->norm & V4L2_STD_525_60)
- fmt->height = 480;
+ format->format.height = 480;
else
- fmt->height = 576;
- fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
+ format->format.height = 576;
+ format->format.colorspace = V4L2_COLORSPACE_SMPTE170M;
return 0;
}
- fmt->colorspace = V4L2_COLORSPACE_SRGB;
- if (state->timings.bt.flags & V4L2_DV_FL_IS_CE_VIDEO) {
- fmt->colorspace = (state->timings.bt.height <= 576) ?
- V4L2_COLORSPACE_SMPTE170M : V4L2_COLORSPACE_REC709;
+ adv7842_fill_format(state, &format->format);
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ format->format.code = fmt->code;
+ } else {
+ format->format.code = state->format->code;
}
+
+ return 0;
+}
+
+static int adv7842_set_format(struct v4l2_subdev *sd,
+ struct v4l2_subdev_pad_config *cfg,
+ struct v4l2_subdev_format *format)
+{
+ struct adv7842_state *state = to_state(sd);
+ const struct adv7842_format_info *info;
+
+ if (format->pad != ADV7842_PAD_SOURCE)
+ return -EINVAL;
+
+ if (state->mode == ADV7842_MODE_SDP)
+ return adv7842_get_format(sd, cfg, format);
+
+ info = adv7842_format_info(state, format->format.code);
+ if (info == NULL)
+ info = adv7842_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
+
+ adv7842_fill_format(state, &format->format);
+ format->format.code = info->code;
+
+ if (format->which == V4L2_SUBDEV_FORMAT_TRY) {
+ struct v4l2_mbus_framefmt *fmt;
+
+ fmt = v4l2_subdev_get_try_format(sd, cfg, format->pad);
+ fmt->code = format->format.code;
+ } else {
+ state->format = info;
+ adv7842_setup_format(state);
+ }
+
return 0;
}
@@ -2551,14 +2762,11 @@ static int adv7842_core_init(struct v4l2_subdev *sd)
0xf0 |
pdata->alt_gamma << 3 |
pdata->op_656_range << 2 |
- pdata->rgb_out << 1 |
pdata->alt_data_sat << 0);
- io_write(sd, 0x03, pdata->op_format_sel);
- io_write_and_or(sd, 0x04, 0x1f, pdata->op_ch_sel << 5);
io_write_and_or(sd, 0x05, 0xf0, pdata->blank_data << 3 |
pdata->insert_av_codes << 2 |
- pdata->replicate_av_codes << 1 |
- pdata->invert_cbcr << 0);
+ pdata->replicate_av_codes << 1);
+ adv7842_setup_format(state);
/* HDMI audio */
hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */
@@ -2818,13 +3026,13 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = {
};
static const struct v4l2_subdev_pad_ops adv7842_pad_ops = {
+ .enum_mbus_code = adv7842_enum_mbus_code,
+ .get_fmt = adv7842_get_format,
+ .set_fmt = adv7842_set_format,
.get_edid = adv7842_get_edid,
.set_edid = adv7842_set_edid,
.enum_dv_timings = adv7842_enum_dv_timings,
.dv_timings_cap = adv7842_dv_timings_cap,
- .enum_mbus_code = adv7842_enum_mbus_code,
- .get_fmt = adv7842_fill_fmt,
- .set_fmt = adv7842_fill_fmt,
};
static const struct v4l2_subdev_ops adv7842_ops = {
@@ -2991,6 +3199,7 @@ static int adv7842_probe(struct i2c_client *client,
/* platform data */
state->pdata = *pdata;
state->timings = cea640x480;
+ state->format = adv7842_format_info(state, MEDIA_BUS_FMT_YUYV8_2X8);
sd = &state->sd;
v4l2_i2c_subdev_init(sd, client, &adv7842_ops);