Skip to contentSkip to conversation

drm/nouveau: bring back hdmi audio device after switcheroo power down

RFC PATCH In review Dave Airlie 5 participants 5 messages Jul 24, 2013 – Jul 25, 2013 2 messages not in the archive

Subsystems (3)
lore
Share & tools
lore

Dave Airlie

author maintainer
#0 lore raw

RFC PATCH 1/1 drm/nouveau: bring back hdmi audio device after switcheroo power down

After a full device powerdown via the optimus power switch, we seem to lose the HDMI device completely on power on, this keep track of whether we had a hdmi audio sub function device at power on, and pokes a magic register to make it reappear after the optimus power switch is thrown.

This at least works on my NVC4 machine, probably needs testing on a few other laptops with other nvidia GPUs.

---
 drivers/gpu/drm/nouveau/nouveau_drm.c | 32 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nouveau_drm.h |  2 ++
 drivers/gpu/drm/nouveau/nouveau_vga.c | 17 +++++++++++++++++
 3 files changed, 51 insertions(+)
drivers/gpu/drm/nouveau/nouveau_drm.c +32 -0
index 6197266..12a6240 100644
@@ -296,6 +296,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
 	return 0;
 }
 
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+static void
+nouveau_get_hdmi_dev(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = dev->dev_private;
+	struct pci_dev *pdev = dev->pdev;
+
+	/* subfunction one is a hdmi audio device? */
+	drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
+						PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
+
+	if (!drm->hdmi_device) {
+		DRM_INFO("hdmi device  not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
+		return;
+	}
+
+	if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
+		DRM_INFO("possible hdmi device  not audio %d\n", drm->hdmi_device->class);
+		pci_dev_put(drm->hdmi_device);
+		drm->hdmi_device = NULL;
+		return;
+	}
+}
+
 static int
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
@@ -314,6 +339,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 	INIT_LIST_HEAD(&drm->clients);
 	spin_lock_init(&drm->tile.lock);
 
+	nouveau_get_hdmi_dev(dev);
+
 	/* make sure AGP controller is in a consistent state before we
 	 * (possibly) execute vbios init tables (see nouveau_agp.h)
 	 */
@@ -400,6 +427,9 @@ fail_ttm:
 	nouveau_agp_fini(drm);
 	nouveau_vga_fini(drm);
 fail_device:
+	if (drm->hdmi_device)
+		pci_dev_put(drm->hdmi_device);
+
 	nouveau_cli_destroy(&drm->client);
 	return ret;
 }
@@ -424,6 +454,8 @@ nouveau_drm_unload(struct drm_device *dev)
 	nouveau_agp_fini(drm);
 	nouveau_vga_fini(drm);
 
+	if (drm->hdmi_device)
+		pci_dev_put(drm->hdmi_device);
 	nouveau_cli_destroy(&drm->client);
 	return 0;
 }
drivers/gpu/drm/nouveau/nouveau_drm.h +2 -0
index 41ff7e0..f276e37 100644
@@ -129,6 +129,8 @@ struct nouveau_drm {
 
 	/* power management */
 	struct nouveau_pm *pm;
+
+	struct pci_dev *hdmi_device;
 };
 
 static inline struct nouveau_drm *
drivers/gpu/drm/nouveau/nouveau_vga.c +17 -0
index 25d3495..d8af49c 100644
@@ -27,6 +27,22 @@ nouveau_vga_set_decode(void *priv, bool state)
 }
 
 static void
