Dave Airlie
2013-Jul-24  07:13 UTC
[Nouveau] [PATCH] [RFC] 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.
Signed-off-by: Dave Airlie <airlied at redhat.com>
---
 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(+)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c
b/drivers/gpu/drm/nouveau/nouveau_drm.c
index 6197266..12a6240 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.c
@@ -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;
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h
b/drivers/gpu/drm/nouveau/nouveau_drm.h
index 41ff7e0..f276e37 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drm.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drm.h
@@ -129,6 +129,8 @@ struct nouveau_drm {
 
 	/* power management */
 	struct nouveau_pm *pm;
+
+	struct pci_dev *hdmi_device;
 };
 
 static inline struct nouveau_drm *
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c
b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 25d3495..d8af49c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -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");
-- 
1.8.2.1
Paul Menzel
2013-Jul-24  08:45 UTC
[Nouveau] [PATCH] [RFC] drm/nouveau: bring back hdmi audio device after switcheroo power down
Am Mittwoch, den 24.07.2013, 17:13 +1000 schrieb 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 ofkeep*s*> 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. > > Signed-off-by: Dave Airlie <airlied at redhat.com> > --- > 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(+) > > diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c > index 6197266..12a6240 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_drm.c > +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c > @@ -296,6 +296,31 @@ static int nouveau_drm_probe(struct pci_dev *pdev, > return 0; > } > > +#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403Should that go into some header?> + > +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>?> + 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?> + 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; > } > diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h > index 41ff7e0..f276e37 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_drm.h > +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h > @@ -129,6 +129,8 @@ struct nouveau_drm { > > /* power management */ > struct nouveau_pm *pm; > + > + struct pci_dev *hdmi_device; > }; > > static inline struct nouveau_drm * > diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c > index 25d3495..d8af49c 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_vga.c > +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c > @@ -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);Use a define for this nevertheless?> +} > + > +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");Otherwise this looks good, Thanks, Paul -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 198 bytes Desc: This is a digitally signed message part URL: <http://lists.freedesktop.org/archives/nouveau/attachments/20130724/3c7d7caf/attachment.pgp>
Pasi Kärkkäinen
2013-Jul-24  12:06 UTC
[Nouveau] [PATCH] [RFC] drm/nouveau: bring back hdmi audio device after switcheroo power down
On Wed, Jul 24, 2013 at 05:13:42PM +1000, Dave Airlie wrote:> 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
Maarten Lankhorst
2013-Jul-24  13:38 UTC
[Nouveau] [PATCH] [RFC] drm/nouveau: bring back hdmi audio device after switcheroo power down
Op 24-07-13 09:13, Dave Airlie schreef:> 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. > > Signed-off-by: Dave Airlie <airlied at redhat.com> > --- > 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(+) > > diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c > index 6197266..12a6240 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_drm.c > +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c > @@ -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; > } > diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h > index 41ff7e0..f276e37 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_drm.h > +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h > @@ -129,6 +129,8 @@ struct nouveau_drm { > > /* power management */ > struct nouveau_pm *pm; > + > + struct pci_dev *hdmi_device; > }; > > static inline struct nouveau_drm * > diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c > index 25d3495..d8af49c 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_vga.c > +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c > @@ -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); > +}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
Dave Airlie
2013-Jul-25  00:05 UTC
[Nouveau] [PATCH] [RFC] drm/nouveau: bring back hdmi audio device after switcheroo power down
On Wed, Jul 24, 2013 at 11:38 PM, Maarten Lankhorst <maarten.lankhorst at canonical.com> wrote:> Op 24-07-13 09:13, Dave Airlie schreef: >> 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. >> >> Signed-off-by: Dave Airlie <airlied at redhat.com> >> --- >> 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(+) >> >> diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c >> index 6197266..12a6240 100644 >> --- a/drivers/gpu/drm/nouveau/nouveau_drm.c >> +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c >> @@ -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; >> } >> diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h >> index 41ff7e0..f276e37 100644 >> --- a/drivers/gpu/drm/nouveau/nouveau_drm.h >> +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h >> @@ -129,6 +129,8 @@ struct nouveau_drm { >> >> /* power management */ >> struct nouveau_pm *pm; >> + >> + struct pci_dev *hdmi_device; >> }; >> >> static inline struct nouveau_drm * >> diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c >> index 25d3495..d8af49c 100644 >> --- a/drivers/gpu/drm/nouveau/nouveau_vga.c >> +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c >> @@ -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); >> +} > use nv_mask(dev, 0x88488, 0x02000000, 0x02000000);much nicer,> > 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.
Seemingly Similar Threads
- [PATCH] [RFC] drm/nouveau: bring back hdmi audio device after switcheroo power down
 - [PATCH 0/7] Modernize vga_switcheroo by using device link for HDA
 - [PATCH v2 0/7] Modernize vga_switcheroo by using device link for HDA
 - [PATCH 1/2] drm/nouveau: make hdmi device finding failure prints debug level
 - [PATCH] drm: compute runpm on load, don't register autosuspend for non-runpm