Merge branch 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86

* 'for_linus' of git://cavan.codon.org.uk/platform-drivers-x86: (45 commits)
  acer-wmi: replaced the hard coded bitmap by the communication devices bitmap from SMBIOS
  acer-wmi: check the existence of internal wireless device when set capability
  acer-wmi: add ACER_WMID_v2 interface flag to represent new notebooks
  sony-laptop:irq: Remove IRQF_DISABLED
  asus-laptop: Add rfkill support for Pegatron Lucid tablet
  asus-laptop: pega_accel - Report accelerometer orientation change through udev
  asus-laptop: fix module description
  asus-laptop: hide leds on Pegatron Lucid
  asus-laptop: Pegatron Lucid accelerometer
  asus-laptop: allow boot time control of Pegatron ALS sensor
  Platform: samsung_laptop: add support for X520 machines.
  platform: samsung_laptop: add dmi information for Samsung R700 laptops
  hp_accel: Add axis-mapping for HP ProBook / EliteBook
  hp_accel: Add a new PNP id
  WMI: properly cleanup devices to avoid crashes
  ideapad: remove sysfs node for cfg
  ideapad: add debugfs support
  ideapad: add event for Novo key
  ideapad: change parameter of ideapad_sync_rfk_state
  ideapad: define vpc commands
  ...
This commit is contained in:
Linus Torvalds 2011-10-31 16:10:33 -07:00
commit 91e67a996c
17 changed files with 1454 additions and 647 deletions

View File

@ -0,0 +1,19 @@
What: /sys/kernel/debug/ideapad/cfg
Date: Sep 2011
KernelVersion: 3.2
Contact: Ike Panhc <ike.pan@canonical.com>
Description:
cfg shows the return value of _CFG method in VPC2004 device. It tells machine
capability and what graphic component within the machine.
What: /sys/kernel/debug/ideapad/status
Date: Sep 2011
KernelVersion: 3.2
Contact: Ike Panhc <ike.pan@canonical.com>
Description:
status shows infos we can read and tells its meaning and value.

View File

@ -5,19 +5,4 @@ Contact: "Ike Panhc <ike.pan@canonical.com>"
Description: Description:
Control the power of camera module. 1 means on, 0 means off. Control the power of camera module. 1 means on, 0 means off.
What: /sys/devices/platform/ideapad/cfg
Date: Jun 2011
KernelVersion: 3.1
Contact: "Ike Panhc <ike.pan@canonical.com>"
Description:
Ideapad capability bits.
Bit 8-10: 1 - Intel graphic only
2 - ATI graphic only
3 - Nvidia graphic only
4 - Intel and ATI graphic
5 - Intel and Nvidia graphic
Bit 16: Bluetooth exist (1 for exist)
Bit 17: 3G exist (1 for exist)
Bit 18: Wifi exist (1 for exist)
Bit 19: Camera exist (1 for exist)

View File

@ -3227,6 +3227,13 @@ F: Documentation/ide/
F: drivers/ide/ F: drivers/ide/
F: include/linux/ide.h F: include/linux/ide.h
IDEAPAD LAPTOP EXTRAS DRIVER
M: Ike Panhc <ike.pan@canonical.com>
L: platform-driver-x86@vger.kernel.org
W: http://launchpad.net/ideapad-laptop
S: Maintained
F: drivers/platform/x86/ideapad-laptop.c
IDE/ATAPI DRIVERS IDE/ATAPI DRIVERS
M: Borislav Petkov <petkovbb@gmail.com> M: Borislav Petkov <petkovbb@gmail.com>
L: linux-ide@vger.kernel.org L: linux-ide@vger.kernel.org

View File

@ -61,15 +61,18 @@ config ASUS_LAPTOP
depends on INPUT depends on INPUT
depends on RFKILL || RFKILL = n depends on RFKILL || RFKILL = n
select INPUT_SPARSEKMAP select INPUT_SPARSEKMAP
select INPUT_POLLDEV
---help--- ---help---
This is the new Linux driver for Asus laptops. It may also support some This is a driver for Asus laptops, Lenovo SL and the Pegatron
MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate Lucid tablet. It may also support some MEDION, JVC or VICTOR
standard ACPI events and input events. It also adds laptops. It makes all the extra buttons generate standard
support for video output switching, LCD backlight control, Bluetooth and ACPI events and input events, and on the Lucid the built-in
Wlan control, and most importantly, allows you to blink those fancy LEDs. accelerometer appears as an input device. It also adds
support for video output switching, LCD backlight control,
Bluetooth and Wlan control, and most importantly, allows you
to blink those fancy LEDs.
For more information and a userspace daemon for handling the extra For more information see <http://acpi4asus.sf.net>.
buttons see <http://acpi4asus.sf.net>.
If you have an ACPI-compatible ASUS laptop, say Y or M here. If you have an ACPI-compatible ASUS laptop, say Y or M here.

View File

