Commit ID: 25c2e13f9b6cf869efc9a8796a30b3b138929580
Change ID: wozsktpupxvwozummnopmkrqotuvokwr
Author   : Gregor Best <gbe@unobtanium.de> (2025-07-20 22:42:48)
Committer: Gregor Best <gbe@unobtanium.de> (2025-08-05 16:11:35)

    platform/x86: panasonic-laptop: add support for A1, A2 buttons on Panasonic CF-33

    On Panasonic CF-33, the buttons labelled A1 and A2 are handled by ACPI device MAT003C, which doesn't do much else. Attach to that device in addition to already detected ACPI devices that handle brightness buttons and similar things.

    Relax the checks on the minium SINF number a bit because the MAT003C device has only one of those and add checks later for code that assumes the size of the SINF array.

    Map the A1 and A2 keys to LAUNCH3 and LAUNCH4 instead of MACROxy because MACROxy keys do not seem to be supported by Xorg and result in no keys getting reported.

diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 255317e6fe..5d4e516d26 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -192,6 +192,7 @@
 	{ "MAT0013", 0},
 	{ "MAT0018", 0},
 	{ "MAT0019", 0},
+	{ "MAT003C", 0}, // CF33 "A1" and "A2" keys
 	{ "", 0},
 };
 MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
@@ -236,6 +237,11 @@
 	{ KE_KEY, 41, { KEY_MACRO9 } },
 	{ KE_KEY, 42, { KEY_MACRO10 } },
 	{ KE_KEY, 43, { KEY_MACRO11 } },
+	// These are not KEY_MACROxy because those can't be used in X11 (the key codes are too high.)
+	{ KE_KEY, 0x38, { KEY_PROG3 } }, // A1 key down on CF-33
+	{ KE_KEY, 0x39, { KEY_PROG3 } }, // A1 key up on CF-33
+	{ KE_KEY, 0x42, { KEY_PROG4 } }, // A2 key down on CF-33
+	{ KE_KEY, 0x43, { KEY_PROG4 } }, // A2 key up on CF-33
 	{ KE_END, 0 }
 };
 
@@ -847,8 +853,20 @@
 	}
 
 	key = result & GENMASK(6, 0);
+
 	updown = result & BIT(7); /* 0x80 == key down; 0x00 = key up */
 
+	/* hack: A1 and A2 keys on CF-33 generate two different key codes each,
+	   one for "key pressed" and one for "key released". Both are sent by the
+	   hardware as "key down" events, so we'll build our own key down/up
+	   events. */
+	if (key == 0x38 || key == 0x42) {
+		updown = 0x80; // Pretend this was "key down".
+	}
+	if (key == 0x39 || key == 0x43) {
+		updown = 0x0; // Pretend this was a "key up" event
+	}
+
 	/* hack: some firmware sends no key down for sleep / hibernate */
 	if (key == 7 || key == 10) {
 		if (updown)
@@ -927,7 +945,7 @@
 	}
 }
 
-static int acpi_pcc_init_input(struct pcc_acpi *pcc)
+static int acpi_pcc_init_input(struct pcc_acpi *pcc, const char *device_hid)
 {
 	struct input_dev *input_dev;
 	int error;
@@ -936,7 +954,7 @@
 	if (!input_dev)
 		return -ENOMEM;
 
-	input_dev->name = ACPI_PCC_DRIVER_NAME;
+	input_dev->name = kasprintf(GFP_KERNEL, "%s (%s)", ACPI_PCC_DRIVER_NAME, device_hid);
 	input_dev->phys = ACPI_PCC_INPUT_PHYS;
 	input_dev->id.bustype = BUS_HOST;
 	input_dev->id.vendor = 0x0001;
@@ -960,6 +978,7 @@
 
  err_free_dev:
 	input_free_device(input_dev);
+	kfree(input_dev->name);
 	return error;
 }
 
@@ -1003,11 +1022,10 @@
 	num_sifr = acpi_pcc_get_sqty(device);
 
 	/*
-	 * pcc->sinf is expected to at least have the AC+DC brightness entries.
-	 * Accesses to higher SINF entries are checked against num_sifr.
+	 * Check if pcc->sinf is sane.
 	 */
-	if (num_sifr <= SINF_DC_CUR_BRIGHT || num_sifr > 255) {
-		pr_err("num_sifr %d out of range %d - 255\n", num_sifr, SINF_DC_CUR_BRIGHT + 1);
+	if (num_sifr < 0 || num_sifr > 255) {
+		pr_err("num_sifr %d out of range 0 - 255 for %s\n", num_sifr, acpi_device_hid(device));
 		return -ENODEV;
 	}
 
@@ -1036,7 +1054,7 @@
 	strscpy(acpi_device_name(device), ACPI_PCC_DEVICE_NAME);
 	strscpy(acpi_device_class(device), ACPI_PCC_CLASS);
 
-	result = acpi_pcc_init_input(pcc);
+	result = acpi_pcc_init_input(pcc, acpi_device_hid(device));
 	if (result) {
 		pr_err("Error installing keyinput handler\n");
 		goto out_sinf;
@@ -1048,7 +1066,7 @@
 		goto out_input;
 	}
 
-	if (acpi_video_get_backlight_type() == acpi_backlight_vendor) {
+	if (acpi_video_get_backlight_type() == acpi_backlight_vendor && pcc->num_sifr > SINF_DC_CUR_BRIGHT) {
 		/* initialize backlight */
 		memset(&props, 0, sizeof(struct backlight_properties));
 		props.type = BACKLIGHT_PLATFORM;
@@ -1069,14 +1087,16 @@
 	acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0);
 	pcc->sticky_key = 0;
 
-	pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
-	pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT];
-	if (pcc->num_sifr > SINF_MUTE)
-		pcc->mute = pcc->sinf[SINF_MUTE];
-	if (pcc->num_sifr > SINF_ECO_MODE)
-		pcc->eco_mode = pcc->sinf[SINF_ECO_MODE];
-	if (pcc->num_sifr > SINF_CUR_BRIGHT)
-		pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];
+	if (pcc->num_sifr > SINF_DC_CUR_BRIGHT) {
+		pcc->ac_brightness = pcc->sinf[SINF_AC_CUR_BRIGHT];
+		pcc->dc_brightness = pcc->sinf[SINF_DC_CUR_BRIGHT];
+		if (pcc->num_sifr > SINF_MUTE)
+			pcc->mute = pcc->sinf[SINF_MUTE];
+		if (pcc->num_sifr > SINF_ECO_MODE)
+			pcc->eco_mode = pcc->sinf[SINF_ECO_MODE];
+		if (pcc->num_sifr > SINF_CUR_BRIGHT)
+			pcc->current_brightness = pcc->sinf[SINF_CUR_BRIGHT];
+	}
 
 	/* add sysfs attributes */
 	result = sysfs_create_group(&device->dev.kobj, &pcc_attr_group);
@@ -1101,6 +1121,7 @@
 	}
 
 	i8042_install_filter(panasonic_i8042_filter, NULL);
+
 	return 0;
 
 out_platform:
@@ -1138,6 +1159,7 @@
 
 	input_unregister_device(pcc->input_dev);
 
+	kfree(pcc->input_dev->name);
 	kfree(pcc->sinf);
 	kfree(pcc);
 }
