Commit bf155714 authored by Mattia Dongili's avatar Mattia Dongili Committed by Matthew Garrett

sony-laptop: implement keyboard backlight support

Recent Vaios have the opportunity to control the keyboard backlight via
ACPI calls to the SNC device.
Introduce two module parameters to control how keyboard backlight should
be set at module loading (default to on and with 10 seconds timeout).
Tested-by: default avatarMarco Chiappero <marco@absence.it>
Signed-off-by: default avatarMattia Dongili <malattia@linux.it>
Signed-off-by: default avatarMatthew Garrett <mjg@redhat.com>
parent 2a4f0c81
......@@ -125,6 +125,19 @@ MODULE_PARM_DESC(minor,
"default is -1 (automatic)");
#endif
static int kbd_backlight; /* = 1 */
module_param(kbd_backlight, int, 0444);
MODULE_PARM_DESC(kbd_backlight,
"set this to 0 to disable keyboard backlight, "
"1 to enable it (default: 0)");
static int kbd_backlight_timeout; /* = 0 */
module_param(kbd_backlight_timeout, int, 0444);
MODULE_PARM_DESC(kbd_backlight_timeout,
"set this to 0 to set the default 10 seconds timeout, "
"1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
"(default: 0)");
enum sony_nc_rfkill {
SONY_WIFI,
SONY_BLUETOOTH,
......@@ -1309,6 +1322,161 @@ static void sony_nc_rfkill_setup(struct acpi_device *device)
return;
}
/* Keyboard backlight feature */
#define KBDBL_HANDLER 0x137
#define KBDBL_PRESENT 0xB00
#define SET_MODE 0xC00
#define SET_TIMEOUT 0xE00
struct kbd_backlight {
int mode;
int timeout;
struct device_attribute mode_attr;
struct device_attribute timeout_attr;
};
struct kbd_backlight *kbdbl_handle;
static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value)
{
int result;
if (value > 1)
return -EINVAL;
if (sony_call_snc_handle(KBDBL_HANDLER,
(value << 0x10) | SET_MODE, &result))
return -EIO;
kbdbl_handle->mode = value;
return 0;
}
static ssize_t sony_nc_kbd_backlight_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
int ret = 0;
unsigned long value;
if (count > 31)
return -EINVAL;
if (strict_strtoul(buffer, 10, &value))
return -EINVAL;
ret = __sony_nc_kbd_backlight_mode_set(value);
if (ret < 0)
return ret;
return count;
}
static ssize_t sony_nc_kbd_backlight_mode_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count = 0;
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->mode);
return count;
}
static int __sony_nc_kbd_backlight_timeout_set(u8 value)
{
int result;
if (value > 3)
return -EINVAL;
if (sony_call_snc_handle(KBDBL_HANDLER,
(value << 0x10) | SET_TIMEOUT, &result))
return -EIO;
kbdbl_handle->timeout = value;
return 0;
}
static ssize_t sony_nc_kbd_backlight_timeout_store(struct device *dev,
struct device_attribute *attr,
const char *buffer, size_t count)
{
int ret = 0;
unsigned long value;
if (count > 31)
return -EINVAL;
if (strict_strtoul(buffer, 10, &value))
return -EINVAL;
ret = __sony_nc_kbd_backlight_timeout_set(value);
if (ret < 0)
return ret;
return count;
}
static ssize_t sony_nc_kbd_backlight_timeout_show(struct device *dev,
struct device_attribute *attr, char *buffer)
{
ssize_t count = 0;
count = snprintf(buffer, PAGE_SIZE, "%d\n", kbdbl_handle->timeout);
return count;
}
static int sony_nc_kbd_backlight_setup(struct platform_device *pd)
{
int result;
if (sony_call_snc_handle(0x137, KBDBL_PRESENT, &result))
return 0;
if (!(result & 0x02))
return 0;
kbdbl_handle = kzalloc(sizeof(*kbdbl_handle), GFP_KERNEL);
sysfs_attr_init(&kbdbl_handle->mode_attr.attr);
kbdbl_handle->mode_attr.attr.name = "kbd_backlight";
kbdbl_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR;
kbdbl_handle->mode_attr.show = sony_nc_kbd_backlight_mode_show;
kbdbl_handle->mode_attr.store = sony_nc_kbd_backlight_mode_store;
sysfs_attr_init(&kbdbl_handle->timeout_attr.attr);
kbdbl_handle->timeout_attr.attr.name = "kbd_backlight_timeout";
kbdbl_handle->timeout_attr.attr.mode = S_IRUGO | S_IWUSR;
kbdbl_handle->timeout_attr.show = sony_nc_kbd_backlight_timeout_show;
kbdbl_handle->timeout_attr.store = sony_nc_kbd_backlight_timeout_store;
if (device_create_file(&pd->dev, &kbdbl_handle->mode_attr))
goto outkzalloc;
if (device_create_file(&pd->dev, &kbdbl_handle->timeout_attr))
goto outmode;
__sony_nc_kbd_backlight_mode_set(kbd_backlight);
__sony_nc_kbd_backlight_timeout_set(kbd_backlight_timeout);
return 0;
outmode:
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
outkzalloc:
kfree(kbdbl_handle);
kbdbl_handle = NULL;
return -1;
}
static int sony_nc_kbd_backlight_cleanup(struct platform_device *pd)
{
if (kbdbl_handle) {
device_remove_file(&pd->dev, &kbdbl_handle->mode_attr);
device_remove_file(&pd->dev, &kbdbl_handle->timeout_attr);
kfree(kbdbl_handle);
}
return 0;
}
static int sony_nc_add(struct acpi_device *device)
{
acpi_status status;
......@@ -1359,6 +1527,8 @@ static int sony_nc_add(struct acpi_device *device)
dprintk("Doing SNC setup\n");
if (sony_nc_handles_setup(sony_pf_device))
goto outpresent;
if (sony_nc_kbd_backlight_setup(sony_pf_device))
goto outsnc;
sony_nc_function_setup(device);
sony_nc_rfkill_setup(device);
}
......@@ -1367,7 +1537,7 @@ static int sony_nc_add(struct acpi_device *device)
result = sony_laptop_setup_input(device);
if (result) {
pr_err(DRV_PFX "Unable to create input devices.\n");
goto outsnc;
goto outkbdbacklight;
}
if (acpi_video_backlight_support()) {
......@@ -1445,6 +1615,9 @@ static int sony_nc_add(struct acpi_device *device)
sony_laptop_remove_input();
outkbdbacklight:
sony_nc_kbd_backlight_cleanup(sony_pf_device);
outsnc:
sony_nc_handles_cleanup(sony_pf_device);
......@@ -1469,6 +1642,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
device_remove_file(&sony_pf_device->dev, &item->devattr);
}
sony_nc_kbd_backlight_cleanup(sony_pf_device);
sony_nc_handles_cleanup(sony_pf_device);
sony_pf_remove();
sony_laptop_remove_input();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment