MLK-19583-1: hdmi rx: Add HPD support
authorSandor Yu <Sandor.yu@nxp.com>
Fri, 14 Sep 2018 05:26:15 +0000 (13:26 +0800)
committerSandor Yu <Sandor.yu@nxp.com>
Fri, 19 Apr 2019 02:40:50 +0000 (10:40 +0800)
Add HPD support.
Add s_param function that may needed by user application.
Add mutex protect for hdp register access.

Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
drivers/media/platform/imx8/hdmi/mxc-hdmi-hw.c
drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c
drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h

index d8ebd01..a6f582c 100644 (file)
@@ -67,93 +67,111 @@ S_HDMI_SCDC_SET_MSG scdcExampleData = {
             20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34}
 };
 
-u8 hdmi_infoframe_poll(state_struct *state)
+int hdmi_infoframe_poll(state_struct *state)
 {
        GENERAL_Read_Register_response regresp;
-       u32 regread;
        struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+       u32 regread;
+       u32 i;
 
        /* Unmask "AVI InfoFrame received" interrupt */
        CDN_API_General_Write_Register_blocking(state,
                                                ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_MASK << 2),
                                                F_TMDS_MHL_HD_MASK(0xFD));
-       dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt unmasked.\n");
 
        /* Unmask "tmds_mhl_hd_int" interrupt */
        CDN_API_General_Write_Register_blocking(state,
                                                ADDR_SINK_MHL_HD + (MHL_HD_INT_MASK << 2),
                                                F_MHL_HD_INT_MASK(0xE));
-       dev_dbg(&hdmi_rx->pdev->dev, "tmds_mhl_hd_int interrupt unmasked.\n");
 
        /* MHL_HD_INT_STAT */
-       dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...\n");
-       do {
-               CDN_API_General_Read_Register_blocking(state,
-                                                      ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2),
-                                                      &regresp);
-       } while (!(regresp.val & (1 << 0)));
-       dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...DONE\n");
+       CDN_API_General_Read_Register_blocking(state,
+                                      ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2), &regresp);
+       for (i = 0; i < 5; i++) {
+               if (regresp.val & (1 << 0))
+                       break;
+               else
+                       CDN_API_General_Read_Register_blocking(state,
+                             ADDR_SINK_MHL_HD + (MHL_HD_INT_STAT << 2), &regresp);
+               msleep(20);
+       }
+       if (i == 5) {
+               dev_err(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...failed!\n");
+               return -1;
+       } else
+               dev_dbg(&hdmi_rx->pdev->dev, "Waiting for tmds_mhl_hd_int...DONE\n");
 
        /* Mask "AVI InfoFrame received" interrupt */
        CDN_API_General_Write_Register_blocking(state,
                                                ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_MASK << 2),
                                                F_TMDS_MHL_HD_MASK(0xFF));
-       dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt masked.\n");
 
        /* Mask "tmds_mhl_hd_int" interrupt */
        CDN_API_General_Write_Register_blocking(state,
                                                ADDR_SINK_MHL_HD + (MHL_HD_INT_MASK << 2),
                                                F_MHL_HD_INT_MASK(0xF));
-       dev_dbg(&hdmi_rx->pdev->dev, "tmds_mhl_hd_int interrupt masked.\n");
 
        /* Read back TMDS_MHL_HD_INT_STAT to clear the interrupt */
        CDN_API_General_Read_Register_blocking(state,
                                               ADDR_SINK_MHL_HD + (TMDS_MHL_HD_INT_STAT << 2),
                                               &regresp);
-       dev_dbg(&hdmi_rx->pdev->dev, "AVI InfoFrame packet received - interrupt cleared.\n");
 
        /* Packet 0 read request */
        cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2),
                      F_PACKET_RDN_WR(0x0) | F_PACKET_NUM(0x0));
-       dev_dbg(&hdmi_rx->pdev->dev, "PIF Packet 0 read request.\n");
 
        /* Wait for pkt_host_rdy_status */
-       dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...\n");
-       do {
-               cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
-       } while (!(regread & (1 << 16)));
-       dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...DONE\n");
+       cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
+       for (i = 0; i < 5; i++) {
+               if (regread & (1 << 16))
+                       break;
+               else
+                       cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), &regread);
+               msleep(20);
+       }
+       if (i == 5) {
+               dev_err(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...FAILED!\n");
+               return -1;
+       } else
+               dev_dbg(&hdmi_rx->pdev->dev, "Waiting for packet data available for reading...DONE\n");
 
        return 0;
 }
 
 /* Exemplary code to configure the controller */
 /* for the AVI_InfoFrame reception */