+nouveau_reenable_hdmi_device(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_device *device = nv_device(drm->device);
+	uint32_t val;
+
+	if (!drm->hdmi_device)
+		return;
+
+	/* write magic value into magic place */
+	val = nv_rd32(device, 0x88488);
+	val |= (1 << 25);
+	nv_wr32(device, 0x88488, val);
+}
+
+static void
 nouveau_switcheroo_set_state(struct pci_dev *pdev,
 			     enum vga_switcheroo_state state)
 {
@@ -37,6 +53,7 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
 		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
 		nouveau_pmops_resume(&pdev->dev);
 		drm_kms_helper_poll_enable(dev);
+		nouveau_reenable_hdmi_device(dev);
 		dev->switch_power_state = DRM_SWITCH_POWER_ON;
 	} else {
 		printk(KERN_ERR "VGA switcheroo: switched nouveau off\n");
Signature · 1 line
1.8.2.1
#1 lore raw

Am Mittwoch, den 24.07.2013, 17:13 +1000 schrieb Dave Airlie:

Quoted 2 lines

After a full device powerdown via the optimus power switch, we seem to lose the HDMI device completely on power on, this keep track of

keep*s*

Quoted 19 lines

whether we had a hdmi audio sub function device at power on, and pokes a magic register to make it reappear after the optimus power switch is thrown.

This at least works on my NVC4 machine, probably needs testing on a few other laptops with other nvidia GPUs.

---
 drivers/gpu/drm/nouveau/nouveau_drm.c | 32 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nouveau_drm.h |  2 ++
 drivers/gpu/drm/nouveau/nouveau_vga.c | 17 +++++++++++++++++
 3 files changed, 51 insertions(+)
drivers/gpu/drm/nouveau/nouveau_drm.c +0 -0
index 6197266..12a6240 100644

drivers/gpu/drm/nouveau/nouveau_drm.c static int nouveau_drm_probe(struct pci_dev *pdev, · line 299

Quoted 4 lines
drivers/gpu/drm/nouveau/nouveau_drm.c +1 -0
@@ -296,6 +296,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
 	return 0;
 }
 
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403

Should that go into some header?

drivers/gpu/drm/nouveau/nouveau_drm.c static int nouveau_drm_probe(struct pci_dev *pdev, · line 307

Quoted 8 lines
drivers/gpu/drm/nouveau/nouveau_drm.c +8 -0
+
+static void
+nouveau_get_hdmi_dev(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = dev->dev_private;
+	struct pci_dev *pdev = dev->pdev;
+
+	/* subfunction one is a hdmi audio device? */

Just function as in <domain>:<bus>:<slot>.<func>?

drivers/gpu/drm/nouveau/nouveau_drm.c static int nouveau_drm_probe(struct pci_dev *pdev, · line 312

Quoted 5 lines
drivers/gpu/drm/nouveau/nouveau_drm.c +5 -0
+	drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
+						PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
+
+	if (!drm->hdmi_device) {
+		DRM_INFO("hdmi device  not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);

Just one space after device?

Quoted 66 lines
drivers/gpu/drm/nouveau/nouveau_drm.c +18 -0
+		return;
+	}
+
+	if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
+		DRM_INFO("possible hdmi device  not audio %d\n", drm->hdmi_device->class);
+		pci_dev_put(drm->hdmi_device);
+		drm->hdmi_device = NULL;
+		return;
+	}
+}
+
 static int
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
@@ -314,6 +339,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 	INIT_LIST_HEAD(&drm->clients);
 	spin_lock_init(&drm->tile.lock);
 
+	nouveau_get_hdmi_dev(dev);
+
 	/* make sure AGP controller is in a consistent state before we
 	 * (possibly) execute vbios init tables (see nouveau_agp.h)
 	 */
@@ -400,6 +427,9 @@ fail_ttm:
 	nouveau_agp_fini(drm);
 	nouveau_vga_fini(drm);
 fail_device:
+	if (drm->hdmi_device)
+		pci_dev_put(drm->hdmi_device);
+
 	nouveau_cli_destroy(&drm->client);
 	return ret;
 }
@@ -424,6 +454,8 @@ nouveau_drm_unload(struct drm_device *dev)
 	nouveau_agp_fini(drm);
 	nouveau_vga_fini(drm);
 
+	if (drm->hdmi_device)
+		pci_dev_put(drm->hdmi_device);
 	nouveau_cli_destroy(&drm->client);
 	return 0;
 }
drivers/gpu/drm/nouveau/nouveau_drm.h +2 -0
index 41ff7e0..f276e37 100644
@@ -129,6 +129,8 @@ struct nouveau_drm {
 
 	/* power management */
 	struct nouveau_pm *pm;
+
+	struct pci_dev *hdmi_device;
 };
 
 static inline struct nouveau_drm *
drivers/gpu/drm/nouveau/nouveau_vga.c +3 -0
index 25d3495..d8af49c 100644
@@ -27,6 +27,22 @@ nouveau_vga_set_decode(void *priv, bool state)
 }
 
 static void
