Commit 674b5d58 authored by Darren Hart's avatar Darren Hart Committed by Darren Hart (VMware)

Merge branch 'linux-leds/dell-laptop-changes-for-4.12'

Merge branch 'dell-laptop-changes-for-4.12' of
git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds.git
to avoid linux-next merge conflict with dell-laptop.c.
Signed-off-by: default avatarDarren Hart (VMware) <dvhart@infradead.org>
parents 6b8e7d8f ab768386
...@@ -463,15 +463,6 @@ config LEDS_ADP5520 ...@@ -463,15 +463,6 @@ config LEDS_ADP5520
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called leds-adp5520. be called leds-adp5520.
config LEDS_DELL_NETBOOKS
tristate "External LED on Dell Business Netbooks"
depends on LEDS_CLASS
depends on X86 && ACPI_WMI
depends on DELL_SMBIOS
help
This adds support for the Latitude 2100 and similar
notebooks that have an external LED.
config LEDS_MC13783 config LEDS_MC13783
tristate "LED Support for MC13XXX PMIC" tristate "LED Support for MC13XXX PMIC"
depends on LEDS_CLASS depends on LEDS_CLASS
......
...@@ -52,7 +52,6 @@ obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o ...@@ -52,7 +52,6 @@ obj-$(CONFIG_LEDS_REGULATOR) += leds-regulator.o
obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o obj-$(CONFIG_LEDS_INTEL_SS4200) += leds-ss4200.o
obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o obj-$(CONFIG_LEDS_LT3593) += leds-lt3593.o
obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o obj-$(CONFIG_LEDS_ADP5520) += leds-adp5520.o
obj-$(CONFIG_LEDS_DELL_NETBOOKS) += dell-led.o
obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o obj-$(CONFIG_LEDS_MC13783) += leds-mc13783.o
obj-$(CONFIG_LEDS_NS2) += leds-ns2.o obj-$(CONFIG_LEDS_NS2) += leds-ns2.o
obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o obj-$(CONFIG_LEDS_NETXBIG) += leds-netxbig.o
......
...@@ -31,12 +31,16 @@ ...@@ -31,12 +31,16 @@
#define MAX_NAME_LEN 8 #define MAX_NAME_LEN 8
struct led_trigger_cpu { struct led_trigger_cpu {
bool is_active;
char name[MAX_NAME_LEN]; char name[MAX_NAME_LEN];
struct led_trigger *_trig; struct led_trigger *_trig;
}; };
static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
static struct led_trigger *trig_cpu_all;
static atomic_t num_active_cpus = ATOMIC_INIT(0);
/** /**
* ledtrig_cpu - emit a CPU event as a trigger * ledtrig_cpu - emit a CPU event as a trigger
* @evt: CPU event to be emitted * @evt: CPU event to be emitted
...@@ -47,26 +51,46 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); ...@@ -47,26 +51,46 @@ static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
void ledtrig_cpu(enum cpu_led_event ledevt) void ledtrig_cpu(enum cpu_led_event ledevt)
{ {
struct led_trigger_cpu *trig = this_cpu_ptr(&cpu_trig); struct led_trigger_cpu *trig = this_cpu_ptr(&cpu_trig);
bool is_active = trig->is_active;
/* Locate the correct CPU LED */ /* Locate the correct CPU LED */
switch (ledevt) { switch (ledevt) {
case CPU_LED_IDLE_END: case CPU_LED_IDLE_END:
case CPU_LED_START: case CPU_LED_START:
/* Will turn the LED on, max brightness */ /* Will turn the LED on, max brightness */
led_trigger_event(trig->_trig, LED_FULL); is_active = true;
break; break;
case CPU_LED_IDLE_START: case CPU_LED_IDLE_START:
case CPU_LED_STOP: case CPU_LED_STOP:
case CPU_LED_HALTED: case CPU_LED_HALTED:
/* Will turn the LED off */ /* Will turn the LED off */
led_trigger_event(trig->_trig, LED_OFF); is_active = false;
break; break;
default: default:
/* Will leave the LED as it is */ /* Will leave the LED as it is */
break; break;
} }
if (is_active != trig->is_active) {
unsigned int active_cpus;
unsigned int total_cpus;
/* Update trigger state */
trig->is_active = is_active;
atomic_add(is_active ? 1 : -1, &num_active_cpus);
active_cpus = atomic_read(&num_active_cpus);
total_cpus = num_present_cpus();
led_trigger_event(trig->_trig,
is_active ? LED_FULL : LED_OFF);
led_trigger_event(trig_cpu_all,
DIV_ROUND_UP(LED_FULL * active_cpus, total_cpus));
}
} }
EXPORT_SYMBOL(ledtrig_cpu); EXPORT_SYMBOL(ledtrig_cpu);
...@@ -112,6 +136,11 @@ static int __init ledtrig_cpu_init(void) ...@@ -112,6 +136,11 @@ static int __init ledtrig_cpu_init(void)
/* Supports up to 9999 cpu cores */ /* Supports up to 9999 cpu cores */
BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); BUILD_BUG_ON(CONFIG_NR_CPUS > 9999);
/*
* Registering a trigger for all CPUs.
*/
led_trigger_register_simple("cpu", &trig_cpu_all);
/* /*
* Registering CPU led trigger for each CPU core here * Registering CPU led trigger for each CPU core here
* ignores CPU hotplug, but after this CPU hotplug works * ignores CPU hotplug, but after this CPU hotplug works
......
...@@ -141,6 +141,14 @@ config DELL_WMI_AIO ...@@ -141,6 +141,14 @@ config DELL_WMI_AIO
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called dell-wmi-aio. be called dell-wmi-aio.
config DELL_WMI_LED
tristate "External LED on Dell Business Netbooks"
depends on LEDS_CLASS
depends on ACPI_WMI
help
This adds support for the Latitude 2100 and similar
notebooks that have an external LED.
config DELL_SMO8800 config DELL_SMO8800
tristate "Dell Latitude freefall driver (ACPI SMO88XX)" tristate "Dell Latitude freefall driver (ACPI SMO88XX)"
depends on ACPI depends on ACPI
......
...@@ -15,6 +15,7 @@ obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o ...@@ -15,6 +15,7 @@ obj-$(CONFIG_DELL_SMBIOS) += dell-smbios.o
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
obj-$(CONFIG_DELL_WMI_LED) += dell-wmi-led.o
obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o obj-$(CONFIG_DELL_SMO8800) += dell-smo8800.o
obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o obj-$(CONFIG_DELL_RBTN) += dell-rbtn.o
obj-$(CONFIG_ACER_WMI) += acer-wmi.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/i8042.h> #include <linux/i8042.h>
#include <linux/debugfs.h> #include <linux/debugfs.h>
#include <linux/dell-led.h>
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <acpi/video.h> #include <acpi/video.h>
#include "dell-rbtn.h" #include "dell-rbtn.h"
...@@ -42,6 +43,8 @@ ...@@ -42,6 +43,8 @@
#define KBD_LED_AUTO_50_TOKEN 0x02EB #define KBD_LED_AUTO_50_TOKEN 0x02EB
#define KBD_LED_AUTO_75_TOKEN 0x02EC #define KBD_LED_AUTO_75_TOKEN 0x02EC
#define KBD_LED_AUTO_100_TOKEN 0x02F6 #define KBD_LED_AUTO_100_TOKEN 0x02F6
#define GLOBAL_MIC_MUTE_ENABLE 0x0364
#define GLOBAL_MIC_MUTE_DISABLE 0x0365
struct quirk_entry { struct quirk_entry {
u8 touchpad_led; u8 touchpad_led;
...@@ -2046,6 +2049,31 @@ static struct notifier_block dell_laptop_notifier = { ...@@ -2046,6 +2049,31 @@ static struct notifier_block dell_laptop_notifier = {
.notifier_call = dell_laptop_notifier_call, .notifier_call = dell_laptop_notifier_call,
}; };
int dell_micmute_led_set(int state)
{
struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
if (state == 0)
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE);
else if (state == 1)
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE);
else
return -EINVAL;
if (!token)
return -ENODEV;
buffer = dell_smbios_get_buffer();
buffer->input[0] = token->location;
buffer->input[1] = token->value;
dell_smbios_send_request(1, 0);
dell_smbios_release_buffer();
return state;
}
EXPORT_SYMBOL_GPL(dell_micmute_led_set);
static int __init dell_init(void) static int __init dell_init(void)
{ {
struct calling_interface_buffer *buffer; struct calling_interface_buffer *buffer;
......
/* /*
* dell_led.c - Dell LED Driver
*
* Copyright (C) 2010 Dell Inc. * Copyright (C) 2010 Dell Inc.
* Louis Davis <louis_davis@dell.com> * Louis Davis <louis_davis@dell.com>
* Jim Dailey <jim_dailey@dell.com> * Jim Dailey <jim_dailey@dell.com>
...@@ -15,16 +13,12 @@ ...@@ -15,16 +13,12 @@
#include <linux/leds.h> #include <linux/leds.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/dmi.h>
#include <linux/dell-led.h>
#include "../platform/x86/dell-smbios.h"
MODULE_AUTHOR("Louis Davis/Jim Dailey"); MODULE_AUTHOR("Louis Davis/Jim Dailey");
MODULE_DESCRIPTION("Dell LED Control Driver"); MODULE_DESCRIPTION("Dell LED Control Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
#define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396" #define DELL_LED_BIOS_GUID "F6E4FE6E-909D-47cb-8BAB-C9F6F2F8D396"
#define DELL_APP_GUID "A80593CE-A997-11DA-B012-B622A1EF5492"
MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
/* Error Result Codes: */ /* Error Result Codes: */
...@@ -43,53 +37,6 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID); ...@@ -43,53 +37,6 @@ MODULE_ALIAS("wmi:" DELL_LED_BIOS_GUID);
#define CMD_LED_OFF 17 #define CMD_LED_OFF 17
#define CMD_LED_BLINK 18 #define CMD_LED_BLINK 18
#define GLOBAL_MIC_MUTE_ENABLE 0x364
#define GLOBAL_MIC_MUTE_DISABLE 0x365
static int dell_micmute_led_set(int state)
{
struct calling_interface_buffer *buffer;
struct calling_interface_token *token;
if (!wmi_has_guid(DELL_APP_GUID))
return -ENODEV;
if (state == 0)
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_DISABLE);
else if (state == 1)
token = dell_smbios_find_token(GLOBAL_MIC_MUTE_ENABLE);
else
return -EINVAL;
if (!token)
return -ENODEV;
buffer = dell_smbios_get_buffer();
buffer->input[0] = token->location;
buffer->input[1] = token->value;
dell_smbios_send_request(1, 0);
dell_smbios_release_buffer();
return state;
}
int dell_app_wmi_led_set(int whichled, int on)
{
int state = 0;
switch (whichled) {
case DELL_LED_MICMUTE:
state = dell_micmute_led_set(on);
break;
default:
pr_warn("led type %x is not supported\n", whichled);
break;
}
return state;
}
EXPORT_SYMBOL_GPL(dell_app_wmi_led_set);
struct bios_args { struct bios_args {
unsigned char length; unsigned char length;
unsigned char result_code; unsigned char result_code;
...@@ -99,37 +46,29 @@ struct bios_args { ...@@ -99,37 +46,29 @@ struct bios_args {
unsigned char off_time; unsigned char off_time;
}; };
static int dell_led_perform_fn(u8 length, static int dell_led_perform_fn(u8 length, u8 result_code, u8 device_id,
u8 result_code, u8 command, u8 on_time, u8 off_time)
u8 device_id,
u8 command,
u8 on_time,
u8 off_time)
{ {
struct bios_args *bios_return;
u8 return_code;
union acpi_object *obj;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct bios_args *bios_return;
struct acpi_buffer input; struct acpi_buffer input;
union acpi_object *obj;
acpi_status status; acpi_status status;
u8 return_code;
struct bios_args args; struct bios_args args = {
args.length = length; .length = length,
args.result_code = result_code; .result_code = result_code,
args.device_id = device_id; .device_id = device_id,
args.command = command; .command = command,
args.on_time = on_time; .on_time = on_time,
args.off_time = off_time; .off_time = off_time
};
input.length = sizeof(struct bios_args); input.length = sizeof(struct bios_args);
input.pointer = &args; input.pointer = &args;
status = wmi_evaluate_method(DELL_LED_BIOS_GUID, status = wmi_evaluate_method(DELL_LED_BIOS_GUID, 1, 1, &input, &output);
1,
1,
&input,
&output);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return status; return status;
...@@ -137,7 +76,7 @@ static int dell_led_perform_fn(u8 length, ...@@ -137,7 +76,7 @@ static int dell_led_perform_fn(u8 length,
if (!obj) if (!obj)
return -EINVAL; return -EINVAL;
else if (obj->type != ACPI_TYPE_BUFFER) { if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj); kfree(obj);
return -EINVAL; return -EINVAL;
} }
...@@ -170,8 +109,7 @@ static int led_off(void) ...@@ -170,8 +109,7 @@ static int led_off(void)
0); /* not used */ 0); /* not used */
} }
static int led_blink(unsigned char on_eighths, static int led_blink(unsigned char on_eighths, unsigned char off_eighths)
unsigned char off_eighths)
{ {
return dell_led_perform_fn(5, /* Length of command */ return dell_led_perform_fn(5, /* Length of command */
INTERFACE_ERROR, /* Init to INTERFACE_ERROR */ INTERFACE_ERROR, /* Init to INTERFACE_ERROR */
...@@ -182,7 +120,7 @@ static int led_blink(unsigned char on_eighths, ...@@ -182,7 +120,7 @@ static int led_blink(unsigned char on_eighths,
} }
static void dell_led_set(struct led_classdev *led_cdev, static void dell_led_set(struct led_classdev *led_cdev,
enum led_brightness value) enum led_brightness value)
{ {
if (value == LED_OFF) if (value == LED_OFF)
led_off(); led_off();
...@@ -191,27 +129,22 @@ static void dell_led_set(struct led_classdev *led_cdev, ...@@ -191,27 +129,22 @@ static void dell_led_set(struct led_classdev *led_cdev,
} }
static int dell_led_blink(struct led_classdev *led_cdev, static int dell_led_blink(struct led_classdev *led_cdev,
unsigned long *delay_on, unsigned long *delay_on, unsigned long *delay_off)
unsigned long *delay_off)
{ {
unsigned long on_eighths; unsigned long on_eighths;
unsigned long off_eighths; unsigned long off_eighths;
/* The Dell LED delay is based on 125ms intervals. /*
Need to round up to next interval. */ * The Dell LED delay is based on 125ms intervals.
* Need to round up to next interval.
*/
on_eighths = (*delay_on + 124) / 125; on_eighths = DIV_ROUND_UP(*delay_on, 125);
if (0 == on_eighths) on_eighths = clamp_t(unsigned long, on_eighths, 1, 255);
on_eighths = 1;
if (on_eighths > 255)
on_eighths = 255;
*delay_on = on_eighths * 125; *delay_on = on_eighths * 125;
off_eighths = (*delay_off + 124) / 125; off_eighths = DIV_ROUND_UP(*delay_off, 125);
if (0 == off_eighths) off_eighths = clamp_t(unsigned long, off_eighths, 1, 255);
off_eighths = 1;
if (off_eighths > 255)
off_eighths = 255;
*delay_off = off_eighths * 125; *delay_off = off_eighths * 125;
led_blink(on_eighths, off_eighths); led_blink(on_eighths, off_eighths);
...@@ -232,29 +165,21 @@ static int __init dell_led_init(void) ...@@ -232,29 +165,21 @@ static int __init dell_led_init(void)
{ {
int error = 0; int error = 0;
if (!wmi_has_guid(DELL_LED_BIOS_GUID) && !wmi_has_guid(DELL_APP_GUID)) if (!wmi_has_guid(DELL_LED_BIOS_GUID))
return -ENODEV; return -ENODEV;
if (wmi_has_guid(DELL_LED_BIOS_GUID)) { error = led_off();
error = led_off(); if (error != 0)
if (error != 0) return -ENODEV;
return -ENODEV;
error = led_classdev_register(NULL, &dell_led);
}
return error; return led_classdev_register(NULL, &dell_led);
} }
static void __exit dell_led_exit(void) static void __exit dell_led_exit(void)
{ {
int error = 0; led_classdev_unregister(&dell_led);
if (wmi_has_guid(DELL_LED_BIOS_GUID)) { led_off();
error = led_off();
if (error == 0)
led_classdev_unregister(&dell_led);
}
} }
module_init(dell_led_init); module_init(dell_led_init);
......
#ifndef __DELL_LED_H__ #ifndef __DELL_LED_H__
#define __DELL_LED_H__ #define __DELL_LED_H__
enum { int dell_micmute_led_set(int on);
DELL_LED_MICMUTE,
};
int dell_app_wmi_led_set(int whichled, int on);
#endif #endif
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
* to be included from codec driver * to be included from codec driver
*/ */
#if IS_ENABLED(CONFIG_LEDS_DELL_NETBOOKS) #if IS_ENABLED(CONFIG_DELL_LAPTOP)
#include <linux/dell-led.h> #include <linux/dell-led.h>
static int dell_led_value; static int dell_led_value;
static int (*dell_led_set_func)(int, int); static int (*dell_micmute_led_set_func)(int);
static void (*dell_old_cap_hook)(struct hda_codec *, static void (*dell_old_cap_hook)(struct hda_codec *,
struct snd_kcontrol *, struct snd_kcontrol *,
struct snd_ctl_elem_value *); struct snd_ctl_elem_value *);
...@@ -18,7 +18,7 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec, ...@@ -18,7 +18,7 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec,
if (dell_old_cap_hook) if (dell_old_cap_hook)
dell_old_cap_hook(codec, kcontrol, ucontrol); dell_old_cap_hook(codec, kcontrol, ucontrol);
if (!ucontrol || !dell_led_set_func) if (!ucontrol || !dell_micmute_led_set_func)
return; return;
if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) { if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
/* TODO: How do I verify if it's a mono or stereo here? */ /* TODO: How do I verify if it's a mono or stereo here? */
...@@ -26,8 +26,8 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec, ...@@ -26,8 +26,8 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec,
if (val == dell_led_value) if (val == dell_led_value)
return; return;
dell_led_value = val; dell_led_value = val;
if (dell_led_set_func) if (dell_micmute_led_set_func)
dell_led_set_func(DELL_LED_MICMUTE, dell_led_value); dell_micmute_led_set_func(dell_led_value);
} }
} }
...@@ -39,15 +39,15 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec, ...@@ -39,15 +39,15 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
bool removefunc = false; bool removefunc = false;
if (action == HDA_FIXUP_ACT_PROBE) { if (action == HDA_FIXUP_ACT_PROBE) {
if (!dell_led_set_func) if (!dell_micmute_led_set_func)
dell_led_set_func = symbol_request(dell_app_wmi_led_set); dell_micmute_led_set_func = symbol_request(dell_micmute_led_set);
if (!dell_led_set_func) { if (!dell_micmute_led_set_func) {
codec_warn(codec, "Failed to find dell wmi symbol dell_app_wmi_led_set\n"); codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n");
return; return;
} }
removefunc = true; removefunc = true;
if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) { if (dell_micmute_led_set_func(false) >= 0) {
dell_led_value = 0; dell_led_value = 0;
if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch) if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
codec_dbg(codec, "Skipping micmute LED control due to several ADCs"); codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
...@@ -60,17 +60,17 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec, ...@@ -60,17 +60,17 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
} }
if (dell_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) { if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
symbol_put(dell_app_wmi_led_set); symbol_put(dell_micmute_led_set);
dell_led_set_func = NULL; dell_micmute_led_set_func = NULL;
dell_old_cap_hook = NULL; dell_old_cap_hook = NULL;
} }
} }
#else /* CONFIG_LEDS_DELL_NETBOOKS */ #else /* CONFIG_DELL_LAPTOP */
static void alc_fixup_dell_wmi(struct hda_codec *codec, static void alc_fixup_dell_wmi(struct hda_codec *codec,
const struct hda_fixup *fix, int action) const struct hda_fixup *fix, int action)
{ {
} }
#endif /* CONFIG_LEDS_DELL_NETBOOKS */ #endif /* CONFIG_DELL_LAPTOP */
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