-static void get_avi_infoframe(state_struct *state)
+static int get_avi_infoframe(state_struct *state)
 {
        struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
        GENERAL_Read_Register_response regresp;
+       int ret;
 
        dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
        /* Get VIC code and pixel encoding */
-       hdmi_infoframe_poll(state);
+       ret = hdmi_infoframe_poll(state);
+       if (ret < 0)
+               return -1;
 
        CDN_API_General_Read_Register_blocking(state, ADDR_SINK_MHL_HD + (PKT_AVI_DATA_LOW << 2), &regresp);
        hdmi_rx->vic_code = (regresp.val & 0xFF000000) >> 24;
        hdmi_rx->pixel_encoding = (regresp.val & 0x00000060) >> 5;
+       return 0;
 }
 
-static void get_vendor_infoframe(state_struct *state)
+static int get_vendor_infoframe(state_struct *state)
 {
        struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
        u32 regread;
+       int ret;
 
        dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
        /* Set info_type1 to vendor Info Frame */
        cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2),
                      F_INFO_TYPE1(0x81));
 
-       hdmi_infoframe_poll(state);
+       ret = hdmi_infoframe_poll(state);
+       if (ret < 0)
+               return -1;
 
        /* Get IEEE OUI */
        cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), &regread);
@@ -177,6 +195,8 @@ static void get_vendor_infoframe(state_struct *state)
        /* Clear info_type1 */
        cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2),
                      F_INFO_TYPE1(0x00));
+
+       return 0;
 }
 
 static void get_color_depth(struct mxc_hdmi_rx_dev *hdmi_rx,
@@ -246,9 +266,32 @@ static void get_color_depth(struct mxc_hdmi_rx_dev *hdmi_rx,
        }
 }
 
-int hdmi_rx_init(state_struct *state)
+/* Set edid data sample */
+void hdmirx_edid_set(state_struct *state)
 {
+       struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+
+       /* Set EDID - block 0 */
+       CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 0, &block0[0]);
+       /* Set EDID - block 1 */
+       CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 1, &block1[0]);
+       dev_dbg(&hdmi_rx->pdev->dev, "EDID block 0/1 set complete.\n");
+}
+
+/* Set SCDC data sample */
+void hdmirx_scdc_set(state_struct *state)
+{
+       struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+
+       CDN_API_HDMIRX_SET_SCDC_SLAVE_blocking(state, &scdcExampleData);
+       dev_dbg(&hdmi_rx->pdev->dev, "SCDC set complete.\n");
+}
+
+int hdmirx_init(state_struct *state)
+{
+       u8 sts;
        u32 ret = 0;
+
        /* Set uCPU Clock frequency for FW's use [MHz]; */
        CDN_API_SetClock(state, 200);
 
@@ -257,23 +300,63 @@ int hdmi_rx_init(state_struct *state)
 
        /* Check if the firmware is running */
        ret = CDN_API_CheckAlive_blocking(state);
+       if (ret < 0)
+               return ret;
+
+       /* Set driver and firmware active */
+       CDN_API_MainControl_blocking(state, 1, &sts);
+       /* set sample edid and scdc */
+       hdmirx_edid_set(state);
+       hdmirx_scdc_set(state);
+
        return ret;
 }
 
+int hdmirx_check5v(state_struct *state)
+{
+       struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
+       u8 event5V = 0;
+       u32 i;
+
+       /* check for 5v to get hdmi cable state */
+       CDN_API_HDMIRX_ReadEvent(state, &event5V);
+       for (i = 0; i < 5; i++) {
+               if (event5V & (1 << HDMI_RX_EVENT_5V_VAL)) {
+                       dev_dbg(&hdmi_rx->pdev->dev, "HDMI 5V present\n");
+                       break;
+               }
+               msleep(20);
+               CDN_API_HDMIRX_ReadEvent(state, &event5V);
+       }
+       if (i == 5) {
+               dev_dbg(&hdmi_rx->pdev->dev, "No HDMI 5V present!!!\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+void hdmirx_hotplug_trigger(state_struct *state)
+{
+       /* Clear HPD */
+       CDN_API_HDMIRX_SetHpd_blocking(state, 0);
+       /* provide minimum low pulse length (100ms) */
+       msleep(110);
+       CDN_API_HDMIRX_SetHpd_blocking(state, 1);
+}
+
 /* Bring-up sequence for the HDMI-RX */
 int hdmirx_startup(state_struct *state)
 {
        u8 sts;
        u32 rx_clk_freq;
-       u8 event5V = 0;
        u8 data_rate_change = 0;
        u8 scrambling_en;
        clk_ratio_t clk_ratio, clk_ratio_detected;
        tmds_bit_clock_ratio_t tmds_bit_clock_ratio;
        struct mxc_hdmi_rx_dev *hdmi_rx = state_to_mxc_hdmirx(state);
        S_HDMI_SCDC_GET_MSG *scdcData = &hdmi_rx->scdcData;
-       u8 ret = 0;
-       u32 i;
+       int ret = 0;
 
        /* Start from TMDS/pixel clock ratio of 1:1.
         * It affects only pixel clock frequency as the character/data clocks are generated based on
@@ -282,55 +365,16 @@ int hdmirx_startup(state_struct *state)
         * of the pixel clock ratio being programmed. */
        clk_ratio = CLK_RATIO_1_1;
 
-       /* Set driver and firmware active */
-       CDN_API_MainControl_blocking(state, 1, &sts);
-
-       /* Set EDID - block 0 */
-       CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 0, &block0[0]);
-
-       /* Set EDID - block 1 */
-       CDN_API_HDMIRX_SET_EDID_blocking(state, 0, 1, &block1[0]);
-       dev_dbg(&hdmi_rx->pdev->dev, "EDID block 0/1 set complete.\n");
-
-       /* Set SCDC data sample */
-       CDN_API_HDMIRX_SET_SCDC_SLAVE_blocking(state, &scdcExampleData);
-       dev_dbg(&hdmi_rx->pdev->dev, "SCDC set complete.\n");
-
        /* Get TMDS_Bit_Clock_Ratio and Scrambling setting */
        CDN_API_HDMIRX_GET_SCDC_SLAVE_blocking(state, scdcData);
        tmds_bit_clock_ratio =
            ((scdcData->TMDS_Config & (1 << 1)) >> 1) ?
-               TMDS_BIT_CLOCK_RATIO_1_40 : TMDS_BIT_CLOCK_RATIO_1_10;
+                               TMDS_BIT_CLOCK_RATIO_1_40 : TMDS_BIT_CLOCK_RATIO_1_10;
        scrambling_en = scdcData->TMDS_Config & (1 << 0);
+
        dev_dbg(&hdmi_rx->pdev->dev,
                        "TMDS ratio: 1/%0d, Scrambling %0d).\n", tmds_bit_clock_ratio, scrambling_en);
 
-       /* Clear HPD */
-       CDN_API_HDMIRX_SetHpd_blocking(state, 0);
-       dev_dbg(&hdmi_rx->pdev->dev, "Clear HDP\n");
-
-       /* check for 5v to get hdmi cable state */
-       CDN_API_HDMIRX_ReadEvent(state, &event5V);
-       dev_dbg(&hdmi_rx->pdev->dev, "event5V = 0x%02X\n", event5V);
-       for (i = 0; i < 5; i++) {
-               if (event5V & (1 << HDMI_RX_EVENT_5V_VAL)) {
-                       dev_info(&hdmi_rx->pdev->dev, "HDMI 5V present\n");
-                       break;
-               }
-               msleep(20);
-               CDN_API_HDMIRX_ReadEvent(state, &event5V);
-               dev_dbg(&hdmi_rx->pdev->dev, "event5V = 0x%02X\n", event5V);
-       }
-       if (i == 5) {
-               dev_info(&hdmi_rx->pdev->dev, "No HDMI 5V present!!!\n");
-               return -1;
-       }
-
-       /* Got 5v, set hpd */
-       msleep(100);    /* provide minimum low pulse length (100ms) */
-       CDN_API_HDMIRX_SetHpd_blocking(state, 1);
-       dev_dbg(&hdmi_rx->pdev->dev, "Set HDP\n");
-
        /* Configure the PHY */
        pma_config(state);
 
@@ -388,13 +432,23 @@ int hdmirx_startup(state_struct *state)
         * 5. If mismatch found - reprogram the PHY
         * 6. Enable the video data path in the controller */
 
-       get_avi_infoframe(state);
-       get_vendor_infoframe(state);
+       ret = get_avi_infoframe(state);
+       if (ret < 0) {
+               pr_err("Get AVI info frame failed\n");
+               return -1;
+       }
+       ret = get_vendor_infoframe(state);
+       if (ret < 0)
+               pr_info("No Vendor info frame\n");
 
        dev_dbg(&hdmi_rx->pdev->dev,
                        "get_avi_infoframe() vic_code: %0d, pixel_encoding: %0d.\n",
                        hdmi_rx->vic_code, hdmi_rx->pixel_encoding);
-       mxc_hdmi_frame_timing(hdmi_rx);
+       ret = mxc_hdmi_frame_timing(hdmi_rx);
+       if (ret < 0) {
+               pr_err("Get frame timing failed\n\n");
+               return -1;
+       }
 
        clk_ratio_detected = clk_ratio_detect(state, rx_clk_freq,
                                              hdmi_rx->timings->timings.bt.
@@ -438,3 +492,24 @@ int hdmirx_startup(state_struct *state)
                                F_SINK_CEC_SYS_CLK_RSTN_EN(1));
        return 0;
 }
+
+/* Stop for the HDMI-RX */
+void hdmirx_stop(state_struct *state)
+{
+       CDN_API_HDMIRX_Stop_blocking(state);
+}
+
+void hdmirx_phy_pix_engine_reset(state_struct *state)
+{
+       GENERAL_Read_Register_response regresp;
+
+       CDN_API_General_Read_Register_blocking(state, ADDR_SINK_CAR +
+                                              (SINK_MHL_HD_CAR << 2),
+                                              &regresp);
+       CDN_API_General_Write_Register_blocking(state, ADDR_SINK_CAR +
+                                               (SINK_MHL_HD_CAR << 2),
+                                               regresp.val & 0x3F);
+       CDN_API_General_Write_Register_blocking(state, ADDR_SINK_CAR +
+                                               (SINK_MHL_HD_CAR << 2),
+                                               regresp.val);
+}
index 92acd0e..cb2e2de 100644 (file)
@@ -11,7 +11,7 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
+#include <linux/irq.h>
 #include "mxc-hdmi-rx.h"
 #include "API_AFE_ss28fdsoi_hdmirx.h"
 
@@ -243,7 +243,7 @@ static void mxc_hdmi_clock_disable(struct mxc_hdmi_rx_dev *hdmi_rx)
        clk_disable_unprepare(hdmi_rx->pxl_link_clk);
 }
 
-static void imx8qm_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx)
+static void mxc_hdmi_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx)
 {
        u32 val;
 
@@ -276,6 +276,15 @@ static void imx8qm_pixel_link_encoder(struct mxc_hdmi_rx_dev *hdmi_rx)
 /* -----------------------------------------------------------------------------
  * v4l2_subdev_video_ops
  */
+static int mxc_hdmi_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
+{
+       struct mxc_hdmi_rx_dev *hdmi_rx = imx_sd_to_hdmi(sd);
+       struct device *dev = &hdmi_rx->pdev->dev;
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       return 0;
+}
 
 static int mxc_hdmi_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *a)
 {
@@ -318,7 +327,7 @@ static int mxc_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
        u32 val;
 
        dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
-       imx8qm_pixel_link_encoder(hdmi_rx);
+       mxc_hdmi_pixel_link_encoder(hdmi_rx);
 
        if (enable) {
                val = readl(hdmi_rx->mem.ss_base);
@@ -346,6 +355,7 @@ static int mxc_hdmi_s_stream(struct v4l2_subdev *sd, int enable)
 static const struct v4l2_subdev_video_ops imx_video_ops_hdmi = {
        .s_stream = mxc_hdmi_s_stream,
        .g_parm =       mxc_hdmi_g_parm,
+       .s_parm =       mxc_hdmi_s_parm,
 };
 
 /* -----------------------------------------------------------------------------
@@ -578,24 +588,32 @@ void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset)
 static int imx8qm_hdp_read(struct hdp_mem *mem, u32 addr, u32 *value)
 {
        u32 temp;
-       void *tmp_addr = (addr & 0xfff) + mem->regs_base;
-       void *off_addr = 0x4 + mem->ss_base;;
+       void *tmp_addr;
+       void *off_addr;
 
+       mutex_lock(&mem->mutex);
+       tmp_addr = (addr & 0xfff) + mem->regs_base;
+       off_addr = 0x4 + mem->ss_base;
        __raw_writel(addr >> 12, off_addr);
        temp = __raw_readl((volatile u32 *)tmp_addr);
 
        *value = temp;
+       mutex_unlock(&mem->mutex);
        return 0;
 }
 
 static int imx8qm_hdp_write(struct hdp_mem *mem, u32 addr, u32 value)
 {
-       void *tmp_addr = (addr & 0xfff) + mem->regs_base;
-       void *off_addr = 0x4 + mem->ss_base;;
+       void *tmp_addr;
+       void *off_addr;
 
+       mutex_lock(&mem->mutex);
+       tmp_addr = (addr & 0xfff) + mem->regs_base;
+       off_addr = 0x4 + mem->ss_base;;
        __raw_writel(addr >> 12, off_addr);
 
        __raw_writel(value, (volatile u32 *) tmp_addr);
+       mutex_unlock(&mem->mutex);
 
        return 0;
 }
@@ -603,23 +621,31 @@ static int imx8qm_hdp_write(struct hdp_mem *mem, u32 addr, u32 value)
 static int imx8qm_hdp_sread(struct hdp_mem *mem, u32 addr, u32 *value)
 {
        u32 temp;
-       void *tmp_addr = (addr & 0xfff) + mem->regs_base;
-       void *off_addr = 0xc + mem->ss_base;;
+       void *tmp_addr;
+       void *off_addr;
 
+       mutex_lock(&mem->mutex);
+       tmp_addr = (addr & 0xfff) + mem->regs_base;
+       off_addr = 0xc + mem->ss_base;
        __raw_writel(addr >> 12, off_addr);
 
        temp = __raw_readl((volatile u32 *)tmp_addr);
        *value = temp;
+       mutex_unlock(&mem->mutex);
        return 0;
 }
 
 static int imx8qm_hdp_swrite(struct hdp_mem *mem, u32 addr, u32 value)
 {
-       void *tmp_addr = (addr & 0xfff) + mem->regs_base;
-       void *off_addr = 0xc + mem->ss_base;
+       void *tmp_addr;
+       void *off_addr;
 
+       mutex_lock(&mem->mutex);
+       tmp_addr = (addr & 0xfff) + mem->regs_base;
+       off_addr = 0xc + mem->ss_base;
        __raw_writel(addr >> 12, off_addr);
        __raw_writel(value, (volatile u32 *)tmp_addr);
+       mutex_unlock(&mem->mutex);
 
        return 0;
 }
@@ -662,7 +688,6 @@ static void mxc_hdmi_cec_init(struct mxc_hdmi_rx_dev *hdmi_rx)
 int mxc_hdmi_init(struct mxc_hdmi_rx_dev *hdmi_rx)
 {
        sc_err_t sciErr;
-       u32 ret = 0;
 
        dev_dbg(&hdmi_rx->pdev->dev, "%s\n", __func__);
        mxc_hdmi_state_init(hdmi_rx);
@@ -679,9 +704,59 @@ int mxc_hdmi_init(struct mxc_hdmi_rx_dev *hdmi_rx)
                return -EINVAL;
        }
 
-       ret = hdmi_rx_init(&hdmi_rx->state);
 
-       return ret;
+       return 0;
+}
+
+static void hpd5v_work_func(struct work_struct *work)
+{
+       struct mxc_hdmi_rx_dev *hdmi_rx = container_of(work, struct mxc_hdmi_rx_dev,
+                                                               hpd5v_work.work);
+       char event_string[32];
+       char *envp[] = { event_string, NULL };
+       int hpd5v;
+
+       /* Check cable states before enable irq */
+       hpd5v = hdmirx_check5v(&hdmi_rx->state);
+       if (hpd5v == 0) {
+               pr_info("HDMI RX Cable Plug In\n");
+               hdmirx_hotplug_trigger(&hdmi_rx->state);
+               hdmirx_startup(&hdmi_rx->state);
+               enable_irq(hdmi_rx->irq[HPD5V_IRQ_OUT]);
+               sprintf(event_string, "EVENT=hdmirxin");
+               kobject_uevent_env(&hdmi_rx->pdev->dev.kobj, KOBJ_CHANGE, envp);
+#ifdef CONFIG_IMX_HDP_CEC
+               if (hdmi_rx->is_cec) {
+                       mxc_hdmi_cec_init(hdmi_rx);
+                       imx_cec_register(&hdmi_rx->cec);
+               }
+#endif
+       } else {
+               pr_info("HDMI RX Cable Plug Out\n");
+               hdmirx_stop(&hdmi_rx->state);
+#ifdef CONFIG_IMX_HDP_CEC
+               if (hdmi_rx->is_cec) {
+                       imx_cec_unregister(&hdmi_rx->cec);
+               }
+#endif
+               hdmirx_phy_pix_engine_reset(&hdmi_rx->state);
+               sprintf(event_string, "EVENT=hdmirxout");
+               kobject_uevent_env(&hdmi_rx->pdev->dev.kobj, KOBJ_CHANGE, envp);
+               enable_irq(hdmi_rx->irq[HPD5V_IRQ_IN]);
+       }
+}
+
+#define HOTPLUG_DEBOUNCE_MS            200
+static irqreturn_t mxc_hdp5v_irq_thread(int irq, void *data)
+{
+       struct mxc_hdmi_rx_dev *hdmi_rx =  data;
+
+       disable_irq_nosync(irq);
+
+       mod_delayed_work(system_wq, &hdmi_rx->hpd5v_work,
+                       msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+
+       return IRQ_HANDLED;
 }
 
 static int mxc_hdmi_probe(struct platform_device *pdev)
@@ -690,6 +765,7 @@ static int mxc_hdmi_probe(struct platform_device *pdev)
        struct mxc_hdmi_rx_dev *hdmi_rx;
        struct resource *res;
        int ret = 0;
+       int hpd5v;
 
        dev_dbg(dev, "%s\n", __func__);
        hdmi_rx = devm_kzalloc(dev, sizeof(*hdmi_rx), GFP_KERNEL);
@@ -698,8 +774,7 @@ static int mxc_hdmi_probe(struct platform_device *pdev)
 
        hdmi_rx->pdev = pdev;
 
-       spin_lock_init(&hdmi_rx->slock);
-
+       mutex_init(&hdmi_rx->mem.mutex);
        /* register map */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        hdmi_rx->mem.regs_base = devm_ioremap_resource(dev, res);
@@ -715,21 +790,16 @@ static int mxc_hdmi_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-#if 0
-       /* TODO B0 */
-       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (res == NULL) {
-               dev_warn(dev, "Failed to get IRQ resource\n");
-               return -EINVAL;
-       }
+       hdmi_rx->irq[HPD5V_IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
+       if (hdmi_rx->irq[HPD5V_IRQ_IN] < 0)
+               dev_info(&pdev->dev, "No plug_in irq number\n");
+
+       hdmi_rx->irq[HPD5V_IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
+       if (hdmi_rx->irq[HPD5V_IRQ_OUT] < 0)
+               dev_info(&pdev->dev, "No plug_out irq number\n");
+
+       INIT_DELAYED_WORK(&hdmi_rx->hpd5v_work, hpd5v_work_func);
 
-       ret = devm_request_irq(dev, res->start, mxc_hdmi_irq_handler,
-                              0, dev_name(dev), mxc_hdmi);
-       if (ret < 0) {
-               dev_err(dev, "failed to install irq (%d)\n", ret);
-               return -EINVAL;
-       }
-#endif
 
        v4l2_subdev_init(&hdmi_rx->sd, &imx_ops_hdmi);
        /* sd.dev may use by match_of */
@@ -775,25 +845,56 @@ static int mxc_hdmi_probe(struct platform_device *pdev)
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
        ret = mxc_hdmi_init(hdmi_rx);
-       if (ret) {
-               dev_info(dev, "mxc hdmi rx init failed\n");
+       if (ret < 0) {
+               dev_err(dev, "mxc hdmi init failed\n");
                goto failed;
        }
-       ret = hdmirx_startup(&hdmi_rx->state);
-       if (ret) {
-               dev_info(dev, "mxc hdmi rx startup failed\n");
+       ret = hdmirx_init(&hdmi_rx->state);
+       if (ret < 0) {
+               dev_err(dev, "mxc hdmi rx init failed\n");
                goto failed;
        }
-#ifdef CONFIG_IMX_HDP_CEC
-       if (hdmi_rx->is_cec) {
-               mxc_hdmi_cec_init(hdmi_rx);
-               imx_cec_register(&hdmi_rx->cec);
+
+       /* Check cable states before enable irq */
+       hpd5v = hdmirx_check5v(&hdmi_rx->state);
+
+       /* Enable Hotplug Detect IRQ thread */
+       if (hdmi_rx->irq[HPD5V_IRQ_IN] > 0) {
+               irq_set_status_flags(hdmi_rx->irq[HPD5V_IRQ_IN], IRQ_NOAUTOEN);
+               ret = devm_request_threaded_irq(dev, hdmi_rx->irq[HPD5V_IRQ_IN],
+                                               NULL, mxc_hdp5v_irq_thread,
+                                               IRQF_ONESHOT, dev_name(dev), hdmi_rx);
+               if (ret) {
+                       dev_err(&pdev->dev, "can't claim irq %d\n",
+                                                       hdmi_rx->irq[HPD5V_IRQ_IN]);
+                       goto failed;
+               }
+               /* Cable Disconnedted, enable Plug in IRQ */
+               if (hpd5v < 0)
+                       enable_irq(hdmi_rx->irq[HPD5V_IRQ_IN]);
        }
-#endif
+       if (hdmi_rx->irq[HPD5V_IRQ_OUT] > 0) {
+               irq_set_status_flags(hdmi_rx->irq[HPD5V_IRQ_OUT], IRQ_NOAUTOEN);
+               ret = devm_request_threaded_irq(dev, hdmi_rx->irq[HPD5V_IRQ_OUT],
+                                               NULL, mxc_hdp5v_irq_thread,
+                                               IRQF_ONESHOT, dev_name(dev), hdmi_rx);
+               if (ret) {
+                       dev_err(&pdev->dev, "can't claim irq %d\n",
+                                                       hdmi_rx->irq[HPD5V_IRQ_OUT]);
+                       goto failed;
+               }
+               if (hpd5v == 0) {
+                       hdmirx_hotplug_trigger(&hdmi_rx->state);
+                       hdmirx_startup(&hdmi_rx->state);
+                       /* Cable Connected, enable Plug out IRQ */
+                       enable_irq(hdmi_rx->irq[HPD5V_IRQ_OUT]);
+               }
+       }
+
 
        mxc_hdmi_rx_register_audio_driver(dev);
 
-       dev_info(dev, "mxc hdmi rx probe successfully\n");
+       dev_info(dev, "iMX8 HDMI RX probe successfully\n");
 
        return ret;
 failed:
index 5c892d1..4ba9ba5 100644 (file)
@@ -76,8 +76,13 @@ struct mxc_hdmi_rx_dev_video_standards {
        u8 fps;
 };
 
+enum hdp_rx_irq {
+       HPD5V_IRQ_IN,
+       HPD5V_IRQ_OUT,
+       HPD5V_IRQ_NUM,
+};
+
 struct mxc_hdmi_rx_dev {
-       spinlock_t                              slock;
        struct mutex                    lock;
        wait_queue_head_t               irq_queue;
        struct media_pad pads[MXC_HDMI_RX_PADS_NUM];
@@ -122,6 +127,9 @@ struct mxc_hdmi_rx_dev {
        u32 sample_rate;
        u32 sample_width;
        u32 channels;
+
+       int irq[HPD5V_IRQ_NUM];
+       struct delayed_work hpd5v_work;
 };
 
 enum mxc_hdmi_rx_power_state {
@@ -130,10 +138,14 @@ enum mxc_hdmi_rx_power_state {
        MXC_HDMI_RX_RUNTIME_SUSPEND = 0x04,
 };
 
+void hdmirx_stop(state_struct *state);
+void hdmirx_hotplug_trigger(state_struct *state);
+void hdmirx_phy_pix_engine_reset(state_struct *state);
 int hdmirx_startup(state_struct *state);
-void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset);
-int hdmi_rx_init(state_struct *state);
+int hdmirx_init(state_struct *state);
+int hdmirx_check5v(state_struct *state);
 int mxc_hdmi_frame_timing(struct mxc_hdmi_rx_dev *hdmi_rx);
 void mxc_hdmi_rx_register_audio_driver(struct device *dev);
+void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset);
 
 #endif