From fe364a7d95c24e07e9b3f2ab917f01d6d8330bba Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Jul 2021 14:39:40 +0300 Subject: dmaengine: dw: Program xBAR hardware for Elkhart Lake Intel Elkhart Lake PSE DMA implementation is integrated with crossbar IP in order to serve more hardware than there are DMA request lines available. Due to this, program xBAR hardware to make flexible support of PSE peripheral. The Device-to-Device has not been tested and it's not supported by DMA Engine, but it's left in the code for the sake of documenting hardware features. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210712113940.42753-1-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-dw.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index b34a094b2258..b11b0c8bc5da 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -52,6 +52,7 @@ struct dw_dma_slave { * @max_burst: Maximum value of burst transaction size supported by hardware * per channel (in units of CTL.SRC_TR_WIDTH/CTL.DST_TR_WIDTH). * @protctl: Protection control signals setting per channel. + * @quirks: Optional platform quirks. */ struct dw_dma_platform_data { unsigned int nr_channels; @@ -71,6 +72,8 @@ struct dw_dma_platform_data { #define CHAN_PROTCTL_CACHEABLE BIT(2) #define CHAN_PROTCTL_MASK GENMASK(2, 0) unsigned char protctl; +#define DW_DMA_QUIRK_XBAR_PRESENT BIT(0) + unsigned int quirks; }; #endif /* _PLATFORM_DATA_DMA_DW_H */ -- cgit v1.2.3 From cf0a95659e659d36838e56cc439d3986dcb46870 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Jul 2021 22:34:50 +0300 Subject: clk: x86: Rename clk-lpt to more specific clk-lpss-atom The LPT stands for Lynxpoint PCH. However the driver is used on a few Intel Atom SoCs. Rename it to reflect this in a way how another clock driver, i.e. clk-pmc-atom, is called. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20210722193450.35321-1-andriy.shevchenko@linux.intel.com Acked-by: Rafael J. Wysocki Signed-off-by: Stephen Boyd --- drivers/acpi/acpi_lpss.c | 6 ++-- drivers/clk/x86/Makefile | 2 +- drivers/clk/x86/clk-lpss-atom.c | 47 ++++++++++++++++++++++++++++++ drivers/clk/x86/clk-lpt.c | 47 ------------------------------ include/linux/platform_data/x86/clk-lpss.h | 2 +- 5 files changed, 53 insertions(+), 51 deletions(-) create mode 100644 drivers/clk/x86/clk-lpss-atom.c delete mode 100644 drivers/clk/x86/clk-lpt.c (limited to 'include/linux/platform_data') diff --git a/drivers/acpi/acpi_lpss.c b/drivers/acpi/acpi_lpss.c index 894b7e6ae144..7f163074e4e4 100644 --- a/drivers/acpi/acpi_lpss.c +++ b/drivers/acpi/acpi_lpss.c @@ -385,7 +385,9 @@ static struct platform_device *lpss_clk_dev; static inline void lpt_register_clock_device(void) { - lpss_clk_dev = platform_device_register_simple("clk-lpt", -1, NULL, 0); + lpss_clk_dev = platform_device_register_simple("clk-lpss-atom", + PLATFORM_DEVID_NONE, + NULL, 0); } static int register_device_clock(struct acpi_device *adev, @@ -1337,7 +1339,7 @@ void __init acpi_lpss_init(void) const struct x86_cpu_id *id; int ret; - ret = lpt_clk_init(); + ret = lpss_atom_clk_init(); if (ret) return; diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile index 18564efdc651..1244c4e568ff 100644 --- a/drivers/clk/x86/Makefile +++ b/drivers/clk/x86/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o obj-$(CONFIG_X86_AMD_PLATFORM_DEVICE) += clk-fch.o -clk-x86-lpss-objs := clk-lpt.o +clk-x86-lpss-y := clk-lpss-atom.o obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o obj-$(CONFIG_CLK_LGM_CGU) += clk-cgu.o clk-cgu-pll.o clk-lgm.o diff --git a/drivers/clk/x86/clk-lpss-atom.c b/drivers/clk/x86/clk-lpss-atom.c new file mode 100644 index 000000000000..aa9d0bb98f8b --- /dev/null +++ b/drivers/clk/x86/clk-lpss-atom.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel Low Power Subsystem clocks. + * + * Copyright (C) 2013, Intel Corporation + * Authors: Mika Westerberg + * Heikki Krogerus + */ + +#include +#include +#include +#include +#include + +static int lpss_atom_clk_probe(struct platform_device *pdev) +{ + struct lpss_clk_data *drvdata; + struct clk *clk; + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + /* LPSS free running clock */ + drvdata->name = "lpss_clk"; + clk = clk_register_fixed_rate(&pdev->dev, drvdata->name, NULL, + 0, 100000000); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + drvdata->clk = clk; + platform_set_drvdata(pdev, drvdata); + return 0; +} + +static struct platform_driver lpss_atom_clk_driver = { + .driver = { + .name = "clk-lpss-atom", + }, + .probe = lpss_atom_clk_probe, +}; + +int __init lpss_atom_clk_init(void) +{ + return platform_driver_register(&lpss_atom_clk_driver); +} diff --git a/drivers/clk/x86/clk-lpt.c b/drivers/clk/x86/clk-lpt.c deleted file mode 100644 index fbe9fd3ed948..000000000000 --- a/drivers/clk/x86/clk-lpt.c +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Low Power Subsystem clocks. - * - * Copyright (C) 2013, Intel Corporation - * Authors: Mika Westerberg - * Heikki Krogerus - */ - -#include -#include -#include -#include -#include - -static int lpt_clk_probe(struct platform_device *pdev) -{ - struct lpss_clk_data *drvdata; - struct clk *clk; - - drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - - /* LPSS free running clock */ - drvdata->name = "lpss_clk"; - clk = clk_register_fixed_rate(&pdev->dev, drvdata->name, NULL, - 0, 100000000); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - drvdata->clk = clk; - platform_set_drvdata(pdev, drvdata); - return 0; -} - -static struct platform_driver lpt_clk_driver = { - .driver = { - .name = "clk-lpt", - }, - .probe = lpt_clk_probe, -}; - -int __init lpt_clk_init(void) -{ - return platform_driver_register(&lpt_clk_driver); -} diff --git a/include/linux/platform_data/x86/clk-lpss.h b/include/linux/platform_data/x86/clk-lpss.h index 207e1a317800..41df326583f9 100644 --- a/include/linux/platform_data/x86/clk-lpss.h +++ b/include/linux/platform_data/x86/clk-lpss.h @@ -15,6 +15,6 @@ struct lpss_clk_data { struct clk *clk; }; -extern int lpt_clk_init(void); +extern int lpss_atom_clk_init(void); #endif /* __CLK_LPSS_H */ -- cgit v1.2.3 From 08bf54fcf5ca87328541e035090c6a85c8e064f4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 2 Aug 2021 21:43:54 +0300 Subject: dmaengine: dw: Convert members to u32 in platform data u32 is a type that is used for properties retrieval from DT. With the type change it allows to clean up properties reading routine. While at it, order the fields in way how they are parsed. Signed-off-by: Andy Shevchenko Reviewed-by: Serge Semin Tested-by: Serge Semin Link: https://lore.kernel.org/r/20210802184355.49879-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/platform_data/dma-dw.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux/platform_data') diff --git a/include/linux/platform_data/dma-dw.h b/include/linux/platform_data/dma-dw.h index b11b0c8bc5da..860ba4bc5ead 100644 --- a/include/linux/platform_data/dma-dw.h +++ b/include/linux/platform_data/dma-dw.h @@ -41,11 +41,11 @@ struct dw_dma_slave { /** * struct dw_dma_platform_data - Controller configuration parameters + * @nr_masters: Number of AHB masters supported by the controller * @nr_channels: Number of channels supported by hardware (max 8) * @chan_allocation_order: Allocate channels starting from 0 or 7 * @chan_priority: Set channel priority increasing from 0 to 7 or 7 to 0. * @block_size: Maximum block size supported by the controller - * @nr_masters: Number of AHB masters supported by the controller * @data_width: Maximum data width supported by hardware per AHB master * (in bytes, power of 2) * @multi_block: Multi block transfers supported by hardware per channel. @@ -55,25 +55,25 @@ struct dw_dma_slave { * @quirks: Optional platform quirks. */ struct dw_dma_platform_data { - unsigned int nr_channels; + u32 nr_masters; + u32 nr_channels; #define CHAN_ALLOCATION_ASCENDING 0 /* zero to seven */ #define CHAN_ALLOCATION_DESCENDING 1 /* seven to zero */ - unsigned char chan_allocation_order; + u32 chan_allocation_order; #define CHAN_PRIORITY_ASCENDING 0 /* chan0 highest */ #define CHAN_PRIORITY_DESCENDING 1 /* chan7 highest */ - unsigned char chan_priority; - unsigned int block_size; - unsigned char nr_masters; - unsigned char data_width[DW_DMA_MAX_NR_MASTERS]; - unsigned char multi_block[DW_DMA_MAX_NR_CHANNELS]; + u32 chan_priority; + u32 block_size; + u32 data_width[DW_DMA_MAX_NR_MASTERS]; + u32 multi_block[DW_DMA_MAX_NR_CHANNELS]; u32 max_burst[DW_DMA_MAX_NR_CHANNELS]; #define CHAN_PROTCTL_PRIVILEGED BIT(0) #define CHAN_PROTCTL_BUFFERABLE BIT(1) #define CHAN_PROTCTL_CACHEABLE BIT(2) #define CHAN_PROTCTL_MASK GENMASK(2, 0) - unsigned char protctl; + u32 protctl; #define DW_DMA_QUIRK_XBAR_PRESENT BIT(0) - unsigned int quirks; + u32 quirks; }; #endif /* _PLATFORM_DATA_DMA_DW_H */ -- cgit v1.2.3 From c1b291e96a6d27ac83938596829086945ff8a36e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 4 Aug 2021 19:00:16 +0300 Subject: gpio: dwapb: Unify ACPI enumeration checks in get_irq() and configure_irqs() Shared IRQ is only enabled for ACPI enumeration, there is no need to have a special flag for that, since we simple can test if device has been enumerated by ACPI. This unifies the checks in dwapb_get_irq() and dwapb_configure_irqs(). Signed-off-by: Andy Shevchenko Acked-by: Lee Jones Acked-by: Serge Semin Tested-by: Serge Semin --- drivers/gpio/gpio-dwapb.c | 24 ++++++++++++------------ drivers/mfd/intel_quark_i2c_gpio.c | 1 - include/linux/platform_data/gpio-dwapb.h | 1 - 3 files changed, 12 insertions(+), 14 deletions(-) (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 3eb13d6d31ef..4c7153cb646c 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -436,21 +436,17 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio, pirq->irqchip.irq_set_wake = dwapb_irq_set_wake; #endif - if (!pp->irq_shared) { - girq->num_parents = pirq->nr_irqs; - girq->parents = pirq->irq; - girq->parent_handler_data = gpio; - girq->parent_handler = dwapb_irq_handler; - } else { - /* This will let us handle the parent IRQ in the driver */ + /* + * Intel ACPI-based platforms mostly have the DesignWare APB GPIO + * IRQ lane shared between several devices. In that case the parental + * IRQ has to be handled in the shared way so to be properly delivered + * to all the connected devices. + */ + if (has_acpi_companion(gpio->dev)) { girq->num_parents = 0; girq->parents = NULL; girq->parent_handler = NULL; - /* - * Request a shared IRQ since where MFD would have devices - * using the same irq pin - */ err = devm_request_irq(gpio->dev, pp->irq[0], dwapb_irq_handler_mfd, IRQF_SHARED, DWAPB_DRIVER_NAME, gpio); @@ -458,6 +454,11 @@ static void dwapb_configure_irqs(struct dwapb_gpio *gpio, dev_err(gpio->dev, "error requesting IRQ\n"); goto err_kfree_pirq; } + } else { + girq->num_parents = pirq->nr_irqs; + girq->parents = pirq->irq; + girq->parent_handler_data = gpio; + girq->parent_handler = dwapb_irq_handler; } girq->chip = &pirq->irqchip; @@ -581,7 +582,6 @@ static struct dwapb_platform_data *dwapb_gpio_get_pdata(struct device *dev) pp->ngpio = DWAPB_MAX_GPIOS; } - pp->irq_shared = false; pp->gpio_base = -1; /* diff --git a/drivers/mfd/intel_quark_i2c_gpio.c b/drivers/mfd/intel_quark_i2c_gpio.c index 01935ae4e9e1..a43993e38b6e 100644 --- a/drivers/mfd/intel_quark_i2c_gpio.c +++ b/drivers/mfd/intel_quark_i2c_gpio.c @@ -227,7 +227,6 @@ static int intel_quark_gpio_setup(struct pci_dev *pdev) pdata->properties->ngpio = INTEL_QUARK_MFD_NGPIO; pdata->properties->gpio_base = INTEL_QUARK_MFD_GPIO_BASE; pdata->properties->irq[0] = pci_irq_vector(pdev, 0); - pdata->properties->irq_shared = true; cell->platform_data = pdata; cell->pdata_size = sizeof(*pdata); diff --git a/include/linux/platform_data/gpio-dwapb.h b/include/linux/platform_data/gpio-dwapb.h index 0aa5c6720259..535e5ed549d9 100644 --- a/include/linux/platform_data/gpio-dwapb.h +++ b/include/linux/platform_data/gpio-dwapb.h @@ -14,7 +14,6 @@ struct dwapb_port_property { unsigned int ngpio; unsigned int gpio_base; int irq[DWAPB_MAX_GPIOS]; - bool irq_shared; }; struct dwapb_platform_data { -- cgit v1.2.3 From 5111c2b6b0194b509f47e6338c4deeeb4497bda8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 4 Aug 2021 19:00:19 +0300 Subject: gpio: dwapb: Get rid of legacy platform data Platform data is a legacy interface to supply device properties to the driver. In this case we don't have anymore in-kernel users for it. Just remove it for good. Signed-off-by: Andy Shevchenko Acked-by: Serge Semin Tested-by: Serge Semin --- drivers/gpio/gpio-dwapb.c | 28 ++++++++++++++++++---------- include/linux/platform_data/gpio-dwapb.h | 24 ------------------------ 2 files changed, 18 insertions(+), 34 deletions(-) delete mode 100644 include/linux/platform_data/gpio-dwapb.h (limited to 'include/linux/platform_data') diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index 674e91e69cc5..f98fa33e1679 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,7 @@ #define DWAPB_DRIVER_NAME "gpio-dwapb" #define DWAPB_MAX_PORTS 4 +#define DWAPB_MAX_GPIOS 32 #define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */ #define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */ @@ -65,6 +65,19 @@ struct dwapb_gpio; +struct dwapb_port_property { + struct fwnode_handle *fwnode; + unsigned int idx; + unsigned int ngpio; + unsigned int gpio_base; + int irq[DWAPB_MAX_GPIOS]; +}; + +struct dwapb_platform_data { + struct dwapb_port_property *properties; + unsigned int nports; +}; + #ifdef CONFIG_PM_SLEEP /* Store GPIO context across system-wide suspend/resume transitions */ struct dwapb_context { @@ -674,17 +687,12 @@ static int dwapb_gpio_probe(struct platform_device *pdev) unsigned int i; struct dwapb_gpio *gpio; int err; + struct dwapb_platform_data *pdata; struct device *dev = &pdev->dev; - struct dwapb_platform_data *pdata = dev_get_platdata(dev); - - if (!pdata) { - pdata = dwapb_gpio_get_pdata(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); - } - if (!pdata->nports) - return -ENODEV; + pdata = dwapb_gpio_get_pdata(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); if (!gpio) diff --git a/include/linux/platform_data/gpio-dwapb.h b/include/linux/platform_data/gpio-dwapb.h deleted file mode 100644 index 535e5ed549d9..000000000000 --- a/include/linux/platform_data/gpio-dwapb.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright(c) 2014 Intel Corporation. - */ - -#ifndef GPIO_DW_APB_H -#define GPIO_DW_APB_H - -#define DWAPB_MAX_GPIOS 32 - -struct dwapb_port_property { - struct fwnode_handle *fwnode; - unsigned int idx; - unsigned int ngpio; - unsigned int gpio_base; - int irq[DWAPB_MAX_GPIOS]; -}; - -struct dwapb_platform_data { - struct dwapb_port_property *properties; - unsigned int nports; -}; - -#endif -- cgit v1.2.3 From ca91ea34778f9b2a44a391b10164bcd73b4b0f25 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sat, 7 Aug 2021 14:36:54 +1200 Subject: asus-wmi: Add panel overdrive functionality Some ASUS ROG laptops have the ability to drive the display panel a higher rate to eliminate or reduce ghosting. Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20210807023656.25020-2-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 92 ++++++++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 1 + 2 files changed, 93 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ebaeb7bb80f5..cbf91a9134fd 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -216,6 +216,9 @@ struct asus_wmi { // The RSOC controls the maximum charging percentage. bool battery_rsoc_available; + bool panel_overdrive_available; + bool panel_overdrive; + struct hotplug_slot hotplug_slot; struct mutex hotplug_lock; struct mutex wmi_lock; @@ -1221,6 +1224,87 @@ exit: return result; } +/* Panel Overdrive ************************************************************/ +static int panel_od_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->panel_overdrive_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_PANEL_OD, &result); + if (err) { + if (err == -ENODEV) + return 0; + return err; + } + + if (result & ASUS_WMI_DSTS_PRESENCE_BIT) { + asus->panel_overdrive_available = true; + asus->panel_overdrive = result & ASUS_WMI_DSTS_STATUS_BIT; + } + + return 0; +} + +static int panel_od_write(struct asus_wmi *asus) +{ + u32 retval; + u8 value; + int err; + + /* Don't rely on type conversion */ + value = asus->panel_overdrive ? 1 : 0; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_PANEL_OD, value, &retval); + + if (err) { + pr_warn("Failed to set panel overdrive: %d\n", err); + return err; + } + + if (retval > 1 || retval < 0) { + pr_warn("Failed to set panel overdrive (retval): 0x%x\n", retval); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "panel_od"); + + return 0; +} + +static ssize_t panel_od_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", asus->panel_overdrive); +} + +static ssize_t panel_od_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool overdrive; + int result; + + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = kstrtobool(buf, &overdrive); + if (result) + return result; + + asus->panel_overdrive = overdrive; + result = panel_od_write(asus); + + if (result) + return result; + + return count; +} + +static DEVICE_ATTR_RW(panel_od); + /* Quirks *********************************************************************/ static void asus_wmi_set_xusb2pr(struct asus_wmi *asus) @@ -2332,6 +2416,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, &dev_attr_throttle_thermal_policy.attr, + &dev_attr_panel_od.attr, NULL }; @@ -2357,6 +2442,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, ok = asus->fan_boost_mode_available; else if (attr == &dev_attr_throttle_thermal_policy.attr) ok = asus->throttle_thermal_policy_available; + else if (attr == &dev_attr_panel_od.attr) + ok = asus->panel_overdrive_available; if (devid != -1) ok = !(asus_wmi_get_devstate_simple(asus, devid) < 0); @@ -2622,6 +2709,10 @@ static int asus_wmi_add(struct platform_device *pdev) else throttle_thermal_policy_set_default(asus); + err = panel_od_check_present(asus); + if (err) + goto fail_panel_od; + err = asus_wmi_sysfs_init(asus->platform_device); if (err) goto fail_sysfs; @@ -2709,6 +2800,7 @@ fail_sysfs: fail_throttle_thermal_policy: fail_fan_boost_mode: fail_platform: +fail_panel_od: kfree(asus); return err; } diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 2f274cf52805..428aea701c7b 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -61,6 +61,7 @@ #define ASUS_WMI_DEVID_THROTTLE_THERMAL_POLICY 0x00120075 /* Misc */ +#define ASUS_WMI_DEVID_PANEL_OD 0x00050019 #define ASUS_WMI_DEVID_CAMERA 0x00060013 #define ASUS_WMI_DEVID_LID_FLIP 0x00060062 -- cgit v1.2.3 From 98829e84dc67630efb7de675f0a70066620468a3 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sat, 7 Aug 2021 14:36:55 +1200 Subject: asus-wmi: Add dgpu disable method In Windows the ASUS Armory Crate program can enable or disable the dGPU via a WMI call. This functions much the same as various Linux methods in software where the dGPU is removed from the device tree. However the WMI call saves the state of dGPU (enabled or not) and this then changes the dGPU visibility in Linux with no way for Linux users to re-enable it. We expose the WMI method so users can see and change the dGPU ACPI state. Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20210807023656.25020-3-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 98 ++++++++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 3 + 2 files changed, 101 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index cbf91a9134fd..bee22a12bf3d 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -210,6 +210,9 @@ struct asus_wmi { u8 fan_boost_mode_mask; u8 fan_boost_mode; + bool dgpu_disable_available; + bool dgpu_disable; + bool throttle_thermal_policy_available; u8 throttle_thermal_policy_mode; @@ -427,6 +430,93 @@ static void lid_flip_tablet_mode_get_state(struct asus_wmi *asus) } } +/* dGPU ********************************************************************/ +static int dgpu_disable_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->dgpu_disable_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_DGPU, &result); + if (err) { + if (err == -ENODEV) + return 0; + return err; + } + + if (result & ASUS_WMI_DSTS_PRESENCE_BIT) { + asus->dgpu_disable_available = true; + asus->dgpu_disable = result & ASUS_WMI_DSTS_STATUS_BIT; + } + + return 0; +} + +static int dgpu_disable_write(struct asus_wmi *asus) +{ + u32 retval; + u8 value; + int err; + + /* Don't rely on type conversion */ + value = asus->dgpu_disable ? 1 : 0; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_DGPU, value, &retval); + if (err) { + pr_warn("Failed to set dgpu disable: %d\n", err); + return err; + } + + if (retval > 1 || retval < 0) { + pr_warn("Failed to set dgpu disable (retval): 0x%x\n", retval); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "dgpu_disable"); + + return 0; +} + +static ssize_t dgpu_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + u8 mode = asus->dgpu_disable; + + return sysfs_emit(buf, "%d\n", mode); +} + +/* + * A user may be required to store the value twice, typcial store first, then + * rescan PCI bus to activate power, then store a second time to save correctly. + * The reason for this is that an extra code path in the ACPI is enabled when + * the device and bus are powered. + */ +static ssize_t dgpu_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool disable; + int result; + + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = kstrtobool(buf, &disable); + if (result) + return result; + + asus->dgpu_disable = disable; + + result = dgpu_disable_write(asus); + if (result) + return result; + + return count; +} + +static DEVICE_ATTR_RW(dgpu_disable); + /* Battery ********************************************************************/ /* The battery maximum charging percentage */ @@ -2412,6 +2502,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_camera.attr, &dev_attr_cardr.attr, &dev_attr_touchpad.attr, + &dev_attr_dgpu_disable.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, &dev_attr_fan_boost_mode.attr, @@ -2438,6 +2529,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_LID_RESUME; else if (attr == &dev_attr_als_enable.attr) devid = ASUS_WMI_DEVID_ALS_ENABLE; + else if (attr == &dev_attr_dgpu_disable.attr) + ok = asus->dgpu_disable_available; else if (attr == &dev_attr_fan_boost_mode.attr) ok = asus->fan_boost_mode_available; else if (attr == &dev_attr_throttle_thermal_policy.attr) @@ -2699,6 +2792,10 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; + err = dgpu_disable_check_present(asus); + if (err) + goto fail_dgpu_disable; + err = fan_boost_mode_check_present(asus); if (err) goto fail_fan_boost_mode; @@ -2799,6 +2896,7 @@ fail_input: fail_sysfs: fail_throttle_thermal_policy: fail_fan_boost_mode: +fail_dgpu_disable: fail_platform: fail_panel_od: kfree(asus); diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 428aea701c7b..a528f9d0e4b7 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -90,6 +90,9 @@ /* Keyboard dock */ #define ASUS_WMI_DEVID_KBD_DOCK 0x00120063 +/* dgpu on/off */ +#define ASUS_WMI_DEVID_DGPU 0x00090020 + /* DSTS masks */ #define ASUS_WMI_DSTS_STATUS_BIT 0x00000001 #define ASUS_WMI_DSTS_UNKNOWN_BIT 0x00000002 -- cgit v1.2.3 From 382b91db8044669d254006df799df9d85d4ad891 Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Sat, 7 Aug 2021 14:36:56 +1200 Subject: asus-wmi: Add egpu enable method The X13 Flow laptops can utilise an external GPU. This requires toggling an ACPI method which will first disable the internal dGPU, and then enable the eGPU. Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20210807023656.25020-4-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-wmi.c | 99 ++++++++++++++++++++++++++++++ include/linux/platform_data/x86/asus-wmi.h | 3 + 2 files changed, 102 insertions(+) (limited to 'include/linux/platform_data') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index bee22a12bf3d..90a6a0d00deb 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -210,6 +210,9 @@ struct asus_wmi { u8 fan_boost_mode_mask; u8 fan_boost_mode; + bool egpu_enable_available; // 0 = enable + bool egpu_enable; + bool dgpu_disable_available; bool dgpu_disable; @@ -517,6 +520,94 @@ static ssize_t dgpu_disable_store(struct device *dev, static DEVICE_ATTR_RW(dgpu_disable); +/* eGPU ********************************************************************/ +static int egpu_enable_check_present(struct asus_wmi *asus) +{ + u32 result; + int err; + + asus->egpu_enable_available = false; + + err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_EGPU, &result); + if (err) { + if (err == -ENODEV) + return 0; + return err; + } + + if (result & ASUS_WMI_DSTS_PRESENCE_BIT) { + asus->egpu_enable_available = true; + asus->egpu_enable = result & ASUS_WMI_DSTS_STATUS_BIT; + } + + return 0; +} + +static int egpu_enable_write(struct asus_wmi *asus) +{ + u32 retval; + u8 value; + int err; + + /* Don't rely on type conversion */ + value = asus->egpu_enable ? 1 : 0; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_EGPU, value, &retval); + + if (err) { + pr_warn("Failed to set egpu disable: %d\n", err); + return err; + } + + if (retval > 1 || retval < 0) { + pr_warn("Failed to set egpu disable (retval): 0x%x\n", retval); + return -EIO; + } + + sysfs_notify(&asus->platform_device->dev.kobj, NULL, "egpu_enable"); + + return 0; +} + +static ssize_t egpu_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct asus_wmi *asus = dev_get_drvdata(dev); + bool mode = asus->egpu_enable; + + return sysfs_emit(buf, "%d\n", mode); +} + +/* The ACPI call to enable the eGPU also disables the internal dGPU */ +static ssize_t egpu_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + bool enable; + int result; + + struct asus_wmi *asus = dev_get_drvdata(dev); + + result = kstrtobool(buf, &enable); + if (result) + return result; + + asus->egpu_enable = enable; + + result = egpu_enable_write(asus); + if (result) + return result; + + /* Ensure that the kernel status of dgpu is updated */ + result = dgpu_disable_check_present(asus); + if (result) + return result; + + return count; +} + +static DEVICE_ATTR_RW(egpu_enable); + /* Battery ********************************************************************/ /* The battery maximum charging percentage */ @@ -2502,6 +2593,7 @@ static struct attribute *platform_attributes[] = { &dev_attr_camera.attr, &dev_attr_cardr.attr, &dev_attr_touchpad.attr, + &dev_attr_egpu_enable.attr, &dev_attr_dgpu_disable.attr, &dev_attr_lid_resume.attr, &dev_attr_als_enable.attr, @@ -2529,6 +2621,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj, devid = ASUS_WMI_DEVID_LID_RESUME; else if (attr == &dev_attr_als_enable.attr) devid = ASUS_WMI_DEVID_ALS_ENABLE; + else if (attr == &dev_attr_egpu_enable.attr) + ok = asus->egpu_enable_available; else if (attr == &dev_attr_dgpu_disable.attr) ok = asus->dgpu_disable_available; else if (attr == &dev_attr_fan_boost_mode.attr) @@ -2792,6 +2886,10 @@ static int asus_wmi_add(struct platform_device *pdev) if (err) goto fail_platform; + err = egpu_enable_check_present(asus); + if (err) + goto fail_egpu_enable; + err = dgpu_disable_check_present(asus); if (err) goto fail_dgpu_disable; @@ -2896,6 +2994,7 @@ fail_input: fail_sysfs: fail_throttle_thermal_policy: fail_fan_boost_mode: +fail_egpu_enable: fail_dgpu_disable: fail_platform: fail_panel_od: diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index a528f9d0e4b7..17dc5cb6f3f2 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -90,6 +90,9 @@ /* Keyboard dock */ #define ASUS_WMI_DEVID_KBD_DOCK 0x00120063 +/* dgpu on/off */ +#define ASUS_WMI_DEVID_EGPU 0x00090019 + /* dgpu on/off */ #define ASUS_WMI_DEVID_DGPU 0x00090020 -- cgit v1.2.3