aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/i2c/adv7511-v4l2.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/i2c/adv7511-v4l2.c')
-rw-r--r--drivers/media/i2c/adv7511-v4l2.c531
1 files changed, 504 insertions, 27 deletions
diff --git a/drivers/media/i2c/adv7511-v4l2.c b/drivers/media/i2c/adv7511-v4l2.c
index 62763ec4cd07..6ed9728d54cc 100644
--- a/drivers/media/i2c/adv7511-v4l2.c
+++ b/drivers/media/i2c/adv7511-v4l2.c
@@ -52,6 +52,7 @@ MODULE_LICENSE("GPL v2");
#define ADV7511_MAX_HEIGHT 1200
#define ADV7511_MIN_PIXELCLOCK 20000000
#define ADV7511_MAX_PIXELCLOCK 225000000
+#define XYLON_LOGICVC_INTG
#define ADV7511_MAX_ADDRS (3)
@@ -79,7 +80,50 @@ struct adv7511_state_edid {
bool complete;
};
+struct adv7511_in_params {
+ uint8_t input_id;
+ uint8_t input_style;
+ uint8_t input_color_depth;
+ uint8_t bit_justification;
+ uint8_t hsync_polarity;
+ uint8_t vsync_polarity;
+ uint8_t clock_delay;
+};
+
+struct adv7511_csc_coeff {
+ uint16_t a1;
+ uint16_t a2;
+ uint16_t a3;
+ uint16_t a4;
+ uint16_t b1;
+ uint16_t b2;
+ uint16_t b3;
+ uint16_t b4;
+ uint16_t c1;
+ uint16_t c2;
+ uint16_t c3;
+ uint16_t c4;
+};
+
+struct adv7511_out_params {
+ bool hdmi_mode;
+ uint8_t output_format;
+ uint8_t output_color_space;
+ uint8_t up_conversion;
+ uint8_t csc_enable;
+ uint8_t csc_scaling_factor;
+ struct adv7511_csc_coeff csc_coeff;
+};
+
+struct adv7511_config {
+ struct adv7511_in_params in_params;
+ struct adv7511_out_params out_params;
+ bool embedded_sync;
+ bool loaded;
+};
+
struct adv7511_state {
+ struct adv7511_config cfg;
struct adv7511_platform_data pdata;
struct v4l2_subdev sd;
struct media_pad pad;
@@ -379,6 +423,10 @@ static void adv7511_set_rgb_quantization_mode(struct v4l2_subdev *sd, struct v4l
{
struct adv7511_state *state = get_adv7511_state(sd);
+#ifdef XYLON_LOGICVC_INTG
+ return 0;
+#endif
+
/* Only makes sense for RGB formats */
if (state->fmt_code != MEDIA_BUS_FMT_RGB888_1X24) {
/* so just keep quantization */
@@ -1527,34 +1575,278 @@ static void adv7511_audio_setup(struct v4l2_subdev *sd)
adv7511_s_routing(sd, 0, 0, 0);
}
-/* Configure hdmi transmitter. */
-static void adv7511_setup(struct v4l2_subdev *sd)
+static void adv7511_set_ofdt_config(struct v4l2_subdev *sd)
{
struct adv7511_state *state = get_adv7511_state(sd);
+ struct adv7511_config *config = &state->cfg;
+ uint8_t val_mask, val;
v4l2_dbg(1, debug, sd, "%s\n", __func__);
- /* Input format: RGB 4:4:4 */
- adv7511_wr_and_or(sd, 0x15, 0xf0, 0x0);
- /* Output format: RGB 4:4:4 */
- adv7511_wr_and_or(sd, 0x16, 0x7f, 0x0);
- /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, Aspect ratio: 16:9 */
- adv7511_wr_and_or(sd, 0x17, 0xf9, 0x06);
- /* Disable pixel repetition */
- adv7511_wr_and_or(sd, 0x3b, 0x9f, 0x0);
- /* Disable CSC */
- adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0);
- /* Output format: RGB 4:4:4, Active Format Information is valid,
- * underscanned */
- adv7511_wr_and_or(sd, 0x55, 0x9c, 0x12);
- /* AVI Info frame packet enable, Audio Info frame disable */
+ /* Input format */
+ val_mask = 0;
+ switch (config->in_params.input_id) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ case 2:
+ val = 0x02;
+ config->embedded_sync = true;
+ break;
+ case 3:
+ val = 0x03;
+ break;
+ case 4:
+ val = 0x04;
+ config->embedded_sync = true;
+ break;
+ case 5:
+ val = 0x05;
+ break;
+ case 6:
+ val = 0x06;
+ break;
+ case 7:
+ val = 0x07;
+ break;
+ case 8:
+ val = 0x08;
+ config->embedded_sync = true;
+ break;
+ }
+ val_mask |= val;
+ adv7511_wr(sd, 0x15, val_mask);
+
+ /* Output format */
+ val_mask = 0;
+ switch (config->out_params.output_color_space) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ }
+ val_mask |= (val << 0);
+ switch (config->in_params.input_style) {
+ case 1:
+ val = 0x02;
+ break;
+ case 2:
+ val = 0x01;
+ break;
+ case 3:
+ val = 0x03;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ val_mask |= (val << 2);
+ switch (config->in_params.input_color_depth) {
+ case 8:
+ val = 0x03;
+ break;
+ case 10:
+ val = 0x01;
+ break;
+ case 12:
+ val = 0x02;
+ break;
+ default:
+ val = 0x00;
+ break;
+ }
+ val_mask |= (val << 4);
+ switch (config->out_params.output_format) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ }
+ val_mask |= (val << 7);
+ adv7511_wr(sd, 0x16, val_mask);
+
+ /* H, V sync polarity, interpolation style */
+ val_mask = 0;
+ switch (config->out_params.up_conversion) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ }
+ val_mask |= (val << 2);
+ switch (config->in_params.hsync_polarity) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ }
+ val_mask |= (val << 5);
+ switch (config->in_params.vsync_polarity) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ }
+ val_mask |= (val << 6);
+ adv7511_wr(sd, 0x17, val_mask);
+
+ /* CSC mode, CSC coefficients */
+ if (config->out_params.csc_enable) {
+ switch (config->out_params.csc_scaling_factor) {
+ case 1:
+ val = 0x00;
+ break;
+ case 2:
+ val = 0x01;
+ break;
+ case 4:
+ default:
+ val = 0x02;
+ break;
+ }
+ adv7511_csc_conversion_mode(sd, val);
+ adv7511_csc_coeff(sd,
+ config->out_params.csc_coeff.a1,
+ config->out_params.csc_coeff.a2,
+ config->out_params.csc_coeff.a3,
+ config->out_params.csc_coeff.a4,
+ config->out_params.csc_coeff.b1,
+ config->out_params.csc_coeff.b2,
+ config->out_params.csc_coeff.b3,
+ config->out_params.csc_coeff.b4,
+ config->out_params.csc_coeff.c1,
+ config->out_params.csc_coeff.c2,
+ config->out_params.csc_coeff.c3,
+ config->out_params.csc_coeff.c4);
+ /* enable CSC */
+ adv7511_wr_and_or(sd, 0x18, 0x7f, 0x80);
+ /* AVI infoframe: Limited range RGB (16-235) */
+ adv7511_wr_and_or(sd, 0x57, 0xf3, 0x04);
+ }
+
+ /* AVI Info, Audio Info */
adv7511_wr_and_or(sd, 0x44, 0xe7, 0x10);
- /* Colorimetry, Active format aspect ratio: same as picure. */
- adv7511_wr(sd, 0x56, 0xa8);
- /* No encryption */
- adv7511_wr_and_or(sd, 0xaf, 0xed, 0x0);
- /* Positive clk edge capture for input video clock */
- adv7511_wr_and_or(sd, 0xba, 0x1f, 0x60);
+ /* Video input justification */
+ val_mask = 0;
+ switch (config->in_params.bit_justification) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ case 2:
+ val = 0x02;
+ break;
+ }
+ val_mask |= (val << 3);
+ adv7511_wr(sd, 0x48, val_mask);
+
+ /* Output format */
+ val_mask = 0x00;
+ if (config->out_params.output_format == 1) {
+ if (config->out_params.output_color_space == 0)
+ val_mask = 0x02;
+ else if (config->out_params.output_format == 1)
+ val_mask = 0x01;
+ }
+ val_mask <<= 5;
+ adv7511_wr(sd, 0x55, val_mask);
+
+ /* Picture format aspect ratio */
+ adv7511_wr(sd, 0x56, 0x28);
+
+ /* HDCP, Frame encryption, HDMI/DVI */
+ val_mask = 0x04;
+ if (config->out_params.hdmi_mode)
+ val_mask |= 0x02;
+ adv7511_wr(sd, 0xaf, val_mask);
+
+ /* Capture for input video clock */
+ val_mask = 0;
+ switch (config->in_params.clock_delay) {
+ default:
+ case 0:
+ val = 0x00;
+ break;
+ case 1:
+ val = 0x01;
+ break;
+ case 2:
+ val = 0x02;
+ break;
+ case 3:
+ val = 0x03;
+ break;
+ case 4:
+ val = 0x04;
+ break;
+ case 5:
+ val = 0x05;
+ break;
+ case 6:
+ val = 0x06;
+ break;
+ case 7:
+ val = 0x07;
+ break;
+ }
+ val_mask |= (val << 5);
+ adv7511_wr_and_or(sd, 0xba, 0x1f, val_mask);
+}
+
+/* Configure hdmi transmitter. */
+static void adv7511_setup(struct v4l2_subdev *sd)
+{
+ struct adv7511_state *state = get_adv7511_state(sd);
+ v4l2_dbg(1, debug, sd, "%s\n", __func__);
+
+ if (!state->cfg.loaded) {
+ /* Input format: RGB 4:4:4 */
+ adv7511_wr_and_or(sd, 0x15, 0xf0, 0x0);
+ /* Output format: RGB 4:4:4 */
+ adv7511_wr_and_or(sd, 0x16, 0x7f, 0x0);
+ /* 1st order interpolation 4:2:2 -> 4:4:4 up conversion, Aspect ratio: 16:9 */
+ adv7511_wr_and_or(sd, 0x17, 0xf9, 0x06);
+ /* Disable pixel repetition */
+ adv7511_wr_and_or(sd, 0x3b, 0x9f, 0x0);
+ /* Disable CSC */
+ adv7511_wr_and_or(sd, 0x18, 0x7f, 0x0);
+ /* Output format: RGB 4:4:4, Active Format Information is valid,
+ * underscanned */
+ adv7511_wr_and_or(sd, 0x55, 0x9c, 0x12);
+ /* AVI Info frame packet enable, Audio Info frame disable */
+ adv7511_wr_and_or(sd, 0x44, 0xe7, 0x10);
+ /* Colorimetry, Active format aspect ratio: same as picure. */
+ adv7511_wr(sd, 0x56, 0xa8);
+ /* No encryption */
+ adv7511_wr_and_or(sd, 0xaf, 0xed, 0x0);
+
+ /* Positive clk edge capture for input video clock */
+ adv7511_wr_and_or(sd, 0xba, 0x1f, 0x60);
+ } else {
+ adv7511_set_ofdt_config(sd);
+ }
adv7511_audio_setup(sd);
@@ -1792,6 +2084,181 @@ static void adv7511_init_setup(struct v4l2_subdev *sd)
adv7511_cec_write(sd, 0x4e, ratio << 2);
}
+
+static void adv7511_get_ofdt_config(struct i2c_client *client,
+ struct adv7511_state *state)
+{
+ struct device_node *dn = client->dev.of_node;
+ struct device_node *np;
+ struct adv7511_config *config = &state->cfg;
+ u32 const *prop;
+ int size;
+ bool vin_loaded, vout_loaded;
+
+ vin_loaded = vout_loaded = false;
+
+ prop = of_get_property(dn, "edid-addr", &size);
+ if (prop)
+ state->pdata.i2c_edid = (uint8_t)be32_to_cpup(prop);
+
+ prop = of_get_property(dn, "pktmem-addr", &size);
+ if (prop)
+ state->pdata.i2c_pktmem = (uint8_t)be32_to_cpup(prop);
+
+ prop = of_get_property(dn, "cec-addr", &size);
+ if (prop)
+ state->pdata.i2c_cec = (uint8_t)be32_to_cpup(prop);
+
+ np = of_find_node_by_name(dn, "video-input");
+ if (np) {
+ prop = of_get_property(np, "input-id", &size);
+ if (prop)
+ config->in_params.input_id =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "input-style", &size);
+ if (prop)
+ config->in_params.input_style =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "input-color-depth", &size);
+ if (prop)
+ config->in_params.input_color_depth =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "bit-justification", &size);
+ if (prop)
+ config->in_params.bit_justification =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "hsync-polarity", &size);
+ if (prop)
+ config->in_params.hsync_polarity =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "vsync-polarity", &size);
+ if (prop)
+ config->in_params.vsync_polarity =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "clock-delay", &size);
+ if (prop)
+ config->in_params.clock_delay =
+ (uint8_t)be32_to_cpup(prop);
+ vin_loaded = true;
+ } else {
+ pr_info("No video input configuration, using device default\n");
+ }
+
+ np = of_find_node_by_name(dn, "video-output");
+ if (np) {
+ prop = of_get_property(np, "hdmi-mode", &size);
+ if (prop) {
+ if (be32_to_cpup(prop) == 1)
+ config->out_params.hdmi_mode = true;
+ }
+ prop = of_get_property(np, "output-format", &size);
+ if (prop)
+ config->out_params.output_format =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "output-color-space", &size);
+ if (prop)
+ config->out_params.output_color_space =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "up-conversion", &size);
+ if (prop)
+ config->out_params.up_conversion =
+ (uint8_t)be32_to_cpup(prop);
+ prop = of_get_property(np, "csc-enable", &size);
+ if (prop)
+ config->out_params.csc_enable =
+ (uint8_t)be32_to_cpup(prop);
+ if (config->out_params.csc_enable) {
+ prop = of_get_property(np, "csc-scaling-factor", &size);
+ if (prop) {
+ config->out_params.csc_scaling_factor =
+ (uint8_t)be32_to_cpup(prop);
+ }
+ np = of_find_node_by_name(dn, "csc-coefficients");
+ if (np) {
+ prop = of_get_property(np, "a1", &size);
+ if (prop) {
+ config->out_params.csc_coeff.a1 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "a2", &size);
+ if (prop) {
+ config->out_params.csc_coeff.a2 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "a3", &size);
+ if (prop) {
+ config->out_params.csc_coeff.a3 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "a4", &size);
+ if (prop) {
+ config->out_params.csc_coeff.a4 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "b1", &size);
+ if (prop) {
+ config->out_params.csc_coeff.b1 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "b2", &size);
+ if (prop) {
+ config->out_params.csc_coeff.b2 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "b3", &size);
+ if (prop) {
+ config->out_params.csc_coeff.b3 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "b4", &size);
+ if (prop) {
+ config->out_params.csc_coeff.b4 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "c1", &size);
+ if (prop) {
+ config->out_params.csc_coeff.c1 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "c2", &size);
+ if (prop) {
+ config->out_params.csc_coeff.c2 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "c3", &size);
+ if (prop) {
+ config->out_params.csc_coeff.c3 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ prop = of_get_property(np, "c4", &size);
+ if (prop) {
+ config->out_params.csc_coeff.c4 =
+ (uint16_t)be32_to_cpup(prop);
+ }
+ } else {
+ pr_info("No CSC coefficients, using default\n");
+ }
+ }
+ vout_loaded = true;
+ } else {
+ pr_info("No video output configuration, using device default\n");
+ }
+
+ if (vin_loaded && vout_loaded)
+ config->loaded = true;
+}
+
+struct v4l2_subdev *adv7511_subdev(struct v4l2_subdev *sd)
+{
+ static struct v4l2_subdev *subdev;
+
+ if (sd)
+ subdev = sd;
+
+ return subdev;
+}
+EXPORT_SYMBOL(adv7511_subdev);
+
static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct adv7511_state *state;
@@ -1809,11 +2276,17 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
if (!state)
return -ENOMEM;
- /* Platform data */
- if (!pdata) {
- v4l_err(client, "No platform data!\n");
- return -ENODEV;
+ if (client->dev.of_node) {
+ adv7511_get_ofdt_config(client, state);
+ } else {
+ /* Platform data */
+ if (!pdata) {
+ v4l_err(client, "No platform data!\n");
+ return -ENODEV;
+ }
+ memcpy(&state->pdata, pdata, sizeof(state->pdata));
}
+
memcpy(&state->pdata, pdata, sizeof(state->pdata));
state->fmt_code = MEDIA_BUS_FMT_RGB888_1X24;
state->colorspace = V4L2_COLORSPACE_SRGB;
@@ -1824,6 +2297,7 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
client->addr << 1);
v4l2_i2c_subdev_init(sd, client, &adv7511_ops);
+ adv7511_subdev(sd);
sd->internal_ops = &adv7511_int_ops;
hdl = &state->hdl;
@@ -1917,7 +2391,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
INIT_DELAYED_WORK(&state->edid_handler, adv7511_edid_handler);
+#ifndef XYLON_LOGICVC_INTG
adv7511_init_setup(sd);
+#endif
#if IS_ENABLED(CONFIG_VIDEO_ADV7511_CEC)
state->cec_adap = cec_allocate_adapter(&adv7511_cec_adap_ops,
@@ -1931,8 +2407,9 @@ static int adv7511_probe(struct i2c_client *client, const struct i2c_device_id *
#endif
adv7511_set_isr(sd, true);
+#ifndef XYLON_LOGICVC_INTG
adv7511_check_monitor_present_status(sd);
-
+#endif
v4l2_info(sd, "%s found @ 0x%x (%s)\n", client->name,
client->addr << 1, client->adapter->name);
return 0;