diff options
author | mohit singh <mpsingh@nvidia.com> | 2011-02-15 10:04:24 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2012-03-23 17:28:15 -0700 |
commit | 90293b2ee0478b32b2004e2c7639cd66fc130503 (patch) | |
tree | ffdd82697a76d0f2820c6d265afbf718f73b1f36 /drivers/video/tegra/dc | |
parent | 4a1b34de9726759f588e8b7053fd4c35161c83f3 (diff) |
arm: tegra: hdmi:added support for edid like data
- added support for eld extraction from edid.
- added support for propogation of eld to HDA.
- bug 790232.
Original-Change-Id: Ic94560c3bbef61e19fc901895052a892c5b7661f
Reviewed-on: http://git-master/r/19547
Tested-by: Mohit Singh <mpsingh@nvidia.com>
Reviewed-by: Dara Ramesh <dramesh@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Original-Change-Id: Ib331531ad735ea44f7c123610f7829573eb40623
Rebase-Id: R7ba53a66b41eb9aa87cf2842414f18d8f25d98c7
Diffstat (limited to 'drivers/video/tegra/dc')
-rw-r--r-- | drivers/video/tegra/dc/edid.c | 66 | ||||
-rw-r--r-- | drivers/video/tegra/dc/edid.h | 24 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 107 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi_reg.h | 2 |
4 files changed, 198 insertions, 1 deletions
diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c index b06be214fd30..ad742e8926d0 100644 --- a/drivers/video/tegra/dc/edid.c +++ b/drivers/video/tegra/dc/edid.c @@ -34,6 +34,7 @@ struct tegra_edid { u8 *data; unsigned len; u8 support_stereo; + struct tegra_edid_hdmi_eld eld; }; #if defined(DEBUG) || defined(CONFIG_DEBUG_FS) @@ -170,7 +171,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]) { @@ -183,13 +193,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) && @@ -209,7 +236,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; @@ -252,9 +300,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]; @@ -283,6 +338,15 @@ 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..9952db7c21df 100644 --- a/drivers/video/tegra/dc/edid.h +++ b/drivers/video/tegra/dc/edid.h @@ -21,11 +21,35 @@ #include <linux/i2c.h> #include <linux/wait.h> +#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 b9e19d1e71b2..935c91e4fd9b 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -45,9 +45,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; @@ -61,6 +74,7 @@ struct tegra_dc_hdmi_data { spinlock_t suspend_lock; bool suspended; bool hpd_pending; + bool eld_retrieved; bool dvi; }; @@ -542,6 +556,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 */ @@ -720,6 +740,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); dc->out->depth = 24; @@ -809,6 +830,82 @@ 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); @@ -1122,6 +1219,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 + @@ -1245,6 +1347,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); } diff --git a/drivers/video/tegra/dc/hdmi_reg.h b/drivers/video/tegra/dc/hdmi_reg.h index 731e9ddfa833..4468d1dc951b 100644 --- a/drivers/video/tegra/dc/hdmi_reg.h +++ b/drivers/video/tegra/dc/hdmi_reg.h @@ -389,6 +389,8 @@ #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) |