platform: msm_shared: add dynamic audio configuration support

Read EDID and check for supported audio sample rate. If 48KHz
is not supported, fall back to 32KHz.

Change-Id: Iadea72724d10e44675c3fd429281810745cb8451
diff --git a/platform/msm_shared/mdss_hdmi.c b/platform/msm_shared/mdss_hdmi.c
index 5ef0fc0..fb345ef 100644
--- a/platform/msm_shared/mdss_hdmi.c
+++ b/platform/msm_shared/mdss_hdmi.c
@@ -40,9 +40,64 @@
 #include <target/display.h>
 
 static struct msm_fb_panel_data panel;
-
 extern int msm_display_init(struct msm_fb_panel_data *pdata);
 
+/* Supported HDMI Audio channels */
+#define MSM_HDMI_AUDIO_CHANNEL_2		1
+#define MSM_HDMI_AUDIO_CHANNEL_MAX		8
+
+enum msm_hdmi_supported_audio_sample_rates {
+	AUDIO_SAMPLE_RATE_32KHZ,
+	AUDIO_SAMPLE_RATE_44_1KHZ,
+	AUDIO_SAMPLE_RATE_48KHZ,
+	AUDIO_SAMPLE_RATE_88_2KHZ,
+	AUDIO_SAMPLE_RATE_96KHZ,
+	AUDIO_SAMPLE_RATE_176_4KHZ,
+	AUDIO_SAMPLE_RATE_192KHZ,
+	AUDIO_SAMPLE_RATE_MAX
+};
+
+struct hdmi_msm_audio_acr {
+	uint32_t n;	/* N parameter for clock regeneration */
+	uint32_t cts;	/* CTS parameter for clock regeneration */
+};
+
+struct hdmi_msm_audio_arcs {
+	uint32_t pclk;
+	struct hdmi_msm_audio_acr lut[AUDIO_SAMPLE_RATE_MAX];
+};
+
+#define HDMI_MSM_AUDIO_ARCS(pclk, ...) { pclk, __VA_ARGS__ }
+
+/* Audio constants lookup table for hdmi_msm_audio_acr_setup */
+/* Valid Pixel-Clock rates: 25.2MHz, 27MHz, 27.03MHz, 74.25MHz, 148.5MHz */
+static struct hdmi_msm_audio_arcs hdmi_audio_acr_lut[] = {
+	/*  25.200MHz  */
+	HDMI_MSM_AUDIO_ARCS(25200, {
+		{4096, 25200}, {6272, 28000}, {6144, 25200}, {12544, 28000},
+		{12288, 25200}, {25088, 28000}, {24576, 25200} }),
+	/*  27.000MHz  */
+	HDMI_MSM_AUDIO_ARCS(27000, {
+		{4096, 27000}, {6272, 30000}, {6144, 27000}, {12544, 30000},
+		{12288, 27000}, {25088, 30000}, {24576, 27000} }),
+	/*  27.027MHz */
+	HDMI_MSM_AUDIO_ARCS(27030, {
+		{4096, 27027}, {6272, 30030}, {6144, 27027}, {12544, 30030},
+		{12288, 27027}, {25088, 30030}, {24576, 27027} }),
+	/*  74.250MHz */
+	HDMI_MSM_AUDIO_ARCS(74250, {
+		{4096, 74250}, {6272, 82500}, {6144, 74250}, {12544, 82500},
+		{12288, 74250}, {25088, 82500}, {24576, 74250} }),
+	/* 148.500MHz */
+	HDMI_MSM_AUDIO_ARCS(148500, {
+		{4096, 148500}, {6272, 165000}, {6144, 148500}, {12544, 165000},
+		{12288, 148500}, {25088, 165000}, {24576, 148500} }),
+	/* 297.000MHz */
+	HDMI_MSM_AUDIO_ARCS(297000, {
+		{3072, 222750}, {4704, 247500}, {5120, 247500}, {9408, 247500},
+		{10240, 247500}, {18816, 247500}, {20480, 247500} }),
+};
+
 /* AVI INFOFRAME DATA */
 #define NUM_MODES_AVI 20
 #define AVI_MAX_DATA_BYTES 13
@@ -81,6 +136,7 @@
 #define LEFT_SHIFT_WORD(x) ((x) << 16)
 #define LEFT_SHIFT_24BITS(x) ((x) << 24)
 
+#define MAX_AUDIO_DATA_BLOCK_SIZE       0x80
 #define DBC_START_OFFSET                4
 #define VIC_INDEX                       3
 #define HDMI_VIC_STR_MAX                3
@@ -213,52 +269,114 @@
 		0x39, 0x04, 0x00, 0x00, 0x81, 0x07},
 };
 
