From 8617cc3287916f66ee0ff8f2ae6cef1eb967cc90 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 29 May 2018 13:36:13 -0600 Subject: [PATCH 1/3] Add NVIDIA HDA initialization --- system76.c | 12 +++- system76_nv_hda.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 system76_nv_hda.c diff --git a/system76.c b/system76.c index 590e4c1..a6bf32d 100644 --- a/system76.c +++ b/system76.c @@ -34,7 +34,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -95,6 +97,7 @@ static int s76_wmbb(u32 method_id, u32 arg, u32 *retval) { #include "system76_input.c" #include "system76_kb-led.c" #include "system76_hwmon.c" +#include "system76_nv_hda.c" static void s76_wmi_notify(u32 value, void *context) { u32 event; @@ -164,6 +167,11 @@ static int s76_probe(struct platform_device *dev) { s76_hwmon_init(&dev->dev); #endif + err = nv_hda_init(&dev->dev); + if (unlikely(err)) { + S76_ERROR("Could not register NVIDIA audio device\n"); + } + err = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL); if (unlikely(ACPI_FAILURE(err))) { S76_ERROR("Could not register WMI notify handler (%0#6x)\n", err); @@ -184,10 +192,10 @@ static int s76_probe(struct platform_device *dev) { static int s76_remove(struct platform_device *dev) { wmi_remove_notify_handler(S76_EVENT_GUID); + nv_hda_exit(); #ifdef S76_HAS_HWMON s76_hwmon_fini(&dev->dev); #endif - s76_input_exit(); kb_led_exit(); ap_led_exit(); @@ -287,4 +295,4 @@ module_exit(s76_exit); MODULE_AUTHOR("Jeremy Soller "); MODULE_DESCRIPTION("System76 laptop driver"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.1"); +MODULE_VERSION("0.0.3"); diff --git a/system76_nv_hda.c b/system76_nv_hda.c new file mode 100644 index 0000000..634b8cb --- /dev/null +++ b/system76_nv_hda.c @@ -0,0 +1,173 @@ +/* Based on bbswitch, + * Copyright (C) 2011-2013 Bumblebee Project + * Author: Peter Wu + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +enum { + CARD_UNCHANGED = -1, + CARD_OFF = 0, + CARD_ON = 1, +}; + +static struct pci_dev *dis_dev; +static struct pci_dev *sub_dev = NULL; + +// Returns 1 if the card is disabled, 0 if enabled +static int is_card_disabled(void) { + //check for: 1.bit is set 2.sub-function is available. + u32 cfg_word; + struct pci_dev *tmp_dev = NULL; + + sub_dev = NULL; + + // read config word at 0x488 + pci_read_config_dword(dis_dev, 0x488, &cfg_word); + if ((cfg_word & 0x2000000)==0x2000000) { + //check for subdevice. read first config dword of sub function 1 + while ((tmp_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, tmp_dev)) != NULL) { + int pci_class = tmp_dev->class >> 8; + + if (pci_class != 0x403) + continue; + + if (tmp_dev->vendor == PCI_VENDOR_ID_NVIDIA) { + sub_dev = tmp_dev; + S76_INFO("Found nv audio device %s\n", dev_name(&tmp_dev->dev)); + } + } + + if (sub_dev == NULL) { + S76_INFO("No audio device found, unsetting config bit.\n"); + cfg_word|=0x2000000; + pci_write_config_dword(dis_dev, 0x488, cfg_word); + return 1; + } + + return 0; + } else { + return 1; + } +} + +static void nv_hda_off(void) { + u32 cfg_word; + if (is_card_disabled()) { + return; + } + + //remove device + pci_dev_put(sub_dev); + pci_stop_and_remove_bus_device(sub_dev); + + S76_INFO("NVIDIA audio: disabling\n"); + + //setting bit to turn off + pci_read_config_dword(dis_dev, 0x488, &cfg_word); + cfg_word&=0xfdffffff; + pci_write_config_dword(dis_dev, 0x488, cfg_word); +} + +static void nv_hda_on(void) { + u32 cfg_word; + u8 hdr_type; + + if (!is_card_disabled()) { + return; + } + + S76_INFO("NVIDIA audio: enabling\n"); + + // read,set bit, write config word at 0x488 + pci_read_config_dword(dis_dev, 0x488, &cfg_word); + cfg_word|=0x2000000; + pci_write_config_dword(dis_dev, 0x488, cfg_word); + + //pci_scan_single_device + pci_read_config_byte(dis_dev, PCI_HEADER_TYPE, &hdr_type); + + if (!(hdr_type & 0x80)) { + S76_ERROR("Not multifunction, no audio\n"); + return; + } + + sub_dev = pci_scan_single_device(dis_dev->bus, 1); + if (!sub_dev) { + S76_ERROR("No nv audio device found\n"); + return; + } + + S76_INFO("Audio found, adding\n"); + pci_assign_unassigned_bus_resources(dis_dev->bus); + pci_bus_add_devices(dis_dev->bus); + pci_dev_get(sub_dev); +} + +/* power bus so we can read PCI configuration space */ +static void dis_dev_get(void) { + if (dis_dev->bus && dis_dev->bus->self) { + pm_runtime_get_sync(&dis_dev->bus->self->dev); + } +} + +static void dis_dev_put(void) { + if (dis_dev->bus && dis_dev->bus->self) { + pm_runtime_put_sync(&dis_dev->bus->self->dev); + } +} + + +static int __init nv_hda_init(struct device *dev) { + struct pci_dev *pdev = NULL; + + while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) { + int pci_class = pdev->class >> 8; + + if (pci_class != PCI_CLASS_DISPLAY_VGA && pci_class != PCI_CLASS_DISPLAY_3D) { + continue; + } + + if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) { + dis_dev = pdev; + S76_INFO("NVIDIA device %s\n", dev_name(&pdev->dev)); + } + } + + if (dis_dev == NULL) { + S76_ERROR("No NVIDIA device found\n"); + return -ENODEV; + } + + dis_dev_get(); + + nv_hda_on(); + + S76_INFO("NVIDIA Audio %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on"); + + dis_dev_put(); + + return 0; +} + +static void __exit nv_hda_exit(void) { + dis_dev_get(); + + nv_hda_off(); + + pr_info("NVIDIA Audio %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on"); + + dis_dev_put(); + +} From fa50365f29024c3c003e020711ffae2ac887e8c7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 29 May 2018 14:03:23 -0600 Subject: [PATCH 2/3] Adjust messages --- system76_nv_hda.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/system76_nv_hda.c b/system76_nv_hda.c index 634b8cb..73a8c9f 100644 --- a/system76_nv_hda.c +++ b/system76_nv_hda.c @@ -45,12 +45,12 @@ static int is_card_disabled(void) { if (tmp_dev->vendor == PCI_VENDOR_ID_NVIDIA) { sub_dev = tmp_dev; - S76_INFO("Found nv audio device %s\n", dev_name(&tmp_dev->dev)); + S76_INFO("Found NVIDIA audio device %s\n", dev_name(&tmp_dev->dev)); } } if (sub_dev == NULL) { - S76_INFO("No audio device found, unsetting config bit.\n"); + S76_INFO("No NVIDIA audio device found, unsetting config bit.\n"); cfg_word|=0x2000000; pci_write_config_dword(dis_dev, 0x488, cfg_word); return 1; @@ -99,17 +99,17 @@ static void nv_hda_on(void) { pci_read_config_byte(dis_dev, PCI_HEADER_TYPE, &hdr_type); if (!(hdr_type & 0x80)) { - S76_ERROR("Not multifunction, no audio\n"); + S76_ERROR("NVIDIA not multifunction, no audio\n"); return; } sub_dev = pci_scan_single_device(dis_dev->bus, 1); if (!sub_dev) { - S76_ERROR("No nv audio device found\n"); + S76_ERROR("No NVIDIA audio device found\n"); return; } - S76_INFO("Audio found, adding\n"); + S76_INFO("NVIDIA audio found, adding\n"); pci_assign_unassigned_bus_resources(dis_dev->bus); pci_bus_add_devices(dis_dev->bus); pci_dev_get(sub_dev); From 37653308b1e8c76c06a303e20af8692e1e6e6da6 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 30 May 2018 07:19:43 -0600 Subject: [PATCH 3/3] Load system76-dkms in initrd --- debian/system76-dkms.install | 2 +- debian/system76-dkms.triggers | 1 + usr/share/initramfs-tools/hooks/system76-dkms | 50 +++++++++++++++++++ .../modules.d}/system76-dkms.conf | 0 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 debian/system76-dkms.triggers create mode 100644 usr/share/initramfs-tools/hooks/system76-dkms rename {etc/modules-load.d => usr/share/initramfs-tools/modules.d}/system76-dkms.conf (100%) diff --git a/debian/system76-dkms.install b/debian/system76-dkms.install index 3db00e7..336a893 100644 --- a/debian/system76-dkms.install +++ b/debian/system76-dkms.install @@ -1 +1 @@ -etc/modules-load.d +usr/share/initramfs-tools diff --git a/debian/system76-dkms.triggers b/debian/system76-dkms.triggers new file mode 100644 index 0000000..6c9f454 --- /dev/null +++ b/debian/system76-dkms.triggers @@ -0,0 +1 @@ +activate update-initramfs diff --git a/usr/share/initramfs-tools/hooks/system76-dkms b/usr/share/initramfs-tools/hooks/system76-dkms new file mode 100644 index 0000000..0465b60 --- /dev/null +++ b/usr/share/initramfs-tools/hooks/system76-dkms @@ -0,0 +1,50 @@ +#!/bin/sh + +OPTION=FRAMEBUFFER +PREREQ="" + +prereqs() +{ + echo "$PREREQ" +} + +case $1 in +# get pre-requisites +prereqs) + prereqs + exit 0 + ;; +esac + +. /usr/share/initramfs-tools/hook-functions + + +# Copy entire subtrees to the initramfs as long as they match a pattern +copy_modules_dir_filter() +{ + local kmod exclude + local dir="$1" + shift + local pattern="$1" + shift + + if ! [ -d "${MODULESDIR}/${dir}" ]; then + return; + fi + if [ "${verbose}" = "y" ]; then + echo "Copying module directory ${dir}" + if [ $# -ge 1 ]; then + echo "(excluding $*)" + fi + fi + while [ $# -ge 1 ]; do + exclude="${exclude:-} -name $1 -prune -o " + shift + done + for kmod in $(find "${MODULESDIR}/${dir}" ${exclude:-} -name "$pattern*.ko" -print); do + manual_add_modules $(basename ${kmod} .ko) + done +} + +copy_modules_dir_filter updates/dkms system76 + diff --git a/etc/modules-load.d/system76-dkms.conf b/usr/share/initramfs-tools/modules.d/system76-dkms.conf similarity index 100% rename from etc/modules-load.d/system76-dkms.conf rename to usr/share/initramfs-tools/modules.d/system76-dkms.conf