+nouveau_reenable_hdmi_device(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_drm(dev);

drivers/gpu/drm/nouveau/nouveau_vga.c nouveau_vga_set_decode(void *priv, bool state) · line 42

Quoted 10 lines
drivers/gpu/drm/nouveau/nouveau_vga.c +10 -0
+	struct nouveau_device *device = nv_device(drm->device);
+	uint32_t val;
+
+	if (!drm->hdmi_device)
+		return;
+
+	/* write magic value into magic place */
+	val = nv_rd32(device, 0x88488);
+	val |= (1 << 25);
+	nv_wr32(device, 0x88488, val);

Use a define for this nevertheless?

Quoted 7 lines
drivers/gpu/drm/nouveau/nouveau_vga.c +3 -0
+}
+
+static void
 nouveau_switcheroo_set_state(struct pci_dev *pdev,
 			     enum vga_switcheroo_state state)
 {

drivers/gpu/drm/nouveau/nouveau_vga.c nouveau_switcheroo_set_state(struct pci_dev *pdev, · line 59

Quoted 7 lines
drivers/gpu/drm/nouveau/nouveau_vga.c +1 -0
@@ -37,6 +53,7 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev,
 		dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
 		nouveau_pmops_resume(&pdev->dev);
 		drm_kms_helper_poll_enable(dev);
+		nouveau_reenable_hdmi_device(dev);
 		dev->switch_power_state = DRM_SWITCH_POWER_ON;
 	} else {
 		printk(KERN_ERR "VGA switcheroo: switched nouveau off\n");

Otherwise this looks good,

Thanks,

Paul

_______________________________________________
dri-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/dri-devel

#2 lore raw
In reply to Dave Airlie
Quoted 9 lines · Dave Airlie

After a full device powerdown via the optimus power switch, we seem to lose the HDMI device completely on power on, this keep track of whether we had a hdmi audio sub function device at power on, and pokes a magic register to make it reappear after the optimus power switch is thrown.

This at least works on my NVC4 machine, probably needs testing on a few other laptops with other nvidia GPUs.

Sorry for a stupid question, but how do you test "optimus power switch" ? (I have a laptop with hybrid Optimus graphics, so I could test..)

Thanks,

-- Pasi

A message that is not in the archive (sent off-list or not yet received): 1374650022-17865-1-git-send-email-airlied-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org

#4 lore raw

Op 24-07-13 09:13, Dave Airlie schreef:

Quoted 105 lines

After a full device powerdown via the optimus power switch, we seem to lose the HDMI device completely on power on, this keep track of whether we had a hdmi audio sub function device at power on, and pokes a magic register to make it reappear after the optimus power switch is thrown.

This at least works on my NVC4 machine, probably needs testing on a few other laptops with other nvidia GPUs.

---
 drivers/gpu/drm/nouveau/nouveau_drm.c | 32 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nouveau_drm.h |  2 ++
 drivers/gpu/drm/nouveau/nouveau_vga.c | 17 +++++++++++++++++
 3 files changed, 51 insertions(+)
drivers/gpu/drm/nouveau/nouveau_drm.c +32 -0
index 6197266..12a6240 100644
@@ -296,6 +296,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
 	return 0;
 }
 
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+static void
+nouveau_get_hdmi_dev(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = dev->dev_private;
+	struct pci_dev *pdev = dev->pdev;
+
+	/* subfunction one is a hdmi audio device? */
+	drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
+						PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
+
+	if (!drm->hdmi_device) {
+		DRM_INFO("hdmi device  not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
+		return;
+	}
+
+	if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
+		DRM_INFO("possible hdmi device  not audio %d\n", drm->hdmi_device->class);
+		pci_dev_put(drm->hdmi_device);
+		drm->hdmi_device = NULL;
+		return;
+	}
+}
+
 static int
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
@@ -314,6 +339,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 	INIT_LIST_HEAD(&drm->clients);
 	spin_lock_init(&drm->tile.lock);
 
+	nouveau_get_hdmi_dev(dev);
+
 	/* make sure AGP controller is in a consistent state before we
 	 * (possibly) execute vbios init tables (see nouveau_agp.h)
 	 */
@@ -400,6 +427,9 @@ fail_ttm:
 	nouveau_agp_fini(drm);
 	nouveau_vga_fini(drm);
 fail_device:
+	if (drm->hdmi_device)
+		pci_dev_put(drm->hdmi_device);
+
 	nouveau_cli_destroy(&drm->client);
 	return ret;
 }
