From cb38551403684b15911bbf8b83876d72b57260b1 Mon Sep 17 00:00:00 2001 From: syed rafiuddin Date: Thu, 16 Jun 2011 17:29:51 +0530 Subject: video: tegra: hdmi: support for edid like data - Added support for eld extraction from edid and support for propogation of eld to HDA. - Differentiate video/audio hotplug - Change the value sent to HDMI hotplug switch class driver so that we can get hdmi hotplug events without audio enabled based on whether the display has audio or not. bug 836548 Change-Id: I89475049df19ed36d9b77eb15f3aee2e0fdf8f71 Reviewed-on: http://git-master/r/35672 Reviewed-by: Niket Sirsi Tested-by: Niket Sirsi --- drivers/video/tegra/dc/edid.c | 67 ++++++++++++++++++++- drivers/video/tegra/dc/edid.h | 25 ++++++++ drivers/video/tegra/dc/hdmi.c | 120 +++++++++++++++++++++++++++++++++++++- drivers/video/tegra/dc/hdmi_reg.h | 5 ++ 4 files changed, 215 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c index 327ed63493e8..6b87854b9b77 100644 --- a/drivers/video/tegra/dc/edid.c +++ b/drivers/video/tegra/dc/edid.c @@ -33,6 +33,7 @@ struct tegra_edid { u8 *data; unsigned len; u8 support_stereo; + struct tegra_edid_hdmi_eld eld; }; #if defined(DEBUG) || defined(CONFIG_DEBUG_FS) @@ -169,7 +170,16 @@ int tegra_edid_parse_ext_block(u8 *raw, int idx, struct tegra_edid *edid) u8 tmp; u8 code; int len; + int i; + + ptr = &raw[0]; + /* If CEA 861 block get info for eld struct */ + if (edid && ptr) { + if (*ptr <= 3) + edid->eld.eld_ver = 0x02; + edid->eld.cea_edid_ver = ptr[1]; + } ptr = &raw[4]; while (ptr < &raw[idx]) { @@ -182,13 +192,30 @@ int tegra_edid_parse_ext_block(u8 *raw, int idx, struct tegra_edid *edid) * tag code 2: video data block * tag code 3: vendor specific data block */ - code = (tmp >> 5) & 0x3; + code = (tmp >> 5) & 0x7; switch (code) { + case 1: + { + edid->eld.sad_count = len; + edid->eld.conn_type = 0x00; + edid->eld.support_hdcp = 0x00; + for (i = 0; (i < len) && (i < ELD_MAX_SAD); i++) + edid->eld.sad[i] = ptr[i + 1]; + len++; + ptr += len; /* adding the header */ + break; + } /* case 2 is commented out for now */ case 3: { int j = 0; + if ((ptr[1] == 0x03) && + (ptr[2] == 0x0c) && + (ptr[3] == 0)) { + edid->eld.port_id[0] = ptr[4]; + edid->eld.port_id[1] = ptr[5]; + } if ((len >= 8) && (ptr[1] == 0x03) && (ptr[2] == 0x0c) && @@ -208,7 +235,28 @@ int tegra_edid_parse_ext_block(u8 *raw, int idx, struct tegra_edid *edid) edid->support_stereo = 1; } } + if ((len > 5) && + (ptr[1] == 0x03) && + (ptr[2] == 0x0c) && + (ptr[3] == 0)) { + + edid->eld.support_ai = (ptr[6] & 0x80); + } + if ((len > 9) && + (ptr[1] == 0x03) && + (ptr[2] == 0x0c) && + (ptr[3] == 0)) { + + edid->eld.aud_synch_delay = ptr[10]; + } + len++; + ptr += len; /* adding the header */ + break; + } + case 4: + { + edid->eld.spk_alloc = ptr[1]; len++; ptr += len; /* adding the header */ break; @@ -251,9 +299,16 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs) return ret; memset(specs, 0x0, sizeof(struct fb_monspecs)); + memset(&edid->eld, 0x0, sizeof(struct tegra_edid_hdmi_eld)); fb_edid_to_monspecs(edid->data, specs); if (specs->modedb == NULL) return -EINVAL; + memcpy(edid->eld.monitor_name, specs->monitor, sizeof(specs->monitor)); + edid->eld.mnl = strlen(edid->eld.monitor_name) + 1; + edid->eld.product_id[0] = edid->data[0x8]; + edid->eld.product_id[1] = edid->data[0x9]; + edid->eld.manufacture_id[0] = edid->data[0xA]; + edid->eld.manufacture_id[1] = edid->data[0xB]; extension_blocks = edid->data[0x7e]; @@ -282,6 +337,16 @@ int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs) edid->len = i * 128; tegra_edid_dump(edid); + return 0; +} + +int tegra_edid_get_eld(struct tegra_edid *edid, + struct tegra_edid_hdmi_eld *elddata) +{ + if (!elddata) + return -EFAULT; + + memcpy(elddata, &edid->eld, sizeof(struct tegra_edid_hdmi_eld)); return 0; } diff --git a/drivers/video/tegra/dc/edid.h b/drivers/video/tegra/dc/edid.h index 821da90a8b4f..fbb8adc08a53 100644 --- a/drivers/video/tegra/dc/edid.h +++ b/drivers/video/tegra/dc/edid.h @@ -21,11 +21,36 @@ #include #include +#define ELD_MAX_MNL 16 +#define ELD_MAX_SAD 16 struct tegra_edid; +/* + * ELD: EDID Like Data + */ +struct tegra_edid_hdmi_eld { + u8 baseline_len; + u8 eld_ver; + u8 cea_edid_ver; + char monitor_name[ELD_MAX_MNL + 1]; + u8 mnl; + u8 manufacture_id[2]; + u8 product_id[2]; + u8 port_id[8]; + u8 support_hdcp; + u8 support_ai; + u8 conn_type; + u8 aud_synch_delay; + u8 spk_alloc; + u8 sad_count; + u8 sad[ELD_MAX_SAD]; +}; + struct tegra_edid *tegra_edid_create(int bus); void tegra_edid_destroy(struct tegra_edid *edid); int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs); +int tegra_edid_get_eld(struct tegra_edid *edid, + struct tegra_edid_hdmi_eld *elddata); #endif diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 155ca88134d7..8d3247a3c4cc 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -46,9 +46,22 @@ #define HDMI_REKEY_DEFAULT 56 +#define HDMI_ELD_RESERVED1_INDEX 1 +#define HDMI_ELD_RESERVED2_INDEX 3 +#define HDMI_ELD_VER_INDEX 0 +#define HDMI_ELD_BASELINE_LEN_INDEX 2 +#define HDMI_ELD_CEA_VER_MNL_INDEX 4 +#define HDMI_ELD_SAD_CNT_CON_TYP_SAI_HDCP_INDEX 5 +#define HDMI_ELD_AUD_SYNC_DELAY_INDEX 6 +#define HDMI_ELD_SPK_ALLOC_INDEX 7 +#define HDMI_ELD_PORT_ID_INDEX 8 +#define HDMI_ELD_MANF_NAME_INDEX 16 +#define HDMI_ELD_PRODUCT_CODE_INDEX 18 +#define HDMI_ELD_MONITOR_NAME_INDEX 20 struct tegra_dc_hdmi_data { struct tegra_dc *dc; struct tegra_edid *edid; + struct tegra_edid_hdmi_eld eld; struct tegra_nvhdcp *nvhdcp; struct delayed_work work; @@ -66,6 +79,7 @@ struct tegra_dc_hdmi_data { bool hpd_pending; bool dvi; + bool eld_retrieved; }; const struct fb_videomode tegra_dc_hdmi_supported_modes[] = { @@ -566,6 +580,12 @@ static bool tegra_dc_hdmi_detect(struct tegra_dc *dc) goto fail; } + err = tegra_edid_get_eld(hdmi->edid, &hdmi->eld); + if (err < 0) { + dev_err(&dc->ndev->dev, "error populating eld\n"); + goto fail; + } + hdmi->eld_retrieved = true; /* monitors like to lie about these but they are still useful for * detecting aspect ratios */ @@ -576,12 +596,18 @@ static bool tegra_dc_hdmi_detect(struct tegra_dc *dc) hdmi->dvi = !(specs.misc & FB_MISC_HDMI); tegra_fb_update_monspecs(dc->fb, &specs, tegra_dc_hdmi_mode_filter); + hdmi->hpd_switch.state = 0; - switch_set_state(&hdmi->hpd_switch, 1); + if (hdmi->eld.spk_alloc) + switch_set_state(&hdmi->hpd_switch, 1); + else + switch_set_state(&hdmi->hpd_switch, 2); + dev_info(&dc->ndev->dev, "display detected\n"); return true; fail: + hdmi->hpd_switch.state = 1; switch_set_state(&hdmi->hpd_switch, 0); tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0); return false; @@ -746,6 +772,7 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc) hdmi->disp2_clk = disp2_clk; hdmi->suspended = false; hdmi->hpd_pending = false; + hdmi->eld_retrieved = false; spin_lock_init(&hdmi->suspend_lock); hdmi->hpd_switch.name = "hdmi"; @@ -837,6 +864,87 @@ static void tegra_dc_hdmi_setup_audio_fs_tables(struct tegra_dc *dc) } } +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) +static void tegra_dc_hdmi_setup_eld_buff(struct tegra_dc *dc) +{ + int i; + int j; + u8 tmp; + + struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); + + /* program ELD stuff */ + for (i = 0; i < HDMI_ELD_MONITOR_NAME_INDEX; i++) { + switch (i) { + case HDMI_ELD_VER_INDEX: + tmp = (hdmi->eld.eld_ver << 3); + tegra_hdmi_writel(hdmi, (i << 8) | tmp, + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + break; + case HDMI_ELD_BASELINE_LEN_INDEX: + break; + case HDMI_ELD_CEA_VER_MNL_INDEX: + tmp = (hdmi->eld.cea_edid_ver << 5); + tmp |= (hdmi->eld.mnl & 0x1f); + tegra_hdmi_writel(hdmi, (i << 8) | tmp, + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + break; + case HDMI_ELD_SAD_CNT_CON_TYP_SAI_HDCP_INDEX: + tmp = (hdmi->eld.sad_count << 4); + tmp |= (hdmi->eld.conn_type & 0xC); + tmp |= (hdmi->eld.support_ai & 0x2); + tmp |= (hdmi->eld.support_hdcp & 0x1); + tegra_hdmi_writel(hdmi, (i << 8) | tmp, + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + break; + case HDMI_ELD_AUD_SYNC_DELAY_INDEX: + tegra_hdmi_writel(hdmi, (i << 8) | + (hdmi->eld.aud_synch_delay), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + break; + case HDMI_ELD_SPK_ALLOC_INDEX: + tegra_hdmi_writel(hdmi, (i << 8) | + (hdmi->eld.spk_alloc), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + break; + case HDMI_ELD_PORT_ID_INDEX: + for (j = 0; j < 8; j++) { + tegra_hdmi_writel(hdmi, ((i + j) << 8) | + (hdmi->eld.port_id[j]), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + } + break; + case HDMI_ELD_MANF_NAME_INDEX: + for (j = 0; j < 2; j++) { + tegra_hdmi_writel(hdmi, ((i + j) << 8) | + (hdmi->eld.manufacture_id[j]), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + } + break; + case HDMI_ELD_PRODUCT_CODE_INDEX: + for (j = 0; j < 2; j++) { + tegra_hdmi_writel(hdmi, ((i + j) << 8) | + (hdmi->eld.product_id[j]), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + } + break; + } + } + for (j = 0; j < hdmi->eld.mnl; j++) { + tegra_hdmi_writel(hdmi, ((j + HDMI_ELD_MONITOR_NAME_INDEX) << 8) | + (hdmi->eld.monitor_name[j]), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + } + for (j = 0; j < hdmi->eld.sad_count; j++) { + tegra_hdmi_writel(hdmi, ((j + HDMI_ELD_MONITOR_NAME_INDEX + + hdmi->eld.mnl) << 8) | (hdmi->eld.sad[j]), + HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0); + } + /* set presence andvalid bit */ + tegra_hdmi_writel(hdmi, 3, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0); +} +#endif + static int tegra_dc_hdmi_setup_audio(struct tegra_dc *dc) { struct tegra_dc_hdmi_data *hdmi = tegra_dc_get_outdata(dc); @@ -1141,6 +1249,11 @@ static void tegra_dc_hdmi_enable(struct tegra_dc *dc) hdmi->dvi = true; } +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) + if (hdmi->eld_retrieved) + tegra_dc_hdmi_setup_eld_buff(dc); +#endif + rekey = HDMI_REKEY_DEFAULT; val = HDMI_CTRL_REKEY(rekey); val |= HDMI_CTRL_MAX_AC_PACKET((dc->mode.h_sync_width + @@ -1264,6 +1377,11 @@ static void tegra_dc_hdmi_disable(struct tegra_dc *dc) tegra_nvhdcp_set_plug(hdmi->nvhdcp, 0); +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) + tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0); +#endif + hdmi->eld_retrieved = false; + tegra_periph_reset_assert(hdmi->clk); clk_disable(hdmi->clk); tegra_dvfs_set_rate(hdmi->clk, 0); diff --git a/drivers/video/tegra/dc/hdmi_reg.h b/drivers/video/tegra/dc/hdmi_reg.h index 57f292ac825c..4468d1dc951b 100644 --- a/drivers/video/tegra/dc/hdmi_reg.h +++ b/drivers/video/tegra/dc/hdmi_reg.h @@ -387,6 +387,11 @@ #define HDMI_NV_PDISP_AUDIO_PULSE_WIDTH 0x89 #define HDMI_NV_PDISP_AUDIO_THRESHOLD 0x8a #define HDMI_NV_PDISP_AUDIO_CNTRL0 0x8b +#if !defined(CONFIG_ARCH_TEGRA_2x_SOC) +#define HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_0 0xac +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR_0 0xbc +#define HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE_0 0xbd +#endif #define AUDIO_CNTRL0_ERROR_TOLERANCE(x) (((x) & 0xff) << 0) #define AUDIO_CNTRL0_SOFT_RESET (1 << 8) #define AUDIO_CNTRL0_SOFT_RESET_ALL (1 << 12) -- cgit v1.2.3