From fd6e3e7bf0c5316ca2d0d1715352e27360e68f2e Mon Sep 17 00:00:00 2001
From: "Dustin L. Howett" <dustin@howett.net>
Date: Fri, 4 Aug 2023 22:12:50 -0500
Subject: [PATCH] Framework ACPI: Switch to ACPI driver from Platform driver

---
 framework_laptop.c | 123 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 117 insertions(+), 6 deletions(-)

diff --git a/framework_laptop.c b/framework_laptop.c
index 21ed49a..4a1023b 100644
--- a/framework_laptop.c
+++ b/framework_laptop.c
@@ -32,6 +32,10 @@
 #define FRAMEWORK_LAPTOP_EC_DEVICE_NAME "cros_ec_lpcs.0"
 
 static struct device *ec_device;
+struct framework_data {
+	struct acpi_device *acpi_dev;
+	struct led_classdev kb_led;
+};
 
 #define EC_CMD_CHARGE_LIMIT_CONTROL 0x3E03
 
@@ -95,6 +99,83 @@ static int charge_limit_control(enum ec_chg_limit_control_modes modes, uint8_t m
 	return resp->max_percentage;
 }
 
+// Get the last set keyboard LED brightness
+static enum led_brightness kb_led_get(struct led_classdev *led)
+{
+	struct {
+		struct cros_ec_command msg;
+		union {
+			struct ec_response_pwm_get_keyboard_backlight resp;
+		};
+	} __packed buf;
+
+	struct ec_response_pwm_get_keyboard_backlight *resp = &buf.resp;
+	struct cros_ec_command *msg = &buf.msg;
+	struct cros_ec_device *ec;
+	int ret;
+	if (!ec_device)
+		goto out;
+
+	ec = dev_get_drvdata(ec_device);
+
+	memset(&buf, 0, sizeof(buf));
+	
+	msg->version = 0;
+	msg->command = EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT;
+	msg->insize = sizeof(*resp);
+	msg->outsize = 0;
+
+	ret = cros_ec_cmd_xfer_status(ec, msg);
+	if (ret < 0) {
+		goto out;
+	}
+
+	if (resp->enabled) {
+		return resp->percent;
+	}
+
+out:
+	return 0;
+}
+
+// Set the keyboard LED brightness
+static int kb_led_set(struct led_classdev *led, enum led_brightness value)
+{
+	struct {
+		struct cros_ec_command msg;
+		union {
+			struct ec_params_pwm_set_keyboard_backlight params;
+		};
+	} __packed buf;
+
+	struct ec_params_pwm_set_keyboard_backlight *params = &buf.params;
+	struct cros_ec_command *msg = &buf.msg;
+	struct cros_ec_device *ec;
+	int ret;
+
+	if (!ec_device)
+		return -EIO;
+
+	ec = dev_get_drvdata(ec_device);
+
+	memset(&buf, 0, sizeof(buf));
+	
+	msg->version = 0;
+	msg->command = EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT;
+	msg->insize = 0;
+	msg->outsize = sizeof(*params);
+
+	params->percent = value;
+
+	ret = cros_ec_cmd_xfer_status(ec, msg);
+	if (ret < 0) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
 static ssize_t battery_get_threshold(char *buf)
 {
 	int ret;
@@ -146,7 +227,7 @@ static struct attribute *framework_laptop_battery_attrs[] = {
 
 ATTRIBUTE_GROUPS(framework_laptop_battery);
 
-static int framework_laptop_battery_add(struct power_supply *battery)
+static int framework_laptop_battery_add(struct power_supply *battery, struct acpi_battery_hook *hook)
 {
 	// Framework EC only supports 1 battery
 	if (strcmp(battery->desc->name, "BAT1") != 0)
@@ -158,7 +239,7 @@ static int framework_laptop_battery_add(struct power_supply *battery)
 	return 0;
 }
 
-static int framework_laptop_battery_remove(struct power_supply *battery)
+static int framework_laptop_battery_remove(struct power_supply *battery, struct acpi_battery_hook *hook)
 {
 	device_remove_groups(&battery->dev, framework_laptop_battery_groups);
 	return 0;
@@ -170,6 +251,12 @@ static struct acpi_battery_hook framework_laptop_battery_hook = {
 	.name = "Framework Laptop Battery Extension",
 };
 
+static const struct acpi_device_id device_ids[] = {
+	{"FRMW0001", 0},
+	{"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, device_ids);
+
 static const struct dmi_system_id framework_laptop_dmi_table[] __initconst = {
 	{
 		/* the Framework Laptop */
@@ -182,8 +269,9 @@ static const struct dmi_system_id framework_laptop_dmi_table[] __initconst = {
 };
 MODULE_DEVICE_TABLE(dmi, framework_laptop_dmi_table);
 
-static int __init framework_laptop_init(void)
+static int framework_add(struct acpi_device *acpi_dev)
 {
+	struct framework_data *data;
 	int ret = 0;
 
 	if (!dmi_check_system(framework_laptop_dmi_table)) {
@@ -195,6 +283,21 @@ static int __init framework_laptop_init(void)
 	if (!ec_device)
 		return -EINVAL;
 
+	data = devm_kzalloc(&acpi_dev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	acpi_dev->driver_data = data;
+	data->acpi_dev = acpi_dev;
+
+	data->kb_led.name = "framework_acpi::kbd_backlight";
+	data->kb_led.brightness_get = kb_led_get;
+	data->kb_led.brightness_set_blocking = kb_led_set;
+	data->kb_led.max_brightness = 100;
+	ret = devm_led_classdev_register(&acpi_dev->dev, &data->kb_led);
+	if (ret)
+		return ret;
+
 #if 0
 	/* Register the driver */
 	ret = platform_driver_register(&cros_ec_lpc_driver);
@@ -216,15 +319,23 @@ static int __init framework_laptop_init(void)
 	return ret;
 }
 
-static void __exit framework_laptop_exit(void)
+static void framework_remove(struct acpi_device *acpi_dev)
 {
 	battery_hook_unregister(&framework_laptop_battery_hook);
 
 	put_device(ec_device);
 }
 
-module_init(framework_laptop_init);
-module_exit(framework_laptop_exit);
+static struct acpi_driver framework_driver = {
+	.name = "Framework ACPI Driver",
+	.class = "laptop",
+	.ids = device_ids,
+	.ops = {
+		.add = framework_add,
+		.remove = framework_remove,
+	},
+};
+module_acpi_driver(framework_driver);
 
 MODULE_DESCRIPTION("Framework Laptop Platform Driver");
 MODULE_AUTHOR("Dustin L. Howett <dustin@howett.net>");
-- 
GitLab