-static void mdss_hdmi_audio_acr_setup(void)
+static void mdss_hdmi_audio_acr_setup(uint32_t sample_rate)
 {
-	int n, cts, layout, multiplier;
-	uint32_t aud_pck_ctrl_2_reg = 0, acr_pck_ctrl_reg = 0;
+	/* Read first before writing */
+	uint32_t acr_pck_ctrl_reg = readl(HDMI_ACR_PKT_CTRL);
+	struct mdss_hdmi_timing_info tinfo = {0};
+	uint32_t ret = mdss_hdmi_get_timing_info(&tinfo, mdss_hdmi_video_fmt);
+	struct hdmi_msm_audio_arcs *audio_acr = &hdmi_audio_acr_lut[0];
+	uint32_t lut_size = sizeof(hdmi_audio_acr_lut)
+		/ sizeof(*hdmi_audio_acr_lut);
+	uint32_t i, n, cts, layout, multiplier, aud_pck_ctrl_2_reg;
+	uint32_t channel_num = MSM_HDMI_AUDIO_CHANNEL_2;
 
-	/* 74.25MHz ACR settings */
-	n = 4096;
-	cts = 74250;
-	layout = 0;
-	multiplier = 1;
+	if (ret || !tinfo.supported) {
+		dprintf(CRITICAL, "%s: video format %d not supported\n",
+			__func__, mdss_hdmi_video_fmt);
+		return;
+	}
+
+	for (i = 0; i < lut_size; audio_acr = &hdmi_audio_acr_lut[++i]) {
+		if (audio_acr->pclk == tinfo.pixel_freq)
+			break;
+	}
+
+	if (i >= lut_size) {
+		dprintf(CRITICAL, "%s: pixel clk %d not supported\n", __func__,
+			tinfo.pixel_freq);
+		return;
+	}
+
+	n = audio_acr->lut[sample_rate].n;
+	cts = audio_acr->lut[sample_rate].cts;
+	layout = (MSM_HDMI_AUDIO_CHANNEL_2 == channel_num) ? 0 : 1;
+
+	if ((AUDIO_SAMPLE_RATE_192KHZ == sample_rate) ||
+		(AUDIO_SAMPLE_RATE_176_4KHZ == sample_rate)) {
+		multiplier = 4;
+		n >>= 2; /* divide N by 4 and use multiplier */
+	} else if ((AUDIO_SAMPLE_RATE_96KHZ == sample_rate) ||
+		(AUDIO_SAMPLE_RATE_88_2KHZ == sample_rate)) {
+		multiplier = 2;
+		n >>= 1; /* divide N by 2 and use multiplier */
+	} else {
+		multiplier = 1;
+	}
+
+	dprintf(SPEW, "%s: n=%u, cts=%u, layout=%u\n", __func__, n, cts,
+		layout);
 
 	/* AUDIO_PRIORITY | SOURCE */
 	acr_pck_ctrl_reg |= 0x80000100;
 
+	/* Reset multiplier bits */
+	acr_pck_ctrl_reg &= ~(7 << 16);
+
 	/* N_MULTIPLE(multiplier) */
 	acr_pck_ctrl_reg |= (multiplier & 7) << 16;
 
-	/* SELECT(3) */
-	acr_pck_ctrl_reg |= 3 << 4;
+	if ((AUDIO_SAMPLE_RATE_48KHZ == sample_rate) ||
+	(AUDIO_SAMPLE_RATE_96KHZ == sample_rate) ||
+	(AUDIO_SAMPLE_RATE_192KHZ == sample_rate)) {
+		/* SELECT(3) */
+		acr_pck_ctrl_reg |= 3 << 4;
+		/* CTS_48 */
+		cts <<= 12;
 
-	/* CTS_48 */
-	cts <<= 12;
+		/* CTS: need to determine how many fractional bits */
+		writel(cts, HDMI_ACR_48_0);
+		/* N */
+		writel(n, HDMI_ACR_48_1);
+	} else if ((AUDIO_SAMPLE_RATE_44_1KHZ == sample_rate) ||
+		(AUDIO_SAMPLE_RATE_88_2KHZ == sample_rate) ||
+		(AUDIO_SAMPLE_RATE_176_4KHZ == sample_rate)) {
+		/* SELECT(2) */
+		acr_pck_ctrl_reg |= 2 << 4;
+		/* CTS_44 */
+		cts <<= 12;
 
-	/* CTS: need to determine how many fractional bits */
-	writel(cts, HDMI_ACR_48_0);
+		/* CTS: need to determine how many fractional bits */
+		writel(cts, HDMI_ACR_44_0);
+		/* N */
+		writel(n, HDMI_ACR_44_1);
+	} else {	/* default to 32k */
+		/* SELECT(1) */
+		acr_pck_ctrl_reg |= 1 << 4;
+		/* CTS_32 */
+		cts <<= 12;
 
-	/* N */
-	/* HDMI_ACR_48_1 */
-	writel(n, HDMI_ACR_48_1);
+		/* CTS: need to determine how many fractional bits */
+		writel(cts, HDMI_ACR_32_0);
+		/* N */
+		writel(n, HDMI_ACR_32_1);
+	}
 
 	/* Payload layout depends on number of audio channels */
+	/* LAYOUT_SEL(layout) */
 	aud_pck_ctrl_2_reg = 1 | (layout << 1);
-
 	/* override | layout */
 	writel(aud_pck_ctrl_2_reg, HDMI_AUDIO_PKT_CTRL2);
 
 	/* SEND | CONT */
-	acr_pck_ctrl_reg |= 0x3;
+	acr_pck_ctrl_reg |= 0x00000003;
 
 	writel(acr_pck_ctrl_reg, HDMI_ACR_PKT_CTRL);
 }
 
