linux-stable/include/linux/apple-gmux.h
Hans de Goede d143908f80 platform/x86: apple-gmux: Add apple_gmux_detect() helper
Add a new (static inline) apple_gmux_detect() helper to apple-gmux.h
which can be used for gmux detection instead of apple_gmux_present().

The latter is not really reliable since an ACPI device with a HID
of APP000B is present on some devices without a gmux at all, as well
as on devices with a newer (unsupported) MMIO based gmux model.

This causes apple_gmux_present() to return false-positives on
a number of different Apple laptop models.

This new helper uses the same probing as the actual apple-gmux
driver, so that it does not return false positives.

To avoid code duplication the gmux_probe() function of the actual
driver is also moved over to using the new apple_gmux_detect() helper.

This avoids false positives (vs _HID + IO region detection) on:

MacBookPro5,4
https://pastebin.com/8Xjq7RhS

MacBookPro8,1
https://linux-hardware.org/?probe=e513cfbadb&log=dmesg

MacBookPro9,2
https://bugzilla.kernel.org/attachment.cgi?id=278961

MacBookPro10,2
https://lkml.org/lkml/2014/9/22/657

MacBookPro11,2
https://forums.fedora-fr.org/viewtopic.php?id=70142

MacBookPro11,4
https://raw.githubusercontent.com/im-0/investigate-card-reader-suspend-problem-on-mbp11.4/master/test-16/dmesg

Fixes: 21245df307 ("ACPI: video: Add Apple GMUX brightness control detection")
Link: https://lore.kernel.org/platform-driver-x86/20230123113750.462144-1-hdegoede@redhat.com/
Reported-by: Emmanouil Kouroupakis <kartebi@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20230124105754.62167-3-hdegoede@redhat.com
2023-01-24 13:41:57 +01:00

144 lines
3.7 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* apple-gmux.h - microcontroller built into dual GPU MacBook Pro & Mac Pro
* Copyright (C) 2015 Lukas Wunner <lukas@wunner.de>
*/
#ifndef LINUX_APPLE_GMUX_H
#define LINUX_APPLE_GMUX_H
#include <linux/acpi.h>
#include <linux/io.h>
#include <linux/pnp.h>
#define GMUX_ACPI_HID "APP000B"
/*
* gmux port offsets. Many of these are not yet used, but may be in the
* future, and it's useful to have them documented here anyhow.
*/
#define GMUX_PORT_VERSION_MAJOR 0x04
#define GMUX_PORT_VERSION_MINOR 0x05
#define GMUX_PORT_VERSION_RELEASE 0x06
#define GMUX_PORT_SWITCH_DISPLAY 0x10
#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11
#define GMUX_PORT_INTERRUPT_ENABLE 0x14
#define GMUX_PORT_INTERRUPT_STATUS 0x16
#define GMUX_PORT_SWITCH_DDC 0x28
#define GMUX_PORT_SWITCH_EXTERNAL 0x40
#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41
#define GMUX_PORT_DISCRETE_POWER 0x50
#define GMUX_PORT_MAX_BRIGHTNESS 0x70
#define GMUX_PORT_BRIGHTNESS 0x74
#define GMUX_PORT_VALUE 0xc2
#define GMUX_PORT_READ 0xd0
#define GMUX_PORT_WRITE 0xd4
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
#if IS_ENABLED(CONFIG_APPLE_GMUX)
static inline bool apple_gmux_is_indexed(unsigned long iostart)
{
u16 val;
outb(0xaa, iostart + 0xcc);
outb(0x55, iostart + 0xcd);
outb(0x00, iostart + 0xce);
val = inb(iostart + 0xcc) | (inb(iostart + 0xcd) << 8);
if (val == 0x55aa)
return true;
return false;
}
/**
* apple_gmux_detect() - detect if gmux is built into the machine
*
* @pnp_dev: Device to probe or NULL to use the first matching device
* @indexed_ret: Returns (by reference) if the gmux is indexed or not
*
* Detect if a supported gmux device is present by actually probing it.
* This avoids the false positives returned on some models by
* apple_gmux_present().
*
* Return: %true if a supported gmux ACPI device is detected and the kernel
* was configured with CONFIG_APPLE_GMUX, %false otherwise.
*/
static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
{
u8 ver_major, ver_minor, ver_release;
struct device *dev = NULL;
struct acpi_device *adev;
struct resource *res;
bool indexed = false;
bool ret = false;
if (!pnp_dev) {
adev = acpi_dev_get_first_match_dev(GMUX_ACPI_HID, NULL, -1);
if (!adev)
return false;
dev = get_device(acpi_get_first_physical_node(adev));
acpi_dev_put(adev);
if (!dev)
return false;
pnp_dev = to_pnp_dev(dev);
}
res = pnp_get_resource(pnp_dev, IORESOURCE_IO, 0);
if (!res || resource_size(res) < GMUX_MIN_IO_LEN)
goto out;
/*
* Invalid version information may indicate either that the gmux
* device isn't present or that it's a new one that uses indexed io.
*/
ver_major = inb(res->start + GMUX_PORT_VERSION_MAJOR);
ver_minor = inb(res->start + GMUX_PORT_VERSION_MINOR);
ver_release = inb(res->start + GMUX_PORT_VERSION_RELEASE);
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
indexed = apple_gmux_is_indexed(res->start);
if (!indexed)
goto out;
}
if (indexed_ret)
*indexed_ret = indexed;
ret = true;
out:
put_device(dev);
return ret;
}
/**
* apple_gmux_present() - check if gmux ACPI device is present
*
* Drivers may use this to activate quirks specific to dual GPU MacBook Pros
* and Mac Pros, e.g. for deferred probing, runtime pm and backlight.
*
* Return: %true if gmux ACPI device is present and the kernel was configured
* with CONFIG_APPLE_GMUX, %false otherwise.
*/
static inline bool apple_gmux_present(void)
{
return acpi_dev_found(GMUX_ACPI_HID);
}
#else /* !CONFIG_APPLE_GMUX */
static inline bool apple_gmux_present(void)
{
return false;
}
static inline bool apple_gmux_detect(struct pnp_dev *pnp_dev, bool *indexed_ret)
{
return false;
}
#endif /* !CONFIG_APPLE_GMUX */
#endif /* LINUX_APPLE_GMUX_H */