From b9d4dcaa43ef66a91a44e33208fb4509b347c306 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Fri, 22 Dec 2017 11:29:36 +0000 Subject: [PATCH] nouveau: GP104: enable HDMI audio device function For some reason, many new platforms such as the Asus GL502VS boot without the BIOS activating the PCI HDMI audio function. This means that HDMI audio functionality is not available at all. On their forum, nvidia detailed the relevant bit in PCI extended configuration space which must be set in order for this device to become accessible. We must also explicitly probe the device function after setting this; Linux will not search for it automatically because it previously noted that the device advertised as being non-multifunction. Under Windows, the HDMI audio device appears (via this bit being set) when a HDMI cable is connected, and the reverse operation is done when the HDMI cable is disconnected. On Linux we start with the more simplistic approach of just enabling the HDMI audio device as long as a HDMI output connector is present. https://bugs.freedesktop.org/show_bug.cgi?id=75985 https://devtalk.nvidia.com/default/topic/1024022/linux/gtx-1060-no-audio-over-hdmi-only-hda-intel-detected-azalia Signed-off-by: Daniel Drake --- drivers/gpu/drm/nouveau/nouveau_drm.c | 68 +++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8d4a5be3b913..8cf16bba149f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -510,6 +510,67 @@ static int nouveau_drm_probe(struct pci_dev *pdev, return 0; } +static struct pci_dev * +nouveau_enable_hdmi_dev(struct nouveau_drm *drm) +{ + struct pci_dev *pdev = drm->dev->pdev; + struct pci_dev *ret = NULL; + u8 hdr_type; + u32 val; + struct drm_device *dev = drm->dev; + struct drm_connector *connector; + bool hdmi_found = false; + + /* For some reason, many BIOSes now boot with the PCI HDMI audio + * function disabled. It can be turned on in this magic register 0x488. + * + * https://devtalk.nvidia.com/default/topic/1024022/linux/gtx-1060-no-audio-over-hdmi-only-hda-intel-detected-azalia + * + * Restrict this tweak only to cards where we know that the HDMI + * audio device is controlled in this way. + */ + if (drm->client.device.info.chipset != 0x134) + return NULL; + + /* Only do this if we have a HDMI video output */ + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA || + connector->connector_type == DRM_MODE_CONNECTOR_HDMIB) { + hdmi_found = true; + break; + } + } + + if (!hdmi_found) + return NULL; + + pci_read_config_dword(pdev, 0x488, &val); + pci_write_config_dword(pdev, 0x488, val | 0x2000000); + + /* Now we would expect to find the HDMI audio function at devfn 1 so + * we can look for it there. We first check if this is a multifunction + * device in order to follow the behaviour of a normal PCI bus scan. + * + * We can't do this using pdev->multifunction as that will be a + * stale cached value, showing that it is not multifunction. + * However, re-reading PCI_HEADER_TYPE after the 0x488 magic above + * will now advertise a multifunction device on these affected systems. + */ + pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr_type); + NV_DEBUG(drm, "HDMI enable was %x, hdr_type %x\n", val, hdr_type); + + if (!(hdr_type & 0x80)) + return NULL; + + ret = pci_scan_single_device(pdev->bus, 1); + if (!ret) + return NULL; + + pci_assign_unassigned_bus_resources(pdev->bus); + pci_bus_add_devices(pdev->bus); + return pci_dev_get(ret); +} + #define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403 static void @@ -527,6 +588,9 @@ nouveau_get_hdmi_dev(struct nouveau_drm *drm) 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->hdmi_device = nouveau_enable_hdmi_dev(drm); + if (!drm->hdmi_device) { NV_DEBUG(drm, "hdmi device not found %d %d %d\n", pdev->bus->number, PCI_SLOT(pdev->devfn), 1); return; @@ -567,8 +631,6 @@ 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(drm); - /* workaround an odd issue on nvc1 by disabling the device's * nosnoop capability. hopefully won't cause issues until a * better fix is found - assuming there is one... @@ -590,6 +652,8 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) if (ret) goto fail_dispctor; + nouveau_get_hdmi_dev(drm); + if (dev->mode_config.num_crtc) { ret = nouveau_display_init(dev); if (ret) -- 2.14.1