-static int mdss_hdmi_audio_info_setup(void)
+static void mdss_hdmi_audio_info_setup(void)
 {
-	uint32_t channel_count = 1;	/* Default to 2 channels
-					   -> See Table 17 in CEA-D spec */
+	uint32_t channel_count = MSM_HDMI_AUDIO_CHANNEL_2;
 	uint32_t channel_allocation = 0;
 	uint32_t level_shift = 0;
 	uint32_t down_mix = 0;
@@ -267,7 +385,7 @@
 	uint32_t aud_pck_ctrl_2_reg;
 	uint32_t layout;
 
-	layout = 0;
+	layout = (MSM_HDMI_AUDIO_CHANNEL_2 == channel_count) ? 0 : 1;;
 	aud_pck_ctrl_2_reg = 1 | (layout << 1);
 	writel(aud_pck_ctrl_2_reg, HDMI_AUDIO_PKT_CTRL2);
 
@@ -321,8 +439,6 @@
 
 	/* HDMI_INFOFRAME_CTRL0[0x002C] */
 	writel(audio_info_ctrl_reg, HDMI_INFOFRAME_CTRL0);
-
-	return 0;
 }
 
 static uint8_t* hdmi_edid_find_block(uint32_t start_offset,
@@ -359,9 +475,53 @@
 	return NULL;
 }
 
+static bool mdss_hdmi_is_audio_freq_supported(uint32_t freq)
+{
+	uint8_t *in_buf = mdss_hdmi_edid_buf;
+	const uint8_t *adb = NULL;
+	uint32_t adb_size = 0;
+	uint8_t len = 0, count = 0;
+	uint32_t next_offset = DBC_START_OFFSET;
+	uint8_t audio_data_block[MAX_AUDIO_DATA_BLOCK_SIZE];
+
+	do {
+		adb = hdmi_edid_find_block(next_offset,
+			AUDIO_DATA_BLOCK, &len);
+
+		if ((adb_size + len) > MAX_AUDIO_DATA_BLOCK_SIZE) {
+			dprintf(INFO, "%s: invalid adb length\n", __func__);
+			break;
+		}
+
+		if (!adb)
+			break;
+
+		memcpy((audio_data_block + adb_size), adb + 1, len);
+		next_offset = (adb - in_buf) + 1 + len;
+
+		adb_size += len;
+
+	} while (adb);
+
+	count = adb_size/3;
+	adb = audio_data_block;
+
+	while (count--) {
+		uint8_t freq_lst = *(adb + 1) & 0xFF;
+
+		if (freq_lst & BIT(freq))
+			return true;
+
+		adb += 3;
+	}
+
+	return false;
+}
+
 static void mdss_hdmi_audio_playback(void)
 {
 	char *base_addr;
+	uint32_t sample_rate;
 
 	base_addr = (char *) memalign(4096, 0x1000);
 	if (base_addr == NULL) {
@@ -389,13 +549,21 @@
 	writel(0x00002030, HDMI_VBI_PKT_CTRL);
 	writel(0x00002030, HDMI_VBI_PKT_CTRL);
 
-	mdss_hdmi_audio_acr_setup();
+	if (mdss_hdmi_is_audio_freq_supported(AUDIO_SAMPLE_RATE_48KHZ))
+		sample_rate = AUDIO_SAMPLE_RATE_48KHZ;
+	else
+		sample_rate = AUDIO_SAMPLE_RATE_32KHZ;
+
+	mdss_hdmi_audio_acr_setup(sample_rate);
 	mdss_hdmi_audio_info_setup();
 
 	writel(0x00000010, HDMI_AUDIO_PKT_CTRL);
 	writel(0x00000080, HDMI_AUDIO_CFG);
 	writel(0x00000011, HDMI_AUDIO_PKT_CTRL);
 	writel(0x00000081, HDMI_AUDIO_CFG);
+
+	dprintf(SPEW, "audio sample rate %s\n",
+		sample_rate == AUDIO_SAMPLE_RATE_48KHZ ? "48KHz" : "32KHz");
 }
 
 static uint32_t mdss_hdmi_panel_clock(uint8_t enable, struct msm_panel_info *pinfo)