summaryrefslogtreecommitdiff
path: root/drivers/video/tegra/dc
diff options
context:
space:
mode:
authormohit singh <mpsingh@nvidia.com>2011-02-15 10:04:24 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2012-03-23 17:28:15 -0700
commit90293b2ee0478b32b2004e2c7639cd66fc130503 (patch)
treeffdd82697a76d0f2820c6d265afbf718f73b1f36 /drivers/video/tegra/dc
parent4a1b34de9726759f588e8b7053fd4c35161c83f3 (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.c66
-rw-r--r--drivers/video/tegra/dc/edid.h24
-rw-r--r--drivers/video/tegra/dc/hdmi.c107
-rw-r--r--drivers/video/tegra/dc/hdmi_reg.h2
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)