summaryrefslogtreecommitdiff
path: root/drivers/video/wmt/hdmi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/wmt/hdmi.c')
-rw-r--r--drivers/video/wmt/hdmi.c1614
1 files changed, 1614 insertions, 0 deletions
diff --git a/drivers/video/wmt/hdmi.c b/drivers/video/wmt/hdmi.c
new file mode 100644
index 00000000..47626d77
--- /dev/null
+++ b/drivers/video/wmt/hdmi.c
@@ -0,0 +1,1614 @@
+/*++
+ * linux/drivers/video/wmt/hdmi.c
+ * WonderMedia video post processor (VPP) driver
+ *
+ * Copyright c 2013 WonderMedia Technologies, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * WonderMedia Technologies, Inc.
+ * 4F, 533, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C
+--*/
+
+#define HDMI_C
+#undef DEBUG
+/* #define DEBUG */
+/*----------------------- DEPENDENCE -----------------------------------------*/
+#include "hdmi.h"
+#include "vout.h"
+#ifdef __KERNEL__
+#include <asm/div64.h>
+#endif
+
+/*----------------------- PRIVATE MACRO --------------------------------------*/
+
+/*----------------------- PRIVATE CONSTANTS ----------------------------------*/
+/* #define HDMI_XXXX 1 *//*Example*/
+/* #define CONFIG_HDMI_INFOFRAME_DISABLE */
+/* #define CONFIG_HDMI_EDID_DISABLE */
+
+#define HDMI_I2C_FREQ 80000
+
+#define MAX_ACR_TV_NUM 128
+/*----------------------- PRIVATE TYPE --------------------------------------*/
+/* typedef xxxx hdmi_xxx_t; *//*Example*/
+enum hdmi_fifo_slot_t {
+ HDMI_FIFO_SLOT_AVI = 0,
+ HDMI_FIFO_SLOT_VENDOR = 1,
+ HDMI_FIFO_SLOT_AUDIO = 2,
+ HDMI_FIFO_SLOT_CONTROL = 3,
+ HDMI_FIFO_SLOT_MAX = 15
+};
+
+/*----------EXPORTED PRIVATE VARIABLES are defined in hdmi.h -------------*/
+/*----------------------- INTERNAL PRIVATE VARIABLES - -----------------------*/
+/* int hdmi_xxx; *//*Example*/
+
+/*
+Added by howayhuo
+Some TV need fix the HDMI ACR ration, otherwise these TV will show "overclock"
+Fill in the TV vendor name and TV monitor name to this list if your TV needs fix acr
+*/
+static const tv_name_t fix_acr_tv_list[] = {
+ {"TCL", "RTD2662"}, //another name: TCL L19E09
+};
+
+static tv_name_t *p_fix_acr_tv;
+
+static int fixed_acr_ration_val = 2300;
+/*--------------------- INTERNAL PRIVATE FUNCTIONS ---------------------------*/
+/* void hdmi_xxx(void); *//*Example*/
+
+/*----------------------- Function Body --------------------------------------*/
+/*---------------------------- HDMI COMMON API -------------------------------*/
+unsigned int hdmi_reg32_write(U32 offset, U32 mask, U32 shift, U32 val)
+{
+ unsigned int new_val;
+
+ new_val = (vppif_reg32_in(offset) & ~(mask))
+ | (((val) << (shift)) & mask);
+ if (offset == REG_HDMI_GENERAL_CTRL) {
+ new_val &= ~(BIT24 | BIT25 | BIT26);
+ new_val |= g_vpp.hdmi_ctrl;
+ DBG_MSG("[HDMI] reg32_wr 0x%x ctrl 0x%x\n",
+ new_val, g_vpp.hdmi_ctrl);
+ }
+ vppif_reg32_out(offset, new_val);
+ return new_val;
+}
+
+unsigned char hdmi_ecc(unsigned char *buf, int bit_cnt)
+{
+ #define HDMI_CRC_LEN 9
+
+ int crc[HDMI_CRC_LEN], crc_o[HDMI_CRC_LEN];
+ int i, j;
+ int input, result, result_rev = 0;
+
+ for (i = 0; i < HDMI_CRC_LEN; i++)
+ crc[i] = 0;
+
+ for (i = 0; i < bit_cnt; i++) {
+ for (j = 0; j < HDMI_CRC_LEN; j++)
+ crc_o[j] = crc[j];
+ input = (buf[i/8] & (1<<(i%8))) ? 1 : 0;
+ crc[0] = crc_o[7] ^ input;
+ crc[1] = crc_o[0];
+ crc[2] = crc_o[1];
+ crc[3] = crc_o[2];
+ crc[4] = crc_o[3];
+ crc[5] = crc_o[4];
+ crc[6] = crc_o[5] ^ crc_o[7] ^ input;
+ crc[7] = crc_o[6] ^ crc_o[7] ^ input;
+ crc[8] = crc_o[7];
+
+ result = 0;
+ result_rev = 0;
+ for (j = 0; j < HDMI_CRC_LEN - 1; j++) {
+ result += (crc[j] << j);
+ result_rev += (crc[j] << (HDMI_CRC_LEN - 2 - j));
+ }
+ }
+
+/* DPRINT("[HDMI] crc 0x%x, %x %x %x %x %x %x %x\n",result_rev,
+ buf[0],buf[1],buf[2],buf[3],buf[4],buf[5],buf[6]); */
+ return result_rev;
+}
+
+unsigned char hdmi_checksum(unsigned char *header,
+ unsigned char *buf, int cnt)
+{
+ unsigned char sum;
+ int i;
+
+ for (i = 0, sum = 0; i < cnt; i++)
+ sum += buf[i];
+ for (i = 0; i < 3; i++)
+ sum += header[i];
+ return 0 - sum;
+}
+
+#ifdef WMT_FTBLK_HDMI
+/*---------------------------- HDMI HAL --------------------------------------*/
+void hdmi_set_power_down(int pwrdn)
+{
+ DBG_DETAIL("(%d)\n", pwrdn);
+
+ if ((vppif_reg32_read(HDMI_PD) == 0) && (pwrdn == 0)) {
+ return; /* avoid HDMI reset */
+ }
+
+ vppif_reg32_write(HDMI_INTERNAL_LDO, (pwrdn) ? 0 : 1);
+ vppif_reg32_write(HDMI_PD, pwrdn);
+ if (!pwrdn) {
+ vppif_reg32_write(HDMI_RESET_PLL, 1);
+ mdelay(1);
+ vppif_reg32_write(HDMI_RESET_PLL, 0);
+ }
+ mdelay(1);
+ vppif_reg32_write(HDMI_PD_L2HA, pwrdn);
+}
+
+int hdmi_get_power_down(void)
+{
+ return vppif_reg32_read(HDMI_PD);
+}
+
+void hdmi_set_enable(vpp_flag_t enable)
+{
+ hdmi_reg32_write(HDMI_ENABLE, enable);
+ vppif_reg32_write(HDMI_MODE, (enable) ? 0 : 1);
+}
+
+void hdmi_set_avmute(vpp_flag_t mute)
+{
+ vppif_reg32_write(HDMI_AVMUTE_SET_ENABLE, mute);
+}
+
+void hdmi_set_dvi_enable(vpp_flag_t enable)
+{
+ hdmi_reg32_write(HDMI_DVI_MODE_ENABLE, enable);
+}
+
+void hdmi_set_sync_low_active(vpp_flag_t hsync, vpp_flag_t vsync)
+{
+ hdmi_reg32_write(HDMI_HSYNC_LOW_ACTIVE, hsync);
+ hdmi_reg32_write(HDMI_VSYNC_LOW_ACTIVE, vsync);
+}
+
+void hdmi_get_sync_polar(int *hsync_hi, int *vsync_hi)
+{
+ *hsync_hi = (vppif_reg32_read(HDMI_HSYNC_LOW_ACTIVE)) ? 0 : 1;
+ *vsync_hi = (vppif_reg32_read(HDMI_VSYNC_LOW_ACTIVE)) ? 0 : 1;
+}
+
+void hdmi_set_output_colfmt(vdo_color_fmt colfmt)
+{
+ unsigned int val;
+
+ switch (colfmt) {
+ default:
+ case VDO_COL_FMT_ARGB:
+ val = 0;
+ break;
+ case VDO_COL_FMT_YUV444:
+ val = 1;
+ break;
+ case VDO_COL_FMT_YUV422H:
+ case VDO_COL_FMT_YUV422V:
+ val = 2;
+ break;
+ }
+ hdmi_reg32_write(HDMI_CONVERT_YUV422, (val == 2) ? 1 : 0);
+ hdmi_reg32_write(HDMI_OUTPUT_FORMAT, val);
+}
+
+vdo_color_fmt hdmi_get_output_colfmt(void)
+{
+ unsigned int val;
+
+ val = vppif_reg32_read(HDMI_OUTPUT_FORMAT);
+ switch (val) {
+ default:
+ case 0:
+ return VDO_COL_FMT_ARGB;
+ case 1:
+ return VDO_COL_FMT_YUV444;
+ case 2:
+ return VDO_COL_FMT_YUV422H;
+ }
+ return VDO_COL_FMT_ARGB;
+}
+
+int hdmi_get_plugin(void)
+{
+ int plugin;
+
+ if (vppif_reg32_read(HDMI_HOTPLUG_IN_INT)) {
+ plugin = vppif_reg32_read(HDMI_HOTPLUG_IN);
+ } else {
+ int tre_en;
+
+ tre_en = vppif_reg32_read(HDMI_TRE_EN);
+ vppif_reg32_write(HDMI_TRE_EN, 0);
+ plugin = vppif_reg32_read(HDMI_RSEN);
+ vppif_reg32_write(HDMI_TRE_EN, tre_en);
+ }
+ return plugin;
+}
+
+int hdmi_get_plug_status(void)
+{
+ int reg;
+
+ reg = vppif_reg32_in(REG_HDMI_HOTPLUG_DETECT);
+ return reg & 0x3000000;
+}
+
+void hdmi_clear_plug_status(void)
+{
+ vppif_reg32_write(HDMI_HOTPLUG_IN_STS, 1);
+ vppif_reg32_write(HDMI_HOTPLUG_OUT_STS, 1);
+}
+
+void hdmi_enable_plugin(int enable)
+{
+ vppif_reg32_write(HDMI_HOTPLUG_OUT_INT, enable);
+ vppif_reg32_write(HDMI_HOTPLUG_IN_INT, enable);
+}
+
+void hdmi_write_fifo(enum hdmi_fifo_slot_t no, unsigned int *buf, int cnt)
+{
+ int i;
+
+ if (no > HDMI_FIFO_SLOT_MAX)
+ return;
+#ifdef DEBUG
+{
+ char *ptr;
+
+ DPRINT("[HDMI] wr fifo %d,cnt %d", no, cnt);
+ ptr = (char *) buf;
+ for (i = 0; i < cnt; i++) {
+ if ((i % 4) == 0)
+ DPRINT("\n %02d :", i);
+ DPRINT(" 0x%02x", ptr[i]);
+ }
+ DPRINT("\n[HDMI] AVI info package end\n");
+}
+#endif
+
+ vppif_reg32_out(REG_HDMI_FIFO_CTRL, (no << 8));
+ cnt = (cnt + 3) / 4;
+ for (i = 0; i < cnt; i++)
+ vppif_reg32_out(REG_HDMI_WR_FIFO_ADDR + 4 * i, buf[i]);
+ vppif_reg32_write(HDMI_INFOFRAME_WR_STROBE, 1);
+}
+
+void hdmi_read_fifo(enum hdmi_fifo_slot_t no, unsigned int *buf, int cnt)
+{
+ int i;
+ int rdy;
+
+ if (no > HDMI_FIFO_SLOT_MAX)
+ return;
+
+ rdy = vppif_reg32_read(HDMI_INFOFRAME_FIFO1_RDY);
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_RDY, 0);
+
+ no = no - 1;
+ vppif_reg32_out(REG_HDMI_FIFO_CTRL, (no << 8));
+ vppif_reg32_write(HDMI_INFOFRAME_RD_STROBE, 1);
+ cnt = (cnt + 3) / 4;
+ for (i = 0; i < cnt; i++)
+ buf[i] = vppif_reg32_in(REG_HDMI_RD_FIFO_ADDR + 4 * i);
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_RDY, rdy);
+#ifdef DEBUG
+{
+ char *ptr;
+
+ cnt *= 4;
+ DPRINT("[HDMI] rd fifo %d,cnt %d", no, cnt);
+ ptr = (char *) buf;
+ for (i = 0; i < cnt; i++) {
+ if ((i % 4) == 0)
+ DPRINT("\n %02d :", i);
+ DPRINT(" 0x%02x", ptr[i]);
+ }
+ DPRINT("\n[HDMI] AVI info package end\n");
+}
+#endif
+}
+
+#if 1
+int hdmi_ddc_delay_us = 5;
+int hdmi_ddc_ctrl_delay_us = 5;
+
+#define HDMI_DDC_OUT
+#define HDMI_DDC_DELAY hdmi_ddc_delay_us
+#define HDMI_DDC_CHK_DELAY 1
+#define HDMI_DDC_CTRL_DELAY hdmi_ddc_ctrl_delay_us
+#else
+#define HDMI_DDC_OUT
+#define HDMI_DDC_DELAY 1
+#define HDMI_DDC_CHK_DELAY 1
+#define HDMI_DDC_CTRL_DELAY 1
+#endif
+
+#define HDMI_STATUS_START BIT16
+#define HDMI_STATUS_STOP BIT17
+#define HDMI_STATUS_WR_AVAIL BIT18
+#define HDMI_STATUS_CP_USE BIT19
+#define HDMI_STATUS_SW_READ BIT25
+int hdmi_DDC_check_status(unsigned int checkbits, int condition)
+{
+ int status = 1;
+ unsigned int i = 0, maxloop;
+
+ maxloop = 500 / HDMI_DDC_CHK_DELAY;
+ udelay(HDMI_DDC_DELAY);
+
+ if (condition) { /* wait 1 --> 0 */
+ while ((vppif_reg32_in(REG_HDMI_I2C_CTRL2) & checkbits)
+ && (i < maxloop)) {
+ udelay(HDMI_DDC_CHK_DELAY);
+ if (++i == maxloop)
+ status = 0;
+ }
+ } else { /* wait 0 --> 1 */
+ while (!(vppif_reg32_in(REG_HDMI_I2C_CTRL2) & checkbits)
+ && (i < maxloop)) {
+ udelay(HDMI_DDC_CHK_DELAY);
+ if (++i == maxloop)
+ status = 0;
+ }
+ }
+
+ if ((status == 0) && (checkbits != HDMI_STATUS_SW_READ)) {
+ unsigned int reg;
+
+ reg = vppif_reg32_in(REG_HDMI_I2C_CTRL2);
+ DBG_DETAIL("[HDMI] status timeout check 0x%x,wait to %s\n",
+ checkbits, (condition) ? "0" : "1");
+ DBG_DETAIL("[HDMI] 0x%x,sta %d,stop %d,wr %d,rd %d,cp %d\n",
+ reg, (reg & HDMI_STATUS_START) ? 1 : 0,
+ (reg & HDMI_STATUS_STOP) ? 1 : 0,
+ (reg & HDMI_STATUS_WR_AVAIL) ? 1 : 0,
+ (reg & HDMI_STATUS_SW_READ) ? 1 : 0,
+ (reg & HDMI_STATUS_CP_USE) ? 1 : 0);
+ }
+ return status;
+}
+
+void hdmi_DDC_set_freq(unsigned int hz)
+{
+ unsigned int clock;
+ unsigned int div;
+
+ clock = 25000000*15/100; /* RTC clock source */
+ div = clock / hz;
+
+ vppif_reg32_write(HDMI_I2C_CLK_DIVIDER, div);
+ DBG_DETAIL("[HDMI] set freq(%d,clk %d,div %d)\n", hz, clock, div);
+}
+
+void hdmi_DDC_reset(void)
+{
+ vppif_reg32_write(HDMI_I2C_SW_RESET, 1);
+ udelay(1);
+ vppif_reg32_write(HDMI_I2C_SW_RESET, 0);
+}
+
+int hdmi_DDC_read_func(char addr, int index, char *buf, int length)
+{
+ int status = 1;
+ unsigned int i = 0;
+ int err_cnt = 0;
+
+ DBG_DETAIL("[HDMI] read DDC(index 0x%x,len %d),reg 0x%x\n",
+ index, length, vppif_reg32_in(REG_HDMI_I2C_CTRL2));
+
+#ifdef CONFIG_HDMI_EDID_DISABLE
+ return status;
+#endif
+
+ hdmi_DDC_set_freq(g_vpp.hdmi_i2c_freq);
+ /* enhanced DDC read */
+ if (index >= 256) {
+ /* sw start, write data avail */
+ vppif_reg32_write(REG_HDMI_I2C_CTRL2, BIT18 + BIT16, 16, 0x5);
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait start & wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_START +
+ HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* start\n");
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+ /* Slave address */
+ vppif_reg32_write(HDMI_WR_DATA, 0x60);
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* slave addr 0x%x\n", addr);
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+ /* Offset */
+ vppif_reg32_write(HDMI_WR_DATA, 0x1);
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* index 0x%x\n", index);
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+ index -= 256;
+ }
+
+ /* START */
+#ifdef HDMI_DDC_OUT
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, 0x50000);
+#else
+#if 0
+ vppif_reg32_write(HDMI_SW_START_REQ, 1); /* sw start */
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+#endif
+ /* sw start, write data avail */
+ vppif_reg32_write(REG_HDMI_I2C_CTRL2, BIT18 + BIT16, 16, 0x5);
+#endif
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait start & wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_START +
+ HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* start\n");
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+ /* Slave address */
+#ifdef HDMI_DDC_OUT
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, 0x400A0);
+#else
+ vppif_reg32_write(HDMI_WR_DATA, addr);
+ udelay(HDMI_DDC_DELAY);
+ while (vppif_reg32_read(HDMI_WR_DATA) != addr)
+ ;
+ udelay(HDMI_DDC_DELAY);
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+#endif
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* slave addr 0x%x\n", addr);
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+ /* Offset */
+#ifdef HDMI_DDC_OUT
+{
+ unsigned int reg;
+
+ reg = 0x40000;
+ reg |= index;
+
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, reg);
+}
+#else
+ vppif_reg32_write(HDMI_WR_DATA, index);
+ udelay(HDMI_DDC_DELAY);
+ while (vppif_reg32_read(HDMI_WR_DATA) != index)
+ ;
+ udelay(HDMI_DDC_DELAY);
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+#endif
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* index 0x%x\n", index);
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+/* vppif_reg32_write(HDMI_WR_DATA,addr+1); */
+
+ /* START */
+#ifdef HDMI_DDC_OUT
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, 0x50000);
+#else
+#if 0
+ vppif_reg32_write(HDMI_SW_START_REQ, 1); /* sw start */
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+#endif
+ /* sw start, write data avail */
+ vppif_reg32_write(REG_HDMI_I2C_CTRL2, BIT18 + BIT16, 16, 0x5);
+#endif
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait start & wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_START +
+ HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* restart\n");
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+ /* Slave Address + 1 */
+#ifdef HDMI_DDC_OUT
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, 0x400A1);
+#else
+ vppif_reg32_write(HDMI_WR_DATA, addr + 1);
+ udelay(HDMI_DDC_DELAY);
+ while (vppif_reg32_read(HDMI_WR_DATA) != (addr + 1))
+ ;
+ udelay(HDMI_DDC_DELAY);
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+#endif
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* slave addr 0x%x\n", addr + 1);
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+
+ /* Read Data */
+ for (i = 0; i < length; i++) {
+#ifdef HDMI_DDC_OUT
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, 0x40000);
+#else
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+ /* hdmi_reg32_write(HDMI_WR_DATA_AVAIL,1); */
+#endif
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_WR_AVAIL, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* wr ACK(%d)\n", i);
+ err_cnt++;
+ goto ddc_read_fail;
+ /* break; */
+ }
+
+ /* wait sw read not set */
+ status = hdmi_DDC_check_status(HDMI_STATUS_SW_READ, 0);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* read avail(%d)\n", i);
+ if (i == 0) {
+ err_cnt++;
+ /* goto ddc_read_fail; */
+ } else {
+ /* g_vpp.dbg_hdmi_ddc_read_err++; */
+ }
+ goto ddc_read_fail;
+ /* break; */
+ }
+
+ *buf++ = vppif_reg32_read(HDMI_RD_DATA);
+ udelay(HDMI_DDC_DELAY);
+#ifdef HDMI_DDC_OUT
+ vppif_reg32_out(REG_HDMI_I2C_CTRL2, 0x2000000);
+#else
+ vppif_reg32_write(HDMI_SW_READ, 1);
+#endif
+ udelay(HDMI_DDC_DELAY);
+ }
+
+ /* STOP */
+#if 0
+ vppif_reg32_write(HDMI_SW_STOP_REQ, 1); /* sw stop */
+ vppif_reg32_write(HDMI_WR_DATA_AVAIL, 1); /* write data avail */
+#endif
+ /* sw stop, write data avail */
+ vppif_reg32_write(REG_HDMI_I2C_CTRL2, BIT18 + BIT17, 17, 3);
+ udelay(HDMI_DDC_CTRL_DELAY);
+ /* wait start & wr data avail */
+ status = hdmi_DDC_check_status(HDMI_STATUS_STOP +
+ HDMI_STATUS_WR_AVAIL + HDMI_STATUS_CP_USE, 1);
+ if (status == 0) {
+ DBGMSG("[HDMI] *E* stop\n");
+ err_cnt++;
+ goto ddc_read_fail;
+ }
+ udelay(HDMI_DDC_DELAY);
+
+ddc_read_fail:
+ if (err_cnt)
+ DBGMSG("[HDMI] *E* read DDC %d\n", err_cnt);
+ return (err_cnt) ? 1 : 0;
+}
+
+int hdmi_DDC_read(char addr, int index, char *buf, int length)
+{
+ int retry = 3;
+ do {
+ if (hdmi_DDC_read_func(addr, index, buf, length) == 0)
+ break;
+ hdmi_DDC_reset();
+ DPRINT("[HDMI] *W* DDC reset\n");
+ retry--;
+ } while (retry);
+ return (retry == 0) ? 1 : 0;
+}
+
+void hdmi_audio_enable(vpp_flag_t enable)
+{
+ vppif_reg32_write(HDMI_AUD_ENABLE, enable);
+}
+
+void hdmi_audio_mute(vpp_flag_t enable)
+{
+ vppif_reg32_write(HDMI_AUD_MUTE, enable);
+}
+
+/*----------------------- HDMI API --------------------------------------*/
+void hdmi_write_packet(unsigned int header, unsigned char *packet,
+ int cnt)
+{
+ unsigned char buf[36];
+ int i;
+ enum hdmi_fifo_slot_t no;
+
+#ifdef CONFIG_HDMI_INFOFRAME_DISABLE
+ return;
+#endif
+ memcpy(&buf[0], &header, 3);
+ buf[3] = hdmi_ecc((unsigned char *)&header, 24);
+ for (i = 0; i < cnt / 7; i++) {
+ memcpy(&buf[4+8*i], &packet[7*i], 7);
+ buf[11+8*i] = hdmi_ecc(&packet[7*i], 56);
+ }
+
+ switch (header & 0xFF) {
+ case HDMI_PACKET_INFOFRAME_AVI:
+ no = HDMI_FIFO_SLOT_AVI;
+ break;
+ case HDMI_PACKET_INFOFRAME_AUDIO:
+ no = HDMI_FIFO_SLOT_AUDIO;
+ break;
+ case HDMI_PACKET_INFOFRAME_VENDOR:
+ no = HDMI_FIFO_SLOT_VENDOR;
+ break;
+ default:
+ no = HDMI_FIFO_SLOT_CONTROL;
+ break;
+ }
+ hdmi_write_fifo(no, (unsigned int *)buf, (4 + 8 * (cnt / 7)));
+}
+
+void hdmi_tx_null_packet(void)
+{
+ hdmi_write_packet(HDMI_PACKET_NULL, 0, 0);
+}
+
+void hdmi_tx_general_control_packet(int mute)
+{
+ unsigned char buf[7];
+ memset(buf, 0x0, 7);
+ buf[0] = (mute) ? 0x01 : 0x10;
+ buf[1] = HDMI_COLOR_DEPTH_24 | (HDMI_PHASE_4 << 4);
+ hdmi_write_packet(HDMI_PACKET_GENERAL_CTRL, buf, 7);
+}
+
+int hdmi_get_pic_aspect(hdmi_video_code_t vic)
+{
+ switch (vic) {
+ case HDMI_640x480p60_4x3:
+ case HDMI_720x480p60_4x3:
+ case HDMI_1440x480i60_4x3:
+ case HDMI_1440x240p60_4x3:
+ case HDMI_2880x480i60_4x3:
+ case HDMI_2880x240p60_4x3:
+ case HDMI_1440x480p60_4x3:
+ case HDMI_720x576p50_4x3:
+ case HDMI_1440x576i50_4x3:
+ case HDMI_1440x288p50_4x3:
+ case HDMI_2880x576i50_4x3:
+ case HDMI_2880x288p50_4x3:
+ case HDMI_1440x576p50_4x3:
+ return HDMI_PIC_ASPECT_4_3;
+ default:
+ break;
+ }
+ return HDMI_PIC_ASPECT_16_9;
+}
+
+int hdmi_get_vic(int resx, int resy, int fps, int interlace)
+{
+ hdmi_vic_t info;
+ int i;
+
+ info.resx = resx;
+ info.resy = resy;
+ info.freq = fps;
+ info.option = (interlace) ? HDMI_VIC_INTERLACE : HDMI_VIC_PROGRESS;
+ info.option |= (vout_check_ratio_16_9(resx, resy)) ?
+ HDMI_VIC_16x9 : HDMI_VIC_4x3;
+ for (i = 0; i < HDMI_VIDEO_CODE_MAX; i++) {
+ if (memcmp(&hdmi_vic_info[i], &info, sizeof(hdmi_vic_t)) == 0)
+ return i;
+ }
+ return HDMI_UNKNOW;
+}
+
+void hdmi_tx_avi_infoframe_packet(vdo_color_fmt colfmt,
+ hdmi_video_code_t vic)
+{
+ unsigned int header;
+ unsigned char buf[28];
+ unsigned char temp;
+
+ memset(buf, 0x0, 28);
+ header = HDMI_PACKET_INFOFRAME_AVI + (0x2 << 8) + (0x0d << 16);
+ buf[1] = HDMI_SI_NO_DATA + (HDMI_BI_V_H_VALID << 2) +
+ (HDMI_AF_INFO_NO_DATA << 4);
+ switch (colfmt) {
+ case VDO_COL_FMT_YUV422H:
+ case VDO_COL_FMT_YUV422V:
+ temp = HDMI_OUTPUT_YUV422;
+ break;
+ case VDO_COL_FMT_YUV444:
+ temp = HDMI_OUTPUT_YUV444;
+ break;
+ case VDO_COL_FMT_ARGB:
+ default:
+ temp = HDMI_OUTPUT_RGB;
+ break;
+ }
+ buf[1] += (temp << 5);
+ buf[2] = HDMI_ASPECT_RATIO_PIC + (hdmi_get_pic_aspect(vic) << 4) +
+ (HDMI_COLORIMETRY_ITU709 << 6);
+ buf[3] = 0x84;
+ buf[4] = vic;
+ switch (vic) {
+ case HDMI_1440x480i60_16x9:
+ case HDMI_1440x576i50_16x9:
+ buf[5] = HDMI_PIXEL_REP_2;
+ break;
+ default:
+ buf[5] = HDMI_PIXEL_REP_NO;
+ break;
+ }
+ buf[0] = hdmi_checksum((unsigned char *)&header, buf, 28);
+ hdmi_write_packet(header, buf, 28);
+}
+
+void hdmi_tx_audio_infoframe_packet(int channel, int freq)
+{
+ unsigned int header;
+ unsigned char buf[28];
+
+ memset(buf, 0x0, 28);
+ header = HDMI_PACKET_INFOFRAME_AUDIO + (0x1 << 8) + (0x0a << 16);
+ buf[1] = (channel - 1) + (HDMI_AUD_TYPE_REF_STM << 4);
+ buf[2] = 0x0; /* HDMI_AUD_SAMPLE_24 + (freq << 2); */
+ buf[3] = 0x00;
+ buf[4] = 0x0;
+ buf[5] = 0x0; /* 0 db */
+ buf[0] = hdmi_checksum((unsigned char *)&header, buf, 28);
+ hdmi_write_packet(header, buf, 28);
+}
+
+void hdmi_tx_vendor_specific_infoframe_packet(void)
+{
+ unsigned int header;
+ unsigned char buf[28];
+ unsigned char structure_3d, meta_present;
+ unsigned char hdmi_video_format;
+
+ /* 0-No,1-1 byte param,2-3D format */
+ hdmi_video_format = (g_vpp.hdmi_3d_type) ? 2 : 0;
+ /* HDMI_3D_STRUCTURE_XXX; */
+ structure_3d = (g_vpp.hdmi_3d_type == 1) ? 0 : g_vpp.hdmi_3d_type;
+ meta_present = 0;
+
+ memset(buf, 0x0, 28);
+ header = HDMI_PACKET_INFOFRAME_VENDOR + (0x1 << 8) + (0xa << 16);
+ buf[1] = 0x3;
+ buf[2] = 0xC;
+ buf[3] = 0x0;
+ buf[4] = (hdmi_video_format << 5);
+ buf[5] = (structure_3d << 4) + ((meta_present) ? 0x8 : 0x0);
+ buf[6] = 0x0; /* 3D_Ext_Data */
+#if 0 /* metadata present */
+ buf[7] = 0x0; /* 3D_Metadata_type,3D_Metadata_Length(N) */
+ buf[8] = 0x0; /* 3D Metadata 1_N */
+#endif
+ buf[0] = hdmi_checksum((unsigned char *)&header, buf, 28);
+ hdmi_write_packet(header, buf, 28);
+}
+
+/*
+--> Added by howayhuo.
+Some TV (example: TCL L19E09) will overclock if ACR ratio too large,
+So we need decrease the ACR ratio for some special TV
+*/
+static void print_acr(void)
+{
+ int i;
+
+ if(p_fix_acr_tv != NULL) {
+ for(i = 0; i < MAX_ACR_TV_NUM; i++) {
+ if(strlen(p_fix_acr_tv[i].vendor_name) == 0
+ || strlen(p_fix_acr_tv[i].monitor_name) == 0)
+ break;
+
+ if(i == 0)
+ printk("ACR TV Name:\n");
+
+ printk(" %s,%s\n", p_fix_acr_tv[i].vendor_name, p_fix_acr_tv[i].monitor_name);
+ }
+ }
+}
+
+static void acr_init(void)
+{
+ char buf[512] = {0};
+ int buflen = 512;
+ unsigned long val;
+ int i, j, k, tv_num;
+ int ret, to_save_vendor;
+ tv_name_t tv_name;
+
+ if(p_fix_acr_tv != NULL) {
+ kfree(p_fix_acr_tv);
+ p_fix_acr_tv = NULL;
+ }
+
+ if(wmt_getsyspara("wmt.acr.ratio", buf, &buflen) == 0) {
+ ret = strict_strtoul(buf, 10, &val);
+ if(ret) {
+ printk("[HDMI] Wrong wmt.acr.ratio value: %s\n", buf);
+ return;
+ }
+ if(val >= 0 && val < 0xFFFFF) // total 20 bits
+ fixed_acr_ration_val = (int)val;
+ else
+ printk("[HDMI] Invalid Fixed ACR Ratio: %lu\n", val);
+ }
+
+ if(fixed_acr_ration_val == 0)
+ return;
+
+ /*
+ For example: setenv wmt.acr.tv 'TCH,RTD2662;PHL,Philips 244E'
+ */
+ if(wmt_getsyspara("wmt.acr.tv", buf, &buflen) != 0) {
+ p_fix_acr_tv = (tv_name_t *)kzalloc(sizeof(fix_acr_tv_list) + sizeof(tv_name_t), GFP_KERNEL);
+ if(p_fix_acr_tv) {
+ memcpy(p_fix_acr_tv, fix_acr_tv_list, sizeof(fix_acr_tv_list));
+ print_acr();
+ } else
+ printk("[HDMI] malloc for ACR fail. malloc len = %d\n",
+ sizeof(fix_acr_tv_list) + sizeof(tv_name_t));
+
+ return;
+ }
+
+ tv_num = 0;
+ buflen = strlen(buf);
+ if(buflen == 0)
+ return;
+
+ if(buflen == sizeof(buf)) {
+ printk("[HDMI] wmt.acr.tv too long\n");
+ return;
+ }
+
+ for(i = 0; i < buflen; i++) {
+ if(buf[i] == ',')
+ tv_num++;
+ }
+
+ /*
+ Limit TV Number
+ */
+ if(tv_num > MAX_ACR_TV_NUM)
+ tv_num = MAX_ACR_TV_NUM;
+
+ if(tv_num == 0)
+ return;
+
+ printk("acr_tv_num = %d\n", tv_num);
+ p_fix_acr_tv = (tv_name_t *)kzalloc((tv_num + 1) * sizeof(tv_name_t), GFP_KERNEL);
+ if(!p_fix_acr_tv) {
+ printk("[HDMI] malloc for ACR fail. malloc len = %d\n",
+ sizeof(fix_acr_tv_list) + sizeof(tv_name_t));
+ return;
+ }
+ memset(&tv_name, 0, sizeof(tv_name_t));
+
+ j = 0;
+ k = 0;
+ to_save_vendor= 1;
+ for(i = 0; i < buflen + 1; i++) {
+ if(buf[i] != ',' && buf[i] != ';' && buf[i] != '\0') {
+ if(to_save_vendor) {
+ if(k < VENDOR_NAME_LEN)
+ tv_name.vendor_name[k] = buf[i];
+ } else {
+ if(k < MONITOR_NAME_LEN)
+ tv_name.monitor_name[k] = buf[i];
+ }
+ k++;
+ } else if(buf[i] == ',') {
+ to_save_vendor = 0;
+ k = 0;
+ } else {
+ if(strlen(tv_name.vendor_name) == 0 || strlen(tv_name.monitor_name) == 0) {
+ printk("[HDMI] Wrong wmt.acr.tv format\n");
+ kfree(p_fix_acr_tv);
+ p_fix_acr_tv = NULL;
+ break;
+ } else {
+ if(j < tv_num) {
+ memcpy(p_fix_acr_tv + j, &tv_name, sizeof(tv_name_t));
+ memset(&tv_name, 0, sizeof(tv_name_t));
+ j++;
+ }
+
+ if(j == tv_num)
+ break;
+ }
+
+ if(buf[i]== ';') {
+ to_save_vendor = 1;
+ k = 0;
+ } else
+ break;
+ }
+ }
+
+ print_acr();
+}
+
+void acr_exit(void)
+{
+ if(p_fix_acr_tv != NULL) {
+ kfree(p_fix_acr_tv);
+ p_fix_acr_tv = NULL;
+ }
+}
+
+static int use_fix_acr_ratio(void)
+{
+ int i;
+
+ if(fixed_acr_ration_val == 0 || p_fix_acr_tv == NULL)
+ return 0;
+
+ for(i = 0; i < MAX_ACR_TV_NUM; i++) {
+ if(strlen(p_fix_acr_tv[i].vendor_name) == 0
+ || strlen(p_fix_acr_tv[i].monitor_name) == 0)
+ break;
+
+ if(!strcmp(edid_parsed.tv_name.vendor_name, p_fix_acr_tv[i].vendor_name)
+ && !strcmp(edid_parsed.tv_name.monitor_name, p_fix_acr_tv[i].monitor_name)) {
+ printk("TV is \"%s %s\". Use fixed HDMI ACR Ratio: %d\n",
+ edid_parsed.tv_name.vendor_name,
+ edid_parsed.tv_name.monitor_name,
+ fixed_acr_ration_val);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+/*
+<-- end added by howayhuo
+*/
+
+void hdmi_set_audio_n_cts(unsigned int freq)
+{
+ unsigned int n, cts;
+
+ n = 128 * freq / 1000;
+#ifdef __KERNEL__
+{
+ unsigned int tmp;
+ unsigned int pll_clk;
+
+ pll_clk = auto_pll_divisor(DEV_I2S, GET_FREQ, 0, 0);
+ tmp = (vppif_reg32_in(AUDREGF_BASE_ADDR+0x70) & 0xF);
+
+ switch (tmp) {
+ case 0 ... 4:
+ tmp = 0x01 << tmp;
+ break;
+ case 9 ... 12:
+ tmp = 3 * (0x1 << (tmp-9));
+ break;
+ default:
+ tmp = 1;
+ break;
+ }
+
+ {
+ unsigned long long tmp2;
+ unsigned long long div2;
+ unsigned long mod;
+
+ tmp2 = g_vpp.hdmi_pixel_clock;
+ tmp2 = tmp2 * n * tmp;
+ div2 = pll_clk;
+ mod = do_div(tmp2, div2);
+ cts = tmp2;
+ }
+ DBGMSG("[HDMI] i2s %d,cts %d,reg 0x%x\n", pll_clk, cts,
+ vppif_reg32_in(AUDREGF_BASE_ADDR + 0x70));
+}
+
+ vppif_reg32_write(HDMI_AUD_N_20BITS, n);
+ if(use_fix_acr_ratio())
+ vppif_reg32_write(HDMI_AUD_ACR_RATIO, fixed_acr_ration_val);
+ else
+ vppif_reg32_write(HDMI_AUD_ACR_RATIO, cts - 1);
+#else
+#if 1
+ cts = g_vpp.hdmi_pixel_clock / 1000;
+#else
+ cts = vpp_get_base_clock(VPP_MOD_GOVRH) / 1000;
+#endif
+ vppif_reg32_write(HDMI_AUD_N_20BITS, n);
+ if(use_fix_acr_ratio())
+ vppif_reg32_write(HDMI_AUD_ACR_RATIO, fixed_acr_ration_val);
+ else
+ vppif_reg32_write(HDMI_AUD_ACR_RATIO, cts - 2);
+#endif
+
+#if 1 /* auto detect CTS */
+ vppif_reg32_write(HDMI_AUD_CTS_SELECT, 0);
+ cts = 0;
+#else
+ vppif_reg32_write(HDMI_AUD_CTS_SELECT, 1);
+#endif
+ vppif_reg32_write(HDMI_AUD_CTS_LOW_12BITS, cts & 0xFFF);
+ vppif_reg32_write(HDMI_AUD_CTS_HI_8BITS, (cts & 0xFF000) >> 12);
+
+ DBGMSG("[HDMI] set audio freq %d,n %d,cts %d,tmds %d\n",
+ freq, n, cts, g_vpp.hdmi_pixel_clock);
+}
+void hdmi_config_audio(vout_audio_t *info)
+{
+ unsigned int freq;
+
+ hdmi_info.channel = info->channel;
+ hdmi_info.freq = info->sample_rate;
+
+ /* enable ARF & ARFP clock */
+ REG32_VAL(PM_CTRL_BASE_ADDR + 0x254) |= (BIT4 | BIT3);
+ hdmi_tx_audio_infoframe_packet(info->channel - 1, info->sample_rate);
+ hdmi_audio_enable(VPP_FLAG_DISABLE);
+ vppif_reg32_write(HDMI_AUD_LAYOUT, (info->channel == 8) ? 1 : 0);
+ vppif_reg32_write(HDMI_AUD_2CH_ECO, 1);
+
+ switch (info->sample_rate) {
+ case 32000:
+ freq = 0x3;
+ break;
+ case 44100:
+ freq = 0x0;
+ break;
+ case 88200:
+ freq = 0x8;
+ break;
+ case 176400:
+ freq = 0xC;
+ break;
+ default:
+ case 48000:
+ freq = 0x2;
+ break;
+ case 96000:
+ freq = 0xA;
+ break;
+ case 192000:
+ freq = 0xE;
+ break;
+ case 768000:
+ freq = 0x9;
+ break;
+ }
+ vppif_reg32_out(REG_HDMI_AUD_CHAN_STATUS0, (freq << 24) + 0x4);
+ vppif_reg32_out(REG_HDMI_AUD_CHAN_STATUS1, 0x0);
+ vppif_reg32_out(REG_HDMI_AUD_CHAN_STATUS2, 0xb);
+ vppif_reg32_out(REG_HDMI_AUD_CHAN_STATUS3, 0x0);
+ vppif_reg32_out(REG_HDMI_AUD_CHAN_STATUS4, 0x0);
+ vppif_reg32_out(REG_HDMI_AUD_CHAN_STATUS5, 0x0);
+
+ hdmi_set_audio_n_cts(info->sample_rate);
+ vppif_reg32_write(HDMI_AUD_ACR_ENABLE, VPP_FLAG_ENABLE);
+ vppif_reg32_write(HDMI_AUD_AIPCLK_RATE, 0);
+ hdmi_audio_enable(hdmi_get_plugin() ?
+ VPP_FLAG_ENABLE : VPP_FLAG_DISABLE);
+
+}
+
+void hdmi_config_video(hdmi_info_t *info)
+{
+ hdmi_set_output_colfmt(info->outfmt);
+ hdmi_tx_avi_infoframe_packet(info->outfmt, info->vic);
+ hdmi_tx_vendor_specific_infoframe_packet();
+}
+
+void hdmi_set_option(unsigned int option)
+{
+ vdo_color_fmt colfmt;
+ int temp;
+
+ hdmi_set_dvi_enable((option & EDID_OPT_HDMI) ?
+ VPP_FLAG_DISABLE : VPP_FLAG_ENABLE);
+ hdmi_audio_enable((option & EDID_OPT_AUDIO) ?
+ VPP_FLAG_ENABLE : VPP_FLAG_DISABLE);
+
+ colfmt = hdmi_get_output_colfmt();
+ switch (colfmt) {
+ case VDO_COL_FMT_YUV422H:
+ temp = option & EDID_OPT_YUV422;
+ break;
+ case VDO_COL_FMT_YUV444:
+ temp = option & EDID_OPT_YUV444;
+ break;
+ default:
+ temp = 1;
+ break;
+ }
+ if (temp == 0) {
+ hdmi_set_output_colfmt(VDO_COL_FMT_ARGB);
+ DBG_MSG("[HDMI] TV not support %s,use default RGB\n",
+ vpp_colfmt_str[colfmt]);
+ }
+ DBG_MSG("[HDMI] set option(8-HDMI,6-AUDIO) 0x%x\n", option);
+}
+
+void hdmi_config(hdmi_info_t *info)
+{
+ vout_audio_t audio_info;
+ int h_porch;
+ int delay_cfg;
+ vpp_clock_t clock;
+
+ vppif_reg32_write(HDMI_HDEN, 0);
+ vppif_reg32_write(HDMI_INFOFRAME_SELECT, 0);
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_RDY, 0);
+ hdmi_config_video(info);
+
+ govrh_get_tg(p_govrh, &clock);
+ h_porch = clock.total_pixel_of_line - clock.end_pixel_of_active; /*fp*/
+ delay_cfg = 47 - h_porch;
+ if (delay_cfg <= 0)
+ delay_cfg = 1;
+ h_porch = clock.begin_pixel_of_active; /* bp */
+ h_porch = (h_porch - (delay_cfg + 1) - 26) / 32;
+ if (h_porch <= 0)
+ h_porch = 1;
+ if (h_porch >= 8)
+ h_porch = 0;
+ hdmi_reg32_write(HDMI_CP_DELAY, delay_cfg);
+ vppif_reg32_write(HDMI_HORIZ_BLANK_MAX_PCK, h_porch);
+ DBGMSG("[HDMI] H blank max pck %d,delay %d\n", h_porch, delay_cfg);
+
+ audio_info.fmt = 16;
+ audio_info.channel = info->channel;
+ audio_info.sample_rate = info->freq;
+ hdmi_config_audio(&audio_info);
+
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_ADDR, 0);
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_LEN, 2);
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_RDY, 1);
+
+ hdmi_set_option(info->option);
+ vppif_reg32_write(HDMI_TRE_EN,
+ (g_vpp.hdmi_pixel_clock < 40000000) ? 3 : 2);
+}
+
+/*----------------------- Module API --------------------------------------*/
+void hdmi_set_cp_enable(vpp_flag_t enable)
+{
+ if (!g_vpp.hdmi_cp_enable)
+ enable = 0;
+
+ if (hdmi_cp)
+ hdmi_cp->enable(enable);
+
+#ifdef __KERNEL__
+ if (hdmi_cp && hdmi_cp->poll) {
+ vpp_irqproc_del_work(VPP_INT_GOVRH_VBIS, (void *)hdmi_cp->poll);
+ if (enable)
+ vpp_irqproc_work(VPP_INT_GOVRH_VBIS,
+ (void *)hdmi_cp->poll, 0, 0, 0);
+ }
+#endif
+}
+
+int hdmi_check_cp_int(void)
+{
+ int ret = 0;
+
+ if (hdmi_cp)
+ ret = hdmi_cp->interrupt();
+ return ret;
+}
+
+void hdmi_get_bksv(unsigned int *bksv)
+{
+ if (hdmi_cp)
+ hdmi_cp->get_bksv(bksv);
+}
+
+#ifdef __KERNEL__
+void hdmi_hotplug_notify(int plug_status)
+{
+ if (g_vpp.hdmi_disable)
+ return;
+ if (g_vpp.virtual_display || (g_vpp.dual_display == 0)) {
+ vpp_netlink_notify_plug(VPP_VOUT_ALL, 0);
+ vpp_netlink_notify_plug(VPP_VOUT_ALL, 1);
+ return;
+ }
+ vpp_netlink_notify_plug(VPP_VOUT_NUM_HDMI, plug_status);
+}
+#else
+#define hdmi_hotplug_notify
+#endif
+
+int hdmi_check_plugin(int hotplug)
+{
+ static int last_plugin = -1;
+ int plugin;
+ int flag;
+
+ if (g_vpp.hdmi_disable)
+ return 0;
+
+ plugin = hdmi_get_plugin();
+ hdmi_clear_plug_status();
+#ifdef __KERNEL__
+ /* disable HDMI before change clock */
+ if (plugin == 0) {
+ hdmi_set_enable(0);
+ hdmi_set_power_down(1);
+ }
+ vpp_set_clock_enable(DEV_HDMII2C, plugin, 1);
+ vpp_set_clock_enable(DEV_HDCE, plugin, 1);
+
+ /* slow down clock for plugout */
+ flag = (auto_pll_divisor(DEV_HDMILVDS, GET_FREQ, 0, 0)
+ == 8000000) ? 0 : 1;
+ if ((plugin != flag) && !g_vpp.virtual_display) {
+ int pixclk;
+
+ pixclk = (plugin) ? g_vpp.hdmi_pixel_clock : 8000000;
+ auto_pll_divisor(DEV_HDMILVDS, SET_PLLDIV, 0, pixclk);
+ }
+#endif
+ if (last_plugin != plugin) {
+ DPRINT("[HDMI] HDMI plug%s,hotplug %d\n", (plugin) ?
+ "in" : "out", hotplug);
+ last_plugin = plugin;
+ }
+#if 0 /* Denzel test */
+ if (plugin == 0)
+ hdmi_set_dvi_enable(VPP_FLAG_ENABLE);
+#endif
+ return plugin;
+}
+
+void hdmi_reg_dump(void)
+{
+ DPRINT("========== HDMI register dump ==========\n");
+ vpp_reg_dump(REG_HDMI_BEGIN, REG_HDMI_END - REG_HDMI_BEGIN);
+ vpp_reg_dump(REG_HDMI2_BEGIN, REG_HDMI2_END - REG_HDMI2_BEGIN);
+
+ DPRINT("---------- HDMI common ----------\n");
+ DPRINT("enable %d,hden %d,reset %d,dvi %d\n",
+ vppif_reg32_read(HDMI_ENABLE), vppif_reg32_read(HDMI_HDEN),
+ vppif_reg32_read(HDMI_RESET),
+ vppif_reg32_read(HDMI_DVI_MODE_ENABLE));
+ DPRINT("colfmt %d,conv 422 %d,hsync low %d,vsync low %d\n",
+ vppif_reg32_read(HDMI_OUTPUT_FORMAT),
+ vppif_reg32_read(HDMI_CONVERT_YUV422),
+ vppif_reg32_read(HDMI_HSYNC_LOW_ACTIVE),
+ vppif_reg32_read(HDMI_VSYNC_LOW_ACTIVE));
+ DPRINT("dbg bus sel %d,state mach %d\n",
+ vppif_reg32_read(HDMI_DBG_BUS_SELECT),
+ vppif_reg32_read(HDMI_STATE_MACHINE_STATUS));
+ DPRINT("eep reset %d,encode %d,eess %d\n",
+ vppif_reg32_read(HDMI_EEPROM_RESET),
+ vppif_reg32_read(HDMI_ENCODE_ENABLE),
+ vppif_reg32_read(HDMI_EESS_ENABLE));
+ DPRINT("verify pj %d,auth test %d,cipher %d\n",
+ vppif_reg32_read(HDMI_VERIFY_PJ_ENABLE),
+ vppif_reg32_read(HDMI_AUTH_TEST_KEY),
+ vppif_reg32_read(HDMI_CIPHER_1_1));
+ DPRINT("preamble %d\n", vppif_reg32_read(HDMI_PREAMBLE));
+
+ DPRINT("---------- HDMI hotplug ----------\n");
+ DPRINT("plug %s\n", vppif_reg32_read(HDMI_HOTPLUG_IN) ? "in" : "out");
+ DPRINT("plug in enable %d, status %d\n",
+ vppif_reg32_read(HDMI_HOTPLUG_IN_INT),
+ vppif_reg32_read(HDMI_HOTPLUG_IN_STS));
+ DPRINT("plug out enable %d, status %d\n",
+ vppif_reg32_read(HDMI_HOTPLUG_OUT_INT),
+ vppif_reg32_read(HDMI_HOTPLUG_OUT_STS));
+ DPRINT("debounce detect %d,sample %d\n",
+ vppif_reg32_read(HDMI_DEBOUNCE_DETECT),
+ vppif_reg32_read(HDMI_DEBOUNCE_SAMPLE));
+
+ DPRINT("---------- I2C ----------\n");
+ DPRINT("enable %d,exit FSM %d,key read %d\n",
+ vppif_reg32_read(HDMI_I2C_ENABLE),
+ vppif_reg32_read(HDMI_FORCE_EXIT_FSM),
+ vppif_reg32_read(HDMI_KEY_READ_WORD));
+ DPRINT("clk divid %d,rd data 0x%x,wr data 0x%x\n",
+ vppif_reg32_read(HDMI_I2C_CLK_DIVIDER),
+ vppif_reg32_read(HDMI_RD_DATA),
+ vppif_reg32_read(HDMI_WR_DATA));
+ DPRINT("start %d,stop %d,wr avail %d\n",
+ vppif_reg32_read(HDMI_SW_START_REQ),
+ vppif_reg32_read(HDMI_SW_STOP_REQ),
+ vppif_reg32_read(HDMI_WR_DATA_AVAIL));
+ DPRINT("status %d,sw read %d,sw i2c req %d\n",
+ vppif_reg32_read(HDMI_I2C_STATUS),
+ vppif_reg32_read(HDMI_SW_READ),
+ vppif_reg32_read(HDMI_SW_I2C_REQ));
+
+ DPRINT("---------- AUDIO ----------\n");
+ DPRINT("enable %d,sub pck %d,spflat %d\n",
+ vppif_reg32_read(HDMI_AUD_ENABLE),
+ vppif_reg32_read(HDMI_AUD_SUB_PACKET),
+ vppif_reg32_read(HDMI_AUD_SPFLAT));
+ DPRINT("aud pck insert reset %d,enable %d,delay %d\n",
+ vppif_reg32_read(HDMI_AUD_PCK_INSERT_RESET),
+ vppif_reg32_read(HDMI_AUD_PCK_INSERT_ENABLE),
+ vppif_reg32_read(HDMI_AUD_INSERT_DELAY));
+ DPRINT("avmute set %d,clr %d,pixel repete %d\n",
+ vppif_reg32_read(HDMI_AVMUTE_SET_ENABLE),
+ vppif_reg32_read(HDMI_AVMUTE_CLR_ENABLE),
+ vppif_reg32_read(HDMI_AUD_PIXEL_REPETITION));
+ DPRINT("acr ratio %d,acr enable %d,mute %d\n",
+ vppif_reg32_read(HDMI_AUD_ACR_RATIO),
+ vppif_reg32_read(HDMI_AUD_ACR_ENABLE),
+ vppif_reg32_read(HDMI_AUD_MUTE));
+ DPRINT("layout %d,pwr save %d,n 20bits %d\n",
+ vppif_reg32_read(HDMI_AUD_LAYOUT),
+ vppif_reg32_read(HDMI_AUD_PWR_SAVING),
+ vppif_reg32_read(HDMI_AUD_N_20BITS));
+ DPRINT("cts low 12 %d,hi 8 %d,cts sel %d\n",
+ vppif_reg32_read(HDMI_AUD_CTS_LOW_12BITS),
+ vppif_reg32_read(HDMI_AUD_CTS_HI_8BITS),
+ vppif_reg32_read(HDMI_AUD_CTS_SELECT));
+ DPRINT("aipclk rate %d\n", vppif_reg32_read(HDMI_AUD_AIPCLK_RATE));
+
+ DPRINT("---------- INFOFRAME ----------\n");
+ DPRINT("sel %d,hor blank pck %d\n",
+ vppif_reg32_read(HDMI_INFOFRAME_SELECT),
+ vppif_reg32_read(HDMI_HORIZ_BLANK_MAX_PCK));
+ DPRINT("fifo1 ready %d,addr 0x%x,len %d\n",
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO1_RDY),
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO1_ADDR),
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO1_LEN));
+ DPRINT("fifo2 ready %d,addr 0x%x,len %d\n",
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO2_RDY),
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO2_ADDR),
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO2_LEN));
+ DPRINT("wr strobe %d,rd strobe %d,fifo addr %d\n",
+ vppif_reg32_read(HDMI_INFOFRAME_WR_STROBE),
+ vppif_reg32_read(HDMI_INFOFRAME_RD_STROBE),
+ vppif_reg32_read(HDMI_INFOFRAME_FIFO_ADDR));
+
+ {
+ int i;
+ unsigned int buf[32];
+
+ for (i = 0; i <= vppif_reg32_read(HDMI_INFOFRAME_FIFO1_LEN); i++) {
+ DPRINT("----- infoframe %d -----\n", i);
+ hdmi_read_fifo(i, buf, 32);
+ vpp_reg_dump((unsigned int) buf, 32);
+ }
+ }
+
+ DPRINT("---------- HDMI test ----------\n");
+ DPRINT("ch0 enable %d, data 0x%x\n",
+ vppif_reg32_read(HDMI_CH0_TEST_MODE_ENABLE),
+ vppif_reg32_read(HDMI_CH0_TEST_DATA));
+ DPRINT("ch1 enable %d, data 0x%x\n",
+ vppif_reg32_read(HDMI_CH1_TEST_MODE_ENABLE),
+ vppif_reg32_read(HDMI_CH1_TEST_DATA));
+ DPRINT("ch2 enable %d, data 0x%x\n",
+ vppif_reg32_read(HDMI_CH2_TEST_MODE_ENABLE),
+ vppif_reg32_read(HDMI_CH2_TEST_DATA));
+
+ if (hdmi_cp)
+ hdmi_cp->dump();
+}
+
+#ifdef CONFIG_PM
+static unsigned int *hdmi_pm_bk;
+static unsigned int *hdmi_pm_bk2;
+static unsigned int hdmi_pm_enable;
+static unsigned int hdmi_pm_enable2;
+static int hdmi_plug_enable = 0xFF;
+extern struct switch_dev vpp_sdev;
+static int hdmi_resume_plug_cnt;
+#define HDMI_RESUME_PLUG_MS 50
+#define HDMI_RESUME_PLUG_CNT 20
+static void hdmi_do_resume_plug(struct work_struct *ptr)
+{
+ vout_t *vo;
+ int plugin;
+ struct delayed_work *dwork = to_delayed_work(ptr);
+
+ plugin = hdmi_check_plugin(0);
+ vo = vout_get_entry(VPP_VOUT_NUM_HDMI);
+ vout_change_status(vo, VPP_VOUT_STS_PLUGIN, plugin);
+ if (plugin)
+ hdmi_hotplug_notify(1);
+ hdmi_resume_plug_cnt --;
+ if (hdmi_resume_plug_cnt && (vpp_sdev.state == 0))
+ schedule_delayed_work(dwork,
+ msecs_to_jiffies(HDMI_RESUME_PLUG_MS));
+}
+
+DECLARE_DELAYED_WORK(hdmi_resume_work, hdmi_do_resume_plug);
+
+void hdmi_suspend(int sts)
+{
+ vo_hdmi_set_clock(1);
+ switch (sts) {
+ case 0: /* disable module */
+ cancel_delayed_work_sync(&hdmi_resume_work);
+ hdmi_pm_enable = vppif_reg32_read(HDMI_ENABLE);
+ hdmi_reg32_write(HDMI_ENABLE, 0);
+ hdmi_pm_enable2 = vppif_reg32_read(HDMI_HDEN);
+ vppif_reg32_write(HDMI_HDEN, 0);
+ if (hdmi_plug_enable == 0xFF)
+ hdmi_plug_enable =
+ vppif_reg32_read(HDMI_HOTPLUG_OUT_INT);
+ hdmi_enable_plugin(0);
+ break;
+ case 1: /* disable tg */
+ break;
+ case 2: /* backup register */
+ hdmi_pm_bk = vpp_backup_reg(REG_HDMI_BEGIN,
+ (REG_HDMI_END - REG_HDMI_BEGIN));
+ hdmi_pm_bk2 = vpp_backup_reg(REG_HDMI2_BEGIN,
+ (REG_HDMI2_END - REG_HDMI2_BEGIN));
+ switch_set_state(&vpp_sdev, 0);
+ hdmi_resume_plug_cnt = 20;
+ break;
+ default:
+ break;
+ }
+ vo_hdmi_set_clock(0);
+}
+
+void hdmi_resume(int sts)
+{
+ vo_hdmi_set_clock(1);
+ switch (sts) {
+ case 0: /* restore register */
+ vpp_restore_reg(REG_HDMI_BEGIN,
+ (REG_HDMI_END - REG_HDMI_BEGIN), hdmi_pm_bk);
+ vpp_restore_reg(REG_HDMI2_BEGIN,
+ (REG_HDMI2_END - REG_HDMI2_BEGIN), hdmi_pm_bk2);
+ hdmi_pm_bk = 0;
+ hdmi_pm_bk2 = 0;
+ hdmi_config(&hdmi_info); /* re-config HDMI info frame */
+ if (g_vpp.hdmi_cp_p && hdmi_cp)
+ hdmi_cp->init();
+ break;
+ case 1: /* enable module */
+ hdmi_reg32_write(HDMI_ENABLE, hdmi_pm_enable);
+ vppif_reg32_write(HDMI_HDEN, hdmi_pm_enable2);
+ break;
+ case 2: /* enable tg */
+ hdmi_check_plugin(0);
+ hdmi_clear_plug_status();
+ hdmi_enable_plugin(hdmi_plug_enable);
+ hdmi_plug_enable = 0xFF;
+ if (vpp_sdev.state == 0) {
+ hdmi_resume_plug_cnt = HDMI_RESUME_PLUG_CNT;
+ schedule_delayed_work(&hdmi_resume_work,
+ msecs_to_jiffies(HDMI_RESUME_PLUG_MS));
+ }
+ break;
+ default:
+ break;
+ }
+ vo_hdmi_set_clock(0);
+}
+#else
+#define hdmi_suspend NULL
+#define hdmi_resume NULL
+#endif
+
+void hdmi_init(void)
+{
+ struct fb_videomode vmode;
+
+ g_vpp.hdmi_pixel_clock = vpp_get_base_clock(VPP_MOD_GOVRH);
+ g_vpp.hdmi_i2c_freq = HDMI_I2C_FREQ;
+ g_vpp.hdmi_i2c_udelay = 0;
+ g_vpp.hdmi_ctrl = 0x1000000;
+ g_vpp.hdmi_audio_pb4 = 0x0;
+ g_vpp.hdmi_audio_pb1 = 0x0;
+
+ hdmi_info.outfmt = hdmi_get_output_colfmt();
+ govrh_get_videomode(p_govrh, &vmode);
+ hdmi_info.vic = hdmi_get_vic(vmode.xres, vmode.yres, vmode.refresh,
+ (vmode.vmode & FB_VMODE_INTERLACED) ? 1 : 0);
+ hdmi_info.channel = 2;
+ hdmi_info.freq = 48000;
+ hdmi_info.option = EDID_OPT_AUDIO + EDID_OPT_HDMI;
+
+ if (g_vpp.govrh_preinit) {
+ DBGMSG("[HDMI] hdmi_init for uboot logo\n");
+ } else {
+ /* bit8-HDMI SDA,bit9-HDMI SCL,bit10-Hotplug,bit26-CEC */
+ /* GPIO disable GPIO function */
+ vppif_reg32_write(GPIO_BASE_ADDR+0x54, 0x4000700, 0, 0);
+ /* GPIO4 disable GPIO out */
+ vppif_reg32_write(GPIO_BASE_ADDR+0x494, 0x4000700, 0, 0);
+#if 0
+ /* Suspend GPIO output enable */
+ vppif_reg32_write(GPIO_BASE_ADDR+0x80, BIT23, 23, 1);
+ /* Suspend GPIO output high */
+ vppif_reg32_write(GPIO_BASE_ADDR+0xC0, BIT23, 23, 1);
+ /* Wake3 disable pull ctrl */
+ vppif_reg32_write(GPIO_BASE_ADDR+0x480, BIT19, 19, 0);
+#endif
+ vppif_reg32_write(HDMI_REG_LEVEL, 1);
+ vppif_reg32_write(HDMI_REG_UPDATE, 1);
+ vppif_reg32_write(HDMI_LDI_SHIFT_LEFT, 1);
+ vppif_reg32_out(REG_HDMI_STATUS, 0x0008c000);
+ vppif_reg32_out(REG_HDMI_TEST, 0x00450409);
+ vppif_reg32_out(REG_HDMI_TEST2, 0x00005022);
+ vppif_reg32_out(REG_HDMI_TEST3,
+ (g_vpp.hdmi_sp_mode) ? 0x00010100 : 0x00000100);
+ hdmi_set_enable(VPP_FLAG_DISABLE);
+ hdmi_set_dvi_enable(VPP_FLAG_DISABLE);
+ vppif_reg32_write(HDMI_CIPHER_1_1, 0);
+
+ vppif_reg32_write(HDMI_INFOFRAME_SRAM_ENABLE, 1);
+ vppif_reg32_write(HDMI_INFOFRAME_SELECT, 0);
+ vppif_reg32_write(HDMI_INFOFRAME_FIFO1_RDY, 0);
+
+ vppif_reg32_out(HDMI_BASE_ADDR+0x3ec, 0x0);
+ vppif_reg32_out(HDMI_BASE_ADDR+0x3e8, 0x1);
+
+ /* vppif_reg32_write(HDMI_AUD_LAYOUT, 1); */
+ hdmi_DDC_reset();
+ hdmi_DDC_set_freq(g_vpp.hdmi_i2c_freq);
+ vppif_reg32_write(HDMI_I2C_ENABLE, 1);
+ }
+ g_vpp.hdmi_init = 1;
+ if (hdmi_cp)
+ hdmi_cp->init();
+
+ acr_init();
+}
+#endif /* WMT_FTBLK_HDMI */