From 67c4ac4764c85aacc1ace749b3e4a6c0781de990 Mon Sep 17 00:00:00 2001 From: Evgeny Zinoviev Date: Fri, 10 May 2019 01:50:45 +0300 Subject: drivers/apple: Add hybrid graphics driver A hybrid graphics driver for Apple MacBook Pro. The driver logic is based on lenovo/hybrid_graphics. It is splitted into romstage and ramstage parts. The mainboard code calls the driver from romstage to get the GPU state. The driver reads the state from the `hybrid_grapihcs_mode` nvram option, switches dGPU power on or off according to the state and returns the state to the mainboard code. The mainboard code then has to hide the disabled PCI device. The ramstage part handles the graphics muxes. The muxes code is based on the apple-gmux linux driver, originally written by: * Canonical Ltd. * Andreas Heider, 2010-2012 * Lukas Wunner, 2015 Tested on MacBook Pro Retina 15 Mid 2012 (MacBook Pro 10,1). Change-Id: I22b66622cd2da0e9951ee726d650d204fbb8a5bc Signed-off-by: Evgeny Zinoviev --- src/drivers/apple/hybrid_graphics/Kconfig | 3 + src/drivers/apple/hybrid_graphics/Makefile.inc | 4 + src/drivers/apple/hybrid_graphics/acpi/gmux.asl | 39 ++++++ src/drivers/apple/hybrid_graphics/chip.h | 17 +++ src/drivers/apple/hybrid_graphics/gmux.c | 138 +++++++++++++++++++++ src/drivers/apple/hybrid_graphics/gmux.h | 43 +++++++ .../apple/hybrid_graphics/hybrid_graphics.c | 50 ++++++++ .../apple/hybrid_graphics/hybrid_graphics.h | 11 ++ src/drivers/apple/hybrid_graphics/romstage.c | 43 +++++++ 9 files changed, 348 insertions(+) create mode 100644 src/drivers/apple/hybrid_graphics/Kconfig create mode 100644 src/drivers/apple/hybrid_graphics/Makefile.inc create mode 100644 src/drivers/apple/hybrid_graphics/acpi/gmux.asl create mode 100644 src/drivers/apple/hybrid_graphics/chip.h create mode 100644 src/drivers/apple/hybrid_graphics/gmux.c create mode 100644 src/drivers/apple/hybrid_graphics/gmux.h create mode 100644 src/drivers/apple/hybrid_graphics/hybrid_graphics.c create mode 100644 src/drivers/apple/hybrid_graphics/hybrid_graphics.h create mode 100644 src/drivers/apple/hybrid_graphics/romstage.c diff --git a/src/drivers/apple/hybrid_graphics/Kconfig b/src/drivers/apple/hybrid_graphics/Kconfig new file mode 100644 index 0000000000..252373fb55 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/Kconfig @@ -0,0 +1,3 @@ +config DRIVERS_APPLE_HYBRID_GRAPHICS + bool + default n diff --git a/src/drivers/apple/hybrid_graphics/Makefile.inc b/src/drivers/apple/hybrid_graphics/Makefile.inc new file mode 100644 index 0000000000..caef42a351 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/Makefile.inc @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +romstage-$(CONFIG_DRIVERS_APPLE_HYBRID_GRAPHICS) += gmux.c romstage.c +ramstage-$(CONFIG_DRIVERS_APPLE_HYBRID_GRAPHICS) += gmux.c hybrid_graphics.c diff --git a/src/drivers/apple/hybrid_graphics/acpi/gmux.asl b/src/drivers/apple/hybrid_graphics/acpi/gmux.asl new file mode 100644 index 0000000000..bf6c8f0e44 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/acpi/gmux.asl @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +Device (GMUX) +{ + Name (_HID, EisaId ("APP000B")) + Name (_CID, "gmux") + Name (_STA, 0x0B) + Name (_CRS, ResourceTemplate () + { + IO (Decode16, + 0x0700, // Range Minimum + 0x07FF, // Range Maximum + 0x01, // Alignment + 0xFF // Length + ) + }) + Name (_PRW, Package (0x02) { 0x16, 0x03 }) + Scope (\_GPE) + { + Method (_L16, 0, NotSerialized) + { + Notify (\_SB.PCI0.LPCB.GMUX, 0x80) + } + } + + Name (GMGP, 0x16) + Method (GMSP, 1, NotSerialized) + { + If (Arg0 == 0) + { + \GP06 |= Arg0 + } + } + + Method (GMLV, 0, NotSerialized) + { + Return (\GP06) + } +} diff --git a/src/drivers/apple/hybrid_graphics/chip.h b/src/drivers/apple/hybrid_graphics/chip.h new file mode 100644 index 0000000000..c7111f98a5 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/chip.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _APPLE_HYBRID_GRAPHICS_CHIP_H_ +#define _APPLE_HYBRID_GRAPHICS_CHIP_H_ + +enum hybrid_graphics_req { + HYBRID_GRAPHICS_INTEGRATED = 0, + HYBRID_GRAPHICS_DISCRETE = 1 +}; + +#define HYBRID_GRAPHICS_DEFAULT_GPU HYBRID_GRAPHICS_INTEGRATED + +struct drivers_apple_hybrid_graphics_config { + bool gmux_indexed; +}; + +#endif /* _APPLE_HYBRID_GRAPHICS_CHIP_H_ */ diff --git a/src/drivers/apple/hybrid_graphics/gmux.c b/src/drivers/apple/hybrid_graphics/gmux.c new file mode 100644 index 0000000000..e3788b2436 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/gmux.c @@ -0,0 +1,138 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include "gmux.h" +#include "chip.h" + +static int gmux_index_wait_ready(void) +{ + int i = 200; + u8 gwr = inb(GMUX_IOSTART + GMUX_PORT_WRITE); + + while (i && (gwr & 0x01)) { + inb(GMUX_IOSTART + GMUX_PORT_READ); + gwr = inb(GMUX_IOSTART + GMUX_PORT_WRITE); + udelay(100); + i--; + } + + return !!i; +} + +static int gmux_index_wait_complete(void) +{ + int i = 200; + u8 gwr = inb(GMUX_IOSTART + GMUX_PORT_WRITE); + + while (i && !(gwr & 0x01)) { + gwr = inb(GMUX_IOSTART + GMUX_PORT_WRITE); + udelay(100); + i--; + } + + if (gwr & 0x01) + inb(GMUX_IOSTART + GMUX_PORT_READ); + + return !!i; +} + +u8 gmux_pio_read8(u8 port) +{ + return inb(GMUX_IOSTART + port); +} + +u8 gmux_index_read8(u8 port) +{ + u8 val; + + gmux_index_wait_ready(); + outb(port, GMUX_IOSTART + GMUX_PORT_READ); + gmux_index_wait_complete(); + val = inb(GMUX_IOSTART + GMUX_PORT_VALUE); + + return val; +} + +void gmux_pio_write8(u8 port, u8 val) +{ + outb(val, GMUX_IOSTART + port); +} + +void gmux_index_write8(u8 port, u8 val) +{ + outb(val, GMUX_IOSTART + GMUX_PORT_VALUE); + gmux_index_wait_ready(); + outb(port, GMUX_IOSTART + GMUX_PORT_WRITE); + gmux_index_wait_complete(); +} + +u32 gmux_pio_read32(u8 port) +{ + return inl(GMUX_IOSTART + port); +} + +u32 gmux_index_read32(u8 port) +{ + u32 val; + + gmux_index_wait_ready(); + outb(port, GMUX_IOSTART + GMUX_PORT_READ); + gmux_index_wait_complete(); + val = inl(GMUX_IOSTART + GMUX_PORT_VALUE); + + return val; +} + +u8 gmux_read8(const struct device *dev, u8 port) +{ + const struct drivers_apple_hybrid_graphics_config + *config = dev->chip_info; + if (config->gmux_indexed) + return gmux_index_read8(port); + else + return gmux_pio_read8(port); +} + +void gmux_write8(const struct device *dev, u8 port, u8 val) +{ + const struct drivers_apple_hybrid_graphics_config + *config = dev->chip_info; + if (config->gmux_indexed) + gmux_index_write8(port, val); + else + gmux_pio_write8(port, val); +} + +u32 gmux_read32(const struct device *dev, u8 port) +{ + const struct drivers_apple_hybrid_graphics_config + *config = dev->chip_info; + if (config->gmux_indexed) + return gmux_index_read32(port); + else + return gmux_pio_read32(port); +} + +void gmux_dgpu_power_enable(const struct device *dev, bool enable) +{ + if (enable) { + gmux_write8(dev, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(dev, GMUX_PORT_DISCRETE_POWER, 3); + } else { + gmux_write8(dev, GMUX_PORT_DISCRETE_POWER, 1); + gmux_write8(dev, GMUX_PORT_DISCRETE_POWER, 0); + } +} + +void gmux_switch(const struct device *dev, bool dgpu) +{ + if (dgpu) { + gmux_write8(dev, GMUX_PORT_SWITCH_DDC, 2); + gmux_write8(dev, GMUX_PORT_SWITCH_DISPLAY, 3); + } else { + gmux_write8(dev, GMUX_PORT_SWITCH_DDC, 1); + gmux_write8(dev, GMUX_PORT_SWITCH_DISPLAY, 2); + } +} diff --git a/src/drivers/apple/hybrid_graphics/gmux.h b/src/drivers/apple/hybrid_graphics/gmux.h new file mode 100644 index 0000000000..075f9b6ca5 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/gmux.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef DRIVERS_APPLE_GMUX_H +#define DRIVERS_APPLE_GMUX_H + +#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_DDC 0x28 +#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_PORT_INTERRUPT_ENABLE 0x14 +#define GMUX_INTERRUPT_ENABLE 0xff +#define GMUX_INTERRUPT_DISABLE 0x00 + +#define GMUX_BRIGHTNESS_MASK 0x00ffffff +#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK + +#define GMUX_IOSTART 0x700 + +u8 gmux_index_read8(u8 port); +u8 gmux_pio_read8(u8 port); +u8 gmux_read8(const struct device *dev, u8 port); + +void gmux_index_write8(u8 port, u8 val); +void gmux_pio_write8(u8 port, u8 val); +void gmux_write8(const struct device *dev, u8 port, u8 val); + +u32 gmux_index_read32(u8 port); +u32 gmux_pio_read32(u8 port); +u32 gmux_read32(const struct device *dev, u8 port); + +void gmux_switch(const struct device *dev, bool dgpu); +void gmux_dgpu_power_enable(const struct device *dev, bool enable); + +#endif /* DRIVERS_APPLE_GMUX_H */ diff --git a/src/drivers/apple/hybrid_graphics/hybrid_graphics.c b/src/drivers/apple/hybrid_graphics/hybrid_graphics.c new file mode 100644 index 0000000000..7ec29fc345 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/hybrid_graphics.c @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include + +#include +#include +#include "chip.h" +#include "gmux.h" + +static void enable_dev(struct device *dev) +{ + const struct drivers_lenovo_hybrid_graphics_config *config; + enum hybrid_graphics_req mode; + u8 ver_major, ver_minor, ver_release; + u32 version, max_brightness, brightness; + + /* Don't confuse anyone else and disable the fake device */ + dev->enabled = 0; + + config = dev->chip_info; + if (!config) { + printk(BIOS_INFO, "Hybrid graphics: Not installed\n"); + return; + } + + version = gmux_index_read32(GMUX_PORT_VERSION_MAJOR); + ver_major = (version >> 24) & 0xff; + ver_minor = (version >> 16) & 0xff; + ver_release = (version >> 8) & 0xff; + max_brightness = gmux_index_read32(GMUX_PORT_MAX_BRIGHTNESS); + brightness = gmux_index_read32(GMUX_PORT_BRIGHTNESS) + & GMUX_BRIGHTNESS_MASK; + + printk(BIOS_INFO, "gmux version: %d.%d.%d\n", + ver_major, ver_minor, ver_release); + printk(BIOS_INFO, "gmux max brightness: %d\n", max_brightness); + printk(BIOS_INFO, "gmux brightness: %d\n", brightness); + + mode = HYBRID_GRAPHICS_DEFAULT_GPU; + get_option(&mode, "hybrid_graphics_mode"); + + gmux_switch(dev, mode == HYBRID_GRAPHICS_DISCRETE); +} + +struct chip_operations drivers_apple_hybrid_graphics_ops = { + CHIP_NAME("Apple hybrid graphics driver") + .enable_dev = enable_dev +}; diff --git a/src/drivers/apple/hybrid_graphics/hybrid_graphics.h b/src/drivers/apple/hybrid_graphics/hybrid_graphics.h new file mode 100644 index 0000000000..af2d56d528 --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/hybrid_graphics.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef _DRIVERS_APPLE_HYBRID_GRAPHICS_H_ +#define _DRIVERS_APPLE_HYBRID_GRAPHICS_H_ + +#define HYBRID_GRAPHICS_PORT 0xff +#define HYBRID_GRAPHICS_DEVICE 0xf + +void early_hybrid_graphics(bool *enable_igd, bool *enable_peg); + +#endif /* _DRIVERS_APPLE_HYBRID_GRAPHICS_CHIP_H_ */ diff --git a/src/drivers/apple/hybrid_graphics/romstage.c b/src/drivers/apple/hybrid_graphics/romstage.c new file mode 100644 index 0000000000..8f2667111b --- /dev/null +++ b/src/drivers/apple/hybrid_graphics/romstage.c @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#include +#include +#include +#include +#include "hybrid_graphics.h" +#include "chip.h" +#include "gmux.h" + +void early_hybrid_graphics(bool *enable_igd, bool *enable_peg) +{ + const struct device *dev; + enum hybrid_graphics_req mode = HYBRID_GRAPHICS_DEFAULT_GPU; + + /* TODO: Use generic device instead of dummy PNP device */ + dev = dev_find_slot_pnp(HYBRID_GRAPHICS_PORT, HYBRID_GRAPHICS_DEVICE); + + if (!dev || !dev->chip_info) { + printk(BIOS_ERR, "Hybrid graphics: ERROR\n"); + *enable_igd = true; + *enable_peg = false; + return; + } + + get_option(&mode, "hybrid_graphics_mode"); + + if (mode == HYBRID_GRAPHICS_DISCRETE) { + printk(BIOS_DEBUG, "Hybrid graphics:" + " Disabling integrated GPU.\n"); + + *enable_igd = false; + *enable_peg = true; + } else if (mode == HYBRID_GRAPHICS_INTEGRATED) { + printk(BIOS_DEBUG, "Hybrid graphics:" + " Disabling discrete GPU.\n"); + + *enable_igd = true; + *enable_peg = false; + } + + gmux_dgpu_power_enable(dev, *enable_peg); +} -- cgit v1.2.3