MLK-16946-2: hdmi: Enable cable hotplug detect function
authorSandor Yu <Sandor.yu@nxp.com>
Thu, 23 Nov 2017 06:39:53 +0000 (14:39 +0800)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
-Enable HDMI/DP cable hotplug detect function.
-Remove HPD polling thread function.
-Move HDMI/DP FW init and download function
before hdmi drm register.

Signed-off-by: Sandor Yu <Sandor.yu@nxp.com>
drivers/gpu/drm/imx/hdp/imx-dp.c
drivers/gpu/drm/imx/hdp/imx-dp.h
drivers/gpu/drm/imx/hdp/imx-hdmi.c
drivers/gpu/drm/imx/hdp/imx-hdmi.h
drivers/gpu/drm/imx/hdp/imx-hdp.c
drivers/gpu/drm/imx/hdp/imx-hdp.h

index bbed1b2..b4be8c9 100644 (file)
@@ -199,7 +199,10 @@ int dp_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
        return ret;
 }
 
-void dp_get_hpd_state(state_struct *state, u8 *hpd)
+int dp_get_hpd_state(state_struct *state, u8 *hpd)
 {
-       CDN_API_DPTX_GetHpdStatus_blocking(state, hpd);
+       int ret;
+
+       ret = CDN_API_DPTX_GetHpdStatus_blocking(state, hpd);
+       return ret;
 }
index 6e5de76..26300a8 100644 (file)
@@ -19,6 +19,6 @@ void dp_fw_init(state_struct *state);
 void dp_mode_set(state_struct *state, int vic, int format, int color_depth, int max_link_rate);
 int dp_phy_init(state_struct *state, int vic, int format, int color_depth);
 int dp_get_edid_block(void *data, u8 *buf, u32 block, size_t len);
-void dp_get_hpd_state(state_struct *state, u8 *hpd);
+int dp_get_hpd_state(state_struct *state, u8 *hpd);
 
 #endif
index 78a8844..f212f4d 100644 (file)
@@ -229,8 +229,10 @@ int hdmi_get_edid_block(void *data, u8 *buf, u32 block, size_t len)
        return ret;
 }
 
-void hdmi_get_hpd_state(state_struct *state, u8 *hpd)
+int hdmi_get_hpd_state(state_struct *state, u8 *hpd)
 {
-       printk("%s\n", __func__);
-       CDN_API_HDMITX_GetHpdStatus_blocking(state, hpd);
+       int ret;
+
+       ret = CDN_API_HDMITX_GetHpdStatus_blocking(state, hpd);
+       return ret;
 }
index b712c6e..e6c4b7c 100644 (file)
@@ -19,7 +19,7 @@ void hdmi_fw_init(state_struct *state);
 int hdmi_phy_init(state_struct *state, int vic, int format, int color_depth);
 void hdmi_mode_set(state_struct *state, int vic, int format, int color_depth, int temp);
 int hdmi_get_edid_block(void *data, u8 *buf, u32 block, size_t len);
-void hdmi_get_hpd_state(state_struct *state, u8 *hpd);
+int hdmi_get_hpd_state(state_struct *state, u8 *hpd);
 int hdmi_phy_init_t28hpc(state_struct *state, int vic, int format, int color_depth);
 void hdmi_mode_set_t28hpc(state_struct *state, int vic, int format, int color_depth, int temp);
 
index 9698f36..6576a14 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/component.h>
 #include <linux/mfd/syscon.h>
 #include <linux/of.h>
+#include <linux/irq.h>
 #include <linux/of_device.h>
 
 #include "imx-hdp.h"
@@ -566,25 +567,6 @@ void imx8qm_dp_ipg_clock_set_rate(struct hdp_clks *clks)
        }
 }
 
-static int imx_hdp_deinit(struct imx_hdp *hdp)
-{
-       u8 bresp;
-       u32 ret;
-
-       /* Stop link training */
-       CDN_API_DPTX_TrainingControl_blocking(&hdp->state, 0);
-
-       /* Disable HPD event and training */
-       CDN_API_DPTX_EnableEvent_blocking(&hdp->state, 0, 0);
-
-       /* turn off hdp controller IP activity 0-standby */
-       ret = CDN_API_MainControl_blocking(&hdp->state, 0, &bresp);
-       if (ret != CDN_OK)
-               return -1;
-
-       return ret;
-}
-
 static int imx_get_vic_index(struct drm_display_mode *mode)
 {
        int i;
@@ -627,19 +609,6 @@ static void imx_hdp_mode_setup(struct imx_hdp *hdp, struct drm_display_mode *mod
        hdp->vic = drm_match_cea_mode(mode);
 }
 
-#if 0
-static int imx_hdp_cable_plugin(struct imx_hdp *hdp)
-{
-       return 0;
-}
-
-static int imx_hdp_cable_plugout(struct imx_hdp *hdp)
-{
-       imx_hdp_call(hdp, pixel_clock_disable, &hdp->clks);
-       return 0;
-}
-#endif
-
 static void imx_hdp_bridge_mode_set(struct drm_bridge *bridge,
                                    struct drm_display_mode *orig_mode,
                                    struct drm_display_mode *mode)
@@ -667,7 +636,21 @@ static void imx_hdp_bridge_enable(struct drm_bridge *bridge)
 static enum drm_connector_status
 imx_hdp_connector_detect(struct drm_connector *connector, bool force)
 {
-       return connector_status_connected;
+       struct imx_hdp *hdp = container_of(connector,
+                                               struct imx_hdp, connector);
+       int ret;
+       u8 hpd;
+
+       ret = imx_hdp_call(hdp, get_hpd_state, &hdp->state, &hpd);
+       if (ret > 0)
+               return connector_status_unknown;
+
+       if (hpd == 1)
+               /* Cable Connected */
+               return connector_status_connected;
+       else
+               /* Cable Disconnedted  */
+               return connector_status_disconnected;
 }
 
 static int imx_hdp_connector_get_modes(struct drm_connector *connector)
@@ -893,6 +876,7 @@ static struct hdp_ops imx8qm_dp_ops = {
        .phy_init = dp_phy_init,
        .mode_set = dp_mode_set,
        .get_edid_block = dp_get_edid_block,
+       .get_hpd_state = dp_get_hpd_state,
 
        .phy_reset = imx8qm_phy_reset,
        .pixel_link_init = imx8qm_pixel_link_init,
@@ -914,6 +898,7 @@ static struct hdp_ops imx8qm_hdmi_ops = {
        .phy_init = hdmi_phy_init,
        .mode_set = hdmi_mode_set,
        .get_edid_block = hdmi_get_edid_block,
+       .get_hpd_state = hdmi_get_hpd_state,
 
        .phy_reset = imx8qm_phy_reset,
        .pixel_link_init = imx8qm_pixel_link_init,
@@ -956,6 +941,7 @@ static struct hdp_ops imx8mq_ops = {
        .phy_init = hdmi_phy_init_t28hpc,
        .mode_set = hdmi_mode_set_t28hpc,
        .get_edid_block = hdmi_get_edid_block,
+       .get_hpd_state = hdmi_get_hpd_state,
 };
 
 static struct hdp_devtype imx8mq_hdmi_devtype = {
@@ -974,79 +960,36 @@ static const struct of_device_id imx_hdp_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, imx_hdp_dt_ids);
 
-#if 0
-#ifdef hdp_irq
-static irqreturn_t imx_hdp_irq_handler(int irq, void *data)
+static void hotplug_work_func(struct work_struct *work)
 {
-       struct imx_hdp *hdp = data;
-       u8 eventId;
-       u8 HPDevents;
-       u8 aux_sts;
-       u8 aux_hpd;
-       u32 evt;
-       u8 hpdevent;
-
-       CDN_API_Get_Event(&hdp->state, &evt);
-
-       if (evt & 0x1) {
-               /* HPD event */
-               pr_info("\nevt=%d\n", evt);
-               drm_helper_hpd_irq_event(hdp->connector.dev);
-               CDN_API_DPTX_ReadEvent_blocking(&hdp->state, &eventId, &HPDevents);
-               pr_info("ReadEvent  ID = %d HPD = %d\n", eventId, HPDevents);
-               CDN_API_DPTX_GetHpdStatus_blocking(&hdp->state, &aux_hpd);
-               pr_info("aux_hpd = 0xx\n", aux_hpd);
-       } else if (evt & 0x2) {
-               /* Link training event */
-       } else
-               pr_info(".\r");
+       struct imx_hdp *hdp = container_of(work, struct imx_hdp,
+                                                               hotplug_work.work);
+       struct drm_connector *connector = &hdp->connector;
 
-       return IRQ_HANDLED;
+       drm_helper_hpd_irq_event(connector->dev);
+
+       if (connector->status == connector_status_connected) {
+               /* Cable Connected */
+               DRM_INFO("HDMI/DP Cable Plug In\n");
+               enable_irq(hdp->irq[HPD_IRQ_OUT]);
+       } else {
+               /* Cable Disconnedted  */
+               DRM_INFO("HDMI/DP Cable Plug Out\n");
+               enable_irq(hdp->irq[HPD_IRQ_IN]);
+       }
 }
-#else
-static int hpd_det_worker(void *_dp)
+
+static irqreturn_t imx_hdp_irq_thread(int irq, void *data)
 {
-       struct imx_hdp *hdp = (struct imx_hdp *) _dp;
-       u8 eventId;
-       u8 HPDevents;
-       u8 aux_hpd;
-       u32 evt;
-
-       for (;;) {
-               CDN_API_Get_Event(&hdp->state, &evt);
-               if (evt & 0x1) {
-                       /* HPD event */
-                       CDN_API_DPTX_ReadEvent_blocking(&hdp->state, &eventId, &HPDevents);
-                       pr_info("ReadEvent  ID = %d HPD = %d\n", eventId, HPDevents);
-                       CDN_API_DPTX_GetHpdStatus_blocking(&hdp->state, &aux_hpd);
-                       if (HPDevents & 0x1) {
-                               pr_info("cable plugin\n");
-                               imx_hdp_cable_plugin(hdp);
-                               hdp->cable_state = true;
-                               drm_kms_helper_hotplug_event(hdp->connector.dev);
-                               imx_hdp_mode_setup(hdp, &hdp->video.cur_mode);
-                       } else if (HPDevents & 0x2) {
-                               pr_info("cable plugout\n");
-                               hdp->cable_state = false;
-                               imx_hdp_cable_plugout(hdp);
-                               drm_kms_helper_hotplug_event(hdp->connector.dev);
-                       } else
-                               pr_info("HPDevent=0x%x\n", HPDevents);
-               } else if (evt & 0x2) {
-                       /* Link training event */
-                       pr_info("evt=0x%x\n", evt);
-                       CDN_API_DPTX_ReadEvent_blocking(&hdp->state, &eventId, &HPDevents);
-                       pr_info("ReadEvent  ID = %d HPD = %d\n", eventId, HPDevents);
-               } else if (evt & 0xf)
-                       pr_info("evt=0x%x\n", evt);
-
-               schedule_timeout_idle(200);
-       }
+       struct imx_hdp *hdp = data;
 
-       return 0;
+       disable_irq_nosync(irq);
+
+       mod_delayed_work(system_wq, &hdp->hotplug_work,
+                       msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+
+       return IRQ_HANDLED;
 }
-#endif
-#endif
 
 static int imx_hdp_imx_bind(struct device *dev, struct device *master,
                            void *data)
@@ -1061,10 +1004,7 @@ static int imx_hdp_imx_bind(struct device *dev, struct device *master,
        struct drm_bridge *bridge;
        struct drm_connector *connector;
        struct resource *res;
-#if 0
-       struct task_struct *hpd_worker;
-#endif
-       int irq;
+       u8 hpd;
        int ret;
 
        if (!pdev->dev.of_node)
@@ -1082,11 +1022,13 @@ static int imx_hdp_imx_bind(struct device *dev, struct device *master,
        g_hdp = hdp;
        mutex_init(&hdp->mutex);
 
-       irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_err(&pdev->dev, "can't get irq number\n");
-               return irq;
-       }
+       hdp->irq[HPD_IRQ_IN] = platform_get_irq_byname(pdev, "plug_in");
+       if (hdp->irq[HPD_IRQ_IN] < 0)
+               dev_info(&pdev->dev, "No plug_in irq number\n");
+
+       hdp->irq[HPD_IRQ_OUT] = platform_get_irq_byname(pdev, "plug_out");
+       if (hdp->irq[HPD_IRQ_OUT] < 0)
+               dev_info(&pdev->dev, "No plug_out irq number\n");
 
        /* register map */
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1109,46 +1051,7 @@ static int imx_hdp_imx_bind(struct device *dev, struct device *master,
        hdp->ops = devtype->ops;
        hdp->rw = devtype->rw;
 
-       /* encoder */
-       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
-       /*
-        * If we failed to find the CRTC(s) which this encoder is
-        * supposed to be connected to, it's because the CRTC has
-        * not been registered yet.  Defer probing, and hope that
-        * the required CRTC is added later.
-        */
-       if (encoder->possible_crtcs == 0) {
-               return -EPROBE_DEFER;
-       }
-
-       /* encoder */
-       drm_encoder_helper_add(encoder, &imx_hdp_imx_encoder_helper_funcs);
-       drm_encoder_init(drm, encoder, &imx_hdp_imx_encoder_funcs,
-                        DRM_MODE_ENCODER_TMDS, NULL);
-
-       /* bridge */
-       bridge->driver_private = hdp;
-       bridge->funcs = &imx_hdp_bridge_funcs;
-       ret = drm_bridge_attach(encoder, bridge, NULL);
-       if (ret) {
-               DRM_ERROR("Failed to initialize bridge with drm\n");
-               return -EINVAL;
-       }
-
-       encoder->bridge = bridge;
-
-       /* connector */
-       drm_connector_helper_add(connector,
-                                &imx_hdp_connector_helper_funcs);
-
-       drm_connector_init(drm, connector,
-                          &imx_hdp_connector_funcs,
-                          DRM_MODE_CONNECTOR_HDMIA);
-
-       drm_connector_attach_encoder(connector, encoder);
-
-       dev_set_drvdata(dev, hdp);
-
+       /* HDP controller init */
        imx_hdp_state_init(hdp);
 
        hdp->link_rate = AFE_LINK_RATE_1_6;
@@ -1190,31 +1093,85 @@ static int imx_hdp_imx_bind(struct device *dev, struct device *master,
                return ret;
        }
 
-#if 0
-#ifdef hdp_irq
-       ret = devm_request_threaded_irq(dev, irq,
-                                       NULL, imx_hdp_irq_handler,
-                                       IRQF_IRQPOLL, dev_name(dev), dp);
+       /* encoder */
+       encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+       /*
+        * If we failed to find the CRTC(s) which this encoder is
+        * supposed to be connected to, it's because the CRTC has
+        * not been registered yet.  Defer probing, and hope that
+        * the required CRTC is added later.
+        */
+       if (encoder->possible_crtcs == 0)
+               return -EPROBE_DEFER;
+
+       /* encoder */
+       drm_encoder_helper_add(encoder, &imx_hdp_imx_encoder_helper_funcs);
+       drm_encoder_init(drm, encoder, &imx_hdp_imx_encoder_funcs,
+                        DRM_MODE_ENCODER_TMDS, NULL);
+
+       /* bridge */
+       bridge->driver_private = hdp;
+       bridge->funcs = &imx_hdp_bridge_funcs;
+       ret = drm_bridge_attach(encoder, bridge, NULL);
        if (ret) {
-               dev_err(&pdev->dev, "can't claim irq %d\n", irq);
-               goto err_irq;
-       }
-#else
-       hpd_worker = kthread_create(hpd_det_worker, hdp, "hdp-hpd");
-       if (IS_ERR(hpd_worker)) {
-               dev_err(&pdev->dev, "failed  create hpd thread\n");
+               DRM_ERROR("Failed to initialize bridge with drm\n");
+               return -EINVAL;
        }
 
-       wake_up_process(hpd_worker);    /* avoid contributing to loadavg */
-#endif
-#endif
+       encoder->bridge = bridge;
+       hdp->connector.polled = DRM_CONNECTOR_POLL_HPD;
+
+       /* connector */
+       drm_connector_helper_add(connector,
+                                &imx_hdp_connector_helper_funcs);
+
+       drm_connector_init(drm, connector,
+                          &imx_hdp_connector_funcs,
+                          DRM_MODE_CONNECTOR_HDMIA);
+
+       drm_connector_attach_encoder(connector, encoder);
+
+       dev_set_drvdata(dev, hdp);
+
+       INIT_DELAYED_WORK(&hdp->hotplug_work, hotplug_work_func);
+
+       /* Check cable states before enable irq */
+       imx_hdp_call(hdp, get_hpd_state, &hdp->state, &hpd);
+
+       /* Enable Hotplug Detect IRQ thread */
+       if (hdp->irq[HPD_IRQ_IN] > 0) {
+               irq_set_status_flags(hdp->irq[HPD_IRQ_IN], IRQ_NOAUTOEN);
+               ret = devm_request_threaded_irq(dev, hdp->irq[HPD_IRQ_IN],
+                                               NULL, imx_hdp_irq_thread,
+                                               IRQF_ONESHOT, dev_name(dev), hdp);
+               if (ret) {
+                       dev_err(&pdev->dev, "can't claim irq %d\n",
+                                                       hdp->irq[HPD_IRQ_IN]);
+                       goto err_irq;
+               }
+               /* Cable Disconnedted, enable Plug in IRQ */
+               if (hpd == 0)
+                       enable_irq(hdp->irq[HPD_IRQ_IN]);
+       }
+       if (hdp->irq[HPD_IRQ_OUT] > 0) {
+               irq_set_status_flags(hdp->irq[HPD_IRQ_OUT], IRQ_NOAUTOEN);
+               ret = devm_request_threaded_irq(dev, hdp->irq[HPD_IRQ_OUT],
+                                               NULL, imx_hdp_irq_thread,
+                                               IRQF_ONESHOT, dev_name(dev), hdp);
+               if (ret) {
+                       dev_err(&pdev->dev, "can't claim irq %d\n",
+                                                       hdp->irq[HPD_IRQ_OUT]);
+                       goto err_irq;
+               }
+               /* Cable Connected, enable Plug out IRQ */
+               if (hpd == 1)
+                       enable_irq(hdp->irq[HPD_IRQ_OUT]);
+       }
 
        return 0;
-#ifdef hdp_irq
 err_irq:
        drm_encoder_cleanup(encoder);
        return ret;
-#endif
 }
 
 static void imx_hdp_imx_unbind(struct device *dev, struct device *master,
@@ -1222,8 +1179,6 @@ static void imx_hdp_imx_unbind(struct device *dev, struct device *master,
 {
        struct imx_hdp *hdp = dev_get_drvdata(dev);
 
-       imx_hdp_deinit(hdp);
-
        imx_hdp_call(hdp, pixel_link_deinit, &hdp->state);
 }
 
index f59976f..9451110 100644 (file)
@@ -53,6 +53,8 @@
 #define CSR_HDP_TX_CTRL_CTRL0          0x08
 #define CSR_HDP_TX_CTRL_CTRL1          0x0c
 
+#define HOTPLUG_DEBOUNCE_MS            200
+
 /**
  * imx_hdp_call - Calls a struct imx hdp_operations operation on
  *     an entity
@@ -83,7 +85,7 @@ struct hdp_ops {
        int (*phy_init)(state_struct *state, int vic, int format, int color_depth);
        void (*mode_set)(state_struct *state, int vic, int format, int color_depth, int max_link);
        int (*get_edid_block)(void *data, u8 *buf, u32 block, size_t len);
-       void (*get_hpd_state)(state_struct *state, u8 *hpd);
+       int (*get_hpd_state)(state_struct *state, u8 *hpd);
 
        void (*phy_reset)(sc_ipc_t ipcHndl, u8 reset);
        int (*pixel_link_init)(state_struct *state);
@@ -172,6 +174,12 @@ struct hdp_clks {
        struct clk *clk_i2s_bypass;
 };
 
+enum hdp_tx_irq {
+       HPD_IRQ_IN,
+       HPD_IRQ_OUT,
+       HPD_IRQ_NUM,
+};
+
 struct imx_hdp {
        struct device *dev;
        struct drm_connector connector;
@@ -208,6 +216,8 @@ struct imx_hdp {
        struct hdp_clks clks;
        state_struct state;
        int vic;
+       int irq[HPD_IRQ_NUM];
+       struct delayed_work hotplug_work;
 
 };