platform: msm_shared: add support for HDMI audio configuration

Add HDMI audio support to configure 48KHz, 2 channels, ACR and
audio infoframe.

Change-Id: I82aa038a5d1f44cbf6a24403a3f7ac8deb4ebd0a
diff --git a/platform/msm_shared/mdss_hdmi.c b/platform/msm_shared/mdss_hdmi.c
index c6a5bab..b0f060e 100644
--- a/platform/msm_shared/mdss_hdmi.c
+++ b/platform/msm_shared/mdss_hdmi.c
@@ -105,6 +105,157 @@
 	0x10, 0x28, 0x00, 0x04, 0x00, 0x00, 0x00,
 	0xD1, 0x02, 0x00, 0x00, 0x01, 0x05};
 
+static void mdss_hdmi_audio_acr_setup(void)
+{
+	int n, cts, layout, multiplier;
+	uint32_t aud_pck_ctrl_2_reg = 0, acr_pck_ctrl_reg = 0;
+
+	/* 74.25MHz ACR settings */
+	n = 4096;
+	cts = 74250;
+	layout = 0;
+	multiplier = 1;
+
+	/* AUDIO_PRIORITY | SOURCE */
+	acr_pck_ctrl_reg |= 0x80000100;
+
+	/* N_MULTIPLE(multiplier) */
+	acr_pck_ctrl_reg |= (multiplier & 7) << 16;
+
+	/* SELECT(3) */
+	acr_pck_ctrl_reg |= 3 << 4;
+
+	/* CTS_48 */
+	cts <<= 12;
+
+	/* CTS: need to determine how many fractional bits */
+	writel(cts, HDMI_ACR_48_0);
+
+	/* N */
+	/* HDMI_ACR_48_1 */
+	writel(n, HDMI_ACR_48_1);
+
+	/* Payload layout depends on number of audio channels */
+	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;
+
+	writel(acr_pck_ctrl_reg, HDMI_ACR_PKT_CTRL);
+}
+
+static int 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_allocation = 0;
+	uint32_t level_shift = 0;
+	uint32_t down_mix = 0;
+	uint32_t check_sum, audio_info_0_reg, audio_info_1_reg;
+	uint32_t audio_info_ctrl_reg;
+	uint32_t aud_pck_ctrl_2_reg;
+	uint32_t layout;
+
+	layout = 0;
+	aud_pck_ctrl_2_reg = 1 | (layout << 1);
+	writel(aud_pck_ctrl_2_reg, HDMI_AUDIO_PKT_CTRL2);
+
+	/* Read first then write because it is bundled with other controls */
+	audio_info_ctrl_reg = readl(HDMI_INFOFRAME_CTRL0);
+
+	channel_allocation = 0;	/* Default to FR,FL */
+
+	/* Program the Channel-Speaker allocation */
+	audio_info_1_reg = 0;
+
+	/* CA(channel_allocation) */
+	audio_info_1_reg |= channel_allocation & 0xff;
+
+	/* Program the Level shifter */
+	/* LSV(level_shift) */
+	audio_info_1_reg |= (level_shift << 11) & 0x00007800;
+
+	/* Program the Down-mix Inhibit Flag */
+	/* DM_INH(down_mix) */
+	audio_info_1_reg |= (down_mix << 15) & 0x00008000;
+
+	writel(audio_info_1_reg, HDMI_AUDIO_INFO1);
+
+	check_sum = 0;
+	/* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_TYPE[0x84] */
+	check_sum += 0x84;
+	/* HDMI_AUDIO_INFO_FRAME_PACKET_HEADER_VERSION[0x01] */
+	check_sum += 1;
+	/* HDMI_AUDIO_INFO_FRAME_PACKET_LENGTH[0x0A] */
+	check_sum += 0x0A;
+	check_sum += channel_count;
+	check_sum += channel_allocation;
+	/* See Table 8.5 in HDMI spec */
+	check_sum += (level_shift & 0xF) << 3 | (down_mix & 0x1) << 7;
+	check_sum &= 0xFF;
+	check_sum = (256 - check_sum);
+
+	audio_info_0_reg = 0;
+	/* CHECKSUM(check_sum) */
+	audio_info_0_reg |= check_sum & 0xff;
+	/* CC(channel_count) */
+	audio_info_0_reg |= (channel_count << 8) & 0x00000700;
+
+	writel(audio_info_0_reg, HDMI_AUDIO_INFO0);
+
+	/* Set these flags */
+	/* AUDIO_INFO_UPDATE | AUDIO_INFO_SOURCE | AUDIO_INFO_CONT
+	 | AUDIO_INFO_SEND */
+	audio_info_ctrl_reg |= 0xF0;
+
+	/* HDMI_INFOFRAME_CTRL0[0x002C] */
+	writel(audio_info_ctrl_reg, HDMI_INFOFRAME_CTRL0);
+
+	return 0;
+}
+
+static void mdss_hdmi_audio_playback(void)
+{
+	uint32_t base_addr;
+
+	base_addr = memalign(4096, 0x1000);
+	if (base_addr == NULL) {
+		dprintf(CRITICAL, "%s: Error audio buffer alloc\n", __func__);
+		return;
+	}
+
+	memset(base_addr, 0, 0x1000);
+
+	writel(0x00000010, HDMI_AUDIO_PKT_CTRL);
+	writel(0x00000080, HDMI_AUDIO_CFG);
+
+	writel(0x0000096E, LPASS_LPAIF_RDDMA_CTL0);
+	writel(0x00000A6E, LPASS_LPAIF_RDDMA_CTL0);
+	writel(0x00002000, HDMI_VBI_PKT_CTRL);
+	writel(0x00000000, HDMI_GEN_PKT_CTRL);
+	writel(0x0000096E, LPASS_LPAIF_RDDMA_CTL0);
+	writel(base_addr,  LPASS_LPAIF_RDDMA_BASE0);
+	writel(0x000005FF, LPASS_LPAIF_RDDMA_BUFF_LEN0);
+	writel(0x000005FF, LPASS_LPAIF_RDDMA_PER_LEN0);
+	writel(0x0000096F, LPASS_LPAIF_RDDMA_CTL0);
+	writel(0x00000010, LPASS_LPAIF_DEBUG_CTL);
+	writel(0x00000000, HDMI_GC);
+	writel(0x00002030, HDMI_VBI_PKT_CTRL);
+	writel(0x00002030, HDMI_VBI_PKT_CTRL);
+	writel(0x00002030, HDMI_VBI_PKT_CTRL);
+
+	mdss_hdmi_audio_acr_setup();
+	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);
+}
+
 static int mdss_hdmi_panel_clock(uint8_t enable, struct msm_panel_info *pinfo)
 {
 	return target_hdmi_panel_clock(enable, pinfo);
@@ -299,16 +450,19 @@
 
 	hdmi_phy_init();
 
-	// Enable USEC REF timer
+	/* Enable USEC REF timer */
 	writel(0x0001001B, HDMI_USEC_REFTIMER);
 
-	// Video setup for HDMI
+	/* Audio settings */
+	mdss_hdmi_audio_playback();
+
+	/* Video settings */
 	mdss_hdmi_video_setup();
 
-	// AVI info setup
+	/* AVI info settings */
 	mdss_hdmi_avi_info_frame();
 
-	// Write 1 to HDMI_CTRL to enable HDMI
+	/* Enable HDMI */
 	mdss_hdmi_set_mode(true);
 
 	return 0;