@ -190,6 +190,7 @@ enum interface_flags {
ACER_AMW0, ACER_AMW0,
ACER_AMW0_V2, ACER_AMW0_V2,
ACER_WMID, ACER_WMID,
ACER_WMID_v2,
}; };
#define ACER_DEFAULT_WIRELESS 0 #define ACER_DEFAULT_WIRELESS 0
@ -205,6 +206,7 @@ static int threeg = -1;
static int force_series; static int force_series;
static bool ec_raw_mode; static bool ec_raw_mode;
static bool has_type_aa; static bool has_type_aa;
static u16 commun_func_bitmap;
module_param(mailled, int, 0444); module_param(mailled, int, 0444);
module_param(brightness, int, 0444); module_param(brightness, int, 0444);
@ -464,6 +466,15 @@ static struct dmi_system_id acer_quirks[] = {
}, },
.driver_data = &quirk_lenovo_ideapad_s205, .driver_data = &quirk_lenovo_ideapad_s205,
}, },
{
.callback = dmi_matched,
.ident = "Lenovo 3000 N200",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "0687A31"),
},
.driver_data = &quirk_fujitsu_amilo_li_1718,
},
{} {}
}; };
@ -868,6 +879,174 @@ static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface)
return WMI_execute_u32(method_id, (u32)value, NULL); return WMI_execute_u32(method_id, (u32)value, NULL);
} }
static acpi_status wmid3_get_device_status(u32 *value, u16 device)
{
struct wmid3_gds_return_value return_value;
acpi_status status;
union acpi_object *obj;
struct wmid3_gds_input_param params = {
.function_num = 0x1,
.hotkey_number = 0x01,
.devices = device,
};
struct acpi_buffer input = {
sizeof(struct wmid3_gds_input_param),
&params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
if (ACPI_FAILURE(status))
return status;
obj = output.pointer;
if (!obj)
return AE_ERROR;
else if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj);
return AE_ERROR;
}
if (obj->buffer.length != 8) {
pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
kfree(obj);
if (return_value.error_code || return_value.ec_return_value)
pr_warn("Get 0x%x Device Status failed: 0x%x - 0x%x\n",
device,
return_value.error_code,
return_value.ec_return_value);
else
*value = !!(return_value.devices & device);
return status;
}
static acpi_status wmid_v2_get_u32(u32 *value, u32 cap)
{
u16 device;
switch (cap) {
case ACER_CAP_WIRELESS:
device = ACER_WMID3_GDS_WIRELESS;
break;
case ACER_CAP_BLUETOOTH:
device = ACER_WMID3_GDS_BLUETOOTH;
break;
case ACER_CAP_THREEG:
device = ACER_WMID3_GDS_THREEG;
break;
default:
return AE_ERROR;
}
return wmid3_get_device_status(value, device);
}
static acpi_status wmid3_set_device_status(u32 value, u16 device)
{
struct wmid3_gds_return_value return_value;
acpi_status status;
union acpi_object *obj;
u16 devices;
struct wmid3_gds_input_param params = {
.function_num = 0x1,
.hotkey_number = 0x01,
.devices = commun_func_bitmap,
};
struct acpi_buffer input = {
sizeof(struct wmid3_gds_input_param),
&params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
if (ACPI_FAILURE(status))
return status;
obj = output.pointer;
if (!obj)
return AE_ERROR;
else if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj);
return AE_ERROR;
}
if (obj->buffer.length != 8) {
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
kfree(obj);
if (return_value.error_code || return_value.ec_return_value) {
pr_warning("Get Current Device Status failed: "
"0x%x - 0x%x\n", return_value.error_code,
return_value.ec_return_value);
return status;
}
devices = return_value.devices;
params.function_num = 0x2;
params.hotkey_number = 0x01;
params.devices = (value) ? (devices | device) : (devices & ~device);
status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
if (ACPI_FAILURE(status))
return status;
obj = output2.pointer;
if (!obj)
return AE_ERROR;
else if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj);
return AE_ERROR;
}
if (obj->buffer.length != 4) {
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
kfree(obj);
if (return_value.error_code || return_value.ec_return_value)
pr_warning("Set Device Status failed: "
"0x%x - 0x%x\n", return_value.error_code,
return_value.ec_return_value);
return status;
}
static acpi_status wmid_v2_set_u32(u32 value, u32 cap)
{
u16 device;
switch (cap) {
case ACER_CAP_WIRELESS:
device = ACER_WMID3_GDS_WIRELESS;
break;
case ACER_CAP_BLUETOOTH:
device = ACER_WMID3_GDS_BLUETOOTH;
break;
case ACER_CAP_THREEG:
device = ACER_WMID3_GDS_THREEG;
break;
default:
return AE_ERROR;
}
return wmid3_set_device_status(value, device);
}
static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
{ {
struct hotkey_function_type_aa *type_aa; struct hotkey_function_type_aa *type_aa;
@ -881,6 +1060,7 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy)
pr_info("Function bitmap for Communication Button: 0x%x\n", pr_info("Function bitmap for Communication Button: 0x%x\n",
type_aa->commun_func_bitmap); type_aa->commun_func_bitmap);
commun_func_bitmap = type_aa->commun_func_bitmap;
if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS) if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_WIRELESS)
interface->capability |= ACER_CAP_WIRELESS; interface->capability |= ACER_CAP_WIRELESS;
@ -913,17 +1093,13 @@ static acpi_status WMID_set_capabilities(void)
return AE_ERROR; return AE_ERROR;
} }
dmi_walk(type_aa_dmi_decode, NULL); pr_info("Function bitmap for Communication Device: 0x%x\n", devices);
if (!has_type_aa) { if (devices & 0x07)
interface->capability |= ACER_CAP_WIRELESS; interface->capability |= ACER_CAP_WIRELESS;
if (devices & 0x40) if (devices & 0x40)
interface->capability |= ACER_CAP_THREEG; interface->capability |= ACER_CAP_THREEG;
if (devices & 0x10) if (devices & 0x10)
interface->capability |= ACER_CAP_BLUETOOTH; interface->capability |= ACER_CAP_BLUETOOTH;
}
/* WMID always provides brightness methods */
interface->capability |= ACER_CAP_BRIGHTNESS;
if (!(devices & 0x20)) if (!(devices & 0x20))
max_brightness = 0x9; max_brightness = 0x9;
@ -936,6 +1112,10 @@ static struct wmi_interface wmid_interface = {
.type = ACER_WMID, .type = ACER_WMID,
}; };
static struct wmi_interface wmid_v2_interface = {
.type = ACER_WMID_v2,
};
/* /*
* Generic Device (interface-independent) * Generic Device (interface-independent)
*/ */
@ -956,6 +1136,14 @@ static acpi_status get_u32(u32 *value, u32 cap)
case ACER_WMID: case ACER_WMID:
status = WMID_get_u32(value, cap, interface); status = WMID_get_u32(value, cap, interface);
break; break;
case ACER_WMID_v2:
if (cap & (ACER_CAP_WIRELESS |
ACER_CAP_BLUETOOTH |
ACER_CAP_THREEG))
status = wmid_v2_get_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
status = WMID_get_u32(value, cap, interface);
break;
} }
return status; return status;
@ -989,6 +1177,13 @@ static acpi_status set_u32(u32 value, u32 cap)
} }
case ACER_WMID: case ACER_WMID:
return WMID_set_u32(value, cap, interface); return WMID_set_u32(value, cap, interface);
case ACER_WMID_v2:
if (cap & (ACER_CAP_WIRELESS |
ACER_CAP_BLUETOOTH |
ACER_CAP_THREEG))
return wmid_v2_set_u32(value, cap);
else if (wmi_has_guid(WMID_GUID2))
return WMID_set_u32(value, cap, interface);
default: default:
return AE_BAD_PARAMETER; return AE_BAD_PARAMETER;
} }
@ -1095,186 +1290,6 @@ static void acer_backlight_exit(void)
backlight_device_unregister(acer_backlight_device); backlight_device_unregister(acer_backlight_device);
} }
static acpi_status wmid3_get_device_status(u32 *value, u16 device)
{
struct wmid3_gds_return_value return_value;
acpi_status status;
union acpi_object *obj;
struct wmid3_gds_input_param params = {
.function_num = 0x1,
.hotkey_number = 0x01,
.devices = device,
};
struct acpi_buffer input = {
sizeof(struct wmid3_gds_input_param),
&params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
if (ACPI_FAILURE(status))
return status;
obj = output.pointer;
if (!obj)
return AE_ERROR;
else if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj);
return AE_ERROR;
}
if (obj->buffer.length != 8) {
pr_warn("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
kfree(obj);
if (return_value.error_code || return_value.ec_return_value)
pr_warn("Get Device Status failed: 0x%x - 0x%x\n",
return_value.error_code,
return_value.ec_return_value);
else
*value = !!(return_value.devices & device);
return status;
}
static acpi_status get_device_status(u32 *value, u32 cap)
{
if (wmi_has_guid(WMID_GUID3)) {
u16 device;
switch (cap) {
case ACER_CAP_WIRELESS:
device = ACER_WMID3_GDS_WIRELESS;
break;
case ACER_CAP_BLUETOOTH:
device = ACER_WMID3_GDS_BLUETOOTH;
break;
case ACER_CAP_THREEG:
device = ACER_WMID3_GDS_THREEG;
break;
default:
return AE_ERROR;
}
return wmid3_get_device_status(value, device);
} else {
return get_u32(value, cap);
}
}
static acpi_status wmid3_set_device_status(u32 value, u16 device)
{
struct wmid3_gds_return_value return_value;
acpi_status status;
union acpi_object *obj;
u16 devices;
struct wmid3_gds_input_param params = {
.function_num = 0x1,
.hotkey_number = 0x01,
.devices = ACER_WMID3_GDS_WIRELESS |
ACER_WMID3_GDS_THREEG |
ACER_WMID3_GDS_WIMAX |
ACER_WMID3_GDS_BLUETOOTH,
};
struct acpi_buffer input = {
sizeof(struct wmid3_gds_input_param),
&params
};
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_buffer output2 = { ACPI_ALLOCATE_BUFFER, NULL };
status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &input, &output);
if (ACPI_FAILURE(status))
return status;
obj = output.pointer;
if (!obj)
return AE_ERROR;
else if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj);
return AE_ERROR;
}
if (obj->buffer.length != 8) {
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
kfree(obj);
if (return_value.error_code || return_value.ec_return_value) {
pr_warning("Get Current Device Status failed: "
"0x%x - 0x%x\n", return_value.error_code,
return_value.ec_return_value);
return status;
}
devices = return_value.devices;
params.function_num = 0x2;
params.hotkey_number = 0x01;
params.devices = (value) ? (devices | device) : (devices & ~device);
status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2);
if (ACPI_FAILURE(status))
return status;
obj = output2.pointer;
if (!obj)
return AE_ERROR;
else if (obj->type != ACPI_TYPE_BUFFER) {
kfree(obj);
return AE_ERROR;
}
if (obj->buffer.length != 4) {
pr_warning("Unknown buffer length %d\n", obj->buffer.length);
kfree(obj);
return AE_ERROR;
}
return_value = *((struct wmid3_gds_return_value *)obj->buffer.pointer);
kfree(obj);
if (return_value.error_code || return_value.ec_return_value)
pr_warning("Set Device Status failed: "
"0x%x - 0x%x\n", return_value.error_code,
return_value.ec_return_value);
return status;
}
static acpi_status set_device_status(u32 value, u32 cap)
{
if (wmi_has_guid(WMID_GUID3)) {
u16 device;
switch (cap) {
case ACER_CAP_WIRELESS:
device = ACER_WMID3_GDS_WIRELESS;
break;
case ACER_CAP_BLUETOOTH:
device = ACER_WMID3_GDS_BLUETOOTH;
break;
case ACER_CAP_THREEG:
device = ACER_WMID3_GDS_THREEG;
break;
default:
return AE_ERROR;
}
return wmid3_set_device_status(value, device);
} else {
return set_u32(value, cap);
}
}
/* /*
* Rfkill devices * Rfkill devices
*/ */
@ -1285,12 +1300,13 @@ static void acer_rfkill_update(struct work_struct *ignored)
u32 state; u32 state;
acpi_status status; acpi_status status;
status = get_u32(&state, ACER_CAP_WIRELESS); if (has_cap(ACER_CAP_WIRELESS)) {
if (ACPI_SUCCESS(status)) { status = get_u32(&state, ACER_CAP_WIRELESS);
if (quirks->wireless == 3) { if (ACPI_SUCCESS(status)) {
rfkill_set_hw_state(wireless_rfkill, !state); if (quirks->wireless == 3)
} else { rfkill_set_hw_state(wireless_rfkill, !state);
rfkill_set_sw_state(wireless_rfkill, !state); else
rfkill_set_sw_state(wireless_rfkill, !state);
} }
} }
@ -1301,8 +1317,7 @@ static void acer_rfkill_update(struct work_struct *ignored)
} }
if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) { if (has_cap(ACER_CAP_THREEG) && wmi_has_guid(WMID_GUID3)) {
status = wmid3_get_device_status(&state, status = get_u32(&state, ACER_WMID3_GDS_THREEG);
ACER_WMID3_GDS_THREEG);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
rfkill_set_sw_state(threeg_rfkill, !state); rfkill_set_sw_state(threeg_rfkill, !state);
} }
@ -1316,7 +1331,7 @@ static int acer_rfkill_set(void *data, bool blocked)
u32 cap = (unsigned long)data; u32 cap = (unsigned long)data;
if (rfkill_inited) { if (rfkill_inited) {
status = set_device_status(!blocked, cap); status = set_u32(!blocked, cap);
if (ACPI_FAILURE(status)) if (ACPI_FAILURE(status))
return -ENODEV; return -ENODEV;
} }
@ -1343,7 +1358,7 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
if (!rfkill_dev) if (!rfkill_dev)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
status = get_device_status(&state, cap); status = get_u32(&state, cap);
err = rfkill_register(rfkill_dev); err = rfkill_register(rfkill_dev);
if (err) { if (err) {
@ -1359,19 +1374,24 @@ static struct rfkill *acer_rfkill_register(struct device *dev,
static int acer_rfkill_init(struct device *dev) static int acer_rfkill_init(struct device *dev)
{ {
wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN, int err;
"acer-wireless", ACER_CAP_WIRELESS);
if (IS_ERR(wireless_rfkill)) if (has_cap(ACER_CAP_WIRELESS)) {
return PTR_ERR(wireless_rfkill); wireless_rfkill = acer_rfkill_register(dev, RFKILL_TYPE_WLAN,
"acer-wireless", ACER_CAP_WIRELESS);
if (IS_ERR(wireless_rfkill)) {
err = PTR_ERR(wireless_rfkill);
goto error_wireless;
}
}
if (has_cap(ACER_CAP_BLUETOOTH)) { if (has_cap(ACER_CAP_BLUETOOTH)) {
bluetooth_rfkill = acer_rfkill_register(dev, bluetooth_rfkill = acer_rfkill_register(dev,
RFKILL_TYPE_BLUETOOTH, "acer-bluetooth", RFKILL_TYPE_BLUETOOTH, "acer-bluetooth",
ACER_CAP_BLUETOOTH); ACER_CAP_BLUETOOTH);
if (IS_ERR(bluetooth_rfkill)) { if (IS_ERR(bluetooth_rfkill)) {
rfkill_unregister(wireless_rfkill); err = PTR_ERR(bluetooth_rfkill);
rfkill_destroy(wireless_rfkill); goto error_bluetooth;
return PTR_ERR(bluetooth_rfkill);
} }
} }
@ -1380,30 +1400,44 @@ static int acer_rfkill_init(struct device *dev)
RFKILL_TYPE_WWAN, "acer-threeg", RFKILL_TYPE_WWAN, "acer-threeg",
ACER_CAP_THREEG); ACER_CAP_THREEG);
if (IS_ERR(threeg_rfkill)) { if (IS_ERR(threeg_rfkill)) {
rfkill_unregister(wireless_rfkill); err = PTR_ERR(threeg_rfkill);
rfkill_destroy(wireless_rfkill); goto error_threeg;
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
return PTR_ERR(threeg_rfkill);
} }
} }
rfkill_inited = true; rfkill_inited = true;
if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
schedule_delayed_work(&acer_rfkill_work, schedule_delayed_work(&acer_rfkill_work,
round_jiffies_relative(HZ)); round_jiffies_relative(HZ));
return 0; return 0;
error_threeg:
if (has_cap(ACER_CAP_BLUETOOTH)) {
rfkill_unregister(bluetooth_rfkill);
rfkill_destroy(bluetooth_rfkill);
}
error_bluetooth:
if (has_cap(ACER_CAP_WIRELESS)) {
rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
}
error_wireless:
return err;
} }
static void acer_rfkill_exit(void) static void acer_rfkill_exit(void)
{ {
if (ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) if ((ec_raw_mode || !wmi_has_guid(ACERWMID_EVENT_GUID)) &&
has_cap(ACER_CAP_WIRELESS | ACER_CAP_BLUETOOTH | ACER_CAP_THREEG))
cancel_delayed_work_sync(&acer_rfkill_work); cancel_delayed_work_sync(&acer_rfkill_work);
rfkill_unregister(wireless_rfkill); if (has_cap(ACER_CAP_WIRELESS)) {
rfkill_destroy(wireless_rfkill); rfkill_unregister(wireless_rfkill);
rfkill_destroy(wireless_rfkill);
}
if (has_cap(ACER_CAP_BLUETOOTH)) { if (has_cap(ACER_CAP_BLUETOOTH)) {
rfkill_unregister(bluetooth_rfkill); rfkill_unregister(bluetooth_rfkill);
@ -1428,11 +1462,7 @@ static ssize_t show_bool_threeg(struct device *dev,
pr_info("This threeg sysfs will be removed in 2012" pr_info("This threeg sysfs will be removed in 2012"
" - used by: %s\n", current->comm); " - used by: %s\n", current->comm);
if (wmi_has_guid(WMID_GUID3)) status = get_u32(&result, ACER_CAP_THREEG);
status = wmid3_get_device_status(&result,
ACER_WMID3_GDS_THREEG);
else
status = get_u32(&result, ACER_CAP_THREEG);
if (ACPI_SUCCESS(status)) if (ACPI_SUCCESS(status))
return sprintf(buf, "%u\n", result); return sprintf(buf, "%u\n", result);
return sprintf(buf, "Read error\n"); return sprintf(buf, "Read error\n");
@ -1464,6 +1494,8 @@ static ssize_t show_interface(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "AMW0 v2\n"); return sprintf(buf, "AMW0 v2\n");
case ACER_WMID: case ACER_WMID:
return sprintf(buf, "WMID\n"); return sprintf(buf, "WMID\n");
case ACER_WMID_v2:
return sprintf(buf, "WMID v2\n");
default: default:
return sprintf(buf, "Error!\n"); return sprintf(buf, "Error!\n");
} }
@ -1883,12 +1915,20 @@ static int __init acer_wmi_init(void)
if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1)) if (!wmi_has_guid(AMW0_GUID1) && wmi_has_guid(WMID_GUID1))
interface = &wmid_interface; interface = &wmid_interface;
if (wmi_has_guid(WMID_GUID3))
interface = &wmid_v2_interface;
if (interface)
dmi_walk(type_aa_dmi_decode, NULL);
if (wmi_has_guid(WMID_GUID2) && interface) { if (wmi_has_guid(WMID_GUID2) && interface) {
if (ACPI_FAILURE(WMID_set_capabilities())) { if (!has_type_aa && ACPI_FAILURE(WMID_set_capabilities())) {
pr_err("Unable to detect available WMID devices\n"); pr_err("Unable to detect available WMID devices\n");
return -ENODEV; return -ENODEV;
} }
} else if (!wmi_has_guid(WMID_GUID2) && interface) { /* WMID always provides brightness methods */
interface->capability |= ACER_CAP_BRIGHTNESS;
} else if (!wmi_has_guid(WMID_GUID2) && interface && !has_type_aa) {
pr_err("No WMID device detection method found\n"); pr_err("No WMID device detection method found\n");
return -ENODEV; return -ENODEV;
} }
@ -1912,7 +1952,7 @@ static int __init acer_wmi_init(void)
set_quirks(); set_quirks();
if (acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { if (acpi_video_backlight_support()) {
interface->capability &= ~ACER_CAP_BRIGHTNESS; interface->capability &= ~ACER_CAP_BRIGHTNESS;
pr_info("Brightness must be controlled by " pr_info("Brightness must be controlled by "
"generic video driver\n"); "generic video driver\n");

View File

@ -4,6 +4,7 @@
* *
* Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
* Copyright (C) 2006-2007 Corentin Chary * Copyright (C) 2006-2007 Corentin Chary
* Copyright (C) 2011 Wind River Systems
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@ -48,6 +49,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/input.h> #include <linux/input.h>
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/input-polldev.h>
#include <linux/rfkill.h> #include <linux/rfkill.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/dmi.h> #include <linux/dmi.h>
@ -83,26 +85,32 @@ static int wlan_status = 1;
static int bluetooth_status = 1; static int bluetooth_status = 1;
static int wimax_status = -1; static int wimax_status = -1;
static int wwan_status = -1; static int wwan_status = -1;
static int als_status;
module_param(wlan_status, int, 0444); module_param(wlan_status, int, 0444);
MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot " MODULE_PARM_DESC(wlan_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). " "(0 = disabled, 1 = enabled, -1 = don't do anything). "
"default is 1"); "default is -1");
module_param(bluetooth_status, int, 0444); module_param(bluetooth_status, int, 0444);
MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot " MODULE_PARM_DESC(bluetooth_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). " "(0 = disabled, 1 = enabled, -1 = don't do anything). "
"default is 1"); "default is -1");
module_param(wimax_status, int, 0444); module_param(wimax_status, int, 0444);
MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot " MODULE_PARM_DESC(wimax_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). " "(0 = disabled, 1 = enabled, -1 = don't do anything). "
"default is 1"); "default is -1");
module_param(wwan_status, int, 0444); module_param(wwan_status, int, 0444);
MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot " MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
"(0 = disabled, 1 = enabled, -1 = don't do anything). " "(0 = disabled, 1 = enabled, -1 = don't do anything). "
"default is 1"); "default is -1");
module_param(als_status, int, 0444);
MODULE_PARM_DESC(als_status, "Set the ALS status on boot "
"(0 = disabled, 1 = enabled). "
"default is 0");
/* /*
* Some events we use, same for all Asus * Some events we use, same for all Asus
@ -173,6 +181,29 @@ MODULE_PARM_DESC(wwan_status, "Set the wireless status on boot "
#define METHOD_KBD_LIGHT_SET "SLKB" #define METHOD_KBD_LIGHT_SET "SLKB"
#define METHOD_KBD_LIGHT_GET "GLKB" #define METHOD_KBD_LIGHT_GET "GLKB"
/* For Pegatron Lucid tablet */
#define DEVICE_NAME_PEGA "Lucid"
#define METHOD_PEGA_ENABLE "ENPR"
#define METHOD_PEGA_DISABLE "DAPR"
#define PEGA_WLAN 0x00
#define PEGA_BLUETOOTH 0x01
#define PEGA_WWAN 0x02
#define PEGA_ALS 0x04
#define PEGA_ALS_POWER 0x05
#define METHOD_PEGA_READ "RDLN"
#define PEGA_READ_ALS_H 0x02
#define PEGA_READ_ALS_L 0x03
#define PEGA_ACCEL_NAME "pega_accel"
#define PEGA_ACCEL_DESC "Pegatron Lucid Tablet Accelerometer"
#define METHOD_XLRX "XLRX"
#define METHOD_XLRY "XLRY"
#define METHOD_XLRZ "XLRZ"
#define PEGA_ACC_CLAMP 512 /* 1G accel is reported as ~256, so clamp to 2G */
#define PEGA_ACC_RETRIES 3
/* /*
* Define a specific led structure to keep the main structure clean * Define a specific led structure to keep the main structure clean
*/ */
@ -184,6 +215,15 @@ struct asus_led {
const char *method; const char *method;
}; };
/*
* Same thing for rfkill
*/
struct asus_pega_rfkill {
int control_id; /* type of control. Maps to PEGA_* values */
struct rfkill *rfkill;
struct asus_laptop *asus;
};
/* /*
* This is the main structure, we can use it to store anything interesting * This is the main structure, we can use it to store anything interesting
* about the hotk device * about the hotk device
@ -198,6 +238,7 @@ struct asus_laptop {
struct input_dev *inputdev; struct input_dev *inputdev;
struct key_entry *keymap; struct key_entry *keymap;
struct input_polled_dev *pega_accel_poll;
struct asus_led mled; struct asus_led mled;
struct asus_led tled; struct asus_led tled;
@ -209,9 +250,18 @@ struct asus_laptop {
int wireless_status; int wireless_status;
bool have_rsts; bool have_rsts;
bool is_pega_lucid;
bool pega_acc_live;
int pega_acc_x;
int pega_acc_y;
int pega_acc_z;
struct rfkill *gps_rfkill; struct rfkill *gps_rfkill;
struct asus_pega_rfkill wlanrfk;
struct asus_pega_rfkill btrfk;
struct asus_pega_rfkill wwanrfk;
acpi_handle handle; /* the handle of the hotk device */ acpi_handle handle; /* the handle of the hotk device */
u32 ledd_status; /* status of the LED display */ u32 ledd_status; /* status of the LED display */
u8 light_level; /* light sensor level */ u8 light_level; /* light sensor level */
@ -323,6 +373,127 @@ static int acpi_check_handle(acpi_handle handle, const char *method,
return 0; return 0;
} }
static bool asus_check_pega_lucid(struct asus_laptop *asus)
{
return !strcmp(asus->name, DEVICE_NAME_PEGA) &&
!acpi_check_handle(asus->handle, METHOD_PEGA_ENABLE, NULL) &&
!acpi_check_handle(asus->handle, METHOD_PEGA_DISABLE, NULL) &&
!acpi_check_handle(asus->handle, METHOD_PEGA_READ, NULL);
}
static int asus_pega_lucid_set(struct asus_laptop *asus, int unit, bool enable)
{
char *method = enable ? METHOD_PEGA_ENABLE : METHOD_PEGA_DISABLE;
return write_acpi_int(asus->handle, method, unit);
}
static int pega_acc_axis(struct asus_laptop *asus, int curr, char *method)
{
int i, delta;
unsigned long long val;
for (i = 0; i < PEGA_ACC_RETRIES; i++) {
acpi_evaluate_integer(asus->handle, method, NULL, &val);
/* The output is noisy. From reading the ASL
* dissassembly, timeout errors are returned with 1's
* in the high word, and the lack of locking around
* thei hi/lo byte reads means that a transition
* between (for example) -1 and 0 could be read as
* 0xff00 or 0x00ff. */
delta = abs(curr - (short)val);
if (delta < 128 && !(val & ~0xffff))
break;
}
return clamp_val((short)val, -PEGA_ACC_CLAMP, PEGA_ACC_CLAMP);
}
static void pega_accel_poll(struct input_polled_dev *ipd)
{
struct device *parent = ipd->input->dev.parent;
struct asus_laptop *asus = dev_get_drvdata(parent);
/* In some cases, the very first call to poll causes a
* recursive fault under the polldev worker. This is
* apparently related to very early userspace access to the
* device, and perhaps a firmware bug. Fake the first report. */
if (!asus->pega_acc_live) {
asus->pega_acc_live = true;
input_report_abs(ipd->input, ABS_X, 0);
input_report_abs(ipd->input, ABS_Y, 0);
input_report_abs(ipd->input, ABS_Z, 0);
input_sync(ipd->input);
return;
}
asus->pega_acc_x = pega_acc_axis(asus, asus->pega_acc_x, METHOD_XLRX);
asus->pega_acc_y = pega_acc_axis(asus, asus->pega_acc_y, METHOD_XLRY);
asus->pega_acc_z = pega_acc_axis(asus, asus->pega_acc_z, METHOD_XLRZ);
/* Note transform, convert to "right/up/out" in the native
* landscape orientation (i.e. the vector is the direction of
* "real up" in the device's cartiesian coordinates). */
input_report_abs(ipd->input, ABS_X, -asus->pega_acc_x);
input_report_abs(ipd->input, ABS_Y, -asus->pega_acc_y);
input_report_abs(ipd->input, ABS_Z, asus->pega_acc_z);
input_sync(ipd->input);
}
static void pega_accel_exit(struct asus_laptop *asus)
{
if (asus->pega_accel_poll) {
input_unregister_polled_device(asus->pega_accel_poll);
input_free_polled_device(asus->pega_accel_poll);
}
asus->pega_accel_poll = NULL;
}
static int pega_accel_init(struct asus_laptop *asus)
{
int err;
struct input_polled_dev *ipd;
if (!asus->is_pega_lucid)
return -ENODEV;
if (acpi_check_handle(asus->handle, METHOD_XLRX, NULL) ||
acpi_check_handle(asus->handle, METHOD_XLRY, NULL) ||
acpi_check_handle(asus->handle, METHOD_XLRZ, NULL))
return -ENODEV;
ipd = input_allocate_polled_device();
if (!ipd)
return -ENOMEM;
ipd->poll = pega_accel_poll;
ipd->poll_interval = 125;
ipd->poll_interval_min = 50;
ipd->poll_interval_max = 2000;
ipd->input->name = PEGA_ACCEL_DESC;
ipd->input->phys = PEGA_ACCEL_NAME "/input0";
ipd->input->dev.parent = &asus->platform_device->dev;
ipd->input->id.bustype = BUS_HOST;
set_bit(EV_ABS, ipd->input->evbit);
input_set_abs_params(ipd->input, ABS_X,
-PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
input_set_abs_params(ipd->input, ABS_Y,
-PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
input_set_abs_params(ipd->input, ABS_Z,
-PEGA_ACC_CLAMP, PEGA_ACC_CLAMP, 0, 0);
err = input_register_polled_device(ipd);
if (err)
goto exit;
asus->pega_accel_poll = ipd;
return 0;
exit:
input_free_polled_device(ipd);
return err;
}
/* Generic LED function */ /* Generic LED function */
static int asus_led_set(struct asus_laptop *asus, const char *method, static int asus_led_set(struct asus_laptop *asus, const char *method,
int value) int value)
@ -430,17 +601,17 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev)
static void asus_led_exit(struct asus_laptop *asus) static void asus_led_exit(struct asus_laptop *asus)
{ {
if (asus->mled.led.dev) if (!IS_ERR_OR_NULL(asus->mled.led.dev))
led_classdev_unregister(&asus->mled.led); led_classdev_unregister(&asus->mled.led);
if (asus->tled.led.dev) if (!IS_ERR_OR_NULL(asus->tled.led.dev))
led_classdev_unregister(&asus->tled.led); led_classdev_unregister(&asus->tled.led);
if (asus->pled.led.dev) if (!IS_ERR_OR_NULL(asus->pled.led.dev))
led_classdev_unregister(&asus->pled.led); led_classdev_unregister(&asus->pled.led);
if (asus->rled.led.dev) if (!IS_ERR_OR_NULL(asus->rled.led.dev))
led_classdev_unregister(&asus->rled.led); led_classdev_unregister(&asus->rled.led);
if (asus->gled.led.dev) if (!IS_ERR_OR_NULL(asus->gled.led.dev))
led_classdev_unregister(&asus->gled.led); led_classdev_unregister(&asus->gled.led);
if (asus->kled.led.dev) if (!IS_ERR_OR_NULL(asus->kled.led.dev))
led_classdev_unregister(&asus->kled.led); led_classdev_unregister(&asus->kled.led);
if (asus->led_workqueue) { if (asus->led_workqueue) {
destroy_workqueue(asus->led_workqueue); destroy_workqueue(asus->led_workqueue);
@ -473,6 +644,13 @@ static int asus_led_init(struct asus_laptop *asus)
{ {
int r; int r;
/*
* The Pegatron Lucid has no physical leds, but all methods are
* available in the DSDT...
*/
if (asus->is_pega_lucid)
return 0;
/* /*
* Functions that actually update the LED's are called from a * Functions that actually update the LED's are called from a
* workqueue. By doing this as separate work rather than when the LED * workqueue. By doing this as separate work rather than when the LED
@ -907,8 +1085,18 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr,
*/ */
static void asus_als_switch(struct asus_laptop *asus, int value) static void asus_als_switch(struct asus_laptop *asus, int value)
{ {
if (write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value)) int ret;
pr_warn("Error setting light sensor switch\n");
if (asus->is_pega_lucid) {
ret = asus_pega_lucid_set(asus, PEGA_ALS, value);
if (!ret)
ret = asus_pega_lucid_set(asus, PEGA_ALS_POWER, value);
} else {
ret = write_acpi_int(asus->handle, METHOD_ALS_CONTROL, value);
}
if (ret)
pr_warning("Error setting light sensor switch\n");
asus->light_switch = value; asus->light_switch = value;
} }
@ -964,6 +1152,35 @@ static ssize_t store_lslvl(struct device *dev, struct device_attribute *attr,
return rv; return rv;
} }
static int pega_int_read(struct asus_laptop *asus, int arg, int *result)
{
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
int err = write_acpi_int_ret(asus->handle, METHOD_PEGA_READ, arg,
&buffer);
if (!err) {
union acpi_object *obj = buffer.pointer;
if (obj && obj->type == ACPI_TYPE_INTEGER)
*result = obj->integer.value;
else
err = -EIO;
}
return err;
}
static ssize_t show_lsvalue(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct asus_laptop *asus = dev_get_drvdata(dev);
int err, hi, lo;
err = pega_int_read(asus, PEGA_READ_ALS_H, &hi);
if (!err)
err = pega_int_read(asus, PEGA_READ_ALS_L, &lo);
if (!err)
return sprintf(buf, "%d\n", 10 * hi + lo);
return err;
}
/* /*
* GPS * GPS
*/ */
@ -1062,6 +1279,86 @@ static int asus_rfkill_init(struct asus_laptop *asus)
return result; return result;
} }
static int pega_rfkill_set(void *data, bool blocked)
{
struct asus_pega_rfkill *pega_rfk = data;
int ret = asus_pega_lucid_set(pega_rfk->asus, pega_rfk->control_id, !blocked);
pr_warn("Setting rfkill %d, to %d; returned %d\n", pega_rfk->control_id, !blocked, ret);
return ret;
}
static const struct rfkill_ops pega_rfkill_ops = {
.set_block = pega_rfkill_set,
};
static void pega_rfkill_terminate(struct asus_pega_rfkill *pega_rfk)
{
pr_warn("Terminating %d\n", pega_rfk->control_id);
if (pega_rfk->rfkill) {
rfkill_unregister(pega_rfk->rfkill);
rfkill_destroy(pega_rfk->rfkill);
pega_rfk->rfkill = NULL;
}
}
static void pega_rfkill_exit(struct asus_laptop *asus)
{
pega_rfkill_terminate(&asus->wwanrfk);
pega_rfkill_terminate(&asus->btrfk);
pega_rfkill_terminate(&asus->wlanrfk);
}
static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_pega_rfkill *pega_rfk,
const char *name, int controlid, int rfkill_type)
{
int result;
pr_warn("Setting up rfk %s, control %d, type %d\n", name, controlid, rfkill_type);
pega_rfk->control_id = controlid;
pega_rfk->asus = asus;
pega_rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev,
rfkill_type, &pega_rfkill_ops, pega_rfk);
if (!pega_rfk->rfkill)
return -EINVAL;
result = rfkill_register(pega_rfk->rfkill);
if (result) {
rfkill_destroy(pega_rfk->rfkill);
pega_rfk->rfkill = NULL;
}
return result;
}
static int pega_rfkill_init(struct asus_laptop *asus)
{
int ret = 0;
if(!asus->is_pega_lucid)
return -ENODEV;
ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN);
if(ret)
return ret;
ret = pega_rfkill_setup(asus, &asus->btrfk, "pega-bt", PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH);
if(ret)
goto err_btrfk;
ret = pega_rfkill_setup(asus, &asus->wwanrfk, "pega-wwan", PEGA_WWAN, RFKILL_TYPE_WWAN);
if(ret)
goto err_wwanrfk;
pr_warn("Pega rfkill init succeeded\n");
return 0;
err_wwanrfk:
pega_rfkill_terminate(&asus->btrfk);
err_btrfk:
pega_rfkill_terminate(&asus->wlanrfk);
return ret;
}
/* /*
* Input device (i.e. hotkeys) * Input device (i.e. hotkeys)
*/ */
@ -1141,6 +1438,14 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)
} }
return ; return ;
} }
/* Accelerometer "coarse orientation change" event */
if (asus->pega_accel_poll && event == 0xEA) {
kobject_uevent(&asus->pega_accel_poll->input->dev.kobj,
KOBJ_CHANGE);
return ;
}
asus_input_notify(asus, event); asus_input_notify(asus, event);
} }
@ -1152,6 +1457,7 @@ 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(wwan, S_IRUGO | S_IWUSR, show_wwan, store_wwan);
static DEVICE_ATTR(display, S_IWUSR, NULL, 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(ledd, S_IRUGO | S_IWUSR, show_ledd, store_ledd);
static DEVICE_ATTR(ls_value, S_IRUGO, show_lsvalue, NULL);
static DEVICE_ATTR(ls_level, S_IRUGO | S_IWUSR, show_lslvl, store_lslvl); 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); static DEVICE_ATTR(ls_switch, S_IRUGO | S_IWUSR, show_lssw, store_lssw);
static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps); static DEVICE_ATTR(gps, S_IRUGO | S_IWUSR, show_gps, store_gps);
@ -1164,6 +1470,7 @@ static struct attribute *asus_attributes[] = {
&dev_attr_wwan.attr, &dev_attr_wwan.attr,
&dev_attr_display.attr, &dev_attr_display.attr,
&dev_attr_ledd.attr, &dev_attr_ledd.attr,
&dev_attr_ls_value.attr,
&dev_attr_ls_level.attr, &dev_attr_ls_level.attr,
&dev_attr_ls_switch.attr, &dev_attr_ls_switch.attr,
&dev_attr_gps.attr, &dev_attr_gps.attr,
@ -1180,6 +1487,19 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj,
acpi_handle handle = asus->handle; acpi_handle handle = asus->handle;
bool supported; bool supported;
if (asus->is_pega_lucid) {
/* no ls_level interface on the Lucid */
if (attr == &dev_attr_ls_switch.attr)
supported = true;
else if (attr == &dev_attr_ls_level.attr)
supported = false;
else
goto normal;
return supported;
}
normal:
if (attr == &dev_attr_wlan.attr) { if (attr == &dev_attr_wlan.attr) {
supported = !acpi_check_handle(handle, METHOD_WLAN, NULL); supported = !acpi_check_handle(handle, METHOD_WLAN, NULL);
@ -1202,8 +1522,9 @@ static mode_t asus_sysfs_is_visible(struct kobject *kobj,
} else if (attr == &dev_attr_ls_switch.attr || } else if (attr == &dev_attr_ls_switch.attr ||
attr == &dev_attr_ls_level.attr) { attr == &dev_attr_ls_level.attr) {
supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) && supported = !acpi_check_handle(handle, METHOD_ALS_CONTROL, NULL) &&
!acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL); !acpi_check_handle(handle, METHOD_ALS_LEVEL, NULL);
} else if (attr == &dev_attr_ls_value.attr) {
supported = asus->is_pega_lucid;
} else if (attr == &dev_attr_gps.attr) { } else if (attr == &dev_attr_gps.attr) {
supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) && supported = !acpi_check_handle(handle, METHOD_GPS_ON, NULL) &&
!acpi_check_handle(handle, METHOD_GPS_OFF, NULL) && !acpi_check_handle(handle, METHOD_GPS_OFF, NULL) &&
@ -1258,7 +1579,7 @@ static struct platform_driver platform_driver = {
.driver = { .driver = {
.name = ASUS_LAPTOP_FILE, .name = ASUS_LAPTOP_FILE,
.owner = THIS_MODULE, .owner = THIS_MODULE,
} },
}; };
/* /*
@ -1388,11 +1709,13 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)
asus->ledd_status = 0xFFF; asus->ledd_status = 0xFFF;
/* Set initial values of light sensor and level */ /* Set initial values of light sensor and level */
asus->light_switch = 0; /* Default to light sensor disabled */ asus->light_switch = !!als_status;
asus->light_level = 5; /* level 5 for sensor sensitivity */ asus->light_level = 5; /* level 5 for sensor sensitivity */
if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) && if (asus->is_pega_lucid) {
!acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) { asus_als_switch(asus, asus->light_switch);
} else if (!acpi_check_handle(asus->handle, METHOD_ALS_CONTROL, NULL) &&
!acpi_check_handle(asus->handle, METHOD_ALS_LEVEL, NULL)) {
asus_als_switch(asus, asus->light_switch); asus_als_switch(asus, asus->light_switch);
asus_als_level(asus, asus->light_level); asus_als_level(asus, asus->light_level);
} }
@ -1439,9 +1762,10 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
goto fail_platform; goto fail_platform;
/* /*
* Register the platform device first. It is used as a parent for the * Need platform type detection first, then the platform
* sub-devices below. * device. It is used as a parent for the sub-devices below.
*/ */
asus->is_pega_lucid = asus_check_pega_lucid(asus);
result = asus_platform_init(asus); result = asus_platform_init(asus);
if (result) if (result)
goto fail_platform; goto fail_platform;
@ -1465,9 +1789,21 @@ static int __devinit asus_acpi_add(struct acpi_device *device)
if (result) if (result)
goto fail_rfkill; goto fail_rfkill;
result = pega_accel_init(asus);
if (result && result != -ENODEV)
goto fail_pega_accel;
result = pega_rfkill_init(asus);
if (result && result != -ENODEV)
goto fail_pega_rfkill;
asus_device_present = true; asus_device_present = true;
return 0; return 0;
fail_pega_rfkill:
pega_accel_exit(asus);
fail_pega_accel:
asus_rfkill_exit(asus);
fail_rfkill: fail_rfkill:
asus_led_exit(asus); asus_led_exit(asus);
fail_led: fail_led:
@ -1491,6 +1827,8 @@ static int asus_acpi_remove(struct acpi_device *device, int type)
asus_rfkill_exit(asus); asus_rfkill_exit(asus);
asus_led_exit(asus); asus_led_exit(asus);
asus_input_exit(asus); asus_input_exit(asus);
pega_accel_exit(asus);
pega_rfkill_exit(asus);
asus_platform_exit(asus); asus_platform_exit(asus);
kfree(asus->name); kfree(asus->name);

