diff options
Diffstat (limited to 'drivers/media/i2c/adv7842.c')
-rw-r--r-- | drivers/media/i2c/adv7842.c | 646 |
1 files changed, 412 insertions, 234 deletions
diff --git a/drivers/media/i2c/adv7842.c b/drivers/media/i2c/adv7842.c index b154f36740b4..1effc21e1cdd 100644 --- a/drivers/media/i2c/adv7842.c +++ b/drivers/media/i2c/adv7842.c @@ -20,10 +20,13 @@ /* * References (c = chapter, p = page): - * REF_01 - Analog devices, ADV7842, Register Settings Recommendations, - * Revision 2.5, June 2010 - * REF_02 - Analog devices, Register map documentation, Documentation of - * the register maps, Software manual, Rev. F, June 2010 + * REF_01 - Analog devices, ADV7842, + * Register Settings Recommendations, Rev. 1.9, April 2011 + * REF_02 - Analog devices, Software User Guide, UG-206, + * ADV7842 I2C Register Maps, Rev. 0, November 2010 + * REF_03 - Analog devices, Hardware User Guide, UG-214, + * ADV7842 Fast Switching 2:1 HDMI 1.4 Receiver with 3D-Comb + * Decoder and Digitizer , Rev. 0, January 2011 */ @@ -61,6 +64,7 @@ MODULE_LICENSE("GPL"); */ struct adv7842_state { + struct adv7842_platform_data pdata; struct v4l2_subdev sd; struct media_pad pad; struct v4l2_ctrl_handler hdl; @@ -81,7 +85,7 @@ struct adv7842_state { bool is_cea_format; struct workqueue_struct *work_queues; struct delayed_work delayed_work_enable_hotplug; - bool connector_hdmi; + bool restart_stdi_once; bool hdmi_port_a; /* i2c clients */ @@ -491,6 +495,11 @@ static inline int hdmi_write(struct v4l2_subdev *sd, u8 reg, u8 val) return adv_smbus_write_byte_data(state->i2c_hdmi, reg, val); } +static inline int hdmi_write_and_or(struct v4l2_subdev *sd, u8 reg, u8 mask, u8 val) +{ + return hdmi_write(sd, reg, (hdmi_read(sd, reg) & mask) | val); +} + static inline int cp_read(struct v4l2_subdev *sd, u8 reg) { struct adv7842_state *state = to_state(sd); @@ -532,7 +541,7 @@ static void main_reset(struct v4l2_subdev *sd) adv_smbus_write_byte_no_check(client, 0xff, 0x80); - mdelay(2); + mdelay(5); } /* ----------------------------------------------------------------------- */ @@ -587,10 +596,10 @@ static void adv7842_delayed_work_enable_hotplug(struct work_struct *work) v4l2_dbg(2, debug, sd, "%s: enable hotplug on ports: 0x%x\n", __func__, present); - if (present & 0x1) - mask |= 0x20; /* port A */ - if (present & 0x2) - mask |= 0x10; /* port B */ + if (present & (0x04 << ADV7842_EDID_PORT_A)) + mask |= 0x20; + if (present & (0x04 << ADV7842_EDID_PORT_B)) + mask |= 0x10; io_write_and_or(sd, 0x20, 0xcf, mask); } @@ -679,14 +688,12 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7842_state *state = to_state(sd); const u8 *val = state->hdmi_edid.edid; - u8 cur_mask = rep_read(sd, 0x77) & 0x0c; - u8 mask = port == 0 ? 0x4 : 0x8; int spa_loc = edid_spa_location(val); int err = 0; int i; - v4l2_dbg(2, debug, sd, "%s: write EDID on port %d (spa at 0x%x)\n", - __func__, port, spa_loc); + v4l2_dbg(2, debug, sd, "%s: write EDID on port %c (spa at 0x%x)\n", + __func__, (port == ADV7842_EDID_PORT_A) ? 'A' : 'B', spa_loc); /* HPA disable on port A and B */ io_write_and_or(sd, 0x20, 0xcf, 0x00); @@ -694,6 +701,9 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) /* Disable I2C access to internal EDID ram from HDMI DDC ports */ rep_write_and_or(sd, 0x77, 0xf3, 0x00); + if (!state->hdmi_edid.present) + return 0; + /* edid segment pointer '0' for HDMI ports */ rep_write_and_or(sd, 0x77, 0xef, 0x00); @@ -703,44 +713,32 @@ static int edid_write_hdmi_segment(struct v4l2_subdev *sd, u8 port) if (err) return err; - if (spa_loc > 0) { - if (port == 0) { - /* port A SPA */ - rep_write(sd, 0x72, val[spa_loc]); - rep_write(sd, 0x73, val[spa_loc + 1]); - } else { - /* port B SPA */ - rep_write(sd, 0x74, val[spa_loc]); - rep_write(sd, 0x75, val[spa_loc + 1]); - } - rep_write(sd, 0x76, spa_loc); + if (spa_loc < 0) + spa_loc = 0xc0; /* Default value [REF_02, p. 199] */ + + if (port == ADV7842_EDID_PORT_A) { + rep_write(sd, 0x72, val[spa_loc]); + rep_write(sd, 0x73, val[spa_loc + 1]); } else { - /* default register values for SPA */ - if (port == 0) { - /* port A SPA */ - rep_write(sd, 0x72, 0); - rep_write(sd, 0x73, 0); - } else { - /* port B SPA */ - rep_write(sd, 0x74, 0); - rep_write(sd, 0x75, 0); - } - rep_write(sd, 0x76, 0xc0); + rep_write(sd, 0x74, val[spa_loc]); + rep_write(sd, 0x75, val[spa_loc + 1]); } - rep_write_and_or(sd, 0x77, 0xbf, 0x00); + rep_write(sd, 0x76, spa_loc & 0xff); + rep_write_and_or(sd, 0x77, 0xbf, (spa_loc >> 2) & 0x40); /* Calculates the checksums and enables I2C access to internal * EDID ram from HDMI DDC ports */ - rep_write_and_or(sd, 0x77, 0xf3, mask | cur_mask); + rep_write_and_or(sd, 0x77, 0xf3, state->hdmi_edid.present); for (i = 0; i < 1000; i++) { - if (rep_read(sd, 0x7d) & mask) + if (rep_read(sd, 0x7d) & state->hdmi_edid.present) break; mdelay(1); } if (i == 1000) { - v4l_err(client, "error enabling edid on port %d\n", port); + v4l_err(client, "error enabling edid on port %c\n", + (port == ADV7842_EDID_PORT_A) ? 'A' : 'B'); return -EIO; } @@ -927,7 +925,7 @@ static int configure_predefined_video_timings(struct v4l2_subdev *sd, cp_write(sd, 0x27, 0x00); cp_write(sd, 0x28, 0x00); cp_write(sd, 0x29, 0x00); - cp_write(sd, 0x8f, 0x00); + cp_write(sd, 0x8f, 0x40); cp_write(sd, 0x90, 0x00); cp_write(sd, 0xa5, 0x00); cp_write(sd, 0xa6, 0x00); @@ -1033,34 +1031,60 @@ static void set_rgb_quantization_range(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); + v4l2_dbg(2, debug, sd, "%s: rgb_quantization_range = %d\n", + __func__, state->rgb_quantization_range); + switch (state->rgb_quantization_range) { case V4L2_DV_RGB_RANGE_AUTO: - /* automatic */ - if (is_digital_input(sd) && !(hdmi_read(sd, 0x05) & 0x80)) { - /* receiving DVI-D signal */ - - /* ADV7842 selects RGB limited range regardless of - input format (CE/IT) in automatic mode */ - if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); - - } else { - /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); - } - } else { - /* receiving HDMI or analog signal, set automode */ + if (state->mode == ADV7842_MODE_RGB) { + /* Receiving analog RGB signal + * Set RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + break; + } + + if (state->mode == ADV7842_MODE_COMP) { + /* Receiving analog YPbPr signal + * Set automode */ io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } + + if (hdmi_read(sd, 0x05) & 0x80) { + /* Receiving HDMI signal + * Set automode */ + io_write_and_or(sd, 0x02, 0x0f, 0xf0); + break; + } + + /* Receiving DVI-D signal + * ADV7842 selects RGB limited range regardless of + * input format (CE/IT) in automatic mode */ + if (state->timings.bt.standards & V4L2_DV_BT_STD_CEA861) { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); } break; case V4L2_DV_RGB_RANGE_LIMITED: - /* RGB limited range (16-235) */ - io_write_and_or(sd, 0x02, 0x0f, 0x00); + if (state->mode == ADV7842_MODE_COMP) { + /* YCrCb limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x20); + } else { + /* RGB limited range (16-235) */ + io_write_and_or(sd, 0x02, 0x0f, 0x00); + } break; case V4L2_DV_RGB_RANGE_FULL: - /* RGB full range (0-255) */ - io_write_and_or(sd, 0x02, 0x0f, 0x10); + if (state->mode == ADV7842_MODE_COMP) { + /* YCrCb full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x60); + } else { + /* RGB full range (0-255) */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + } break; } } @@ -1298,7 +1322,7 @@ static int adv7842_dv_timings_cap(struct v4l2_subdev *sd, } /* Fill the optional fields .standards and .flags in struct v4l2_dv_timings - if the format is listed in adv7604_timings[] */ + if the format is listed in adv7842_timings[] */ static void adv7842_fill_optional_dv_timings_fields(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings) { @@ -1314,119 +1338,106 @@ static int adv7842_query_dv_timings(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt = &timings->bt; struct stdi_readback stdi = { 0 }; + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + /* SDP block */ if (state->mode == ADV7842_MODE_SDP) return -ENODATA; /* read STDI */ if (read_stdi(sd, &stdi)) { + state->restart_stdi_once = true; v4l2_dbg(1, debug, sd, "%s: no valid signal\n", __func__); return -ENOLINK; } bt->interlaced = stdi.interlaced ? V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | - ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); - bt->vsync = stdi.lcvs; if (is_digital_input(sd)) { - bool lock = hdmi_read(sd, 0x04) & 0x02; - bool interlaced = hdmi_read(sd, 0x0b) & 0x20; - unsigned w = (hdmi_read(sd, 0x07) & 0x1f) * 256 + hdmi_read(sd, 0x08); - unsigned h = (hdmi_read(sd, 0x09) & 0x1f) * 256 + hdmi_read(sd, 0x0a); - unsigned w_total = (hdmi_read(sd, 0x1e) & 0x3f) * 256 + - hdmi_read(sd, 0x1f); - unsigned h_total = ((hdmi_read(sd, 0x26) & 0x3f) * 256 + - hdmi_read(sd, 0x27)) / 2; - unsigned freq = (((hdmi_read(sd, 0x51) << 1) + - (hdmi_read(sd, 0x52) >> 7)) * 1000000) + - ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; - int i; + uint32_t freq; - if (is_hdmi(sd)) { - /* adjust for deep color mode */ - freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0)>>6) * 2 + 8); - } - - /* No lock? */ - if (!lock) { - v4l2_dbg(1, debug, sd, "%s: no lock on TMDS signal\n", __func__); - return -ENOLCK; - } - /* Interlaced? */ - if (interlaced) { - v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); - return -ERANGE; - } - - for (i = 0; v4l2_dv_timings_presets[i].bt.width; i++) { - const struct v4l2_bt_timings *bt = &v4l2_dv_timings_presets[i].bt; - - if (!v4l2_valid_dv_timings(&v4l2_dv_timings_presets[i], - adv7842_get_dv_timings_cap(sd), - adv7842_check_dv_timings, NULL)) - continue; - if (w_total != htotal(bt) || h_total != vtotal(bt)) - continue; + timings->type = V4L2_DV_BT_656_1120; - if (w != bt->width || h != bt->height) - continue; + bt->width = (hdmi_read(sd, 0x07) & 0x0f) * 256 + hdmi_read(sd, 0x08); + bt->height = (hdmi_read(sd, 0x09) & 0x0f) * 256 + hdmi_read(sd, 0x0a); + freq = (hdmi_read(sd, 0x06) * 1000000) + + ((hdmi_read(sd, 0x3b) & 0x30) >> 4) * 250000; - if (abs(freq - bt->pixelclock) > 1000000) - continue; - *timings = v4l2_dv_timings_presets[i]; - return 0; + if (is_hdmi(sd)) { + /* adjust for deep color mode */ + freq = freq * 8 / (((hdmi_read(sd, 0x0b) & 0xc0) >> 5) + 8); } - - timings->type = V4L2_DV_BT_656_1120; - - bt->width = w; - bt->height = h; - bt->interlaced = (hdmi_read(sd, 0x0b) & 0x20) ? - V4L2_DV_INTERLACED : V4L2_DV_PROGRESSIVE; - bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? - V4L2_DV_VSYNC_POS_POL : 0) | ((hdmi_read(sd, 0x05) & 0x20) ? - V4L2_DV_HSYNC_POS_POL : 0); - bt->pixelclock = (((hdmi_read(sd, 0x51) << 1) + - (hdmi_read(sd, 0x52) >> 7)) * 1000000) + - ((hdmi_read(sd, 0x52) & 0x7f) * 1000000) / 128; - bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x1f) * 256 + + bt->pixelclock = freq; + bt->hfrontporch = (hdmi_read(sd, 0x20) & 0x03) * 256 + hdmi_read(sd, 0x21); - bt->hsync = (hdmi_read(sd, 0x22) & 0x1f) * 256 + + bt->hsync = (hdmi_read(sd, 0x22) & 0x03) * 256 + hdmi_read(sd, 0x23); - bt->hbackporch = (hdmi_read(sd, 0x24) & 0x1f) * 256 + + bt->hbackporch = (hdmi_read(sd, 0x24) & 0x03) * 256 + hdmi_read(sd, 0x25); - bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x3f) * 256 + - hdmi_read(sd, 0x2b)) / 2; - bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x3f) * 256 + - hdmi_read(sd, 0x2d)) / 2; - bt->vsync = ((hdmi_read(sd, 0x2e) & 0x3f) * 256 + - hdmi_read(sd, 0x2f)) / 2; - bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x3f) * 256 + - hdmi_read(sd, 0x31)) / 2; - bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x3f) * 256 + - hdmi_read(sd, 0x33)) / 2; - bt->il_vbackporch = ((hdmi_read(sd, 0x34) & 0x3f) * 256 + - hdmi_read(sd, 0x35)) / 2; - - bt->standards = 0; - bt->flags = 0; - } else { - /* Interlaced? */ - if (stdi.interlaced) { - v4l2_dbg(1, debug, sd, "%s: interlaced video not supported\n", __func__); - return -ERANGE; + bt->vfrontporch = ((hdmi_read(sd, 0x2a) & 0x1f) * 256 + + hdmi_read(sd, 0x2b)) / 2; + bt->vsync = ((hdmi_read(sd, 0x2e) & 0x1f) * 256 + + hdmi_read(sd, 0x2f)) / 2; + bt->vbackporch = ((hdmi_read(sd, 0x32) & 0x1f) * 256 + + hdmi_read(sd, 0x33)) / 2; + bt->polarities = ((hdmi_read(sd, 0x05) & 0x10) ? V4L2_DV_VSYNC_POS_POL : 0) | + ((hdmi_read(sd, 0x05) & 0x20) ? V4L2_DV_HSYNC_POS_POL : 0); + if (bt->interlaced == V4L2_DV_INTERLACED) { + bt->height += (hdmi_read(sd, 0x0b) & 0x0f) * 256 + + hdmi_read(sd, 0x0c); + bt->il_vfrontporch = ((hdmi_read(sd, 0x2c) & 0x1f) * 256 + + hdmi_read(sd, 0x2d)) / 2; + bt->il_vsync = ((hdmi_read(sd, 0x30) & 0x1f) * 256 + + hdmi_read(sd, 0x31)) / 2; + bt->vbackporch = ((hdmi_read(sd, 0x34) & 0x1f) * 256 + + hdmi_read(sd, 0x35)) / 2; } - + adv7842_fill_optional_dv_timings_fields(sd, timings); + } else { + /* find format + * Since LCVS values are inaccurate [REF_03, p. 339-340], + * stdi2dv_timings() is called with lcvs +-1 if the first attempt fails. + */ + if (!stdi2dv_timings(sd, &stdi, timings)) + goto found; + stdi.lcvs += 1; + v4l2_dbg(1, debug, sd, "%s: lcvs + 1 = %d\n", __func__, stdi.lcvs); + if (!stdi2dv_timings(sd, &stdi, timings)) + goto found; + stdi.lcvs -= 2; + v4l2_dbg(1, debug, sd, "%s: lcvs - 1 = %d\n", __func__, stdi.lcvs); if (stdi2dv_timings(sd, &stdi, timings)) { + /* + * The STDI block may measure wrong values, especially + * for lcvs and lcf. If the driver can not find any + * valid timing, the STDI block is restarted to measure + * the video timings again. The function will return an + * error, but the restart of STDI will generate a new + * STDI interrupt and the format detection process will + * restart. + */ + if (state->restart_stdi_once) { + v4l2_dbg(1, debug, sd, "%s: restart STDI\n", __func__); + /* TODO restart STDI for Sync Channel 2 */ + /* enter one-shot mode */ + cp_write_and_or(sd, 0x86, 0xf9, 0x00); + /* trigger STDI restart */ + cp_write_and_or(sd, 0x86, 0xf9, 0x04); + /* reset to continuous mode */ + cp_write_and_or(sd, 0x86, 0xf9, 0x02); + state->restart_stdi_once = false; + return -ENOLINK; + } v4l2_dbg(1, debug, sd, "%s: format not supported\n", __func__); return -ERANGE; } + state->restart_stdi_once = true; } +found: if (debug > 1) - v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings: ", - timings, true); + v4l2_print_dv_timings(sd->name, "adv7842_query_dv_timings:", + timings, true); return 0; } @@ -1437,9 +1448,16 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, struct v4l2_bt_timings *bt; int err; + v4l2_dbg(1, debug, sd, "%s:\n", __func__); + if (state->mode == ADV7842_MODE_SDP) return -ENODATA; + if (v4l2_match_dv_timings(&state->timings, timings, 0)) { + v4l2_dbg(1, debug, sd, "%s: no change\n", __func__); + return 0; + } + bt = &timings->bt; if (!v4l2_valid_dv_timings(timings, adv7842_get_dv_timings_cap(sd), @@ -1450,7 +1468,7 @@ static int adv7842_s_dv_timings(struct v4l2_subdev *sd, state->timings = *timings; - cp_write(sd, 0x91, bt->interlaced ? 0x50 : 0x10); + cp_write(sd, 0x91, bt->interlaced ? 0x40 : 0x00); /* Use prim_mode and vid_std when available */ err = configure_predefined_video_timings(sd, timings); @@ -1483,18 +1501,18 @@ static int adv7842_g_dv_timings(struct v4l2_subdev *sd, static void enable_input(struct v4l2_subdev *sd) { struct adv7842_state *state = to_state(sd); + + set_rgb_quantization_range(sd); switch (state->mode) { case ADV7842_MODE_SDP: case ADV7842_MODE_COMP: case ADV7842_MODE_RGB: - /* enable */ io_write(sd, 0x15, 0xb0); /* Disable Tristate of Pins (no audio) */ break; case ADV7842_MODE_HDMI: - /* enable */ - hdmi_write(sd, 0x1a, 0x0a); /* Unmute audio */ hdmi_write(sd, 0x01, 0x00); /* Enable HDMI clock terminators */ io_write(sd, 0x15, 0xa0); /* Disable Tristate of Pins */ + hdmi_write_and_or(sd, 0x1a, 0xef, 0x00); /* Unmute audio */ break; default: v4l2_dbg(2, debug, sd, "%s: Unknown mode %d\n", @@ -1505,9 +1523,9 @@ static void enable_input(struct v4l2_subdev *sd) static void disable_input(struct v4l2_subdev *sd) { - /* disable */ + hdmi_write_and_or(sd, 0x1a, 0xef, 0x10); /* Mute audio [REF_01, c. 2.2.2] */ + msleep(16); /* 512 samples with >= 32 kHz sample rate [REF_03, c. 8.29] */ io_write(sd, 0x15, 0xbe); /* Tristate all outputs from video core */ - hdmi_write(sd, 0x1a, 0x1a); /* Mute audio */ hdmi_write(sd, 0x01, 0x78); /* Disable HDMI clock terminators */ } @@ -1575,9 +1593,6 @@ static void select_input(struct v4l2_subdev *sd, afe_write(sd, 0x00, 0x00); /* power up ADC */ afe_write(sd, 0xc8, 0x00); /* phase control */ - io_write(sd, 0x19, 0x83); /* LLC DLL phase */ - io_write(sd, 0x33, 0x40); /* LLC DLL enable */ - io_write(sd, 0xdd, 0x90); /* Manual 2x output clock */ /* script says register 0xde, which don't exist in manual */ @@ -1611,8 +1626,6 @@ static void select_input(struct v4l2_subdev *sd, /* deinterlacer enabled and 3D comb */ sdp_write_and_or(sd, 0x12, 0xf6, 0x09); - sdp_write(sd, 0xdd, 0x08); /* free run auto */ - break; case ADV7842_MODE_COMP: @@ -1627,6 +1640,13 @@ static void select_input(struct v4l2_subdev *sd, afe_write(sd, 0x00, 0x00); /* power up ADC */ afe_write(sd, 0xc8, 0x00); /* phase control */ + if (state->mode == ADV7842_MODE_COMP) { + /* force to YCrCb */ + io_write_and_or(sd, 0x02, 0x0f, 0x60); + } else { + /* force to RGB */ + io_write_and_or(sd, 0x02, 0x0f, 0x10); + } /* set ADI recommended settings for digitizer */ /* "ADV7842 Register Settings Recommendations @@ -1722,19 +1742,19 @@ static int adv7842_s_routing(struct v4l2_subdev *sd, switch (input) { case ADV7842_SELECT_HDMI_PORT_A: - /* TODO select HDMI_COMP or HDMI_GR */ state->mode = ADV7842_MODE_HDMI; state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; state->hdmi_port_a = true; break; case ADV7842_SELECT_HDMI_PORT_B: - /* TODO select HDMI_COMP or HDMI_GR */ state->mode = ADV7842_MODE_HDMI; state->vid_std_select = ADV7842_HDMI_COMP_VID_STD_HD_1250P; state->hdmi_port_a = false; break; case ADV7842_SELECT_VGA_COMP: - v4l2_info(sd, "%s: VGA component: todo\n", __func__); + state->mode = ADV7842_MODE_COMP; + state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; + break; case ADV7842_SELECT_VGA_RGB: state->mode = ADV7842_MODE_RGB; state->vid_std_select = ADV7842_RGB_VID_STD_AUTO_GRAPH_MODE; @@ -1814,12 +1834,15 @@ static void adv7842_irq_enable(struct v4l2_subdev *sd, bool enable) io_write(sd, 0x78, 0x03); /* Enable SDP Standard Detection Change and SDP Video Detected */ io_write(sd, 0xa0, 0x09); + /* Enable HDMI_MODE interrupt */ + io_write(sd, 0x69, 0x08); } else { io_write(sd, 0x46, 0x0); io_write(sd, 0x5a, 0x0); io_write(sd, 0x73, 0x0); io_write(sd, 0x78, 0x0); io_write(sd, 0xa0, 0x0); + io_write(sd, 0x69, 0x0); } } @@ -1827,11 +1850,9 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) { struct adv7842_state *state = to_state(sd); u8 fmt_change_cp, fmt_change_digital, fmt_change_sdp; - u8 irq_status[5]; - u8 irq_cfg = io_read(sd, 0x40); + u8 irq_status[6]; - /* disable irq-pin output */ - io_write(sd, 0x40, irq_cfg | 0x3); + adv7842_irq_enable(sd, false); /* read status */ irq_status[0] = io_read(sd, 0x43); @@ -1839,6 +1860,7 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) irq_status[2] = io_read(sd, 0x70); irq_status[3] = io_read(sd, 0x75); irq_status[4] = io_read(sd, 0x9d); + irq_status[5] = io_read(sd, 0x66); /* and clear */ if (irq_status[0]) @@ -1851,10 +1873,14 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) io_write(sd, 0x76, irq_status[3]); if (irq_status[4]) io_write(sd, 0x9e, irq_status[4]); + if (irq_status[5]) + io_write(sd, 0x67, irq_status[5]); - v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x\n", __func__, + adv7842_irq_enable(sd, true); + + v4l2_dbg(1, debug, sd, "%s: irq %x, %x, %x, %x, %x, %x\n", __func__, irq_status[0], irq_status[1], irq_status[2], - irq_status[3], irq_status[4]); + irq_status[3], irq_status[4], irq_status[5]); /* format change CP */ fmt_change_cp = irq_status[0] & 0x9c; @@ -1871,25 +1897,72 @@ static int adv7842_isr(struct v4l2_subdev *sd, u32 status, bool *handled) else fmt_change_digital = 0; - /* notify */ + /* format change */ if (fmt_change_cp || fmt_change_digital || fmt_change_sdp) { v4l2_dbg(1, debug, sd, "%s: fmt_change_cp = 0x%x, fmt_change_digital = 0x%x, fmt_change_sdp = 0x%x\n", __func__, fmt_change_cp, fmt_change_digital, fmt_change_sdp); v4l2_subdev_notify(sd, ADV7842_FMT_CHANGE, NULL); + if (handled) + *handled = true; } - /* 5v cable detect */ - if (irq_status[2]) + /* HDMI/DVI mode */ + if (irq_status[5] & 0x08) { + v4l2_dbg(1, debug, sd, "%s: irq %s mode\n", __func__, + (io_read(sd, 0x65) & 0x08) ? "HDMI" : "DVI"); + if (handled) + *handled = true; + } + + /* tx 5v detect */ + if (irq_status[2] & 0x3) { + v4l2_dbg(1, debug, sd, "%s: irq tx_5v\n", __func__); adv7842_s_detect_tx_5v_ctrl(sd); + if (handled) + *handled = true; + } + return 0; +} + +static int adv7842_get_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + struct adv7842_state *state = to_state(sd); + u8 *data = NULL; - if (handled) - *handled = true; + if (edid->pad > ADV7842_EDID_PORT_VGA) + return -EINVAL; + if (edid->blocks == 0) + return -EINVAL; + if (edid->blocks > 2) + return -EINVAL; + if (edid->start_block > 1) + return -EINVAL; + if (edid->start_block == 1) + edid->blocks = 1; + if (!edid->edid) + return -EINVAL; - /* re-enable irq-pin output */ - io_write(sd, 0x40, irq_cfg); + switch (edid->pad) { + case ADV7842_EDID_PORT_A: + case ADV7842_EDID_PORT_B: + if (state->hdmi_edid.present & (0x04 << edid->pad)) + data = state->hdmi_edid.edid; + break; + case ADV7842_EDID_PORT_VGA: + if (state->vga_edid.present) + data = state->vga_edid.edid; + break; + default: + return -EINVAL; + } + if (!data) + return -ENODATA; + memcpy(edid->edid, + data + edid->start_block * 128, + edid->blocks * 128); return 0; } @@ -1898,7 +1971,7 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) struct adv7842_state *state = to_state(sd); int err = 0; - if (e->pad > 2) + if (e->pad > ADV7842_EDID_PORT_VGA) return -EINVAL; if (e->start_block != 0) return -EINVAL; @@ -1911,20 +1984,25 @@ static int adv7842_set_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *e) state->aspect_ratio = v4l2_calc_aspect_ratio(e->edid[0x15], e->edid[0x16]); - if (e->pad == 2) { + switch (e->pad) { + case ADV7842_EDID_PORT_VGA: memset(&state->vga_edid.edid, 0, 256); state->vga_edid.present = e->blocks ? 0x1 : 0x0; memcpy(&state->vga_edid.edid, e->edid, 128 * e->blocks); err = edid_write_vga_segment(sd); - } else { - u32 mask = 0x1<<e->pad; + break; + case ADV7842_EDID_PORT_A: + case ADV7842_EDID_PORT_B: memset(&state->hdmi_edid.edid, 0, 256); if (e->blocks) - state->hdmi_edid.present |= mask; + state->hdmi_edid.present |= 0x04 << e->pad; else - state->hdmi_edid.present &= ~mask; - memcpy(&state->hdmi_edid.edid, e->edid, 128*e->blocks); + state->hdmi_edid.present &= ~(0x04 << e->pad); + memcpy(&state->hdmi_edid.edid, e->edid, 128 * e->blocks); err = edid_write_hdmi_segment(sd, e->pad); + break; + default: + return -EINVAL; } if (err < 0) v4l2_err(sd, "error %d writing edid on port %d\n", err, e->pad); @@ -2156,7 +2234,7 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) static const char * const input_color_space_txt[16] = { "RGB limited range (16-235)", "RGB full range (0-255)", "YCbCr Bt.601 (16-235)", "YCbCr Bt.709 (16-235)", - "XvYCC Bt.601", "XvYCC Bt.709", + "xvYCC Bt.601", "xvYCC Bt.709", "YCbCr Bt.601 (0-255)", "YCbCr Bt.709 (0-255)", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "automatic" @@ -2175,8 +2253,6 @@ static int adv7842_cp_log_status(struct v4l2_subdev *sd) v4l2_info(sd, "-----Chip status-----\n"); v4l2_info(sd, "Chip power: %s\n", no_power(sd) ? "off" : "on"); - v4l2_info(sd, "Connector type: %s\n", state->connector_hdmi ? - "HDMI" : (is_digital_input(sd) ? "DVI-D" : "DVI-A")); v4l2_info(sd, "HDMI/DVI-D port selected: %s\n", state->hdmi_port_a ? "A" : "B"); v4l2_info(sd, "EDID A %s, B %s\n", @@ -2354,15 +2430,63 @@ static int adv7842_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) return 0; } +static void adv7842_s_sdp_io(struct v4l2_subdev *sd, struct adv7842_sdp_io_sync_adjustment *s) +{ + if (s && s->adjust) { + sdp_io_write(sd, 0x94, (s->hs_beg >> 8) & 0xf); + sdp_io_write(sd, 0x95, s->hs_beg & 0xff); + sdp_io_write(sd, 0x96, (s->hs_width >> 8) & 0xf); + sdp_io_write(sd, 0x97, s->hs_width & 0xff); + sdp_io_write(sd, 0x98, (s->de_beg >> 8) & 0xf); + sdp_io_write(sd, 0x99, s->de_beg & 0xff); + sdp_io_write(sd, 0x9a, (s->de_end >> 8) & 0xf); + sdp_io_write(sd, 0x9b, s->de_end & 0xff); + sdp_io_write(sd, 0xa8, s->vs_beg_o); + sdp_io_write(sd, 0xa9, s->vs_beg_e); + sdp_io_write(sd, 0xaa, s->vs_end_o); + sdp_io_write(sd, 0xab, s->vs_end_e); + sdp_io_write(sd, 0xac, s->de_v_beg_o); + sdp_io_write(sd, 0xad, s->de_v_beg_e); + sdp_io_write(sd, 0xae, s->de_v_end_o); + sdp_io_write(sd, 0xaf, s->de_v_end_e); + } else { + /* set to default */ + sdp_io_write(sd, 0x94, 0x00); + sdp_io_write(sd, 0x95, 0x00); + sdp_io_write(sd, 0x96, 0x00); + sdp_io_write(sd, 0x97, 0x20); + sdp_io_write(sd, 0x98, 0x00); + sdp_io_write(sd, 0x99, 0x00); + sdp_io_write(sd, 0x9a, 0x00); + sdp_io_write(sd, 0x9b, 0x00); + sdp_io_write(sd, 0xa8, 0x04); + sdp_io_write(sd, 0xa9, 0x04); + sdp_io_write(sd, 0xaa, 0x04); + sdp_io_write(sd, 0xab, 0x04); + sdp_io_write(sd, 0xac, 0x04); + sdp_io_write(sd, 0xad, 0x04); + sdp_io_write(sd, 0xae, 0x04); + sdp_io_write(sd, 0xaf, 0x04); + } +} + static int adv7842_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) { struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = &state->pdata; v4l2_dbg(1, debug, sd, "%s:\n", __func__); if (state->mode != ADV7842_MODE_SDP) return -ENODATA; + if (norm & V4L2_STD_625_50) + adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_625); + else if (norm & V4L2_STD_525_60) + adv7842_s_sdp_io(sd, &pdata->sdp_io_sync_525); + else + adv7842_s_sdp_io(sd, NULL); + if (norm & V4L2_STD_ALL) { state->norm = norm; return 0; @@ -2385,9 +2509,10 @@ static int adv7842_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) /* ----------------------------------------------------------------------- */ -static int adv7842_core_init(struct v4l2_subdev *sd, - const struct adv7842_platform_data *pdata) +static int adv7842_core_init(struct v4l2_subdev *sd) { + struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = &state->pdata; hdmi_write(sd, 0x48, (pdata->disable_pwrdnb ? 0x80 : 0) | (pdata->disable_cable_det_rst ? 0x40 : 0)); @@ -2400,7 +2525,7 @@ static int adv7842_core_init(struct v4l2_subdev *sd, /* video format */ io_write(sd, 0x02, - pdata->inp_color_space << 4 | + 0xf0 | pdata->alt_gamma << 3 | pdata->op_656_range << 2 | pdata->rgb_out << 1 | @@ -2412,13 +2537,24 @@ static int adv7842_core_init(struct v4l2_subdev *sd, pdata->replicate_av_codes << 1 | pdata->invert_cbcr << 0); + /* HDMI audio */ + hdmi_write_and_or(sd, 0x1a, 0xf1, 0x08); /* Wait 1 s before unmute */ + /* Drive strength */ - io_write_and_or(sd, 0x14, 0xc0, pdata->drive_strength.data<<4 | - pdata->drive_strength.clock<<2 | - pdata->drive_strength.sync); + io_write_and_or(sd, 0x14, 0xc0, + pdata->dr_str_data << 4 | + pdata->dr_str_clk << 2 | + pdata->dr_str_sync); /* HDMI free run */ - cp_write(sd, 0xba, (pdata->hdmi_free_run_mode << 1) | 0x01); + cp_write_and_or(sd, 0xba, 0xfc, pdata->hdmi_free_run_enable | + (pdata->hdmi_free_run_mode << 1)); + + /* SPD free run */ + sdp_write_and_or(sd, 0xdd, 0xf0, pdata->sdp_free_run_force | + (pdata->sdp_free_run_cbar_en << 1) | + (pdata->sdp_free_run_man_col_en << 2) | + (pdata->sdp_free_run_force << 3)); /* TODO from platform data */ cp_write(sd, 0x69, 0x14); /* Enable CP CSC */ @@ -2431,18 +2567,6 @@ static int adv7842_core_init(struct v4l2_subdev *sd, sdp_csc_coeff(sd, &pdata->sdp_csc_coeff); - if (pdata->sdp_io_sync.adjust) { - const struct adv7842_sdp_io_sync_adjustment *s = &pdata->sdp_io_sync; - sdp_io_write(sd, 0x94, (s->hs_beg>>8) & 0xf); - sdp_io_write(sd, 0x95, s->hs_beg & 0xff); - sdp_io_write(sd, 0x96, (s->hs_width>>8) & 0xf); - sdp_io_write(sd, 0x97, s->hs_width & 0xff); - sdp_io_write(sd, 0x98, (s->de_beg>>8) & 0xf); - sdp_io_write(sd, 0x99, s->de_beg & 0xff); - sdp_io_write(sd, 0x9a, (s->de_end>>8) & 0xf); - sdp_io_write(sd, 0x9b, s->de_end & 0xff); - } - /* todo, improve settings for sdram */ if (pdata->sd_ram_size >= 128) { sdp_write(sd, 0x12, 0x0d); /* Frame TBC,3D comb enabled */ @@ -2483,12 +2607,11 @@ static int adv7842_core_init(struct v4l2_subdev *sd, io_write_and_or(sd, 0x20, 0xcf, 0x00); /* LLC */ - /* Set phase to 16. TODO: get this from platform_data */ - io_write(sd, 0x19, 0x90); + io_write(sd, 0x19, 0x80 | pdata->llc_dll_phase); io_write(sd, 0x33, 0x40); /* interrupts */ - io_write(sd, 0x40, 0xe2); /* Configure INT1 */ + io_write(sd, 0x40, 0xf2); /* Configure INT1 */ adv7842_irq_enable(sd, true); @@ -2588,6 +2711,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); struct adv7842_state *state = to_state(sd); struct adv7842_platform_data *pdata = client->dev.platform_data; + struct v4l2_dv_timings timings; int ret = 0; if (!pdata) @@ -2610,7 +2734,7 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) adv7842_rewrite_i2c_addresses(sd, pdata); /* and re-init chip and state */ - adv7842_core_init(sd, pdata); + adv7842_core_init(sd); disable_input(sd); @@ -2618,11 +2742,15 @@ static int adv7842_command_ram_test(struct v4l2_subdev *sd) enable_input(sd); - adv7842_s_dv_timings(sd, &state->timings); - edid_write_vga_segment(sd); - edid_write_hdmi_segment(sd, 0); - edid_write_hdmi_segment(sd, 1); + edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_A); + edid_write_hdmi_segment(sd, ADV7842_EDID_PORT_B); + + timings = state->timings; + + memset(&state->timings, 0, sizeof(struct v4l2_dv_timings)); + + adv7842_s_dv_timings(sd, &timings); return ret; } @@ -2670,6 +2798,7 @@ static const struct v4l2_subdev_video_ops adv7842_video_ops = { }; static const struct v4l2_subdev_pad_ops adv7842_pad_ops = { + .get_edid = adv7842_get_edid, .set_edid = adv7842_set_edid, }; @@ -2712,8 +2841,9 @@ static const struct v4l2_ctrl_config adv7842_ctrl_free_run_color = { }; -static void adv7842_unregister_clients(struct adv7842_state *state) +static void adv7842_unregister_clients(struct v4l2_subdev *sd) { + struct adv7842_state *state = to_state(sd); if (state->i2c_avlink) i2c_unregister_device(state->i2c_avlink); if (state->i2c_cec) @@ -2736,21 +2866,79 @@ static void adv7842_unregister_clients(struct adv7842_state *state) i2c_unregister_device(state->i2c_cp); if (state->i2c_vdp) i2c_unregister_device(state->i2c_vdp); + + state->i2c_avlink = NULL; + state->i2c_cec = NULL; + state->i2c_infoframe = NULL; + state->i2c_sdp_io = NULL; + state->i2c_sdp = NULL; + state->i2c_afe = NULL; + state->i2c_repeater = NULL; + state->i2c_edid = NULL; + state->i2c_hdmi = NULL; + state->i2c_cp = NULL; + state->i2c_vdp = NULL; } -static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, +static struct i2c_client *adv7842_dummy_client(struct v4l2_subdev *sd, const char *desc, u8 addr, u8 io_reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); + struct i2c_client *cp; io_write(sd, io_reg, addr << 1); - return i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + + if (addr == 0) { + v4l2_err(sd, "no %s i2c addr configured\n", desc); + return NULL; + } + + cp = i2c_new_dummy(client->adapter, io_read(sd, io_reg) >> 1); + if (!cp) + v4l2_err(sd, "register %s on i2c addr 0x%x failed\n", desc, addr); + + return cp; +} + +static int adv7842_register_clients(struct v4l2_subdev *sd) +{ + struct adv7842_state *state = to_state(sd); + struct adv7842_platform_data *pdata = &state->pdata; + + state->i2c_avlink = adv7842_dummy_client(sd, "avlink", pdata->i2c_avlink, 0xf3); + state->i2c_cec = adv7842_dummy_client(sd, "cec", pdata->i2c_cec, 0xf4); + state->i2c_infoframe = adv7842_dummy_client(sd, "infoframe", pdata->i2c_infoframe, 0xf5); + state->i2c_sdp_io = adv7842_dummy_client(sd, "sdp_io", pdata->i2c_sdp_io, 0xf2); + state->i2c_sdp = adv7842_dummy_client(sd, "sdp", pdata->i2c_sdp, 0xf1); + state->i2c_afe = adv7842_dummy_client(sd, "afe", pdata->i2c_afe, 0xf8); + state->i2c_repeater = adv7842_dummy_client(sd, "repeater", pdata->i2c_repeater, 0xf9); + state->i2c_edid = adv7842_dummy_client(sd, "edid", pdata->i2c_edid, 0xfa); + state->i2c_hdmi = adv7842_dummy_client(sd, "hdmi", pdata->i2c_hdmi, 0xfb); + state->i2c_cp = adv7842_dummy_client(sd, "cp", pdata->i2c_cp, 0xfd); + state->i2c_vdp = adv7842_dummy_client(sd, "vdp", pdata->i2c_vdp, 0xfe); + + if (!state->i2c_avlink || + !state->i2c_cec || + !state->i2c_infoframe || + !state->i2c_sdp_io || + !state->i2c_sdp || + !state->i2c_afe || + !state->i2c_repeater || + !state->i2c_edid || + !state->i2c_hdmi || + !state->i2c_cp || + !state->i2c_vdp) + return -1; + + return 0; } static int adv7842_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7842_state *state; + static const struct v4l2_dv_timings cea640x480 = + V4L2_DV_BT_CEA_640X480P59_94; struct adv7842_platform_data *pdata = client->dev.platform_data; struct v4l2_ctrl_handler *hdl; struct v4l2_subdev *sd; @@ -2775,13 +2963,17 @@ static int adv7842_probe(struct i2c_client *client, return -ENOMEM; } + /* platform data */ + state->pdata = *pdata; + state->timings = cea640x480; + sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7842_ops); sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; - state->connector_hdmi = pdata->connector_hdmi; state->mode = pdata->mode; - state->hdmi_port_a = true; + state->hdmi_port_a = pdata->input == ADV7842_SELECT_HDMI_PORT_A; + state->restart_stdi_once = true; /* i2c access to adv7842? */ rev = adv_smbus_read_byte_data_check(client, 0xea, false) << 8 | @@ -2843,21 +3035,7 @@ static int adv7842_probe(struct i2c_client *client, goto err_hdl; } - state->i2c_avlink = adv7842_dummy_client(sd, pdata->i2c_avlink, 0xf3); - state->i2c_cec = adv7842_dummy_client(sd, pdata->i2c_cec, 0xf4); - state->i2c_infoframe = adv7842_dummy_client(sd, pdata->i2c_infoframe, 0xf5); - state->i2c_sdp_io = adv7842_dummy_client(sd, pdata->i2c_sdp_io, 0xf2); - state->i2c_sdp = adv7842_dummy_client(sd, pdata->i2c_sdp, 0xf1); - state->i2c_afe = adv7842_dummy_client(sd, pdata->i2c_afe, 0xf8); - state->i2c_repeater = adv7842_dummy_client(sd, pdata->i2c_repeater, 0xf9); - state->i2c_edid = adv7842_dummy_client(sd, pdata->i2c_edid, 0xfa); - state->i2c_hdmi = adv7842_dummy_client(sd, pdata->i2c_hdmi, 0xfb); - state->i2c_cp = adv7842_dummy_client(sd, pdata->i2c_cp, 0xfd); - state->i2c_vdp = adv7842_dummy_client(sd, pdata->i2c_vdp, 0xfe); - if (!state->i2c_avlink || !state->i2c_cec || !state->i2c_infoframe || - !state->i2c_sdp_io || !state->i2c_sdp || !state->i2c_afe || - !state->i2c_repeater || !state->i2c_edid || !state->i2c_hdmi || - !state->i2c_cp || !state->i2c_vdp) { + if (adv7842_register_clients(sd) < 0) { err = -ENOMEM; v4l2_err(sd, "failed to create all i2c clients\n"); goto err_i2c; @@ -2879,7 +3057,7 @@ static int adv7842_probe(struct i2c_client *client, if (err) goto err_work_queues; - err = adv7842_core_init(sd, pdata); + err = adv7842_core_init(sd); if (err) goto err_entity; @@ -2893,7 +3071,7 @@ err_work_queues: cancel_delayed_work(&state->delayed_work_enable_hotplug); destroy_workqueue(state->work_queues); err_i2c: - adv7842_unregister_clients(state); + adv7842_unregister_clients(sd); err_hdl: v4l2_ctrl_handler_free(hdl); return err; @@ -2912,7 +3090,7 @@ static int adv7842_remove(struct i2c_client *client) destroy_workqueue(state->work_queues); v4l2_device_unregister_subdev(sd); media_entity_cleanup(&sd->entity); - adv7842_unregister_clients(to_state(sd)); + adv7842_unregister_clients(sd); v4l2_ctrl_handler_free(sd->ctrl_handler); return 0; } |