Compare commits
18 Commits
f01bffda3c
...
d3d9ce28c6
| Author | SHA1 | Date |
|---|---|---|
|
|
d3d9ce28c6 | |
|
|
79f5488fd2 | |
|
|
44aa423b2c | |
|
|
293de8964e | |
|
|
3fa766c549 | |
|
|
e457babab4 | |
|
|
9c210e3060 | |
|
|
cc4802bd26 | |
|
|
dd00fe93ca | |
|
|
cc41a32079 | |
|
|
76181a1acc | |
|
|
af173d245a | |
|
|
f886317ae8 | |
|
|
c81a34d0fc | |
|
|
5ea0d7de14 | |
|
|
c24eadde07 | |
|
|
8b1da5327c | |
|
|
dfb9ca4b39 |
|
|
@ -0,0 +1,21 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.c]
|
||||||
|
indent_size = 8
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[{Makefile,Makefile.*,*.mk}]
|
||||||
|
indent_size = 8
|
||||||
|
indent_style = tab
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = space
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
obj-y += src/
|
||||||
3
Makefile
3
Makefile
|
|
@ -1,4 +1,5 @@
|
||||||
obj-m := system76.o
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
KERNEL_DIR = /lib/modules/$(shell uname -r)/build
|
KERNEL_DIR = /lib/modules/$(shell uname -r)/build
|
||||||
|
|
||||||
all:
|
all:
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ make
|
||||||
# Remove any old instances
|
# Remove any old instances
|
||||||
sudo modprobe -r system76
|
sudo modprobe -r system76
|
||||||
# Insert the new module
|
# Insert the new module
|
||||||
sudo insmod system76.ko
|
sudo insmod src/system76.ko
|
||||||
# View log messages
|
# View log messages
|
||||||
dmesg | grep system76
|
dmesg | grep system76
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,28 @@
|
||||||
|
system76-dkms (1.0.21) jammy; urgency=medium
|
||||||
|
|
||||||
|
* Add oryp13
|
||||||
|
|
||||||
|
-- Tim Crawford <tcrawford@system76.com> Tue, 16 Sep 2025 13:04:08 -0600
|
||||||
|
|
||||||
|
system76-dkms (1.0.20) focal; urgency=medium
|
||||||
|
|
||||||
|
* Refactor code
|
||||||
|
* Add addw5
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Tue, 01 Jul 2025 11:27:12 -0600
|
||||||
|
|
||||||
|
system76-dkms (1.0.19) focal; urgency=medium
|
||||||
|
|
||||||
|
* Add serw14
|
||||||
|
|
||||||
|
-- Tim Crawford <tcrawford@system76.com> Thu, 05 Jun 2025 17:27:15 -0600
|
||||||
|
|
||||||
|
system76-dkms (1.0.18) focal; urgency=medium
|
||||||
|
|
||||||
|
* Add bonw16
|
||||||
|
|
||||||
|
-- Jeremy Soller <jeremy@system76.com> Thu, 27 Mar 2025 15:04:41 -0600
|
||||||
|
|
||||||
system76-dkms (1.0.17) focal; urgency=medium
|
system76-dkms (1.0.17) focal; urgency=medium
|
||||||
|
|
||||||
* Fix building on Linux 6.12
|
* Fix building on Linux 6.12
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ include /usr/share/dpkg/pkg-info.mk
|
||||||
dh $@ --with dkms
|
dh $@ --with dkms
|
||||||
|
|
||||||
override_dh_install:
|
override_dh_install:
|
||||||
dh_install Makefile *.c usr/src/system76-$(DEB_VERSION_UPSTREAM)/
|
dh_install Makefile Kbuild src/Kbuild src/*.c usr/src/system76-$(DEB_VERSION_UPSTREAM)/
|
||||||
|
|
||||||
override_dh_dkms:
|
override_dh_dkms:
|
||||||
dh_dkms -V $(DEB_VERSION_UPSTREAM)
|
dh_dkms -V $(DEB_VERSION_UPSTREAM)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
obj-m += system76.o
|
||||||
|
|
@ -1,31 +1,21 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* ap_led.c
|
* ap_led.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.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 enum led_brightness ap_led_brightness = 1;
|
static enum led_brightness ap_led_brightness = 1;
|
||||||
|
|
||||||
static bool ap_led_invert = TRUE;
|
static bool ap_led_invert = TRUE;
|
||||||
|
|
||||||
static enum led_brightness ap_led_get(struct led_classdev *led_cdev) {
|
static enum led_brightness ap_led_get(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
return ap_led_brightness;
|
return ap_led_brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ap_led_set(struct led_classdev *led_cdev, enum led_brightness value) {
|
static int ap_led_set(struct led_classdev *led_cdev, enum led_brightness value)
|
||||||
|
{
|
||||||
u8 byte;
|
u8 byte;
|
||||||
|
|
||||||
ec_read(0xD9, &byte);
|
ec_read(0xD9, &byte);
|
||||||
|
|
@ -54,18 +44,20 @@ static int ap_led_set(struct led_classdev *led_cdev, enum led_brightness value)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct led_classdev ap_led = {
|
static struct led_classdev ap_led = {
|
||||||
.name = "system76::airplane",
|
.name = "system76::airplane",
|
||||||
.brightness_get = ap_led_get,
|
.brightness_get = ap_led_get,
|
||||||
.brightness_set_blocking = ap_led_set,
|
.brightness_set_blocking = ap_led_set,
|
||||||
.max_brightness = 1,
|
.max_brightness = 1,
|
||||||
.default_trigger = "rfkill-any"
|
.default_trigger = "rfkill-any"
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t ap_led_invert_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
static ssize_t ap_led_invert_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
return sprintf(buf, "%d\n", (int)ap_led_invert);
|
{
|
||||||
|
return sysfs_emit(buf, "%d\n", (int)ap_led_invert);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ap_led_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) {
|
static ssize_t ap_led_invert_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
int ret;
|
int ret;
|
||||||
enum led_brightness brightness;
|
enum led_brightness brightness;
|
||||||
|
|
@ -97,20 +89,23 @@ static struct device_attribute ap_led_invert_dev_attr = {
|
||||||
.store = ap_led_invert_store,
|
.store = ap_led_invert_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ap_led_resume(void) {
|
static void ap_led_resume(void)
|
||||||
|
{
|
||||||
ap_led_set(&ap_led, ap_led_brightness);
|
ap_led_set(&ap_led, ap_led_brightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init ap_led_init(struct device *dev) {
|
static int __init ap_led_init(struct device *dev)
|
||||||
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = led_classdev_register(dev, &ap_led);
|
err = devm_led_classdev_register(dev, &ap_led);
|
||||||
if (unlikely(err)) {
|
if (err < 0) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_create_file(ap_led.dev, &ap_led_invert_dev_attr) != 0) {
|
err = device_create_file(ap_led.dev, &ap_led_invert_dev_attr);
|
||||||
S76_ERROR("failed to create ap_led_invert\n");
|
if (err < 0) {
|
||||||
|
pr_err("failed to create ap_led_invert\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
ap_led_resume();
|
ap_led_resume();
|
||||||
|
|
@ -118,10 +113,7 @@ static int __init ap_led_init(struct device *dev) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit ap_led_exit(void) {
|
static void __exit ap_led_exit(void)
|
||||||
|
{
|
||||||
device_remove_file(ap_led.dev, &ap_led_invert_dev_attr);
|
device_remove_file(ap_led.dev, &ap_led_invert_dev_attr);
|
||||||
|
|
||||||
if (!IS_ERR_OR_NULL(ap_led.dev)) {
|
|
||||||
led_classdev_unregister(&ap_led);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/*
|
||||||
|
* fan.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>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define EXPERIMENTAL
|
||||||
|
|
||||||
|
#if S76_HAS_HWMON
|
||||||
|
|
||||||
|
struct s76_hwmon {
|
||||||
|
struct device *dev;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct s76_hwmon *s76_hwmon;
|
||||||
|
|
||||||
|
static int s76_read_fan(int idx)
|
||||||
|
{
|
||||||
|
u8 value;
|
||||||
|
int raw_rpm;
|
||||||
|
|
||||||
|
ec_read(0xd0 + 0x2 * idx, &value);
|
||||||
|
raw_rpm = value << 8;
|
||||||
|
ec_read(0xd1 + 0x2 * idx, &value);
|
||||||
|
raw_rpm += value;
|
||||||
|
if (!raw_rpm)
|
||||||
|
return 0;
|
||||||
|
return 2156220 / raw_rpm;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s76_read_pwm(int idx)
|
||||||
|
{
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
ec_read(0xce + idx, &value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s76_write_pwm(int idx, u8 duty)
|
||||||
|
{
|
||||||
|
u8 values[] = {idx + 1, duty};
|
||||||
|
|
||||||
|
return ec_transaction(0x99, values, sizeof(values), NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s76_write_pwm_auto(int idx)
|
||||||
|
{
|
||||||
|
u8 values[] = {0xff, idx + 1};
|
||||||
|
|
||||||
|
return ec_transaction(0x99, values, sizeof(values), NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_fan_input(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
int index = to_sensor_dev_attr(attr)->index;
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%i\n", s76_read_fan(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_fan_label(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
switch (to_sensor_dev_attr(attr)->index) {
|
||||||
|
case 0:
|
||||||
|
return sysfs_emit(buf, "CPU fan\n");
|
||||||
|
case 1:
|
||||||
|
return sysfs_emit(buf, "GPU fan\n");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pwm_enabled[] = {2, 2};
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_pwm(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
int index = to_sensor_dev_attr(attr)->index;
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%i\n", s76_read_pwm(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_set_pwm(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
int err;
|
||||||
|
int index = to_sensor_dev_attr(attr)->index;
|
||||||
|
|
||||||
|
err = kstrtou32(buf, 10, &value);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
if (value > 255)
|
||||||
|
return -EINVAL;
|
||||||
|
err = s76_write_pwm(index, value);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
pwm_enabled[index] = 1;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_pwm_enable(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
int index = to_sensor_dev_attr(attr)->index;
|
||||||
|
|
||||||
|
return sysfs_emit(buf, "%i\n", pwm_enabled[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_set_pwm_enable(struct device *dev,
|
||||||
|
struct device_attribute *attr, const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
u32 value;
|
||||||
|
int err;
|
||||||
|
int index = to_sensor_dev_attr(attr)->index;
|
||||||
|
|
||||||
|
err = kstrtou32(buf, 10, &value);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
if (value == 0) {
|
||||||
|
err = s76_write_pwm(index, 255);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
pwm_enabled[index] = value;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
if (value == 1) {
|
||||||
|
err = s76_write_pwm(index, 0);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
pwm_enabled[index] = value;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
if (value == 2) {
|
||||||
|
err = s76_write_pwm_auto(index);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
pwm_enabled[index] = value;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_temp1_input(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
ec_read(0x07, &value);
|
||||||
|
return sysfs_emit(buf, "%i\n", value * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_temp1_label(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
return sysfs_emit(buf, "CPU temperature\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
static ssize_t s76_hwmon_show_temp2_input(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
u8 value;
|
||||||
|
|
||||||
|
ec_read(0xcd, &value);
|
||||||
|
return sysfs_emit(buf, "%i\n", value * 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t s76_hwmon_show_temp2_label(struct device *dev,
|
||||||
|
struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
|
return sysfs_emit(buf, "GPU temperature\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static SENSOR_DEVICE_ATTR(fan1_input, 0444, s76_hwmon_show_fan_input, NULL, 0);
|
||||||
|
static SENSOR_DEVICE_ATTR(fan1_label, 0444, s76_hwmon_show_fan_label, NULL, 0);
|
||||||
|
static SENSOR_DEVICE_ATTR(pwm1, 0644, s76_hwmon_show_pwm, s76_hwmon_set_pwm, 0);
|
||||||
|
static SENSOR_DEVICE_ATTR(pwm1_enable, 0644, s76_hwmon_show_pwm_enable, s76_hwmon_set_pwm_enable, 0);
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
static SENSOR_DEVICE_ATTR(fan2_input, 0444, s76_hwmon_show_fan_input, NULL, 1);
|
||||||
|
static SENSOR_DEVICE_ATTR(fan2_label, 0444, s76_hwmon_show_fan_label, NULL, 1);
|
||||||
|
static SENSOR_DEVICE_ATTR(pwm2, 0644, s76_hwmon_show_pwm, s76_hwmon_set_pwm, 1);
|
||||||
|
static SENSOR_DEVICE_ATTR(pwm2_enable, 0644, s76_hwmon_show_pwm_enable, s76_hwmon_set_pwm_enable, 1);
|
||||||
|
#endif
|
||||||
|
static SENSOR_DEVICE_ATTR(temp1_input, 0444, s76_hwmon_show_temp1_input, NULL, 0);
|
||||||
|
static SENSOR_DEVICE_ATTR(temp1_label, 0444, s76_hwmon_show_temp1_label, NULL, 0);
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
static SENSOR_DEVICE_ATTR(temp2_input, 0444, s76_hwmon_show_temp2_input, NULL, 1);
|
||||||
|
static SENSOR_DEVICE_ATTR(temp2_label, 0444, s76_hwmon_show_temp2_label, NULL, 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static struct attribute *hwmon_default_attributes[] = {
|
||||||
|
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_fan1_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_pwm1.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_fan2_label.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_pwm2.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
||||||
|
#endif
|
||||||
|
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
||||||
|
&sensor_dev_attr_temp2_label.dev_attr.attr,
|
||||||
|
#endif
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct attribute_group hwmon_default_group = {
|
||||||
|
.attrs = hwmon_default_attributes,
|
||||||
|
};
|
||||||
|
__ATTRIBUTE_GROUPS(hwmon_default);
|
||||||
|
|
||||||
|
static int s76_hwmon_reboot_callback(struct notifier_block *nb,
|
||||||
|
unsigned long action, void *data)
|
||||||
|
{
|
||||||
|
s76_write_pwm_auto(0);
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
s76_write_pwm_auto(1);
|
||||||
|
#endif
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct notifier_block s76_hwmon_reboot_notifier = {
|
||||||
|
.notifier_call = s76_hwmon_reboot_callback
|
||||||
|
};
|
||||||
|
|
||||||
|
static int s76_hwmon_init(struct device *dev)
|
||||||
|
{
|
||||||
|
s76_hwmon = devm_kzalloc(dev, sizeof(*s76_hwmon), GFP_KERNEL);
|
||||||
|
if (!s76_hwmon)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
s76_hwmon->dev = devm_hwmon_device_register_with_groups(dev, S76_DRIVER_NAME, NULL, hwmon_default_groups);
|
||||||
|
if (IS_ERR(s76_hwmon->dev)) {
|
||||||
|
return PTR_ERR(s76_hwmon->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
(void)devm_register_reboot_notifier(dev, &s76_hwmon_reboot_notifier);
|
||||||
|
s76_write_pwm_auto(0);
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
s76_write_pwm_auto(1);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int s76_hwmon_fini(struct device *dev)
|
||||||
|
{
|
||||||
|
if (!s76_hwmon || IS_ERR_OR_NULL(s76_hwmon->dev))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
s76_write_pwm_auto(0);
|
||||||
|
#ifdef EXPERIMENTAL
|
||||||
|
s76_write_pwm_auto(1);
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // S76_HAS_HWMON
|
||||||
|
|
@ -1,22 +1,10 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* input.c
|
* input.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
||||||
* Copyright (C) 2014-2016 Arnoud Willemsen <mail@lynthium.com>
|
* Copyright (C) 2014-2016 Arnoud Willemsen <mail@lynthium.com>
|
||||||
* Copyright (C) 2013-2015 TUXEDO Computers GmbH <tux@tuxedocomputers.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/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define AIRPLANE_KEY KEY_WLAN
|
#define AIRPLANE_KEY KEY_WLAN
|
||||||
|
|
@ -51,13 +39,14 @@ static const struct kernel_param_ops param_ops_poll_freq = {
|
||||||
|
|
||||||
static unsigned char param_poll_freq = POLL_FREQ_DEFAULT;
|
static unsigned char param_poll_freq = POLL_FREQ_DEFAULT;
|
||||||
#define param_check_poll_freq param_check_byte
|
#define param_check_poll_freq param_check_byte
|
||||||
module_param_named(poll_freq, param_poll_freq, poll_freq, S_IRUSR);
|
module_param_named(poll_freq, param_poll_freq, poll_freq, 0400);
|
||||||
MODULE_PARM_DESC(poll_freq, "Set polling frequency");
|
MODULE_PARM_DESC(poll_freq, "Set polling frequency");
|
||||||
|
|
||||||
static struct task_struct *s76_input_polling_task;
|
static struct task_struct *s76_input_polling_task;
|
||||||
|
|
||||||
static void s76_input_key(unsigned int code) {
|
static void s76_input_key(unsigned int code)
|
||||||
S76_DEBUG("Send key %x\n", code);
|
{
|
||||||
|
pr_debug("Send key %x\n", code);
|
||||||
|
|
||||||
mutex_lock(&s76_input_report_mutex);
|
mutex_lock(&s76_input_report_mutex);
|
||||||
|
|
||||||
|
|
@ -70,8 +59,9 @@ static void s76_input_key(unsigned int code) {
|
||||||
mutex_unlock(&s76_input_report_mutex);
|
mutex_unlock(&s76_input_report_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s76_input_polling_thread(void *data) {
|
static int s76_input_polling_thread(void *data)
|
||||||
S76_DEBUG("Polling thread started (PID: %i), polling at %i Hz\n",
|
{
|
||||||
|
pr_debug("Polling thread started (PID: %i), polling at %i Hz\n",
|
||||||
current->pid, param_poll_freq);
|
current->pid, param_poll_freq);
|
||||||
|
|
||||||
while (!kthread_should_stop()) {
|
while (!kthread_should_stop()) {
|
||||||
|
|
@ -81,7 +71,7 @@ static int s76_input_polling_thread(void *data) {
|
||||||
if (byte & 0x40) {
|
if (byte & 0x40) {
|
||||||
ec_write(0xDB, byte & ~0x40);
|
ec_write(0xDB, byte & ~0x40);
|
||||||
|
|
||||||
S76_DEBUG("Airplane-Mode Hotkey pressed (EC)\n");
|
pr_debug("Airplane-Mode Hotkey pressed (EC)\n");
|
||||||
|
|
||||||
s76_input_key(AIRPLANE_KEY);
|
s76_input_key(AIRPLANE_KEY);
|
||||||
}
|
}
|
||||||
|
|
@ -89,24 +79,27 @@ static int s76_input_polling_thread(void *data) {
|
||||||
msleep_interruptible(1000 / param_poll_freq);
|
msleep_interruptible(1000 / param_poll_freq);
|
||||||
}
|
}
|
||||||
|
|
||||||
S76_DEBUG("Polling thread exiting\n");
|
pr_debug("Polling thread exiting\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s76_input_airplane_wmi(void) {
|
static void s76_input_airplane_wmi(void)
|
||||||
S76_DEBUG("Airplane-Mode Hotkey pressed (WMI)\n");
|
{
|
||||||
|
pr_debug("Airplane-Mode Hotkey pressed (WMI)\n");
|
||||||
|
|
||||||
s76_input_key(AIRPLANE_KEY);
|
s76_input_key(AIRPLANE_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s76_input_screen_wmi(void) {
|
static void s76_input_screen_wmi(void)
|
||||||
S76_DEBUG("Screen Hotkey pressed (WMI)\n");
|
{
|
||||||
|
pr_debug("Screen Hotkey pressed (WMI)\n");
|
||||||
|
|
||||||
s76_input_key(SCREEN_KEY);
|
s76_input_key(SCREEN_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s76_input_open(struct input_dev *dev) {
|
static int s76_input_open(struct input_dev *dev)
|
||||||
|
{
|
||||||
int res = 0;
|
int res = 0;
|
||||||
|
|
||||||
// Run polling thread if AP key driver is used and WMI is not supported
|
// Run polling thread if AP key driver is used and WMI is not supported
|
||||||
|
|
@ -115,10 +108,10 @@ static int s76_input_open(struct input_dev *dev) {
|
||||||
s76_input_polling_thread,
|
s76_input_polling_thread,
|
||||||
NULL, "system76-polld");
|
NULL, "system76-polld");
|
||||||
|
|
||||||
if (unlikely(IS_ERR(s76_input_polling_task))) {
|
if (IS_ERR(s76_input_polling_task)) {
|
||||||
res = PTR_ERR(s76_input_polling_task);
|
res = PTR_ERR(s76_input_polling_task);
|
||||||
s76_input_polling_task = NULL;
|
s76_input_polling_task = NULL;
|
||||||
S76_ERROR("Could not create polling thread: %d\n", res);
|
pr_err("Could not create polling thread: %d\n", res);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -126,8 +119,9 @@ static int s76_input_open(struct input_dev *dev) {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void s76_input_close(struct input_dev *dev) {
|
static void s76_input_close(struct input_dev *dev)
|
||||||
if (unlikely(IS_ERR_OR_NULL(s76_input_polling_task))) {
|
{
|
||||||
|
if (IS_ERR_OR_NULL(s76_input_polling_task)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -135,21 +129,21 @@ 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(struct device *dev) {
|
static int __init s76_input_init(struct device *dev)
|
||||||
int err;
|
{
|
||||||
u8 byte;
|
u8 byte;
|
||||||
|
|
||||||
s76_input_device = input_allocate_device();
|
s76_input_device = devm_input_allocate_device(dev);
|
||||||
if (unlikely(!s76_input_device)) {
|
if (!s76_input_device) {
|
||||||
S76_ERROR("Error allocating input device\n");
|
pr_err("Error allocating input device\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
s76_input_device->name = "System76 Hotkeys";
|
s76_input_device->name = "System76 Hotkeys";
|
||||||
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 = dev;
|
|
||||||
set_bit(EV_KEY, s76_input_device->evbit);
|
set_bit(EV_KEY, s76_input_device->evbit);
|
||||||
|
|
||||||
if (driver_flags & DRIVER_AP_KEY) {
|
if (driver_flags & DRIVER_AP_KEY) {
|
||||||
set_bit(AIRPLANE_KEY, s76_input_device->keybit);
|
set_bit(AIRPLANE_KEY, s76_input_device->keybit);
|
||||||
ec_read(0xDB, &byte);
|
ec_read(0xDB, &byte);
|
||||||
|
|
@ -162,25 +156,5 @@ static int __init s76_input_init(struct device *dev) {
|
||||||
s76_input_device->open = s76_input_open;
|
s76_input_device->open = s76_input_open;
|
||||||
s76_input_device->close = s76_input_close;
|
s76_input_device->close = s76_input_close;
|
||||||
|
|
||||||
err = input_register_device(s76_input_device);
|
return input_register_device(s76_input_device);
|
||||||
if (unlikely(err)) {
|
|
||||||
S76_ERROR("Error registering input device\n");
|
|
||||||
goto err_free_input_device;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_free_input_device:
|
|
||||||
input_free_device(s76_input_device);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit s76_input_exit(void) {
|
|
||||||
if (unlikely(!s76_input_device)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
input_unregister_device(s76_input_device);
|
|
||||||
s76_input_device = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +1,8 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* kb_led.c
|
* kb_led.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.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/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SET_KB_LED 0x67
|
#define SET_KB_LED 0x67
|
||||||
|
|
@ -25,13 +13,13 @@ union kb_led_color {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum kb_led_region {
|
enum kb_led_region {
|
||||||
KB_LED_REGION_LEFT,
|
KB_LED_REGION_LEFT,
|
||||||
KB_LED_REGION_CENTER,
|
KB_LED_REGION_CENTER,
|
||||||
KB_LED_REGION_RIGHT,
|
KB_LED_REGION_RIGHT,
|
||||||
KB_LED_REGION_EXTRA,
|
KB_LED_REGION_EXTRA,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum led_brightness kb_led_brightness = 0;
|
static enum led_brightness kb_led_brightness;
|
||||||
|
|
||||||
static enum led_brightness kb_led_toggle_brightness = 72;
|
static enum led_brightness kb_led_toggle_brightness = 72;
|
||||||
|
|
||||||
|
|
@ -44,7 +32,7 @@ static union kb_led_color kb_led_regions[] = {
|
||||||
{ .rgb = 0xFFFFFF }
|
{ .rgb = 0xFFFFFF }
|
||||||
};
|
};
|
||||||
|
|
||||||
static int kb_led_colors_i = 0;
|
static int kb_led_colors_i;
|
||||||
|
|
||||||
static union kb_led_color kb_led_colors[] = {
|
static union kb_led_color kb_led_colors[] = {
|
||||||
{ .rgb = 0xFFFFFF },
|
{ .rgb = 0xFFFFFF },
|
||||||
|
|
@ -56,12 +44,14 @@ static union kb_led_color kb_led_colors[] = {
|
||||||
{ .rgb = 0xFFFF00 }
|
{ .rgb = 0xFFFF00 }
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum led_brightness kb_led_get(struct led_classdev *led_cdev) {
|
static enum led_brightness kb_led_get(struct led_classdev *led_cdev)
|
||||||
|
{
|
||||||
return kb_led_brightness;
|
return kb_led_brightness;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int kb_led_set(struct led_classdev *led_cdev, enum led_brightness value) {
|
static int kb_led_set(struct led_classdev *led_cdev, enum led_brightness value)
|
||||||
S76_DEBUG("kb_led_set %d\n", (int)value);
|
{
|
||||||
|
pr_debug("%s %d\n", __func__, (int)value);
|
||||||
|
|
||||||
if (!s76_wmbb(SET_KB_LED, 0xF4000000 | value, NULL)) {
|
if (!s76_wmbb(SET_KB_LED, 0xF4000000 | value, NULL)) {
|
||||||
kb_led_brightness = value;
|
kb_led_brightness = value;
|
||||||
|
|
@ -70,10 +60,11 @@ static int kb_led_set(struct led_classdev *led_cdev, enum led_brightness value)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_led_color_set(enum kb_led_region region, union kb_led_color color) {
|
static void kb_led_color_set_wmi(enum kb_led_region region, union kb_led_color color)
|
||||||
|
{
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
|
|
||||||
S76_DEBUG("kb_led_color_set %d %06X\n", (int)region, (int)color.rgb);
|
pr_debug("%s %d %06X\n", __func__, (int)region, (int)color.rgb);
|
||||||
|
|
||||||
switch (region) {
|
switch (region) {
|
||||||
case KB_LED_REGION_LEFT:
|
case KB_LED_REGION_LEFT:
|
||||||
|
|
@ -101,6 +92,71 @@ static void kb_led_color_set(enum kb_led_region region, union kb_led_color color
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: Directly call ECMD to fix serw14
|
||||||
|
static void kb_led_color_set(enum kb_led_region region, union kb_led_color color)
|
||||||
|
{
|
||||||
|
struct acpi_object_list input;
|
||||||
|
union acpi_object obj;
|
||||||
|
acpi_handle handle;
|
||||||
|
acpi_status status;
|
||||||
|
u8 *buf;
|
||||||
|
|
||||||
|
buf = kzalloc(8, GFP_KERNEL);
|
||||||
|
|
||||||
|
pr_debug("%s %d %06X\n", __func__, (int)region, (int)color.rgb);
|
||||||
|
|
||||||
|
buf[0] = 5;
|
||||||
|
buf[2] = 0xCA;
|
||||||
|
buf[4] = color.b;
|
||||||
|
buf[5] = color.r;
|
||||||
|
buf[6] = color.g;
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case KB_LED_REGION_LEFT:
|
||||||
|
buf[3] = 0x03;
|
||||||
|
break;
|
||||||
|
case KB_LED_REGION_CENTER:
|
||||||
|
buf[3] = 0x04;
|
||||||
|
break;
|
||||||
|
case KB_LED_REGION_RIGHT:
|
||||||
|
buf[3] = 0x05;
|
||||||
|
break;
|
||||||
|
case KB_LED_REGION_EXTRA:
|
||||||
|
buf[3] = 0x0B;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.type = ACPI_TYPE_BUFFER;
|
||||||
|
obj.buffer.length = 8;
|
||||||
|
obj.buffer.pointer = buf;
|
||||||
|
|
||||||
|
input.count = 1;
|
||||||
|
input.pointer = &obj;
|
||||||
|
|
||||||
|
status = acpi_get_handle(NULL, (acpi_string)"\\_SB.PC00.LPCB.EC", &handle);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
pr_err("%s failed to get handle: %x\n", __func__, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
status = acpi_evaluate_object(handle, "ECMD", &input, NULL);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
pr_err("%s failed to call EC_CMD: %x\n", __func__, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update lightbar to match keyboard color
|
||||||
|
buf[3] = 0x07;
|
||||||
|
status = acpi_evaluate_object(handle, "ECMD", &input, NULL);
|
||||||
|
if (ACPI_FAILURE(status)) {
|
||||||
|
pr_err("%s failed to call EC_CMD: %x\n", __func__, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(buf);
|
||||||
|
kb_led_regions[region] = color;
|
||||||
|
}
|
||||||
|
|
||||||
static struct led_classdev kb_led = {
|
static struct led_classdev kb_led = {
|
||||||
.name = "system76::kbd_backlight",
|
.name = "system76::kbd_backlight",
|
||||||
.flags = LED_BRIGHT_HW_CHANGED,
|
.flags = LED_BRIGHT_HW_CHANGED,
|
||||||
|
|
@ -109,11 +165,13 @@ static struct led_classdev kb_led = {
|
||||||
.max_brightness = 255,
|
.max_brightness = 255,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t kb_led_color_show(enum kb_led_region region, char *buf) {
|
static ssize_t kb_led_color_show(enum kb_led_region region, char *buf)
|
||||||
return sprintf(buf, "%06X\n", (int)kb_led_regions[region].rgb);
|
{
|
||||||
|
return sysfs_emit(buf, "%06X\n", (int)kb_led_regions[region].rgb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t kb_led_color_store(enum kb_led_region region, const char *buf, size_t size) {
|
static ssize_t kb_led_color_store(enum kb_led_region region, const char *buf, size_t size)
|
||||||
|
{
|
||||||
unsigned int val;
|
unsigned int val;
|
||||||
int ret;
|
int ret;
|
||||||
union kb_led_color color;
|
union kb_led_color color;
|
||||||
|
|
@ -124,16 +182,21 @@ static ssize_t kb_led_color_store(enum kb_led_region region, const char *buf, si
|
||||||
}
|
}
|
||||||
|
|
||||||
color.rgb = (u32)val;
|
color.rgb = (u32)val;
|
||||||
kb_led_color_set(region, color);
|
if (driver_flags & DRIVER_KB_LED_WMI)
|
||||||
|
kb_led_color_set_wmi(region, color);
|
||||||
|
else
|
||||||
|
kb_led_color_set(region, color);
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t kb_led_color_left_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
static ssize_t kb_led_color_left_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
return kb_led_color_show(KB_LED_REGION_LEFT, buf);
|
return kb_led_color_show(KB_LED_REGION_LEFT, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t kb_led_color_left_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) {
|
static ssize_t kb_led_color_left_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
return kb_led_color_store(KB_LED_REGION_LEFT, buf, size);
|
return kb_led_color_store(KB_LED_REGION_LEFT, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -146,11 +209,13 @@ static struct device_attribute kb_led_color_left_dev_attr = {
|
||||||
.store = kb_led_color_left_store,
|
.store = kb_led_color_left_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t kb_led_color_center_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
static ssize_t kb_led_color_center_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
return kb_led_color_show(KB_LED_REGION_CENTER, buf);
|
return kb_led_color_show(KB_LED_REGION_CENTER, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t kb_led_color_center_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) {
|
static ssize_t kb_led_color_center_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
return kb_led_color_store(KB_LED_REGION_CENTER, buf, size);
|
return kb_led_color_store(KB_LED_REGION_CENTER, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,11 +228,13 @@ static struct device_attribute kb_led_color_center_dev_attr = {
|
||||||
.store = kb_led_color_center_store,
|
.store = kb_led_color_center_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t kb_led_color_right_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
static ssize_t kb_led_color_right_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
return kb_led_color_show(KB_LED_REGION_RIGHT, buf);
|
return kb_led_color_show(KB_LED_REGION_RIGHT, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t kb_led_color_right_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) {
|
static ssize_t kb_led_color_right_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
return kb_led_color_store(KB_LED_REGION_RIGHT, buf, size);
|
return kb_led_color_store(KB_LED_REGION_RIGHT, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -180,11 +247,13 @@ static struct device_attribute kb_led_color_right_dev_attr = {
|
||||||
.store = kb_led_color_right_store,
|
.store = kb_led_color_right_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t kb_led_color_extra_show(struct device *dev, struct device_attribute *attr, char *buf) {
|
static ssize_t kb_led_color_extra_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||||
|
{
|
||||||
return kb_led_color_show(KB_LED_REGION_EXTRA, buf);
|
return kb_led_color_show(KB_LED_REGION_EXTRA, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t kb_led_color_extra_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) {
|
static ssize_t kb_led_color_extra_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size)
|
||||||
|
{
|
||||||
return kb_led_color_store(KB_LED_REGION_EXTRA, buf, size);
|
return kb_led_color_store(KB_LED_REGION_EXTRA, buf, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -197,36 +266,43 @@ static struct device_attribute kb_led_color_extra_dev_attr = {
|
||||||
.store = kb_led_color_extra_store,
|
.store = kb_led_color_extra_store,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void kb_led_enable(void) {
|
static void kb_led_enable(void)
|
||||||
S76_DEBUG("kb_led_enable\n");
|
{
|
||||||
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
s76_wmbb(SET_KB_LED, 0xE007F001, NULL);
|
s76_wmbb(SET_KB_LED, 0xE007F001, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_led_disable(void) {
|
static void kb_led_disable(void)
|
||||||
S76_DEBUG("kb_led_disable\n");
|
{
|
||||||
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
s76_wmbb(SET_KB_LED, 0xE0003001, NULL);
|
s76_wmbb(SET_KB_LED, 0xE0003001, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_led_suspend(void) {
|
static void kb_led_suspend(void)
|
||||||
S76_DEBUG("kb_led_suspend\n");
|
{
|
||||||
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
// Disable keyboard backlight
|
// Disable keyboard backlight
|
||||||
kb_led_disable();
|
kb_led_disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_led_resume(void) {
|
static void kb_led_resume(void)
|
||||||
|
{
|
||||||
enum kb_led_region region;
|
enum kb_led_region region;
|
||||||
|
|
||||||
S76_DEBUG("kb_led_resume\n");
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
// Disable keyboard backlight
|
// Disable keyboard backlight
|
||||||
kb_led_disable();
|
kb_led_disable();
|
||||||
|
|
||||||
// Reset current color
|
// Reset current color
|
||||||
for (region = 0; region < sizeof(kb_led_regions)/sizeof(union kb_led_color); region++) {
|
for (region = 0; region < sizeof(kb_led_regions)/sizeof(union kb_led_color); region++) {
|
||||||
kb_led_color_set(region, kb_led_regions[region]);
|
if (driver_flags & DRIVER_KB_LED_WMI)
|
||||||
|
kb_led_color_set_wmi(region, kb_led_regions[region]);
|
||||||
|
else
|
||||||
|
kb_led_color_set(region, kb_led_regions[region]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset current brightness
|
// Reset current brightness
|
||||||
|
|
@ -236,28 +312,29 @@ static void kb_led_resume(void) {
|
||||||
kb_led_enable();
|
kb_led_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init kb_led_init(struct device *dev) {
|
static int __init kb_led_init(struct device *dev)
|
||||||
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
err = led_classdev_register(dev, &kb_led);
|
err = devm_led_classdev_register(dev, &kb_led);
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_create_file(kb_led.dev, &kb_led_color_left_dev_attr) != 0) {
|
if (device_create_file(kb_led.dev, &kb_led_color_left_dev_attr) != 0) {
|
||||||
S76_ERROR("failed to create kb_led_color_left\n");
|
pr_err("failed to create kb_led_color_left\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_create_file(kb_led.dev, &kb_led_color_center_dev_attr) != 0) {
|
if (device_create_file(kb_led.dev, &kb_led_color_center_dev_attr) != 0) {
|
||||||
S76_ERROR("failed to create kb_led_color_center\n");
|
pr_err("failed to create kb_led_color_center\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_create_file(kb_led.dev, &kb_led_color_right_dev_attr) != 0) {
|
if (device_create_file(kb_led.dev, &kb_led_color_right_dev_attr) != 0) {
|
||||||
S76_ERROR("failed to create kb_led_color_right\n");
|
pr_err("failed to create kb_led_color_right\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (device_create_file(kb_led.dev, &kb_led_color_extra_dev_attr) != 0) {
|
if (device_create_file(kb_led.dev, &kb_led_color_extra_dev_attr) != 0) {
|
||||||
S76_ERROR("failed to create kb_led_color_extra\n");
|
pr_err("failed to create kb_led_color_extra\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
kb_led_resume();
|
kb_led_resume();
|
||||||
|
|
@ -265,25 +342,24 @@ static int __init kb_led_init(struct device *dev) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit kb_led_exit(void) {
|
static void __exit kb_led_exit(void)
|
||||||
|
{
|
||||||
device_remove_file(kb_led.dev, &kb_led_color_extra_dev_attr);
|
device_remove_file(kb_led.dev, &kb_led_color_extra_dev_attr);
|
||||||
device_remove_file(kb_led.dev, &kb_led_color_right_dev_attr);
|
device_remove_file(kb_led.dev, &kb_led_color_right_dev_attr);
|
||||||
device_remove_file(kb_led.dev, &kb_led_color_center_dev_attr);
|
device_remove_file(kb_led.dev, &kb_led_color_center_dev_attr);
|
||||||
device_remove_file(kb_led.dev, &kb_led_color_left_dev_attr);
|
device_remove_file(kb_led.dev, &kb_led_color_left_dev_attr);
|
||||||
|
|
||||||
if (!IS_ERR_OR_NULL(kb_led.dev)) {
|
|
||||||
led_classdev_unregister(&kb_led);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_wmi_brightness(enum led_brightness value) {
|
static void kb_wmi_brightness(enum led_brightness value)
|
||||||
S76_DEBUG("kb_wmi_brightness %d\n", (int)value);
|
{
|
||||||
|
pr_debug("%s %d\n", __func__, (int)value);
|
||||||
|
|
||||||
kb_led_set(&kb_led, value);
|
kb_led_set(&kb_led, value);
|
||||||
led_classdev_notify_brightness_hw_changed(&kb_led, value);
|
led_classdev_notify_brightness_hw_changed(&kb_led, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_wmi_toggle(void) {
|
static void kb_wmi_toggle(void)
|
||||||
|
{
|
||||||
if (kb_led_brightness > 0) {
|
if (kb_led_brightness > 0) {
|
||||||
kb_led_toggle_brightness = kb_led_brightness;
|
kb_led_toggle_brightness = kb_led_brightness;
|
||||||
kb_wmi_brightness(LED_OFF);
|
kb_wmi_brightness(LED_OFF);
|
||||||
|
|
@ -292,7 +368,8 @@ static void kb_wmi_toggle(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_wmi_dec(void) {
|
static void kb_wmi_dec(void)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (kb_led_brightness > 0) {
|
if (kb_led_brightness > 0) {
|
||||||
|
|
@ -307,7 +384,8 @@ static void kb_wmi_dec(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_wmi_inc(void) {
|
static void kb_wmi_inc(void)
|
||||||
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (kb_led_brightness > 0) {
|
if (kb_led_brightness > 0) {
|
||||||
|
|
@ -322,7 +400,8 @@ static void kb_wmi_inc(void) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kb_wmi_color(void) {
|
static void kb_wmi_color(void)
|
||||||
|
{
|
||||||
enum kb_led_region region;
|
enum kb_led_region region;
|
||||||
|
|
||||||
kb_led_colors_i += 1;
|
kb_led_colors_i += 1;
|
||||||
|
|
@ -331,7 +410,10 @@ static void kb_wmi_color(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (region = 0; region < sizeof(kb_led_regions)/sizeof(union kb_led_color); region++) {
|
for (region = 0; region < sizeof(kb_led_regions)/sizeof(union kb_led_color); region++) {
|
||||||
kb_led_color_set(region, kb_led_colors[kb_led_colors_i]);
|
if (driver_flags & DRIVER_KB_LED_WMI)
|
||||||
|
kb_led_color_set_wmi(region, kb_led_colors[kb_led_colors_i]);
|
||||||
|
else
|
||||||
|
kb_led_color_set(region, kb_led_colors[kb_led_colors_i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
led_classdev_notify_brightness_hw_changed(&kb_led, kb_led_brightness);
|
led_classdev_notify_brightness_hw_changed(&kb_led, kb_led_brightness);
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
/* Based on bbswitch,
|
||||||
|
* Copyright (C) 2011-2013 Bumblebee Project
|
||||||
|
* Author: Peter Wu <lekensteyn@gmail.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum {
|
||||||
|
CARD_UNCHANGED = -1,
|
||||||
|
CARD_OFF = 0,
|
||||||
|
CARD_ON = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct pci_dev *dis_dev;
|
||||||
|
static struct pci_dev *sub_dev;
|
||||||
|
|
||||||
|
// Returns 1 if the card is disabled, 0 if enabled
|
||||||
|
static int is_card_disabled(void)
|
||||||
|
{
|
||||||
|
// check for: 1.bit is set 2.sub-function is available.
|
||||||
|
u32 cfg_word;
|
||||||
|
struct pci_dev *tmp_dev = NULL;
|
||||||
|
|
||||||
|
sub_dev = NULL;
|
||||||
|
|
||||||
|
// read config word at 0x488
|
||||||
|
pci_read_config_dword(dis_dev, 0x488, &cfg_word);
|
||||||
|
if ((cfg_word & 0x2000000) == 0x2000000) {
|
||||||
|
// check for subdevice. read first config dword of sub function 1
|
||||||
|
while ((tmp_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, tmp_dev)) != NULL) {
|
||||||
|
int pci_class = tmp_dev->class >> 8;
|
||||||
|
|
||||||
|
if (pci_class != 0x403)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (tmp_dev->vendor == PCI_VENDOR_ID_NVIDIA) {
|
||||||
|
sub_dev = tmp_dev;
|
||||||
|
pr_info("Found NVIDIA audio device %s\n", dev_name(&tmp_dev->dev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sub_dev == NULL) {
|
||||||
|
pr_info("No NVIDIA audio device found, unsetting config bit.\n");
|
||||||
|
cfg_word |= 0x2000000;
|
||||||
|
pci_write_config_dword(dis_dev, 0x488, cfg_word);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nv_hda_off(void)
|
||||||
|
{
|
||||||
|
u32 cfg_word;
|
||||||
|
|
||||||
|
if (is_card_disabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove device
|
||||||
|
pci_dev_put(sub_dev);
|
||||||
|
pci_stop_and_remove_bus_device(sub_dev);
|
||||||
|
|
||||||
|
pr_info("NVIDIA audio: disabling\n");
|
||||||
|
|
||||||
|
// setting bit to turn off
|
||||||
|
pci_read_config_dword(dis_dev, 0x488, &cfg_word);
|
||||||
|
cfg_word &= 0xfdffffff;
|
||||||
|
pci_write_config_dword(dis_dev, 0x488, cfg_word);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nv_hda_on(void)
|
||||||
|
{
|
||||||
|
u32 cfg_word;
|
||||||
|
u8 hdr_type;
|
||||||
|
|
||||||
|
if (!is_card_disabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("NVIDIA audio: enabling\n");
|
||||||
|
|
||||||
|
// read,set bit, write config word at 0x488
|
||||||
|
pci_read_config_dword(dis_dev, 0x488, &cfg_word);
|
||||||
|
cfg_word |= 0x2000000;
|
||||||
|
pci_write_config_dword(dis_dev, 0x488, cfg_word);
|
||||||
|
|
||||||
|
//pci_scan_single_device
|
||||||
|
pci_read_config_byte(dis_dev, PCI_HEADER_TYPE, &hdr_type);
|
||||||
|
|
||||||
|
if (!(hdr_type & 0x80)) {
|
||||||
|
pr_err("NVIDIA not multifunction, no audio\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub_dev = pci_scan_single_device(dis_dev->bus, 1);
|
||||||
|
if (!sub_dev) {
|
||||||
|
pr_err("No NVIDIA audio device found\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("NVIDIA audio found, adding\n");
|
||||||
|
pci_assign_unassigned_bus_resources(dis_dev->bus);
|
||||||
|
pci_bus_add_devices(dis_dev->bus);
|
||||||
|
pci_dev_get(sub_dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* power bus so we can read PCI configuration space */
|
||||||
|
static void dis_dev_get(void)
|
||||||
|
{
|
||||||
|
if (dis_dev->bus && dis_dev->bus->self) {
|
||||||
|
pm_runtime_get_sync(&dis_dev->bus->self->dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dis_dev_put(void)
|
||||||
|
{
|
||||||
|
if (dis_dev->bus && dis_dev->bus->self) {
|
||||||
|
pm_runtime_put_sync(&dis_dev->bus->self->dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int __init nv_hda_init(struct device *dev)
|
||||||
|
{
|
||||||
|
struct pci_dev *pdev = NULL;
|
||||||
|
|
||||||
|
while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
|
||||||
|
int pci_class = pdev->class >> 8;
|
||||||
|
|
||||||
|
if (pci_class != PCI_CLASS_DISPLAY_VGA && pci_class != PCI_CLASS_DISPLAY_3D) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) {
|
||||||
|
dis_dev = pdev;
|
||||||
|
pr_info("NVIDIA device %s\n", dev_name(&pdev->dev));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dis_dev == NULL) {
|
||||||
|
pr_err("No NVIDIA device found\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
dis_dev_get();
|
||||||
|
|
||||||
|
nv_hda_on();
|
||||||
|
|
||||||
|
pr_info("NVIDIA Audio %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
|
||||||
|
|
||||||
|
dis_dev_put();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit nv_hda_exit(void)
|
||||||
|
{
|
||||||
|
if (dis_dev == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dis_dev_get();
|
||||||
|
|
||||||
|
nv_hda_off();
|
||||||
|
|
||||||
|
pr_info("NVIDIA Audio %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
|
||||||
|
|
||||||
|
dis_dev_put();
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,10 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
/*
|
/*
|
||||||
* system76.c
|
* system76.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
* Copyright (C) 2017 Jeremy Soller <jeremy@system76.com>
|
||||||
* Copyright (C) 2014-2016 Arnoud Willemsen <mail@lynthium.com>
|
* Copyright (C) 2014-2016 Arnoud Willemsen <mail@lynthium.com>
|
||||||
* Copyright (C) 2013-2015 TUXEDO Computers GmbH <tux@tuxedocomputers.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/>.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define S76_DRIVER_NAME KBUILD_MODNAME
|
#define S76_DRIVER_NAME KBUILD_MODNAME
|
||||||
|
|
@ -43,13 +31,6 @@
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
|
||||||
#define __S76_PR(lvl, fmt, ...) do { pr_##lvl(fmt, ##__VA_ARGS__); } \
|
|
||||||
while (0)
|
|
||||||
#define S76_INFO(fmt, ...) __S76_PR(info, fmt, ##__VA_ARGS__)
|
|
||||||
#define S76_ERROR(fmt, ...) __S76_PR(err, fmt, ##__VA_ARGS__)
|
|
||||||
#define S76_DEBUG(fmt, ...) __S76_PR(debug, "[%s:%u] " fmt, \
|
|
||||||
__func__, __LINE__, ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#define S76_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000"
|
#define S76_EVENT_GUID "ABBC0F6B-8EA1-11D1-00A0-C90629100000"
|
||||||
#define S76_WMBB_GUID "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
#define S76_WMBB_GUID "ABBC0F6D-8EA1-11D1-00A0-C90629100000"
|
||||||
|
|
||||||
|
|
@ -58,27 +39,29 @@
|
||||||
/* method IDs for S76_GET */
|
/* method IDs for S76_GET */
|
||||||
#define GET_EVENT 0x01 /* 1 */
|
#define GET_EVENT 0x01 /* 1 */
|
||||||
|
|
||||||
#define DRIVER_AP_KEY (1 << 0)
|
#define DRIVER_AP_KEY (1 << 0)
|
||||||
#define DRIVER_AP_LED (1 << 1)
|
#define DRIVER_AP_LED (1 << 1)
|
||||||
#define DRIVER_HWMON (1 << 2)
|
#define DRIVER_HWMON (1 << 2)
|
||||||
#define DRIVER_KB_LED (1 << 3)
|
#define DRIVER_KB_LED_WMI (1 << 3)
|
||||||
#define DRIVER_OLED (1 << 4)
|
#define DRIVER_OLED (1 << 4)
|
||||||
#define DRIVER_AP_WMI (1 << 5)
|
#define DRIVER_AP_WMI (1 << 5)
|
||||||
|
#define DRIVER_KB_LED (1 << 6)
|
||||||
|
|
||||||
#define DRIVER_INPUT (DRIVER_AP_KEY | DRIVER_OLED)
|
#define DRIVER_INPUT (DRIVER_AP_KEY | DRIVER_OLED)
|
||||||
|
|
||||||
static uint64_t driver_flags = 0;
|
static uint64_t driver_flags;
|
||||||
|
|
||||||
struct platform_device *s76_platform_device;
|
struct platform_device *s76_platform_device;
|
||||||
|
|
||||||
static int s76_wmbb(u32 method_id, u32 arg, u32 *retval) {
|
static int s76_wmbb(u32 method_id, u32 arg, u32 *retval)
|
||||||
|
{
|
||||||
struct acpi_buffer in = { (acpi_size) sizeof(arg), &arg };
|
struct acpi_buffer in = { (acpi_size) sizeof(arg), &arg };
|
||||||
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
union acpi_object *obj;
|
union acpi_object *obj;
|
||||||
acpi_status status;
|
acpi_status status;
|
||||||
u32 tmp;
|
u32 tmp;
|
||||||
|
|
||||||
S76_DEBUG("%0#4x IN : %0#6x\n", method_id, arg);
|
pr_debug("%0#4x IN : %0#6x\n", method_id, arg);
|
||||||
|
|
||||||
status = wmi_evaluate_method(S76_WMBB_GUID, 0, method_id, &in, &out);
|
status = wmi_evaluate_method(S76_WMBB_GUID, 0, method_id, &in, &out);
|
||||||
|
|
||||||
|
|
@ -93,7 +76,7 @@ static int s76_wmbb(u32 method_id, u32 arg, u32 *retval) {
|
||||||
tmp = 0;
|
tmp = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
S76_DEBUG("%0#4x OUT: %0#6x (IN: %0#6x)\n", method_id, tmp, arg);
|
pr_debug("%0#4x OUT: %0#6x (IN: %0#6x)\n", method_id, tmp, arg);
|
||||||
|
|
||||||
if (likely(retval)) {
|
if (likely(retval)) {
|
||||||
*retval = tmp;
|
*retval = tmp;
|
||||||
|
|
@ -104,48 +87,49 @@ static int s76_wmbb(u32 method_id, u32 arg, u32 *retval) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "system76_ap-led.c"
|
#include "ap-led.c"
|
||||||
#include "system76_input.c"
|
#include "input.c"
|
||||||
#include "system76_kb-led.c"
|
#include "kb-led.c"
|
||||||
#include "system76_hwmon.c"
|
#include "hwmon.c"
|
||||||
#include "system76_nv_hda.c"
|
#include "nv_hda.c"
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,12,0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
static void s76_wmi_notify(union acpi_object *obj, void *context) {
|
static void s76_wmi_notify(union acpi_object *obj, void *context)
|
||||||
#else
|
#else
|
||||||
static void s76_wmi_notify(u32 value, void *context) {
|
static void s76_wmi_notify(u32 value, void *context)
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
u32 event;
|
u32 event;
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,12,0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
if (obj->type != ACPI_TYPE_INTEGER) {
|
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||||
S76_DEBUG("Unexpected WMI event (%0#6x)\n", obj);
|
pr_debug("Unexpected WMI event (%0#6x)\n", obj->type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (value != 0xD0) {
|
if (value != 0xD0) {
|
||||||
S76_DEBUG("Unexpected WMI event (%0#6x)\n", value);
|
pr_debug("Unexpected WMI event (%0#6x)\n", value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
s76_wmbb(GET_EVENT, 0, &event);
|
s76_wmbb(GET_EVENT, 0, &event);
|
||||||
|
|
||||||
S76_DEBUG("WMI event code (%x)\n", event);
|
pr_debug("WMI event code (%x)\n", event);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case 0x81:
|
case 0x81:
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
kb_wmi_dec();
|
kb_wmi_dec();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x82:
|
case 0x82:
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
kb_wmi_inc();
|
kb_wmi_inc();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x83:
|
case 0x83:
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
kb_wmi_color();
|
kb_wmi_color();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -156,7 +140,7 @@ static void s76_wmi_notify(u32 value, void *context) {
|
||||||
//TODO: Fn+ESC
|
//TODO: Fn+ESC
|
||||||
break;
|
break;
|
||||||
case 0x9F:
|
case 0x9F:
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
kb_wmi_toggle();
|
kb_wmi_toggle();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -178,32 +162,33 @@ static void s76_wmi_notify(u32 value, void *context) {
|
||||||
// Touchpad WMI (enable)
|
// Touchpad WMI (enable)
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
S76_DEBUG("Unknown WMI event code (%x)\n", event);
|
pr_debug("Unknown WMI event code (%x)\n", event);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init s76_probe(struct platform_device *dev) {
|
static int __init s76_probe(struct platform_device *dev)
|
||||||
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (driver_flags & DRIVER_AP_LED) {
|
if (driver_flags & DRIVER_AP_LED) {
|
||||||
err = ap_led_init(&dev->dev);
|
err = ap_led_init(&dev->dev);
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
S76_ERROR("Could not register LED device\n");
|
pr_err("Could not register LED device\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
err = kb_led_init(&dev->dev);
|
err = kb_led_init(&dev->dev);
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
S76_ERROR("Could not register LED device\n");
|
pr_err("Could not register LED device\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (driver_flags & DRIVER_INPUT) {
|
if (driver_flags & DRIVER_INPUT) {
|
||||||
err = s76_input_init(&dev->dev);
|
err = s76_input_init(&dev->dev);
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
S76_ERROR("Could not register input device\n");
|
pr_err("Could not register input device\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -215,12 +200,12 @@ static int __init s76_probe(struct platform_device *dev) {
|
||||||
|
|
||||||
err = nv_hda_init(&dev->dev);
|
err = nv_hda_init(&dev->dev);
|
||||||
if (unlikely(err)) {
|
if (unlikely(err)) {
|
||||||
S76_ERROR("Could not register NVIDIA audio device\n");
|
pr_err("Could not register NVIDIA audio device\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
err = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL);
|
err = wmi_install_notify_handler(S76_EVENT_GUID, s76_wmi_notify, NULL);
|
||||||
if (unlikely(ACPI_FAILURE(err))) {
|
if (unlikely(ACPI_FAILURE(err))) {
|
||||||
S76_ERROR("Could not register WMI notify handler (%0#6x)\n", err);
|
pr_err("Could not register WMI notify handler (%0#6x)\n", err);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,11 +220,12 @@ static int __init s76_probe(struct platform_device *dev) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,11,0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 11, 0)
|
||||||
static void s76_remove(struct platform_device *dev) {
|
static void s76_remove(struct platform_device *dev)
|
||||||
#else
|
#else
|
||||||
static int s76_remove(struct platform_device *dev) {
|
static int s76_remove(struct platform_device *dev)
|
||||||
#endif
|
#endif
|
||||||
|
{
|
||||||
wmi_remove_notify_handler(S76_EVENT_GUID);
|
wmi_remove_notify_handler(S76_EVENT_GUID);
|
||||||
|
|
||||||
nv_hda_exit();
|
nv_hda_exit();
|
||||||
|
|
@ -248,40 +234,39 @@ static int s76_remove(struct platform_device *dev) {
|
||||||
s76_hwmon_fini(&dev->dev);
|
s76_hwmon_fini(&dev->dev);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (driver_flags & DRIVER_INPUT) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
s76_input_exit();
|
|
||||||
}
|
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
|
||||||
kb_led_exit();
|
kb_led_exit();
|
||||||
}
|
}
|
||||||
if (driver_flags & DRIVER_AP_LED) {
|
if (driver_flags & DRIVER_AP_LED) {
|
||||||
ap_led_exit();
|
ap_led_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6,11,0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s76_suspend(struct platform_device *dev, pm_message_t status) {
|
static int s76_suspend(struct device *dev)
|
||||||
S76_DEBUG("s76_suspend\n");
|
{
|
||||||
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
kb_led_suspend();
|
kb_led_suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int s76_resume(struct platform_device *dev) {
|
static int s76_resume(struct device *dev)
|
||||||
S76_DEBUG("s76_resume\n");
|
{
|
||||||
|
pr_debug("%s\n", __func__);
|
||||||
|
|
||||||
msleep(2000);
|
msleep(2000);
|
||||||
|
|
||||||
if (driver_flags & DRIVER_AP_LED) {
|
if (driver_flags & DRIVER_AP_LED) {
|
||||||
ap_led_resume();
|
ap_led_resume();
|
||||||
}
|
}
|
||||||
if (driver_flags & DRIVER_KB_LED) {
|
if (driver_flags & (DRIVER_KB_LED_WMI | DRIVER_KB_LED)) {
|
||||||
kb_led_resume();
|
kb_led_resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,18 +281,28 @@ static int s76_resume(struct platform_device *dev) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
||||||
|
static DEFINE_SIMPLE_DEV_PM_OPS(s76_pm, s76_suspend, s76_resume);
|
||||||
|
#else
|
||||||
|
static SIMPLE_DEV_PM_OPS(s76_pm, s76_suspend, s76_resume);
|
||||||
|
#endif
|
||||||
|
|
||||||
static struct platform_driver s76_platform_driver = {
|
static struct platform_driver s76_platform_driver = {
|
||||||
.remove = s76_remove,
|
.remove = s76_remove,
|
||||||
.suspend = s76_suspend,
|
|
||||||
.resume = s76_resume,
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = S76_DRIVER_NAME,
|
.name = S76_DRIVER_NAME,
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
||||||
|
.pm = pm_sleep_ptr(&s76_pm),
|
||||||
|
#else
|
||||||
|
.pm = pm_ptr(&s76_pm),
|
||||||
|
#endif
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init s76_dmi_matched(const struct dmi_system_id *id) {
|
static int __init s76_dmi_matched(const struct dmi_system_id *id)
|
||||||
S76_INFO("Model %s found\n", id->ident);
|
{
|
||||||
|
pr_info("Model %s found\n", id->ident);
|
||||||
driver_flags = (uint64_t)id->driver_data;
|
driver_flags = (uint64_t)id->driver_data;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
@ -334,74 +329,78 @@ static int __init s76_dmi_matched(const struct dmi_system_id *id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dmi_system_id s76_dmi_table[] __initdata = {
|
static struct dmi_system_id s76_dmi_table[] __initdata = {
|
||||||
DMI_TABLE_LEGACY("bonw13", DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE_LEGACY("bonw13", DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("addw1", DRIVER_AP_LED | DRIVER_KB_LED | DRIVER_OLED),
|
DMI_TABLE("addw1", DRIVER_AP_LED | DRIVER_KB_LED_WMI | DRIVER_OLED),
|
||||||
DMI_TABLE("addw2", DRIVER_AP_LED | DRIVER_KB_LED | DRIVER_OLED),
|
DMI_TABLE("addw2", DRIVER_AP_LED | DRIVER_KB_LED_WMI | DRIVER_OLED),
|
||||||
DMI_TABLE("bonw15-b", DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("addw5", DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("darp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("bonw15-b", DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("darp6", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("bonw16", DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
|
DMI_TABLE("darp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
|
DMI_TABLE("darp6", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("galp2", DRIVER_HWMON),
|
DMI_TABLE("galp2", DRIVER_HWMON),
|
||||||
DMI_TABLE("galp3", DRIVER_HWMON),
|
DMI_TABLE("galp3", DRIVER_HWMON),
|
||||||
DMI_TABLE("galp3-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON),
|
DMI_TABLE("galp3-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON),
|
||||||
DMI_TABLE("galp3-c", DRIVER_AP_LED | DRIVER_HWMON),
|
DMI_TABLE("galp3-c", DRIVER_AP_LED | DRIVER_HWMON),
|
||||||
DMI_TABLE("galp4", DRIVER_AP_LED | DRIVER_HWMON),
|
DMI_TABLE("galp4", DRIVER_AP_LED | DRIVER_HWMON),
|
||||||
DMI_TABLE("gaze13", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON),
|
DMI_TABLE("gaze13", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON),
|
||||||
DMI_TABLE("gaze14", DRIVER_AP_LED | DRIVER_KB_LED),
|
DMI_TABLE("gaze14", DRIVER_AP_LED | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("gaze15", DRIVER_AP_LED | DRIVER_KB_LED),
|
DMI_TABLE("gaze15", DRIVER_AP_LED | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("kudu5", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON),
|
DMI_TABLE("kudu5", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON),
|
||||||
DMI_TABLE("kudu6", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED),
|
DMI_TABLE("kudu6", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("oryp3-jeremy", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("oryp3-jeremy", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("oryp4", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("oryp4", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("oryp4-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("oryp4-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("oryp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("oryp5", DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("oryp6", DRIVER_AP_LED | DRIVER_KB_LED),
|
DMI_TABLE("oryp6", DRIVER_AP_LED | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("pang10", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED),
|
DMI_TABLE("oryp13", DRIVER_AP_KEY | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("pang11", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED),
|
DMI_TABLE("pang10", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("serw11", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("pang11", DRIVER_AP_KEY | DRIVER_AP_WMI | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("serw11-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED),
|
DMI_TABLE("serw11", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
DMI_TABLE("serw12", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_AP_WMI | DRIVER_KB_LED),
|
DMI_TABLE("serw11-b", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_HWMON | DRIVER_KB_LED_WMI),
|
||||||
|
DMI_TABLE("serw12", DRIVER_AP_KEY | DRIVER_AP_LED | DRIVER_AP_WMI | DRIVER_KB_LED_WMI),
|
||||||
|
DMI_TABLE("serw14", DRIVER_HWMON | DRIVER_KB_LED),
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
MODULE_DEVICE_TABLE(dmi, s76_dmi_table);
|
MODULE_DEVICE_TABLE(dmi, s76_dmi_table);
|
||||||
|
|
||||||
static int __init s76_init(void) {
|
static int __init s76_init(void)
|
||||||
|
{
|
||||||
if (!dmi_check_system(s76_dmi_table)) {
|
if (!dmi_check_system(s76_dmi_table)) {
|
||||||
S76_INFO("Model does not utilize this driver");
|
pr_info("Model does not utilize this driver");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!driver_flags) {
|
if (!driver_flags) {
|
||||||
S76_INFO("Driver data not defined");
|
pr_info("Driver data not defined");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wmi_has_guid(S76_EVENT_GUID)) {
|
if (!wmi_has_guid(S76_EVENT_GUID)) {
|
||||||
S76_INFO("No known WMI event notification GUID found\n");
|
pr_info("No known WMI event notification GUID found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wmi_has_guid(S76_WMBB_GUID)) {
|
if (!wmi_has_guid(S76_WMBB_GUID)) {
|
||||||
S76_INFO("No known WMI control method GUID found\n");
|
pr_info("No known WMI control method GUID found\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
s76_platform_device =
|
s76_platform_device =
|
||||||
platform_create_bundle(&s76_platform_driver, s76_probe, NULL, 0, NULL, 0);
|
platform_create_bundle(&s76_platform_driver, s76_probe, NULL, 0, NULL, 0);
|
||||||
|
|
||||||
if (unlikely(IS_ERR(s76_platform_device))) {
|
if (IS_ERR(s76_platform_device)) {
|
||||||
return PTR_ERR(s76_platform_device);
|
return PTR_ERR(s76_platform_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
module_init(s76_init);
|
||||||
|
|
||||||
static void __exit s76_exit(void) {
|
static void __exit s76_exit(void)
|
||||||
|
{
|
||||||
platform_device_unregister(s76_platform_device);
|
platform_device_unregister(s76_platform_device);
|
||||||
platform_driver_unregister(&s76_platform_driver);
|
platform_driver_unregister(&s76_platform_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(s76_init);
|
|
||||||
module_exit(s76_exit);
|
module_exit(s76_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>");
|
MODULE_AUTHOR("Jeremy Soller <jeremy@system76.com>");
|
||||||
279
system76_hwmon.c
279
system76_hwmon.c
|
|
@ -1,279 +0,0 @@
|
||||||
/*
|
|
||||||
* fan.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define EXPERIMENTAL
|
|
||||||
|
|
||||||
#if S76_HAS_HWMON
|
|
||||||
struct s76_hwmon {
|
|
||||||
struct device *dev;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct s76_hwmon *s76_hwmon = NULL;
|
|
||||||
|
|
||||||
static int
|
|
||||||
s76_read_fan(int idx) {
|
|
||||||
u8 value;
|
|
||||||
int raw_rpm;
|
|
||||||
ec_read(0xd0 + 0x2 * idx, &value);
|
|
||||||
raw_rpm = value << 8;
|
|
||||||
ec_read(0xd1 + 0x2 * idx, &value);
|
|
||||||
raw_rpm += value;
|
|
||||||
if (!raw_rpm)
|
|
||||||
return 0;
|
|
||||||
return 2156220 / raw_rpm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
s76_read_pwm(int idx) {
|
|
||||||
u8 value;
|
|
||||||
ec_read(0xce + idx, &value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
s76_write_pwm(int idx, u8 duty) {
|
|
||||||
u8 values[] = {idx + 1, duty};
|
|
||||||
return ec_transaction(0x99, values, sizeof(values), NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
s76_write_pwm_auto(int idx) {
|
|
||||||
u8 values[] = {0xff, idx + 1};
|
|
||||||
return ec_transaction(0x99, values, sizeof(values), NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_name(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
return sprintf(buf, S76_DRIVER_NAME "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_fan_input(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
int index = to_sensor_dev_attr(attr)->index;
|
|
||||||
return sprintf(buf, "%i\n", s76_read_fan(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_fan_label(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
switch (to_sensor_dev_attr(attr)->index) {
|
|
||||||
case 0:
|
|
||||||
return sprintf(buf, "CPU fan\n");
|
|
||||||
case 1:
|
|
||||||
return sprintf(buf, "GPU fan\n");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int pwm_enabled[] = {2, 2};
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_pwm(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
int index = to_sensor_dev_attr(attr)->index;
|
|
||||||
return sprintf(buf, "%i\n", s76_read_pwm(index));
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_set_pwm(struct device *dev, struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
u32 value;
|
|
||||||
int err;
|
|
||||||
int index = to_sensor_dev_attr(attr)->index;
|
|
||||||
|
|
||||||
err = kstrtou32(buf, 10, &value);
|
|
||||||
if (err) return err;
|
|
||||||
if (value > 255) return -EINVAL;
|
|
||||||
err = s76_write_pwm(index, value);
|
|
||||||
if (err) return err;
|
|
||||||
pwm_enabled[index] = 1;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_pwm_enable(struct device *dev, struct device_attribute *attr,
|
|
||||||
char *buf)
|
|
||||||
{
|
|
||||||
int index = to_sensor_dev_attr(attr)->index;
|
|
||||||
return sprintf(buf, "%i\n", pwm_enabled[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_set_pwm_enable(struct device *dev, struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
|
||||||
u32 value;
|
|
||||||
int err;
|
|
||||||
int index = to_sensor_dev_attr(attr)->index;
|
|
||||||
|
|
||||||
err = kstrtou32(buf, 10, &value);
|
|
||||||
if (err) return err;
|
|
||||||
if (value == 0) {
|
|
||||||
err = s76_write_pwm(index, 255);
|
|
||||||
if (err) return err;
|
|
||||||
pwm_enabled[index] = value;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
if (value == 1) {
|
|
||||||
err = s76_write_pwm(index, 0);
|
|
||||||
if (err) return err;
|
|
||||||
pwm_enabled[index] = value;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
if (value == 2) {
|
|
||||||
err = s76_write_pwm_auto(index);
|
|
||||||
if (err) return err;
|
|
||||||
pwm_enabled[index] = value;
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_temp1_input(struct device *dev, struct device_attribute *attr, char *buf) {
|
|
||||||
u8 value;
|
|
||||||
ec_read(0x07, &value);
|
|
||||||
return sprintf(buf, "%i\n", value * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_temp1_label(struct device *dev, struct device_attribute *attr, char *buf) {
|
|
||||||
return sprintf(buf, "CPU temperature\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_temp2_input(struct device *dev, struct device_attribute *attr, char *buf) {
|
|
||||||
u8 value;
|
|
||||||
ec_read(0xcd, &value);
|
|
||||||
return sprintf(buf, "%i\n", value * 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t
|
|
||||||
s76_hwmon_show_temp2_label(struct device *dev, struct device_attribute *attr, char *buf) {
|
|
||||||
return sprintf(buf, "GPU temperature\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static SENSOR_DEVICE_ATTR(name, S_IRUGO, s76_hwmon_show_name, NULL, 0);
|
|
||||||
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, s76_hwmon_show_fan_input, NULL, 0);
|
|
||||||
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, s76_hwmon_show_fan_label, NULL, 0);
|
|
||||||
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, s76_hwmon_show_pwm, s76_hwmon_set_pwm, 0);
|
|
||||||
static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, s76_hwmon_show_pwm_enable, s76_hwmon_set_pwm_enable, 0);
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, s76_hwmon_show_fan_input, NULL, 1);
|
|
||||||
static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, s76_hwmon_show_fan_label, NULL, 1);
|
|
||||||
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, s76_hwmon_show_pwm, s76_hwmon_set_pwm, 1);
|
|
||||||
static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, s76_hwmon_show_pwm_enable, s76_hwmon_set_pwm_enable, 1);
|
|
||||||
#endif
|
|
||||||
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, s76_hwmon_show_temp1_input, NULL, 0);
|
|
||||||
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, s76_hwmon_show_temp1_label, NULL, 0);
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, s76_hwmon_show_temp2_input, NULL, 1);
|
|
||||||
static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, s76_hwmon_show_temp2_label, NULL, 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct attribute *hwmon_default_attributes[] = {
|
|
||||||
&sensor_dev_attr_name.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_fan1_input.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_fan1_label.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_pwm1.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_pwm1_enable.dev_attr.attr,
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
&sensor_dev_attr_fan2_input.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_fan2_label.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_pwm2.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_pwm2_enable.dev_attr.attr,
|
|
||||||
#endif
|
|
||||||
&sensor_dev_attr_temp1_input.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_temp1_label.dev_attr.attr,
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
&sensor_dev_attr_temp2_input.dev_attr.attr,
|
|
||||||
&sensor_dev_attr_temp2_label.dev_attr.attr,
|
|
||||||
#endif
|
|
||||||
NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct attribute_group hwmon_default_attrgroup = {
|
|
||||||
.attrs = hwmon_default_attributes,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int s76_hwmon_reboot_callback(struct notifier_block *nb,
|
|
||||||
unsigned long action, void *data) {
|
|
||||||
s76_write_pwm_auto(0);
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
s76_write_pwm_auto(1);
|
|
||||||
#endif
|
|
||||||
return NOTIFY_DONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct notifier_block s76_hwmon_reboot_notifier = {
|
|
||||||
.notifier_call = s76_hwmon_reboot_callback
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
|
||||||
s76_hwmon_init(struct device *dev) {
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
s76_hwmon = kzalloc(sizeof(*s76_hwmon), GFP_KERNEL);
|
|
||||||
if (!s76_hwmon)
|
|
||||||
return -ENOMEM;
|
|
||||||
s76_hwmon->dev = hwmon_device_register(dev);
|
|
||||||
if (IS_ERR(s76_hwmon->dev)) {
|
|
||||||
ret = PTR_ERR(s76_hwmon->dev);
|
|
||||||
s76_hwmon->dev = NULL;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = sysfs_create_group(&s76_hwmon->dev->kobj, &hwmon_default_attrgroup);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
register_reboot_notifier(&s76_hwmon_reboot_notifier);
|
|
||||||
s76_write_pwm_auto(0);
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
s76_write_pwm_auto(1);
|
|
||||||
#endif
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
s76_hwmon_fini(struct device *dev) {
|
|
||||||
if (!s76_hwmon || !s76_hwmon->dev)
|
|
||||||
return 0;
|
|
||||||
s76_write_pwm_auto(0);
|
|
||||||
#ifdef EXPERIMENTAL
|
|
||||||
s76_write_pwm_auto(1);
|
|
||||||
#endif
|
|
||||||
unregister_reboot_notifier(&s76_hwmon_reboot_notifier);
|
|
||||||
sysfs_remove_group(&s76_hwmon->dev->kobj, &hwmon_default_attrgroup);
|
|
||||||
hwmon_device_unregister(s76_hwmon->dev);
|
|
||||||
kfree(s76_hwmon);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif // S76_HAS_HWMON
|
|
||||||
|
|
@ -1,176 +0,0 @@
|
||||||
/* Based on bbswitch,
|
|
||||||
* Copyright (C) 2011-2013 Bumblebee Project
|
|
||||||
* Author: Peter Wu <lekensteyn@gmail.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/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
enum {
|
|
||||||
CARD_UNCHANGED = -1,
|
|
||||||
CARD_OFF = 0,
|
|
||||||
CARD_ON = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct pci_dev *dis_dev;
|
|
||||||
static struct pci_dev *sub_dev = NULL;
|
|
||||||
|
|
||||||
// Returns 1 if the card is disabled, 0 if enabled
|
|
||||||
static int is_card_disabled(void) {
|
|
||||||
//check for: 1.bit is set 2.sub-function is available.
|
|
||||||
u32 cfg_word;
|
|
||||||
struct pci_dev *tmp_dev = NULL;
|
|
||||||
|
|
||||||
sub_dev = NULL;
|
|
||||||
|
|
||||||
// read config word at 0x488
|
|
||||||
pci_read_config_dword(dis_dev, 0x488, &cfg_word);
|
|
||||||
if ((cfg_word & 0x2000000)==0x2000000) {
|
|
||||||
//check for subdevice. read first config dword of sub function 1
|
|
||||||
while ((tmp_dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, tmp_dev)) != NULL) {
|
|
||||||
int pci_class = tmp_dev->class >> 8;
|
|
||||||
|
|
||||||
if (pci_class != 0x403)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (tmp_dev->vendor == PCI_VENDOR_ID_NVIDIA) {
|
|
||||||
sub_dev = tmp_dev;
|
|
||||||
S76_INFO("Found NVIDIA audio device %s\n", dev_name(&tmp_dev->dev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sub_dev == NULL) {
|
|
||||||
S76_INFO("No NVIDIA audio device found, unsetting config bit.\n");
|
|
||||||
cfg_word|=0x2000000;
|
|
||||||
pci_write_config_dword(dis_dev, 0x488, cfg_word);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nv_hda_off(void) {
|
|
||||||
u32 cfg_word;
|
|
||||||
if (is_card_disabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//remove device
|
|
||||||
pci_dev_put(sub_dev);
|
|
||||||
pci_stop_and_remove_bus_device(sub_dev);
|
|
||||||
|
|
||||||
S76_INFO("NVIDIA audio: disabling\n");
|
|
||||||
|
|
||||||
//setting bit to turn off
|
|
||||||
pci_read_config_dword(dis_dev, 0x488, &cfg_word);
|
|
||||||
cfg_word&=0xfdffffff;
|
|
||||||
pci_write_config_dword(dis_dev, 0x488, cfg_word);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void nv_hda_on(void) {
|
|
||||||
u32 cfg_word;
|
|
||||||
u8 hdr_type;
|
|
||||||
|
|
||||||
if (!is_card_disabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
S76_INFO("NVIDIA audio: enabling\n");
|
|
||||||
|
|
||||||
// read,set bit, write config word at 0x488
|
|
||||||
pci_read_config_dword(dis_dev, 0x488, &cfg_word);
|
|
||||||
cfg_word|=0x2000000;
|
|
||||||
pci_write_config_dword(dis_dev, 0x488, cfg_word);
|
|
||||||
|
|
||||||
//pci_scan_single_device
|
|
||||||
pci_read_config_byte(dis_dev, PCI_HEADER_TYPE, &hdr_type);
|
|
||||||
|
|
||||||
if (!(hdr_type & 0x80)) {
|
|
||||||
S76_ERROR("NVIDIA not multifunction, no audio\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub_dev = pci_scan_single_device(dis_dev->bus, 1);
|
|
||||||
if (!sub_dev) {
|
|
||||||
S76_ERROR("No NVIDIA audio device found\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
S76_INFO("NVIDIA audio found, adding\n");
|
|
||||||
pci_assign_unassigned_bus_resources(dis_dev->bus);
|
|
||||||
pci_bus_add_devices(dis_dev->bus);
|
|
||||||
pci_dev_get(sub_dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* power bus so we can read PCI configuration space */
|
|
||||||
static void dis_dev_get(void) {
|
|
||||||
if (dis_dev->bus && dis_dev->bus->self) {
|
|
||||||
pm_runtime_get_sync(&dis_dev->bus->self->dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dis_dev_put(void) {
|
|
||||||
if (dis_dev->bus && dis_dev->bus->self) {
|
|
||||||
pm_runtime_put_sync(&dis_dev->bus->self->dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int __init nv_hda_init(struct device *dev) {
|
|
||||||
struct pci_dev *pdev = NULL;
|
|
||||||
|
|
||||||
while ((pdev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pdev)) != NULL) {
|
|
||||||
int pci_class = pdev->class >> 8;
|
|
||||||
|
|
||||||
if (pci_class != PCI_CLASS_DISPLAY_VGA && pci_class != PCI_CLASS_DISPLAY_3D) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pdev->vendor == PCI_VENDOR_ID_NVIDIA) {
|
|
||||||
dis_dev = pdev;
|
|
||||||
S76_INFO("NVIDIA device %s\n", dev_name(&pdev->dev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dis_dev == NULL) {
|
|
||||||
S76_ERROR("No NVIDIA device found\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
dis_dev_get();
|
|
||||||
|
|
||||||
nv_hda_on();
|
|
||||||
|
|
||||||
S76_INFO("NVIDIA Audio %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
|
|
||||||
|
|
||||||
dis_dev_put();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit nv_hda_exit(void) {
|
|
||||||
if (dis_dev == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
dis_dev_get();
|
|
||||||
|
|
||||||
nv_hda_off();
|
|
||||||
|
|
||||||
pr_info("NVIDIA Audio %s is %s\n", dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
|
|
||||||
|
|
||||||
dis_dev_put();
|
|
||||||
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue