HDMI/PM: Port two HDMI hotplug patches from R2
authorGeng Xiujun <xiujun.geng@intel.com>
Thu, 29 Mar 2012 05:52:51 +0000 (13:52 +0800)
committerbuildbot <buildbot@intel.com>
Sat, 31 Mar 2012 05:59:37 +0000 (22:59 -0700)
BZ: 29595

The issue is tracked by BZ 19079, HDMI cable plug/unplugged not detected when
device is in S0 with Screen OFF. Jouni's two patches are ported to R3.

0001-hdmi-Fixes-for-hdmi-hotplug-detection.patch

Remove random control of HDMI rails around gfx driver. Power on is now
done in psb_intel_hdmi.c:mdfld_hdmi_init during boot up.

Global variable hdmi_state is not updated properly when display is
blanked. Fix this by updating hdmi_state in hdmi_do_hotplug_wq.

Also msic_irq should be type IRQF_NO_SUSPEND to ensure hpd interrupts
are not lost.

0002-hdmi-Implement-hdmi-cable-detection.patch

According to MSIC HDMI Integration Guide both, VCC330 and VHDMI, must
be enabled hotplug detection to work. This would cause unacceptable
power leak in S3 state -> Take approach to have hotplug detection
working only when device is not in S3. HDMI cable Plug in/out during
S3 is detected when waking up from S3. This allows powering off VCC330
and VHDMI during S3.

To implement this following changes are done in this patch:

Create dedicated freezeable workqueue for hotplug detection to ensure
ongoing detection is stopped before powering off HDMI rails on
suspend and resumed only when rails are powered on again.

Add mechanism to detect cable plug out during S3 when waking up.

Change-Id: I161928534a3c6b197c0d2ac450e5587ff196a74b
Signed-off-by: Geng Xiujun <xiujun.geng@intel.com>
Reviewed-on: http://android.intel.com:8080/41579
Reviewed-by: Xu, Randy <randy.xu@intel.com>
Reviewed-by: Tong, BoX <box.tong@intel.com>
Tested-by: Tong, BoX <box.tong@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
drivers/staging/mrst/drv/mdfld_msic.c
drivers/staging/mrst/drv/psb_drv.c
drivers/staging/mrst/drv/psb_powermgmt.c

index 97d17cd..0726cdd 100644 (file)
@@ -34,6 +34,9 @@
 #define SRAM_MSIC_VRINT_ADDR 0xFFFF7FCB
 static u8 *sram_vreg_addr = 0;
 unsigned char vrint_dat;
+static bool hpd_suspended;
+static u8 hdmi_saved_status;
+
 /*
  *
  */
@@ -94,7 +97,7 @@ void hpd_notify_um(struct drm_device *dev)
        }
 
        /* send drm uevent message */
-       schedule_work(&dev_priv->hdmi_hotplug_wq);
+       queue_work(dev_priv->hpd_detect, &dev_priv->hdmi_hotplug_wq);
 
        return;
 }
@@ -108,6 +111,9 @@ irqreturn_t msic_vreg_handler(int irq, void *dev_id)
        struct drm_psb_private *dev_priv = NULL;
        vrint_dat = 0;
 
+       if (hpd_suspended)
+               return IRQ_HANDLED;
+
        /* Need to add lock later.*/
 
        /* Read VREG interrupt status register */
@@ -136,6 +142,127 @@ irqreturn_t msic_vreg_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+/*
+ * Tell irq handler that possible hpd interrupts should be handled
+ * again (hpd_suspended).
+ *
+ * Power on HDMI rails again. In case we have cable plugged in -> HPD
+ * interrupt is generated and detection is started.
+ *
+ * Check possible cable plug out by comparing current hdmi status
+ * against the saved one (hdmi_saved_status).
+ *
+ * Please note that workqueue which does the detection itself is frozen at
+ * this point. Possible hpd interrupt after hpd_suspended is set as
+ * false will queue detection, but the detection is started later
+ * when workqueue is woken up again.
+ */
+static int msic_resume(struct device *dev)
+{
+       int ret = 0;
+       u8 hdmi_status;
+       struct drm_device *drm_dev = hdmi_priv ? hdmi_priv->dev : NULL;
+
+       hpd_suspended = false;
+
+       ret = intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_ON);
+       if (ret) {
+               DRM_ERROR("%s: Failed to read MSIC_HDMI_STATUS.\n",
+                         __func__);
+               goto err;
+       }
+
+       /* MSIC documentation requires that there be a 500us delay
+          after enabling VCC330 before you can enable VHDMI */
+       usleep_range(500, 1000);
+
+       ret = intel_scu_ipc_iowrite8(MSIC_VHDMICNT, VHDMI_ON | VHDMI_DB_30MS);
+       if (ret) {
+               DRM_ERROR("%s: Failed to power on VHDMI.\n",
+                         __func__);
+               goto err;
+       }
+
+       ret = intel_scu_ipc_ioread8(MSIC_HDMI_STATUS, &hdmi_status);
+       if (ret)
+               DRM_ERROR("%s: Failed to read MSIC_HDMI_STATUS.\n",
+                         __func__);
+
+       if (!ret && (hdmi_saved_status & HPD_SIGNAL_STATUS) &&
+           !(hdmi_status & HPD_SIGNAL_STATUS))
+               hpd_notify_um(drm_dev);
+
+       return ret;
+
+err:
+       hpd_suspended = true;
+       return ret;
+}
+
+/*
+ * On suspend we want to power off VHDMI and VCC330. Hotplug
+ * detection doesn't work without these rails. This is acceptable as
+ * long as changes are detected when waking up from suspend.
+ *
+ * HDMI cable plug out detection during suspend is done in msic_resume by saving
+ * hdmi status (hdmi_saved_status) and using it in in resume to detect
+ * cable unplug. Cable plug in is detected as hpd interrupt is
+ * generated always when powering on HDMI rails while HDMI cable is
+ * plugged in.
+ *
+ * Hpd_suspended is used to tell irq handler that the detection is now
+ * suspended and possible HPD interrupts can be ignored. Otherwise
+ * powering down HDMI rails would cause interrupt and detection when
+ * suspending with HDMI cable plugged in.
+ *
+ * Please note that workqueue which does the detection itself is frozen at
+ * this point. Possible hpd interrupt before hpd_suspended is set true
+ * will queue detection, but it's started later when workqueue is woken up
+ * again on resume path.
+ */
+static int msic_suspend(struct device *dev)
+{
+       int ret = 0, err = 0;
+       u8 hdmi_status;
+       struct drm_device *drm_dev = hdmi_priv ? hdmi_priv->dev : NULL;
+
+       ret = intel_scu_ipc_ioread8(MSIC_HDMI_STATUS, &hdmi_saved_status);
+       if (ret) {
+               DRM_ERROR("%s: Failed to read MSIC_HDMI_STATUS.\n",
+                         __func__);
+               goto err1;
+       }
+
+       hpd_suspended = true;
+
+       ret = intel_scu_ipc_iowrite8(MSIC_VHDMICNT, VHDMI_OFF);
+       if (ret) {
+               DRM_ERROR("%s: Failed to power off VHDMI.\n",
+                         __func__);
+               goto err2;
+       }
+
+       ret = intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_OFF);
+       if (ret) {
+               DRM_ERROR("%s: Failed to power off VCC330.\n",
+                         __func__);
+               goto err3;
+       }
+
+       return ret;
+
+err3:
+       err |= intel_scu_ipc_iowrite8(MSIC_VHDMICNT, VHDMI_ON | VHDMI_DB_30MS);
+err2:
+       hpd_suspended = false;
+       err |= intel_scu_ipc_ioread8(MSIC_HDMI_STATUS, &hdmi_status);
+       if (!err && (hdmi_saved_status & HPD_SIGNAL_STATUS) !=
+           (hdmi_status & HPD_SIGNAL_STATUS))
+               hpd_notify_um(drm_dev);
+err1:
+       return ret;
+}
+
 /**
  *  msic_probe
  */
@@ -143,36 +270,64 @@ static int __devinit msic_probe(struct pci_dev *pdev,
                        const struct pci_device_id *ent)
 {
        struct drm_device *dev = hdmi_priv ? hdmi_priv->dev : 0;
+       struct drm_psb_private *dev_priv = psb_priv(dev);
        int ret = 0;
 
-       if (dev)
-       {
-               PSB_DEBUG_ENTRY("\n");
+       if (pdev->device != MSIC_PCI_DEVICE_ID) {
+               DRM_ERROR("%s: pciid = 0x%x is not msic_hdmi pciid.\n",
+                         __func__, pdev->device);
+               goto err1;
        }
+
        /* enable msic hdmi device */
        ret = pci_enable_device(pdev);
+       if (ret) {
+               DRM_ERROR("%s: Enable pci device failed. ret = 0x%x.\n",
+                         __func__, ret);
+               goto err1;
+       }
 
-       if (!ret) {
-
-               if (pdev->device == MSIC_PCI_DEVICE_ID) {
-                       sram_vreg_addr = ioremap_nocache(SRAM_MSIC_VRINT_ADDR, 0x2);
-                       ret = request_irq(pdev->irq, msic_vreg_handler,
-                                       IRQF_SHARED,
-                                       "msic_hdmi_driver", (void *)&hdmi_priv);
-               } else
-                       DRM_ERROR("%s: pciid = 0x%x is not msic_hdmi pciid.\n",
-                                       __func__, pdev->device);
-
-               if (ret) {
-                       pci_dev_put(pdev);
-                       DRM_ERROR("%s: request_irq failed. ret = 0x%x.\n",
-                                       __func__, ret);
-               }
+       sram_vreg_addr = ioremap_nocache(SRAM_MSIC_VRINT_ADDR, 0x2);
+       if (sram_vreg_addr == NULL) {
+               DRM_ERROR("%s: Memory map failed", __func__);
+               ret = -ENOMEM;
+               goto err2;
+       }
+
+       dev_priv->hpd_detect = create_freezable_workqueue("hpd_detect");
+       if (dev_priv->hpd_detect == NULL) {
+               DRM_ERROR("%s: Creating workqueue failed", __func__);
+               ret = -ENOMEM;
+               goto err3;
+       }
+
+       ret = request_irq(pdev->irq, msic_vreg_handler, IRQF_SHARED,
+                         "msic_hdmi_driver", (void *)&hdmi_priv);
+       if (ret) {
+               DRM_ERROR("%s: request_irq failed. ret = 0x%x.\n",
+                         __func__, ret);
+               goto err4;
        }
 
        return ret;
+
+err4:
+       destroy_workqueue(dev_priv->hpd_detect);
+err3:
+       iounmap(sram_vreg_addr);
+err2:
+       pci_disable_device(pdev);
+err1:
+       pci_dev_put(pdev);
+
+       return ret;
 }
 
+static const struct dev_pm_ops msic_pm_ops = {
+       .suspend = msic_suspend,
+       .resume = msic_resume,
+};
+
 static struct pci_device_id msic_pci_id_list[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_INTEL, MSIC_PCI_DEVICE_ID) },
        { 0 }
@@ -185,7 +340,8 @@ static struct pci_device_id msic_pci_id_list[] = {
 static struct pci_driver msic_pci_driver = {
        .name = "msic_hdmi_driver",
        .id_table = msic_pci_id_list,
-       .probe = msic_probe
+       .probe = msic_probe,
+       .driver.pm = &msic_pm_ops,
 };
 
 /**
index 44ef38f..9176e4d 100644 (file)
@@ -1226,8 +1226,6 @@ void hdmi_do_hotplug_wq(struct work_struct *work)
        */
 
        if (IS_MDFLD_OLD(dev)) {
-               intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_ON);
-
                if (vrint_dat & HDMI_OCP_STATUS) {
                        /*
                         * when there occurs overcurrent in msic hdmi hdp,
@@ -1261,6 +1259,7 @@ void hdmi_do_hotplug_wq(struct work_struct *work)
        if (hdmi_hpd_connected) {
                DRM_INFO("%s: HDMI plugged in\n", __func__);
                dev_priv->bhdmiconnected = true;
+               hdmi_state = 1;
                drm_sysfs_hotplug_event(dev_priv->dev);
        } else {
                DRM_INFO("%s: HDMI unplugged\n", __func__);
@@ -1295,7 +1294,6 @@ void hdmi_do_audio_wq(struct work_struct *work)
        DRM_INFO("hdmi_do_audio_wq: Checking for HDMI connection at boot\n");
 
        if (IS_MDFLD_OLD(dev_priv->dev)) {
-               intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_ON);
                intel_scu_ipc_ioread8(MSIC_HDMI_STATUS, &data);
 
                if (data & HPD_SIGNAL_STATUS)
index 09c907b..00c0052 100644 (file)
@@ -1602,11 +1602,6 @@ void ospm_resume_display(struct pci_dev *pdev)
                                                psb_sysfs_uevent(dev_priv->dev, uevent_string);
                                        }
                                }
-                       } else {
-                               PSB_DEBUG_ENTRY("hdmi is unplugged: %d!\n", hdmi_state);
-                               if (hdmi_state)
-                                       schedule_work(&dev_priv->hdmi_hotplug_wq);
-                               msleep(100);
                        }
 #endif
                }
@@ -1996,25 +1991,6 @@ void ospm_power_island_up(int hw_islands)
                 __func__, hw_islands);
 #endif
        if (hw_islands & OSPM_DISPLAY_ISLAND) {
-
-#ifdef CONFIG_MDFD_HDMI
-               if (IS_MDFLD_OLD(dev_priv->dev)) {
-                       /*
-                        * Always turn on MSIC VCC330 and VHDMI when display is
-                        * on.
-                        */
-                       intel_scu_ipc_iowrite8(MSIC_VCC330CNT, VCC330_ON);
-                       /*
-                        * MSIC documentation requires that there be a 500us
-                        * delay after enabling VCC330 before you can enable
-                        * VHDMI
-                        */
-                       usleep_range(500, 1000);
-                       /* turn on HDMI power rails */
-                       intel_scu_ipc_iowrite8(MSIC_VHDMICNT,
-                                       VHDMI_ON | VHDMI_DB_30MS);
-               }
-#endif
                /*Power-up required islands only*/
                if (dev_priv->panel_desc & DISPLAY_A)
                        dc_islands |= OSPM_DISPLAY_A_ISLAND;
@@ -2144,21 +2120,6 @@ void ospm_power_island_down(int hw_islands)
                        BUG();
                spin_unlock_irqrestore(&dev_priv->ospm_lock, flags);
 
-#ifdef CONFIG_MDFD_HDMI
-               if (IS_MDFLD_OLD(dev_priv->dev)) {
-                       /*
-                        * Turn off MSIC VCC330 and VHDMI if HDMI is
-                        * disconnected.
-                        */
-                       if (!hdmi_state) {
-                               /* turn off HDMI power rails */
-                               intel_scu_ipc_iowrite8(MSIC_VHDMICNT,
-                                               VHDMI_OFF);
-                               intel_scu_ipc_iowrite8(MSIC_VCC330CNT,
-                                               VCC330_OFF);
-                       }
-               }
-#endif
                /* handle other islands */
                gfx_islands = hw_islands & ~OSPM_DISPLAY_ISLAND;
        }