@@ -424,6 +454,8 @@ nouveau_drm_unload(struct drm_device *dev)
 	nouveau_agp_fini(drm);
 	nouveau_vga_fini(drm);
 
+	if (drm->hdmi_device)
+		pci_dev_put(drm->hdmi_device);
 	nouveau_cli_destroy(&drm->client);
 	return 0;
 }
drivers/gpu/drm/nouveau/nouveau_drm.h +2 -0
index 41ff7e0..f276e37 100644
@@ -129,6 +129,8 @@ struct nouveau_drm {
 
 	/* power management */
 	struct nouveau_pm *pm;
+
+	struct pci_dev *hdmi_device;
 };
 
 static inline struct nouveau_drm *
drivers/gpu/drm/nouveau/nouveau_vga.c +4 -0
index 25d3495..d8af49c 100644
@@ -27,6 +27,22 @@ nouveau_vga_set_decode(void *priv, bool state)
 }
 
 static void
+nouveau_reenable_hdmi_device(struct drm_device *dev)
+{
+	struct nouveau_drm *drm = nouveau_drm(dev);
+	struct nouveau_device *device = nv_device(drm->device);

drivers/gpu/drm/nouveau/nouveau_vga.c nouveau_vga_set_decode(void *priv, bool state) · line 43

Quoted 10 lines
drivers/gpu/drm/nouveau/nouveau_vga.c +10 -0
+	uint32_t val;
+
+	if (!drm->hdmi_device)
+		return;
+
+	/* write magic value into magic place */
+	val = nv_rd32(device, 0x88488);
+	val |= (1 << 25);
+	nv_wr32(device, 0x88488, val);
+}

use nv_mask(dev, 0x88488, 0x02000000, 0x02000000);

Could this be added in core/engine/disp/hdminv84.c nv84_hdmi_ctrl to get the same effect?

~Maarten

A message that is not in the archive (sent off-list or not yet received): [email protected]

#6 lore raw
Quoted 101 lines · Maarten Lankhorst

Op 24-07-13 09:13, Dave Airlie schreef:

Quoted 100 lines

After a full device powerdown via the optimus power switch, we seem to lose the HDMI device completely on power on, this keep track of whether we had a hdmi audio sub function device at power on, and pokes a magic register to make it reappear after the optimus power switch is thrown.

This at least works on my NVC4 machine, probably needs testing on a few other laptops with other nvidia GPUs.

---
 drivers/gpu/drm/nouveau/nouveau_drm.c | 32 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nouveau_drm.h |  2 ++
 drivers/gpu/drm/nouveau/nouveau_vga.c | 17 +++++++++++++++++
 3 files changed, 51 insertions(+)
drivers/gpu/drm/nouveau/nouveau_drm.c +32 -0
index 6197266..12a6240 100644
@@ -296,6 +296,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev,
      return 0;
 }
 
+#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
+
+static void
+nouveau_get_hdmi_dev(struct drm_device *dev)
+{
+     struct nouveau_drm *drm = dev->dev_private;
+     struct pci_dev *pdev = dev->pdev;
+
+     /* subfunction one is a hdmi audio device? */
+     drm->hdmi_device = pci_get_bus_and_slot((unsigned int)pdev->bus->number,
+                                             PCI_DEVFN(PCI_SLOT(pdev->devfn), 1));
+
+     if (!drm->hdmi_device) {
+             DRM_INFO("hdmi device  not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1);
+             return;
+     }
+
+     if ((drm->hdmi_device->class >> 8) != PCI_CLASS_MULTIMEDIA_HD_AUDIO) {
+             DRM_INFO("possible hdmi device  not audio %d\n", drm->hdmi_device->class);
+             pci_dev_put(drm->hdmi_device);
+             drm->hdmi_device = NULL;
+             return;
+     }
+}
+
 static int
 nouveau_drm_load(struct drm_device *dev, unsigned long flags)
 {
@@ -314,6 +339,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)
      INIT_LIST_HEAD(&drm->clients);
      spin_lock_init(&drm->tile.lock);
 
+     nouveau_get_hdmi_dev(dev);
+
      /* make sure AGP controller is in a consistent state before we
       * (possibly) execute vbios init tables (see nouveau_agp.h)
       */
@@ -400,6 +427,9 @@ fail_ttm:
      nouveau_agp_fini(drm);
      nouveau_vga_fini(drm);
 fail_device:
+     if (drm->hdmi_device)
+             pci_dev_put(drm->hdmi_device);
+
      nouveau_cli_destroy(&drm->client);
      return ret;
 }
@@ -424,6 +454,8 @@ nouveau_drm_unload(struct drm_device *dev)
      nouveau_agp_fini(drm);
      nouveau_vga_fini(drm);
 
+     if (drm->hdmi_device)
+             pci_dev_put(drm->hdmi_device);
      nouveau_cli_destroy(&drm->client);
      return 0;
 }
drivers/gpu/drm/nouveau/nouveau_drm.h +2 -0
index 41ff7e0..f276e37 100644
@@ -129,6 +129,8 @@ struct nouveau_drm {
 
      /* power management */
      struct nouveau_pm *pm;
+
+     struct pci_dev *hdmi_device;
 };
 
 static inline struct nouveau_drm *
drivers/gpu/drm/nouveau/nouveau_vga.c +0 -0
index 25d3495..d8af49c 100644
@@ -27,6 +27,22 @@ nouveau_vga_set_decode(void *priv, bool state)
 }
 
Quoted 16 lines
Quoted 5 lines
drivers/gpu/drm/nouveau/nouveau_vga.c +4 -0
 static void
+nouveau_reenable_hdmi_device(struct drm_device *dev)
+{
+     struct nouveau_drm *drm = nouveau_drm(dev);
+     struct nouveau_device *device = nv_device(drm->device);

drivers/gpu/drm/nouveau/nouveau_vga.c nouveau_vga_set_decode(void *priv, bool state) · line 43

Quoted 10 lines
drivers/gpu/drm/nouveau/nouveau_vga.c +10 -0
+     uint32_t val;
+
+     if (!drm->hdmi_device)
+             return;
+
+     /* write magic value into magic place */
+     val = nv_rd32(device, 0x88488);
+     val |= (1 << 25);
+     nv_wr32(device, 0x88488, val);
+}

use nv_mask(dev, 0x88488, 0x02000000, 0x02000000);

much nicer,

Quoted 3 lines

Could this be added in core/engine/disp/hdminv84.c nv84_hdmi_ctrl to get the same effect?

Don't think so its nothing to do with the hdmi audio part on the device, it just controls the PCI sub function appearing/disappearing.

Dave.