Add keyboard LED class driver
This commit is contained in:
parent
48a919d220
commit
fb13ee2dfe
|
|
@ -19,18 +19,18 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static struct workqueue_struct *led_workqueue;
|
static struct workqueue_struct *ap_led_workqueue;
|
||||||
|
|
||||||
static struct _led_work {
|
static struct _ap_led_work {
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
int wk;
|
int wk;
|
||||||
} led_work;
|
} ap_led_work;
|
||||||
|
|
||||||
static void airplane_led_update(struct work_struct *work) {
|
static void ap_led_update(struct work_struct *work) {
|
||||||
u8 byte;
|
u8 byte;
|
||||||
struct _led_work *w;
|
struct _ap_led_work *w;
|
||||||
|
|
||||||
w = container_of(work, struct _led_work, work);
|
w = container_of(work, struct _ap_led_work, work);
|
||||||
|
|
||||||
ec_read(0xD9, &byte);
|
ec_read(0xD9, &byte);
|
||||||
|
|
||||||
|
|
@ -39,7 +39,7 @@ static void airplane_led_update(struct work_struct *work) {
|
||||||
/* wmbb 0x6C 1 (?) */
|
/* wmbb 0x6C 1 (?) */
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum led_brightness airplane_led_get(struct led_classdev *led_cdev) {
|
static enum led_brightness ap_led_get(struct led_classdev *led_cdev) {
|
||||||
u8 byte;
|
u8 byte;
|
||||||
|
|
||||||
ec_read(0xD9, &byte);
|
ec_read(0xD9, &byte);
|
||||||
|
|
@ -48,30 +48,30 @@ static enum led_brightness airplane_led_get(struct led_classdev *led_cdev) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* must not sleep */
|
/* must not sleep */
|
||||||
static void airplane_led_set(struct led_classdev *led_cdev, enum led_brightness value) {
|
static void ap_led_set(struct led_classdev *led_cdev, enum led_brightness value) {
|
||||||
led_work.wk = value;
|
ap_led_work.wk = value;
|
||||||
queue_work(led_workqueue, &led_work.work);
|
queue_work(ap_led_workqueue, &ap_led_work.work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct led_classdev airplane_led = {
|
static struct led_classdev ap_led = {
|
||||||
.name = "system76::airplane",
|
.name = "system76::airplane",
|
||||||
.brightness_get = airplane_led_get,
|
.brightness_get = ap_led_get,
|
||||||
.brightness_set = airplane_led_set,
|
.brightness_set = ap_led_set,
|
||||||
.max_brightness = 1,
|
.max_brightness = 1,
|
||||||
.default_trigger = "rfkill-any"
|
.default_trigger = "rfkill-any"
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init s76_led_init(void) {
|
static int __init ap_led_init(struct device *dev) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
led_workqueue = create_singlethread_workqueue("led_workqueue");
|
ap_led_workqueue = create_singlethread_workqueue("ap_led_workqueue");
|
||||||
if (unlikely(!led_workqueue)) {
|
if (unlikely(!ap_led_workqueue)) {
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&led_work.work, airplane_led_update);
|
INIT_WORK(&ap_led_work.work, ap_led_update);
|
||||||
|
|
||||||
err = led_classdev_register(&s76_platform_device->dev, &airplane_led);
|
err = led_classdev_register(dev, &ap_led);
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
goto err_destroy_workqueue;
|
goto err_destroy_workqueue;
|
||||||
}
|
}
|
||||||
|
|
@ -79,15 +79,15 @@ static int __init s76_led_init(void) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_destroy_workqueue:
|
err_destroy_workqueue:
|
||||||
destroy_workqueue(led_workqueue);
|
destroy_workqueue(ap_led_workqueue);
|
||||||
led_workqueue = NULL;
|
ap_led_workqueue = NULL;
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit s76_led_exit(void) {
|
static void __exit ap_led_exit(void) {
|
||||||
if (!IS_ERR_OR_NULL(airplane_led.dev))
|
if (!IS_ERR_OR_NULL(ap_led.dev))
|
||||||
led_classdev_unregister(&airplane_led);
|
led_classdev_unregister(&ap_led);
|
||||||
if (led_workqueue)
|
if (ap_led_workqueue)
|
||||||
destroy_workqueue(led_workqueue);
|
destroy_workqueue(ap_led_workqueue);
|
||||||
}
|
}
|
||||||
4
input.c
4
input.c
|
|
@ -138,7 +138,7 @@ static void s76_input_close(struct input_dev *dev) {
|
||||||
s76_input_polling_task = NULL;
|
s76_input_polling_task = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init s76_input_init(void) {
|
static int __init s76_input_init(struct device *dev) {
|
||||||
int err;
|
int err;
|
||||||
u8 byte;
|
u8 byte;
|
||||||
|
|
||||||
|
|
@ -151,7 +151,7 @@ static int __init s76_input_init(void) {
|
||||||
s76_input_device->name = "System76 Airplane-Mode Hotkey";
|
s76_input_device->name = "System76 Airplane-Mode Hotkey";
|
||||||
s76_input_device->phys = "system76/input0";
|
s76_input_device->phys = "system76/input0";
|
||||||
s76_input_device->id.bustype = BUS_HOST;
|
s76_input_device->id.bustype = BUS_HOST;
|
||||||
s76_input_device->dev.parent = &s76_platform_device->dev;
|
s76_input_device->dev.parent = dev;
|
||||||
set_bit(EV_KEY, s76_input_device->evbit);
|
set_bit(EV_KEY, s76_input_device->evbit);
|
||||||
set_bit(AIRPLANE_KEY, s76_input_device->keybit);
|
set_bit(AIRPLANE_KEY, s76_input_device->keybit);
|
||||||
set_bit(KEY_TOUCHPAD_ON, s76_input_device->keybit);
|
set_bit(KEY_TOUCHPAD_ON, s76_input_device->keybit);
|
||||||
|
|
|
||||||
4
kb.c
4
kb.c
|
|
@ -289,6 +289,8 @@ static void kb_full_color__set_brightness(unsigned i)
|
||||||
{
|
{
|
||||||
u8 lvl_to_raw[] = { 63, 126, 189, 252 };
|
u8 lvl_to_raw[] = { 63, 126, 189, 252 };
|
||||||
|
|
||||||
|
led_classdev_notify_brightness_hw_changed(&kb_led, i);
|
||||||
|
|
||||||
i = clamp_t(unsigned, i, 0, ARRAY_SIZE(lvl_to_raw) - 1);
|
i = clamp_t(unsigned, i, 0, ARRAY_SIZE(lvl_to_raw) - 1);
|
||||||
|
|
||||||
if (!s76_wmbb(SET_KB_LED,
|
if (!s76_wmbb(SET_KB_LED,
|
||||||
|
|
@ -334,9 +336,11 @@ static void kb_full_color__set_state(enum kb_state state)
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case KB_STATE_OFF:
|
case KB_STATE_OFF:
|
||||||
|
led_classdev_notify_brightness_hw_changed(&kb_led, 0);
|
||||||
cmd |= 0x003001;
|
cmd |= 0x003001;
|
||||||
break;
|
break;
|
||||||
case KB_STATE_ON:
|
case KB_STATE_ON:
|
||||||
|
led_classdev_notify_brightness_hw_changed(&kb_led, kb_backlight.brightness);
|
||||||
cmd |= 0x07F001;
|
cmd |= 0x07F001;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* led.c
|
||||||
|
*
|
||||||
|
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
||||||
|
* Copyright (C) 2014-2016 Arnoud Willemsen <mail@lynthium.com>
|
||||||
|
* Copyright (C) 2013-2015 TUXEDO Computers GmbH <tux@tuxedocomputers.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static struct workqueue_struct *kb_led_workqueue;
|
||||||
|
|
||||||
|
static struct _kb_led_work {
|
||||||
|
struct work_struct work;
|
||||||
|
int wk;
|
||||||
|
} kb_led_work;
|
||||||
|
|
||||||
|
static void kb_led_update(struct work_struct *work) {
|
||||||
|
u8 byte;
|
||||||
|
struct _kb_led_work *w;
|
||||||
|
|
||||||
|
w = container_of(work, struct _kb_led_work, work);
|
||||||
|
//
|
||||||
|
// ec_read(0xD9, &byte);
|
||||||
|
//
|
||||||
|
// ec_write(0xD9, w->wk ? byte & ~0x40 : byte | 0x40);
|
||||||
|
//
|
||||||
|
// /* wmbb 0x6C 1 (?) */
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum led_brightness kb_led_get(struct led_classdev *led_cdev) {
|
||||||
|
// u8 byte;
|
||||||
|
//
|
||||||
|
// ec_read(0xD9, &byte);
|
||||||
|
//
|
||||||
|
// return byte & 0x40 ? LED_OFF : LED_FULL;
|
||||||
|
|
||||||
|
return LED_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* must not sleep */
|
||||||
|
static void kb_led_set(struct led_classdev *led_cdev, enum led_brightness value) {
|
||||||
|
kb_led_work.wk = value;
|
||||||
|
queue_work(kb_led_workqueue, &kb_led_work.work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct led_classdev kb_led = {
|
||||||
|
.name = "system76::kbd_backlight",
|
||||||
|
.flags = LED_BRIGHT_HW_CHANGED,
|
||||||
|
.brightness_get = kb_led_get,
|
||||||
|
.brightness_set = kb_led_set,
|
||||||
|
.max_brightness = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init kb_led_init(struct device *dev) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
kb_led_workqueue = create_singlethread_workqueue("kb_led_workqueue");
|
||||||
|
if (unlikely(!kb_led_workqueue)) {
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&kb_led_work.work, kb_led_update);
|
||||||
|
|
||||||
|
err = led_classdev_register(dev, &kb_led);
|
||||||
|
if (unlikely(err)) {
|
||||||
|
goto err_destroy_workqueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err_destroy_workqueue:
|
||||||
|
destroy_workqueue(kb_led_workqueue);
|
||||||
|
kb_led_workqueue = NULL;
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit kb_led_exit(void) {
|
||||||
|
if (!IS_ERR_OR_NULL(kb_led.dev))
|
||||||
|
led_classdev_unregister(&kb_led);
|
||||||
|
if (kb_led_workqueue)
|
||||||
|
destroy_workqueue(kb_led_workqueue);
|
||||||
|
}
|
||||||
123
system76.c
123
system76.c
|
|
@ -91,8 +91,9 @@ static int s76_wmbb(u32 method_id, u32 arg, u32 *retval) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ec.c"
|
#include "ec.c"
|
||||||
#include "led.c"
|
#include "ap_led.c"
|
||||||
#include "input.c"
|
#include "input.c"
|
||||||
|
#include "kb_led.c"
|
||||||
#include "kb.c"
|
#include "kb.c"
|
||||||
#include "hwmon.c"
|
#include "hwmon.c"
|
||||||
|
|
||||||
|
|
@ -177,11 +178,55 @@ static void s76_wmi_notify(u32 value, void *context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s76_probe(struct platform_device *dev) {
|
static int s76_probe(struct platform_device *dev) {
|
||||||
int status;
|
int err;
|
||||||
|
|
||||||
status = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL);
|
err = ec_init();
|
||||||
if (unlikely(ACPI_FAILURE(status))) {
|
if (unlikely(err)) {
|
||||||
S76_ERROR("Could not register WMI notify handler (%0#6x)\n", status);
|
S76_ERROR("Could not register EC device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ap_led_init(&dev->dev);
|
||||||
|
if (unlikely(err)) {
|
||||||
|
S76_ERROR("Could not register LED device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kb_led_init(&dev->dev);
|
||||||
|
if (unlikely(err)) {
|
||||||
|
S76_ERROR("Could not register LED device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s76_input_init(&dev->dev);
|
||||||
|
if (unlikely(err)) {
|
||||||
|
S76_ERROR("Could not register input device\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_create_file(&dev->dev, &dev_attr_kb_brightness) != 0) {
|
||||||
|
S76_ERROR("Sysfs attribute creation failed for brightness\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_create_file(&dev->dev, &dev_attr_kb_state) != 0) {
|
||||||
|
S76_ERROR("Sysfs attribute creation failed for state\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_create_file(&dev->dev, &dev_attr_kb_mode) != 0) {
|
||||||
|
S76_ERROR("Sysfs attribute creation failed for mode\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device_create_file(&dev->dev, &dev_attr_kb_color) != 0) {
|
||||||
|
S76_ERROR("Sysfs attribute creation failed for color\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kb_backlight.ops) {
|
||||||
|
kb_backlight.ops->init();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef S76_HAS_HWMON
|
||||||
|
s76_hwmon_init(&dev->dev);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
err = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL);
|
||||||
|
if (unlikely(ACPI_FAILURE(err))) {
|
||||||
|
S76_ERROR("Could not register WMI notify handler (%0#6x)\n", err);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,16 +238,27 @@ static int s76_probe(struct platform_device *dev) {
|
||||||
//i8042_command(NULL, 0x97);
|
//i8042_command(NULL, 0x97);
|
||||||
//i8042_unlock_chip();
|
//i8042_unlock_chip();
|
||||||
|
|
||||||
if (kb_backlight.ops) {
|
|
||||||
kb_backlight.ops->init();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s76_remove(struct platform_device *dev) {
|
static int s76_remove(struct platform_device *dev) {
|
||||||
wmi_remove_notify_handler(S76_EVENT_GUID);
|
wmi_remove_notify_handler(S76_EVENT_GUID);
|
||||||
|
|
||||||
|
#ifdef S76_HAS_HWMON
|
||||||
|
s76_hwmon_fini(&dev->dev);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
device_remove_file(&dev->dev, &dev_attr_kb_color);
|
||||||
|
device_remove_file(&dev->dev, &dev_attr_kb_mode);
|
||||||
|
device_remove_file(&dev->dev, &dev_attr_kb_state);
|
||||||
|
device_remove_file(&dev->dev, &dev_attr_kb_brightness);
|
||||||
|
|
||||||
|
s76_input_exit();
|
||||||
|
kb_led_exit();
|
||||||
|
ap_led_exit();
|
||||||
|
|
||||||
|
ec_exit();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,59 +336,10 @@ static int __init s76_init(void) {
|
||||||
return PTR_ERR(s76_platform_device);
|
return PTR_ERR(s76_platform_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s76_input_init();
|
|
||||||
if (unlikely(err)) {
|
|
||||||
S76_ERROR("Could not register input device\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
err = s76_led_init();
|
|
||||||
if (unlikely(err)) {
|
|
||||||
S76_ERROR("Could not register LED device\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_brightness) != 0) {
|
|
||||||
S76_ERROR("Sysfs attribute creation failed for brightness\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_state) != 0) {
|
|
||||||
S76_ERROR("Sysfs attribute creation failed for state\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_mode) != 0) {
|
|
||||||
S76_ERROR("Sysfs attribute creation failed for mode\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (device_create_file(&s76_platform_device->dev, &dev_attr_kb_color) != 0) {
|
|
||||||
S76_ERROR("Sysfs attribute creation failed for color\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef S76_HAS_HWMON
|
|
||||||
s76_hwmon_init(&s76_platform_device->dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
err = ec_init();
|
|
||||||
if (unlikely(err)) {
|
|
||||||
S76_ERROR("Could not register EC device\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit s76_exit(void) {
|
static void __exit s76_exit(void) {
|
||||||
ec_exit();
|
|
||||||
|
|
||||||
#ifdef S76_HAS_HWMON
|
|
||||||
s76_hwmon_fini(&s76_platform_device->dev);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
device_remove_file(&s76_platform_device->dev, &dev_attr_kb_color);
|
|
||||||
device_remove_file(&s76_platform_device->dev, &dev_attr_kb_mode);
|
|
||||||
device_remove_file(&s76_platform_device->dev, &dev_attr_kb_state);
|
|
||||||
device_remove_file(&s76_platform_device->dev, &dev_attr_kb_brightness);
|
|
||||||
|
|
||||||
s76_led_exit();
|
|
||||||
s76_input_exit();
|
|
||||||
|
|
||||||
platform_device_unregister(s76_platform_device);
|
platform_device_unregister(s76_platform_device);
|
||||||
platform_driver_unregister(&s76_platform_driver);
|
platform_driver_unregister(&s76_platform_driver);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue