#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;
+
/*
*
*/
}
/* send drm uevent message */
- schedule_work(&dev_priv->hdmi_hotplug_wq);
+ queue_work(dev_priv->hpd_detect, &dev_priv->hdmi_hotplug_wq);
return;
}
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 */
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
*/
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 }
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,
};
/**
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
}
__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;
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;
}