drivers/gfx/generic: Add support for gpio based EPS

Add support to control EPS via a PCH gpio

Change-Id: I6f570fd43e1649fb23255b0890e01086e34f844a
Signed-off-by: Rajat Jain <rajatja@google.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/39154
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Mathew King <mathewk@chromium.org>
diff --git a/src/drivers/gfx/generic/generic.c b/src/drivers/gfx/generic/generic.c
index 8488040..0b3fcca 100644
--- a/src/drivers/gfx/generic/generic.c
+++ b/src/drivers/gfx/generic/generic.c
@@ -24,6 +24,11 @@
 
 #define ACPI_DSM_PRIVACY_SCREEN_UUID	"C7033113-8720-4CEB-9090-9D52B3E52D73"
 
+#define ACPI_METHOD_EPS_PRESENT		"EPSP"
+#define ACPI_METHOD_EPS_STATE		"EPSS"
+#define ACPI_METHOD_EPS_ENABLE		"EPSE"
+#define ACPI_METHOD_EPS_DISABLE		"EPSD"
+
 static void privacy_screen_detect_cb(void *arg)
 {
 	struct drivers_gfx_generic_privacy_screen_config *config = arg;
@@ -62,6 +67,52 @@
 	privacy_screen_disable_cb,
 };
 
+static void privacy_gpio_acpigen(struct acpi_gpio *gpio)
+{
+	/* EPS Present */
+	acpigen_write_method(ACPI_METHOD_EPS_PRESENT, 0);
+	acpigen_write_return_byte(1);
+	acpigen_pop_len();
+
+	/* EPS State */
+	acpigen_write_method(ACPI_METHOD_EPS_STATE, 0);
+	acpigen_get_rx_gpio(gpio);
+	acpigen_emit_byte(RETURN_OP);
+	acpigen_emit_byte(LOCAL0_OP);
+	acpigen_pop_len();
+
+	/* EPS Enable */
+	acpigen_write_method(ACPI_METHOD_EPS_ENABLE, 0);
+	acpigen_enable_tx_gpio(gpio);
+	acpigen_pop_len();
+
+	/* EPS Disable */
+	acpigen_write_method(ACPI_METHOD_EPS_DISABLE, 0);
+	acpigen_disable_tx_gpio(gpio);
+	acpigen_pop_len();
+}
+
+static void gfx_fill_privacy_screen_dsm(
+		struct drivers_gfx_generic_privacy_screen_config *privacy)
+{
+	if (!privacy->enabled)
+		return;
+
+	/* Populate ACPI methods, if EPS controlled via gpio */
+	if (privacy->gpio.pin_count == 1) {
+		privacy_gpio_acpigen(&privacy->gpio);
+		privacy->detect_function = ACPI_METHOD_EPS_PRESENT;
+		privacy->status_function = ACPI_METHOD_EPS_STATE;
+		privacy->enable_function = ACPI_METHOD_EPS_ENABLE;
+		privacy->disable_function = ACPI_METHOD_EPS_DISABLE;
+	}
+
+	acpigen_write_dsm(ACPI_DSM_PRIVACY_SCREEN_UUID,
+		privacy_screen_callbacks,
+		ARRAY_SIZE(privacy_screen_callbacks),
+		privacy);
+}
+
 static void gfx_fill_ssdt_generator(struct device *dev)
 {
 	size_t i;
@@ -69,7 +120,7 @@
 
 	const char *scope = acpi_device_scope(dev);
 
-	if (!scope)
+	if (!scope || !dev->enabled)
 		return;
 
 	acpigen_write_scope(scope);
@@ -85,17 +136,9 @@
 
 	for (i = 0; i < config->device_count; i++) {
 		acpigen_write_device(config->device[i].name);
-
 		acpigen_write_name_integer("_ADR", config->device[i].addr);
 		acpigen_write_name_integer("_STA", 0xF);
-
-		if (config->device[i].privacy.enabled) {
-			acpigen_write_dsm(ACPI_DSM_PRIVACY_SCREEN_UUID,
-				privacy_screen_callbacks,
-				ARRAY_SIZE(privacy_screen_callbacks),
-				&config->device[i].privacy);
-		}
-
+		gfx_fill_privacy_screen_dsm(&config->device[i].privacy);
 		acpigen_pop_len(); /* Device */
 	}
 	acpigen_pop_len(); /* Scope */
@@ -117,7 +160,7 @@
 {
 	struct drivers_gfx_generic_config *config = dev->chip_info;
 
-	if (!config)
+	if (!config || !dev->enabled)
 		return;
 
 	dev->ops = &gfx_ops;
@@ -127,3 +170,14 @@
 	CHIP_NAME("Generic Graphics Device")
 	.enable_dev = gfx_enable
 };
+
+struct device *find_gfx_dev(void)
+{
+	struct device *dev;
+
+	for (dev = all_devices; dev; dev = dev->next) {
+		if (dev->chip_ops && dev->chip_ops == &drivers_gfx_generic_ops)
+			return dev;
+	}
+	return NULL;
+}