forked from Minki/linux
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mjg59/platform-drivers-x86: (81 commits) xo15-ebook: Remove device.wakeup_count ips: use interruptible waits in ips-monitor acer-wmi: does not poll device status when WMI event is available acer-wmi: does not set persistence state by rfkill_init_sw_state platform-drivers: x86: fix common misspellings acer-wmi: use pr_<level> for messages asus-wmi: potential NULL dereference in show_call() asus-wmi: signedness bug in read_brightness() platform-driver-x86: samsung-laptop: make dmi_check_cb to return 1 instead of 0 platform-driver-x86: fix wrong merge for compal-laptop.c msi-laptop: use pr_<level> for messages Platform: add Samsung Laptop platform driver acer-wmi: Fix WMI ID acer-wmi: deactive mail led when power off msi-laptop: send out touchpad on/off key acer-wmi: set the touchpad toggle key code to KEY_TOUCHPAD_TOGGLE platform-driver-x86: intel_mid_thermal: fix unterminated platform_device_id table sony-laptop: potential null dereference sony-laptop: handle allocation failures sony-laptop: return negative on failure in sony_nc_add() ...
This commit is contained in:
commit
baaca1a614
19
Documentation/ABI/testing/sysfs-driver-samsung-laptop
Normal file
19
Documentation/ABI/testing/sysfs-driver-samsung-laptop
Normal file
@ -0,0 +1,19 @@
|
||||
What: /sys/devices/platform/samsung/performance_level
|
||||
Date: January 1, 2010
|
||||
KernelVersion: 2.6.33
|
||||
Contact: Greg Kroah-Hartman <gregkh@suse.de>
|
||||
Description: Some Samsung laptops have different "performance levels"
|
||||
that are can be modified by a function key, and by this
|
||||
sysfs file. These values don't always make a whole lot
|
||||
of sense, but some users like to modify them to keep
|
||||
their fans quiet at all costs. Reading from this file
|
||||
will show the current performance level. Writing to the
|
||||
file can change this value.
|
||||
Valid options:
|
||||
"silent"
|
||||
"normal"
|
||||
"overclock"
|
||||
Note that not all laptops support all of these options.
|
||||
Specifically, not all support the "overclock" option,
|
||||
and it's still unknown if this value even changes
|
||||
anything, other than making the user feel a bit better.
|
31
Documentation/ABI/testing/sysfs-platform-asus-wmi
Normal file
31
Documentation/ABI/testing/sysfs-platform-asus-wmi
Normal file
@ -0,0 +1,31 @@
|
||||
What: /sys/devices/platform/<platform>/cpufv
|
||||
Date: Oct 2010
|
||||
KernelVersion: 2.6.37
|
||||
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||
Description:
|
||||
Change CPU clock configuration (write-only).
|
||||
There are three available clock configuration:
|
||||
* 0 -> Super Performance Mode
|
||||
* 1 -> High Performance Mode
|
||||
* 2 -> Power Saving Mode
|
||||
|
||||
What: /sys/devices/platform/<platform>/camera
|
||||
Date: Jan 2010
|
||||
KernelVersion: 2.6.39
|
||||
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||
Description:
|
||||
Control the camera. 1 means on, 0 means off.
|
||||
|
||||
What: /sys/devices/platform/<platform>/cardr
|
||||
Date: Jan 2010
|
||||
KernelVersion: 2.6.39
|
||||
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||
Description:
|
||||
Control the card reader. 1 means on, 0 means off.
|
||||
|
||||
What: /sys/devices/platform/<platform>/touchpad
|
||||
Date: Jan 2010
|
||||
KernelVersion: 2.6.39
|
||||
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||
Description:
|
||||
Control the card touchpad. 1 means on, 0 means off.
|
@ -1,10 +0,0 @@
|
||||
What: /sys/devices/platform/eeepc-wmi/cpufv
|
||||
Date: Oct 2010
|
||||
KernelVersion: 2.6.37
|
||||
Contact: "Corentin Chary" <corentincj@iksaif.net>
|
||||
Description:
|
||||
Change CPU clock configuration (write-only).
|
||||
There are three available clock configuration:
|
||||
* 0 -> Super Performance Mode
|
||||
* 1 -> High Performance Mode
|
||||
* 2 -> Power Saving Mode
|
@ -14,7 +14,8 @@ Some models report hotkeys through the SNC or SPIC devices, such events are
|
||||
reported both through the ACPI subsystem as acpi events and through the INPUT
|
||||
subsystem. See the logs of acpid or /proc/acpi/event and
|
||||
/proc/bus/input/devices to find out what those events are and which input
|
||||
devices are created by the driver.
|
||||
devices are created by the driver. Additionally, loading the driver with the
|
||||
debug option will report all events in the kernel log.
|
||||
|
||||
Backlight control:
|
||||
------------------
|
||||
@ -64,6 +65,16 @@ powers off the sound card,
|
||||
# echo "1" > /sys/devices/platform/sony-laptop/audiopower
|
||||
powers on the sound card.
|
||||
|
||||
|
||||
RFkill control:
|
||||
---------------
|
||||
More recent Vaio models expose a consistent set of ACPI methods to
|
||||
control radio frequency emitting devices. If you are a lucky owner of
|
||||
such a laptop you will find the necessary rfkill devices under
|
||||
/sys/class/rfkill. Check those starting with sony-* in
|
||||
# grep . /sys/class/rfkill/*/{state,name}
|
||||
|
||||
|
||||
Development:
|
||||
------------
|
||||
|
||||
@ -75,8 +86,21 @@ pass the option 'debug=1'.
|
||||
REPEAT: DON'T DO THIS IF YOU DON'T LIKE RISKY BUSINESS.
|
||||
|
||||
In your kernel logs you will find the list of all ACPI methods
|
||||
the SNC device has on your laptop. You can see the GCDP/GCDP methods
|
||||
used to pwer on/off the CD drive, but there are others.
|
||||
the SNC device has on your laptop.
|
||||
|
||||
* For new models you will see a long list of meaningless method names,
|
||||
reading the DSDT table source should reveal that:
|
||||
(1) the SNC device uses an internal capability lookup table
|
||||
(2) SN00 is used to find values in the lookup table
|
||||
(3) SN06 and SN07 are used to call into the real methods based on
|
||||
offsets you can obtain iterating the table using SN00
|
||||
(4) SN02 used to enable events.
|
||||
Some values in the capability lookup table are more or less known, see
|
||||
the code for all sony_call_snc_handle calls, others are more obscure.
|
||||
|
||||
* For old models you can see the GCDP/GCDP methods used to pwer on/off
|
||||
the CD drive, but there are others and they are usually different from
|
||||
model to model.
|
||||
|
||||
I HAVE NO IDEA WHAT THOSE METHODS DO.
|
||||
|
||||
@ -108,9 +132,8 @@ Bugs/Limitations:
|
||||
laptop, including permanent damage.
|
||||
|
||||
* The sony-laptop and sonypi drivers do not interact at all. In the
|
||||
future, sonypi could use sony-laptop to do (part of) its business.
|
||||
future, sonypi will be removed and replaced by sony-laptop.
|
||||
|
||||
* spicctrl, which is the userspace tool used to communicate with the
|
||||
sonypi driver (through /dev/sonypi) does not try to use the
|
||||
sony-laptop driver. In the future, spicctrl could try sonypi first,
|
||||
and if it isn't present, try sony-laptop instead.
|
||||
sonypi driver (through /dev/sonypi) is deprecated as well since all
|
||||
its features are now available under the sysfs tree via sony-laptop.
|
||||
|
30
MAINTAINERS
30
MAINTAINERS
@ -1157,14 +1157,14 @@ S: Maintained
|
||||
F: Documentation/hwmon/asc7621
|
||||
F: drivers/hwmon/asc7621.c
|
||||
|
||||
ASUS ACPI EXTRAS DRIVER
|
||||
ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS
|
||||
M: Corentin Chary <corentincj@iksaif.net>
|
||||
M: Karol Kozimor <sziwan@users.sourceforge.net>
|
||||
L: acpi4asus-user@lists.sourceforge.net
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
W: http://acpi4asus.sf.net
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/asus_acpi.c
|
||||
F: drivers/platform/x86/asus*.c
|
||||
F: drivers/platform/x86/eeepc*.c
|
||||
|
||||
ASUS ASB100 HARDWARE MONITOR DRIVER
|
||||
M: "Mark M. Hoffman" <mhoffman@lightlink.com>
|
||||
@ -1172,14 +1172,6 @@ L: lm-sensors@lm-sensors.org
|
||||
S: Maintained
|
||||
F: drivers/hwmon/asb100.c
|
||||
|
||||
ASUS LAPTOP EXTRAS DRIVER
|
||||
M: Corentin Chary <corentincj@iksaif.net>
|
||||
L: acpi4asus-user@lists.sourceforge.net
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
W: http://acpi4asus.sf.net
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/asus-laptop.c
|
||||
|
||||
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
|
||||
M: Dan Williams <dan.j.williams@intel.com>
|
||||
W: http://sourceforge.net/projects/xscaleiop
|
||||
@ -2414,22 +2406,6 @@ T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: sound/usb/misc/ua101.c
|
||||
|
||||
EEEPC LAPTOP EXTRAS DRIVER
|
||||
M: Corentin Chary <corentincj@iksaif.net>
|
||||
L: acpi4asus-user@lists.sourceforge.net
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
W: http://acpi4asus.sf.net
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/eeepc-laptop.c
|
||||
|
||||
EEEPC WMI EXTRAS DRIVER
|
||||
M: Corentin Chary <corentincj@iksaif.net>
|
||||
L: acpi4asus-user@lists.sourceforge.net
|
||||
L: platform-driver-x86@vger.kernel.org
|
||||
W: http://acpi4asus.sf.net
|
||||
S: Maintained
|
||||
F: drivers/platform/x86/eeepc-wmi.c
|
||||
|
||||
EFIFB FRAMEBUFFER DRIVER
|
||||
L: linux-fbdev@vger.kernel.org
|
||||
M: Peter Jones <pjones@redhat.com>
|
||||
|
@ -101,6 +101,19 @@ config DELL_WMI
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called dell-wmi.
|
||||
|
||||
config DELL_WMI_AIO
|
||||
tristate "WMI Hotkeys for Dell All-In-One series"
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
select INPUT_SPARSEKMAP
|
||||
---help---
|
||||
Say Y here if you want to support WMI-based hotkeys on Dell
|
||||
All-In-One machines.
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called dell-wmi.
|
||||
|
||||
|
||||
config FUJITSU_LAPTOP
|
||||
tristate "Fujitsu Laptop Extras"
|
||||
depends on ACPI
|
||||
@ -438,23 +451,53 @@ config EEEPC_LAPTOP
|
||||
Bluetooth, backlight and allows powering on/off some other
|
||||
devices.
|
||||
|
||||
If you have an Eee PC laptop, say Y or M here.
|
||||
If you have an Eee PC laptop, say Y or M here. If this driver
|
||||
doesn't work on your Eee PC, try eeepc-wmi instead.
|
||||
|
||||
config EEEPC_WMI
|
||||
tristate "Eee PC WMI Hotkey Driver (EXPERIMENTAL)"
|
||||
config ASUS_WMI
|
||||
tristate "ASUS WMI Driver (EXPERIMENTAL)"
|
||||
depends on ACPI_WMI
|
||||
depends on INPUT
|
||||
depends on HWMON
|
||||
depends on EXPERIMENTAL
|
||||
depends on BACKLIGHT_CLASS_DEVICE
|
||||
depends on RFKILL || RFKILL = n
|
||||
depends on HOTPLUG_PCI
|
||||
select INPUT_SPARSEKMAP
|
||||
select LEDS_CLASS
|
||||
select NEW_LEDS
|
||||
---help---
|
||||
Say Y here if you want to support WMI-based hotkeys on Eee PC laptops.
|
||||
Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new
|
||||
Asus Notebooks).
|
||||
|
||||
To compile this driver as a module, choose M here: the module will
|
||||
be called eeepc-wmi.
|
||||
be called asus-wmi.
|
||||
|
||||
config ASUS_NB_WMI
|
||||
tristate "Asus Notebook WMI Driver (EXPERIMENTAL)"
|
||||
depends on ASUS_WMI
|
||||
---help---
|
||||
This is a driver for newer Asus notebooks. It adds extra features
|
||||
like wireless radio and bluetooth control, leds, hotkeys, backlight...
|
||||
|
||||
For more informations, see
|
||||
<file:Documentation/ABI/testing/sysfs-platform-asus-wmi>
|
||||
|
||||
If you have an ACPI-WMI compatible Asus Notebook, say Y or M
|
||||
here.
|
||||
|
||||
config EEEPC_WMI
|
||||
tristate "Eee PC WMI Driver (EXPERIMENTAL)"
|
||||
depends on ASUS_WMI
|
||||
---help---
|
||||
This is a driver for newer Eee PC laptops. It adds extra features
|
||||
like wireless radio and bluetooth control, leds, hotkeys, backlight...
|
||||
|
||||
For more informations, see
|
||||
<file:Documentation/ABI/testing/sysfs-platform-asus-wmi>
|
||||
|
||||
If you have an ACPI-WMI compatible Eee PC laptop (>= 1000), say Y or M
|
||||
here.
|
||||
|
||||
config ACPI_WMI
|
||||
tristate "WMI"
|
||||
@ -616,6 +659,21 @@ config GPIO_INTEL_PMIC
|
||||
Say Y here to support GPIO via the SCU IPC interface
|
||||
on Intel MID platforms.
|
||||
|
||||
config INTEL_MID_POWER_BUTTON
|
||||
tristate "power button driver for Intel MID platforms"
|
||||
depends on INTEL_SCU_IPC && INPUT
|
||||
help
|
||||
This driver handles the power button on the Intel MID platforms.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config INTEL_MFLD_THERMAL
|
||||
tristate "Thermal driver for Intel Medfield platform"
|
||||
depends on INTEL_SCU_IPC && THERMAL
|
||||
help
|
||||
Say Y here to enable thermal driver support for the Intel Medfield
|
||||
platform.
|
||||
|
||||
config RAR_REGISTER
|
||||
bool "Restricted Access Region Register Driver"
|
||||
depends on PCI && X86_MRST
|
||||
@ -672,4 +730,26 @@ config XO1_RFKILL
|
||||
Support for enabling/disabling the WLAN interface on the OLPC XO-1
|
||||
laptop.
|
||||
|
||||
config XO15_EBOOK
|
||||
tristate "OLPC XO-1.5 ebook switch"
|
||||
depends on ACPI && INPUT
|
||||
---help---
|
||||
Support for the ebook switch on the OLPC XO-1.5 laptop.
|
||||
|
||||
This switch is triggered as the screen is rotated and folded down to
|
||||
convert the device into ebook form.
|
||||
|
||||
config SAMSUNG_LAPTOP
|
||||
tristate "Samsung Laptop driver"
|
||||
depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86
|
||||
---help---
|
||||
This module implements a driver for a wide range of different
|
||||
Samsung laptops. It offers control over the different
|
||||
function keys, wireless LED, LCD backlight level, and
|
||||
sometimes provides a "performance_control" sysfs file to allow
|
||||
the performance level of the laptop to be changed.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called samsung-laptop.
|
||||
|
||||
endif # X86_PLATFORM_DEVICES
|
||||
|
@ -3,6 +3,8 @@
|
||||
# x86 Platform-Specific Drivers
|
||||
#
|
||||
obj-$(CONFIG_ASUS_LAPTOP) += asus-laptop.o
|
||||
obj-$(CONFIG_ASUS_WMI) += asus-wmi.o
|
||||
obj-$(CONFIG_ASUS_NB_WMI) += asus-nb-wmi.o
|
||||
obj-$(CONFIG_EEEPC_LAPTOP) += eeepc-laptop.o
|
||||
obj-$(CONFIG_EEEPC_WMI) += eeepc-wmi.o
|
||||
obj-$(CONFIG_MSI_LAPTOP) += msi-laptop.o
|
||||
@ -10,6 +12,7 @@ obj-$(CONFIG_ACPI_CMPC) += classmate-laptop.o
|
||||
obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o
|
||||
obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o
|
||||
obj-$(CONFIG_DELL_WMI) += dell-wmi.o
|
||||
obj-$(CONFIG_DELL_WMI_AIO) += dell-wmi-aio.o
|
||||
obj-$(CONFIG_ACER_WMI) += acer-wmi.o
|
||||
obj-$(CONFIG_ACERHDF) += acerhdf.o
|
||||
obj-$(CONFIG_HP_ACCEL) += hp_accel.o
|
||||
@ -29,9 +32,13 @@ obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o
|
||||
obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o
|
||||
obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL)+= intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
obj-$(CONFIG_RAR_REGISTER) += intel_rar_register.o
|
||||
obj-$(CONFIG_INTEL_IPS) += intel_ips.o
|
||||
obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o
|
||||
obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
|
||||
obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o
|
||||
obj-$(CONFIG_IBM_RTL) += ibm_rtl.o
|
||||
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
|
||||
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
|
@ -22,6 +22,8 @@
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
@ -46,12 +48,6 @@ MODULE_AUTHOR("Carlos Corbacho");
|
||||
MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define ACER_LOGPREFIX "acer-wmi: "
|
||||
#define ACER_ERR KERN_ERR ACER_LOGPREFIX
|
||||
#define ACER_NOTICE KERN_NOTICE ACER_LOGPREFIX
|
||||
#define ACER_INFO KERN_INFO ACER_LOGPREFIX
|
||||
#define ACER_WARNING KERN_WARNING ACER_LOGPREFIX
|
||||
|
||||
/*
|
||||
* Magic Number
|
||||
* Meaning is unknown - this number is required for writing to ACPI for AMW0
|
||||
@ -84,7 +80,7 @@ MODULE_LICENSE("GPL");
|
||||
#define AMW0_GUID1 "67C3371D-95A3-4C37-BB61-DD47B491DAAB"
|
||||
#define AMW0_GUID2 "431F16ED-0C2B-444C-B267-27DEB140CF9C"
|
||||
#define WMID_GUID1 "6AF4F258-B401-42FD-BE91-3D4AC2D7C0D3"
|
||||
#define WMID_GUID2 "95764E09-FB56-4e83-B31A-37761F60994A"
|
||||
#define WMID_GUID2 "95764E09-FB56-4E83-B31A-37761F60994A"
|
||||
#define WMID_GUID3 "61EF69EA-865C-4BC3-A502-A0DEBA0CB531"
|
||||
|
||||
/*
|
||||
@ -93,7 +89,7 @@ MODULE_LICENSE("GPL");
|
||||
#define ACERWMID_EVENT_GUID "676AA15E-6A47-4D9F-A2CC-1E6D18D14026"
|
||||
|
||||
MODULE_ALIAS("wmi:67C3371D-95A3-4C37-BB61-DD47B491DAAB");
|
||||
MODULE_ALIAS("wmi:6AF4F258-B401-42fd-BE91-3D4AC2D7C0D3");
|
||||
MODULE_ALIAS("wmi:6AF4F258-B401-42Fd-BE91-3D4AC2D7C0D3");
|
||||
MODULE_ALIAS("wmi:676AA15E-6A47-4D9F-A2CC-1E6D18D14026");
|
||||
|
||||
enum acer_wmi_event_ids {
|
||||
@ -108,7 +104,7 @@ static const struct key_entry acer_wmi_keymap[] = {
|
||||
{KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */
|
||||
{KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */
|
||||
{KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} }, /* Display Switch */
|
||||
{KE_KEY, 0x82, {KEY_F22} }, /* Touch Pad On/Off */
|
||||
{KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} }, /* Touch Pad On/Off */
|
||||
{KE_END, 0}
|
||||
};
|
||||
|
||||
@ -221,6 +217,7 @@ struct acer_debug {
|
||||
static struct rfkill *wireless_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *threeg_rfkill;
|
||||
static bool rfkill_inited;
|
||||
|
||||
/* Each low-level interface must define at least some of the following */
|
||||
struct wmi_interface {
|
||||
@ -845,7 +842,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
|
||||
has_type_aa = true;
|
||||
type_aa = (struct hotkey_function_type_aa *) header;
|
||||
|
||||
printk(ACER_INFO "Function bitmap for Communication Button: 0x%x\n",
|
||||
pr_info("Function bitmap for Communication Button: 0x%x\n",
|
||||
type_aa->commun_func_bitmap);
|
||||
|
||||
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
|
||||
@ -991,6 +988,7 @@ static int __devinit acer_led_init(struct device *dev)
|
||||
|
||||
static void acer_led_exit(void)
|
||||
{
|
||||
set_u32(LED_OFF, ACER_CAP_MAILLED);
|
||||
led_classdev_unregister(&mail_led);
|
||||
}
|
||||
|
||||
@ -1036,7 +1034,7 @@ static int __devinit acer_backlight_init(struct device *dev)
|
||||
bd = backlight_device_register("acer-wmi", dev, NULL, &acer_bl_ops,
|
||||
&props);
|
||||
if (IS_ERR(bd)) {
|
||||
printk(ACER_ERR "Could not register Acer backlight device\n");
|
||||
pr_err("Could not register Acer backlight device\n");
|
||||
acer_backlight_device = NULL;
|
||||
return PTR_ERR(bd);
|
||||
}
|
||||
@ -1083,8 +1081,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device)
|
||||
return AE_ERROR;
|
||||
}
|
||||
if (obj->buffer.length != 8) {
|
||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
||||
obj->buffer.length);
|
||||
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
|
||||
kfree(obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
@ -1093,7 +1090,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device)
|
||||
kfree(obj);
|
||||
|
||||
if (return_value.error_code || return_value.ec_return_value)
|
||||
printk(ACER_WARNING "Get Device Status failed: "
|
||||
pr_warning("Get Device Status failed: "
|
||||
"0x%x - 0x%x\n", return_value.error_code,
|
||||
return_value.ec_return_value);
|
||||
else
|
||||
@ -1161,9 +1158,13 @@ static int acer_rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
acpi_status status;
|
||||
u32 cap = (unsigned long)data;
|
||||
status = set_u32(!blocked, cap);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
if (rfkill_inited) {
|
||||
status = set_u32(!blocked, cap);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1187,14 +1188,16 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
status = get_device_status(&state, cap);
|
||||
if (ACPI_SUCCESS(status))
|
||||
rfkill_init_sw_state(rfkill_dev, !state);
|
||||
|
||||
err = rfkill_register(rfkill_dev);
|
||||
if (err) {
|
||||
rfkill_destroy(rfkill_dev);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (ACPI_SUCCESS(status))
|
||||
rfkill_set_sw_state(rfkill_dev, !state);
|
||||
|
||||
return rfkill_dev;
|
||||
}
|
||||
|
||||
@ -1229,14 +1232,19 @@ static int acer_rfkill_init(struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ));
|
||||
rfkill_inited = true;
|
||||
|
||||
if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
|
||||
schedule_delayed_work(&acer_rfkill_work,
|
||||
round_jiffies_relative(HZ));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acer_rfkill_exit(void)
|
||||
{
|
||||
cancel_delayed_work_sync(&acer_rfkill_work);
|
||||
if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID))
|
||||
cancel_delayed_work_sync(&acer_rfkill_work);
|
||||
|
||||
rfkill_unregister(wireless_rfkill);
|
||||
rfkill_destroy(wireless_rfkill);
|
||||
@ -1309,7 +1317,7 @@ static void acer_wmi_notify(u32 value, void *context)
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
printk(ACER_WARNING "bad event status 0x%x\n", status);
|
||||
pr_warning("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1318,14 +1326,12 @@ static void acer_wmi_notify(u32 value, void *context)
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->type != ACPI_TYPE_BUFFER) {
|
||||
printk(ACER_WARNING "Unknown response received %d\n",
|
||||
obj->type);
|
||||
pr_warning("Unknown response received %d\n", obj->type);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
if (obj->buffer.length != 8) {
|
||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
||||
obj->buffer.length);
|
||||
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
|
||||
kfree(obj);
|
||||
return;
|
||||
}
|
||||
@ -1335,13 +1341,26 @@ static void acer_wmi_notify(u32 value, void *context)
|
||||
|
||||
switch (return_value.function) {
|
||||
case WMID_HOTKEY_EVENT:
|
||||
if (return_value.device_state) {
|
||||
u16 device_state = return_value.device_state;
|
||||
pr_debug("deivces states: 0x%x\n", device_state);
|
||||
if (has_cap(ACER_CAP_WIRELESS))
|
||||
rfkill_set_sw_state(wireless_rfkill,
|
||||
!(device_state & ACER_WMID3_GDS_WIRELESS));
|
||||
if (has_cap(ACER_CAP_BLUETOOTH))
|
||||
rfkill_set_sw_state(bluetooth_rfkill,
|
||||
!(device_state & ACER_WMID3_GDS_BLUETOOTH));
|
||||
if (has_cap(ACER_CAP_THREEG))
|
||||
rfkill_set_sw_state(threeg_rfkill,
|
||||
!(device_state & ACER_WMID3_GDS_THREEG));
|
||||
}
|
||||
if (!sparse_keymap_report_event(acer_wmi_input_dev,
|
||||
return_value.key_num, 1, true))
|
||||
printk(ACER_WARNING "Unknown key number - 0x%x\n",
|
||||
pr_warning("Unknown key number - 0x%x\n",
|
||||
return_value.key_num);
|
||||
break;
|
||||
default:
|
||||
printk(ACER_WARNING "Unknown function number - %d - %d\n",
|
||||
pr_warning("Unknown function number - %d - %d\n",
|
||||
return_value.function, return_value.key_num);
|
||||
break;
|
||||
}
|
||||
@ -1370,8 +1389,7 @@ wmid3_set_lm_mode(struct lm_input_params *params,
|
||||
return AE_ERROR;
|
||||
}
|
||||
if (obj->buffer.length != 4) {
|
||||
printk(ACER_WARNING "Unknown buffer length %d\n",
|
||||
obj->buffer.length);
|
||||
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
|
||||
kfree(obj);
|
||||
return AE_ERROR;
|
||||
}
|
||||
@ -1396,11 +1414,11 @@ static int acer_wmi_enable_ec_raw(void)
|
||||
status = wmid3_set_lm_mode(¶ms, &return_value);
|
||||
|
||||
if (return_value.error_code || return_value.ec_return_value)
|
||||
printk(ACER_WARNING "Enabling EC raw mode failed: "
|
||||
pr_warning("Enabling EC raw mode failed: "
|
||||
"0x%x - 0x%x\n", return_value.error_code,
|
||||
return_value.ec_return_value);
|
||||
else
|
||||
printk(ACER_INFO "Enabled EC raw mode");
|
||||
pr_info("Enabled EC raw mode");
|
||||
|
||||
return status;
|
||||
}
|
||||
@ -1419,7 +1437,7 @@ static int acer_wmi_enable_lm(void)
|
||||
status = wmid3_set_lm_mode(¶ms, &return_value);
|
||||
|
||||
if (return_value.error_code || return_value.ec_return_value)
|
||||
printk(ACER_WARNING "Enabling Launch Manager failed: "
|
||||
pr_warning("Enabling Launch Manager failed: "
|
||||
"0x%x - 0x%x\n", return_value.error_code,
|
||||
return_value.ec_return_value);
|
||||
|
||||
@ -1553,6 +1571,7 @@ pm_message_t state)
|
||||
|
||||
if (has_cap(ACER_CAP_MAILLED)) {
|
||||
get_u32(&value, ACER_CAP_MAILLED);
|
||||
set_u32(LED_OFF, ACER_CAP_MAILLED);
|
||||
data->mailled = value;
|
||||
}
|
||||
|
||||
@ -1580,6 +1599,17 @@ static int acer_platform_resume(struct platform_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acer_platform_shutdown(struct platform_device *device)
|
||||
{
|
||||
struct acer_data *data = &interface->data;
|
||||
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
if (has_cap(ACER_CAP_MAILLED))
|
||||
set_u32(LED_OFF, ACER_CAP_MAILLED);
|
||||
}
|
||||
|
||||
static struct platform_driver acer_platform_driver = {
|
||||
.driver = {
|
||||
.name = "acer-wmi",
|
||||
@ -1589,6 +1619,7 @@ static struct platform_driver acer_platform_driver = {
|
||||
.remove = acer_platform_remove,
|
||||
.suspend = acer_platform_suspend,
|
||||
.resume = acer_platform_resume,
|
||||
.shutdown = acer_platform_shutdown,
|
||||
};
|
||||
|
||||
static struct platform_device *acer_platform_device;
|
||||
@ -1636,7 +1667,7 @@ static int create_debugfs(void)
|
||||
{
|
||||
interface->debug.root = debugfs_create_dir("acer-wmi", NULL);
|
||||
if (!interface->debug.root) {
|
||||
printk(ACER_ERR "Failed to create debugfs directory");
|
||||
pr_err("Failed to create debugfs directory");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
@ -1657,11 +1688,10 @@ static int __init acer_wmi_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk(ACER_INFO "Acer Laptop ACPI-WMI Extras\n");
|
||||
pr_info("Acer Laptop ACPI-WMI Extras\n");
|
||||
|
||||
if (dmi_check_system(acer_blacklist)) {
|
||||
printk(ACER_INFO "Blacklisted hardware detected - "
|
||||
"not loading\n");
|
||||
pr_info("Blacklisted hardware detected - not loading\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1678,12 +1708,11 @@ static int __init acer_wmi_init(void)
|
||||
|
||||
if (wmi_has_guid(WMID_GUID2) && interface) {
|
||||
if (ACPI_FAILURE(WMID_set_capabilities())) {
|
||||
printk(ACER_ERR "Unable to detect available WMID "
|
||||
"devices\n");
|
||||
pr_err("Unable to detect available WMID devices\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else if (!wmi_has_guid(WMID_GUID2) && interface) {
|
||||
printk(ACER_ERR "No WMID device detection method found\n");
|
||||
pr_err("No WMID device detection method found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1691,8 +1720,7 @@ static int __init acer_wmi_init(void)
|
||||
interface = &AMW0_interface;
|
||||
|
||||
if (ACPI_FAILURE(AMW0_set_capabilities())) {
|
||||
printk(ACER_ERR "Unable to detect available AMW0 "
|
||||
"devices\n");
|
||||
pr_err("Unable to detect available AMW0 devices\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
}
|
||||
@ -1701,8 +1729,7 @@ static int __init acer_wmi_init(void)
|
||||
AMW0_find_mailled();
|
||||
|
||||
if (!interface) {
|
||||
printk(ACER_INFO "No or unsupported WMI interface, unable to "
|
||||
"load\n");
|
||||
pr_err("No or unsupported WMI interface, unable to load\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1710,22 +1737,22 @@ static int __init acer_wmi_init(void)
|
||||
|
||||
if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) {
|
||||
interface->capability &= ~ACER_CAP_BRIGHTNESS;
|
||||
printk(ACER_INFO "Brightness must be controlled by "
|
||||
pr_info("Brightness must be controlled by "
|
||||
"generic video driver\n");
|
||||
}
|
||||
|
||||
if (wmi_has_guid(WMID_GUID3)) {
|
||||
if (ec_raw_mode) {
|
||||
if (ACPI_FAILURE(acer_wmi_enable_ec_raw())) {
|
||||
printk(ACER_ERR "Cannot enable EC raw mode\n");
|
||||
pr_err("Cannot enable EC raw mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else if (ACPI_FAILURE(acer_wmi_enable_lm())) {
|
||||
printk(ACER_ERR "Cannot enable Launch Manager mode\n");
|
||||
pr_err("Cannot enable Launch Manager mode\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
} else if (ec_raw_mode) {
|
||||
printk(ACER_INFO "No WMID EC raw mode enable method\n");
|
||||
pr_info("No WMID EC raw mode enable method\n");
|
||||
}
|
||||
|
||||
if (wmi_has_guid(ACERWMID_EVENT_GUID)) {
|
||||
@ -1736,7 +1763,7 @@ static int __init acer_wmi_init(void)
|
||||
|
||||
err = platform_driver_register(&acer_platform_driver);
|
||||
if (err) {
|
||||
printk(ACER_ERR "Unable to register platform driver.\n");
|
||||
pr_err("Unable to register platform driver.\n");
|
||||
goto error_platform_register;
|
||||
}
|
||||
|
||||
@ -1791,7 +1818,7 @@ static void __exit acer_wmi_exit(void)
|
||||
platform_device_unregister(acer_platform_device);
|
||||
platform_driver_unregister(&acer_platform_driver);
|
||||
|
||||
printk(ACER_INFO "Acer Laptop WMI Extras unloaded\n");
|
||||
pr_info("Acer Laptop WMI Extras unloaded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
* John Belmonte - ACPI code for Toshiba laptop was a good starting point.
|
||||
* Eric Burghard - LED display support for W1N
|
||||
* Josh Green - Light Sens support
|
||||
* Thomas Tuttle - His first patch for led support was very helpfull
|
||||
* Thomas Tuttle - His first patch for led support was very helpful
|
||||
* Sam Lin - GPS support
|
||||
*/
|
||||
|
||||
@ -50,6 +50,7 @@
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
|
||||
@ -157,46 +158,9 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
|
||||
#define METHOD_BRIGHTNESS_SET "SPLV"
|
||||
#define METHOD_BRIGHTNESS_GET "GPLV"
|
||||
|
||||
/* Backlight */
|
||||
static acpi_handle lcd_switch_handle;
|
||||
static char *lcd_switch_paths[] = {
|
||||
"\\_SB.PCI0.SBRG.EC0._Q10", /* All new models */
|
||||
"\\_SB.PCI0.ISA.EC0._Q10", /* A1x */
|
||||
"\\_SB.PCI0.PX40.ECD0._Q10", /* L3C */
|
||||
"\\_SB.PCI0.PX40.EC0.Q10", /* M1A */
|
||||
"\\_SB.PCI0.LPCB.EC0._Q10", /* P30 */
|
||||
"\\_SB.PCI0.LPCB.EC0._Q0E", /* P30/P35 */
|
||||
"\\_SB.PCI0.PX40.Q10", /* S1x */
|
||||
"\\Q10"}; /* A2x, L2D, L3D, M2E */
|
||||
|
||||
/* Display */
|
||||
#define METHOD_SWITCH_DISPLAY "SDSP"
|
||||
|
||||
static acpi_handle display_get_handle;
|
||||
static char *display_get_paths[] = {
|
||||
/* A6B, A6K A6R A7D F3JM L4R M6R A3G M6A M6V VX-1 V6J V6V W3Z */
|
||||
"\\_SB.PCI0.P0P1.VGA.GETD",
|
||||
/* A3E A4K, A4D A4L A6J A7J A8J Z71V M9V S5A M5A z33A W1Jc W2V G1 */
|
||||
"\\_SB.PCI0.P0P2.VGA.GETD",
|
||||
/* A6V A6Q */
|
||||
"\\_SB.PCI0.P0P3.VGA.GETD",
|
||||
/* A6T, A6M */
|
||||
"\\_SB.PCI0.P0PA.VGA.GETD",
|
||||
/* L3C */
|
||||
"\\_SB.PCI0.PCI1.VGAC.NMAP",
|
||||
/* Z96F */
|
||||
"\\_SB.PCI0.VGA.GETD",
|
||||
/* A2D */
|
||||
"\\ACTD",
|
||||
/* A4G Z71A W1N W5A W5F M2N M3N M5N M6N S1N S5N */
|
||||
"\\ADVG",
|
||||
/* P30 */
|
||||
"\\DNXT",
|
||||
/* A2H D1 L2D L3D L3H L2E L5D L5C M1A M2E L4L W3V */
|
||||
"\\INFB",
|
||||
/* A3F A6F A3N A3L M6N W3N W6A */
|
||||
"\\SSTE"};
|
||||
|
||||
#define METHOD_ALS_CONTROL "ALSC" /* Z71A Z71V */
|
||||
#define METHOD_ALS_LEVEL "ALSL" /* Z71A Z71V */
|
||||
|
||||
@ -246,7 +210,6 @@ struct asus_laptop {
|
||||
|
||||
int wireless_status;
|
||||
bool have_rsts;
|
||||
int lcd_state;
|
||||
|
||||
struct rfkill *gps_rfkill;
|
||||
|
||||
@ -559,48 +522,6 @@ error:
|
||||
/*
|
||||
* Backlight device
|
||||
*/
|
||||
static int asus_lcd_status(struct asus_laptop *asus)
|
||||
{
|
||||
return asus->lcd_state;
|
||||
}
|
||||
|
||||
static int asus_lcd_set(struct asus_laptop *asus, int value)
|
||||
{
|
||||
int lcd = 0;
|
||||
acpi_status status = 0;
|
||||
|
||||
lcd = !!value;
|
||||
|
||||
if (lcd == asus_lcd_status(asus))
|
||||
return 0;
|
||||
|
||||
if (!lcd_switch_handle)
|
||||
return -ENODEV;
|
||||
|
||||
status = acpi_evaluate_object(lcd_switch_handle,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_warning("Error switching LCD\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
asus->lcd_state = lcd;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void lcd_blank(struct asus_laptop *asus, int blank)
|
||||
{
|
||||
struct backlight_device *bd = asus->backlight_device;
|
||||
|
||||
asus->lcd_state = (blank == FB_BLANK_UNBLANK);
|
||||
|
||||
if (bd) {
|
||||
bd->props.power = blank;
|
||||
backlight_update_status(bd);
|
||||
}
|
||||
}
|
||||
|
||||
static int asus_read_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct asus_laptop *asus = bl_get_data(bd);
|
||||
@ -628,16 +549,9 @@ static int asus_set_brightness(struct backlight_device *bd, int value)
|
||||
|
||||
static int update_bl_status(struct backlight_device *bd)
|
||||
{
|
||||
struct asus_laptop *asus = bl_get_data(bd);
|
||||
int rv;
|
||||
int value = bd->props.brightness;
|
||||
|
||||
rv = asus_set_brightness(bd, value);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0;
|
||||
return asus_lcd_set(asus, value);
|
||||
return asus_set_brightness(bd, value);
|
||||
}
|
||||
|
||||
static const struct backlight_ops asusbl_ops = {
|
||||
@ -661,8 +575,7 @@ static int asus_backlight_init(struct asus_laptop *asus)
|
||||
struct backlight_properties props;
|
||||
|
||||
if (acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_GET, NULL) ||
|
||||
acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL) ||
|
||||
!lcd_switch_handle)
|
||||
acpi_check_handle(asus->handle, METHOD_BRIGHTNESS_SET, NULL))
|
||||
return 0;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
@ -971,41 +884,6 @@ static void asus_set_display(struct asus_laptop *asus, int value)
|
||||
return;
|
||||
}
|
||||
|
||||
static int read_display(struct asus_laptop *asus)
|
||||
{
|
||||
unsigned long long value = 0;
|
||||
acpi_status rv = AE_OK;
|
||||
|
||||
/*
|
||||
* In most of the case, we know how to set the display, but sometime
|
||||
* we can't read it
|
||||
*/
|
||||
if (display_get_handle) {
|
||||
rv = acpi_evaluate_integer(display_get_handle, NULL,
|
||||
NULL, &value);
|
||||
if (ACPI_FAILURE(rv))
|
||||
pr_warning("Error reading display status\n");
|
||||
}
|
||||
|
||||
value &= 0x0F; /* needed for some models, shouldn't hurt others */
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/*
|
||||
* Now, *this* one could be more user-friendly, but so far, no-one has
|
||||
* complained. The significance of bits is the same as in store_disp()
|
||||
*/
|
||||
static ssize_t show_disp(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct asus_laptop *asus = dev_get_drvdata(dev);
|
||||
|
||||
if (!display_get_handle)
|
||||
return -ENODEV;
|
||||
return sprintf(buf, "%d\n", read_display(asus));
|
||||
}
|
||||
|
||||
/*
|
||||
* Experimental support for display switching. As of now: 1 should activate
|
||||
* the LCD output, 2 should do for CRT, 4 for TV-Out and 8 for DVI.
|
||||
@ -1247,15 +1125,6 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
|
||||
struct asus_laptop *asus = acpi_driver_data(device);
|
||||
u16 count;
|
||||
|
||||
/*
|
||||
* We need to tell the backlight device when the backlight power is
|
||||
* switched
|
||||
*/
|
||||
if (event == ATKD_LCD_ON)
|
||||
lcd_blank(asus, FB_BLANK_UNBLANK);
|
||||
else if (event == ATKD_LCD_OFF)
|
||||
lcd_blank(asus, FB_BLANK_POWERDOWN);
|
||||
|
||||
/* TODO Find a better way to handle events count. */
|
||||
count = asus->event_count[event % 128]++;
|
||||
acpi_bus_generate_proc_event(asus->device, event, count);
|
||||
@ -1282,7 +1151,7 @@ static DEVICE_ATTR(bluetooth, S_IRUGO | S_IWUSR,
|
||||
show_bluetooth, store_bluetooth);
|
||||
static DEVICE_ATTR(wimax, S_IRUGO | S_IWUSR, show_wimax, store_wimax);
|
||||
static DEVICE_ATTR(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan);
|
||||
static DEVICE_ATTR(display, S_IRUGO | S_IWUSR, show_disp, store_disp);
|
||||
static DEVICE_ATTR(display, S_IWUSR, NULL, store_disp);
|
||||
static DEVICE_ATTR(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
|
||||
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl);
|
||||
static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
|
||||
@ -1393,26 +1262,6 @@ static struct platform_driver platform_driver = {
|
||||
}
|
||||
};
|
||||
|
||||
static int asus_handle_init(char *name, acpi_handle * handle,
|
||||
char **paths, int num_paths)
|
||||
{
|
||||
int i;
|
||||
acpi_status status;
|
||||
|
||||
for (i = 0; i < num_paths; i++) {
|
||||
status = acpi_get_handle(NULL, paths[i], handle);
|
||||
if (ACPI_SUCCESS(status))
|
||||
return 0;
|
||||
}
|
||||
|
||||
*handle = NULL;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
#define ASUS_HANDLE_INIT(object) \
|
||||
asus_handle_init(#object, &object##_handle, object##_paths, \
|
||||
ARRAY_SIZE(object##_paths))
|
||||
|
||||
/*
|
||||
* This function is used to initialize the context with right values. In this
|
||||
* method, we can make all the detection we want, and modify the asus_laptop
|
||||
@ -1498,10 +1347,6 @@ static int asus_laptop_get_info(struct asus_laptop *asus)
|
||||
if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))
|
||||
asus->have_rsts = true;
|
||||
|
||||
/* Scheduled for removal */
|
||||
ASUS_HANDLE_INIT(lcd_switch);
|
||||
ASUS_HANDLE_INIT(display_get);
|
||||
|
||||
kfree(model);
|
||||
|
||||
return AE_OK;
|
||||
@ -1553,10 +1398,23 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
|
||||
asus_als_level(asus, asus->light_level);
|
||||
}
|
||||
|
||||
asus->lcd_state = 1; /* LCD should be on when the module load */
|
||||
return result;
|
||||
}
|
||||
|
||||
static void __devinit asus_dmi_check(void)
|
||||
{
|
||||
const char *model;
|
||||
|
||||
model = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
/* On L1400B WLED control the sound card, don't mess with it ... */
|
||||
if (strncmp(model, "L1400B", 6) == 0) {
|
||||
wlan_status = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static bool asus_device_present;
|
||||
|
||||
static int __devinit asus_acpi_add(struct acpi_device *device)
|
||||
@ -1575,6 +1433,8 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
|
||||
device->driver_data = asus;
|
||||
asus->device = device;
|
||||
|
||||
asus_dmi_check();
|
||||
|
||||
result = asus_acpi_init(asus);
|
||||
if (result)
|
||||
goto fail_platform;
|
||||
|
98
drivers/platform/x86/asus-nb-wmi.c
Normal file
98
drivers/platform/x86/asus-nb-wmi.c
Normal file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Asus Notebooks WMI hotkey driver
|
||||
*
|
||||
* Copyright(C) 2010 Corentin Chary <corentin.chary@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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
|
||||
#include "asus-wmi.h"
|
||||
|
||||
#define ASUS_NB_WMI_FILE "asus-nb-wmi"
|
||||
|
||||
MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>");
|
||||
MODULE_DESCRIPTION("Asus Notebooks WMI Hotkey Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define ASUS_NB_WMI_EVENT_GUID "0B3CBB35-E3C2-45ED-91C2-4C5A6D195D1C"
|
||||
|
||||
MODULE_ALIAS("wmi:"ASUS_NB_WMI_EVENT_GUID);
|
||||
|
||||
static const struct key_entry asus_nb_wmi_keymap[] = {
|
||||
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 0x32, { KEY_MUTE } },
|
||||
{ KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */
|
||||
{ KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */
|
||||
{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },
|
||||
{ KE_KEY, 0x41, { KEY_NEXTSONG } },
|
||||
{ KE_KEY, 0x43, { KEY_STOPCD } },
|
||||
{ KE_KEY, 0x45, { KEY_PLAYPAUSE } },
|
||||
{ KE_KEY, 0x4c, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x50, { KEY_EMAIL } },
|
||||
{ KE_KEY, 0x51, { KEY_WWW } },
|
||||
{ KE_KEY, 0x55, { KEY_CALC } },
|
||||
{ KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */
|
||||
{ KE_KEY, 0x5D, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x5E, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x5F, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } },
|
||||
{ KE_KEY, 0x7E, { KEY_BLUETOOTH } },
|
||||
{ KE_KEY, 0x7D, { KEY_BLUETOOTH } },
|
||||
{ KE_KEY, 0x82, { KEY_CAMERA } },
|
||||
{ KE_KEY, 0x88, { KEY_RFKILL } },
|
||||
{ KE_KEY, 0x8A, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0x95, { KEY_MEDIA } },
|
||||
{ KE_KEY, 0x99, { KEY_PHONE } },
|
||||
{ KE_KEY, 0xb5, { KEY_CALC } },
|
||||
{ KE_KEY, 0xc4, { KEY_KBDILLUMUP } },
|
||||
{ KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } },
|
||||
{ KE_END, 0},
|
||||
};
|
||||
|
||||
static struct asus_wmi_driver asus_nb_wmi_driver = {
|
||||
.name = ASUS_NB_WMI_FILE,
|
||||
.owner = THIS_MODULE,
|
||||
.event_guid = ASUS_NB_WMI_EVENT_GUID,
|
||||
.keymap = asus_nb_wmi_keymap,
|
||||
.input_name = "Asus WMI hotkeys",
|
||||
.input_phys = ASUS_NB_WMI_FILE "/input0",
|
||||
};
|
||||
|
||||
|
||||
static int __init asus_nb_wmi_init(void)
|
||||
{
|
||||
return asus_wmi_register_driver(&asus_nb_wmi_driver);
|
||||
}
|
||||
|
||||
static void __exit asus_nb_wmi_exit(void)
|
||||
{
|
||||
asus_wmi_unregister_driver(&asus_nb_wmi_driver);
|
||||
}
|
||||
|
||||
module_init(asus_nb_wmi_init);
|
||||
module_exit(asus_nb_wmi_exit);
|
1656
drivers/platform/x86/asus-wmi.c
Normal file
1656
drivers/platform/x86/asus-wmi.c
Normal file
File diff suppressed because it is too large
Load Diff
58
drivers/platform/x86/asus-wmi.h
Normal file
58
drivers/platform/x86/asus-wmi.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Asus PC WMI hotkey driver
|
||||
*
|
||||
* Copyright(C) 2010 Intel Corporation.
|
||||
* Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
|
||||
*
|
||||
* Portions based on wistron_btns.c:
|
||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||
* Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
|
||||
* Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _ASUS_WMI_H_
|
||||
#define _ASUS_WMI_H_
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
struct module;
|
||||
struct key_entry;
|
||||
struct asus_wmi;
|
||||
|
||||
struct asus_wmi_driver {
|
||||
bool hotplug_wireless;
|
||||
|
||||
const char *name;
|
||||
struct module *owner;
|
||||
|
||||
const char *event_guid;
|
||||
|
||||
const struct key_entry *keymap;
|
||||
const char *input_name;
|
||||
const char *input_phys;
|
||||
|
||||
int (*probe) (struct platform_device *device);
|
||||
void (*quirks) (struct asus_wmi_driver *driver);
|
||||
|
||||
struct platform_driver platform_driver;
|
||||
struct platform_device *platform_device;
|
||||
};
|
||||
|
||||
int asus_wmi_register_driver(struct asus_wmi_driver *driver);
|
||||
void asus_wmi_unregister_driver(struct asus_wmi_driver *driver);
|
||||
|
||||
#endif /* !_ASUS_WMI_H_ */
|
@ -201,7 +201,7 @@ static bool extra_features;
|
||||
* into 0x4F and read a few bytes from the output, like so:
|
||||
* u8 writeData = 0x33;
|
||||
* ec_transaction(0x4F, &writeData, 1, buffer, 32, 0);
|
||||
* That address is labled "fan1 table information" in the service manual.
|
||||
* That address is labelled "fan1 table information" in the service manual.
|
||||
* It should be clear which value in 'buffer' changes). This seems to be
|
||||
* related to fan speed. It isn't a proper 'realtime' fan speed value
|
||||
* though, because physically stopping or speeding up the fan doesn't
|
||||
@ -275,7 +275,7 @@ static int set_backlight_level(int level)
|
||||
|
||||
ec_write(BACKLIGHT_LEVEL_ADDR, level);
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_backlight_level(void)
|
||||
@ -763,7 +763,7 @@ static int dmi_check_cb(const struct dmi_system_id *id)
|
||||
printk(KERN_INFO DRIVER_NAME": Identified laptop model '%s'\n",
|
||||
id->ident);
|
||||
extra_features = false;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dmi_check_cb_extra(const struct dmi_system_id *id)
|
||||
@ -772,7 +772,7 @@ static int dmi_check_cb_extra(const struct dmi_system_id *id)
|
||||
"enabling extra features\n",
|
||||
id->ident);
|
||||
extra_features = true;
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dmi_system_id __initdata compal_dmi_table[] = {
|
||||
|
171
drivers/platform/x86/dell-wmi-aio.c
Normal file
171
drivers/platform/x86/dell-wmi-aio.c
Normal file
@ -0,0 +1,171 @@
|
||||
/*
|
||||
* WMI hotkeys support for Dell All-In-One series
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
MODULE_DESCRIPTION("WMI hotkeys driver for Dell All-In-One series");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define EVENT_GUID1 "284A0E6B-380E-472A-921F-E52786257FB4"
|
||||
#define EVENT_GUID2 "02314822-307C-4F66-BF0E-48AEAEB26CC8"
|
||||
|
||||
static const char *dell_wmi_aio_guids[] = {
|
||||
EVENT_GUID1,
|
||||
EVENT_GUID2,
|
||||
NULL
|
||||
};
|
||||
|
||||
MODULE_ALIAS("wmi:"EVENT_GUID1);
|
||||
MODULE_ALIAS("wmi:"EVENT_GUID2);
|
||||
|
||||
static const struct key_entry dell_wmi_aio_keymap[] = {
|
||||
{ KE_KEY, 0xc0, { KEY_VOLUMEUP } },
|
||||
{ KE_KEY, 0xc1, { KEY_VOLUMEDOWN } },
|
||||
{ KE_END, 0 }
|
||||
};
|
||||
|
||||
static struct input_dev *dell_wmi_aio_input_dev;
|
||||
|
||||
static void dell_wmi_aio_notify(u32 value, void *context)
|
||||
{
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
pr_info("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
if (obj) {
|
||||
unsigned int scancode;
|
||||
|
||||
switch (obj->type) {
|
||||
case ACPI_TYPE_INTEGER:
|
||||
/* Most All-In-One correctly return integer scancode */
|
||||
scancode = obj->integer.value;
|
||||
sparse_keymap_report_event(dell_wmi_aio_input_dev,
|
||||
scancode, 1, true);
|
||||
break;
|
||||
case ACPI_TYPE_BUFFER:
|
||||
/* Broken machines return the scancode in a buffer */
|
||||
if (obj->buffer.pointer && obj->buffer.length > 0) {
|
||||
scancode = obj->buffer.pointer[0];
|
||||
sparse_keymap_report_event(
|
||||
dell_wmi_aio_input_dev,
|
||||
scancode, 1, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
static int __init dell_wmi_aio_input_setup(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
dell_wmi_aio_input_dev = input_allocate_device();
|
||||
|
||||
if (!dell_wmi_aio_input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dell_wmi_aio_input_dev->name = "Dell AIO WMI hotkeys";
|
||||
dell_wmi_aio_input_dev->phys = "wmi/input0";
|
||||
dell_wmi_aio_input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
err = sparse_keymap_setup(dell_wmi_aio_input_dev,
|
||||
dell_wmi_aio_keymap, NULL);
|
||||
if (err) {
|
||||
pr_err("Unable to setup input device keymap\n");
|
||||
goto err_free_dev;
|
||||
}
|
||||
err = input_register_device(dell_wmi_aio_input_dev);
|
||||
if (err) {
|
||||
pr_info("Unable to register input device\n");
|
||||
goto err_free_keymap;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(dell_wmi_aio_input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(dell_wmi_aio_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static const char *dell_wmi_aio_find(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; dell_wmi_aio_guids[i] != NULL; i++)
|
||||
if (wmi_has_guid(dell_wmi_aio_guids[i]))
|
||||
return dell_wmi_aio_guids[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int __init dell_wmi_aio_init(void)
|
||||
{
|
||||
int err;
|
||||
const char *guid;
|
||||
|
||||
guid = dell_wmi_aio_find();
|
||||
if (!guid) {
|
||||
pr_warning("No known WMI GUID found\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
err = dell_wmi_aio_input_setup();
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = wmi_install_notify_handler(guid, dell_wmi_aio_notify, NULL);
|
||||
if (err) {
|
||||
pr_err("Unable to register notify handler - %d\n", err);
|
||||
sparse_keymap_free(dell_wmi_aio_input_dev);
|
||||
input_unregister_device(dell_wmi_aio_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dell_wmi_aio_exit(void)
|
||||
{
|
||||
const char *guid;
|
||||
|
||||
guid = dell_wmi_aio_find();
|
||||
wmi_remove_notify_handler(guid);
|
||||
sparse_keymap_free(dell_wmi_aio_input_dev);
|
||||
input_unregister_device(dell_wmi_aio_input_dev);
|
||||
}
|
||||
|
||||
module_init(dell_wmi_aio_init);
|
||||
module_exit(dell_wmi_aio_exit);
|
@ -1322,7 +1322,7 @@ static void cmsg_quirk(struct eeepc_laptop *eeepc, int cm, const char *name)
|
||||
{
|
||||
int dummy;
|
||||
|
||||
/* Some BIOSes do not report cm although it is avaliable.
|
||||
/* Some BIOSes do not report cm although it is available.
|
||||
Check if cm_getv[cm] works and, if yes, assume cm should be set. */
|
||||
if (!(eeepc->cm_supported & (1 << cm))
|
||||
&& !read_acpi_int(eeepc->handle, cm_getv[cm], &dummy)) {
|
||||
|
@ -2,7 +2,7 @@
|
||||
* Eee PC WMI hotkey driver
|
||||
*
|
||||
* Copyright(C) 2010 Intel Corporation.
|
||||
* Copyright(C) 2010 Corentin Chary <corentin.chary@gmail.com>
|
||||
* Copyright(C) 2010-2011 Corentin Chary <corentin.chary@gmail.com>
|
||||
*
|
||||
* Portions based on wistron_btns.c:
|
||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||
@ -29,841 +29,57 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
|
||||
#include "asus-wmi.h"
|
||||
|
||||
#define EEEPC_WMI_FILE "eeepc-wmi"
|
||||
|
||||
MODULE_AUTHOR("Yong Wang <yong.y.wang@intel.com>");
|
||||
MODULE_AUTHOR("Corentin Chary <corentincj@iksaif.net>");
|
||||
MODULE_DESCRIPTION("Eee PC WMI Hotkey Driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define EEEPC_ACPI_HID "ASUS010" /* old _HID used in eeepc-laptop */
|
||||
|
||||
#define EEEPC_WMI_EVENT_GUID "ABBC0F72-8EA1-11D1-00A0-C90629100000"
|
||||
#define EEEPC_WMI_MGMT_GUID "97845ED0-4E6D-11DE-8A39-0800200C9A66"
|
||||
|
||||
MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID);
|
||||
MODULE_ALIAS("wmi:"EEEPC_WMI_MGMT_GUID);
|
||||
|
||||
#define NOTIFY_BRNUP_MIN 0x11
|
||||
#define NOTIFY_BRNUP_MAX 0x1f
|
||||
#define NOTIFY_BRNDOWN_MIN 0x20
|
||||
#define NOTIFY_BRNDOWN_MAX 0x2e
|
||||
static bool hotplug_wireless;
|
||||
|
||||
#define EEEPC_WMI_METHODID_DEVS 0x53564544
|
||||
#define EEEPC_WMI_METHODID_DSTS 0x53544344
|
||||
#define EEEPC_WMI_METHODID_CFVS 0x53564643
|
||||
|
||||
#define EEEPC_WMI_DEVID_BACKLIGHT 0x00050012
|
||||
#define EEEPC_WMI_DEVID_TPDLED 0x00100011
|
||||
#define EEEPC_WMI_DEVID_WLAN 0x00010011
|
||||
#define EEEPC_WMI_DEVID_BLUETOOTH 0x00010013
|
||||
#define EEEPC_WMI_DEVID_WWAN3G 0x00010019
|
||||
module_param(hotplug_wireless, bool, 0444);
|
||||
MODULE_PARM_DESC(hotplug_wireless,
|
||||
"Enable hotplug for wireless device. "
|
||||
"If your laptop needs that, please report to "
|
||||
"acpi4asus-user@lists.sourceforge.net.");
|
||||
|
||||
static const struct key_entry eeepc_wmi_keymap[] = {
|
||||
/* Sleep already handled via generic ACPI code */
|
||||
{ KE_KEY, 0x5d, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x32, { KEY_MUTE } },
|
||||
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 0x30, { KEY_VOLUMEUP } },
|
||||
{ KE_IGNORE, NOTIFY_BRNDOWN_MIN, { KEY_BRIGHTNESSDOWN } },
|
||||
{ KE_IGNORE, NOTIFY_BRNUP_MIN, { KEY_BRIGHTNESSUP } },
|
||||
{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },
|
||||
{ KE_KEY, 0x32, { KEY_MUTE } },
|
||||
{ KE_KEY, 0x5c, { KEY_F15 } }, /* Power Gear key */
|
||||
{ KE_KEY, 0x5d, { KEY_WLAN } },
|
||||
{ KE_KEY, 0x6b, { KEY_TOUCHPAD_TOGGLE } }, /* Toggle Touchpad */
|
||||
{ KE_KEY, 0x82, { KEY_CAMERA } },
|
||||
{ KE_KEY, 0x83, { KEY_CAMERA_ZOOMIN } },
|
||||
{ KE_KEY, 0x88, { KEY_WLAN } },
|
||||
{ KE_KEY, 0xcc, { KEY_SWITCHVIDEOMODE } },
|
||||
{ KE_KEY, 0x6b, { KEY_F13 } }, /* Disable Touchpad */
|
||||
{ KE_KEY, 0xe1, { KEY_F14 } },
|
||||
{ KE_KEY, 0xe9, { KEY_DISPLAY_OFF } },
|
||||
{ KE_KEY, 0xe0, { KEY_PROG1 } },
|
||||
{ KE_KEY, 0x5c, { KEY_F15 } },
|
||||
{ KE_KEY, 0xe0, { KEY_PROG1 } }, /* Task Manager */
|
||||
{ KE_KEY, 0xe1, { KEY_F14 } }, /* Change Resolution */
|
||||
{ KE_KEY, 0xe9, { KEY_BRIGHTNESS_ZERO } },
|
||||
{ KE_KEY, 0xeb, { KEY_CAMERA_ZOOMOUT } },
|
||||
{ KE_KEY, 0xec, { KEY_CAMERA_UP } },
|
||||
{ KE_KEY, 0xed, { KEY_CAMERA_DOWN } },
|
||||
{ KE_KEY, 0xee, { KEY_CAMERA_LEFT } },
|
||||
{ KE_KEY, 0xef, { KEY_CAMERA_RIGHT } },
|
||||
{ KE_END, 0},
|
||||
};
|
||||
|
||||
struct bios_args {
|
||||
u32 dev_id;
|
||||
u32 ctrl_param;
|
||||
};
|
||||
|
||||
/*
|
||||
* eeepc-wmi/ - debugfs root directory
|
||||
* dev_id - current dev_id
|
||||
* ctrl_param - current ctrl_param
|
||||
* devs - call DEVS(dev_id, ctrl_param) and print result
|
||||
* dsts - call DSTS(dev_id) and print result
|
||||
*/
|
||||
struct eeepc_wmi_debug {
|
||||
struct dentry *root;
|
||||
u32 dev_id;
|
||||
u32 ctrl_param;
|
||||
};
|
||||
|
||||
struct eeepc_wmi {
|
||||
struct input_dev *inputdev;
|
||||
struct backlight_device *backlight_device;
|
||||
struct platform_device *platform_device;
|
||||
|
||||
struct led_classdev tpd_led;
|
||||
int tpd_led_wk;
|
||||
struct workqueue_struct *led_workqueue;
|
||||
struct work_struct tpd_led_work;
|
||||
|
||||
struct rfkill *wlan_rfkill;
|
||||
struct rfkill *bluetooth_rfkill;
|
||||
struct rfkill *wwan3g_rfkill;
|
||||
|
||||
struct eeepc_wmi_debug debug;
|
||||
};
|
||||
|
||||
/* Only used in eeepc_wmi_init() and eeepc_wmi_exit() */
|
||||
static struct platform_device *platform_device;
|
||||
|
||||
static int eeepc_wmi_input_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
int err;
|
||||
|
||||
eeepc->inputdev = input_allocate_device();
|
||||
if (!eeepc->inputdev)
|
||||
return -ENOMEM;
|
||||
|
||||
eeepc->inputdev->name = "Eee PC WMI hotkeys";
|
||||
eeepc->inputdev->phys = EEEPC_WMI_FILE "/input0";
|
||||
eeepc->inputdev->id.bustype = BUS_HOST;
|
||||
eeepc->inputdev->dev.parent = &eeepc->platform_device->dev;
|
||||
|
||||
err = sparse_keymap_setup(eeepc->inputdev, eeepc_wmi_keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
err = input_register_device(eeepc->inputdev);
|
||||
if (err)
|
||||
goto err_free_keymap;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(eeepc->inputdev);
|
||||
err_free_dev:
|
||||
input_free_device(eeepc->inputdev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_input_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
if (eeepc->inputdev) {
|
||||
sparse_keymap_free(eeepc->inputdev);
|
||||
input_unregister_device(eeepc->inputdev);
|
||||
}
|
||||
|
||||
eeepc->inputdev = NULL;
|
||||
}
|
||||
|
||||
static acpi_status eeepc_wmi_get_devstate(u32 dev_id, u32 *retval)
|
||||
{
|
||||
struct acpi_buffer input = { (acpi_size)sizeof(u32), &dev_id };
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
u32 tmp;
|
||||
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
|
||||
1, EEEPC_WMI_METHODID_DSTS, &input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
tmp = (u32)obj->integer.value;
|
||||
else
|
||||
tmp = 0;
|
||||
|
||||
if (retval)
|
||||
*retval = tmp;
|
||||
|
||||
kfree(obj);
|
||||
|
||||
return status;
|
||||
|
||||
}
|
||||
|
||||
static acpi_status eeepc_wmi_set_devstate(u32 dev_id, u32 ctrl_param,
|
||||
u32 *retval)
|
||||
{
|
||||
struct bios_args args = {
|
||||
.dev_id = dev_id,
|
||||
.ctrl_param = ctrl_param,
|
||||
};
|
||||
struct acpi_buffer input = { (acpi_size)sizeof(args), &args };
|
||||
acpi_status status;
|
||||
|
||||
if (!retval) {
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
|
||||
EEEPC_WMI_METHODID_DEVS,
|
||||
&input, NULL);
|
||||
} else {
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
u32 tmp;
|
||||
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID, 1,
|
||||
EEEPC_WMI_METHODID_DEVS,
|
||||
&input, &output);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return status;
|
||||
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER)
|
||||
tmp = (u32)obj->integer.value;
|
||||
else
|
||||
tmp = 0;
|
||||
|
||||
*retval = tmp;
|
||||
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* LEDs
|
||||
*/
|
||||
/*
|
||||
* These functions actually update the LED's, and are called from a
|
||||
* workqueue. By doing this as separate work rather than when the LED
|
||||
* subsystem asks, we avoid messing with the Eeepc ACPI stuff during a
|
||||
* potentially bad time, such as a timer interrupt.
|
||||
*/
|
||||
static void tpd_led_update(struct work_struct *work)
|
||||
{
|
||||
int ctrl_param;
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = container_of(work, struct eeepc_wmi, tpd_led_work);
|
||||
|
||||
ctrl_param = eeepc->tpd_led_wk;
|
||||
eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_TPDLED, ctrl_param, NULL);
|
||||
}
|
||||
|
||||
static void tpd_led_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
|
||||
|
||||
eeepc->tpd_led_wk = !!value;
|
||||
queue_work(eeepc->led_workqueue, &eeepc->tpd_led_work);
|
||||
}
|
||||
|
||||
static int read_tpd_state(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_TPDLED, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
else if (!retval || retval == 0x00060000)
|
||||
/*
|
||||
* if touchpad led is present, DSTS will set some bits,
|
||||
* usually 0x00020000.
|
||||
* 0x00060000 means that the device is not supported
|
||||
*/
|
||||
return -ENODEV;
|
||||
else
|
||||
/* Status is stored in the first bit */
|
||||
return retval & 0x1;
|
||||
}
|
||||
|
||||
static enum led_brightness tpd_led_get(struct led_classdev *led_cdev)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = container_of(led_cdev, struct eeepc_wmi, tpd_led);
|
||||
|
||||
return read_tpd_state(eeepc);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_led_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (read_tpd_state(eeepc) < 0)
|
||||
return 0;
|
||||
|
||||
eeepc->led_workqueue = create_singlethread_workqueue("led_workqueue");
|
||||
if (!eeepc->led_workqueue)
|
||||
return -ENOMEM;
|
||||
INIT_WORK(&eeepc->tpd_led_work, tpd_led_update);
|
||||
|
||||
eeepc->tpd_led.name = "eeepc::touchpad";
|
||||
eeepc->tpd_led.brightness_set = tpd_led_set;
|
||||
eeepc->tpd_led.brightness_get = tpd_led_get;
|
||||
eeepc->tpd_led.max_brightness = 1;
|
||||
|
||||
rv = led_classdev_register(&eeepc->platform_device->dev,
|
||||
&eeepc->tpd_led);
|
||||
if (rv) {
|
||||
destroy_workqueue(eeepc->led_workqueue);
|
||||
return rv;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_led_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
if (eeepc->tpd_led.dev)
|
||||
led_classdev_unregister(&eeepc->tpd_led);
|
||||
if (eeepc->led_workqueue)
|
||||
destroy_workqueue(eeepc->led_workqueue);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rfkill devices
|
||||
*/
|
||||
static int eeepc_rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
int dev_id = (unsigned long)data;
|
||||
u32 ctrl_param = !blocked;
|
||||
|
||||
return eeepc_wmi_set_devstate(dev_id, ctrl_param, NULL);
|
||||
}
|
||||
|
||||
static void eeepc_rfkill_query(struct rfkill *rfkill, void *data)
|
||||
{
|
||||
int dev_id = (unsigned long)data;
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(dev_id, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return ;
|
||||
|
||||
rfkill_set_sw_state(rfkill, !(retval & 0x1));
|
||||
}
|
||||
|
||||
static const struct rfkill_ops eeepc_rfkill_ops = {
|
||||
.set_block = eeepc_rfkill_set,
|
||||
.query = eeepc_rfkill_query,
|
||||
};
|
||||
|
||||
static int eeepc_new_rfkill(struct eeepc_wmi *eeepc,
|
||||
struct rfkill **rfkill,
|
||||
const char *name,
|
||||
enum rfkill_type type, int dev_id)
|
||||
{
|
||||
int result;
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(dev_id, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
|
||||
/* If the device is present, DSTS will always set some bits
|
||||
* 0x00070000 - 1110000000000000000 - device supported
|
||||
* 0x00060000 - 1100000000000000000 - not supported
|
||||
* 0x00020000 - 0100000000000000000 - device supported
|
||||
* 0x00010000 - 0010000000000000000 - not supported / special mode ?
|
||||
*/
|
||||
if (!retval || retval == 0x00060000)
|
||||
return -ENODEV;
|
||||
|
||||
*rfkill = rfkill_alloc(name, &eeepc->platform_device->dev, type,
|
||||
&eeepc_rfkill_ops, (void *)(long)dev_id);
|
||||
|
||||
if (!*rfkill)
|
||||
return -EINVAL;
|
||||
|
||||
rfkill_init_sw_state(*rfkill, !(retval & 0x1));
|
||||
result = rfkill_register(*rfkill);
|
||||
if (result) {
|
||||
rfkill_destroy(*rfkill);
|
||||
*rfkill = NULL;
|
||||
return result;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_rfkill_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
if (eeepc->wlan_rfkill) {
|
||||
rfkill_unregister(eeepc->wlan_rfkill);
|
||||
rfkill_destroy(eeepc->wlan_rfkill);
|
||||
eeepc->wlan_rfkill = NULL;
|
||||
}
|
||||
if (eeepc->bluetooth_rfkill) {
|
||||
rfkill_unregister(eeepc->bluetooth_rfkill);
|
||||
rfkill_destroy(eeepc->bluetooth_rfkill);
|
||||
eeepc->bluetooth_rfkill = NULL;
|
||||
}
|
||||
if (eeepc->wwan3g_rfkill) {
|
||||
rfkill_unregister(eeepc->wwan3g_rfkill);
|
||||
rfkill_destroy(eeepc->wwan3g_rfkill);
|
||||
eeepc->wwan3g_rfkill = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int eeepc_wmi_rfkill_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
result = eeepc_new_rfkill(eeepc, &eeepc->wlan_rfkill,
|
||||
"eeepc-wlan", RFKILL_TYPE_WLAN,
|
||||
EEEPC_WMI_DEVID_WLAN);
|
||||
|
||||
if (result && result != -ENODEV)
|
||||
goto exit;
|
||||
|
||||
result = eeepc_new_rfkill(eeepc, &eeepc->bluetooth_rfkill,
|
||||
"eeepc-bluetooth", RFKILL_TYPE_BLUETOOTH,
|
||||
EEEPC_WMI_DEVID_BLUETOOTH);
|
||||
|
||||
if (result && result != -ENODEV)
|
||||
goto exit;
|
||||
|
||||
result = eeepc_new_rfkill(eeepc, &eeepc->wwan3g_rfkill,
|
||||
"eeepc-wwan3g", RFKILL_TYPE_WWAN,
|
||||
EEEPC_WMI_DEVID_WWAN3G);
|
||||
|
||||
if (result && result != -ENODEV)
|
||||
goto exit;
|
||||
|
||||
exit:
|
||||
if (result && result != -ENODEV)
|
||||
eeepc_wmi_rfkill_exit(eeepc);
|
||||
|
||||
if (result == -ENODEV)
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* Backlight
|
||||
*/
|
||||
static int read_brightness(struct backlight_device *bd)
|
||||
{
|
||||
u32 retval;
|
||||
acpi_status status;
|
||||
|
||||
status = eeepc_wmi_get_devstate(EEEPC_WMI_DEVID_BACKLIGHT, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
else
|
||||
return retval & 0xFF;
|
||||
}
|
||||
|
||||
static int update_bl_status(struct backlight_device *bd)
|
||||
{
|
||||
|
||||
u32 ctrl_param;
|
||||
acpi_status status;
|
||||
|
||||
ctrl_param = bd->props.brightness;
|
||||
|
||||
status = eeepc_wmi_set_devstate(EEEPC_WMI_DEVID_BACKLIGHT,
|
||||
ctrl_param, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops eeepc_wmi_bl_ops = {
|
||||
.get_brightness = read_brightness,
|
||||
.update_status = update_bl_status,
|
||||
};
|
||||
|
||||
static int eeepc_wmi_backlight_notify(struct eeepc_wmi *eeepc, int code)
|
||||
{
|
||||
struct backlight_device *bd = eeepc->backlight_device;
|
||||
int old = bd->props.brightness;
|
||||
int new = old;
|
||||
|
||||
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
|
||||
new = code - NOTIFY_BRNUP_MIN + 1;
|
||||
else if (code >= NOTIFY_BRNDOWN_MIN && code <= NOTIFY_BRNDOWN_MAX)
|
||||
new = code - NOTIFY_BRNDOWN_MIN;
|
||||
|
||||
bd->props.brightness = new;
|
||||
backlight_update_status(bd);
|
||||
backlight_force_update(bd, BACKLIGHT_UPDATE_HOTKEY);
|
||||
|
||||
return old;
|
||||
}
|
||||
|
||||
static int eeepc_wmi_backlight_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
struct backlight_device *bd;
|
||||
struct backlight_properties props;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 15;
|
||||
bd = backlight_device_register(EEEPC_WMI_FILE,
|
||||
&eeepc->platform_device->dev, eeepc,
|
||||
&eeepc_wmi_bl_ops, &props);
|
||||
if (IS_ERR(bd)) {
|
||||
pr_err("Could not register backlight device\n");
|
||||
return PTR_ERR(bd);
|
||||
}
|
||||
|
||||
eeepc->backlight_device = bd;
|
||||
|
||||
bd->props.brightness = read_brightness(bd);
|
||||
bd->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(bd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_backlight_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
if (eeepc->backlight_device)
|
||||
backlight_device_unregister(eeepc->backlight_device);
|
||||
|
||||
eeepc->backlight_device = NULL;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_notify(u32 value, void *context)
|
||||
{
|
||||
struct eeepc_wmi *eeepc = context;
|
||||
struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object *obj;
|
||||
acpi_status status;
|
||||
int code;
|
||||
int orig_code;
|
||||
|
||||
status = wmi_get_event_data(value, &response);
|
||||
if (status != AE_OK) {
|
||||
pr_err("bad event status 0x%x\n", status);
|
||||
return;
|
||||
}
|
||||
|
||||
obj = (union acpi_object *)response.pointer;
|
||||
|
||||
if (obj && obj->type == ACPI_TYPE_INTEGER) {
|
||||
code = obj->integer.value;
|
||||
orig_code = code;
|
||||
|
||||
if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX)
|
||||
code = NOTIFY_BRNUP_MIN;
|
||||
else if (code >= NOTIFY_BRNDOWN_MIN &&
|
||||
code <= NOTIFY_BRNDOWN_MAX)
|
||||
code = NOTIFY_BRNDOWN_MIN;
|
||||
|
||||
if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) {
|
||||
if (!acpi_video_backlight_support())
|
||||
eeepc_wmi_backlight_notify(eeepc, orig_code);
|
||||
}
|
||||
|
||||
if (!sparse_keymap_report_event(eeepc->inputdev,
|
||||
code, 1, true))
|
||||
pr_info("Unknown key %x pressed\n", code);
|
||||
}
|
||||
|
||||
kfree(obj);
|
||||
}
|
||||
|
||||
static ssize_t store_cpufv(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int value;
|
||||
struct acpi_buffer input = { (acpi_size)sizeof(value), &value };
|
||||
acpi_status status;
|
||||
|
||||
if (!count || sscanf(buf, "%i", &value) != 1)
|
||||
return -EINVAL;
|
||||
if (value < 0 || value > 2)
|
||||
return -EINVAL;
|
||||
|
||||
status = wmi_evaluate_method(EEEPC_WMI_MGMT_GUID,
|
||||
1, EEEPC_WMI_METHODID_CFVS, &input, NULL);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
else
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(cpufv, S_IRUGO | S_IWUSR, NULL, store_cpufv);
|
||||
|
||||
static struct attribute *platform_attributes[] = {
|
||||
&dev_attr_cpufv.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group platform_attribute_group = {
|
||||
.attrs = platform_attributes
|
||||
};
|
||||
|
||||
static void eeepc_wmi_sysfs_exit(struct platform_device *device)
|
||||
{
|
||||
sysfs_remove_group(&device->dev.kobj, &platform_attribute_group);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_sysfs_init(struct platform_device *device)
|
||||
{
|
||||
return sysfs_create_group(&device->dev.kobj, &platform_attribute_group);
|
||||
}
|
||||
|
||||
/*
|
||||
* Platform device
|
||||
*/
|
||||
static int __init eeepc_wmi_platform_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
int err;
|
||||
|
||||
eeepc->platform_device = platform_device_alloc(EEEPC_WMI_FILE, -1);
|
||||
if (!eeepc->platform_device)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(eeepc->platform_device, eeepc);
|
||||
|
||||
err = platform_device_add(eeepc->platform_device);
|
||||
if (err)
|
||||
goto fail_platform_device;
|
||||
|
||||
err = eeepc_wmi_sysfs_init(eeepc->platform_device);
|
||||
if (err)
|
||||
goto fail_sysfs;
|
||||
return 0;
|
||||
|
||||
fail_sysfs:
|
||||
platform_device_del(eeepc->platform_device);
|
||||
fail_platform_device:
|
||||
platform_device_put(eeepc->platform_device);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void eeepc_wmi_platform_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
eeepc_wmi_sysfs_exit(eeepc->platform_device);
|
||||
platform_device_unregister(eeepc->platform_device);
|
||||
}
|
||||
|
||||
/*
|
||||
* debugfs
|
||||
*/
|
||||
struct eeepc_wmi_debugfs_node {
|
||||
struct eeepc_wmi *eeepc;
|
||||
char *name;
|
||||
int (*show)(struct seq_file *m, void *data);
|
||||
};
|
||||
|
||||
static int show_dsts(struct seq_file *m, void *data)
|
||||
{
|
||||
struct eeepc_wmi *eeepc = m->private;
|
||||
acpi_status status;
|
||||
u32 retval = -1;
|
||||
|
||||
status = eeepc_wmi_get_devstate(eeepc->debug.dev_id, &retval);
|
||||
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
seq_printf(m, "DSTS(%x) = %x\n", eeepc->debug.dev_id, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_devs(struct seq_file *m, void *data)
|
||||
{
|
||||
struct eeepc_wmi *eeepc = m->private;
|
||||
acpi_status status;
|
||||
u32 retval = -1;
|
||||
|
||||
status = eeepc_wmi_set_devstate(eeepc->debug.dev_id,
|
||||
eeepc->debug.ctrl_param, &retval);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
seq_printf(m, "DEVS(%x, %x) = %x\n", eeepc->debug.dev_id,
|
||||
eeepc->debug.ctrl_param, retval);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct eeepc_wmi_debugfs_node eeepc_wmi_debug_files[] = {
|
||||
{ NULL, "devs", show_devs },
|
||||
{ NULL, "dsts", show_dsts },
|
||||
};
|
||||
|
||||
static int eeepc_wmi_debugfs_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct eeepc_wmi_debugfs_node *node = inode->i_private;
|
||||
|
||||
return single_open(file, node->show, node->eeepc);
|
||||
}
|
||||
|
||||
static const struct file_operations eeepc_wmi_debugfs_io_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = eeepc_wmi_debugfs_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static void eeepc_wmi_debugfs_exit(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
debugfs_remove_recursive(eeepc->debug.root);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_debugfs_init(struct eeepc_wmi *eeepc)
|
||||
{
|
||||
struct dentry *dent;
|
||||
int i;
|
||||
|
||||
eeepc->debug.root = debugfs_create_dir(EEEPC_WMI_FILE, NULL);
|
||||
if (!eeepc->debug.root) {
|
||||
pr_err("failed to create debugfs directory");
|
||||
goto error_debugfs;
|
||||
}
|
||||
|
||||
dent = debugfs_create_x32("dev_id", S_IRUGO|S_IWUSR,
|
||||
eeepc->debug.root, &eeepc->debug.dev_id);
|
||||
if (!dent)
|
||||
goto error_debugfs;
|
||||
|
||||
dent = debugfs_create_x32("ctrl_param", S_IRUGO|S_IWUSR,
|
||||
eeepc->debug.root, &eeepc->debug.ctrl_param);
|
||||
if (!dent)
|
||||
goto error_debugfs;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(eeepc_wmi_debug_files); i++) {
|
||||
struct eeepc_wmi_debugfs_node *node = &eeepc_wmi_debug_files[i];
|
||||
|
||||
node->eeepc = eeepc;
|
||||
dent = debugfs_create_file(node->name, S_IFREG | S_IRUGO,
|
||||
eeepc->debug.root, node,
|
||||
&eeepc_wmi_debugfs_io_ops);
|
||||
if (!dent) {
|
||||
pr_err("failed to create debug file: %s\n", node->name);
|
||||
goto error_debugfs;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_debugfs:
|
||||
eeepc_wmi_debugfs_exit(eeepc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* WMI Driver
|
||||
*/
|
||||
static struct platform_device * __init eeepc_wmi_add(void)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
acpi_status status;
|
||||
int err;
|
||||
|
||||
eeepc = kzalloc(sizeof(struct eeepc_wmi), GFP_KERNEL);
|
||||
if (!eeepc)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/*
|
||||
* Register the platform device first. It is used as a parent for the
|
||||
* sub-devices below.
|
||||
*/
|
||||
err = eeepc_wmi_platform_init(eeepc);
|
||||
if (err)
|
||||
goto fail_platform;
|
||||
|
||||
err = eeepc_wmi_input_init(eeepc);
|
||||
if (err)
|
||||
goto fail_input;
|
||||
|
||||
err = eeepc_wmi_led_init(eeepc);
|
||||
if (err)
|
||||
goto fail_leds;
|
||||
|
||||
err = eeepc_wmi_rfkill_init(eeepc);
|
||||
if (err)
|
||||
goto fail_rfkill;
|
||||
|
||||
if (!acpi_video_backlight_support()) {
|
||||
err = eeepc_wmi_backlight_init(eeepc);
|
||||
if (err)
|
||||
goto fail_backlight;
|
||||
} else
|
||||
pr_info("Backlight controlled by ACPI video driver\n");
|
||||
|
||||
status = wmi_install_notify_handler(EEEPC_WMI_EVENT_GUID,
|
||||
eeepc_wmi_notify, eeepc);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
pr_err("Unable to register notify handler - %d\n",
|
||||
status);
|
||||
err = -ENODEV;
|
||||
goto fail_wmi_handler;
|
||||
}
|
||||
|
||||
err = eeepc_wmi_debugfs_init(eeepc);
|
||||
if (err)
|
||||
goto fail_debugfs;
|
||||
|
||||
return eeepc->platform_device;
|
||||
|
||||
fail_debugfs:
|
||||
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
|
||||
fail_wmi_handler:
|
||||
eeepc_wmi_backlight_exit(eeepc);
|
||||
fail_backlight:
|
||||
eeepc_wmi_rfkill_exit(eeepc);
|
||||
fail_rfkill:
|
||||
eeepc_wmi_led_exit(eeepc);
|
||||
fail_leds:
|
||||
eeepc_wmi_input_exit(eeepc);
|
||||
fail_input:
|
||||
eeepc_wmi_platform_exit(eeepc);
|
||||
fail_platform:
|
||||
kfree(eeepc);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static int eeepc_wmi_remove(struct platform_device *device)
|
||||
{
|
||||
struct eeepc_wmi *eeepc;
|
||||
|
||||
eeepc = platform_get_drvdata(device);
|
||||
wmi_remove_notify_handler(EEEPC_WMI_EVENT_GUID);
|
||||
eeepc_wmi_backlight_exit(eeepc);
|
||||
eeepc_wmi_input_exit(eeepc);
|
||||
eeepc_wmi_led_exit(eeepc);
|
||||
eeepc_wmi_rfkill_exit(eeepc);
|
||||
eeepc_wmi_debugfs_exit(eeepc);
|
||||
eeepc_wmi_platform_exit(eeepc);
|
||||
|
||||
kfree(eeepc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver platform_driver = {
|
||||
.driver = {
|
||||
.name = EEEPC_WMI_FILE,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
||||
static acpi_status eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
||||
void *context, void **retval)
|
||||
{
|
||||
pr_warning("Found legacy ATKD device (%s)", EEEPC_ACPI_HID);
|
||||
@ -871,7 +87,7 @@ static acpi_status __init eeepc_wmi_parse_device(acpi_handle handle, u32 level,
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
|
||||
static int __init eeepc_wmi_check_atkd(void)
|
||||
static int eeepc_wmi_check_atkd(void)
|
||||
{
|
||||
acpi_status status;
|
||||
bool found = false;
|
||||
@ -884,16 +100,8 @@ static int __init eeepc_wmi_check_atkd(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int __init eeepc_wmi_init(void)
|
||||
static int eeepc_wmi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!wmi_has_guid(EEEPC_WMI_EVENT_GUID) ||
|
||||
!wmi_has_guid(EEEPC_WMI_MGMT_GUID)) {
|
||||
pr_warning("No known WMI GUID found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (eeepc_wmi_check_atkd()) {
|
||||
pr_warning("WMI device present, but legacy ATKD device is also "
|
||||
"present and enabled.");
|
||||
@ -901,33 +109,59 @@ static int __init eeepc_wmi_init(void)
|
||||
"acpi_osi=\"!Windows 2009\"");
|
||||
pr_warning("Can't load eeepc-wmi, use default acpi_osi "
|
||||
"(preferred) or eeepc-laptop");
|
||||
return -ENODEV;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
platform_device = eeepc_wmi_add();
|
||||
if (IS_ERR(platform_device)) {
|
||||
err = PTR_ERR(platform_device);
|
||||
goto fail_eeepc_wmi;
|
||||
}
|
||||
|
||||
err = platform_driver_register(&platform_driver);
|
||||
if (err) {
|
||||
pr_warning("Unable to register platform driver\n");
|
||||
goto fail_platform_driver;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fail_platform_driver:
|
||||
eeepc_wmi_remove(platform_device);
|
||||
fail_eeepc_wmi:
|
||||
return err;
|
||||
static void eeepc_dmi_check(struct asus_wmi_driver *driver)
|
||||
{
|
||||
const char *model;
|
||||
|
||||
model = dmi_get_system_info(DMI_PRODUCT_NAME);
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Whitelist for wlan hotplug
|
||||
*
|
||||
* Asus 1000H needs the current hotplug code to handle
|
||||
* Fn+F2 correctly. We may add other Asus here later, but
|
||||
* it seems that most of the laptops supported by asus-wmi
|
||||
* don't need to be on this list
|
||||
*/
|
||||
if (strcmp(model, "1000H") == 0) {
|
||||
driver->hotplug_wireless = true;
|
||||
pr_info("wlan hotplug enabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void eeepc_wmi_quirks(struct asus_wmi_driver *driver)
|
||||
{
|
||||
driver->hotplug_wireless = hotplug_wireless;
|
||||
eeepc_dmi_check(driver);
|
||||
}
|
||||
|
||||
static struct asus_wmi_driver asus_wmi_driver = {
|
||||
.name = EEEPC_WMI_FILE,
|
||||
.owner = THIS_MODULE,
|
||||
.event_guid = EEEPC_WMI_EVENT_GUID,
|
||||
.keymap = eeepc_wmi_keymap,
|
||||
.input_name = "Eee PC WMI hotkeys",
|
||||
.input_phys = EEEPC_WMI_FILE "/input0",
|
||||
.probe = eeepc_wmi_probe,
|
||||
.quirks = eeepc_wmi_quirks,
|
||||
};
|
||||
|
||||
|
||||
static int __init eeepc_wmi_init(void)
|
||||
{
|
||||
return asus_wmi_register_driver(&asus_wmi_driver);
|
||||
}
|
||||
|
||||
static void __exit eeepc_wmi_exit(void)
|
||||
{
|
||||
eeepc_wmi_remove(platform_device);
|
||||
platform_driver_unregister(&platform_driver);
|
||||
asus_wmi_unregister_driver(&asus_wmi_driver);
|
||||
}
|
||||
|
||||
module_init(eeepc_wmi_init);
|
||||
|
@ -2,6 +2,7 @@
|
||||
* HP WMI hotkeys
|
||||
*
|
||||
* Copyright (C) 2008 Red Hat <mjg@redhat.com>
|
||||
* Copyright (C) 2010, 2011 Anssi Hannula <anssi.hannula@iki.fi>
|
||||
*
|
||||
* Portions based on wistron_btns.c:
|
||||
* Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
|
||||
@ -51,6 +52,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
|
||||
#define HPWMI_HARDWARE_QUERY 0x4
|
||||
#define HPWMI_WIRELESS_QUERY 0x5
|
||||
#define HPWMI_HOTKEY_QUERY 0xc
|
||||
#define HPWMI_WIRELESS2_QUERY 0x1b
|
||||
|
||||
#define PREFIX "HP WMI: "
|
||||
#define UNIMP "Unimplemented "
|
||||
@ -86,7 +88,46 @@ struct bios_args {
|
||||
struct bios_return {
|
||||
u32 sigpass;
|
||||
u32 return_code;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
enum hp_return_value {
|
||||
HPWMI_RET_WRONG_SIGNATURE = 0x02,
|
||||
HPWMI_RET_UNKNOWN_COMMAND = 0x03,
|
||||
HPWMI_RET_UNKNOWN_CMDTYPE = 0x04,
|
||||
HPWMI_RET_INVALID_PARAMETERS = 0x05,
|
||||
};
|
||||
|
||||
enum hp_wireless2_bits {
|
||||
HPWMI_POWER_STATE = 0x01,
|
||||
HPWMI_POWER_SOFT = 0x02,
|
||||
HPWMI_POWER_BIOS = 0x04,
|
||||
HPWMI_POWER_HARD = 0x08,
|
||||
};
|
||||
|
||||
#define IS_HWBLOCKED(x) ((x & (HPWMI_POWER_BIOS | HPWMI_POWER_HARD)) \
|
||||
!= (HPWMI_POWER_BIOS | HPWMI_POWER_HARD))
|
||||
#define IS_SWBLOCKED(x) !(x & HPWMI_POWER_SOFT)
|
||||
|
||||
struct bios_rfkill2_device_state {
|
||||
u8 radio_type;
|
||||
u8 bus_type;
|
||||
u16 vendor_id;
|
||||
u16 product_id;
|
||||
u16 subsys_vendor_id;
|
||||
u16 subsys_product_id;
|
||||
u8 rfkill_id;
|
||||
u8 power;
|
||||
u8 unknown[4];
|
||||
};
|
||||
|
||||
/* 7 devices fit into the 128 byte buffer */
|
||||
#define HPWMI_MAX_RFKILL2_DEVICES 7
|
||||
|
||||
struct bios_rfkill2_state {
|
||||
u8 unknown[7];
|
||||
u8 count;
|
||||
u8 pad[8];
|
||||
struct bios_rfkill2_device_state device[HPWMI_MAX_RFKILL2_DEVICES];
|
||||
};
|
||||
|
||||
static const struct key_entry hp_wmi_keymap[] = {
|
||||
@ -108,6 +149,15 @@ static struct rfkill *wifi_rfkill;
|
||||
static struct rfkill *bluetooth_rfkill;
|
||||
static struct rfkill *wwan_rfkill;
|
||||
|
||||
struct rfkill2_device {
|
||||
u8 id;
|
||||
int num;
|
||||
struct rfkill *rfkill;
|
||||
};
|
||||
|
||||
static int rfkill2_count;
|
||||
static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES];
|
||||
|
||||
static const struct dev_pm_ops hp_wmi_pm_ops = {
|
||||
.resume = hp_wmi_resume_handler,
|
||||
.restore = hp_wmi_resume_handler,
|
||||
@ -129,7 +179,8 @@ static struct platform_driver hp_wmi_driver = {
|
||||
* query: The commandtype -> What should be queried
|
||||
* write: The command -> 0 read, 1 write, 3 ODM specific
|
||||
* buffer: Buffer used as input and/or output
|
||||
* buffersize: Size of buffer
|
||||
* insize: Size of input buffer
|
||||
* outsize: Size of output buffer
|
||||
*
|
||||
* returns zero on success
|
||||
* an HP WMI query specific error code (which is positive)
|
||||
@ -140,25 +191,29 @@ static struct platform_driver hp_wmi_driver = {
|
||||
* size. E.g. Battery info query (0x7) is defined to have 1 byte input
|
||||
* and 128 byte output. The caller would do:
|
||||
* buffer = kzalloc(128, GFP_KERNEL);
|
||||
* ret = hp_wmi_perform_query(0x7, 0, buffer, 128)
|
||||
* ret = hp_wmi_perform_query(0x7, 0, buffer, 1, 128)
|
||||
*/
|
||||
static int hp_wmi_perform_query(int query, int write, u32 *buffer,
|
||||
int buffersize)
|
||||
static int hp_wmi_perform_query(int query, int write, void *buffer,
|
||||
int insize, int outsize)
|
||||
{
|
||||
struct bios_return bios_return;
|
||||
acpi_status status;
|
||||
struct bios_return *bios_return;
|
||||
int actual_outsize;
|
||||
union acpi_object *obj;
|
||||
struct bios_args args = {
|
||||
.signature = 0x55434553,
|
||||
.command = write ? 0x2 : 0x1,
|
||||
.commandtype = query,
|
||||
.datasize = buffersize,
|
||||
.data = *buffer,
|
||||
.datasize = insize,
|
||||
.data = 0,
|
||||
};
|
||||
struct acpi_buffer input = { sizeof(struct bios_args), &args };
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
|
||||
status = wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
|
||||
if (WARN_ON(insize > sizeof(args.data)))
|
||||
return -EINVAL;
|
||||
memcpy(&args.data, buffer, insize);
|
||||
|
||||
wmi_evaluate_method(HPWMI_BIOS_GUID, 0, 0x3, &input, &output);
|
||||
|
||||
obj = output.pointer;
|
||||
|
||||
@ -169,10 +224,26 @@ static int hp_wmi_perform_query(int query, int write, u32 *buffer,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bios_return = *((struct bios_return *)obj->buffer.pointer);
|
||||
bios_return = (struct bios_return *)obj->buffer.pointer;
|
||||
|
||||
memcpy(buffer, &bios_return.value, sizeof(bios_return.value));
|
||||
if (bios_return->return_code) {
|
||||
if (bios_return->return_code != HPWMI_RET_UNKNOWN_CMDTYPE)
|
||||
printk(KERN_WARNING PREFIX "query 0x%x returned "
|
||||
"error 0x%x\n",
|
||||
query, bios_return->return_code);
|
||||
kfree(obj);
|
||||
return bios_return->return_code;
|
||||
}
|
||||
|
||||
if (!outsize) {
|
||||
/* ignore output data */
|
||||
kfree(obj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
actual_outsize = min(outsize, (int)(obj->buffer.length - sizeof(*bios_return)));
|
||||
memcpy(buffer, obj->buffer.pointer + sizeof(*bios_return), actual_outsize);
|
||||
memset(buffer + actual_outsize, 0, outsize - actual_outsize);
|
||||
kfree(obj);
|
||||
return 0;
|
||||
}
|
||||
@ -181,7 +252,7 @@ static int hp_wmi_display_state(void)
|
||||
{
|
||||
int state = 0;
|
||||
int ret = hp_wmi_perform_query(HPWMI_DISPLAY_QUERY, 0, &state,
|
||||
sizeof(state));
|
||||
sizeof(state), sizeof(state));
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
return state;
|
||||
@ -191,7 +262,7 @@ static int hp_wmi_hddtemp_state(void)
|
||||
{
|
||||
int state = 0;
|
||||
int ret = hp_wmi_perform_query(HPWMI_HDDTEMP_QUERY, 0, &state,
|
||||
sizeof(state));
|
||||
sizeof(state), sizeof(state));
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
return state;
|
||||
@ -201,7 +272,7 @@ static int hp_wmi_als_state(void)
|
||||
{
|
||||
int state = 0;
|
||||
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 0, &state,
|
||||
sizeof(state));
|
||||
sizeof(state), sizeof(state));
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
return state;
|
||||
@ -211,7 +282,7 @@ static int hp_wmi_dock_state(void)
|
||||
{
|
||||
int state = 0;
|
||||
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
|
||||
sizeof(state));
|
||||
sizeof(state), sizeof(state));
|
||||
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
@ -223,7 +294,7 @@ static int hp_wmi_tablet_state(void)
|
||||
{
|
||||
int state = 0;
|
||||
int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, &state,
|
||||
sizeof(state));
|
||||
sizeof(state), sizeof(state));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -237,7 +308,7 @@ static int hp_wmi_set_block(void *data, bool blocked)
|
||||
int ret;
|
||||
|
||||
ret = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 1,
|
||||
&query, sizeof(query));
|
||||
&query, sizeof(query), 0);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
@ -252,7 +323,8 @@ static bool hp_wmi_get_sw_state(enum hp_wmi_radio r)
|
||||
int wireless = 0;
|
||||
int mask;
|
||||
hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
|
||||
&wireless, sizeof(wireless));
|
||||
&wireless, sizeof(wireless),
|
||||
sizeof(wireless));
|
||||
/* TBD: Pass error */
|
||||
|
||||
mask = 0x200 << (r * 8);
|
||||
@ -268,7 +340,8 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
|
||||
int wireless = 0;
|
||||
int mask;
|
||||
hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0,
|
||||
&wireless, sizeof(wireless));
|
||||
&wireless, sizeof(wireless),
|
||||
sizeof(wireless));
|
||||
/* TBD: Pass error */
|
||||
|
||||
mask = 0x800 << (r * 8);
|
||||
@ -279,6 +352,51 @@ static bool hp_wmi_get_hw_state(enum hp_wmi_radio r)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int hp_wmi_rfkill2_set_block(void *data, bool blocked)
|
||||
{
|
||||
int rfkill_id = (int)(long)data;
|
||||
char buffer[4] = { 0x01, 0x00, rfkill_id, !blocked };
|
||||
|
||||
if (hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 1,
|
||||
buffer, sizeof(buffer), 0))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rfkill_ops hp_wmi_rfkill2_ops = {
|
||||
.set_block = hp_wmi_rfkill2_set_block,
|
||||
};
|
||||
|
||||
static int hp_wmi_rfkill2_refresh(void)
|
||||
{
|
||||
int err, i;
|
||||
struct bios_rfkill2_state state;
|
||||
|
||||
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
|
||||
0, sizeof(state));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
for (i = 0; i < rfkill2_count; i++) {
|
||||
int num = rfkill2[i].num;
|
||||
struct bios_rfkill2_device_state *devstate;
|
||||
devstate = &state.device[num];
|
||||
|
||||
if (num >= state.count ||
|
||||
devstate->rfkill_id != rfkill2[i].id) {
|
||||
printk(KERN_WARNING PREFIX "power configuration of "
|
||||
"the wireless devices unexpectedly changed\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
rfkill_set_states(rfkill2[i].rfkill,
|
||||
IS_SWBLOCKED(devstate->power),
|
||||
IS_HWBLOCKED(devstate->power));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_display(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
@ -329,7 +447,7 @@ static ssize_t set_als(struct device *dev, struct device_attribute *attr,
|
||||
{
|
||||
u32 tmp = simple_strtoul(buf, NULL, 10);
|
||||
int ret = hp_wmi_perform_query(HPWMI_ALS_QUERY, 1, &tmp,
|
||||
sizeof(tmp));
|
||||
sizeof(tmp), sizeof(tmp));
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
@ -402,6 +520,7 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
case HPWMI_BEZEL_BUTTON:
|
||||
ret = hp_wmi_perform_query(HPWMI_HOTKEY_QUERY, 0,
|
||||
&key_code,
|
||||
sizeof(key_code),
|
||||
sizeof(key_code));
|
||||
if (ret)
|
||||
break;
|
||||
@ -412,6 +531,11 @@ static void hp_wmi_notify(u32 value, void *context)
|
||||
key_code);
|
||||
break;
|
||||
case HPWMI_WIRELESS:
|
||||
if (rfkill2_count) {
|
||||
hp_wmi_rfkill2_refresh();
|
||||
break;
|
||||
}
|
||||
|
||||
if (wifi_rfkill)
|
||||
rfkill_set_states(wifi_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_WIFI),
|
||||
@ -502,32 +626,16 @@ static void cleanup_sysfs(struct platform_device *device)
|
||||
device_remove_file(&device->dev, &dev_attr_tablet);
|
||||
}
|
||||
|
||||
static int __devinit hp_wmi_bios_setup(struct platform_device *device)
|
||||
static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)
|
||||
{
|
||||
int err;
|
||||
int wireless = 0;
|
||||
|
||||
err = hp_wmi_perform_query(HPWMI_WIRELESS_QUERY, 0, &wireless,
|
||||
sizeof(wireless));
|
||||
sizeof(wireless), sizeof(wireless));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = device_create_file(&device->dev, &dev_attr_display);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_hddtemp);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_als);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_dock);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_tablet);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
|
||||
if (wireless & 0x1) {
|
||||
wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev,
|
||||
RFKILL_TYPE_WLAN,
|
||||
@ -573,14 +681,131 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)
|
||||
return 0;
|
||||
register_wwan_err:
|
||||
rfkill_destroy(wwan_rfkill);
|
||||
wwan_rfkill = NULL;
|
||||
if (bluetooth_rfkill)
|
||||
rfkill_unregister(bluetooth_rfkill);
|
||||
register_bluetooth_error:
|
||||
rfkill_destroy(bluetooth_rfkill);
|
||||
bluetooth_rfkill = NULL;
|
||||
if (wifi_rfkill)
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
register_wifi_error:
|
||||
rfkill_destroy(wifi_rfkill);
|
||||
wifi_rfkill = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)
|
||||
{
|
||||
int err, i;
|
||||
struct bios_rfkill2_state state;
|
||||
err = hp_wmi_perform_query(HPWMI_WIRELESS2_QUERY, 0, &state,
|
||||
0, sizeof(state));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (state.count > HPWMI_MAX_RFKILL2_DEVICES) {
|
||||
printk(KERN_WARNING PREFIX "unable to parse 0x1b query output\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < state.count; i++) {
|
||||
struct rfkill *rfkill;
|
||||
enum rfkill_type type;
|
||||
char *name;
|
||||
switch (state.device[i].radio_type) {
|
||||
case HPWMI_WIFI:
|
||||
type = RFKILL_TYPE_WLAN;
|
||||
name = "hp-wifi";
|
||||
break;
|
||||
case HPWMI_BLUETOOTH:
|
||||
type = RFKILL_TYPE_BLUETOOTH;
|
||||
name = "hp-bluetooth";
|
||||
break;
|
||||
case HPWMI_WWAN:
|
||||
type = RFKILL_TYPE_WWAN;
|
||||
name = "hp-wwan";
|
||||
break;
|
||||
default:
|
||||
printk(KERN_WARNING PREFIX "unknown device type 0x%x\n",
|
||||
state.device[i].radio_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!state.device[i].vendor_id) {
|
||||
printk(KERN_WARNING PREFIX "zero device %d while %d "
|
||||
"reported\n", i, state.count);
|
||||
continue;
|
||||
}
|
||||
|
||||
rfkill = rfkill_alloc(name, &device->dev, type,
|
||||
&hp_wmi_rfkill2_ops, (void *)(long)i);
|
||||
if (!rfkill) {
|
||||
err = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rfkill2[rfkill2_count].id = state.device[i].rfkill_id;
|
||||
rfkill2[rfkill2_count].num = i;
|
||||
rfkill2[rfkill2_count].rfkill = rfkill;
|
||||
|
||||
rfkill_init_sw_state(rfkill,
|
||||
IS_SWBLOCKED(state.device[i].power));
|
||||
rfkill_set_hw_state(rfkill,
|
||||
IS_HWBLOCKED(state.device[i].power));
|
||||
|
||||
if (!(state.device[i].power & HPWMI_POWER_BIOS))
|
||||
printk(KERN_INFO PREFIX "device %s blocked by BIOS\n",
|
||||
name);
|
||||
|
||||
err = rfkill_register(rfkill);
|
||||
if (err) {
|
||||
rfkill_destroy(rfkill);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rfkill2_count++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
for (; rfkill2_count > 0; rfkill2_count--) {
|
||||
rfkill_unregister(rfkill2[rfkill2_count - 1].rfkill);
|
||||
rfkill_destroy(rfkill2[rfkill2_count - 1].rfkill);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devinit hp_wmi_bios_setup(struct platform_device *device)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* clear detected rfkill devices */
|
||||
wifi_rfkill = NULL;
|
||||
bluetooth_rfkill = NULL;
|
||||
wwan_rfkill = NULL;
|
||||
rfkill2_count = 0;
|
||||
|
||||
if (hp_wmi_rfkill_setup(device))
|
||||
hp_wmi_rfkill2_setup(device);
|
||||
|
||||
err = device_create_file(&device->dev, &dev_attr_display);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_hddtemp);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_als);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_dock);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
err = device_create_file(&device->dev, &dev_attr_tablet);
|
||||
if (err)
|
||||
goto add_sysfs_error;
|
||||
return 0;
|
||||
|
||||
add_sysfs_error:
|
||||
cleanup_sysfs(device);
|
||||
return err;
|
||||
@ -588,8 +813,14 @@ add_sysfs_error:
|
||||
|
||||
static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
||||
{
|
||||
int i;
|
||||
cleanup_sysfs(device);
|
||||
|
||||
for (i = 0; i < rfkill2_count; i++) {
|
||||
rfkill_unregister(rfkill2[i].rfkill);
|
||||
rfkill_destroy(rfkill2[i].rfkill);
|
||||
}
|
||||
|
||||
if (wifi_rfkill) {
|
||||
rfkill_unregister(wifi_rfkill);
|
||||
rfkill_destroy(wifi_rfkill);
|
||||
@ -622,6 +853,9 @@ static int hp_wmi_resume_handler(struct device *device)
|
||||
input_sync(hp_wmi_input_dev);
|
||||
}
|
||||
|
||||
if (rfkill2_count)
|
||||
hp_wmi_rfkill2_refresh();
|
||||
|
||||
if (wifi_rfkill)
|
||||
rfkill_set_states(wifi_rfkill,
|
||||
hp_wmi_get_sw_state(HPWMI_WIFI),
|
||||
|
@ -459,6 +459,8 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
|
||||
if (test_bit(vpc_bit, &vpc1)) {
|
||||
if (vpc_bit == 9)
|
||||
ideapad_sync_rfk_state(adevice);
|
||||
else if (vpc_bit == 4)
|
||||
read_ec_data(handle, 0x12, &vpc2);
|
||||
else
|
||||
ideapad_input_report(priv, vpc_bit);
|
||||
}
|
||||
|
@ -1111,7 +1111,7 @@ static int ips_monitor(void *data)
|
||||
last_msecs = jiffies_to_msecs(jiffies);
|
||||
expire = jiffies + msecs_to_jiffies(IPS_SAMPLE_PERIOD);
|
||||
|
||||
__set_current_state(TASK_UNINTERRUPTIBLE);
|
||||
__set_current_state(TASK_INTERRUPTIBLE);
|
||||
mod_timer(&timer, expire);
|
||||
schedule();
|
||||
|
||||
|
148
drivers/platform/x86/intel_mid_powerbtn.c
Normal file
148
drivers/platform/x86/intel_mid_powerbtn.c
Normal file
@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Power button driver for Medfield.
|
||||
*
|
||||
* Copyright (C) 2010 Intel Corp
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/input.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define DRIVER_NAME "msic_power_btn"
|
||||
|
||||
#define MSIC_IRQ_STAT 0x02
|
||||
#define MSIC_IRQ_PB (1 << 0)
|
||||
#define MSIC_PB_CONFIG 0x3e
|
||||
#define MSIC_PB_STATUS 0x3f
|
||||
#define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
|
||||
|
||||
struct mfld_pb_priv {
|
||||
struct input_dev *input;
|
||||
unsigned int irq;
|
||||
};
|
||||
|
||||
static irqreturn_t mfld_pb_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct mfld_pb_priv *priv = dev_id;
|
||||
int ret;
|
||||
u8 pbstat;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat);
|
||||
if (ret < 0)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
input_event(priv->input, EV_KEY, KEY_POWER, !(pbstat & MSIC_PB_LEVEL));
|
||||
input_sync(priv->input);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit mfld_pb_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mfld_pb_priv *priv;
|
||||
struct input_dev *input;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return -EINVAL;
|
||||
|
||||
priv = kzalloc(sizeof(struct mfld_pb_priv), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!priv || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
priv->input = input;
|
||||
priv->irq = irq;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "power-button/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||
|
||||
error = request_threaded_irq(priv->irq, NULL, mfld_pb_isr,
|
||||
0, DRIVER_NAME, priv);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to request irq %d for mfld power button\n",
|
||||
irq);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to register input dev, error %d\n", error);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(priv->irq, priv);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(priv);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit mfld_pb_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mfld_pb_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(priv->irq, priv);
|
||||
input_unregister_device(priv->input);
|
||||
kfree(priv);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mfld_pb_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mfld_pb_probe,
|
||||
.remove = __devexit_p(mfld_pb_remove),
|
||||
};
|
||||
|
||||
static int __init mfld_pb_init(void)
|
||||
{
|
||||
return platform_driver_register(&mfld_pb_driver);
|
||||
}
|
||||
module_init(mfld_pb_init);
|
||||
|
||||
static void __exit mfld_pb_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mfld_pb_driver);
|
||||
}
|
||||
module_exit(mfld_pb_exit);
|
||||
|
||||
MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Medfield Power Button Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
576
drivers/platform/x86/intel_mid_thermal.c
Normal file
576
drivers/platform/x86/intel_mid_thermal.c
Normal file
@ -0,0 +1,576 @@
|
||||
/*
|
||||
* intel_mid_thermal.c - Intel MID platform thermal driver
|
||||
*
|
||||
* Copyright (C) 2011 Intel Corporation
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
* Author: Durgadoss R <durgadoss.r@intel.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "intel_mid_thermal: " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
/* Number of thermal sensors */
|
||||
#define MSIC_THERMAL_SENSORS 4
|
||||
|
||||
/* ADC1 - thermal registers */
|
||||
#define MSIC_THERM_ADC1CNTL1 0x1C0
|
||||
#define MSIC_ADC_ENBL 0x10
|
||||
#define MSIC_ADC_START 0x08
|
||||
|
||||
#define MSIC_THERM_ADC1CNTL3 0x1C2
|
||||
#define MSIC_ADCTHERM_ENBL 0x04
|
||||
#define MSIC_ADCRRDATA_ENBL 0x05
|
||||
#define MSIC_CHANL_MASK_VAL 0x0F
|
||||
|
||||
#define MSIC_STOPBIT_MASK 16
|
||||
#define MSIC_ADCTHERM_MASK 4
|
||||
#define ADC_CHANLS_MAX 15 /* Number of ADC channels */
|
||||
#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS)
|
||||
|
||||
/* ADC channel code values */
|
||||
#define SKIN_SENSOR0_CODE 0x08
|
||||
#define SKIN_SENSOR1_CODE 0x09
|
||||
#define SYS_SENSOR_CODE 0x0A
|
||||
#define MSIC_DIE_SENSOR_CODE 0x03
|
||||
|
||||
#define SKIN_THERM_SENSOR0 0
|
||||
#define SKIN_THERM_SENSOR1 1
|
||||
#define SYS_THERM_SENSOR2 2
|
||||
#define MSIC_DIE_THERM_SENSOR3 3
|
||||
|
||||
/* ADC code range */
|
||||
#define ADC_MAX 977
|
||||
#define ADC_MIN 162
|
||||
#define ADC_VAL0C 887
|
||||
#define ADC_VAL20C 720
|
||||
#define ADC_VAL40C 508
|
||||
#define ADC_VAL60C 315
|
||||
|
||||
/* ADC base addresses */
|
||||
#define ADC_CHNL_START_ADDR 0x1C5 /* increments by 1 */
|
||||
#define ADC_DATA_START_ADDR 0x1D4 /* increments by 2 */
|
||||
|
||||
/* MSIC die attributes */
|
||||
#define MSIC_DIE_ADC_MIN 488
|
||||
#define MSIC_DIE_ADC_MAX 1004
|
||||
|
||||
/* This holds the address of the first free ADC channel,
|
||||
* among the 15 channels
|
||||
*/
|
||||
static int channel_index;
|
||||
|
||||
struct platform_info {
|
||||
struct platform_device *pdev;
|
||||
struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS];
|
||||
};
|
||||
|
||||
struct thermal_device_info {
|
||||
unsigned int chnl_addr;
|
||||
int direct;
|
||||
/* This holds the current temperature in millidegree celsius */
|
||||
long curr_temp;
|
||||
};
|
||||
|
||||
/**
|
||||
* to_msic_die_temp - converts adc_val to msic_die temperature
|
||||
* @adc_val: ADC value to be converted
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int to_msic_die_temp(uint16_t adc_val)
|
||||
{
|
||||
return (368 * (adc_val) / 1000) - 220;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_valid_adc - checks whether the adc code is within the defined range
|
||||
* @min: minimum value for the sensor
|
||||
* @max: maximum value for the sensor
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max)
|
||||
{
|
||||
return (adc_val >= min) && (adc_val <= max);
|
||||
}
|
||||
|
||||
/**
|
||||
* adc_to_temp - converts the ADC code to temperature in C
|
||||
* @direct: true if ths channel is direct index
|
||||
* @adc_val: the adc_val that needs to be converted
|
||||
* @tp: temperature return value
|
||||
*
|
||||
* Linear approximation is used to covert the skin adc value into temperature.
|
||||
* This technique is used to avoid very long look-up table to get
|
||||
* the appropriate temp value from ADC value.
|
||||
* The adc code vs sensor temp curve is split into five parts
|
||||
* to achieve very close approximate temp value with less than
|
||||
* 0.5C error
|
||||
*/
|
||||
static int adc_to_temp(int direct, uint16_t adc_val, unsigned long *tp)
|
||||
{
|
||||
int temp;
|
||||
|
||||
/* Direct conversion for die temperature */
|
||||
if (direct) {
|
||||
if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) {
|
||||
*tp = to_msic_die_temp(adc_val) * 1000;
|
||||
return 0;
|
||||
}
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX))
|
||||
return -ERANGE;
|
||||
|
||||
/* Linear approximation for skin temperature */
|
||||
if (adc_val > ADC_VAL0C)
|
||||
temp = 177 - (adc_val/5);
|
||||
else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C))
|
||||
temp = 111 - (adc_val/8);
|
||||
else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C))
|
||||
temp = 92 - (adc_val/10);
|
||||
else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C))
|
||||
temp = 91 - (adc_val/10);
|
||||
else
|
||||
temp = 112 - (adc_val/6);
|
||||
|
||||
/* Convert temperature in celsius to milli degree celsius */
|
||||
*tp = temp * 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_read_temp - read sensors for temperature
|
||||
* @temp: holds the current temperature for the sensor after reading
|
||||
*
|
||||
* reads the adc_code from the channel and converts it to real
|
||||
* temperature. The converted value is stored in temp.
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp)
|
||||
{
|
||||
struct thermal_device_info *td_info = tzd->devdata;
|
||||
uint16_t adc_val, addr;
|
||||
uint8_t data = 0;
|
||||
int ret;
|
||||
unsigned long curr_temp;
|
||||
|
||||
|
||||
addr = td_info->chnl_addr;
|
||||
|
||||
/* Enable the msic for conversion before reading */
|
||||
ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Re-toggle the RRDATARD bit (temporary workaround) */
|
||||
ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read the higher bits of data */
|
||||
ret = intel_scu_ipc_ioread8(addr, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Shift bits to accomodate the lower two data bits */
|
||||
adc_val = (data << 2);
|
||||
addr++;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Adding lower two bits to the higher bits */
|
||||
data &= 03;
|
||||
adc_val += data;
|
||||
|
||||
/* Convert ADC value to temperature */
|
||||
ret = adc_to_temp(td_info->direct, adc_val, &curr_temp);
|
||||
if (ret == 0)
|
||||
*temp = td_info->curr_temp = curr_temp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* configure_adc - enables/disables the ADC for conversion
|
||||
* @val: zero: disables the ADC non-zero:enables the ADC
|
||||
*
|
||||
* Enable/Disable the ADC depending on the argument
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int configure_adc(int val)
|
||||
{
|
||||
int ret;
|
||||
uint8_t data;
|
||||
|
||||
ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val) {
|
||||
/* Enable and start the ADC */
|
||||
data |= (MSIC_ADC_ENBL | MSIC_ADC_START);
|
||||
} else {
|
||||
/* Just stop the ADC */
|
||||
data &= (~MSIC_ADC_START);
|
||||
}
|
||||
|
||||
return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* set_up_therm_channel - enable thermal channel for conversion
|
||||
* @base_addr: index of free msic ADC channel
|
||||
*
|
||||
* Enable all the three channels for conversion
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int set_up_therm_channel(u16 base_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Enable all the sensor channels */
|
||||
ret = intel_scu_ipc_iowrite8(base_addr, SKIN_SENSOR0_CODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Since this is the last channel, set the stop bit
|
||||
to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
|
||||
ret = intel_scu_ipc_iowrite8(base_addr + 3,
|
||||
(MSIC_DIE_SENSOR_CODE | 0x10));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable ADC and start it */
|
||||
return configure_adc(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset_stopbit - sets the stop bit to 0 on the given channel
|
||||
* @addr: address of the channel
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int reset_stopbit(uint16_t addr)
|
||||
{
|
||||
int ret;
|
||||
uint8_t data;
|
||||
ret = intel_scu_ipc_ioread8(addr, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Set the stop bit to zero */
|
||||
return intel_scu_ipc_iowrite8(addr, (data & 0xEF));
|
||||
}
|
||||
|
||||
/**
|
||||
* find_free_channel - finds an empty channel for conversion
|
||||
*
|
||||
* If the ADC is not enabled then start using 0th channel
|
||||
* itself. Otherwise find an empty channel by looking for a
|
||||
* channel in which the stopbit is set to 1. returns the index
|
||||
* of the first free channel if succeeds or an error code.
|
||||
*
|
||||
* Context: can sleep
|
||||
*
|
||||
* FIXME: Ultimately the channel allocator will move into the intel_scu_ipc
|
||||
* code.
|
||||
*/
|
||||
static int find_free_channel(void)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
uint8_t data;
|
||||
|
||||
/* check whether ADC is enabled */
|
||||
ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((data & MSIC_ADC_ENBL) == 0)
|
||||
return 0;
|
||||
|
||||
/* ADC is already enabled; Looking for an empty channel */
|
||||
for (i = 0; i < ADC_CHANLS_MAX; i++) {
|
||||
ret = intel_scu_ipc_ioread8(ADC_CHNL_START_ADDR + i, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data & MSIC_STOPBIT_MASK) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_initialize_adc - initializing the ADC
|
||||
* @dev: our device structure
|
||||
*
|
||||
* Initialize the ADC for reading thermistor values. Can sleep.
|
||||
*/
|
||||
static int mid_initialize_adc(struct device *dev)
|
||||
{
|
||||
u8 data;
|
||||
u16 base_addr;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ensure that adctherm is disabled before we
|
||||
* initialize the ADC
|
||||
*/
|
||||
ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data & MSIC_ADCTHERM_MASK)
|
||||
dev_warn(dev, "ADCTHERM already set");
|
||||
|
||||
/* Index of the first channel in which the stop bit is set */
|
||||
channel_index = find_free_channel();
|
||||
if (channel_index < 0) {
|
||||
dev_err(dev, "No free ADC channels");
|
||||
return channel_index;
|
||||
}
|
||||
|
||||
base_addr = ADC_CHNL_START_ADDR + channel_index;
|
||||
|
||||
if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) {
|
||||
/* Reset stop bit for channels other than 0 and 12 */
|
||||
ret = reset_stopbit(base_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Index of the first free channel */
|
||||
base_addr++;
|
||||
channel_index++;
|
||||
}
|
||||
|
||||
ret = set_up_therm_channel(base_addr);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to enable ADC");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "ADC initialization successful");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize_sensor - sets default temp and timer ranges
|
||||
* @index: index of the sensor
|
||||
*
|
||||
* Context: can sleep
|
||||
*/
|
||||
static struct thermal_device_info *initialize_sensor(int index)
|
||||
{
|
||||
struct thermal_device_info *td_info =
|
||||
kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL);
|
||||
|
||||
if (!td_info)
|
||||
return NULL;
|
||||
|
||||
/* Set the base addr of the channel for this sensor */
|
||||
td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index);
|
||||
/* Sensor 3 is direct conversion */
|
||||
if (index == 3)
|
||||
td_info->direct = 1;
|
||||
return td_info;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_thermal_resume - resume routine
|
||||
* @pdev: platform device structure
|
||||
*
|
||||
* mid thermal resume: re-initializes the adc. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_resume(struct platform_device *pdev)
|
||||
{
|
||||
return mid_initialize_adc(&pdev->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_thermal_suspend - suspend routine
|
||||
* @pdev: platform device structure
|
||||
*
|
||||
* mid thermal suspend implements the suspend functionality
|
||||
* by stopping the ADC. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_suspend(struct platform_device *pdev, pm_message_t mesg)
|
||||
{
|
||||
/*
|
||||
* This just stops the ADC and does not disable it.
|
||||
* temporary workaround until we have a generic ADC driver.
|
||||
* If 0 is passed, it disables the ADC.
|
||||
*/
|
||||
return configure_adc(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* read_curr_temp - reads the current temperature and stores in temp
|
||||
* @temp: holds the current temperature value after reading
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int read_curr_temp(struct thermal_zone_device *tzd, unsigned long *temp)
|
||||
{
|
||||
WARN_ON(tzd == NULL);
|
||||
return mid_read_temp(tzd, temp);
|
||||
}
|
||||
|
||||
/* Can't be const */
|
||||
static struct thermal_zone_device_ops tzd_ops = {
|
||||
.get_temp = read_curr_temp,
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* mid_thermal_probe - mfld thermal initialize
|
||||
* @pdev: platform device structure
|
||||
*
|
||||
* mid thermal probe initializes the hardware and registers
|
||||
* all the sensors with the generic thermal framework. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
static char *name[MSIC_THERMAL_SENSORS] = {
|
||||
"skin0", "skin1", "sys", "msicdie"
|
||||
};
|
||||
|
||||
int ret;
|
||||
int i;
|
||||
struct platform_info *pinfo;
|
||||
|
||||
pinfo = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initializing the hardware */
|
||||
ret = mid_initialize_adc(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ADC init failed");
|
||||
kfree(pinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register each sensor with the generic thermal framework*/
|
||||
for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
|
||||
pinfo->tzd[i] = thermal_zone_device_register(name[i],
|
||||
0, initialize_sensor(i),
|
||||
&tzd_ops, 0, 0, 0, 0);
|
||||
if (IS_ERR(pinfo->tzd[i]))
|
||||
goto reg_fail;
|
||||
}
|
||||
|
||||
pinfo->pdev = pdev;
|
||||
platform_set_drvdata(pdev, pinfo);
|
||||
return 0;
|
||||
|
||||
reg_fail:
|
||||
ret = PTR_ERR(pinfo->tzd[i]);
|
||||
while (--i >= 0)
|
||||
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||
configure_adc(0);
|
||||
kfree(pinfo);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_thermal_remove - mfld thermal finalize
|
||||
* @dev: platform device structure
|
||||
*
|
||||
* MLFD thermal remove unregisters all the sensors from the generic
|
||||
* thermal framework. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct platform_info *pinfo = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < MSIC_THERMAL_SENSORS; i++)
|
||||
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
/* Stop the ADC */
|
||||
return configure_adc(0);
|
||||
}
|
||||
|
||||
/*********************************************************************
|
||||
* Driver initialisation and finalization
|
||||
*********************************************************************/
|
||||
|
||||
#define DRIVER_NAME "msic_sensor"
|
||||
|
||||
static const struct platform_device_id therm_id_table[] = {
|
||||
{ DRIVER_NAME, 1 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver mid_thermal_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = mid_thermal_probe,
|
||||
.suspend = mid_thermal_suspend,
|
||||
.resume = mid_thermal_resume,
|
||||
.remove = __devexit_p(mid_thermal_remove),
|
||||
.id_table = therm_id_table,
|
||||
};
|
||||
|
||||
static int __init mid_thermal_module_init(void)
|
||||
{
|
||||
return platform_driver_register(&mid_thermal_driver);
|
||||
}
|
||||
|
||||
static void __exit mid_thermal_module_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mid_thermal_driver);
|
||||
}
|
||||
|
||||
module_init(mid_thermal_module_init);
|
||||
module_exit(mid_thermal_module_exit);
|
||||
|
||||
MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -485,7 +485,7 @@ EXPORT_SYMBOL(rar_lock);
|
||||
*
|
||||
* The register_rar function is to used by other device drivers
|
||||
* to ensure that this driver is ready. As we cannot be sure of
|
||||
* the compile/execute order of drivers in ther kernel, it is
|
||||
* the compile/execute order of drivers in the kernel, it is
|
||||
* best to give this driver a callback function to call when
|
||||
* it is ready to give out addresses. The callback function
|
||||
* would have those steps that continue the initialization of
|
||||
|
@ -9,7 +9,7 @@
|
||||
* as published by the Free Software Foundation; version 2
|
||||
* of the License.
|
||||
*
|
||||
* SCU runing in ARC processor communicates with other entity running in IA
|
||||
* SCU running in ARC processor communicates with other entity running in IA
|
||||
* core through IPC mechanism which in turn messaging between IA core ad SCU.
|
||||
* SCU has two IPC mechanism IPC-1 and IPC-2. IPC-1 is used between IA32 and
|
||||
* SCU where IPC-2 is used between P-Unit and SCU. This driver delas with
|
||||
|
@ -51,6 +51,8 @@
|
||||
* laptop as MSI S270. YMMV.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
@ -60,6 +62,8 @@
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/sparse-keymap.h>
|
||||
|
||||
#define MSI_DRIVER_VERSION "0.5"
|
||||
|
||||
@ -78,6 +82,9 @@
|
||||
#define MSI_STANDARD_EC_SCM_LOAD_ADDRESS 0x2d
|
||||
#define MSI_STANDARD_EC_SCM_LOAD_MASK (1 << 0)
|
||||
|
||||
#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
|
||||
#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
|
||||
|
||||
static int msi_laptop_resume(struct platform_device *device);
|
||||
|
||||
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
|
||||
@ -90,6 +97,14 @@ static int auto_brightness;
|
||||
module_param(auto_brightness, int, 0);
|
||||
MODULE_PARM_DESC(auto_brightness, "Enable automatic brightness control (0: disabled; 1: enabled; 2: don't touch)");
|
||||
|
||||
static const struct key_entry msi_laptop_keymap[] = {
|
||||
{KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, /* Touch Pad On */
|
||||
{KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },/* Touch Pad On */
|
||||
{KE_END, 0}
|
||||
};
|
||||
|
||||
static struct input_dev *msi_laptop_input_dev;
|
||||
|
||||
static bool old_ec_model;
|
||||
static int wlan_s, bluetooth_s, threeg_s;
|
||||
static int threeg_exists;
|
||||
@ -432,8 +447,7 @@ static struct platform_device *msipf_device;
|
||||
|
||||
static int dmi_check_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
printk(KERN_INFO "msi-laptop: Identified laptop model '%s'.\n",
|
||||
id->ident);
|
||||
pr_info("Identified laptop model '%s'.\n", id->ident);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -605,6 +619,21 @@ static void msi_update_rfkill(struct work_struct *ignored)
|
||||
}
|
||||
static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill);
|
||||
|
||||
static void msi_send_touchpad_key(struct work_struct *ignored)
|
||||
{
|
||||
u8 rdata;
|
||||
int result;
|
||||
|
||||
result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata);
|
||||
if (result < 0)
|
||||
return;
|
||||
|
||||
sparse_keymap_report_event(msi_laptop_input_dev,
|
||||
(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?
|
||||
KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);
|
||||
}
|
||||
static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key);
|
||||
|
||||
static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
struct serio *port)
|
||||
{
|
||||
@ -613,12 +642,17 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
if (str & 0x20)
|
||||
return false;
|
||||
|
||||
/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan*/
|
||||
/* 0x54 wwan, 0x62 bluetooth, 0x76 wlan, 0xE4 touchpad toggle*/
|
||||
if (unlikely(data == 0xe0)) {
|
||||
extended = true;
|
||||
return false;
|
||||
} else if (unlikely(extended)) {
|
||||
extended = false;
|
||||
switch (data) {
|
||||
case 0xE4:
|
||||
schedule_delayed_work(&msi_touchpad_work,
|
||||
round_jiffies_relative(0.5 * HZ));
|
||||
break;
|
||||
case 0x54:
|
||||
case 0x62:
|
||||
case 0x76:
|
||||
@ -626,7 +660,6 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,
|
||||
round_jiffies_relative(0.5 * HZ));
|
||||
break;
|
||||
}
|
||||
extended = false;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -731,6 +764,42 @@ static int msi_laptop_resume(struct platform_device *device)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init msi_laptop_input_setup(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
msi_laptop_input_dev = input_allocate_device();
|
||||
if (!msi_laptop_input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
msi_laptop_input_dev->name = "MSI Laptop hotkeys";
|
||||
msi_laptop_input_dev->phys = "msi-laptop/input0";
|
||||
msi_laptop_input_dev->id.bustype = BUS_HOST;
|
||||
|
||||
err = sparse_keymap_setup(msi_laptop_input_dev,
|
||||
msi_laptop_keymap, NULL);
|
||||
if (err)
|
||||
goto err_free_dev;
|
||||
|
||||
err = input_register_device(msi_laptop_input_dev);
|
||||
if (err)
|
||||
goto err_free_keymap;
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_keymap:
|
||||
sparse_keymap_free(msi_laptop_input_dev);
|
||||
err_free_dev:
|
||||
input_free_device(msi_laptop_input_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void msi_laptop_input_destroy(void)
|
||||
{
|
||||
sparse_keymap_free(msi_laptop_input_dev);
|
||||
input_unregister_device(msi_laptop_input_dev);
|
||||
}
|
||||
|
||||
static int load_scm_model_init(struct platform_device *sdev)
|
||||
{
|
||||
u8 data;
|
||||
@ -759,16 +828,23 @@ static int load_scm_model_init(struct platform_device *sdev)
|
||||
if (result < 0)
|
||||
goto fail_rfkill;
|
||||
|
||||
/* setup input device */
|
||||
result = msi_laptop_input_setup();
|
||||
if (result)
|
||||
goto fail_input;
|
||||
|
||||
result = i8042_install_filter(msi_laptop_i8042_filter);
|
||||
if (result) {
|
||||
printk(KERN_ERR
|
||||
"msi-laptop: Unable to install key filter\n");
|
||||
pr_err("Unable to install key filter\n");
|
||||
goto fail_filter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail_filter:
|
||||
msi_laptop_input_destroy();
|
||||
|
||||
fail_input:
|
||||
rfkill_cleanup();
|
||||
|
||||
fail_rfkill:
|
||||
@ -799,7 +875,7 @@ static int __init msi_init(void)
|
||||
/* Register backlight stuff */
|
||||
|
||||
if (acpi_video_backlight_support()) {
|
||||
printk(KERN_INFO "MSI: Brightness ignored, must be controlled "
|
||||
pr_info("Brightness ignored, must be controlled "
|
||||
"by ACPI video driver\n");
|
||||
} else {
|
||||
struct backlight_properties props;
|
||||
@ -854,7 +930,7 @@ static int __init msi_init(void)
|
||||
if (auto_brightness != 2)
|
||||
set_auto_brightness(auto_brightness);
|
||||
|
||||
printk(KERN_INFO "msi-laptop: driver "MSI_DRIVER_VERSION" successfully loaded.\n");
|
||||
pr_info("driver "MSI_DRIVER_VERSION" successfully loaded.\n");
|
||||
|
||||
return 0;
|
||||
|
||||
@ -886,6 +962,7 @@ static void __exit msi_cleanup(void)
|
||||
{
|
||||
if (load_scm_model) {
|
||||
i8042_remove_filter(msi_laptop_i8042_filter);
|
||||
msi_laptop_input_destroy();
|
||||
cancel_delayed_work_sync(&msi_rfkill_work);
|
||||
rfkill_cleanup();
|
||||
}
|
||||
@ -901,7 +978,7 @@ static void __exit msi_cleanup(void)
|
||||
if (auto_brightness != 2)
|
||||
set_auto_brightness(1);
|
||||
|
||||
printk(KERN_INFO "msi-laptop: driver unloaded.\n");
|
||||
pr_info("driver unloaded.\n");
|
||||
}
|
||||
|
||||
module_init(msi_init);
|
||||
|
832
drivers/platform/x86/samsung-laptop.c
Normal file
832
drivers/platform/x86/samsung-laptop.c
Normal file
@ -0,0 +1,832 @@
|
||||
/*
|
||||
* Samsung Laptop driver
|
||||
*
|
||||
* Copyright (C) 2009,2011 Greg Kroah-Hartman (gregkh@suse.de)
|
||||
* Copyright (C) 2009,2011 Novell Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/backlight.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rfkill.h>
|
||||
|
||||
/*
|
||||
* This driver is needed because a number of Samsung laptops do not hook
|
||||
* their control settings through ACPI. So we have to poke around in the
|
||||
* BIOS to do things like brightness values, and "special" key controls.
|
||||
*/
|
||||
|
||||
/*
|
||||
* We have 0 - 8 as valid brightness levels. The specs say that level 0 should
|
||||
* be reserved by the BIOS (which really doesn't make much sense), we tell
|
||||
* userspace that the value is 0 - 7 and then just tell the hardware 1 - 8
|
||||
*/
|
||||
#define MAX_BRIGHT 0x07
|
||||
|
||||
|
||||
#define SABI_IFACE_MAIN 0x00
|
||||
#define SABI_IFACE_SUB 0x02
|
||||
#define SABI_IFACE_COMPLETE 0x04
|
||||
#define SABI_IFACE_DATA 0x05
|
||||
|
||||
/* Structure to get data back to the calling function */
|
||||
struct sabi_retval {
|
||||
u8 retval[20];
|
||||
};
|
||||
|
||||
struct sabi_header_offsets {
|
||||
u8 port;
|
||||
u8 re_mem;
|
||||
u8 iface_func;
|
||||
u8 en_mem;
|
||||
u8 data_offset;
|
||||
u8 data_segment;
|
||||
};
|
||||
|
||||
struct sabi_commands {
|
||||
/*
|
||||
* Brightness is 0 - 8, as described above.
|
||||
* Value 0 is for the BIOS to use
|
||||
*/
|
||||
u8 get_brightness;
|
||||
u8 set_brightness;
|
||||
|
||||
/*
|
||||
* first byte:
|
||||
* 0x00 - wireless is off
|
||||
* 0x01 - wireless is on
|
||||
* second byte:
|
||||
* 0x02 - 3G is off
|
||||
* 0x03 - 3G is on
|
||||
* TODO, verify 3G is correct, that doesn't seem right...
|
||||
*/
|
||||
u8 get_wireless_button;
|
||||
u8 set_wireless_button;
|
||||
|
||||
/* 0 is off, 1 is on */
|
||||
u8 get_backlight;
|
||||
u8 set_backlight;
|
||||
|
||||
/*
|
||||
* 0x80 or 0x00 - no action
|
||||
* 0x81 - recovery key pressed
|
||||
*/
|
||||
u8 get_recovery_mode;
|
||||
u8 set_recovery_mode;
|
||||
|
||||
/*
|
||||
* on seclinux: 0 is low, 1 is high,
|
||||
* on swsmi: 0 is normal, 1 is silent, 2 is turbo
|
||||
*/
|
||||
u8 get_performance_level;
|
||||
u8 set_performance_level;
|
||||
|
||||
/*
|
||||
* Tell the BIOS that Linux is running on this machine.
|
||||
* 81 is on, 80 is off
|
||||
*/
|
||||
u8 set_linux;
|
||||
};
|
||||
|
||||
struct sabi_performance_level {
|
||||
const char *name;
|
||||
u8 value;
|
||||
};
|
||||
|
||||
struct sabi_config {
|
||||
const char *test_string;
|
||||
u16 main_function;
|
||||
const struct sabi_header_offsets header_offsets;
|
||||
const struct sabi_commands commands;
|
||||
const struct sabi_performance_level performance_levels[4];
|
||||
u8 min_brightness;
|
||||
u8 max_brightness;
|
||||
};
|
||||
|
||||
static const struct sabi_config sabi_configs[] = {
|
||||
{
|
||||
.test_string = "SECLINUX",
|
||||
|
||||
.main_function = 0x4c49,
|
||||
|
||||
.header_offsets = {
|
||||
.port = 0x00,
|
||||
.re_mem = 0x02,
|
||||
.iface_func = 0x03,
|
||||
.en_mem = 0x04,
|
||||
.data_offset = 0x05,
|
||||
.data_segment = 0x07,
|
||||
},
|
||||
|
||||
.commands = {
|
||||
.get_brightness = 0x00,
|
||||
.set_brightness = 0x01,
|
||||
|
||||
.get_wireless_button = 0x02,
|
||||
.set_wireless_button = 0x03,
|
||||
|
||||
.get_backlight = 0x04,
|
||||
.set_backlight = 0x05,
|
||||
|
||||
.get_recovery_mode = 0x06,
|
||||
.set_recovery_mode = 0x07,
|
||||
|
||||
.get_performance_level = 0x08,
|
||||
.set_performance_level = 0x09,
|
||||
|
||||
.set_linux = 0x0a,
|
||||
},
|
||||
|
||||
.performance_levels = {
|
||||
{
|
||||
.name = "silent",
|
||||
.value = 0,
|
||||
},
|
||||
{
|
||||
.name = "normal",
|
||||
.value = 1,
|
||||
},
|
||||
{ },
|
||||
},
|
||||
.min_brightness = 1,
|
||||
.max_brightness = 8,
|
||||
},
|
||||
{
|
||||
.test_string = "SwSmi@",
|
||||
|
||||
.main_function = 0x5843,
|
||||
|
||||
.header_offsets = {
|
||||
.port = 0x00,
|
||||
.re_mem = 0x04,
|
||||
.iface_func = 0x02,
|
||||
.en_mem = 0x03,
|
||||
.data_offset = 0x05,
|
||||
.data_segment = 0x07,
|
||||
},
|
||||
|
||||
.commands = {
|
||||
.get_brightness = 0x10,
|
||||
.set_brightness = 0x11,
|
||||
|
||||
.get_wireless_button = 0x12,
|
||||
.set_wireless_button = 0x13,
|
||||
|
||||
.get_backlight = 0x2d,
|
||||
.set_backlight = 0x2e,
|
||||
|
||||
.get_recovery_mode = 0xff,
|
||||
.set_recovery_mode = 0xff,
|
||||
|
||||
.get_performance_level = 0x31,
|
||||
.set_performance_level = 0x32,
|
||||
|
||||
.set_linux = 0xff,
|
||||
},
|
||||
|
||||
.performance_levels = {
|
||||
{
|
||||
.name = "normal",
|
||||
.value = 0,
|
||||
},
|
||||
{
|
||||
.name = "silent",
|
||||
.value = 1,
|
||||
},
|
||||
{
|
||||
.name = "overclock",
|
||||
.value = 2,
|
||||
},
|
||||
{ },
|
||||
},
|
||||
.min_brightness = 0,
|
||||
.max_brightness = 8,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
|
||||
static const struct sabi_config *sabi_config;
|
||||
|
||||
static void __iomem *sabi;
|
||||
static void __iomem *sabi_iface;
|
||||
static void __iomem *f0000_segment;
|
||||
static struct backlight_device *backlight_device;
|
||||
static struct mutex sabi_mutex;
|
||||
static struct platform_device *sdev;
|
||||
static struct rfkill *rfk;
|
||||
|
||||
static int force;
|
||||
module_param(force, bool, 0);
|
||||
MODULE_PARM_DESC(force,
|
||||
"Disable the DMI check and forces the driver to be loaded");
|
||||
|
||||
static int debug;
|
||||
module_param(debug, bool, S_IRUGO | S_IWUSR);
|
||||
MODULE_PARM_DESC(debug, "Debug enabled or not");
|
||||
|
||||
static int sabi_get_command(u8 command, struct sabi_retval *sretval)
|
||||
{
|
||||
int retval = 0;
|
||||
u16 port = readw(sabi + sabi_config->header_offsets.port);
|
||||
u8 complete, iface_data;
|
||||
|
||||
mutex_lock(&sabi_mutex);
|
||||
|
||||
/* enable memory to be able to write to it */
|
||||
outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
|
||||
|
||||
/* write out the command */
|
||||
writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
|
||||
writew(command, sabi_iface + SABI_IFACE_SUB);
|
||||
writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
|
||||
outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
|
||||
|
||||
/* write protect memory to make it safe */
|
||||
outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
|
||||
|
||||
/* see if the command actually succeeded */
|
||||
complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
|
||||
iface_data = readb(sabi_iface + SABI_IFACE_DATA);
|
||||
if (complete != 0xaa || iface_data == 0xff) {
|
||||
pr_warn("SABI get command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
|
||||
command, complete, iface_data);
|
||||
retval = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
/*
|
||||
* Save off the data into a structure so the caller use it.
|
||||
* Right now we only want the first 4 bytes,
|
||||
* There are commands that need more, but not for the ones we
|
||||
* currently care about.
|
||||
*/
|
||||
sretval->retval[0] = readb(sabi_iface + SABI_IFACE_DATA);
|
||||
sretval->retval[1] = readb(sabi_iface + SABI_IFACE_DATA + 1);
|
||||
sretval->retval[2] = readb(sabi_iface + SABI_IFACE_DATA + 2);
|
||||
sretval->retval[3] = readb(sabi_iface + SABI_IFACE_DATA + 3);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&sabi_mutex);
|
||||
return retval;
|
||||
|
||||
}
|
||||
|
||||
static int sabi_set_command(u8 command, u8 data)
|
||||
{
|
||||
int retval = 0;
|
||||
u16 port = readw(sabi + sabi_config->header_offsets.port);
|
||||
u8 complete, iface_data;
|
||||
|
||||
mutex_lock(&sabi_mutex);
|
||||
|
||||
/* enable memory to be able to write to it */
|
||||
outb(readb(sabi + sabi_config->header_offsets.en_mem), port);
|
||||
|
||||
/* write out the command */
|
||||
writew(sabi_config->main_function, sabi_iface + SABI_IFACE_MAIN);
|
||||
writew(command, sabi_iface + SABI_IFACE_SUB);
|
||||
writeb(0, sabi_iface + SABI_IFACE_COMPLETE);
|
||||
writeb(data, sabi_iface + SABI_IFACE_DATA);
|
||||
outb(readb(sabi + sabi_config->header_offsets.iface_func), port);
|
||||
|
||||
/* write protect memory to make it safe */
|
||||
outb(readb(sabi + sabi_config->header_offsets.re_mem), port);
|
||||
|
||||
/* see if the command actually succeeded */
|
||||
complete = readb(sabi_iface + SABI_IFACE_COMPLETE);
|
||||
iface_data = readb(sabi_iface + SABI_IFACE_DATA);
|
||||
if (complete != 0xaa || iface_data == 0xff) {
|
||||
pr_warn("SABI set command 0x%02x failed with completion flag 0x%02x and data 0x%02x\n",
|
||||
command, complete, iface_data);
|
||||
retval = -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&sabi_mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void test_backlight(void)
|
||||
{
|
||||
struct sabi_retval sretval;
|
||||
|
||||
sabi_get_command(sabi_config->commands.get_backlight, &sretval);
|
||||
printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
|
||||
|
||||
sabi_set_command(sabi_config->commands.set_backlight, 0);
|
||||
printk(KERN_DEBUG "backlight should be off\n");
|
||||
|
||||
sabi_get_command(sabi_config->commands.get_backlight, &sretval);
|
||||
printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
|
||||
|
||||
msleep(1000);
|
||||
|
||||
sabi_set_command(sabi_config->commands.set_backlight, 1);
|
||||
printk(KERN_DEBUG "backlight should be on\n");
|
||||
|
||||
sabi_get_command(sabi_config->commands.get_backlight, &sretval);
|
||||
printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]);
|
||||
}
|
||||
|
||||
static void test_wireless(void)
|
||||
{
|
||||
struct sabi_retval sretval;
|
||||
|
||||
sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
|
||||
printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
|
||||
|
||||
sabi_set_command(sabi_config->commands.set_wireless_button, 0);
|
||||
printk(KERN_DEBUG "wireless led should be off\n");
|
||||
|
||||
sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
|
||||
printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
|
||||
|
||||
msleep(1000);
|
||||
|
||||
sabi_set_command(sabi_config->commands.set_wireless_button, 1);
|
||||
printk(KERN_DEBUG "wireless led should be on\n");
|
||||
|
||||
sabi_get_command(sabi_config->commands.get_wireless_button, &sretval);
|
||||
printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]);
|
||||
}
|
||||
|
||||
static u8 read_brightness(void)
|
||||
{
|
||||
struct sabi_retval sretval;
|
||||
int user_brightness = 0;
|
||||
int retval;
|
||||
|
||||
retval = sabi_get_command(sabi_config->commands.get_brightness,
|
||||
&sretval);
|
||||
if (!retval) {
|
||||
user_brightness = sretval.retval[0];
|
||||
if (user_brightness != 0)
|
||||
user_brightness -= sabi_config->min_brightness;
|
||||
}
|
||||
return user_brightness;
|
||||
}
|
||||
|
||||
static void set_brightness(u8 user_brightness)
|
||||
{
|
||||
u8 user_level = user_brightness - sabi_config->min_brightness;
|
||||
|
||||
sabi_set_command(sabi_config->commands.set_brightness, user_level);
|
||||
}
|
||||
|
||||
static int get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
return (int)read_brightness();
|
||||
}
|
||||
|
||||
static int update_status(struct backlight_device *bd)
|
||||
{
|
||||
set_brightness(bd->props.brightness);
|
||||
|
||||
if (bd->props.power == FB_BLANK_UNBLANK)
|
||||
sabi_set_command(sabi_config->commands.set_backlight, 1);
|
||||
else
|
||||
sabi_set_command(sabi_config->commands.set_backlight, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops backlight_ops = {
|
||||
.get_brightness = get_brightness,
|
||||
.update_status = update_status,
|
||||
};
|
||||
|
||||
static int rfkill_set(void *data, bool blocked)
|
||||
{
|
||||
/* Do something with blocked...*/
|
||||
/*
|
||||
* blocked == false is on
|
||||
* blocked == true is off
|
||||
*/
|
||||
if (blocked)
|
||||
sabi_set_command(sabi_config->commands.set_wireless_button, 0);
|
||||
else
|
||||
sabi_set_command(sabi_config->commands.set_wireless_button, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct rfkill_ops rfkill_ops = {
|
||||
.set_block = rfkill_set,
|
||||
};
|
||||
|
||||
static int init_wireless(struct platform_device *sdev)
|
||||
{
|
||||
int retval;
|
||||
|
||||
rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN,
|
||||
&rfkill_ops, NULL);
|
||||
if (!rfk)
|
||||
return -ENOMEM;
|
||||
|
||||
retval = rfkill_register(rfk);
|
||||
if (retval) {
|
||||
rfkill_destroy(rfk);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_wireless(void)
|
||||
{
|
||||
rfkill_unregister(rfk);
|
||||
rfkill_destroy(rfk);
|
||||
}
|
||||
|
||||
static ssize_t get_performance_level(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct sabi_retval sretval;
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
/* Read the state */
|
||||
retval = sabi_get_command(sabi_config->commands.get_performance_level,
|
||||
&sretval);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
/* The logic is backwards, yeah, lots of fun... */
|
||||
for (i = 0; sabi_config->performance_levels[i].name; ++i) {
|
||||
if (sretval.retval[0] == sabi_config->performance_levels[i].value)
|
||||
return sprintf(buf, "%s\n", sabi_config->performance_levels[i].name);
|
||||
}
|
||||
return sprintf(buf, "%s\n", "unknown");
|
||||
}
|
||||
|
||||
static ssize_t set_performance_level(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (count >= 1) {
|
||||
int i;
|
||||
for (i = 0; sabi_config->performance_levels[i].name; ++i) {
|
||||
const struct sabi_performance_level *level =
|
||||
&sabi_config->performance_levels[i];
|
||||
if (!strncasecmp(level->name, buf, strlen(level->name))) {
|
||||
sabi_set_command(sabi_config->commands.set_performance_level,
|
||||
level->value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!sabi_config->performance_levels[i].name)
|
||||
return -EINVAL;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO,
|
||||
get_performance_level, set_performance_level);
|
||||
|
||||
|
||||
static int __init dmi_check_cb(const struct dmi_system_id *id)
|
||||
{
|
||||
pr_info("found laptop model '%s'\n",
|
||||
id->ident);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct dmi_system_id __initdata samsung_dmi_table[] = {
|
||||
{
|
||||
.ident = "N128",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "N128"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "N128"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "N130",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "N130"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "N130"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "X125",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X125"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "X125"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "X120/X170",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X120/X170"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "X120/X170"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "NC10",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NC10"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "NP-Q45",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "SQ45S70S"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "SQ45S70S"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "X360",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "X360"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "X360"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "R518",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "R518"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "R518"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "R519/R719",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "R519/R719"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "R519/R719"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "N150/N210/N220",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "N150P/N210P/N220P",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "N150P/N210P/N220P"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "N150P/N210P/N220P"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "R530/R730",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "R530/R730"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "R530/R730"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "NF110/NF210/NF310",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "NF110/NF210/NF310"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "NF110/NF210/NF310"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "N145P/N250P/N260P",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "N145P/N250P/N260P"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "N145P/N250P/N260P"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "R70/R71",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR,
|
||||
"SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "R70/R71"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "R70/R71"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{
|
||||
.ident = "P460",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "P460"),
|
||||
DMI_MATCH(DMI_BOARD_NAME, "P460"),
|
||||
},
|
||||
.callback = dmi_check_cb,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
|
||||
|
||||
static int find_signature(void __iomem *memcheck, const char *testStr)
|
||||
{
|
||||
int i = 0;
|
||||
int loca;
|
||||
|
||||
for (loca = 0; loca < 0xffff; loca++) {
|
||||
char temp = readb(memcheck + loca);
|
||||
|
||||
if (temp == testStr[i]) {
|
||||
if (i == strlen(testStr)-1)
|
||||
break;
|
||||
++i;
|
||||
} else {
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
return loca;
|
||||
}
|
||||
|
||||
static int __init samsung_init(void)
|
||||
{
|
||||
struct backlight_properties props;
|
||||
struct sabi_retval sretval;
|
||||
unsigned int ifaceP;
|
||||
int i;
|
||||
int loca;
|
||||
int retval;
|
||||
|
||||
mutex_init(&sabi_mutex);
|
||||
|
||||
if (!force && !dmi_check_system(samsung_dmi_table))
|
||||
return -ENODEV;
|
||||
|
||||
f0000_segment = ioremap_nocache(0xf0000, 0xffff);
|
||||
if (!f0000_segment) {
|
||||
pr_err("Can't map the segment at 0xf0000\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Try to find one of the signatures in memory to find the header */
|
||||
for (i = 0; sabi_configs[i].test_string != 0; ++i) {
|
||||
sabi_config = &sabi_configs[i];
|
||||
loca = find_signature(f0000_segment, sabi_config->test_string);
|
||||
if (loca != 0xffff)
|
||||
break;
|
||||
}
|
||||
|
||||
if (loca == 0xffff) {
|
||||
pr_err("This computer does not support SABI\n");
|
||||
goto error_no_signature;
|
||||
}
|
||||
|
||||
/* point to the SMI port Number */
|
||||
loca += 1;
|
||||
sabi = (f0000_segment + loca);
|
||||
|
||||
if (debug) {
|
||||
printk(KERN_DEBUG "This computer supports SABI==%x\n",
|
||||
loca + 0xf0000 - 6);
|
||||
printk(KERN_DEBUG "SABI header:\n");
|
||||
printk(KERN_DEBUG " SMI Port Number = 0x%04x\n",
|
||||
readw(sabi + sabi_config->header_offsets.port));
|
||||
printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n",
|
||||
readb(sabi + sabi_config->header_offsets.iface_func));
|
||||
printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n",
|
||||
readb(sabi + sabi_config->header_offsets.en_mem));
|
||||
printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n",
|
||||
readb(sabi + sabi_config->header_offsets.re_mem));
|
||||
printk(KERN_DEBUG " SABI data offset = 0x%04x\n",
|
||||
readw(sabi + sabi_config->header_offsets.data_offset));
|
||||
printk(KERN_DEBUG " SABI data segment = 0x%04x\n",
|
||||
readw(sabi + sabi_config->header_offsets.data_segment));
|
||||
}
|
||||
|
||||
/* Get a pointer to the SABI Interface */
|
||||
ifaceP = (readw(sabi + sabi_config->header_offsets.data_segment) & 0x0ffff) << 4;
|
||||
ifaceP += readw(sabi + sabi_config->header_offsets.data_offset) & 0x0ffff;
|
||||
sabi_iface = ioremap_nocache(ifaceP, 16);
|
||||
if (!sabi_iface) {
|
||||
pr_err("Can't remap %x\n", ifaceP);
|
||||
goto exit;
|
||||
}
|
||||
if (debug) {
|
||||
printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
|
||||
printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface);
|
||||
|
||||
test_backlight();
|
||||
test_wireless();
|
||||
|
||||
retval = sabi_get_command(sabi_config->commands.get_brightness,
|
||||
&sretval);
|
||||
printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]);
|
||||
}
|
||||
|
||||
/* Turn on "Linux" mode in the BIOS */
|
||||
if (sabi_config->commands.set_linux != 0xff) {
|
||||
retval = sabi_set_command(sabi_config->commands.set_linux,
|
||||
0x81);
|
||||
if (retval) {
|
||||
pr_warn("Linux mode was not set!\n");
|
||||
goto error_no_platform;
|
||||
}
|
||||
}
|
||||
|
||||
/* knock up a platform device to hang stuff off of */
|
||||
sdev = platform_device_register_simple("samsung", -1, NULL, 0);
|
||||
if (IS_ERR(sdev))
|
||||
goto error_no_platform;
|
||||
|
||||
/* create a backlight device to talk to this one */
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = sabi_config->max_brightness;
|
||||
backlight_device = backlight_device_register("samsung", &sdev->dev,
|
||||
NULL, &backlight_ops,
|
||||
&props);
|
||||
if (IS_ERR(backlight_device))
|
||||
goto error_no_backlight;
|
||||
|
||||
backlight_device->props.brightness = read_brightness();
|
||||
backlight_device->props.power = FB_BLANK_UNBLANK;
|
||||
backlight_update_status(backlight_device);
|
||||
|
||||
retval = init_wireless(sdev);
|
||||
if (retval)
|
||||
goto error_no_rfk;
|
||||
|
||||
retval = device_create_file(&sdev->dev, &dev_attr_performance_level);
|
||||
if (retval)
|
||||
goto error_file_create;
|
||||
|
||||
exit:
|
||||
return 0;
|
||||
|
||||
error_file_create:
|
||||
destroy_wireless();
|
||||
|
||||
error_no_rfk:
|
||||
backlight_device_unregister(backlight_device);
|
||||
|
||||
error_no_backlight:
|
||||
platform_device_unregister(sdev);
|
||||
|
||||
error_no_platform:
|
||||
iounmap(sabi_iface);
|
||||
|
||||
error_no_signature:
|
||||
iounmap(f0000_segment);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void __exit samsung_exit(void)
|
||||
{
|
||||
/* Turn off "Linux" mode in the BIOS */
|
||||
if (sabi_config->commands.set_linux != 0xff)
|
||||
sabi_set_command(sabi_config->commands.set_linux, 0x80);
|
||||
|
||||
device_remove_file(&sdev->dev, &dev_attr_performance_level);
|
||||
backlight_device_unregister(backlight_device);
|
||||
destroy_wireless();
|
||||
iounmap(sabi_iface);
|
||||
iounmap(f0000_segment);
|
||||
platform_device_unregister(sdev);
|
||||
}
|
||||
|
||||
module_init(samsung_init);
|
||||
module_exit(samsung_exit);
|
||||
|
||||
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>");
|
||||
MODULE_DESCRIPTION("Samsung Backlight driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -71,8 +71,9 @@
|
||||
#endif
|
||||
|
||||
#define DRV_PFX "sony-laptop: "
|
||||
#define dprintk(msg...) do { \
|
||||
if (debug) printk(KERN_WARNING DRV_PFX msg); \
|
||||
#define dprintk(msg...) do { \
|
||||
if (debug) \
|
||||
pr_warn(DRV_PFX msg); \
|
||||
} while (0)
|
||||
|
||||
#define SONY_LAPTOP_DRIVER_VERSION "0.6"
|
||||
@ -124,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,
|
||||
@ -402,7 +416,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device)
|
||||
error = kfifo_alloc(&sony_laptop_input.fifo,
|
||||
SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
|
||||
if (error) {
|
||||
printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
|
||||
pr_err(DRV_PFX "kfifo_alloc failed\n");
|
||||
goto err_dec_users;
|
||||
}
|
||||
|
||||
@ -591,7 +605,7 @@ struct sony_nc_value {
|
||||
int value; /* current setting */
|
||||
int valid; /* Has ever been set */
|
||||
int debug; /* active only in debug mode ? */
|
||||
struct device_attribute devattr; /* sysfs atribute */
|
||||
struct device_attribute devattr; /* sysfs attribute */
|
||||
};
|
||||
|
||||
#define SNC_HANDLE_NAMES(_name, _values...) \
|
||||
@ -686,7 +700,7 @@ static int acpi_callgetfunc(acpi_handle handle, char *name, int *result)
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n");
|
||||
pr_warn(DRV_PFX "acpi_callreadfunc failed\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
@ -712,7 +726,7 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
|
||||
if (status == AE_OK) {
|
||||
if (result != NULL) {
|
||||
if (out_obj.type != ACPI_TYPE_INTEGER) {
|
||||
printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad "
|
||||
pr_warn(DRV_PFX "acpi_evaluate_object bad "
|
||||
"return type\n");
|
||||
return -1;
|
||||
}
|
||||
@ -721,34 +735,103 @@ static int acpi_callsetfunc(acpi_handle handle, char *name, int value,
|
||||
return 0;
|
||||
}
|
||||
|
||||
printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n");
|
||||
pr_warn(DRV_PFX "acpi_evaluate_object failed\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sony_nc_handles {
|
||||
u16 cap[0x10];
|
||||
struct device_attribute devattr;
|
||||
};
|
||||
|
||||
static struct sony_nc_handles *handles;
|
||||
|
||||
static ssize_t sony_nc_handles_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buffer)
|
||||
{
|
||||
ssize_t len = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
||||
len += snprintf(buffer + len, PAGE_SIZE - len, "0x%.4x ",
|
||||
handles->cap[i]);
|
||||
}
|
||||
len += snprintf(buffer + len, PAGE_SIZE - len, "\n");
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int sony_nc_handles_setup(struct platform_device *pd)
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
|
||||
handles = kzalloc(sizeof(*handles), GFP_KERNEL);
|
||||
if (!handles)
|
||||
return -ENOMEM;
|
||||
|
||||
sysfs_attr_init(&handles->devattr.attr);
|
||||
handles->devattr.attr.name = "handles";
|
||||
handles->devattr.attr.mode = S_IRUGO;
|
||||
handles->devattr.show = sony_nc_handles_show;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(handles->cap); i++) {
|
||||
if (!acpi_callsetfunc(sony_nc_acpi_handle,
|
||||
"SN00", i + 0x20, &result)) {
|
||||
dprintk("caching handle 0x%.4x (offset: 0x%.2x)\n",
|
||||
result, i);
|
||||
handles->cap[i] = result;
|
||||
}
|
||||
}
|
||||
|
||||
/* allow reading capabilities via sysfs */
|
||||
if (device_create_file(&pd->dev, &handles->devattr)) {
|
||||
kfree(handles);
|
||||
handles = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sony_nc_handles_cleanup(struct platform_device *pd)
|
||||
{
|
||||
if (handles) {
|
||||
device_remove_file(&pd->dev, &handles->devattr);
|
||||
kfree(handles);
|
||||
handles = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sony_find_snc_handle(int handle)
|
||||
{
|
||||
int i;
|
||||
int result;
|
||||
|
||||
for (i = 0x20; i < 0x30; i++) {
|
||||
acpi_callsetfunc(sony_nc_acpi_handle, "SN00", i, &result);
|
||||
if (result == handle)
|
||||
return i-0x20;
|
||||
for (i = 0; i < 0x10; i++) {
|
||||
if (handles->cap[i] == handle) {
|
||||
dprintk("found handle 0x%.4x (offset: 0x%.2x)\n",
|
||||
handle, i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
dprintk("handle 0x%.4x not found\n", handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sony_call_snc_handle(int handle, int argument, int *result)
|
||||
{
|
||||
int ret = 0;
|
||||
int offset = sony_find_snc_handle(handle);
|
||||
|
||||
if (offset < 0)
|
||||
return -1;
|
||||
|
||||
return acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument,
|
||||
result);
|
||||
ret = acpi_callsetfunc(sony_nc_acpi_handle, "SN07", offset | argument,
|
||||
result);
|
||||
dprintk("called SN07 with 0x%.4x (result: 0x%.4x)\n", offset | argument,
|
||||
*result);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -857,11 +940,39 @@ static int sony_backlight_get_brightness(struct backlight_device *bd)
|
||||
return value - 1;
|
||||
}
|
||||
|
||||
static struct backlight_device *sony_backlight_device;
|
||||
static int sony_nc_get_brightness_ng(struct backlight_device *bd)
|
||||
{
|
||||
int result;
|
||||
int *handle = (int *)bl_get_data(bd);
|
||||
|
||||
sony_call_snc_handle(*handle, 0x0200, &result);
|
||||
|
||||
return result & 0xff;
|
||||
}
|
||||
|
||||
static int sony_nc_update_status_ng(struct backlight_device *bd)
|
||||
{
|
||||
int value, result;
|
||||
int *handle = (int *)bl_get_data(bd);
|
||||
|
||||
value = bd->props.brightness;
|
||||
sony_call_snc_handle(*handle, 0x0100 | (value << 16), &result);
|
||||
|
||||
return sony_nc_get_brightness_ng(bd);
|
||||
}
|
||||
|
||||
static const struct backlight_ops sony_backlight_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.update_status = sony_backlight_update_status,
|
||||
.get_brightness = sony_backlight_get_brightness,
|
||||
};
|
||||
static const struct backlight_ops sony_backlight_ng_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.update_status = sony_nc_update_status_ng,
|
||||
.get_brightness = sony_nc_get_brightness_ng,
|
||||
};
|
||||
static int backlight_ng_handle;
|
||||
static struct backlight_device *sony_backlight_device;
|
||||
|
||||
/*
|
||||
* New SNC-only Vaios event mapping to driver known keys
|
||||
@ -972,7 +1083,7 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)
|
||||
}
|
||||
|
||||
if (!key_event->data)
|
||||
printk(KERN_INFO DRV_PFX
|
||||
pr_info(DRV_PFX
|
||||
"Unknown event: 0x%x 0x%x\n",
|
||||
key_handle,
|
||||
ev);
|
||||
@ -996,7 +1107,7 @@ static acpi_status sony_walk_callback(acpi_handle handle, u32 level,
|
||||
struct acpi_device_info *info;
|
||||
|
||||
if (ACPI_SUCCESS(acpi_get_object_info(handle, &info))) {
|
||||
printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n",
|
||||
pr_warn(DRV_PFX "method: name: %4.4s, args %X\n",
|
||||
(char *)&info->name, info->param_count);
|
||||
|
||||
kfree(info);
|
||||
@ -1037,7 +1148,7 @@ static int sony_nc_resume(struct acpi_device *device)
|
||||
ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset,
|
||||
item->value, NULL);
|
||||
if (ret < 0) {
|
||||
printk("%s: %d\n", __func__, ret);
|
||||
pr_err(DRV_PFX "%s: %d\n", __func__, ret);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1054,11 +1165,6 @@ static int sony_nc_resume(struct acpi_device *device)
|
||||
sony_nc_function_setup(device);
|
||||
}
|
||||
|
||||
/* set the last requested brightness level */
|
||||
if (sony_backlight_device &&
|
||||
sony_backlight_update_status(sony_backlight_device) < 0)
|
||||
printk(KERN_WARNING DRV_PFX "unable to restore brightness level\n");
|
||||
|
||||
/* re-read rfkill state */
|
||||
sony_nc_rfkill_update();
|
||||
|
||||
@ -1206,12 +1312,12 @@ static void sony_nc_rfkill_setup(struct acpi_device *device)
|
||||
|
||||
device_enum = (union acpi_object *) buffer.pointer;
|
||||
if (!device_enum) {
|
||||
pr_err("Invalid SN06 return object\n");
|
||||
pr_err(DRV_PFX "No SN06 return object.");
|
||||
goto out_no_enum;
|
||||
}
|
||||
if (device_enum->type != ACPI_TYPE_BUFFER) {
|
||||
pr_err("Invalid SN06 return object type 0x%.2x\n",
|
||||
device_enum->type);
|
||||
pr_err(DRV_PFX "Invalid SN06 return object 0x%.2x\n",
|
||||
device_enum->type);
|
||||
goto out_no_enum;
|
||||
}
|
||||
|
||||
@ -1245,6 +1351,209 @@ out_no_enum:
|
||||
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;
|
||||
};
|
||||
|
||||
static 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);
|
||||
if (!kbdbl_handle)
|
||||
return -ENOMEM;
|
||||
|
||||
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 void sony_nc_backlight_setup(void)
|
||||
{
|
||||
acpi_handle unused;
|
||||
int max_brightness = 0;
|
||||
const struct backlight_ops *ops = NULL;
|
||||
struct backlight_properties props;
|
||||
|
||||
if (sony_find_snc_handle(0x12f) != -1) {
|
||||
backlight_ng_handle = 0x12f;
|
||||
ops = &sony_backlight_ng_ops;
|
||||
max_brightness = 0xff;
|
||||
|
||||
} else if (sony_find_snc_handle(0x137) != -1) {
|
||||
backlight_ng_handle = 0x137;
|
||||
ops = &sony_backlight_ng_ops;
|
||||
max_brightness = 0xff;
|
||||
|
||||
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
||||
&unused))) {
|
||||
ops = &sony_backlight_ops;
|
||||
max_brightness = SONY_MAX_BRIGHTNESS - 1;
|
||||
|
||||
} else
|
||||
return;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.max_brightness = max_brightness;
|
||||
sony_backlight_device = backlight_device_register("sony", NULL,
|
||||
&backlight_ng_handle,
|
||||
ops, &props);
|
||||
|
||||
if (IS_ERR(sony_backlight_device)) {
|
||||
pr_warning(DRV_PFX "unable to register backlight device\n");
|
||||
sony_backlight_device = NULL;
|
||||
} else
|
||||
sony_backlight_device->props.brightness =
|
||||
ops->get_brightness(sony_backlight_device);
|
||||
}
|
||||
|
||||
static void sony_nc_backlight_cleanup(void)
|
||||
{
|
||||
if (sony_backlight_device)
|
||||
backlight_device_unregister(sony_backlight_device);
|
||||
}
|
||||
|
||||
static int sony_nc_add(struct acpi_device *device)
|
||||
{
|
||||
acpi_status status;
|
||||
@ -1252,8 +1561,8 @@ static int sony_nc_add(struct acpi_device *device)
|
||||
acpi_handle handle;
|
||||
struct sony_nc_value *item;
|
||||
|
||||
printk(KERN_INFO DRV_PFX "%s v%s.\n",
|
||||
SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
|
||||
pr_info(DRV_PFX "%s v%s.\n", SONY_NC_DRIVER_NAME,
|
||||
SONY_LAPTOP_DRIVER_VERSION);
|
||||
|
||||
sony_nc_acpi_device = device;
|
||||
strcpy(acpi_device_class(device), "sony/hotkey");
|
||||
@ -1269,13 +1578,18 @@ static int sony_nc_add(struct acpi_device *device)
|
||||
goto outwalk;
|
||||
}
|
||||
|
||||
result = sony_pf_add();
|
||||
if (result)
|
||||
goto outpresent;
|
||||
|
||||
if (debug) {
|
||||
status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle,
|
||||
1, sony_walk_callback, NULL, NULL, NULL);
|
||||
status = acpi_walk_namespace(ACPI_TYPE_METHOD,
|
||||
sony_nc_acpi_handle, 1, sony_walk_callback,
|
||||
NULL, NULL, NULL);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n");
|
||||
pr_warn(DRV_PFX "unable to walk acpi resources\n");
|
||||
result = -ENODEV;
|
||||
goto outwalk;
|
||||
goto outpresent;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1288,6 +1602,12 @@ static int sony_nc_add(struct acpi_device *device)
|
||||
if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "SN00",
|
||||
&handle))) {
|
||||
dprintk("Doing SNC setup\n");
|
||||
result = sony_nc_handles_setup(sony_pf_device);
|
||||
if (result)
|
||||
goto outpresent;
|
||||
result = sony_nc_kbd_backlight_setup(sony_pf_device);
|
||||
if (result)
|
||||
goto outsnc;
|
||||
sony_nc_function_setup(device);
|
||||
sony_nc_rfkill_setup(device);
|
||||
}
|
||||
@ -1295,40 +1615,17 @@ static int sony_nc_add(struct acpi_device *device)
|
||||
/* setup input devices and helper fifo */
|
||||
result = sony_laptop_setup_input(device);
|
||||
if (result) {
|
||||
printk(KERN_ERR DRV_PFX
|
||||
"Unable to create input devices.\n");
|
||||
goto outwalk;
|
||||
pr_err(DRV_PFX "Unable to create input devices.\n");
|
||||
goto outkbdbacklight;
|
||||
}
|
||||
|
||||
if (acpi_video_backlight_support()) {
|
||||
printk(KERN_INFO DRV_PFX "brightness ignored, must be "
|
||||
pr_info(DRV_PFX "brightness ignored, must be "
|
||||
"controlled by ACPI video driver\n");
|
||||
} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",
|
||||
&handle))) {
|
||||
struct backlight_properties props;
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
props.max_brightness = SONY_MAX_BRIGHTNESS - 1;
|
||||
sony_backlight_device = backlight_device_register("sony", NULL,
|
||||
NULL,
|
||||
&sony_backlight_ops,
|
||||
&props);
|
||||
|
||||
if (IS_ERR(sony_backlight_device)) {
|
||||
printk(KERN_WARNING DRV_PFX "unable to register backlight device\n");
|
||||
sony_backlight_device = NULL;
|
||||
} else {
|
||||
sony_backlight_device->props.brightness =
|
||||
sony_backlight_get_brightness
|
||||
(sony_backlight_device);
|
||||
}
|
||||
|
||||
} else {
|
||||
sony_nc_backlight_setup();
|
||||
}
|
||||
|
||||
result = sony_pf_add();
|
||||
if (result)
|
||||
goto outbacklight;
|
||||
|
||||
/* create sony_pf sysfs attributes related to the SNC device */
|
||||
for (item = sony_nc_values; item->name; ++item) {
|
||||
|
||||
@ -1374,14 +1671,19 @@ static int sony_nc_add(struct acpi_device *device)
|
||||
for (item = sony_nc_values; item->name; ++item) {
|
||||
device_remove_file(&sony_pf_device->dev, &item->devattr);
|
||||
}
|
||||
sony_pf_remove();
|
||||
|
||||
outbacklight:
|
||||
if (sony_backlight_device)
|
||||
backlight_device_unregister(sony_backlight_device);
|
||||
sony_nc_backlight_cleanup();
|
||||
|
||||
sony_laptop_remove_input();
|
||||
|
||||
outkbdbacklight:
|
||||
sony_nc_kbd_backlight_cleanup(sony_pf_device);
|
||||
|
||||
outsnc:
|
||||
sony_nc_handles_cleanup(sony_pf_device);
|
||||
|
||||
outpresent:
|
||||
sony_pf_remove();
|
||||
|
||||
outwalk:
|
||||
sony_nc_rfkill_cleanup();
|
||||
return result;
|
||||
@ -1391,8 +1693,7 @@ static int sony_nc_remove(struct acpi_device *device, int type)
|
||||
{
|
||||
struct sony_nc_value *item;
|
||||
|
||||
if (sony_backlight_device)
|
||||
backlight_device_unregister(sony_backlight_device);
|
||||
sony_nc_backlight_cleanup();
|
||||
|
||||
sony_nc_acpi_device = NULL;
|
||||
|
||||
@ -1400,6 +1701,8 @@ 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();
|
||||
sony_nc_rfkill_cleanup();
|
||||
@ -1438,7 +1741,6 @@ static struct acpi_driver sony_nc_driver = {
|
||||
#define SONYPI_DEVICE_TYPE1 0x00000001
|
||||
#define SONYPI_DEVICE_TYPE2 0x00000002
|
||||
#define SONYPI_DEVICE_TYPE3 0x00000004
|
||||
#define SONYPI_DEVICE_TYPE4 0x00000008
|
||||
|
||||
#define SONYPI_TYPE1_OFFSET 0x04
|
||||
#define SONYPI_TYPE2_OFFSET 0x12
|
||||
@ -1584,8 +1886,8 @@ static struct sonypi_event sonypi_blueev[] = {
|
||||
|
||||
/* The set of possible wireless events */
|
||||
static struct sonypi_event sonypi_wlessev[] = {
|
||||
{ 0x59, SONYPI_EVENT_WIRELESS_ON },
|
||||
{ 0x5a, SONYPI_EVENT_WIRELESS_OFF },
|
||||
{ 0x59, SONYPI_EVENT_IGNORE },
|
||||
{ 0x5a, SONYPI_EVENT_IGNORE },
|
||||
{ 0, 0 }
|
||||
};
|
||||
|
||||
@ -1842,7 +2144,7 @@ out:
|
||||
if (pcidev)
|
||||
pci_dev_put(pcidev);
|
||||
|
||||
printk(KERN_INFO DRV_PFX "detected Type%d model\n",
|
||||
pr_info(DRV_PFX "detected Type%d model\n",
|
||||
dev->model == SONYPI_DEVICE_TYPE1 ? 1 :
|
||||
dev->model == SONYPI_DEVICE_TYPE2 ? 2 : 3);
|
||||
}
|
||||
@ -1890,7 +2192,7 @@ static int __sony_pic_camera_ready(void)
|
||||
static int __sony_pic_camera_off(void)
|
||||
{
|
||||
if (!camera) {
|
||||
printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
|
||||
pr_warn(DRV_PFX "camera control not enabled\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1910,7 +2212,7 @@ static int __sony_pic_camera_on(void)
|
||||
int i, j, x;
|
||||
|
||||
if (!camera) {
|
||||
printk(KERN_WARNING DRV_PFX "camera control not enabled\n");
|
||||
pr_warn(DRV_PFX "camera control not enabled\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1933,7 +2235,7 @@ static int __sony_pic_camera_on(void)
|
||||
}
|
||||
|
||||
if (j == 0) {
|
||||
printk(KERN_WARNING DRV_PFX "failed to power on camera\n");
|
||||
pr_warn(DRV_PFX "failed to power on camera\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -1989,7 +2291,7 @@ int sony_pic_camera_command(int command, u8 value)
|
||||
ITERATIONS_SHORT);
|
||||
break;
|
||||
default:
|
||||
printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n",
|
||||
pr_err(DRV_PFX "sony_pic_camera_command invalid: %d\n",
|
||||
command);
|
||||
break;
|
||||
}
|
||||
@ -2396,7 +2698,7 @@ static int sonypi_compat_init(void)
|
||||
error =
|
||||
kfifo_alloc(&sonypi_compat.fifo, SONY_LAPTOP_BUF_SIZE, GFP_KERNEL);
|
||||
if (error) {
|
||||
printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n");
|
||||
pr_err(DRV_PFX "kfifo_alloc failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -2406,11 +2708,11 @@ static int sonypi_compat_init(void)
|
||||
sonypi_misc_device.minor = minor;
|
||||
error = misc_register(&sonypi_misc_device);
|
||||
if (error) {
|
||||
printk(KERN_ERR DRV_PFX "misc_register failed\n");
|
||||
pr_err(DRV_PFX "misc_register failed\n");
|
||||
goto err_free_kfifo;
|
||||
}
|
||||
if (minor == -1)
|
||||
printk(KERN_INFO DRV_PFX "device allocated minor is %d\n",
|
||||
pr_info(DRV_PFX "device allocated minor is %d\n",
|
||||
sonypi_misc_device.minor);
|
||||
|
||||
return 0;
|
||||
@ -2470,8 +2772,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
|
||||
}
|
||||
for (i = 0; i < p->interrupt_count; i++) {
|
||||
if (!p->interrupts[i]) {
|
||||
printk(KERN_WARNING DRV_PFX
|
||||
"Invalid IRQ %d\n",
|
||||
pr_warn(DRV_PFX "Invalid IRQ %d\n",
|
||||
p->interrupts[i]);
|
||||
continue;
|
||||
}
|
||||
@ -2510,7 +2811,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context)
|
||||
ioport->io2.address_length);
|
||||
}
|
||||
else {
|
||||
printk(KERN_ERR DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
|
||||
pr_err(DRV_PFX "Unknown SPIC Type, more than 2 IO Ports\n");
|
||||
return AE_ERROR;
|
||||
}
|
||||
return AE_OK;
|
||||
@ -2538,7 +2839,7 @@ static int sony_pic_possible_resources(struct acpi_device *device)
|
||||
dprintk("Evaluating _STA\n");
|
||||
result = acpi_bus_get_status(device);
|
||||
if (result) {
|
||||
printk(KERN_WARNING DRV_PFX "Unable to read status\n");
|
||||
pr_warn(DRV_PFX "Unable to read status\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
@ -2554,8 +2855,7 @@ static int sony_pic_possible_resources(struct acpi_device *device)
|
||||
status = acpi_walk_resources(device->handle, METHOD_NAME__PRS,
|
||||
sony_pic_read_possible_resource, &spic_dev);
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_WARNING DRV_PFX
|
||||
"Failure evaluating %s\n",
|
||||
pr_warn(DRV_PFX "Failure evaluating %s\n",
|
||||
METHOD_NAME__PRS);
|
||||
result = -ENODEV;
|
||||
}
|
||||
@ -2669,7 +2969,7 @@ static int sony_pic_enable(struct acpi_device *device,
|
||||
|
||||
/* check for total failure */
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR DRV_PFX "Error evaluating _SRS\n");
|
||||
pr_err(DRV_PFX "Error evaluating _SRS\n");
|
||||
result = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
@ -2725,6 +3025,9 @@ static irqreturn_t sony_pic_irq(int irq, void *dev_id)
|
||||
if (ev == dev->event_types[i].events[j].data) {
|
||||
device_event =
|
||||
dev->event_types[i].events[j].event;
|
||||
/* some events may require ignoring */
|
||||
if (!device_event)
|
||||
return IRQ_HANDLED;
|
||||
goto found;
|
||||
}
|
||||
}
|
||||
@ -2744,7 +3047,6 @@ found:
|
||||
sony_laptop_report_input_event(device_event);
|
||||
acpi_bus_generate_proc_event(dev->acpi_dev, 1, device_event);
|
||||
sonypi_compat_report_event(device_event);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -2759,7 +3061,7 @@ static int sony_pic_remove(struct acpi_device *device, int type)
|
||||
struct sony_pic_irq *irq, *tmp_irq;
|
||||
|
||||
if (sony_pic_disable(device)) {
|
||||
printk(KERN_ERR DRV_PFX "Couldn't disable device.\n");
|
||||
pr_err(DRV_PFX "Couldn't disable device.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
@ -2799,8 +3101,8 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
struct sony_pic_ioport *io, *tmp_io;
|
||||
struct sony_pic_irq *irq, *tmp_irq;
|
||||
|
||||
printk(KERN_INFO DRV_PFX "%s v%s.\n",
|
||||
SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION);
|
||||
pr_info(DRV_PFX "%s v%s.\n", SONY_PIC_DRIVER_NAME,
|
||||
SONY_LAPTOP_DRIVER_VERSION);
|
||||
|
||||
spic_dev.acpi_dev = device;
|
||||
strcpy(acpi_device_class(device), "sony/hotkey");
|
||||
@ -2810,16 +3112,14 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
/* read _PRS resources */
|
||||
result = sony_pic_possible_resources(device);
|
||||
if (result) {
|
||||
printk(KERN_ERR DRV_PFX
|
||||
"Unable to read possible resources.\n");
|
||||
pr_err(DRV_PFX "Unable to read possible resources.\n");
|
||||
goto err_free_resources;
|
||||
}
|
||||
|
||||
/* setup input devices and helper fifo */
|
||||
result = sony_laptop_setup_input(device);
|
||||
if (result) {
|
||||
printk(KERN_ERR DRV_PFX
|
||||
"Unable to create input devices.\n");
|
||||
pr_err(DRV_PFX "Unable to create input devices.\n");
|
||||
goto err_free_resources;
|
||||
}
|
||||
|
||||
@ -2829,7 +3129,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
/* request io port */
|
||||
list_for_each_entry_reverse(io, &spic_dev.ioports, list) {
|
||||
if (request_region(io->io1.minimum, io->io1.address_length,
|
||||
"Sony Programable I/O Device")) {
|
||||
"Sony Programmable I/O Device")) {
|
||||
dprintk("I/O port1: 0x%.4x (0x%.4x) + 0x%.2x\n",
|
||||
io->io1.minimum, io->io1.maximum,
|
||||
io->io1.address_length);
|
||||
@ -2837,7 +3137,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
if (io->io2.minimum) {
|
||||
if (request_region(io->io2.minimum,
|
||||
io->io2.address_length,
|
||||
"Sony Programable I/O Device")) {
|
||||
"Sony Programmable I/O Device")) {
|
||||
dprintk("I/O port2: 0x%.4x (0x%.4x) + 0x%.2x\n",
|
||||
io->io2.minimum, io->io2.maximum,
|
||||
io->io2.address_length);
|
||||
@ -2860,7 +3160,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
}
|
||||
}
|
||||
if (!spic_dev.cur_ioport) {
|
||||
printk(KERN_ERR DRV_PFX "Failed to request_region.\n");
|
||||
pr_err(DRV_PFX "Failed to request_region.\n");
|
||||
result = -ENODEV;
|
||||
goto err_remove_compat;
|
||||
}
|
||||
@ -2880,7 +3180,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
}
|
||||
}
|
||||
if (!spic_dev.cur_irq) {
|
||||
printk(KERN_ERR DRV_PFX "Failed to request_irq.\n");
|
||||
pr_err(DRV_PFX "Failed to request_irq.\n");
|
||||
result = -ENODEV;
|
||||
goto err_release_region;
|
||||
}
|
||||
@ -2888,7 +3188,7 @@ static int sony_pic_add(struct acpi_device *device)
|
||||
/* set resource status _SRS */
|
||||
result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq);
|
||||
if (result) {
|
||||
printk(KERN_ERR DRV_PFX "Couldn't enable device.\n");
|
||||
pr_err(DRV_PFX "Couldn't enable device.\n");
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
@ -2997,8 +3297,7 @@ static int __init sony_laptop_init(void)
|
||||
if (!no_spic && dmi_check_system(sonypi_dmi_table)) {
|
||||
result = acpi_bus_register_driver(&sony_pic_driver);
|
||||
if (result) {
|
||||
printk(KERN_ERR DRV_PFX
|
||||
"Unable to register SPIC driver.");
|
||||
pr_err(DRV_PFX "Unable to register SPIC driver.");
|
||||
goto out;
|
||||
}
|
||||
spic_drv_registered = 1;
|
||||
@ -3006,7 +3305,7 @@ static int __init sony_laptop_init(void)
|
||||
|
||||
result = acpi_bus_register_driver(&sony_nc_driver);
|
||||
if (result) {
|
||||
printk(KERN_ERR DRV_PFX "Unable to register SNC driver.");
|
||||
pr_err(DRV_PFX "Unable to register SNC driver.");
|
||||
goto out_unregister_pic;
|
||||
}
|
||||
|
||||
|
@ -2407,7 +2407,7 @@ static void hotkey_compare_and_issue_event(struct tp_nvram_state *oldn,
|
||||
* This code is supposed to duplicate the IBM firmware behaviour:
|
||||
* - Pressing MUTE issues mute hotkey message, even when already mute
|
||||
* - Pressing Volume up/down issues volume up/down hotkey messages,
|
||||
* even when already at maximum or minumum volume
|
||||
* even when already at maximum or minimum volume
|
||||
* - The act of unmuting issues volume up/down notification,
|
||||
* depending which key was used to unmute
|
||||
*
|
||||
@ -2990,7 +2990,7 @@ static void tpacpi_send_radiosw_update(void)
|
||||
* rfkill input events, or we will race the rfkill core input
|
||||
* handler.
|
||||
*
|
||||
* tpacpi_inputdev_send_mutex works as a syncronization point
|
||||
* tpacpi_inputdev_send_mutex works as a synchronization point
|
||||
* for the above.
|
||||
*
|
||||
* We optimize to avoid numerous calls to hotkey_get_wlsw.
|
||||
|
180
drivers/platform/x86/xo15-ebook.c
Normal file
180
drivers/platform/x86/xo15-ebook.c
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
* OLPC XO-1.5 ebook switch driver
|
||||
* (based on generic ACPI button driver)
|
||||
*
|
||||
* Copyright (C) 2009 Paul Fox <pgf@laptop.org>
|
||||
* Copyright (C) 2010 One Laptop per Child
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
#include <acpi/acpi_bus.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
|
||||
#define MODULE_NAME "xo15-ebook"
|
||||
#define PREFIX MODULE_NAME ": "
|
||||
|
||||
#define XO15_EBOOK_CLASS MODULE_NAME
|
||||
#define XO15_EBOOK_TYPE_UNKNOWN 0x00
|
||||
#define XO15_EBOOK_NOTIFY_STATUS 0x80
|
||||
|
||||
#define XO15_EBOOK_SUBCLASS "ebook"
|
||||
#define XO15_EBOOK_HID "XO15EBK"
|
||||
#define XO15_EBOOK_DEVICE_NAME "EBook Switch"
|
||||
|
||||
ACPI_MODULE_NAME(MODULE_NAME);
|
||||
|
||||
MODULE_DESCRIPTION("OLPC XO-1.5 ebook switch driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
static const struct acpi_device_id ebook_device_ids[] = {
|
||||
{ XO15_EBOOK_HID, 0 },
|
||||
{ "", 0 },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ebook_device_ids);
|
||||
|
||||
struct ebook_switch {
|
||||
struct input_dev *input;
|
||||
char phys[32]; /* for input device */
|
||||
};
|
||||
|
||||
static int ebook_send_state(struct acpi_device *device)
|
||||
{
|
||||
struct ebook_switch *button = acpi_driver_data(device);
|
||||
unsigned long long state;
|
||||
acpi_status status;
|
||||
|
||||
status = acpi_evaluate_integer(device->handle, "EBK", NULL, &state);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -EIO;
|
||||
|
||||
/* input layer checks if event is redundant */
|
||||
input_report_switch(button->input, SW_TABLET_MODE, !state);
|
||||
input_sync(button->input);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ebook_switch_notify(struct acpi_device *device, u32 event)
|
||||
{
|
||||
switch (event) {
|
||||
case ACPI_FIXED_HARDWARE_EVENT:
|
||||
case XO15_EBOOK_NOTIFY_STATUS:
|
||||
ebook_send_state(device);
|
||||
break;
|
||||
default:
|
||||
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
|
||||
"Unsupported event [0x%x]\n", event));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ebook_switch_resume(struct acpi_device *device)
|
||||
{
|
||||
return ebook_send_state(device);
|
||||
}
|
||||
|
||||
static int ebook_switch_add(struct acpi_device *device)
|
||||
{
|
||||
struct ebook_switch *button;
|
||||
struct input_dev *input;
|
||||
const char *hid = acpi_device_hid(device);
|
||||
char *name, *class;
|
||||
int error;
|
||||
|
||||
button = kzalloc(sizeof(struct ebook_switch), GFP_KERNEL);
|
||||
if (!button)
|
||||
return -ENOMEM;
|
||||
|
||||
device->driver_data = button;
|
||||
|
||||
button->input = input = input_allocate_device();
|
||||
if (!input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_button;
|
||||
}
|
||||
|
||||
name = acpi_device_name(device);
|
||||
class = acpi_device_class(device);
|
||||
|
||||
if (strcmp(hid, XO15_EBOOK_HID)) {
|
||||
printk(KERN_ERR PREFIX "Unsupported hid [%s]\n", hid);
|
||||
error = -ENODEV;
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
strcpy(name, XO15_EBOOK_DEVICE_NAME);
|
||||
sprintf(class, "%s/%s", XO15_EBOOK_CLASS, XO15_EBOOK_SUBCLASS);
|
||||
|
||||
snprintf(button->phys, sizeof(button->phys), "%s/button/input0", hid);
|
||||
|
||||
input->name = name;
|
||||
input->phys = button->phys;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &device->dev;
|
||||
|
||||
input->evbit[0] = BIT_MASK(EV_SW);
|
||||
set_bit(SW_TABLET_MODE, input->swbit);
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error)
|
||||
goto err_free_input;
|
||||
|
||||
ebook_send_state(device);
|
||||
|
||||
if (device->wakeup.flags.valid) {
|
||||
/* Button's GPE is run-wake GPE */
|
||||
acpi_enable_gpe(device->wakeup.gpe_device,
|
||||
device->wakeup.gpe_number);
|
||||
device_set_wakeup_enable(&device->dev, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_input:
|
||||
input_free_device(input);
|
||||
err_free_button:
|
||||
kfree(button);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ebook_switch_remove(struct acpi_device *device, int type)
|
||||
{
|
||||
struct ebook_switch *button = acpi_driver_data(device);
|
||||
|
||||
input_unregister_device(button->input);
|
||||
kfree(button);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct acpi_driver xo15_ebook_driver = {
|
||||
.name = MODULE_NAME,
|
||||
.class = XO15_EBOOK_CLASS,
|
||||
.ids = ebook_device_ids,
|
||||
.ops = {
|
||||
.add = ebook_switch_add,
|
||||
.resume = ebook_switch_resume,
|
||||
.remove = ebook_switch_remove,
|
||||
.notify = ebook_switch_notify,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xo15_ebook_init(void)
|
||||
{
|
||||
return acpi_bus_register_driver(&xo15_ebook_driver);
|
||||
}
|
||||
|
||||
static void __exit xo15_ebook_exit(void)
|
||||
{
|
||||
acpi_bus_unregister_driver(&xo15_ebook_driver);
|
||||
}
|
||||
|
||||
module_init(xo15_ebook_init);
|
||||
module_exit(xo15_ebook_exit);
|
@ -664,6 +664,13 @@ struct input_keymap_entry {
|
||||
#define KEY_TOUCHPAD_ON 0x213
|
||||
#define KEY_TOUCHPAD_OFF 0x214
|
||||
|
||||
#define KEY_CAMERA_ZOOMIN 0x215
|
||||
#define KEY_CAMERA_ZOOMOUT 0x216
|
||||
#define KEY_CAMERA_UP 0x217
|
||||
#define KEY_CAMERA_DOWN 0x218
|
||||
#define KEY_CAMERA_LEFT 0x219
|
||||
#define KEY_CAMERA_RIGHT 0x21a
|
||||
|
||||
#define BTN_TRIGGER_HAPPY 0x2c0
|
||||
#define BTN_TRIGGER_HAPPY1 0x2c0
|
||||
#define BTN_TRIGGER_HAPPY2 0x2c1
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
/* events the user application reading /dev/sonypi can use */
|
||||
|
||||
#define SONYPI_EVENT_IGNORE 0
|
||||
#define SONYPI_EVENT_JOGDIAL_DOWN 1
|
||||
#define SONYPI_EVENT_JOGDIAL_UP 2
|
||||
#define SONYPI_EVENT_JOGDIAL_DOWN_PRESSED 3
|
||||
|
Loading…
Reference in New Issue
Block a user