View File

@ -453,7 +453,9 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)
static void asus_wmi_led_exit(struct asus_wmi *asus) static void asus_wmi_led_exit(struct asus_wmi *asus)
{ {
if (asus->tpd_led.dev) if (!IS_ERR_OR_NULL(asus->kbd_led.dev))
led_classdev_unregister(&asus->kbd_led);
if (!IS_ERR_OR_NULL(asus->tpd_led.dev))
led_classdev_unregister(&asus->tpd_led); led_classdev_unregister(&asus->tpd_led);
if (asus->led_workqueue) if (asus->led_workqueue)
destroy_workqueue(asus->led_workqueue); destroy_workqueue(asus->led_workqueue);

View File

@ -60,6 +60,22 @@ struct calling_interface_structure {
struct calling_interface_token tokens[]; struct calling_interface_token tokens[];
} __packed; } __packed;
struct quirk_entry {
u8 touchpad_led;
};
static struct quirk_entry *quirks;
static struct quirk_entry quirk_dell_vostro_v130 = {
.touchpad_led = 1,
};
static int dmi_matched(const struct dmi_system_id *dmi)
{
quirks = dmi->driver_data;
return 1;
}
static int da_command_address; static int da_command_address;
static int da_command_code; static int da_command_code;
static int da_num_tokens; static int da_num_tokens;
@ -149,6 +165,27 @@ static struct dmi_system_id __devinitdata dell_blacklist[] = {
{} {}
}; };
static struct dmi_system_id __devinitdata dell_quirks[] = {
{
.callback = dmi_matched,
.ident = "Dell Vostro V130",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V130"),
},
.driver_data = &quirk_dell_vostro_v130,
},
{
.callback = dmi_matched,
.ident = "Dell Vostro V131",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V131"),
},
.driver_data = &quirk_dell_vostro_v130,
},
};
static struct calling_interface_buffer *buffer; static struct calling_interface_buffer *buffer;
static struct page *bufferpage; static struct page *bufferpage;
static DEFINE_MUTEX(buffer_mutex); static DEFINE_MUTEX(buffer_mutex);
@ -552,6 +589,44 @@ static const struct backlight_ops dell_ops = {
.update_status = dell_send_intensity, .update_status = dell_send_intensity,
}; };
static void touchpad_led_on()
{
int command = 0x97;
char data = 1;
i8042_command(&data, command | 1 << 12);
}
static void touchpad_led_off()
{
int command = 0x97;
char data = 2;
i8042_command(&data, command | 1 << 12);
}
static void touchpad_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
if (value > 0)
touchpad_led_on();
else
touchpad_led_off();
}
static struct led_classdev touchpad_led = {
.name = "dell-laptop::touchpad",
.brightness_set = touchpad_led_set,
};
static int __devinit touchpad_led_init(struct device *dev)
{
return led_classdev_register(dev, &touchpad_led);
}
static void touchpad_led_exit(void)
{
led_classdev_unregister(&touchpad_led);
}
static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str, static bool dell_laptop_i8042_filter(unsigned char data, unsigned char str,
struct serio *port) struct serio *port)
{ {
@ -584,6 +659,10 @@ static int __init dell_init(void)
if (!dmi_check_system(dell_device_table)) if (!dmi_check_system(dell_device_table))
return -ENODEV; return -ENODEV;
quirks = NULL;
/* find if this machine support other functions */
dmi_check_system(dell_quirks);
dmi_walk(find_tokens, NULL); dmi_walk(find_tokens, NULL);
if (!da_tokens) { if (!da_tokens) {
@ -626,6 +705,9 @@ static int __init dell_init(void)
goto fail_filter; goto fail_filter;
} }
if (quirks && quirks->touchpad_led)
touchpad_led_init(&platform_device->dev);
dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL); dell_laptop_dir = debugfs_create_dir("dell_laptop", NULL);
if (dell_laptop_dir != NULL) if (dell_laptop_dir != NULL)
debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL, debugfs_create_file("rfkill", 0444, dell_laptop_dir, NULL,
@ -692,6 +774,8 @@ fail_platform_driver:
static void __exit dell_exit(void) static void __exit dell_exit(void)
{ {
debugfs_remove_recursive(dell_laptop_dir); debugfs_remove_recursive(dell_laptop_dir);
if (quirks && quirks->touchpad_led)
touchpad_led_exit();
i8042_remove_filter(dell_laptop_i8042_filter); i8042_remove_filter(dell_laptop_i8042_filter);
cancel_delayed_work_sync(&dell_rfkill_work); cancel_delayed_work_sync(&dell_rfkill_work);
backlight_device_unregister(dell_backlight_device); backlight_device_unregister(dell_backlight_device);

View File

@ -568,7 +568,7 @@ static int eeepc_led_init(struct eeepc_laptop *eeepc)
static void eeepc_led_exit(struct eeepc_laptop *eeepc) static void eeepc_led_exit(struct eeepc_laptop *eeepc)
{ {
if (eeepc->tpd_led.dev) if (!IS_ERR_OR_NULL(eeepc->tpd_led.dev))
led_classdev_unregister(&eeepc->tpd_led); led_classdev_unregister(&eeepc->tpd_led);
if (eeepc->led_workqueue) if (eeepc->led_workqueue)
destroy_workqueue(eeepc->led_workqueue); destroy_workqueue(eeepc->led_workqueue);

View File

@ -76,6 +76,7 @@ static inline void delayed_sysfs_set(struct led_classdev *led_cdev,
/* For automatic insertion of the module */ /* For automatic insertion of the module */
static struct acpi_device_id lis3lv02d_device_ids[] = { static struct acpi_device_id lis3lv02d_device_ids[] = {
{"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */
{"HPQ6000", 0}, /* HP Mobile Data Protection System PNP */
{"", 0}, {"", 0},
}; };
MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids);
@ -228,6 +229,10 @@ static struct dmi_system_id lis3lv02d_dmi_ids[] = {
AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap), AXIS_DMI_MATCH("HPB522x", "HP ProBook 522", xy_swap),
AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted), AXIS_DMI_MATCH("HPB532x", "HP ProBook 532", y_inverted),
AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd), AXIS_DMI_MATCH("Mini510x", "HP Mini 510", xy_rotated_left_usd),
AXIS_DMI_MATCH("HPB63xx", "HP ProBook 63", xy_swap),
AXIS_DMI_MATCH("HPB64xx", "HP ProBook 64", xy_swap),
AXIS_DMI_MATCH("HPB64xx", "HP EliteBook 84", xy_swap),
AXIS_DMI_MATCH("HPB65xx", "HP ProBook 65", x_inverted),
{ NULL, } { NULL, }
/* Laptop models without axis info (yet): /* Laptop models without axis info (yet):
* "NC6910" "HP Compaq 6910" * "NC6910" "HP Compaq 6910"

View File

@ -34,6 +34,8 @@
#include <linux/input/sparse-keymap.h> #include <linux/input/sparse-keymap.h>
#include <linux/backlight.h> #include <linux/backlight.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#define IDEAPAD_RFKILL_DEV_NUM (3) #define IDEAPAD_RFKILL_DEV_NUM (3)
@ -42,15 +44,41 @@
#define CFG_WIFI_BIT (18) #define CFG_WIFI_BIT (18)
#define CFG_CAMERA_BIT (19) #define CFG_CAMERA_BIT (19)
enum {
VPCCMD_R_VPC1 = 0x10,
VPCCMD_R_BL_MAX,
VPCCMD_R_BL,
VPCCMD_W_BL,
VPCCMD_R_WIFI,
VPCCMD_W_WIFI,
VPCCMD_R_BT,
VPCCMD_W_BT,
VPCCMD_R_BL_POWER,
VPCCMD_R_NOVO,
VPCCMD_R_VPC2,
VPCCMD_R_TOUCHPAD,
VPCCMD_W_TOUCHPAD,
VPCCMD_R_CAMERA,
VPCCMD_W_CAMERA,
VPCCMD_R_3G,
VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */
VPCCMD_R_RF = 0x23,
VPCCMD_W_RF,
VPCCMD_W_BL_POWER = 0x33,
};
struct ideapad_private { struct ideapad_private {
struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM]; struct rfkill *rfk[IDEAPAD_RFKILL_DEV_NUM];
struct platform_device *platform_device; struct platform_device *platform_device;
struct input_dev *inputdev; struct input_dev *inputdev;
struct backlight_device *blightdev; struct backlight_device *blightdev;
struct dentry *debug;
unsigned long cfg; unsigned long cfg;
}; };
static acpi_handle ideapad_handle; static acpi_handle ideapad_handle;
static struct ideapad_private *ideapad_priv;
static bool no_bt_rfkill; static bool no_bt_rfkill;
module_param(no_bt_rfkill, bool, 0444); module_param(no_bt_rfkill, bool, 0444);
MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth."); MODULE_PARM_DESC(no_bt_rfkill, "No rfkill for bluetooth.");
@ -163,6 +191,146 @@ static int write_ec_cmd(acpi_handle handle, int cmd, unsigned long data)
return -1; return -1;
} }
/*
* debugfs
*/
#define DEBUGFS_EVENT_LEN (4096)
static int debugfs_status_show(struct seq_file *s, void *data)
{
unsigned long value;
if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &value))
seq_printf(s, "Backlight max:\t%lu\n", value);
if (!read_ec_data(ideapad_handle, VPCCMD_R_BL, &value))
seq_printf(s, "Backlight now:\t%lu\n", value);
if (!read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &value))
seq_printf(s, "BL power value:\t%s\n", value ? "On" : "Off");
seq_printf(s, "=====================\n");
if (!read_ec_data(ideapad_handle, VPCCMD_R_RF, &value))
seq_printf(s, "Radio status:\t%s(%lu)\n",
value ? "On" : "Off", value);
if (!read_ec_data(ideapad_handle, VPCCMD_R_WIFI, &value))
seq_printf(s, "Wifi status:\t%s(%lu)\n",
value ? "On" : "Off", value);
if (!read_ec_data(ideapad_handle, VPCCMD_R_BT, &value))
seq_printf(s, "BT status:\t%s(%lu)\n",
value ? "On" : "Off", value);
if (!read_ec_data(ideapad_handle, VPCCMD_R_3G, &value))
seq_printf(s, "3G status:\t%s(%lu)\n",
value ? "On" : "Off", value);
seq_printf(s, "=====================\n");
if (!read_ec_data(ideapad_handle, VPCCMD_R_TOUCHPAD, &value))
seq_printf(s, "Touchpad status:%s(%lu)\n",
value ? "On" : "Off", value);
if (!read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &value))
seq_printf(s, "Camera status:\t%s(%lu)\n",
value ? "On" : "Off", value);
return 0;
}
static int debugfs_status_open(struct inode *inode, struct file *file)
{
return single_open(file, debugfs_status_show, NULL);
}
static const struct file_operations debugfs_status_fops = {
.owner = THIS_MODULE,
.open = debugfs_status_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int debugfs_cfg_show(struct seq_file *s, void *data)
{
if (!ideapad_priv) {
seq_printf(s, "cfg: N/A\n");
} else {
seq_printf(s, "cfg: 0x%.8lX\n\nCapability: ",
ideapad_priv->cfg);
if (test_bit(CFG_BT_BIT, &ideapad_priv->cfg))
seq_printf(s, "Bluetooth ");
if (test_bit(CFG_3G_BIT, &ideapad_priv->cfg))
seq_printf(s, "3G ");
if (test_bit(CFG_WIFI_BIT, &ideapad_priv->cfg))
seq_printf(s, "Wireless ");
if (test_bit(CFG_CAMERA_BIT, &ideapad_priv->cfg))
seq_printf(s, "Camera ");
seq_printf(s, "\nGraphic: ");
switch ((ideapad_priv->cfg)&0x700) {
case 0x100:
seq_printf(s, "Intel");
break;
case 0x200:
seq_printf(s, "ATI");
break;
case 0x300:
seq_printf(s, "Nvidia");
break;
case 0x400:
seq_printf(s, "Intel and ATI");
break;
case 0x500:
seq_printf(s, "Intel and Nvidia");
break;
}
seq_printf(s, "\n");
}
return 0;
}
static int debugfs_cfg_open(struct inode *inode, struct file *file)
{
return single_open(file, debugfs_cfg_show, NULL);
}
static const struct file_operations debugfs_cfg_fops = {
.owner = THIS_MODULE,
.open = debugfs_cfg_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int __devinit ideapad_debugfs_init(struct ideapad_private *priv)
{
struct dentry *node;
priv->debug = debugfs_create_dir("ideapad", NULL);
if (priv->debug == NULL) {
pr_err("failed to create debugfs directory");
goto errout;
}
node = debugfs_create_file("cfg", S_IRUGO, priv->debug, NULL,
&debugfs_cfg_fops);
if (!node) {
pr_err("failed to create cfg in debugfs");
goto errout;
}
node = debugfs_create_file("status", S_IRUGO, priv->debug, NULL,
&debugfs_status_fops);
if (!node) {
pr_err("failed to create event in debugfs");
goto errout;
}
return 0;
errout:
return -ENOMEM;
}
static void ideapad_debugfs_exit(struct ideapad_private *priv)
{
debugfs_remove_recursive(priv->debug);
priv->debug = NULL;
}
/* /*
* sysfs * sysfs
*/ */
@ -172,7 +340,7 @@ static ssize_t show_ideapad_cam(struct device *dev,
{ {
unsigned long result; unsigned long result;
if (read_ec_data(ideapad_handle, 0x1D, &result)) if (read_ec_data(ideapad_handle, VPCCMD_R_CAMERA, &result))
return sprintf(buf, "-1\n"); return sprintf(buf, "-1\n");
return sprintf(buf, "%lu\n", result); return sprintf(buf, "%lu\n", result);
} }
@ -187,7 +355,7 @@ static ssize_t store_ideapad_cam(struct device *dev,
return 0; return 0;
if (sscanf(buf, "%i", &state) != 1) if (sscanf(buf, "%i", &state) != 1)
return -EINVAL; return -EINVAL;
ret = write_ec_cmd(ideapad_handle, 0x1E, state); ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
if (ret < 0) if (ret < 0)
return ret; return ret;
return count; return count;
@ -195,20 +363,8 @@ static ssize_t store_ideapad_cam(struct device *dev,
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam); static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
static ssize_t show_ideapad_cfg(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ideapad_private *priv = dev_get_drvdata(dev);
return sprintf(buf, "0x%.8lX\n", priv->cfg);
}
static DEVICE_ATTR(cfg, 0444, show_ideapad_cfg, NULL);
static struct attribute *ideapad_attributes[] = { static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr, &dev_attr_camera_power.attr,
&dev_attr_cfg.attr,
NULL NULL
}; };
@ -244,9 +400,9 @@ struct ideapad_rfk_data {
}; };
const struct ideapad_rfk_data ideapad_rfk_data[] = { const struct ideapad_rfk_data ideapad_rfk_data[] = {
{ "ideapad_wlan", CFG_WIFI_BIT, 0x15, RFKILL_TYPE_WLAN }, { "ideapad_wlan", CFG_WIFI_BIT, VPCCMD_W_WIFI, RFKILL_TYPE_WLAN },
{ "ideapad_bluetooth", CFG_BT_BIT, 0x17, RFKILL_TYPE_BLUETOOTH }, { "ideapad_bluetooth", CFG_BT_BIT, VPCCMD_W_BT, RFKILL_TYPE_BLUETOOTH },
{ "ideapad_3g", CFG_3G_BIT, 0x20, RFKILL_TYPE_WWAN }, { "ideapad_3g", CFG_3G_BIT, VPCCMD_W_3G, RFKILL_TYPE_WWAN },
}; };
static int ideapad_rfk_set(void *data, bool blocked) static int ideapad_rfk_set(void *data, bool blocked)
@ -260,13 +416,12 @@ static struct rfkill_ops ideapad_rfk_ops = {
.set_block = ideapad_rfk_set, .set_block = ideapad_rfk_set,
}; };
static void ideapad_sync_rfk_state(struct acpi_device *adevice) static void ideapad_sync_rfk_state(struct ideapad_private *priv)
{ {
struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
unsigned long hw_blocked; unsigned long hw_blocked;
int i; int i;
if (read_ec_data(ideapad_handle, 0x23, &hw_blocked)) if (read_ec_data(ideapad_handle, VPCCMD_R_RF, &hw_blocked))
return; return;
hw_blocked = !hw_blocked; hw_blocked = !hw_blocked;
@ -363,8 +518,10 @@ static void ideapad_platform_exit(struct ideapad_private *priv)
* input device * input device
*/ */
static const struct key_entry ideapad_keymap[] = { static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 0x06, { KEY_SWITCHVIDEOMODE } }, { KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
{ KE_KEY, 0x0D, { KEY_WLAN } }, { KE_KEY, 13, { KEY_WLAN } },
{ KE_KEY, 16, { KEY_PROG1 } },
{ KE_KEY, 17, { KEY_PROG2 } },
{ KE_END, 0 }, { KE_END, 0 },
}; };
@ -419,6 +576,18 @@ static void ideapad_input_report(struct ideapad_private *priv,
sparse_keymap_report_event(priv->inputdev, scancode, 1, true); sparse_keymap_report_event(priv->inputdev, scancode, 1, true);
} }
static void ideapad_input_novokey(struct ideapad_private *priv)
{
unsigned long long_pressed;
if (read_ec_data(ideapad_handle, VPCCMD_R_NOVO, &long_pressed))
return;
if (long_pressed)
ideapad_input_report(priv, 17);
else
ideapad_input_report(priv, 16);
}
/* /*
* backlight * backlight
*/ */
@ -426,16 +595,17 @@ static int ideapad_backlight_get_brightness(struct backlight_device *blightdev)
{ {
unsigned long now; unsigned long now;
if (read_ec_data(ideapad_handle, 0x12, &now)) if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
return -EIO; return -EIO;
return now; return now;
} }
static int ideapad_backlight_update_status(struct backlight_device *blightdev) static int ideapad_backlight_update_status(struct backlight_device *blightdev)
{ {
if (write_ec_cmd(ideapad_handle, 0x13, blightdev->props.brightness)) if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL,
blightdev->props.brightness))
return -EIO; return -EIO;
if (write_ec_cmd(ideapad_handle, 0x33, if (write_ec_cmd(ideapad_handle, VPCCMD_W_BL_POWER,
blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1)) blightdev->props.power == FB_BLANK_POWERDOWN ? 0 : 1))
return -EIO; return -EIO;
@ -453,11 +623,11 @@ static int ideapad_backlight_init(struct ideapad_private *priv)
struct backlight_properties props; struct backlight_properties props;
unsigned long max, now, power; unsigned long max, now, power;
if (read_ec_data(ideapad_handle, 0x11, &max)) if (read_ec_data(ideapad_handle, VPCCMD_R_BL_MAX, &max))
return -EIO; return -EIO;
if (read_ec_data(ideapad_handle, 0x12, &now)) if (read_ec_data(ideapad_handle, VPCCMD_R_BL, &now))
return -EIO; return -EIO;
if (read_ec_data(ideapad_handle, 0x18, &power)) if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
return -EIO; return -EIO;
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(struct backlight_properties));
@ -493,7 +663,9 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
unsigned long power; unsigned long power;
struct backlight_device *blightdev = priv->blightdev; struct backlight_device *blightdev = priv->blightdev;
if (read_ec_data(ideapad_handle, 0x18, &power)) if (!blightdev)
return;
if (read_ec_data(ideapad_handle, VPCCMD_R_BL_POWER, &power))
return; return;
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN; blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
} }
@ -504,7 +676,7 @@ static void ideapad_backlight_notify_brightness(struct ideapad_private *priv)
/* if we control brightness via acpi video driver */ /* if we control brightness via acpi video driver */
if (priv->blightdev == NULL) { if (priv->blightdev == NULL) {
read_ec_data(ideapad_handle, 0x12, &now); read_ec_data(ideapad_handle, VPCCMD_R_BL, &now);
return; return;
} }
@ -533,6 +705,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
dev_set_drvdata(&adevice->dev, priv); dev_set_drvdata(&adevice->dev, priv);
ideapad_priv = priv;
ideapad_handle = adevice->handle; ideapad_handle = adevice->handle;
priv->cfg = cfg; priv->cfg = cfg;
@ -540,6 +713,10 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
if (ret) if (ret)
goto platform_failed; goto platform_failed;
ret = ideapad_debugfs_init(priv);
if (ret)
goto debugfs_failed;
ret = ideapad_input_init(priv); ret = ideapad_input_init(priv);
if (ret) if (ret)
goto input_failed; goto input_failed;
@ -550,7 +727,7 @@ static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
else else
priv->rfk[i] = NULL; priv->rfk[i] = NULL;
} }
ideapad_sync_rfk_state(adevice); ideapad_sync_rfk_state(priv);
if (!acpi_video_backlight_support()) { if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv); ret = ideapad_backlight_init(priv);
@ -565,6 +742,8 @@ backlight_failed:
ideapad_unregister_rfkill(adevice, i); ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv); ideapad_input_exit(priv);
input_failed: input_failed:
ideapad_debugfs_exit(priv);
debugfs_failed:
ideapad_platform_exit(priv); ideapad_platform_exit(priv);
platform_failed: platform_failed:
kfree(priv); kfree(priv);
@ -580,6 +759,7 @@ static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type)
for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++) for (i = 0; i < IDEAPAD_RFKILL_DEV_NUM; i++)
ideapad_unregister_rfkill(adevice, i); ideapad_unregister_rfkill(adevice, i);
ideapad_input_exit(priv); ideapad_input_exit(priv);
ideapad_debugfs_exit(priv);
ideapad_platform_exit(priv); ideapad_platform_exit(priv);
dev_set_drvdata(&adevice->dev, NULL); dev_set_drvdata(&adevice->dev, NULL);
kfree(priv); kfree(priv);
@ -593,9 +773,9 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
acpi_handle handle = adevice->handle; acpi_handle handle = adevice->handle;
unsigned long vpc1, vpc2, vpc_bit; unsigned long vpc1, vpc2, vpc_bit;
if (read_ec_data(handle, 0x10, &vpc1)) if (read_ec_data(handle, VPCCMD_R_VPC1, &vpc1))
return; return;
if (read_ec_data(handle, 0x1A, &vpc2)) if (read_ec_data(handle, VPCCMD_R_VPC2, &vpc2))
return; return;
vpc1 = (vpc2 << 8) | vpc1; vpc1 = (vpc2 << 8) | vpc1;
@ -603,11 +783,14 @@ static void ideapad_acpi_notify(struct acpi_device *adevice, u32 event)
if (test_bit(vpc_bit, &vpc1)) { if (test_bit(vpc_bit, &vpc1)) {
switch (vpc_bit) { switch (vpc_bit) {
case 9: case 9:
ideapad_sync_rfk_state(adevice); ideapad_sync_rfk_state(priv);
break; break;
case 4: case 4:
ideapad_backlight_notify_brightness(priv); ideapad_backlight_notify_brightness(priv);
break; break;
case 3:
ideapad_input_novokey(priv);
break;
case 2: case 2:
ideapad_backlight_notify_power(priv); ideapad_backlight_notify_power(priv);
break; break;

View File

@ -24,7 +24,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <asm/intel_scu_ipc.h> #include <asm/intel_scu_ipc.h>
static u32 major; static int major;
#define MAX_FW_SIZE 264192 #define MAX_FW_SIZE 264192
@ -117,7 +117,11 @@ static const struct file_operations scu_ipc_fops = {
static int __init ipc_module_init(void) static int __init ipc_module_init(void)
{ {
return register_chrdev(0, "intel_mid_scu", &scu_ipc_fops); major = register_chrdev(0, "intel_mid_scu", &scu_ipc_fops);
if (major < 0)
return major;
return 0;
} }
static void __exit ipc_module_exit(void) static void __exit ipc_module_exit(void)

View File

@ -226,6 +226,7 @@ static struct backlight_device *backlight_device;
static struct mutex sabi_mutex; static struct mutex sabi_mutex;
static struct platform_device *sdev; static struct platform_device *sdev;
static struct rfkill *rfk; static struct rfkill *rfk;
static bool has_stepping_quirk;
static int force; static int force;
module_param(force, bool, 0); module_param(force, bool, 0);
@ -370,15 +371,28 @@ static u8 read_brightness(void)
&sretval); &sretval);
if (!retval) { if (!retval) {
user_brightness = sretval.retval[0]; user_brightness = sretval.retval[0];
if (user_brightness != 0) if (user_brightness > sabi_config->min_brightness)
user_brightness -= sabi_config->min_brightness; user_brightness -= sabi_config->min_brightness;
else
user_brightness = 0;
} }
return user_brightness; return user_brightness;
} }
static void set_brightness(u8 user_brightness) static void set_brightness(u8 user_brightness)
{ {
u8 user_level = user_brightness - sabi_config->min_brightness; u8 user_level = user_brightness + sabi_config->min_brightness;
if (has_stepping_quirk && user_level != 0) {
/*
* short circuit if the specified level is what's already set
* to prevent the screen from flickering needlessly
*/
if (user_brightness == read_brightness())
return;
sabi_set_command(sabi_config->commands.set_brightness, 0);
}
sabi_set_command(sabi_config->commands.set_brightness, user_level); sabi_set_command(sabi_config->commands.set_brightness, user_level);
} }
@ -388,6 +402,40 @@ static int get_brightness(struct backlight_device *bd)
return (int)read_brightness(); return (int)read_brightness();
} }
static void check_for_stepping_quirk(void)
{
u8 initial_level;
u8 check_level;
u8 orig_level = read_brightness();
/*
* Some laptops exhibit the strange behaviour of stepping toward
* (rather than setting) the brightness except when changing to/from
* brightness level 0. This behaviour is checked for here and worked
* around in set_brightness.
*/
if (orig_level == 0)
set_brightness(1);
initial_level = read_brightness();
if (initial_level <= 2)
check_level = initial_level + 2;
else
check_level = initial_level - 2;
has_stepping_quirk = false;
set_brightness(check_level);
if (read_brightness() != check_level) {
has_stepping_quirk = true;
pr_info("enabled workaround for brightness stepping quirk\n");
}
set_brightness(orig_level);
}
static int update_status(struct backlight_device *bd) static int update_status(struct backlight_device *bd)
{ {
set_brightness(bd->props.brightness); set_brightness(bd->props.brightness);
@ -620,6 +668,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
}, },
.callback = dmi_check_cb, .callback = dmi_check_cb,
}, },
{
.ident = "N220",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR,
"SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "N220"),
DMI_MATCH(DMI_BOARD_NAME, "N220"),
},
.callback = dmi_check_cb,
},
{ {
.ident = "N150/N210/N220/N230", .ident = "N150/N210/N220/N230",
.matches = { .matches = {
@ -640,6 +698,15 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
}, },
.callback = dmi_check_cb, .callback = dmi_check_cb,
}, },
{
.ident = "R700",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "SR700"),
DMI_MATCH(DMI_BOARD_NAME, "SR700"),
},
.callback = dmi_check_cb,
},
{ {
.ident = "R530/R730", .ident = "R530/R730",
.matches = { .matches = {
@ -686,6 +753,33 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {
}, },
.callback = dmi_check_cb, .callback = dmi_check_cb,
}, },
{
.ident = "R528/R728",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "R528/R728"),
DMI_MATCH(DMI_BOARD_NAME, "R528/R728"),
},
.callback = dmi_check_cb,
},
{
.ident = "NC210/NC110",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "NC210/NC110"),
DMI_MATCH(DMI_BOARD_NAME, "NC210/NC110"),
},
.callback = dmi_check_cb,
},
{
.ident = "X520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
DMI_MATCH(DMI_PRODUCT_NAME, "X520"),
DMI_MATCH(DMI_BOARD_NAME, "X520"),
},
.callback = dmi_check_cb,
},
{ }, { },
}; };
MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); MODULE_DEVICE_TABLE(dmi, samsung_dmi_table);
@ -770,7 +864,7 @@ static int __init samsung_init(void)
sabi_iface = ioremap_nocache(ifaceP, 16); sabi_iface = ioremap_nocache(ifaceP, 16);
if (!sabi_iface) { if (!sabi_iface) {
pr_err("Can't remap %x\n", ifaceP); pr_err("Can't remap %x\n", ifaceP);
goto exit; goto error_no_signature;
} }
if (debug) { if (debug) {
printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP);
@ -794,6 +888,9 @@ static int __init samsung_init(void)
} }
} }
/* Check for stepping quirk */
check_for_stepping_quirk();
/* knock up a platform device to hang stuff off of */ /* knock up a platform device to hang stuff off of */
sdev = platform_device_register_simple("samsung", -1, NULL, 0); sdev = platform_device_register_simple("samsung", -1, NULL, 0);
if (IS_ERR(sdev)) if (IS_ERR(sdev))
@ -802,7 +899,8 @@ static int __init samsung_init(void)
/* create a backlight device to talk to this one */ /* create a backlight device to talk to this one */
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_PLATFORM; props.type = BACKLIGHT_PLATFORM;
props.max_brightness = sabi_config->max_brightness; props.max_brightness = sabi_config->max_brightness -
sabi_config->min_brightness;
backlight_device = backlight_device_register("samsung", &sdev->dev, backlight_device = backlight_device_register("samsung", &sdev->dev,
NULL, &backlight_ops, NULL, &backlight_ops,
&props); &props);
@ -821,7 +919,6 @@ static int __init samsung_init(void)
if (retval) if (retval)
goto error_file_create; goto error_file_create;
exit:
return 0; return 0;
error_file_create: error_file_create:

View File

@ -3281,7 +3281,7 @@ static int sony_pic_add(struct acpi_device *device)
/* request IRQ */ /* request IRQ */
list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) { list_for_each_entry_reverse(irq, &spic_dev.interrupts, list) {
if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, if (!request_irq(irq->irq.interrupts[0], sony_pic_irq,
IRQF_DISABLED, "sony-laptop", &spic_dev)) { 0, "sony-laptop", &spic_dev)) {
dprintk("IRQ: %d - triggering: %d - " dprintk("IRQ: %d - triggering: %d - "
"polarity: %d - shr: %d\n", "polarity: %d - shr: %d\n",
irq->irq.interrupts[0], irq->irq.interrupts[0],

View File

@ -41,6 +41,7 @@ static const struct key_entry topstar_keymap[] = {
{ KE_KEY, 0x8c, { KEY_MEDIA } }, { KE_KEY, 0x8c, { KEY_MEDIA } },
/* Known non hotkey events don't handled or that we don't care yet */ /* Known non hotkey events don't handled or that we don't care yet */
{ KE_IGNORE, 0x82, }, /* backlight event */
{ KE_IGNORE, 0x8e, }, { KE_IGNORE, 0x8e, },
{ KE_IGNORE, 0x8f, }, { KE_IGNORE, 0x8f, },
{ KE_IGNORE, 0x90, }, { KE_IGNORE, 0x90, },

File diff suppressed because it is too large Load Diff

View File

@ -754,9 +754,13 @@ static void wmi_free_devices(void)
struct wmi_block *wblock, *next; struct wmi_block *wblock, *next;
/* Delete devices for all the GUIDs */ /* Delete devices for all the GUIDs */
list_for_each_entry_safe(wblock, next, &wmi_block_list, list) list_for_each_entry_safe(wblock, next, &wmi_block_list, list) {
list_del(&wblock->list);
if (wblock->dev.class) if (wblock->dev.class)
device_unregister(&wblock->dev); device_unregister(&wblock->dev);
else
kfree(wblock);
}
} }
static bool guid_already_parsed(const char *guid_string) static bool guid_already_parsed(const char *guid_string)