summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);
+}