aboutsummaryrefslogtreecommitdiff
path: root/src/drivers/apple/hybrid_graphics/gmux.c
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.com>2019-05-10 01:50:45 +0300
committerEvgeny Zinoviev <me@ch1p.io>2021-02-09 02:26:21 +0300
commit67c4ac4764c85aacc1ace749b3e4a6c0781de990 (patch)
treeb7fdb820af31e99155e897b7452cba4c6f61782b /src/drivers/apple/hybrid_graphics/gmux.c
parentb743178ab3012aacc8210667d664c1098244cec9 (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/drivers/apple/hybrid_graphics/gmux.c')
-rw-r--r--src/drivers/apple/hybrid_graphics/gmux.c138
1 files changed, 138 insertions, 0 deletions
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);
+ }
+}