summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.com>2019-05-10 01:50:45 +0300
committerEvgeny Zinoviev <me@ch1p.io>2021-02-03 22:04:21 +0300
commit4908440ccf6f7c2ae281a3e5b3163440cc7f45ef (patch)
tree30a7996174b7c26da6d2d0b42dc4a26319101f87 /src
parent952830607c23c861991b1e9a1250bf1e6827af90 (diff)
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. <seth.forshee@canonical.com> * Andreas Heider, 2010-2012 <andreas@meetr.de> * Lukas Wunner, 2015 <lukas@wunner.de> Tested on MacBook Pro Retina 15 Mid 2012 (MacBook Pro 10,1). Change-Id: I22b66622cd2da0e9951ee726d650d204fbb8a5bc Signed-off-by: Evgeny Zinoviev <me@ch1p.io>
Diffstat (limited to 'src')
-rw-r--r--src/drivers/apple/hybrid_graphics/Kconfig3
-rw-r--r--src/drivers/apple/hybrid_graphics/Makefile.inc4
-rw-r--r--src/drivers/apple/hybrid_graphics/acpi/gmux.asl39
-rw-r--r--src/drivers/apple/hybrid_graphics/chip.h17
-rw-r--r--src/drivers/apple/hybrid_graphics/gmux.c138
-rw-r--r--src/drivers/apple/hybrid_graphics/gmux.h43
-rw-r--r--src/drivers/apple/hybrid_graphics/hybrid_graphics.c50
-rw-r--r--src/drivers/apple/hybrid_graphics/hybrid_graphics.h11
-rw-r--r--src/drivers/apple/hybrid_graphics/romstage.c43
9 files changed, 348 insertions, 0 deletions
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 <delay.h>
+#include <arch/io.h>
+#include <device/device.h>
+#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 <types.h>
+#include <option.h>
+#include <device/device.h>
+
+#include <southbridge/intel/common/gpio.h>
+#include <console/console.h>
+#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 <types.h>
+#include <option.h>
+#include <device/device.h>
+#include <console/console.h>
+#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);
+}