diff mbox series

[v3,6/6] asus-wmi: Support the hardware GPU MUX on some laptops

Message ID 20220809025054.1626339-7-luke@ljones.dev (mailing list archive)
State Changes Requested, archived
Headers show
Series asus-wmi: Add support for RGB keyboards | expand

Commit Message

Luke D. Jones Aug. 9, 2022, 2:50 a.m. UTC
Support the hardware GPU MUX switch available on some models. This
switch can toggle the MUX between:

- 0, Dedicated mode
- 1, Optimus mode

Optimus mode is the regular iGPU + dGPU available, while dedicated
mode switches the system to have only the dGPU available.

Signed-off-by: Luke D. Jones <luke@ljones.dev>
---
 .../ABI/testing/sysfs-platform-asus-wmi       |  9 ++
 drivers/platform/x86/asus-wmi.c               | 91 +++++++++++++++++++
 include/linux/platform_data/x86/asus-wmi.h    |  3 +
 3 files changed, 103 insertions(+)

Comments

Luke D. Jones Aug. 9, 2022, 7:19 a.m. UTC | #1
Hi all,

This patch still needs some work. I've been analysing the various dumps 
I've collected over the past 2 years.

Some laptops return a `0xFFFFFFFE` in response to query of this method, 
and do not have a corresponding set method. To work around it I was 
using `if (result == 0xFFFFFFFE) return 0;`, but, I'm unsure if 
`0xFFFFFFFE` is actually a valid response in the first place. Is it?

Additionally to this, I should have been reading the devstate in the 
related _show(), not returning the stored value. And this should be 
done for the egpu_enable and dgpu_disable attributes also.

Lastly, some laptops have a valid return for the getter, but no setter 
method.. I'm not sure what to do about this.

Are there any issues with me adding more patches to this series? In 
particular I think I need to add patches for the above mentioned 
things, and I should add the "asus-wmi: Modify behaviour of Fn+F5 fan 
key" patches too, I'm beginning to get merge conflicts in testing, and 
all the work I'm doing is becoming unwieldy.

Kind regards,
Luke.


On Tue, Aug 9 2022 at 14:50:54 +1200, Luke D. Jones <luke@ljones.dev> 
wrote:
> Support the hardware GPU MUX switch available on some models. This
> switch can toggle the MUX between:
> 
> - 0, Dedicated mode
> - 1, Optimus mode
> 
> Optimus mode is the regular iGPU + dGPU available, while dedicated
> mode switches the system to have only the dGPU available.
> 
> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> ---
>  .../ABI/testing/sysfs-platform-asus-wmi       |  9 ++
>  drivers/platform/x86/asus-wmi.c               | 91 
> +++++++++++++++++++
>  include/linux/platform_data/x86/asus-wmi.h    |  3 +
>  3 files changed, 103 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi 
> b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> index 541dbfbbbb26..d483bc3cb2e6 100644
> --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
> +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> @@ -67,6 +67,15 @@ Description:
>  			* 0 - Disable,
>  			* 1 - Enable,
> 
> +What:		/sys/devices/platform/<platform>/gpu_mux_mode
> +Date:		Aug 2022
> +KernelVersion:	6.0
> +Contact:	"Luke Jones" <luke@ljones.dev>
> +Description:
> +		Switch the GPU used by the hardware MUX:
> +			* 0 - Dedicated GPU,
> +			* 1 - Optimus mode,
> +
>  What:		/sys/devices/platform/<platform>/panel_od
>  Date:		Aug 2022
>  KernelVersion:	5.17
> diff --git a/drivers/platform/x86/asus-wmi.c 
> b/drivers/platform/x86/asus-wmi.c
> index 78f1f3af1b12..c5fa21370481 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -246,6 +246,9 @@ struct asus_wmi {
>  	bool dgpu_disable_available;
>  	bool dgpu_disable;
> 
> +	bool gpu_mux_mode_available;
> +	bool gpu_mux_mode;
> +
>  	bool keyboard_rgb_state_available;
>  	bool keyboard_rgb_mode_available;
>  	struct keyboard_rgb_led keyboard_rgb_led;
> @@ -750,6 +753,86 @@ static ssize_t egpu_enable_store(struct device 
> *dev,
> 
>  static DEVICE_ATTR_RW(egpu_enable);
> 
> +/* gpu mux switch 
> *************************************************************/
> +static int gpu_mux_mode_check_present(struct asus_wmi *asus)
> +{
> +	u32 result;
> +	int err;
> +
> +	asus->gpu_mux_mode_available = false;
> +
> +	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_GPU_MUX, &result);
> +	if (err) {
> +		if (err == -ENODEV)
> +			return 0;
> +		return err;
> +	}
> +
> +	if (result & ASUS_WMI_DSTS_PRESENCE_BIT) {
> +		asus->gpu_mux_mode_available = true;
> +		asus->gpu_mux_mode = result & ASUS_WMI_DSTS_STATUS_BIT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpu_mux_mode_write(struct asus_wmi *asus)
> +{
> +	u32 retval;
> +	u8 value;
> +	int err;
> +
> +	/* Don't rely on type conversion */
> +	value = asus->gpu_mux_mode ? 1 : 0;
> +
> +	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, value, &retval);
> +	if (err) {
> +		pr_warn("Failed to set dGPU-only mode: %d\n", err);
> +		return err;
> +	}
> +
> +	if (retval > 1) {
> +		pr_warn("Failed to set dGPU-only mode (retval): 0x%x\n", retval);
> +		return -EIO;
> +	}
> +
> +	sysfs_notify(&asus->platform_device->dev.kobj, NULL, 
> "gpu_mux_mode");
> +
> +	return 0;
> +}
> +
> +static ssize_t gpu_mux_mode_show(struct device *dev,
> +				   struct device_attribute *attr, char *buf)
> +{
> +	struct asus_wmi *asus = dev_get_drvdata(dev);
> +	u8 mode = asus->gpu_mux_mode;
> +
> +	return sysfs_emit(buf, "%d\n", mode);
> +}
> +
> +static ssize_t gpu_mux_mode_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t count)
> +{
> +	bool optimus;
> +	int result;
> +
> +	struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +	result = kstrtobool(buf, &optimus);
> +	if (result)
> +		return result;
> +
> +	asus->gpu_mux_mode = optimus;
> +
> +	result = gpu_mux_mode_write(asus);
> +	if (result)
> +		return result;
> +
> +	return count;
> +}
> +static DEVICE_ATTR_RW(gpu_mux_mode);
> +
>  /* TUF Laptop Keyboard RGB Modes 
> **********************************************/
>  static int keyboard_rgb_check_present(struct asus_wmi *asus)
>  {
> @@ -3496,6 +3579,7 @@ static struct attribute *platform_attributes[] 
> = {
>  	&dev_attr_touchpad.attr,
>  	&dev_attr_egpu_enable.attr,
>  	&dev_attr_dgpu_disable.attr,
> +	&dev_attr_gpu_mux_mode.attr,
>  	&dev_attr_keyboard_rgb_save.attr,
>  	&dev_attr_keyboard_rgb_mode.attr,
>  	&dev_attr_keyboard_rgb_speed.attr,
> @@ -3531,6 +3615,8 @@ static umode_t asus_sysfs_is_visible(struct 
> kobject *kobj,
>  		ok = asus->egpu_enable_available;
>  	else if (attr == &dev_attr_dgpu_disable.attr)
>  		ok = asus->dgpu_disable_available;
> +	else if (attr == &dev_attr_gpu_mux_mode.attr)
> +		ok = asus->gpu_mux_mode_available;
>  	else if (attr == &dev_attr_keyboard_rgb_save.attr)
>  		ok = asus->keyboard_rgb_mode_available;
>  	else if (attr == &dev_attr_keyboard_rgb_mode.attr)
> @@ -3810,6 +3896,10 @@ static int asus_wmi_add(struct platform_device 
> *pdev)
>  	if (err)
>  		goto fail_dgpu_disable;
> 
> +	err = gpu_mux_mode_check_present(asus);
> +	if (err)
> +		goto fail_gpu_mux_mode;
> +
>  	err = keyboard_rgb_check_present(asus);
>  	if (err)
>  		goto fail_keyboard_rgb_mode;
> @@ -3932,6 +4022,7 @@ static int asus_wmi_add(struct platform_device 
> *pdev)
>  fail_fan_boost_mode:
>  fail_egpu_enable:
>  fail_dgpu_disable:
> +fail_gpu_mux_mode:
>  fail_keyboard_rgb_mode:
>  fail_keyboard_rgb_state:
>  fail_platform:
> diff --git a/include/linux/platform_data/x86/asus-wmi.h 
> b/include/linux/platform_data/x86/asus-wmi.h
> index b5c966798ef8..3faeb98f6ea9 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -98,6 +98,9 @@
>  /* dgpu on/off */
>  #define ASUS_WMI_DEVID_DGPU		0x00090020
> 
> +/* gpu mux switch, 0 = dGPU, 1 = Optimus */
> +#define ASUS_WMI_DEVID_GPU_MUX	0x00090016
> +
>  /* TUF laptop RGB control */
>  #define ASUS_WMI_DEVID_TUF_RGB_MODE	0x00100056
>  /* TUF laptop RGB state control */
> --
> 2.37.1
>
Hans de Goede Aug. 11, 2022, 1:53 p.m. UTC | #2
Hi,

On 8/9/22 04:50, Luke D. Jones wrote:
> Support the hardware GPU MUX switch available on some models. This
> switch can toggle the MUX between:
> 
> - 0, Dedicated mode
> - 1, Optimus mode
> 
> Optimus mode is the regular iGPU + dGPU available, while dedicated
> mode switches the system to have only the dGPU available.
> 
> Signed-off-by: Luke D. Jones <luke@ljones.dev>

I see that you have replied to this that it needs more work.

Besides it needing more work, ideally this should hook into
the existing vga-switcheroo mechanism for this. Can you take
a look at that please?

I think this might be the first non GPU driver doing vga-
switcheroo stuff. So this may be something to discuss
on the dri-devel list.

And if things don't work out we can always go back to
just using an asus-wmi specific sysfs file for this as
is done in this patch.

Regards,

Hans



> ---
>  .../ABI/testing/sysfs-platform-asus-wmi       |  9 ++
>  drivers/platform/x86/asus-wmi.c               | 91 +++++++++++++++++++
>  include/linux/platform_data/x86/asus-wmi.h    |  3 +
>  3 files changed, 103 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> index 541dbfbbbb26..d483bc3cb2e6 100644
> --- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
> +++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
> @@ -67,6 +67,15 @@ Description:
>  			* 0 - Disable,
>  			* 1 - Enable,
>  
> +What:		/sys/devices/platform/<platform>/gpu_mux_mode
> +Date:		Aug 2022
> +KernelVersion:	6.0
> +Contact:	"Luke Jones" <luke@ljones.dev>
> +Description:
> +		Switch the GPU used by the hardware MUX:
> +			* 0 - Dedicated GPU,
> +			* 1 - Optimus mode,
> +
>  What:		/sys/devices/platform/<platform>/panel_od
>  Date:		Aug 2022
>  KernelVersion:	5.17
> diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
> index 78f1f3af1b12..c5fa21370481 100644
> --- a/drivers/platform/x86/asus-wmi.c
> +++ b/drivers/platform/x86/asus-wmi.c
> @@ -246,6 +246,9 @@ struct asus_wmi {
>  	bool dgpu_disable_available;
>  	bool dgpu_disable;
>  
> +	bool gpu_mux_mode_available;
> +	bool gpu_mux_mode;
> +
>  	bool keyboard_rgb_state_available;
>  	bool keyboard_rgb_mode_available;
>  	struct keyboard_rgb_led keyboard_rgb_led;
> @@ -750,6 +753,86 @@ static ssize_t egpu_enable_store(struct device *dev,
>  
>  static DEVICE_ATTR_RW(egpu_enable);
>  
> +/* gpu mux switch *************************************************************/
> +static int gpu_mux_mode_check_present(struct asus_wmi *asus)
> +{
> +	u32 result;
> +	int err;
> +
> +	asus->gpu_mux_mode_available = false;
> +
> +	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_GPU_MUX, &result);
> +	if (err) {
> +		if (err == -ENODEV)
> +			return 0;
> +		return err;
> +	}
> +
> +	if (result & ASUS_WMI_DSTS_PRESENCE_BIT) {
> +		asus->gpu_mux_mode_available = true;
> +		asus->gpu_mux_mode = result & ASUS_WMI_DSTS_STATUS_BIT;
> +	}
> +
> +	return 0;
> +}
> +
> +static int gpu_mux_mode_write(struct asus_wmi *asus)
> +{
> +	u32 retval;
> +	u8 value;
> +	int err;
> +
> +	/* Don't rely on type conversion */
> +	value = asus->gpu_mux_mode ? 1 : 0;
> +
> +	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, value, &retval);
> +	if (err) {
> +		pr_warn("Failed to set dGPU-only mode: %d\n", err);
> +		return err;
> +	}
> +
> +	if (retval > 1) {
> +		pr_warn("Failed to set dGPU-only mode (retval): 0x%x\n", retval);
> +		return -EIO;
> +	}
> +
> +	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "gpu_mux_mode");
> +
> +	return 0;
> +}
> +
> +static ssize_t gpu_mux_mode_show(struct device *dev,
> +				   struct device_attribute *attr, char *buf)
> +{
> +	struct asus_wmi *asus = dev_get_drvdata(dev);
> +	u8 mode = asus->gpu_mux_mode;
> +
> +	return sysfs_emit(buf, "%d\n", mode);
> +}
> +
> +static ssize_t gpu_mux_mode_store(struct device *dev,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t count)
> +{
> +	bool optimus;
> +	int result;
> +
> +	struct asus_wmi *asus = dev_get_drvdata(dev);
> +
> +	result = kstrtobool(buf, &optimus);
> +	if (result)
> +		return result;
> +
> +	asus->gpu_mux_mode = optimus;
> +
> +	result = gpu_mux_mode_write(asus);
> +	if (result)
> +		return result;
> +
> +	return count;
> +}
> +static DEVICE_ATTR_RW(gpu_mux_mode);
> +
>  /* TUF Laptop Keyboard RGB Modes **********************************************/
>  static int keyboard_rgb_check_present(struct asus_wmi *asus)
>  {
> @@ -3496,6 +3579,7 @@ static struct attribute *platform_attributes[] = {
>  	&dev_attr_touchpad.attr,
>  	&dev_attr_egpu_enable.attr,
>  	&dev_attr_dgpu_disable.attr,
> +	&dev_attr_gpu_mux_mode.attr,
>  	&dev_attr_keyboard_rgb_save.attr,
>  	&dev_attr_keyboard_rgb_mode.attr,
>  	&dev_attr_keyboard_rgb_speed.attr,
> @@ -3531,6 +3615,8 @@ static umode_t asus_sysfs_is_visible(struct kobject *kobj,
>  		ok = asus->egpu_enable_available;
>  	else if (attr == &dev_attr_dgpu_disable.attr)
>  		ok = asus->dgpu_disable_available;
> +	else if (attr == &dev_attr_gpu_mux_mode.attr)
> +		ok = asus->gpu_mux_mode_available;
>  	else if (attr == &dev_attr_keyboard_rgb_save.attr)
>  		ok = asus->keyboard_rgb_mode_available;
>  	else if (attr == &dev_attr_keyboard_rgb_mode.attr)
> @@ -3810,6 +3896,10 @@ static int asus_wmi_add(struct platform_device *pdev)
>  	if (err)
>  		goto fail_dgpu_disable;
>  
> +	err = gpu_mux_mode_check_present(asus);
> +	if (err)
> +		goto fail_gpu_mux_mode;
> +
>  	err = keyboard_rgb_check_present(asus);
>  	if (err)
>  		goto fail_keyboard_rgb_mode;
> @@ -3932,6 +4022,7 @@ static int asus_wmi_add(struct platform_device *pdev)
>  fail_fan_boost_mode:
>  fail_egpu_enable:
>  fail_dgpu_disable:
> +fail_gpu_mux_mode:
>  fail_keyboard_rgb_mode:
>  fail_keyboard_rgb_state:
>  fail_platform:
> diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
> index b5c966798ef8..3faeb98f6ea9 100644
> --- a/include/linux/platform_data/x86/asus-wmi.h
> +++ b/include/linux/platform_data/x86/asus-wmi.h
> @@ -98,6 +98,9 @@
>  /* dgpu on/off */
>  #define ASUS_WMI_DEVID_DGPU		0x00090020
>  
> +/* gpu mux switch, 0 = dGPU, 1 = Optimus */
> +#define ASUS_WMI_DEVID_GPU_MUX	0x00090016
> +
>  /* TUF laptop RGB control */
>  #define ASUS_WMI_DEVID_TUF_RGB_MODE	0x00100056
>  /* TUF laptop RGB state control */
Luke D. Jones Aug. 11, 2022, 10:01 p.m. UTC | #3
Hi Hans,

On Thu, 2022-08-11 at 15:53 +0200, Hans de Goede wrote:
> Hi,
> 
> On 8/9/22 04:50, Luke D. Jones wrote:
> > Support the hardware GPU MUX switch available on some models. This
> > switch can toggle the MUX between:
> > 
> > - 0, Dedicated mode
> > - 1, Optimus mode
> > 
> > Optimus mode is the regular iGPU + dGPU available, while dedicated
> > mode switches the system to have only the dGPU available.
> > 
> > Signed-off-by: Luke D. Jones <luke@ljones.dev>
> 
> I see that you have replied to this that it needs more work.
> 
> Besides it needing more work, ideally this should hook into
> the existing vga-switcheroo mechanism for this. Can you take
> a look at that please?
> 
> I think this might be the first non GPU driver doing vga-
> switcheroo stuff. So this may be something to discuss
> on the dri-devel list.

I'm not sure how this would work. In typical ASUS fashion they do non-
standard stuff. This switch is a basic toggle that requires a reboot to
enable after writing to the ACPI method, after reboot the dGPU becomes
the only visible GPU on the system and (this GPU) can not be suspended.

In short: it toggles the laptop from discrete-only mode, and optimus
mode, requiring a reboot to switch.

From what I understand of switcheroo it is more to manage having dual
(or more) GPU available during runtime, and manage the power states,
offload etc.

I have a vastly improved patch for this prepared now. Because of how
the actual feature works (and the above explanation) it must be under
the asus-nb-wmi sysfs (next to the dgpu_disable and egpu_enable toggles
which are also unusual and non-standard work-arounds of Windows
issues).

Kind regards,
Luke.
Hans de Goede Aug. 12, 2022, 7:59 a.m. UTC | #4
Hi,

On 8/12/22 00:01, Luke Jones wrote:
> Hi Hans,
> 
> On Thu, 2022-08-11 at 15:53 +0200, Hans de Goede wrote:
>> Hi,
>>
>> On 8/9/22 04:50, Luke D. Jones wrote:
>>> Support the hardware GPU MUX switch available on some models. This
>>> switch can toggle the MUX between:
>>>
>>> - 0, Dedicated mode
>>> - 1, Optimus mode
>>>
>>> Optimus mode is the regular iGPU + dGPU available, while dedicated
>>> mode switches the system to have only the dGPU available.
>>>
>>> Signed-off-by: Luke D. Jones <luke@ljones.dev>
>>
>> I see that you have replied to this that it needs more work.
>>
>> Besides it needing more work, ideally this should hook into
>> the existing vga-switcheroo mechanism for this. Can you take
>> a look at that please?
>>
>> I think this might be the first non GPU driver doing vga-
>> switcheroo stuff. So this may be something to discuss
>> on the dri-devel list.
> 
> I'm not sure how this would work. In typical ASUS fashion they do non-
> standard stuff. This switch is a basic toggle that requires a reboot to
> enable after writing to the ACPI method, after reboot the dGPU becomes
> the only visible GPU on the system and (this GPU) can not be suspended.
> 
> In short: it toggles the laptop from discrete-only mode, and optimus
> mode, requiring a reboot to switch.
> 
> From what I understand of switcheroo it is more to manage having dual
> (or more) GPU available during runtime, and manage the power states,
> offload etc.

Right, I did not realize this requires a reboot, that would be
something to mention in the Documentation bits accompanying the patch.

This is also a reason why it is good to have the docs update in
the same patch as adding the functionality, because the docs may
help with reviewing.

Anyways I agree that if this requires a reboot then using
the vga switcheroo stuff is not applicable. So we can just go with
a simple(ish) asus-wmi sysfs attribute.

> I have a vastly improved patch for this prepared now. Because of how
> the actual feature works (and the above explanation) it must be under
> the asus-nb-wmi sysfs (next to the dgpu_disable and egpu_enable toggles
> which are also unusual and non-standard work-arounds of Windows
> issues).

Ack, sounds good.

Regards,

Hans
Thomas Weißschuh Aug. 12, 2022, 8:31 a.m. UTC | #5
Hi,

On 2022-08-12 09:59+0200, Hans de Goede wrote:
> Date: Fri, 12 Aug 2022 09:59:29 +0200
> From: Hans de Goede <hdegoede@redhat.com>
> To: Luke Jones <luke@ljones.dev>
> Cc: andy.shevchenko@gmail.com, pobrn@protonmail.com, pavel@ucw.cz,
>  platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org
> Subject: Re: [PATCH v3 6/6] asus-wmi: Support the hardware GPU MUX on some
>  laptops
> User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101
>  Thunderbird/91.12.0
> 
> Hi,
> 
> On 8/12/22 00:01, Luke Jones wrote:
> > Hi Hans,
> > 
> > On Thu, 2022-08-11 at 15:53 +0200, Hans de Goede wrote:
> >> Hi,
> >>
> >> On 8/9/22 04:50, Luke D. Jones wrote:
> >>> Support the hardware GPU MUX switch available on some models. This
> >>> switch can toggle the MUX between:
> >>>
> >>> - 0, Dedicated mode
> >>> - 1, Optimus mode
> >>>
> >>> Optimus mode is the regular iGPU + dGPU available, while dedicated
> >>> mode switches the system to have only the dGPU available.
> >>>
> >>> Signed-off-by: Luke D. Jones <luke@ljones.dev>
> >>
> >> I see that you have replied to this that it needs more work.
> >>
> >> Besides it needing more work, ideally this should hook into
> >> the existing vga-switcheroo mechanism for this. Can you take
> >> a look at that please?
> >>
> >> I think this might be the first non GPU driver doing vga-
> >> switcheroo stuff. So this may be something to discuss
> >> on the dri-devel list.
> > 
> > I'm not sure how this would work. In typical ASUS fashion they do non-
> > standard stuff. This switch is a basic toggle that requires a reboot to
> > enable after writing to the ACPI method, after reboot the dGPU becomes
> > the only visible GPU on the system and (this GPU) can not be suspended.
> > 
> > In short: it toggles the laptop from discrete-only mode, and optimus
> > mode, requiring a reboot to switch.
> > 
> > From what I understand of switcheroo it is more to manage having dual
> > (or more) GPU available during runtime, and manage the power states,
> > offload etc.
> 
> Right, I did not realize this requires a reboot, that would be
> something to mention in the Documentation bits accompanying the patch.
> 
> This is also a reason why it is good to have the docs update in
> the same patch as adding the functionality, because the docs may
> help with reviewing.
> 
> Anyways I agree that if this requires a reboot then using
> the vga switcheroo stuff is not applicable. So we can just go with
> a simple(ish) asus-wmi sysfs attribute.

Would this not fit the existing "firmware-attributes" class?
It even has a flag to signal that a reboot is required after an attribute has
been changed.

Maybe it is overkill to use it only for this, though.

> > I have a vastly improved patch for this prepared now. Because of how
> > the actual feature works (and the above explanation) it must be under
> > the asus-nb-wmi sysfs (next to the dgpu_disable and egpu_enable toggles
> > which are also unusual and non-standard work-arounds of Windows
> > issues).
> 
> Ack, sounds good.

Thomas
Hans de Goede Aug. 12, 2022, 8:44 a.m. UTC | #6
Hi,

On 8/12/22 10:31, Thomas Weißschuh wrote:
> Hi,
> 
> On 2022-08-12 09:59+0200, Hans de Goede wrote:
>> Date: Fri, 12 Aug 2022 09:59:29 +0200
>> From: Hans de Goede <hdegoede@redhat.com>
>> To: Luke Jones <luke@ljones.dev>
>> Cc: andy.shevchenko@gmail.com, pobrn@protonmail.com, pavel@ucw.cz,
>>  platform-driver-x86@vger.kernel.org, linux-kernel@vger.kernel.org
>> Subject: Re: [PATCH v3 6/6] asus-wmi: Support the hardware GPU MUX on some
>>  laptops
>> User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:91.0) Gecko/20100101
>>  Thunderbird/91.12.0
>>
>> Hi,
>>
>> On 8/12/22 00:01, Luke Jones wrote:
>>> Hi Hans,
>>>
>>> On Thu, 2022-08-11 at 15:53 +0200, Hans de Goede wrote:
>>>> Hi,
>>>>
>>>> On 8/9/22 04:50, Luke D. Jones wrote:
>>>>> Support the hardware GPU MUX switch available on some models. This
>>>>> switch can toggle the MUX between:
>>>>>
>>>>> - 0, Dedicated mode
>>>>> - 1, Optimus mode
>>>>>
>>>>> Optimus mode is the regular iGPU + dGPU available, while dedicated
>>>>> mode switches the system to have only the dGPU available.
>>>>>
>>>>> Signed-off-by: Luke D. Jones <luke@ljones.dev>
>>>>
>>>> I see that you have replied to this that it needs more work.
>>>>
>>>> Besides it needing more work, ideally this should hook into
>>>> the existing vga-switcheroo mechanism for this. Can you take
>>>> a look at that please?
>>>>
>>>> I think this might be the first non GPU driver doing vga-
>>>> switcheroo stuff. So this may be something to discuss
>>>> on the dri-devel list.
>>>
>>> I'm not sure how this would work. In typical ASUS fashion they do non-
>>> standard stuff. This switch is a basic toggle that requires a reboot to
>>> enable after writing to the ACPI method, after reboot the dGPU becomes
>>> the only visible GPU on the system and (this GPU) can not be suspended.
>>>
>>> In short: it toggles the laptop from discrete-only mode, and optimus
>>> mode, requiring a reboot to switch.
>>>
>>> From what I understand of switcheroo it is more to manage having dual
>>> (or more) GPU available during runtime, and manage the power states,
>>> offload etc.
>>
>> Right, I did not realize this requires a reboot, that would be
>> something to mention in the Documentation bits accompanying the patch.
>>
>> This is also a reason why it is good to have the docs update in
>> the same patch as adding the functionality, because the docs may
>> help with reviewing.
>>
>> Anyways I agree that if this requires a reboot then using
>> the vga switcheroo stuff is not applicable. So we can just go with
>> a simple(ish) asus-wmi sysfs attribute.
> 
> Would this not fit the existing "firmware-attributes" class?
> It even has a flag to signal that a reboot is required after an attribute has
> been changed.

Yes it sounds like a BIOS setting is being toggled from within
Linux, which would normally be done through the
"firmware-attributes" class, but all existing "firmware-attributes"
class drivers allow changing all BIOS setting not just a single
setting, so using the  "firmware-attributes" class here is not really
appropriate.

> Maybe it is overkill to use it only for this, though.

Right :)

Regards,

Hans


>>> I have a vastly improved patch for this prepared now. Because of how
>>> the actual feature works (and the above explanation) it must be under
>>> the asus-nb-wmi sysfs (next to the dgpu_disable and egpu_enable toggles
>>> which are also unusual and non-standard work-arounds of Windows
>>> issues).
>>
>> Ack, sounds good.
> 
> Thomas
>
diff mbox series

Patch

diff --git a/Documentation/ABI/testing/sysfs-platform-asus-wmi b/Documentation/ABI/testing/sysfs-platform-asus-wmi
index 541dbfbbbb26..d483bc3cb2e6 100644
--- a/Documentation/ABI/testing/sysfs-platform-asus-wmi
+++ b/Documentation/ABI/testing/sysfs-platform-asus-wmi
@@ -67,6 +67,15 @@  Description:
 			* 0 - Disable,
 			* 1 - Enable,
 
+What:		/sys/devices/platform/<platform>/gpu_mux_mode
+Date:		Aug 2022
+KernelVersion:	6.0
+Contact:	"Luke Jones" <luke@ljones.dev>
+Description:
+		Switch the GPU used by the hardware MUX:
+			* 0 - Dedicated GPU,
+			* 1 - Optimus mode,
+
 What:		/sys/devices/platform/<platform>/panel_od
 Date:		Aug 2022
 KernelVersion:	5.17
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index 78f1f3af1b12..c5fa21370481 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -246,6 +246,9 @@  struct asus_wmi {
 	bool dgpu_disable_available;
 	bool dgpu_disable;
 
+	bool gpu_mux_mode_available;
+	bool gpu_mux_mode;
+
 	bool keyboard_rgb_state_available;
 	bool keyboard_rgb_mode_available;
 	struct keyboard_rgb_led keyboard_rgb_led;
@@ -750,6 +753,86 @@  static ssize_t egpu_enable_store(struct device *dev,
 
 static DEVICE_ATTR_RW(egpu_enable);
 
+/* gpu mux switch *************************************************************/
+static int gpu_mux_mode_check_present(struct asus_wmi *asus)
+{
+	u32 result;
+	int err;
+
+	asus->gpu_mux_mode_available = false;
+
+	err = asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_GPU_MUX, &result);
+	if (err) {
+		if (err == -ENODEV)
+			return 0;
+		return err;
+	}
+
+	if (result & ASUS_WMI_DSTS_PRESENCE_BIT) {
+		asus->gpu_mux_mode_available = true;
+		asus->gpu_mux_mode = result & ASUS_WMI_DSTS_STATUS_BIT;
+	}
+
+	return 0;
+}
+
+static int gpu_mux_mode_write(struct asus_wmi *asus)
+{
+	u32 retval;
+	u8 value;
+	int err;
+
+	/* Don't rely on type conversion */
+	value = asus->gpu_mux_mode ? 1 : 0;
+
+	err = asus_wmi_set_devstate(ASUS_WMI_DEVID_GPU_MUX, value, &retval);
+	if (err) {
+		pr_warn("Failed to set dGPU-only mode: %d\n", err);
+		return err;
+	}
+
+	if (retval > 1) {
+		pr_warn("Failed to set dGPU-only mode (retval): 0x%x\n", retval);
+		return -EIO;
+	}
+
+	sysfs_notify(&asus->platform_device->dev.kobj, NULL, "gpu_mux_mode");
+
+	return 0;
+}
+
+static ssize_t gpu_mux_mode_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct asus_wmi *asus = dev_get_drvdata(dev);
+	u8 mode = asus->gpu_mux_mode;
+
+	return sysfs_emit(buf, "%d\n", mode);
+}
+
+static ssize_t gpu_mux_mode_store(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	bool optimus;
+	int result;
+
+	struct asus_wmi *asus = dev_get_drvdata(dev);
+
+	result = kstrtobool(buf, &optimus);
+	if (result)
+		return result;
+
+	asus->gpu_mux_mode = optimus;
+
+	result = gpu_mux_mode_write(asus);
+	if (result)
+		return result;
+
+	return count;
+}
+static DEVICE_ATTR_RW(gpu_mux_mode);
+
 /* TUF Laptop Keyboard RGB Modes **********************************************/
 static int keyboard_rgb_check_present(struct asus_wmi *asus)
 {
@@ -3496,6 +3579,7 @@  static struct attribute *platform_attributes[] = {
 	&dev_attr_touchpad.attr,
 	&dev_attr_egpu_enable.attr,
 	&dev_attr_dgpu_disable.attr,
+	&dev_attr_gpu_mux_mode.attr,
 	&dev_attr_keyboard_rgb_save.attr,
 	&dev_attr_keyboard_rgb_mode.attr,
 	&dev_attr_keyboard_rgb_speed.attr,
@@ -3531,6 +3615,8 @@  static umode_t asus_sysfs_is_visible(struct kobject *kobj,
 		ok = asus->egpu_enable_available;
 	else if (attr == &dev_attr_dgpu_disable.attr)
 		ok = asus->dgpu_disable_available;
+	else if (attr == &dev_attr_gpu_mux_mode.attr)
+		ok = asus->gpu_mux_mode_available;
 	else if (attr == &dev_attr_keyboard_rgb_save.attr)
 		ok = asus->keyboard_rgb_mode_available;
 	else if (attr == &dev_attr_keyboard_rgb_mode.attr)
@@ -3810,6 +3896,10 @@  static int asus_wmi_add(struct platform_device *pdev)
 	if (err)
 		goto fail_dgpu_disable;
 
+	err = gpu_mux_mode_check_present(asus);
+	if (err)
+		goto fail_gpu_mux_mode;
+
 	err = keyboard_rgb_check_present(asus);
 	if (err)
 		goto fail_keyboard_rgb_mode;
@@ -3932,6 +4022,7 @@  static int asus_wmi_add(struct platform_device *pdev)
 fail_fan_boost_mode:
 fail_egpu_enable:
 fail_dgpu_disable:
+fail_gpu_mux_mode:
 fail_keyboard_rgb_mode:
 fail_keyboard_rgb_state:
 fail_platform:
diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h
index b5c966798ef8..3faeb98f6ea9 100644
--- a/include/linux/platform_data/x86/asus-wmi.h
+++ b/include/linux/platform_data/x86/asus-wmi.h
@@ -98,6 +98,9 @@ 
 /* dgpu on/off */
 #define ASUS_WMI_DEVID_DGPU		0x00090020
 
+/* gpu mux switch, 0 = dGPU, 1 = Optimus */
+#define ASUS_WMI_DEVID_GPU_MUX	0x00090016
+
 /* TUF laptop RGB control */
 #define ASUS_WMI_DEVID_TUF_RGB_MODE	0x00100056
 /* TUF laptop RGB state control */