From 6fe6ae56a7cebaebc2e6daa11c423e4692f9b592 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Wed, 2 Nov 2011 14:32:00 -0400 Subject: sony-laptop: Enable keyboard backlight by default When the keyboard backlight support was originally added, the commit said to default it to on with a 10 second timeout. That actually wasn't the case, as the default value is commented out for the kbd_backlight parameter. Because it is a static variable, it gets set to 0 by default without some other form of initialization. However, it seems the function to set the value wasn't actually called immediately, so whatever state the keyboard was in initially would remain. Then commit df410d522410e67660 was introduced during the 2.6.39 timeframe to immediately set whatever value was present (as well as attempt to restore/reset the state on module removal or resume). That seems to have now forced the light off immediately when the module is loaded unless the option kbd_backlight=1 is specified. Let's enable it by default again (for the first time). This should solve https://bugzilla.redhat.com/show_bug.cgi?id=728478 Signed-off-by: Josh Boyer Acked-by: Mattia Dongili Signed-off-by: Matthew Garrett --- drivers/platform/x86/sony-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index c006dee5ebfe..40c470507e50 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -127,7 +127,7 @@ MODULE_PARM_DESC(minor, "default is -1 (automatic)"); #endif -static int kbd_backlight; /* = 1 */ +static int kbd_backlight = 1; module_param(kbd_backlight, int, 0444); MODULE_PARM_DESC(kbd_backlight, "set this to 0 to disable keyboard backlight, " -- cgit v1.2.3 From fbd93bf4ffb66b7f76b31e7d777138dc89b37a1d Mon Sep 17 00:00:00 2001 From: Marcos Paulo de Souza Date: Thu, 10 Nov 2011 18:34:23 -0200 Subject: drivers/platform/x86/dell-laptop.c: Remove some unneeded break statements Signed-off-by: Marcos Paulo de Souza Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index d93e962f2610..62d59f13dee7 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -236,9 +236,7 @@ static void __init find_tokens(const struct dmi_header *dm, void *dummy) { switch (dm->type) { case 0xd4: /* Indexed IO */ - break; case 0xd5: /* Protected Area Type 1 */ - break; case 0xd6: /* Protected Area Type 2 */ break; case 0xda: /* Calling interface */ -- cgit v1.2.3 From 35ae64fe6d08bd2f7c9f9c3c6e49182d5573341b Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 14 Nov 2011 00:25:00 -0800 Subject: dell-laptop: switch to using use MODULE_DEVICE_TABLE Use MODULE_DEVCE_TABLE instead of rolling MODULE_ALIAS by hand. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 62d59f13dee7..50dac0c9b670 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -117,6 +117,7 @@ static const struct dmi_system_id __initdata dell_device_table[] = { }, { } }; +MODULE_DEVICE_TABLE(dmi, dell_device_table); static struct dmi_system_id __devinitdata dell_blacklist[] = { /* Supported by compal-laptop */ @@ -792,6 +793,3 @@ module_exit(dell_exit); MODULE_AUTHOR("Matthew Garrett "); MODULE_DESCRIPTION("Dell laptop driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("dmi:*svnDellInc.:*:ct8:*"); -MODULE_ALIAS("dmi:*svnDellInc.:*:ct9:*"); -MODULE_ALIAS("dmi:*svnDellComputerCorporation.:*:ct8:*"); -- cgit v1.2.3 From 4585aba78afd598f034c05506b3c870ed128fd6e Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 14 Nov 2011 00:25:54 -0800 Subject: compal-laptop: switch to using use MODULE_DEVICE_TABLE Use MODULE_DEVCE_TABLE instead of rolling MODULE_ALIAS by hand. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/compal-laptop.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index d96734478324..1887e2f166a4 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -882,6 +882,7 @@ static struct dmi_system_id __initdata compal_dmi_table[] = { }, { } }; +MODULE_DEVICE_TABLE(dmi, compal_dmi_table); static void initialize_power_supply_data(struct compal_data *data) { @@ -1097,16 +1098,3 @@ MODULE_AUTHOR("Roald Frederickx (roald.frederickx@gmail.com)"); MODULE_DESCRIPTION("Compal Laptop Support"); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); - -MODULE_ALIAS("dmi:*:rnIFL90:rvrIFT00:*"); -MODULE_ALIAS("dmi:*:rnIFL90:rvrREFERENCE:*"); -MODULE_ALIAS("dmi:*:rnIFL91:rvrIFT00:*"); -MODULE_ALIAS("dmi:*:rnJFL92:rvrIFT00:*"); -MODULE_ALIAS("dmi:*:rnIFT00:rvrIFT00:*"); -MODULE_ALIAS("dmi:*:rnJHL90:rvrREFERENCE:*"); -MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron910:*"); -MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1010:*"); -MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1011:*"); -MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1012:*"); -MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1110:*"); -MODULE_ALIAS("dmi:*:svnDellInc.:pnInspiron1210:*"); -- cgit v1.2.3 From dc2cbb3b443868c5612c63dd38b005294af3f0de Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 14 Nov 2011 00:27:00 -0800 Subject: intel-oaktrail: switch to using use MODULE_DEVICE_TABLE Use MODULE_DEVCE_TABLE instead of rolling MODULE_ALIAS by hand. Signed-off-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_oaktrail.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 6ee0b5c90933..79a0c2f6be53 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -313,6 +313,7 @@ static struct dmi_system_id __initdata oaktrail_dmi_table[] = { }, { } }; +MODULE_DEVICE_TABLE(dmi, oaktrail_dmi_table); static int __init oaktrail_init(void) { @@ -394,4 +395,3 @@ MODULE_AUTHOR("Yin Kangkai (kangkai.yin@intel.com)"); MODULE_DESCRIPTION("Intel Oaktrail Platform ACPI Extras"); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL"); -MODULE_ALIAS("dmi:*:svnIntelCorporation:pnOakTrailplatform:*"); -- cgit v1.2.3 From 4d6446628a92a2cf706c256606b3031fc72a763e Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 16 Nov 2011 16:41:39 +0000 Subject: intel_scu_ipc: Remove Moorestown support All the production devices use the PC compatible version of this device so don't use the SCU interfaces or the SCU firmware interfaces. Delete lots of code and conditional paths Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_scu_ipc.c | 206 ++++--------------------------- drivers/platform/x86/intel_scu_ipcutil.c | 32 ++--- 2 files changed, 29 insertions(+), 209 deletions(-) diff --git a/drivers/platform/x86/intel_scu_ipc.c b/drivers/platform/x86/intel_scu_ipc.c index f00d0d1e0653..28bdbd03e5de 100644 --- a/drivers/platform/x86/intel_scu_ipc.c +++ b/drivers/platform/x86/intel_scu_ipc.c @@ -174,55 +174,34 @@ static int pwr_reg_rdwr(u16 *addr, u8 *data, u32 count, u32 op, u32 id) return -ENODEV; } - if (platform != MRST_CPU_CHIP_PENWELL) { - bytes = 0; - d = 0; - for (i = 0; i < count; i++) { - cbuf[bytes++] = addr[i]; - cbuf[bytes++] = addr[i] >> 8; - if (id != IPC_CMD_PCNTRL_R) - cbuf[bytes++] = data[d++]; - if (id == IPC_CMD_PCNTRL_M) - cbuf[bytes++] = data[d++]; - } - for (i = 0; i < bytes; i += 4) - ipc_data_writel(wbuf[i/4], i); - ipc_command(bytes << 16 | id << 12 | 0 << 8 | op); - } else { - for (nc = 0; nc < count; nc++, offset += 2) { - cbuf[offset] = addr[nc]; - cbuf[offset + 1] = addr[nc] >> 8; - } + for (nc = 0; nc < count; nc++, offset += 2) { + cbuf[offset] = addr[nc]; + cbuf[offset + 1] = addr[nc] >> 8; + } - if (id == IPC_CMD_PCNTRL_R) { - for (nc = 0, offset = 0; nc < count; nc++, offset += 4) - ipc_data_writel(wbuf[nc], offset); - ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); - } else if (id == IPC_CMD_PCNTRL_W) { - for (nc = 0; nc < count; nc++, offset += 1) - cbuf[offset] = data[nc]; - for (nc = 0, offset = 0; nc < count; nc++, offset += 4) - ipc_data_writel(wbuf[nc], offset); - ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); - } else if (id == IPC_CMD_PCNTRL_M) { - cbuf[offset] = data[0]; - cbuf[offset + 1] = data[1]; - ipc_data_writel(wbuf[0], 0); /* Write wbuff */ - ipc_command(4 << 16 | id << 12 | 0 << 8 | op); - } + if (id == IPC_CMD_PCNTRL_R) { + for (nc = 0, offset = 0; nc < count; nc++, offset += 4) + ipc_data_writel(wbuf[nc], offset); + ipc_command((count*2) << 16 | id << 12 | 0 << 8 | op); + } else if (id == IPC_CMD_PCNTRL_W) { + for (nc = 0; nc < count; nc++, offset += 1) + cbuf[offset] = data[nc]; + for (nc = 0, offset = 0; nc < count; nc++, offset += 4) + ipc_data_writel(wbuf[nc], offset); + ipc_command((count*3) << 16 | id << 12 | 0 << 8 | op); + } else if (id == IPC_CMD_PCNTRL_M) { + cbuf[offset] = data[0]; + cbuf[offset + 1] = data[1]; + ipc_data_writel(wbuf[0], 0); /* Write wbuff */ + ipc_command(4 << 16 | id << 12 | 0 << 8 | op); } err = busy_loop(); if (id == IPC_CMD_PCNTRL_R) { /* Read rbuf */ /* Workaround: values are read as 0 without memcpy_fromio */ memcpy_fromio(cbuf, ipcdev.ipc_base + 0x90, 16); - if (platform != MRST_CPU_CHIP_PENWELL) { - for (nc = 0, offset = 2; nc < count; nc++, offset += 3) - data[nc] = ipc_data_readb(offset); - } else { - for (nc = 0; nc < count; nc++) - data[nc] = ipc_data_readb(nc); - } + for (nc = 0; nc < count; nc++) + data[nc] = ipc_data_readb(nc); } mutex_unlock(&ipclock); return err; @@ -503,148 +482,6 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data) } EXPORT_SYMBOL(intel_scu_ipc_i2c_cntrl); -#define IPC_FW_LOAD_ADDR 0xFFFC0000 /* Storage location for FW image */ -#define IPC_FW_UPDATE_MBOX_ADDR 0xFFFFDFF4 /* Mailbox between ipc and scu */ -#define IPC_MAX_FW_SIZE 262144 /* 256K storage size for loading the FW image */ -#define IPC_FW_MIP_HEADER_SIZE 2048 /* Firmware MIP header size */ -/* IPC inform SCU to get ready for update process */ -#define IPC_CMD_FW_UPDATE_READY 0x10FE -/* IPC inform SCU to go for update process */ -#define IPC_CMD_FW_UPDATE_GO 0x20FE -/* Status code for fw update */ -#define IPC_FW_UPDATE_SUCCESS 0x444f4e45 /* Status code 'DONE' */ -#define IPC_FW_UPDATE_BADN 0x4241444E /* Status code 'BADN' */ -#define IPC_FW_TXHIGH 0x54784849 /* Status code 'IPC_FW_TXHIGH' */ -#define IPC_FW_TXLOW 0x54784c4f /* Status code 'IPC_FW_TXLOW' */ - -struct fw_update_mailbox { - u32 status; - u32 scu_flag; - u32 driver_flag; -}; - - -/** - * intel_scu_ipc_fw_update - Firmware update utility - * @buffer: firmware buffer - * @length: size of firmware buffer - * - * This function provides an interface to load the firmware into - * the SCU. Returns 0 on success or -1 on failure - */ -int intel_scu_ipc_fw_update(u8 *buffer, u32 length) -{ - void __iomem *fw_update_base; - struct fw_update_mailbox __iomem *mailbox = NULL; - int retry_cnt = 0; - u32 status; - - mutex_lock(&ipclock); - fw_update_base = ioremap_nocache(IPC_FW_LOAD_ADDR, (128*1024)); - if (fw_update_base == NULL) { - mutex_unlock(&ipclock); - return -ENOMEM; - } - mailbox = ioremap_nocache(IPC_FW_UPDATE_MBOX_ADDR, - sizeof(struct fw_update_mailbox)); - if (mailbox == NULL) { - iounmap(fw_update_base); - mutex_unlock(&ipclock); - return -ENOMEM; - } - - ipc_command(IPC_CMD_FW_UPDATE_READY); - - /* Intitialize mailbox */ - writel(0, &mailbox->status); - writel(0, &mailbox->scu_flag); - writel(0, &mailbox->driver_flag); - - /* Driver copies the 2KB MIP header to SRAM at 0xFFFC0000*/ - memcpy_toio(fw_update_base, buffer, 0x800); - - /* Driver sends "FW Update" IPC command (CMD_ID 0xFE; MSG_ID 0x02). - * Upon receiving this command, SCU will write the 2K MIP header - * from 0xFFFC0000 into NAND. - * SCU will write a status code into the Mailbox, and then set scu_flag. - */ - - ipc_command(IPC_CMD_FW_UPDATE_GO); - - /*Driver stalls until scu_flag is set */ - while (readl(&mailbox->scu_flag) != 1) { - rmb(); - mdelay(1); - } - - /* Driver checks Mailbox status. - * If the status is 'BADN', then abort (bad NAND). - * If the status is 'IPC_FW_TXLOW', then continue. - */ - while (readl(&mailbox->status) != IPC_FW_TXLOW) { - rmb(); - mdelay(10); - } - mdelay(10); - -update_retry: - if (retry_cnt > 5) - goto update_end; - - if (readl(&mailbox->status) != IPC_FW_TXLOW) - goto update_end; - buffer = buffer + 0x800; - memcpy_toio(fw_update_base, buffer, 0x20000); - writel(1, &mailbox->driver_flag); - while (readl(&mailbox->scu_flag) == 1) { - rmb(); - mdelay(1); - } - - /* check for 'BADN' */ - if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) - goto update_end; - - while (readl(&mailbox->status) != IPC_FW_TXHIGH) { - rmb(); - mdelay(10); - } - mdelay(10); - - if (readl(&mailbox->status) != IPC_FW_TXHIGH) - goto update_end; - - buffer = buffer + 0x20000; - memcpy_toio(fw_update_base, buffer, 0x20000); - writel(0, &mailbox->driver_flag); - - while (mailbox->scu_flag == 0) { - rmb(); - mdelay(1); - } - - /* check for 'BADN' */ - if (readl(&mailbox->status) == IPC_FW_UPDATE_BADN) - goto update_end; - - if (readl(&mailbox->status) == IPC_FW_TXLOW) { - ++retry_cnt; - goto update_retry; - } - -update_end: - status = readl(&mailbox->status); - - iounmap(fw_update_base); - iounmap(mailbox); - mutex_unlock(&ipclock); - - if (status == IPC_FW_UPDATE_SUCCESS) - return 0; - return -EIO; -} -EXPORT_SYMBOL(intel_scu_ipc_fw_update); - /* * Interrupt handler gets called when ioc bit of IPC_COMMAND_REG set to 1 * When ioc bit is set to 1, caller api must wait for interrupt handler called @@ -727,7 +564,6 @@ static void ipc_remove(struct pci_dev *pdev) } static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { - {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080e)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x082a)}, { 0,} }; diff --git a/drivers/platform/x86/intel_scu_ipcutil.c b/drivers/platform/x86/intel_scu_ipcutil.c index 2d0f9136ea9a..02bc5a6343c3 100644 --- a/drivers/platform/x86/intel_scu_ipcutil.c +++ b/drivers/platform/x86/intel_scu_ipcutil.c @@ -26,13 +26,10 @@ static int major; -#define MAX_FW_SIZE 264192 - /* ioctl commnds */ #define INTE_SCU_IPC_REGISTER_READ 0 #define INTE_SCU_IPC_REGISTER_WRITE 1 #define INTE_SCU_IPC_REGISTER_UPDATE 2 -#define INTE_SCU_IPC_FW_UPDATE 0xA2 struct scu_ipc_data { u32 count; /* No. of registers */ @@ -88,27 +85,14 @@ static long scu_ipc_ioctl(struct file *fp, unsigned int cmd, if (!capable(CAP_SYS_RAWIO)) return -EPERM; - if (cmd == INTE_SCU_IPC_FW_UPDATE) { - u8 *fwbuf = kmalloc(MAX_FW_SIZE, GFP_KERNEL); - if (fwbuf == NULL) - return -ENOMEM; - if (copy_from_user(fwbuf, (u8 *)arg, MAX_FW_SIZE)) { - kfree(fwbuf); - return -EFAULT; - } - ret = intel_scu_ipc_fw_update(fwbuf, MAX_FW_SIZE); - kfree(fwbuf); - return ret; - } else { - if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) - return -EFAULT; - ret = scu_reg_access(cmd, &data); - if (ret < 0) - return ret; - if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) - return -EFAULT; - return 0; - } + if (copy_from_user(&data, argp, sizeof(struct scu_ipc_data))) + return -EFAULT; + ret = scu_reg_access(cmd, &data); + if (ret < 0) + return ret; + if (copy_to_user(argp, &data, sizeof(struct scu_ipc_data))) + return -EFAULT; + return 0; } static const struct file_operations scu_ipc_fops = { -- cgit v1.2.3 From 747a562f342895bbb6cfdfcb82104b4b2ae566e6 Mon Sep 17 00:00:00 2001 From: John Hughes Date: Wed, 16 Nov 2011 19:51:57 +0100 Subject: to fix scancodes returned by sony-laptop driver Fix scancodes returned by driver to match scancodes used to remap keys. (Before the patch FN/E returned scancode 0x1B, but to remap scancode 0x14 had to be used). The scancodes returned by the sony-laptop driver for function keys did not match the scancodes used to remap keys. Also, since the scancode was sent to the input subsystem after the mapped keysym the /lib/udev/keymap utility was confused about which scancode to report for which keysym. This patch fixes the driver so the correct scancode is shown for each key. It also adds to the documentation a description of where to find the scancodes. Signed-off-by: John Hughes Acked-by: Dmitry Torokhov Signed-off-by: Matthew Garrett --- Documentation/laptops/sony-laptop.txt | 5 +++++ drivers/platform/x86/sony-laptop.c | 13 ++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Documentation/laptops/sony-laptop.txt b/Documentation/laptops/sony-laptop.txt index 2bd4e82e5d9f..0d5ac7f5287e 100644 --- a/Documentation/laptops/sony-laptop.txt +++ b/Documentation/laptops/sony-laptop.txt @@ -17,6 +17,11 @@ subsystem. See the logs of acpid or /proc/acpi/event and devices are created by the driver. Additionally, loading the driver with the debug option will report all events in the kernel log. +The "scancodes" passed to the input system (that can be remapped with udev) +are indexes to the table "sony_laptop_input_keycode_map" in the sony-laptop.c +module. For example the "FN/E" key combination (EJECTCD on some models) +generates the scancode 20 (0x14). + Backlight control: ------------------ If your laptop model supports it, you will find sysfs files in the diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 40c470507e50..8a51795aa02a 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -347,6 +347,7 @@ static void sony_laptop_report_input_event(u8 event) struct input_dev *jog_dev = sony_laptop_input.jog_dev; struct input_dev *key_dev = sony_laptop_input.key_dev; struct sony_laptop_keypress kp = { NULL }; + int scancode = -1; if (event == SONYPI_EVENT_FNKEY_RELEASED || event == SONYPI_EVENT_ANYBUTTON_RELEASED) { @@ -380,8 +381,8 @@ static void sony_laptop_report_input_event(u8 event) dprintk("sony_laptop_report_input_event, event not known: %d\n", event); break; } - if (sony_laptop_input_index[event] != -1) { - kp.key = sony_laptop_input_keycode_map[sony_laptop_input_index[event]]; + if ((scancode = sony_laptop_input_index[event]) != -1) { + kp.key = sony_laptop_input_keycode_map[scancode]; if (kp.key != KEY_UNKNOWN) kp.dev = key_dev; } @@ -389,9 +390,11 @@ static void sony_laptop_report_input_event(u8 event) } if (kp.dev) { + /* if we have a scancode we emit it so we can always + remap the key */ + if (scancode != -1) + input_event(kp.dev, EV_MSC, MSC_SCAN, scancode); input_report_key(kp.dev, kp.key, 1); - /* we emit the scancode so we can always remap the key */ - input_event(kp.dev, EV_MSC, MSC_SCAN, event); input_sync(kp.dev); /* schedule key release */ @@ -466,7 +469,7 @@ static int sony_laptop_setup_input(struct acpi_device *acpi_device) jog_dev->name = "Sony Vaio Jogdial"; jog_dev->id.bustype = BUS_ISA; jog_dev->id.vendor = PCI_VENDOR_ID_SONY; - key_dev->dev.parent = &acpi_device->dev; + jog_dev->dev.parent = &acpi_device->dev; input_set_capability(jog_dev, EV_KEY, BTN_MIDDLE); input_set_capability(jog_dev, EV_REL, REL_WHEEL); -- cgit v1.2.3 From 2a748853ca395c48ea75baa250f7cea6f0f23dbf Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Thu, 17 Nov 2011 15:30:42 +0800 Subject: dell-laptop: add 3 machines that has touchpad LED Add "Vostro 3555", "Inspiron N311z", and "Inspiron M5110" into quirks, so that they could have touchpad LED function work. Signed-off-by: AceLan Kao Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 50dac0c9b670..27600a11f3cc 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -185,6 +185,33 @@ static struct dmi_system_id __devinitdata dell_quirks[] = { }, .driver_data = &quirk_dell_vostro_v130, }, + { + .callback = dmi_matched, + .ident = "Dell Vostro 3555", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 3555"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron N311z", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron N311z"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, + { + .callback = dmi_matched, + .ident = "Dell Inspiron M5110", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron M5110"), + }, + .driver_data = &quirk_dell_vostro_v130, + }, }; static struct calling_interface_buffer *buffer; -- cgit v1.2.3 From b94e88bcb35b321b55ff4517c10eeec16079d46f Mon Sep 17 00:00:00 2001 From: Borislav Petkov Date: Thu, 24 Nov 2011 15:01:31 +0100 Subject: hdaps: Shut up gcc uninitialized variable warnings Turn off the following triggered with gcc 4.6.1 on Debian testing: drivers/platform/x86/hdaps.c: In function ‘hdaps_temp2_show’: drivers/platform/x86/hdaps.c:398:16: warning: ‘temp’ may be used uninitialized in this function [-Wuninitialized] drivers/platform/x86/hdaps.c: In function ‘hdaps_temp1_show’: drivers/platform/x86/hdaps.c:385:16: warning: ‘temp’ may be used uninitialized in this function [-Wuninitialized] Cc: Frank Seidel Cc: Matthew Garrett Cc: platform-driver-x86@vger.kernel.org Signed-off-by: Borislav Petkov Signed-off-by: Matthew Garrett --- drivers/platform/x86/hdaps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 5a34973dc164..2fdacc23cf7b 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -375,7 +375,7 @@ static ssize_t hdaps_variance_show(struct device *dev, static ssize_t hdaps_temp1_show(struct device *dev, struct device_attribute *attr, char *buf) { - u8 temp; + u8 uninitialized_var(temp); int ret; ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); @@ -388,7 +388,7 @@ static ssize_t hdaps_temp1_show(struct device *dev, static ssize_t hdaps_temp2_show(struct device *dev, struct device_attribute *attr, char *buf) { - u8 temp; + u8 uninitialized_var(temp); int ret; ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); -- cgit v1.2.3 From 73d99a224435f25976a29c3aef53590180cdb774 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 26 Nov 2011 12:14:37 +0800 Subject: platform-drivers-x86: convert drivers/platform/x86/* to use module_platform_driver() This patch converts the drivers in drivers/platform/x86/* to use the module_platform_driver() macro which makes the code smaller and a bit simpler. Cc: Hong Liu Cc: Durgadoss R Cc: Daniel Drake Signed-off-by: Axel Lin Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_mid_powerbtn.c | 12 +----------- drivers/platform/x86/intel_mid_thermal.c | 13 +------------ drivers/platform/x86/xo1-rfkill.c | 13 +------------ 3 files changed, 3 insertions(+), 35 deletions(-) diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index f1ae5078b7ec..ff3de343b64c 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -118,17 +118,7 @@ static struct platform_driver mfld_pb_driver = { .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_platform_driver(mfld_pb_driver); MODULE_AUTHOR("Hong Liu "); MODULE_DESCRIPTION("Intel Medfield Power Button Driver"); diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index ccd7b1f83519..e4cc99209ff1 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -565,18 +565,7 @@ static struct platform_driver mid_thermal_driver = { .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_platform_driver(mid_thermal_driver); MODULE_AUTHOR("Durgadoss R "); MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver"); diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index e549eeeda121..41781ed8301c 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c @@ -67,19 +67,8 @@ static struct platform_driver xo1_rfkill_driver = { .remove = __devexit_p(xo1_rfkill_remove), }; -static int __init xo1_rfkill_init(void) -{ - return platform_driver_register(&xo1_rfkill_driver); -} - -static void __exit xo1_rfkill_exit(void) -{ - platform_driver_unregister(&xo1_rfkill_driver); -} +module_platform_driver(xo1_rfkill_driver); MODULE_AUTHOR("Daniel Drake "); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:xo1-rfkill"); - -module_init(xo1_rfkill_init); -module_exit(xo1_rfkill_exit); -- cgit v1.2.3 From a6df48943a408b493d1aa141791d614a529d484e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 10:59:58 +0100 Subject: samsung-laptop: put all local variables in a single structure Even if this driver can only be loaded once, it is still a good idea to create some kind of context structure. Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 291 ++++++++++++++++++++-------------- 1 file changed, 170 insertions(+), 121 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index fd73ea89b857..c2a74bfc017c 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -217,16 +217,23 @@ static const struct sabi_config sabi_configs[] = { { }, }; -static const struct sabi_config *sabi_config; +struct samsung_laptop { + const struct sabi_config *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 bool has_stepping_quirk; + void __iomem *sabi; + void __iomem *sabi_iface; + void __iomem *f0000_segment; + + struct mutex sabi_mutex; + + struct platform_device *pdev; + struct backlight_device *backlight_device; + struct rfkill *rfk; + + bool has_stepping_quirk; +}; + +static struct samsung_laptop *samsung; static bool force; module_param(force, bool, 0); @@ -239,27 +246,28 @@ MODULE_PARM_DESC(debug, "Debug enabled or not"); static int sabi_get_command(u8 command, struct sabi_retval *sretval) { + const struct sabi_config *config = samsung->config; int retval = 0; - u16 port = readw(sabi + sabi_config->header_offsets.port); + u16 port = readw(samsung->sabi + config->header_offsets.port); u8 complete, iface_data; - mutex_lock(&sabi_mutex); + mutex_lock(&samsung->sabi_mutex); /* enable memory to be able to write to it */ - outb(readb(sabi + sabi_config->header_offsets.en_mem), port); + outb(readb(samsung->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); + writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); + writew(command, samsung->sabi_iface + SABI_IFACE_SUB); + writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); + outb(readb(samsung->sabi + config->header_offsets.iface_func), port); /* write protect memory to make it safe */ - outb(readb(sabi + sabi_config->header_offsets.re_mem), port); + outb(readb(samsung->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); + complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); + iface_data = readb(samsung->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); @@ -272,107 +280,112 @@ static int sabi_get_command(u8 command, struct sabi_retval *sretval) * 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); + sretval->retval[0] = readb(samsung->sabi_iface + SABI_IFACE_DATA); + sretval->retval[1] = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); + sretval->retval[2] = readb(samsung->sabi_iface + SABI_IFACE_DATA + 2); + sretval->retval[3] = readb(samsung->sabi_iface + SABI_IFACE_DATA + 3); exit: - mutex_unlock(&sabi_mutex); + mutex_unlock(&samsung->sabi_mutex); return retval; } static int sabi_set_command(u8 command, u8 data) { + const struct sabi_config *config = samsung->config; int retval = 0; - u16 port = readw(sabi + sabi_config->header_offsets.port); + u16 port = readw(samsung->sabi + config->header_offsets.port); u8 complete, iface_data; - mutex_lock(&sabi_mutex); + mutex_lock(&samsung->sabi_mutex); /* enable memory to be able to write to it */ - outb(readb(sabi + sabi_config->header_offsets.en_mem), port); + outb(readb(samsung->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); + writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); + writew(command, samsung->sabi_iface + SABI_IFACE_SUB); + writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); + writeb(data, samsung->sabi_iface + SABI_IFACE_DATA); + outb(readb(samsung->sabi + config->header_offsets.iface_func), port); /* write protect memory to make it safe */ - outb(readb(sabi + sabi_config->header_offsets.re_mem), port); + outb(readb(samsung->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); + complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); + iface_data = readb(samsung->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); + mutex_unlock(&samsung->sabi_mutex); return retval; } static void test_backlight(void) { + const struct sabi_commands *commands = &samsung->config->commands; struct sabi_retval sretval; - sabi_get_command(sabi_config->commands.get_backlight, &sretval); + sabi_get_command(commands->get_backlight, &sretval); printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); - sabi_set_command(sabi_config->commands.set_backlight, 0); + sabi_set_command(commands->set_backlight, 0); printk(KERN_DEBUG "backlight should be off\n"); - sabi_get_command(sabi_config->commands.get_backlight, &sretval); + sabi_get_command(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); + sabi_set_command(commands->set_backlight, 1); printk(KERN_DEBUG "backlight should be on\n"); - sabi_get_command(sabi_config->commands.get_backlight, &sretval); + sabi_get_command(commands->get_backlight, &sretval); printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); } static void test_wireless(void) { + const struct sabi_commands *commands = &samsung->config->commands; struct sabi_retval sretval; - sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); + sabi_get_command(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); + sabi_set_command(commands->set_wireless_button, 0); printk(KERN_DEBUG "wireless led should be off\n"); - sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); + sabi_get_command(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); + sabi_set_command(commands->set_wireless_button, 1); printk(KERN_DEBUG "wireless led should be on\n"); - sabi_get_command(sabi_config->commands.get_wireless_button, &sretval); + sabi_get_command(commands->get_wireless_button, &sretval); printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); } static u8 read_brightness(void) { + const struct sabi_config *config = samsung->config; + const struct sabi_commands *commands = &samsung->config->commands; struct sabi_retval sretval; int user_brightness = 0; int retval; - retval = sabi_get_command(sabi_config->commands.get_brightness, + retval = sabi_get_command(commands->get_brightness, &sretval); if (!retval) { user_brightness = sretval.retval[0]; - if (user_brightness > sabi_config->min_brightness) - user_brightness -= sabi_config->min_brightness; + if (user_brightness > config->min_brightness) + user_brightness -= config->min_brightness; else user_brightness = 0; } @@ -381,9 +394,11 @@ static u8 read_brightness(void) static void set_brightness(u8 user_brightness) { - u8 user_level = user_brightness + sabi_config->min_brightness; + const struct sabi_config *config = samsung->config; + const struct sabi_commands *commands = &samsung->config->commands; + u8 user_level = user_brightness + config->min_brightness; - if (has_stepping_quirk && user_level != 0) { + if (samsung->has_stepping_quirk && user_level != 0) { /* * short circuit if the specified level is what's already set * to prevent the screen from flickering needlessly @@ -391,10 +406,10 @@ static void set_brightness(u8 user_brightness) if (user_brightness == read_brightness()) return; - sabi_set_command(sabi_config->commands.set_brightness, 0); + sabi_set_command(commands->set_brightness, 0); } - sabi_set_command(sabi_config->commands.set_brightness, user_level); + sabi_set_command(commands->set_brightness, user_level); } static int get_brightness(struct backlight_device *bd) @@ -425,11 +440,11 @@ static void check_for_stepping_quirk(void) else check_level = initial_level - 2; - has_stepping_quirk = false; + samsung->has_stepping_quirk = false; set_brightness(check_level); if (read_brightness() != check_level) { - has_stepping_quirk = true; + samsung->has_stepping_quirk = true; pr_info("enabled workaround for brightness stepping quirk\n"); } @@ -438,12 +453,14 @@ static void check_for_stepping_quirk(void) static int update_status(struct backlight_device *bd) { + const struct sabi_commands *commands = &samsung->config->commands; + set_brightness(bd->props.brightness); if (bd->props.power == FB_BLANK_UNBLANK) - sabi_set_command(sabi_config->commands.set_backlight, 1); + sabi_set_command(commands->set_backlight, 1); else - sabi_set_command(sabi_config->commands.set_backlight, 0); + sabi_set_command(commands->set_backlight, 0); return 0; } @@ -454,15 +471,17 @@ static const struct backlight_ops backlight_ops = { static int rfkill_set(void *data, bool blocked) { + const struct sabi_commands *commands = &samsung->config->commands; + /* Do something with blocked...*/ /* * blocked == false is on * blocked == true is off */ if (blocked) - sabi_set_command(sabi_config->commands.set_wireless_button, 0); + sabi_set_command(commands->set_wireless_button, 0); else - sabi_set_command(sabi_config->commands.set_wireless_button, 1); + sabi_set_command(commands->set_wireless_button, 1); return 0; } @@ -471,18 +490,18 @@ static struct rfkill_ops rfkill_ops = { .set_block = rfkill_set, }; -static int init_wireless(struct platform_device *sdev) +static int init_wireless(struct platform_device *pdev) { int retval; - rfk = rfkill_alloc("samsung-wifi", &sdev->dev, RFKILL_TYPE_WLAN, - &rfkill_ops, NULL); - if (!rfk) + samsung->rfk = rfkill_alloc("samsung-wifi", &samsung->pdev->dev, RFKILL_TYPE_WLAN, + &rfkill_ops, NULL); + if (!samsung->rfk) return -ENOMEM; - retval = rfkill_register(rfk); + retval = rfkill_register(samsung->rfk); if (retval) { - rfkill_destroy(rfk); + rfkill_destroy(samsung->rfk); return -ENODEV; } @@ -491,27 +510,28 @@ static int init_wireless(struct platform_device *sdev) static void destroy_wireless(void) { - rfkill_unregister(rfk); - rfkill_destroy(rfk); + rfkill_unregister(samsung->rfk); + rfkill_destroy(samsung->rfk); } static ssize_t get_performance_level(struct device *dev, struct device_attribute *attr, char *buf) { + const struct sabi_config *config = samsung->config; struct sabi_retval sretval; int retval; int i; /* Read the state */ - retval = sabi_get_command(sabi_config->commands.get_performance_level, + retval = sabi_get_command(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); + for (i = 0; config->performance_levels[i].name; ++i) { + if (sretval.retval[0] == config->performance_levels[i].value) + return sprintf(buf, "%s\n", config->performance_levels[i].name); } return sprintf(buf, "%s\n", "unknown"); } @@ -520,18 +540,20 @@ static ssize_t set_performance_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + const struct sabi_config *config = samsung->config; + if (count >= 1) { int i; - for (i = 0; sabi_config->performance_levels[i].name; ++i) { + for (i = 0; config->performance_levels[i].name; ++i) { const struct sabi_performance_level *level = - &sabi_config->performance_levels[i]; + &config->performance_levels[i]; if (!strncasecmp(level->name, buf, strlen(level->name))) { - sabi_set_command(sabi_config->commands.set_performance_level, + sabi_set_command(config->commands.set_performance_level, level->value); break; } } - if (!sabi_config->performance_levels[i].name) + if (!config->performance_levels[i].name) return -EINVAL; } return count; @@ -805,6 +827,9 @@ static int find_signature(void __iomem *memcheck, const char *testStr) static int __init samsung_init(void) { + const struct sabi_config *config = NULL; + const struct sabi_commands *commands; + struct backlight_device *bd; struct backlight_properties props; struct sabi_retval sretval; unsigned int ifaceP; @@ -812,21 +837,26 @@ static int __init samsung_init(void) 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) { + samsung = kzalloc(sizeof(*samsung), GFP_KERNEL); + if (!samsung) + return -ENOMEM; + + mutex_init(&samsung->sabi_mutex); + + samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); + if (!samsung->f0000_segment) { pr_err("Can't map the segment at 0xf0000\n"); - return -EINVAL; + goto error_cant_map; } /* 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); + samsung->config = &sabi_configs[i]; + loca = find_signature(samsung->f0000_segment, + samsung->config->test_string); if (loca != 0xffff) break; } @@ -836,51 +866,60 @@ static int __init samsung_init(void) goto error_no_signature; } + config = samsung->config; + commands = &config->commands; + /* point to the SMI port Number */ loca += 1; - sabi = (f0000_segment + loca); + samsung->sabi = (samsung->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)); + readw(samsung->sabi + + config->header_offsets.port)); printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", - readb(sabi + sabi_config->header_offsets.iface_func)); + readb(samsung->sabi + + config->header_offsets.iface_func)); printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", - readb(sabi + sabi_config->header_offsets.en_mem)); + readb(samsung->sabi + + config->header_offsets.en_mem)); printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", - readb(sabi + sabi_config->header_offsets.re_mem)); + readb(samsung->sabi + + config->header_offsets.re_mem)); printk(KERN_DEBUG " SABI data offset = 0x%04x\n", - readw(sabi + sabi_config->header_offsets.data_offset)); + readw(samsung->sabi + + config->header_offsets.data_offset)); printk(KERN_DEBUG " SABI data segment = 0x%04x\n", - readw(sabi + sabi_config->header_offsets.data_segment)); + readw(samsung->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) { + ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; + ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; + samsung->sabi_iface = ioremap_nocache(ifaceP, 16); + if (!samsung->sabi_iface) { pr_err("Can't remap %x\n", ifaceP); goto error_no_signature; } if (debug) { printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); - printk(KERN_DEBUG "sabi_iface = %p\n", sabi_iface); + printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface); test_backlight(); test_wireless(); - retval = sabi_get_command(sabi_config->commands.get_brightness, + retval = sabi_get_command(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, + if (commands->set_linux != 0xff) { + retval = sabi_set_command(commands->set_linux, 0x81); if (retval) { pr_warn("Linux mode was not set!\n"); @@ -892,30 +931,32 @@ static int __init samsung_init(void) check_for_stepping_quirk(); /* knock up a platform device to hang stuff off of */ - sdev = platform_device_register_simple("samsung", -1, NULL, 0); - if (IS_ERR(sdev)) + samsung->pdev = platform_device_register_simple("samsung", -1, NULL, 0); + if (IS_ERR(samsung->pdev)) goto error_no_platform; /* create a backlight device to talk to this one */ memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; - props.max_brightness = sabi_config->max_brightness - - sabi_config->min_brightness; - backlight_device = backlight_device_register("samsung", &sdev->dev, - NULL, &backlight_ops, - &props); - if (IS_ERR(backlight_device)) + props.max_brightness = config->max_brightness - + config->min_brightness; + bd = backlight_device_register("samsung", &samsung->pdev->dev, + NULL, &backlight_ops, + &props); + if (IS_ERR(bd)) goto error_no_backlight; - backlight_device->props.brightness = read_brightness(); - backlight_device->props.power = FB_BLANK_UNBLANK; - backlight_update_status(backlight_device); + samsung->backlight_device = bd; + samsung->backlight_device->props.brightness = read_brightness(); + samsung->backlight_device->props.power = FB_BLANK_UNBLANK; + backlight_update_status(samsung->backlight_device); - retval = init_wireless(sdev); + retval = init_wireless(samsung->pdev); if (retval) goto error_no_rfk; - retval = device_create_file(&sdev->dev, &dev_attr_performance_level); + retval = device_create_file(&samsung->pdev->dev, + &dev_attr_performance_level); if (retval) goto error_file_create; @@ -925,31 +966,39 @@ error_file_create: destroy_wireless(); error_no_rfk: - backlight_device_unregister(backlight_device); + backlight_device_unregister(samsung->backlight_device); error_no_backlight: - platform_device_unregister(sdev); + platform_device_unregister(samsung->pdev); error_no_platform: - iounmap(sabi_iface); + iounmap(samsung->sabi_iface); error_no_signature: - iounmap(f0000_segment); + iounmap(samsung->f0000_segment); + +error_cant_map: + kfree(samsung); + samsung = NULL; return -EINVAL; } static void __exit samsung_exit(void) { + const struct sabi_commands *commands = &samsung->config->commands; + /* Turn off "Linux" mode in the BIOS */ - if (sabi_config->commands.set_linux != 0xff) - sabi_set_command(sabi_config->commands.set_linux, 0x80); + if (commands->set_linux != 0xff) + sabi_set_command(commands->set_linux, 0x80); - device_remove_file(&sdev->dev, &dev_attr_performance_level); - backlight_device_unregister(backlight_device); + device_remove_file(&samsung->pdev->dev, &dev_attr_performance_level); + backlight_device_unregister(samsung->backlight_device); destroy_wireless(); - iounmap(sabi_iface); - iounmap(f0000_segment); - platform_device_unregister(sdev); + iounmap(samsung->sabi_iface); + iounmap(samsung->f0000_segment); + platform_device_unregister(samsung->pdev); + kfree(samsung); + samsung = NULL; } module_init(samsung_init); -- cgit v1.2.3 From 5dea7a2094d5e60fe8f8ec4277d22d7ad6fa8c26 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 10:59:59 +0100 Subject: samsung-laptop: move code into init/exit functions Create _init()/_exit() function for each subsystem, remove the local struct samsung_laptop * and only keep a struct platform_device * that can only be used in samsung_init() and samsung_exit(). Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 609 ++++++++++++++++++++-------------- 1 file changed, 360 insertions(+), 249 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index c2a74bfc017c..1dd81d148cb7 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -226,14 +226,14 @@ struct samsung_laptop { struct mutex sabi_mutex; - struct platform_device *pdev; + struct platform_device *platform_device; struct backlight_device *backlight_device; struct rfkill *rfk; bool has_stepping_quirk; }; -static struct samsung_laptop *samsung; + static bool force; module_param(force, bool, 0); @@ -244,7 +244,8 @@ static bool 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) +static int sabi_get_command(struct samsung_laptop *samsung, + u8 command, struct sabi_retval *sretval) { const struct sabi_config *config = samsung->config; int retval = 0; @@ -291,7 +292,8 @@ exit: } -static int sabi_set_command(u8 command, u8 data) +static int sabi_set_command(struct samsung_laptop *samsung, + u8 command, u8 data) { const struct sabi_config *config = samsung->config; int retval = 0; @@ -326,53 +328,53 @@ static int sabi_set_command(u8 command, u8 data) return retval; } -static void test_backlight(void) +static void test_backlight(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; struct sabi_retval sretval; - sabi_get_command(commands->get_backlight, &sretval); + sabi_get_command(samsung, commands->get_backlight, &sretval); printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); - sabi_set_command(commands->set_backlight, 0); + sabi_set_command(samsung, commands->set_backlight, 0); printk(KERN_DEBUG "backlight should be off\n"); - sabi_get_command(commands->get_backlight, &sretval); + sabi_get_command(samsung, commands->get_backlight, &sretval); printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); msleep(1000); - sabi_set_command(commands->set_backlight, 1); + sabi_set_command(samsung, commands->set_backlight, 1); printk(KERN_DEBUG "backlight should be on\n"); - sabi_get_command(commands->get_backlight, &sretval); + sabi_get_command(samsung, commands->get_backlight, &sretval); printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); } -static void test_wireless(void) +static void test_wireless(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; struct sabi_retval sretval; - sabi_get_command(commands->get_wireless_button, &sretval); + sabi_get_command(samsung, commands->get_wireless_button, &sretval); printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); - sabi_set_command(commands->set_wireless_button, 0); + sabi_set_command(samsung, commands->set_wireless_button, 0); printk(KERN_DEBUG "wireless led should be off\n"); - sabi_get_command(commands->get_wireless_button, &sretval); + sabi_get_command(samsung, commands->get_wireless_button, &sretval); printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); msleep(1000); - sabi_set_command(commands->set_wireless_button, 1); + sabi_set_command(samsung, commands->set_wireless_button, 1); printk(KERN_DEBUG "wireless led should be on\n"); - sabi_get_command(commands->get_wireless_button, &sretval); + sabi_get_command(samsung, commands->get_wireless_button, &sretval); printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); } -static u8 read_brightness(void) +static int read_brightness(struct samsung_laptop *samsung) { const struct sabi_config *config = samsung->config; const struct sabi_commands *commands = &samsung->config->commands; @@ -380,7 +382,7 @@ static u8 read_brightness(void) int user_brightness = 0; int retval; - retval = sabi_get_command(commands->get_brightness, + retval = sabi_get_command(samsung, commands->get_brightness, &sretval); if (!retval) { user_brightness = sretval.retval[0]; @@ -392,7 +394,7 @@ static u8 read_brightness(void) return user_brightness; } -static void set_brightness(u8 user_brightness) +static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) { const struct sabi_config *config = samsung->config; const struct sabi_commands *commands = &samsung->config->commands; @@ -403,25 +405,27 @@ static void set_brightness(u8 user_brightness) * short circuit if the specified level is what's already set * to prevent the screen from flickering needlessly */ - if (user_brightness == read_brightness()) + if (user_brightness == read_brightness(samsung)) return; - sabi_set_command(commands->set_brightness, 0); + sabi_set_command(samsung, commands->set_brightness, 0); } - sabi_set_command(commands->set_brightness, user_level); + sabi_set_command(samsung, commands->set_brightness, user_level); } static int get_brightness(struct backlight_device *bd) { - return (int)read_brightness(); + struct samsung_laptop *samsung = bl_get_data(bd); + + return read_brightness(samsung); } -static void check_for_stepping_quirk(void) +static void check_for_stepping_quirk(struct samsung_laptop *samsung) { - u8 initial_level; - u8 check_level; - u8 orig_level = read_brightness(); + int initial_level; + int check_level; + int orig_level = read_brightness(samsung); /* * Some laptops exhibit the strange behaviour of stepping toward @@ -431,9 +435,9 @@ static void check_for_stepping_quirk(void) */ if (orig_level == 0) - set_brightness(1); + set_brightness(samsung, 1); - initial_level = read_brightness(); + initial_level = read_brightness(samsung); if (initial_level <= 2) check_level = initial_level + 2; @@ -441,26 +445,28 @@ static void check_for_stepping_quirk(void) check_level = initial_level - 2; samsung->has_stepping_quirk = false; - set_brightness(check_level); + set_brightness(samsung, check_level); - if (read_brightness() != check_level) { + if (read_brightness(samsung) != check_level) { samsung->has_stepping_quirk = true; pr_info("enabled workaround for brightness stepping quirk\n"); } - set_brightness(orig_level); + set_brightness(samsung, orig_level); } static int update_status(struct backlight_device *bd) { + struct samsung_laptop *samsung = bl_get_data(bd); const struct sabi_commands *commands = &samsung->config->commands; - set_brightness(bd->props.brightness); + set_brightness(samsung, bd->props.brightness); if (bd->props.power == FB_BLANK_UNBLANK) - sabi_set_command(commands->set_backlight, 1); + sabi_set_command(samsung, commands->set_backlight, 1); else - sabi_set_command(commands->set_backlight, 0); + sabi_set_command(samsung, commands->set_backlight, 0); + return 0; } @@ -471,6 +477,7 @@ static const struct backlight_ops backlight_ops = { static int rfkill_set(void *data, bool blocked) { + struct samsung_laptop *samsung = data; const struct sabi_commands *commands = &samsung->config->commands; /* Do something with blocked...*/ @@ -479,9 +486,9 @@ static int rfkill_set(void *data, bool blocked) * blocked == true is off */ if (blocked) - sabi_set_command(commands->set_wireless_button, 0); + sabi_set_command(samsung, commands->set_wireless_button, 0); else - sabi_set_command(commands->set_wireless_button, 1); + sabi_set_command(samsung, commands->set_wireless_button, 1); return 0; } @@ -490,40 +497,18 @@ static struct rfkill_ops rfkill_ops = { .set_block = rfkill_set, }; -static int init_wireless(struct platform_device *pdev) -{ - int retval; - - samsung->rfk = rfkill_alloc("samsung-wifi", &samsung->pdev->dev, RFKILL_TYPE_WLAN, - &rfkill_ops, NULL); - if (!samsung->rfk) - return -ENOMEM; - - retval = rfkill_register(samsung->rfk); - if (retval) { - rfkill_destroy(samsung->rfk); - return -ENODEV; - } - - return 0; -} - -static void destroy_wireless(void) -{ - rfkill_unregister(samsung->rfk); - rfkill_destroy(samsung->rfk); -} - static ssize_t get_performance_level(struct device *dev, struct device_attribute *attr, char *buf) { + struct samsung_laptop *samsung = dev_get_drvdata(dev); const struct sabi_config *config = samsung->config; + const struct sabi_commands *commands = &config->commands; struct sabi_retval sretval; int retval; int i; /* Read the state */ - retval = sabi_get_command(config->commands.get_performance_level, + retval = sabi_get_command(samsung, commands->get_performance_level, &sretval); if (retval) return retval; @@ -540,32 +525,285 @@ static ssize_t set_performance_level(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct samsung_laptop *samsung = dev_get_drvdata(dev); const struct sabi_config *config = samsung->config; + const struct sabi_commands *commands = &config->commands; + int i; - if (count >= 1) { - int i; - for (i = 0; config->performance_levels[i].name; ++i) { - const struct sabi_performance_level *level = - &config->performance_levels[i]; - if (!strncasecmp(level->name, buf, strlen(level->name))) { - sabi_set_command(config->commands.set_performance_level, - level->value); - break; - } + if (count < 1) + return count; + + for (i = 0; config->performance_levels[i].name; ++i) { + const struct sabi_performance_level *level = + &config->performance_levels[i]; + if (!strncasecmp(level->name, buf, strlen(level->name))) { + sabi_set_command(samsung, + commands->set_performance_level, + level->value); + break; } - if (!config->performance_levels[i].name) - return -EINVAL; } + + if (!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 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 void samsung_rfkill_exit(struct samsung_laptop *samsung) +{ + if (samsung->rfk) { + rfkill_unregister(samsung->rfk); + rfkill_destroy(samsung->rfk); + samsung->rfk = NULL; + } +} + +static int __init samsung_rfkill_init(struct samsung_laptop *samsung) +{ + int retval; + + samsung->rfk = rfkill_alloc("samsung-wifi", + &samsung->platform_device->dev, + RFKILL_TYPE_WLAN, + &rfkill_ops, samsung); + if (!samsung->rfk) + return -ENOMEM; + + retval = rfkill_register(samsung->rfk); + if (retval) { + rfkill_destroy(samsung->rfk); + samsung->rfk = NULL; + return -ENODEV; + } + + return 0; +} + +static void samsung_backlight_exit(struct samsung_laptop *samsung) +{ + if (samsung->backlight_device) { + backlight_device_unregister(samsung->backlight_device); + samsung->backlight_device = NULL; + } +} + +static int __init samsung_backlight_init(struct samsung_laptop *samsung) +{ + struct backlight_device *bd; + struct backlight_properties props; + + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = samsung->config->max_brightness - + samsung->config->min_brightness; + + bd = backlight_device_register("samsung", + &samsung->platform_device->dev, + samsung, &backlight_ops, + &props); + if (IS_ERR(bd)) + return PTR_ERR(bd); + + samsung->backlight_device = bd; + samsung->backlight_device->props.brightness = read_brightness(samsung); + samsung->backlight_device->props.power = FB_BLANK_UNBLANK; + backlight_update_status(samsung->backlight_device); + + return 0; +} + +static void samsung_sysfs_exit(struct samsung_laptop *samsung) +{ + device_remove_file(&samsung->platform_device->dev, + &dev_attr_performance_level); +} + +static int __init samsung_sysfs_init(struct samsung_laptop *samsung) +{ + return device_create_file(&samsung->platform_device->dev, + &dev_attr_performance_level); +} + +static void samsung_sabi_exit(struct samsung_laptop *samsung) +{ + const struct sabi_config *config = samsung->config; + + /* Turn off "Linux" mode in the BIOS */ + if (config && config->commands.set_linux != 0xff) + sabi_set_command(samsung, config->commands.set_linux, 0x80); + + if (samsung->sabi_iface) { + iounmap(samsung->sabi_iface); + samsung->sabi_iface = NULL; + } + if (samsung->f0000_segment) { + iounmap(samsung->f0000_segment); + samsung->f0000_segment = NULL; + } + + samsung->config = NULL; +} + +static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca) +{ + const struct sabi_config *config = samsung->config; + + 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(samsung->sabi + config->header_offsets.port)); + printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", + readb(samsung->sabi + config->header_offsets.iface_func)); + printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", + readb(samsung->sabi + config->header_offsets.en_mem)); + printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", + readb(samsung->sabi + config->header_offsets.re_mem)); + printk(KERN_DEBUG " SABI data offset = 0x%04x\n", + readw(samsung->sabi + config->header_offsets.data_offset)); + printk(KERN_DEBUG " SABI data segment = 0x%04x\n", + readw(samsung->sabi + config->header_offsets.data_segment)); +} + +static void __init samsung_sabi_selftest(struct samsung_laptop *samsung, + unsigned int ifaceP) +{ + const struct sabi_config *config = samsung->config; + struct sabi_retval sretval; + + printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); + printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface); + + test_backlight(samsung); + test_wireless(samsung); + + sabi_get_command(samsung, config->commands.get_brightness, &sretval); + printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]); +} + +static int __init samsung_sabi_init(struct samsung_laptop *samsung) +{ + const struct sabi_config *config = NULL; + const struct sabi_commands *commands; + unsigned int ifaceP; + int ret = 0; + int i; + int loca; + + samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); + if (!samsung->f0000_segment) { + pr_err("Can't map the segment at 0xf0000\n"); + ret = -EINVAL; + goto exit; + } + + /* Try to find one of the signatures in memory to find the header */ + for (i = 0; sabi_configs[i].test_string != 0; ++i) { + samsung->config = &sabi_configs[i]; + loca = find_signature(samsung->f0000_segment, + samsung->config->test_string); + if (loca != 0xffff) + break; + } + + if (loca == 0xffff) { + pr_err("This computer does not support SABI\n"); + ret = -ENODEV; + goto exit; + } + + config = samsung->config; + commands = &config->commands; + + /* point to the SMI port Number */ + loca += 1; + samsung->sabi = (samsung->f0000_segment + loca); + + if (debug) + samsung_sabi_infos(samsung, loca); + + /* Get a pointer to the SABI Interface */ + ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; + ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; + samsung->sabi_iface = ioremap_nocache(ifaceP, 16); + if (!samsung->sabi_iface) { + pr_err("Can't remap %x\n", ifaceP); + ret = -EINVAL; + goto exit; + } + + if (debug) + samsung_sabi_selftest(samsung, ifaceP); + + /* Turn on "Linux" mode in the BIOS */ + if (commands->set_linux != 0xff) { + int retval = sabi_set_command(samsung, + commands->set_linux, 0x81); + if (retval) { + pr_warn("Linux mode was not set!\n"); + ret = -ENODEV; + goto exit; + } + } + + /* Check for stepping quirk */ + check_for_stepping_quirk(samsung); + +exit: + if (ret) + samsung_sabi_exit(samsung); + + return ret; +} + +static void samsung_platform_exit(struct samsung_laptop *samsung) +{ + if (samsung->platform_device) { + platform_device_unregister(samsung->platform_device); + samsung->platform_device = NULL; + } +} + +static int __init samsung_platform_init(struct samsung_laptop *samsung) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple("samsung", -1, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + samsung->platform_device = pdev; + platform_set_drvdata(samsung->platform_device, samsung); + return 0; +} + static int __init dmi_check_cb(const struct dmi_system_id *id) { - pr_info("found laptop model '%s'\n", - id->ident); + pr_info("found laptop model '%s'\n", id->ident); return 1; } @@ -806,36 +1044,12 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { }; 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 struct platform_device *samsung_platform_device; static int __init samsung_init(void) { - const struct sabi_config *config = NULL; - const struct sabi_commands *commands; - struct backlight_device *bd; - struct backlight_properties props; - struct sabi_retval sretval; - unsigned int ifaceP; - int i; - int loca; - int retval; + struct samsung_laptop *samsung; + int ret; if (!force && !dmi_check_system(samsung_dmi_table)) return -ENODEV; @@ -846,159 +1060,56 @@ static int __init samsung_init(void) mutex_init(&samsung->sabi_mutex); - samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); - if (!samsung->f0000_segment) { - pr_err("Can't map the segment at 0xf0000\n"); - goto error_cant_map; - } - - /* Try to find one of the signatures in memory to find the header */ - for (i = 0; sabi_configs[i].test_string != 0; ++i) { - samsung->config = &sabi_configs[i]; - loca = find_signature(samsung->f0000_segment, - samsung->config->test_string); - if (loca != 0xffff) - break; - } - - if (loca == 0xffff) { - pr_err("This computer does not support SABI\n"); - goto error_no_signature; - } - - config = samsung->config; - commands = &config->commands; - - /* point to the SMI port Number */ - loca += 1; - samsung->sabi = (samsung->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(samsung->sabi + - config->header_offsets.port)); - printk(KERN_DEBUG " SMI Interface Function = 0x%02x\n", - readb(samsung->sabi + - config->header_offsets.iface_func)); - printk(KERN_DEBUG " SMI enable memory buffer = 0x%02x\n", - readb(samsung->sabi + - config->header_offsets.en_mem)); - printk(KERN_DEBUG " SMI restore memory buffer = 0x%02x\n", - readb(samsung->sabi + - config->header_offsets.re_mem)); - printk(KERN_DEBUG " SABI data offset = 0x%04x\n", - readw(samsung->sabi + - config->header_offsets.data_offset)); - printk(KERN_DEBUG " SABI data segment = 0x%04x\n", - readw(samsung->sabi + - config->header_offsets.data_segment)); - } - - /* Get a pointer to the SABI Interface */ - ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; - ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; - samsung->sabi_iface = ioremap_nocache(ifaceP, 16); - if (!samsung->sabi_iface) { - pr_err("Can't remap %x\n", ifaceP); - goto error_no_signature; - } - if (debug) { - printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); - printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface); - - test_backlight(); - test_wireless(); - - retval = sabi_get_command(commands->get_brightness, - &sretval); - printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]); - } - - /* Turn on "Linux" mode in the BIOS */ - if (commands->set_linux != 0xff) { - retval = sabi_set_command(commands->set_linux, - 0x81); - if (retval) { - pr_warn("Linux mode was not set!\n"); - goto error_no_platform; - } - } - - /* Check for stepping quirk */ - check_for_stepping_quirk(); - - /* knock up a platform device to hang stuff off of */ - samsung->pdev = platform_device_register_simple("samsung", -1, NULL, 0); - if (IS_ERR(samsung->pdev)) - goto error_no_platform; - - /* create a backlight device to talk to this one */ - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = config->max_brightness - - config->min_brightness; - bd = backlight_device_register("samsung", &samsung->pdev->dev, - NULL, &backlight_ops, - &props); - if (IS_ERR(bd)) - goto error_no_backlight; - - samsung->backlight_device = bd; - samsung->backlight_device->props.brightness = read_brightness(); - samsung->backlight_device->props.power = FB_BLANK_UNBLANK; - backlight_update_status(samsung->backlight_device); - - retval = init_wireless(samsung->pdev); - if (retval) - goto error_no_rfk; - - retval = device_create_file(&samsung->pdev->dev, - &dev_attr_performance_level); - if (retval) - goto error_file_create; - - return 0; - -error_file_create: - destroy_wireless(); - -error_no_rfk: - backlight_device_unregister(samsung->backlight_device); - -error_no_backlight: - platform_device_unregister(samsung->pdev); - -error_no_platform: - iounmap(samsung->sabi_iface); - -error_no_signature: - iounmap(samsung->f0000_segment); - -error_cant_map: + ret = samsung_platform_init(samsung); + if (ret) + goto error_platform; + + ret = samsung_sabi_init(samsung); + if (ret) + goto error_sabi; + + ret = samsung_sysfs_init(samsung); + if (ret) + goto error_sysfs; + + ret = samsung_backlight_init(samsung); + if (ret) + goto error_backlight; + + ret = samsung_rfkill_init(samsung); + if (ret) + goto error_rfkill; + + samsung_platform_device = samsung->platform_device; + return ret; + +error_rfkill: + samsung_backlight_exit(samsung); +error_backlight: + samsung_sysfs_exit(samsung); +error_sysfs: + samsung_sabi_exit(samsung); +error_sabi: + samsung_platform_exit(samsung); +error_platform: kfree(samsung); - samsung = NULL; - return -EINVAL; + return ret; } static void __exit samsung_exit(void) { - const struct sabi_commands *commands = &samsung->config->commands; + struct samsung_laptop *samsung; + + samsung = platform_get_drvdata(samsung_platform_device); + + samsung_rfkill_exit(samsung); + samsung_backlight_exit(samsung); + samsung_sysfs_exit(samsung); + samsung_sabi_exit(samsung); + samsung_platform_exit(samsung); - /* Turn off "Linux" mode in the BIOS */ - if (commands->set_linux != 0xff) - sabi_set_command(commands->set_linux, 0x80); - - device_remove_file(&samsung->pdev->dev, &dev_attr_performance_level); - backlight_device_unregister(samsung->backlight_device); - destroy_wireless(); - iounmap(samsung->sabi_iface); - iounmap(samsung->f0000_segment); - platform_device_unregister(samsung->pdev); kfree(samsung); - samsung = NULL; + samsung_platform_device = NULL; } module_init(samsung_init); -- cgit v1.2.3 From f34cd9ca9320876e9c12764f052004628a03ba2d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:00 +0100 Subject: samsung-laptop: don't handle backlight if handled by acpi/video samsung-laptop is not at all related to ACPI, but since this interface is not documented at all, and the driver has to use it at load to understand how it works on the laptop, I think it's a good idea to disable it if a better solution is available. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 1dd81d148cb7..b391a38bc7c0 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -21,6 +21,7 @@ #include #include #include +#include /* * This driver is needed because a number of Samsung laptops do not hook @@ -230,6 +231,7 @@ struct samsung_laptop { struct backlight_device *backlight_device; struct rfkill *rfk; + bool handle_backlight; bool has_stepping_quirk; }; @@ -616,6 +618,9 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung) struct backlight_device *bd; struct backlight_properties props; + if (!samsung->handle_backlight) + return 0; + memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_PLATFORM; props.max_brightness = samsung->config->max_brightness - @@ -698,7 +703,8 @@ static void __init samsung_sabi_selftest(struct samsung_laptop *samsung, printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface); - test_backlight(samsung); + if (samsung->handle_backlight) + test_backlight(samsung); test_wireless(samsung); sabi_get_command(samsung, config->commands.get_brightness, &sretval); @@ -771,7 +777,8 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) } /* Check for stepping quirk */ - check_for_stepping_quirk(samsung); + if (samsung->handle_backlight) + check_for_stepping_quirk(samsung); exit: if (ret) @@ -1059,6 +1066,15 @@ static int __init samsung_init(void) return -ENOMEM; mutex_init(&samsung->sabi_mutex); + samsung->handle_backlight = true; + +#ifdef CONFIG_ACPI + /* Don't handle backlight here if the acpi video already handle it */ + if (acpi_video_backlight_support()) { + pr_info("Backlight controlled by ACPI video driver\n"); + samsung->handle_backlight = false; + } +#endif ret = samsung_platform_init(samsung); if (ret) -- cgit v1.2.3 From a66c16627cfba7cd1da1f8bd04cab4219d09154b Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:01 +0100 Subject: samsung-laptop: use a sysfs group Will be usefull later when we will have more platform sysfs files like battery_life_extender or usb_charge. Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index b391a38bc7c0..9fbf5c0d3646 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -555,6 +555,10 @@ static ssize_t set_performance_level(struct device *dev, static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, get_performance_level, set_performance_level); +static struct attribute *platform_attributes[] = { + &dev_attr_performance_level.attr, + NULL +}; static int find_signature(void __iomem *memcheck, const char *testStr) { @@ -641,16 +645,38 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung) return 0; } +static mode_t samsung_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct platform_device *pdev = to_platform_device(dev); + struct samsung_laptop *samsung = platform_get_drvdata(pdev); + bool ok = true; + + if (attr == &dev_attr_performance_level.attr) + ok = !!samsung->config->performance_levels[0].name; + + return ok ? attr->mode : 0; +} + +static struct attribute_group platform_attribute_group = { + .is_visible = samsung_sysfs_is_visible, + .attrs = platform_attributes +}; + static void samsung_sysfs_exit(struct samsung_laptop *samsung) { - device_remove_file(&samsung->platform_device->dev, - &dev_attr_performance_level); + struct platform_device *device = samsung->platform_device; + + sysfs_remove_group(&device->dev.kobj, &platform_attribute_group); } static int __init samsung_sysfs_init(struct samsung_laptop *samsung) { - return device_create_file(&samsung->platform_device->dev, - &dev_attr_performance_level); + struct platform_device *device = samsung->platform_device; + + return sysfs_create_group(&device->dev.kobj, &platform_attribute_group); + } static void samsung_sabi_exit(struct samsung_laptop *samsung) -- cgit v1.2.3 From 7e9607118b08bd4e1dae65d56e425e8de7f3da1c Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:02 +0100 Subject: samsung-laptop: ehance SABI support * SABI command are on 16 bits, not 8 * SABI can read/write up to 11 byte of data * There is not real difference between "get" and "set" commands, so refactorise the code of both functions Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 225 +++++++++++++++++----------------- 1 file changed, 113 insertions(+), 112 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 9fbf5c0d3646..134444f176e6 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -42,9 +42,17 @@ #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]; +/* Structure get/set data using sabi */ +struct sabi_data { + union { + struct { + u32 d0; + u32 d1; + u16 d2; + u8 d3; + }; + u8 data[11]; + }; }; struct sabi_header_offsets { @@ -61,8 +69,8 @@ struct sabi_commands { * Brightness is 0 - 8, as described above. * Value 0 is for the BIOS to use */ - u8 get_brightness; - u8 set_brightness; + u16 get_brightness; + u16 set_brightness; /* * first byte: @@ -73,37 +81,37 @@ struct sabi_commands { * 0x03 - 3G is on * TODO, verify 3G is correct, that doesn't seem right... */ - u8 get_wireless_button; - u8 set_wireless_button; + u16 get_wireless_button; + u16 set_wireless_button; /* 0 is off, 1 is on */ - u8 get_backlight; - u8 set_backlight; + u16 get_backlight; + u16 set_backlight; /* * 0x80 or 0x00 - no action * 0x81 - recovery key pressed */ - u8 get_recovery_mode; - u8 set_recovery_mode; + u16 get_recovery_mode; + u16 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; + u16 get_performance_level; + u16 set_performance_level; /* * Tell the BIOS that Linux is running on this machine. * 81 is on, 80 is off */ - u8 set_linux; + u16 set_linux; }; struct sabi_performance_level { const char *name; - u8 value; + u16 value; }; struct sabi_config { @@ -246,16 +254,25 @@ static bool debug; module_param(debug, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug enabled or not"); -static int sabi_get_command(struct samsung_laptop *samsung, - u8 command, struct sabi_retval *sretval) +static int sabi_command(struct samsung_laptop *samsung, u16 command, + struct sabi_data *in, + struct sabi_data *out) { const struct sabi_config *config = samsung->config; - int retval = 0; + int ret = 0; u16 port = readw(samsung->sabi + config->header_offsets.port); u8 complete, iface_data; mutex_lock(&samsung->sabi_mutex); + if (debug) { + if (in) + pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}", + command, in->d0, in->d1, in->d2, in->d3); + else + pr_info("SABI 0x%04x", command); + } + /* enable memory to be able to write to it */ outb(readb(samsung->sabi + config->header_offsets.en_mem), port); @@ -263,6 +280,12 @@ static int sabi_get_command(struct samsung_laptop *samsung, writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); writew(command, samsung->sabi_iface + SABI_IFACE_SUB); writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); + if (in) { + writel(in->d0, samsung->sabi_iface + SABI_IFACE_DATA); + writel(in->d1, samsung->sabi_iface + SABI_IFACE_DATA + 4); + writew(in->d2, samsung->sabi_iface + SABI_IFACE_DATA + 8); + writeb(in->d3, samsung->sabi_iface + SABI_IFACE_DATA + 10); + } outb(readb(samsung->sabi + config->header_offsets.iface_func), port); /* write protect memory to make it safe */ @@ -272,127 +295,105 @@ static int sabi_get_command(struct samsung_laptop *samsung, complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); iface_data = readb(samsung->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; + pr_warn("SABI command 0x%04x failed with" + " completion flag 0x%02x and interface data 0x%02x", + command, complete, iface_data); + ret = -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(samsung->sabi_iface + SABI_IFACE_DATA); - sretval->retval[1] = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); - sretval->retval[2] = readb(samsung->sabi_iface + SABI_IFACE_DATA + 2); - sretval->retval[3] = readb(samsung->sabi_iface + SABI_IFACE_DATA + 3); + + if (out) { + out->d0 = readl(samsung->sabi_iface + SABI_IFACE_DATA); + out->d1 = readl(samsung->sabi_iface + SABI_IFACE_DATA + 4); + out->d2 = readw(samsung->sabi_iface + SABI_IFACE_DATA + 2); + out->d3 = readb(samsung->sabi_iface + SABI_IFACE_DATA + 1); + } + + if (debug && out) { + pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}", + out->d0, out->d1, out->d2, out->d3); + } exit: mutex_unlock(&samsung->sabi_mutex); - return retval; - + return ret; } -static int sabi_set_command(struct samsung_laptop *samsung, - u8 command, u8 data) +/* simple wrappers usable with most commands */ +static int sabi_set_commandb(struct samsung_laptop *samsung, + u16 command, u8 data) { - const struct sabi_config *config = samsung->config; - int retval = 0; - u16 port = readw(samsung->sabi + config->header_offsets.port); - u8 complete, iface_data; + struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 }; - mutex_lock(&samsung->sabi_mutex); - - /* enable memory to be able to write to it */ - outb(readb(samsung->sabi + config->header_offsets.en_mem), port); - - /* write out the command */ - writew(config->main_function, samsung->sabi_iface + SABI_IFACE_MAIN); - writew(command, samsung->sabi_iface + SABI_IFACE_SUB); - writeb(0, samsung->sabi_iface + SABI_IFACE_COMPLETE); - writeb(data, samsung->sabi_iface + SABI_IFACE_DATA); - outb(readb(samsung->sabi + config->header_offsets.iface_func), port); - - /* write protect memory to make it safe */ - outb(readb(samsung->sabi + config->header_offsets.re_mem), port); - - /* see if the command actually succeeded */ - complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); - iface_data = readb(samsung->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(&samsung->sabi_mutex); - return retval; + in.data[0] = data; + return sabi_command(samsung, command, &in, NULL); } static void test_backlight(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; - struct sabi_retval sretval; + struct sabi_data sretval; - sabi_get_command(samsung, commands->get_backlight, &sretval); - printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, commands->get_backlight, NULL, &sretval); + printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]); - sabi_set_command(samsung, commands->set_backlight, 0); + sabi_set_commandb(samsung, commands->set_backlight, 0); printk(KERN_DEBUG "backlight should be off\n"); - sabi_get_command(samsung, commands->get_backlight, &sretval); - printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, commands->get_backlight, NULL, &sretval); + printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]); msleep(1000); - sabi_set_command(samsung, commands->set_backlight, 1); + sabi_set_commandb(samsung, commands->set_backlight, 1); printk(KERN_DEBUG "backlight should be on\n"); - sabi_get_command(samsung, commands->get_backlight, &sretval); - printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, commands->get_backlight, NULL, &sretval); + printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]); } static void test_wireless(struct samsung_laptop *samsung) { const struct sabi_commands *commands = &samsung->config->commands; - struct sabi_retval sretval; + struct sabi_data sretval; - sabi_get_command(samsung, commands->get_wireless_button, &sretval); - printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, commands->get_wireless_button, NULL, &sretval); + printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]); - sabi_set_command(samsung, commands->set_wireless_button, 0); + sabi_set_commandb(samsung, commands->set_wireless_button, 0); printk(KERN_DEBUG "wireless led should be off\n"); - sabi_get_command(samsung, commands->get_wireless_button, &sretval); - printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, commands->get_wireless_button, NULL, &sretval); + printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]); msleep(1000); - sabi_set_command(samsung, commands->set_wireless_button, 1); + sabi_set_commandb(samsung, commands->set_wireless_button, 1); printk(KERN_DEBUG "wireless led should be on\n"); - sabi_get_command(samsung, commands->get_wireless_button, &sretval); - printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, commands->get_wireless_button, NULL, &sretval); + printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]); } static int read_brightness(struct samsung_laptop *samsung) { const struct sabi_config *config = samsung->config; const struct sabi_commands *commands = &samsung->config->commands; - struct sabi_retval sretval; + struct sabi_data sretval; int user_brightness = 0; int retval; - retval = sabi_get_command(samsung, commands->get_brightness, - &sretval); - if (!retval) { - user_brightness = sretval.retval[0]; - if (user_brightness > config->min_brightness) - user_brightness -= config->min_brightness; - else - user_brightness = 0; - } + retval = sabi_command(samsung, commands->get_brightness, + NULL, &sretval); + if (retval) + return retval; + + user_brightness = sretval.data[0]; + if (user_brightness > config->min_brightness) + user_brightness -= config->min_brightness; + else + user_brightness = 0; + return user_brightness; } @@ -410,10 +411,10 @@ static void set_brightness(struct samsung_laptop *samsung, u8 user_brightness) if (user_brightness == read_brightness(samsung)) return; - sabi_set_command(samsung, commands->set_brightness, 0); + sabi_set_commandb(samsung, commands->set_brightness, 0); } - sabi_set_command(samsung, commands->set_brightness, user_level); + sabi_set_commandb(samsung, commands->set_brightness, user_level); } static int get_brightness(struct backlight_device *bd) @@ -465,9 +466,9 @@ static int update_status(struct backlight_device *bd) set_brightness(samsung, bd->props.brightness); if (bd->props.power == FB_BLANK_UNBLANK) - sabi_set_command(samsung, commands->set_backlight, 1); + sabi_set_commandb(samsung, commands->set_backlight, 1); else - sabi_set_command(samsung, commands->set_backlight, 0); + sabi_set_commandb(samsung, commands->set_backlight, 0); return 0; } @@ -488,9 +489,9 @@ static int rfkill_set(void *data, bool blocked) * blocked == true is off */ if (blocked) - sabi_set_command(samsung, commands->set_wireless_button, 0); + sabi_set_commandb(samsung, commands->set_wireless_button, 0); else - sabi_set_command(samsung, commands->set_wireless_button, 1); + sabi_set_commandb(samsung, commands->set_wireless_button, 1); return 0; } @@ -505,19 +506,19 @@ static ssize_t get_performance_level(struct device *dev, struct samsung_laptop *samsung = dev_get_drvdata(dev); const struct sabi_config *config = samsung->config; const struct sabi_commands *commands = &config->commands; - struct sabi_retval sretval; + struct sabi_data sretval; int retval; int i; /* Read the state */ - retval = sabi_get_command(samsung, commands->get_performance_level, - &sretval); + retval = sabi_command(samsung, commands->get_performance_level, + NULL, &sretval); if (retval) return retval; /* The logic is backwards, yeah, lots of fun... */ for (i = 0; config->performance_levels[i].name; ++i) { - if (sretval.retval[0] == config->performance_levels[i].value) + if (sretval.data[0] == config->performance_levels[i].value) return sprintf(buf, "%s\n", config->performance_levels[i].name); } return sprintf(buf, "%s\n", "unknown"); @@ -539,9 +540,9 @@ static ssize_t set_performance_level(struct device *dev, const struct sabi_performance_level *level = &config->performance_levels[i]; if (!strncasecmp(level->name, buf, strlen(level->name))) { - sabi_set_command(samsung, - commands->set_performance_level, - level->value); + sabi_set_commandb(samsung, + commands->set_performance_level, + level->value); break; } } @@ -685,7 +686,7 @@ static void samsung_sabi_exit(struct samsung_laptop *samsung) /* Turn off "Linux" mode in the BIOS */ if (config && config->commands.set_linux != 0xff) - sabi_set_command(samsung, config->commands.set_linux, 0x80); + sabi_set_commandb(samsung, config->commands.set_linux, 0x80); if (samsung->sabi_iface) { iounmap(samsung->sabi_iface); @@ -724,7 +725,7 @@ static void __init samsung_sabi_selftest(struct samsung_laptop *samsung, unsigned int ifaceP) { const struct sabi_config *config = samsung->config; - struct sabi_retval sretval; + struct sabi_data sretval; printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface); @@ -733,8 +734,8 @@ static void __init samsung_sabi_selftest(struct samsung_laptop *samsung, test_backlight(samsung); test_wireless(samsung); - sabi_get_command(samsung, config->commands.get_brightness, &sretval); - printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.retval[0]); + sabi_command(samsung, config->commands.get_brightness, NULL, &sretval); + printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.data[0]); } static int __init samsung_sabi_init(struct samsung_laptop *samsung) @@ -793,8 +794,8 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) /* Turn on "Linux" mode in the BIOS */ if (commands->set_linux != 0xff) { - int retval = sabi_set_command(samsung, - commands->set_linux, 0x81); + int retval = sabi_set_commandb(samsung, + commands->set_linux, 0x81); if (retval) { pr_warn("Linux mode was not set!\n"); ret = -ENODEV; -- cgit v1.2.3 From 5b80fc40e5c53da2c69382c20cfc2ece19ece9ce Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:03 +0100 Subject: samsung-laptop: add a small debugfs interface This allow to call arbitrary sabi commands wihout modifying the driver at all. For example, setting the keyboard backlight brightness to 5 using debugfs interface can be done like that: ; Set the command echo 0x78 > command ; Set the data echo 0x0582 > d0 ; Fill the rest with 0 echo 0 > d1 echo 0 > d2 echo 0 > d3 ; And issue the command cat call Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 147 ++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 134444f176e6..d15b6874b1b2 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include /* * This driver is needed because a number of Samsung laptops do not hook @@ -226,6 +228,35 @@ static const struct sabi_config sabi_configs[] = { { }, }; +/* + * samsung-laptop/ - debugfs root directory + * f0000_segment - dump f0000 segment + * command - current command + * data - current data + * d0, d1, d2, d3 - data fields + * call - call SABI using command and data + * + * This allow to call arbitrary sabi commands wihout + * modifying the driver at all. + * For example, setting the keyboard backlight brightness to 5 + * + * echo 0x78 > command + * echo 0x0582 > d0 + * echo 0 > d1 + * echo 0 > d2 + * echo 0 > d3 + * cat call + */ + +struct samsung_laptop_debug { + struct dentry *root; + struct sabi_data data; + u16 command; + + struct debugfs_blob_wrapper f0000_wrapper; + struct debugfs_blob_wrapper data_wrapper; +}; + struct samsung_laptop { const struct sabi_config *config; @@ -239,6 +270,8 @@ struct samsung_laptop { struct backlight_device *backlight_device; struct rfkill *rfk; + struct samsung_laptop_debug debug; + bool handle_backlight; bool has_stepping_quirk; }; @@ -680,6 +713,113 @@ static int __init samsung_sysfs_init(struct samsung_laptop *samsung) } +static int show_call(struct seq_file *m, void *data) +{ + struct samsung_laptop *samsung = m->private; + struct sabi_data *sdata = &samsung->debug.data; + int ret; + + seq_printf(m, "SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", + samsung->debug.command, + sdata->d0, sdata->d1, sdata->d2, sdata->d3); + + ret = sabi_command(samsung, samsung->debug.command, sdata, sdata); + + if (ret) { + seq_printf(m, "SABI command 0x%04x failed\n", + samsung->debug.command); + return ret; + } + + seq_printf(m, "SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}\n", + sdata->d0, sdata->d1, sdata->d2, sdata->d3); + return 0; +} + +static int samsung_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, show_call, inode->i_private); +} + +static const struct file_operations samsung_laptop_call_io_ops = { + .owner = THIS_MODULE, + .open = samsung_debugfs_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void samsung_debugfs_exit(struct samsung_laptop *samsung) +{ + debugfs_remove_recursive(samsung->debug.root); +} + +static int samsung_debugfs_init(struct samsung_laptop *samsung) +{ + struct dentry *dent; + + samsung->debug.root = debugfs_create_dir("samsung-laptop", NULL); + if (!samsung->debug.root) { + pr_err("failed to create debugfs directory"); + goto error_debugfs; + } + + samsung->debug.f0000_wrapper.data = samsung->f0000_segment; + samsung->debug.f0000_wrapper.size = 0xffff; + + samsung->debug.data_wrapper.data = &samsung->debug.data; + samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); + + dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, + samsung->debug.root, &samsung->debug.command); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_u32("d0", S_IRUGO | S_IWUSR, samsung->debug.root, + &samsung->debug.data.d0); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_u32("d1", S_IRUGO | S_IWUSR, samsung->debug.root, + &samsung->debug.data.d1); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_u16("d2", S_IRUGO | S_IWUSR, samsung->debug.root, + &samsung->debug.data.d2); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_u8("d3", S_IRUGO | S_IWUSR, samsung->debug.root, + &samsung->debug.data.d3); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_blob("data", S_IRUGO | S_IWUSR, + samsung->debug.root, + &samsung->debug.data_wrapper); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_blob("f0000_segment", S_IRUSR | S_IWUSR, + samsung->debug.root, + &samsung->debug.f0000_wrapper); + if (!dent) + goto error_debugfs; + + dent = debugfs_create_file("call", S_IFREG | S_IRUGO, + samsung->debug.root, samsung, + &samsung_laptop_call_io_ops); + if (!dent) + goto error_debugfs; + + return 0; + +error_debugfs: + samsung_debugfs_exit(samsung); + return -ENOMEM; +} + static void samsung_sabi_exit(struct samsung_laptop *samsung) { const struct sabi_config *config = samsung->config; @@ -1123,9 +1263,15 @@ static int __init samsung_init(void) if (ret) goto error_rfkill; + ret = samsung_debugfs_init(samsung); + if (ret) + goto error_debugfs; + samsung_platform_device = samsung->platform_device; return ret; +error_debugfs: + samsung_rfkill_exit(samsung); error_rfkill: samsung_backlight_exit(samsung); error_backlight: @@ -1145,6 +1291,7 @@ static void __exit samsung_exit(void) samsung = platform_get_drvdata(samsung_platform_device); + samsung_debugfs_exit(samsung); samsung_rfkill_exit(samsung); samsung_backlight_exit(samsung); samsung_sysfs_exit(samsung); -- cgit v1.2.3 From 49dd77308b7a5a6d607d9d84ec3531a604afd170 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:04 +0100 Subject: samsung-laptop: remove selftest We can now do the self test using debugfs, so remove the code and keep the debug flag to enable more traces. Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 77 ++++------------------------------- 1 file changed, 8 insertions(+), 69 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index d15b6874b1b2..b39fa53baa22 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -362,52 +362,6 @@ static int sabi_set_commandb(struct samsung_laptop *samsung, return sabi_command(samsung, command, &in, NULL); } -static void test_backlight(struct samsung_laptop *samsung) -{ - const struct sabi_commands *commands = &samsung->config->commands; - struct sabi_data sretval; - - sabi_command(samsung, commands->get_backlight, NULL, &sretval); - printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]); - - sabi_set_commandb(samsung, commands->set_backlight, 0); - printk(KERN_DEBUG "backlight should be off\n"); - - sabi_command(samsung, commands->get_backlight, NULL, &sretval); - printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]); - - msleep(1000); - - sabi_set_commandb(samsung, commands->set_backlight, 1); - printk(KERN_DEBUG "backlight should be on\n"); - - sabi_command(samsung, commands->get_backlight, NULL, &sretval); - printk(KERN_DEBUG "backlight = 0x%02x\n", sretval.data[0]); -} - -static void test_wireless(struct samsung_laptop *samsung) -{ - const struct sabi_commands *commands = &samsung->config->commands; - struct sabi_data sretval; - - sabi_command(samsung, commands->get_wireless_button, NULL, &sretval); - printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]); - - sabi_set_commandb(samsung, commands->set_wireless_button, 0); - printk(KERN_DEBUG "wireless led should be off\n"); - - sabi_command(samsung, commands->get_wireless_button, NULL, &sretval); - printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]); - - msleep(1000); - - sabi_set_commandb(samsung, commands->set_wireless_button, 1); - printk(KERN_DEBUG "wireless led should be on\n"); - - sabi_command(samsung, commands->get_wireless_button, NULL, &sretval); - printk(KERN_DEBUG "wireless led = 0x%02x\n", sretval.data[0]); -} - static int read_brightness(struct samsung_laptop *samsung) { const struct sabi_config *config = samsung->config; @@ -840,12 +794,14 @@ static void samsung_sabi_exit(struct samsung_laptop *samsung) samsung->config = NULL; } -static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca) +static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, + unsigned int ifaceP) { const struct sabi_config *config = samsung->config; 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(samsung->sabi + config->header_offsets.port)); @@ -859,23 +815,8 @@ static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca) readw(samsung->sabi + config->header_offsets.data_offset)); printk(KERN_DEBUG " SABI data segment = 0x%04x\n", readw(samsung->sabi + config->header_offsets.data_segment)); -} - -static void __init samsung_sabi_selftest(struct samsung_laptop *samsung, - unsigned int ifaceP) -{ - const struct sabi_config *config = samsung->config; - struct sabi_data sretval; - - printk(KERN_DEBUG "ifaceP = 0x%08x\n", ifaceP); - printk(KERN_DEBUG "sabi_iface = %p\n", samsung->sabi_iface); - if (samsung->handle_backlight) - test_backlight(samsung); - test_wireless(samsung); - - sabi_command(samsung, config->commands.get_brightness, NULL, &sretval); - printk(KERN_DEBUG "brightness = 0x%02x\n", sretval.data[0]); + printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); } static int __init samsung_sabi_init(struct samsung_laptop *samsung) @@ -916,12 +857,13 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) loca += 1; samsung->sabi = (samsung->f0000_segment + loca); - if (debug) - samsung_sabi_infos(samsung, loca); - /* Get a pointer to the SABI Interface */ ifaceP = (readw(samsung->sabi + config->header_offsets.data_segment) & 0x0ffff) << 4; ifaceP += readw(samsung->sabi + config->header_offsets.data_offset) & 0x0ffff; + + if (debug) + samsung_sabi_infos(samsung, loca, ifaceP); + samsung->sabi_iface = ioremap_nocache(ifaceP, 16); if (!samsung->sabi_iface) { pr_err("Can't remap %x\n", ifaceP); @@ -929,9 +871,6 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) goto exit; } - if (debug) - samsung_sabi_selftest(samsung, ifaceP); - /* Turn on "Linux" mode in the BIOS */ if (commands->set_linux != 0xff) { int retval = sabi_set_commandb(samsung, -- cgit v1.2.3 From cb5b5c912ee8d97ea60c2d6c43d17ab6585947b8 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:05 +0100 Subject: samsung-laptop: add battery life extender support Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-driver-samsung-laptop | 10 +++ drivers/platform/x86/samsung-laptop.c | 82 ++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-samsung-laptop b/Documentation/ABI/testing/sysfs-driver-samsung-laptop index 0a810231aad4..a6a56e11ebaf 100644 --- a/Documentation/ABI/testing/sysfs-driver-samsung-laptop +++ b/Documentation/ABI/testing/sysfs-driver-samsung-laptop @@ -17,3 +17,13 @@ Description: Some Samsung laptops have different "performance levels" 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. + +What: /sys/devices/platform/samsung/battery_life_extender +Date: December 1, 2011 +KernelVersion: 3.3 +Contact: Corentin Chary +Description: Max battery charge level can be modified, battery cycle + life can be extended by reducing the max battery charge + level. + 0 means normal battery mode (100% charge) + 1 means battery life extender mode (80% charge) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index b39fa53baa22..918fa358c11e 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -104,6 +104,10 @@ struct sabi_commands { u16 get_performance_level; u16 set_performance_level; + /* 0x80 is off, 0x81 is on */ + u16 get_battery_life_extender; + u16 set_battery_life_extender; + /* * Tell the BIOS that Linux is running on this machine. * 81 is on, 80 is off @@ -157,6 +161,9 @@ static const struct sabi_config sabi_configs[] = { .get_performance_level = 0x08, .set_performance_level = 0x09, + .get_battery_life_extender = 0xFFFF, + .set_battery_life_extender = 0xFFFF, + .set_linux = 0x0a, }, @@ -204,6 +211,9 @@ static const struct sabi_config sabi_configs[] = { .get_performance_level = 0x31, .set_performance_level = 0x32, + .get_battery_life_extender = 0x65, + .set_battery_life_extender = 0x66, + .set_linux = 0xff, }, @@ -543,8 +553,78 @@ static ssize_t set_performance_level(struct device *dev, static DEVICE_ATTR(performance_level, S_IWUSR | S_IRUGO, get_performance_level, set_performance_level); +static int read_battery_life_extender(struct samsung_laptop *samsung) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + int retval; + + if (commands->get_battery_life_extender == 0xFFFF) + return -ENODEV; + + memset(&data, 0, sizeof(data)); + data.data[0] = 0x80; + retval = sabi_command(samsung, commands->get_battery_life_extender, + &data, &data); + + if (retval) + return retval; + + if (data.data[0] != 0 && data.data[0] != 1) + return -ENODEV; + + return data.data[0]; +} + +static int write_battery_life_extender(struct samsung_laptop *samsung, + int enabled) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + + memset(&data, 0, sizeof(data)); + data.data[0] = 0x80 | enabled; + return sabi_command(samsung, commands->set_battery_life_extender, + &data, NULL); +} + +static ssize_t get_battery_life_extender(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_laptop *samsung = dev_get_drvdata(dev); + int ret; + + ret = read_battery_life_extender(samsung); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t set_battery_life_extender(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct samsung_laptop *samsung = dev_get_drvdata(dev); + int ret, value; + + if (!count || sscanf(buf, "%i", &value) != 1) + return -EINVAL; + + ret = write_battery_life_extender(samsung, !!value); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, + get_battery_life_extender, set_battery_life_extender); + static struct attribute *platform_attributes[] = { &dev_attr_performance_level.attr, + &dev_attr_battery_life_extender.attr, NULL }; @@ -643,6 +723,8 @@ static mode_t samsung_sysfs_is_visible(struct kobject *kobj, if (attr == &dev_attr_performance_level.attr) ok = !!samsung->config->performance_levels[0].name; + if (attr == &dev_attr_battery_life_extender.attr) + ok = !!(read_battery_life_extender(samsung) >= 0); return ok ? attr->mode : 0; } -- cgit v1.2.3 From 3a75d378703495a90d6b2b49a3f5332c953879e2 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:06 +0100 Subject: samsung-laptop: add usb charge support Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- .../ABI/testing/sysfs-driver-samsung-laptop | 8 +++ drivers/platform/x86/samsung-laptop.c | 82 ++++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/Documentation/ABI/testing/sysfs-driver-samsung-laptop b/Documentation/ABI/testing/sysfs-driver-samsung-laptop index a6a56e11ebaf..f05b897805e5 100644 --- a/Documentation/ABI/testing/sysfs-driver-samsung-laptop +++ b/Documentation/ABI/testing/sysfs-driver-samsung-laptop @@ -27,3 +27,11 @@ Description: Max battery charge level can be modified, battery cycle level. 0 means normal battery mode (100% charge) 1 means battery life extender mode (80% charge) + +What: /sys/devices/platform/samsung/usb_charge +Date: December 1, 2011 +KernelVersion: 3.3 +Contact: Corentin Chary +Description: Use your USB ports to charge devices, even + when your laptop is powered off. + 1 means enabled, 0 means disabled. diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 918fa358c11e..b8d2145981e0 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -108,6 +108,10 @@ struct sabi_commands { u16 get_battery_life_extender; u16 set_battery_life_extender; + /* 0x80 is off, 0x81 is on */ + u16 get_usb_charge; + u16 set_usb_charge; + /* * Tell the BIOS that Linux is running on this machine. * 81 is on, 80 is off @@ -164,6 +168,9 @@ static const struct sabi_config sabi_configs[] = { .get_battery_life_extender = 0xFFFF, .set_battery_life_extender = 0xFFFF, + .get_usb_charge = 0xFFFF, + .set_usb_charge = 0xFFFF, + .set_linux = 0x0a, }, @@ -214,6 +221,9 @@ static const struct sabi_config sabi_configs[] = { .get_battery_life_extender = 0x65, .set_battery_life_extender = 0x66, + .get_usb_charge = 0x67, + .set_usb_charge = 0x68, + .set_linux = 0xff, }, @@ -622,9 +632,79 @@ static ssize_t set_battery_life_extender(struct device *dev, static DEVICE_ATTR(battery_life_extender, S_IWUSR | S_IRUGO, get_battery_life_extender, set_battery_life_extender); +static int read_usb_charge(struct samsung_laptop *samsung) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + int retval; + + if (commands->get_usb_charge == 0xFFFF) + return -ENODEV; + + memset(&data, 0, sizeof(data)); + data.data[0] = 0x80; + retval = sabi_command(samsung, commands->get_usb_charge, + &data, &data); + + if (retval) + return retval; + + if (data.data[0] != 0 && data.data[0] != 1) + return -ENODEV; + + return data.data[0]; +} + +static int write_usb_charge(struct samsung_laptop *samsung, + int enabled) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + + memset(&data, 0, sizeof(data)); + data.data[0] = 0x80 | enabled; + return sabi_command(samsung, commands->set_usb_charge, + &data, NULL); +} + +static ssize_t get_usb_charge(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct samsung_laptop *samsung = dev_get_drvdata(dev); + int ret; + + ret = read_usb_charge(samsung); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", ret); +} + +static ssize_t set_usb_charge(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct samsung_laptop *samsung = dev_get_drvdata(dev); + int ret, value; + + if (!count || sscanf(buf, "%i", &value) != 1) + return -EINVAL; + + ret = write_usb_charge(samsung, !!value); + if (ret < 0) + return ret; + + return count; +} + +static DEVICE_ATTR(usb_charge, S_IWUSR | S_IRUGO, + get_usb_charge, set_usb_charge); + static struct attribute *platform_attributes[] = { &dev_attr_performance_level.attr, &dev_attr_battery_life_extender.attr, + &dev_attr_usb_charge.attr, NULL }; @@ -725,6 +805,8 @@ static mode_t samsung_sysfs_is_visible(struct kobject *kobj, ok = !!samsung->config->performance_levels[0].name; if (attr == &dev_attr_battery_life_extender.attr) ok = !!(read_battery_life_extender(samsung) >= 0); + if (attr == &dev_attr_usb_charge.attr) + ok = !!(read_usb_charge(samsung) >= 0); return ok ? attr->mode : 0; } -- cgit v1.2.3 From 1c02f2d40acbbffb96ce824de0e292b26e678bef Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:07 +0100 Subject: samsung-laptop: cleanup KConfig Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 15dbd8cc445f..1906fc4762de 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -768,13 +768,16 @@ config XO15_EBOOK config SAMSUNG_LAPTOP tristate "Samsung Laptop driver" - depends on RFKILL && BACKLIGHT_CLASS_DEVICE && X86 + depends on X86 + depends on RFKILL || RFKILL = n + depends on BACKLIGHT_CLASS_DEVICE ---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. + function keys, wireless LED, LCD backlight level. + + It may also provide some sysfs files described in + To compile this driver as a module, choose M here: the module will be called samsung-laptop. -- cgit v1.2.3 From f674ebf1be4a19454549602ec8e6e7294bf9d60d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:08 +0100 Subject: samsung-laptop: add keyboard backlight support Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 + drivers/platform/x86/samsung-laptop.c | 136 ++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 1906fc4762de..912ffef0f148 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -771,6 +771,8 @@ config SAMSUNG_LAPTOP depends on X86 depends on RFKILL || RFKILL = n depends on BACKLIGHT_CLASS_DEVICE + select LEDS_CLASS + select NEW_LEDS ---help--- This module implements a driver for a wide range of different Samsung laptops. It offers control over the different diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index b8d2145981e0..fd0ebedc86e2 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +113,9 @@ struct sabi_commands { u16 get_usb_charge; u16 set_usb_charge; + /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ + u16 kbd_backlight; + /* * Tell the BIOS that Linux is running on this machine. * 81 is on, 80 is off @@ -171,6 +175,8 @@ static const struct sabi_config sabi_configs[] = { .get_usb_charge = 0xFFFF, .set_usb_charge = 0xFFFF, + .kbd_backlight = 0xFFFF, + .set_linux = 0x0a, }, @@ -224,6 +230,8 @@ static const struct sabi_config sabi_configs[] = { .get_usb_charge = 0x67, .set_usb_charge = 0x68, + .kbd_backlight = 0x78, + .set_linux = 0xff, }, @@ -290,6 +298,11 @@ struct samsung_laptop { struct backlight_device *backlight_device; struct rfkill *rfk; + struct led_classdev kbd_led; + int kbd_led_wk; + struct workqueue_struct *led_workqueue; + struct work_struct kbd_led_work; + struct samsung_laptop_debug debug; bool handle_backlight; @@ -757,6 +770,122 @@ static int __init samsung_rfkill_init(struct samsung_laptop *samsung) return 0; } +static int kbd_backlight_enable(struct samsung_laptop *samsung) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + int retval; + + if (commands->kbd_backlight == 0xFFFF) + return -ENODEV; + + memset(&data, 0, sizeof(data)); + data.d0 = 0xaabb; + retval = sabi_command(samsung, commands->kbd_backlight, + &data, &data); + + if (retval) + return retval; + + if (data.d0 != 0xccdd) + return -ENODEV; + return 0; +} + +static int kbd_backlight_read(struct samsung_laptop *samsung) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + int retval; + + memset(&data, 0, sizeof(data)); + data.data[0] = 0x81; + retval = sabi_command(samsung, commands->kbd_backlight, + &data, &data); + + if (retval) + return retval; + + return data.data[0]; +} + +static int kbd_backlight_write(struct samsung_laptop *samsung, int brightness) +{ + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + + memset(&data, 0, sizeof(data)); + data.d0 = 0x82 | ((brightness & 0xFF) << 8); + return sabi_command(samsung, commands->kbd_backlight, + &data, NULL); +} + +static void kbd_led_update(struct work_struct *work) +{ + struct samsung_laptop *samsung; + + samsung = container_of(work, struct samsung_laptop, kbd_led_work); + kbd_backlight_write(samsung, samsung->kbd_led_wk); +} + +static void kbd_led_set(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct samsung_laptop *samsung; + + samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); + + if (value > samsung->kbd_led.max_brightness) + value = samsung->kbd_led.max_brightness; + else if (value < 0) + value = 0; + + samsung->kbd_led_wk = value; + queue_work(samsung->led_workqueue, &samsung->kbd_led_work); +} + +static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) +{ + struct samsung_laptop *samsung; + + samsung = container_of(led_cdev, struct samsung_laptop, kbd_led); + return kbd_backlight_read(samsung); +} + +static void samsung_leds_exit(struct samsung_laptop *samsung) +{ + if (!IS_ERR_OR_NULL(samsung->kbd_led.dev)) + led_classdev_unregister(&samsung->kbd_led); + if (samsung->led_workqueue) + destroy_workqueue(samsung->led_workqueue); +} + +static int __init samsung_leds_init(struct samsung_laptop *samsung) +{ + int ret = 0; + + samsung->led_workqueue = create_singlethread_workqueue("led_workqueue"); + if (!samsung->led_workqueue) + return -ENOMEM; + + if (kbd_backlight_enable(samsung) >= 0) { + INIT_WORK(&samsung->kbd_led_work, kbd_led_update); + + samsung->kbd_led.name = "samsung::kbd_backlight"; + samsung->kbd_led.brightness_set = kbd_led_set; + samsung->kbd_led.brightness_get = kbd_led_get; + samsung->kbd_led.max_brightness = 8; + + ret = led_classdev_register(&samsung->platform_device->dev, + &samsung->kbd_led); + } + + if (ret) + samsung_leds_exit(samsung); + + return ret; +} + static void samsung_backlight_exit(struct samsung_laptop *samsung) { if (samsung->backlight_device) { @@ -1366,6 +1495,10 @@ static int __init samsung_init(void) if (ret) goto error_rfkill; + ret = samsung_leds_init(samsung); + if (ret) + goto error_leds; + ret = samsung_debugfs_init(samsung); if (ret) goto error_debugfs; @@ -1374,6 +1507,8 @@ static int __init samsung_init(void) return ret; error_debugfs: + samsung_leds_exit(samsung); +error_leds: samsung_rfkill_exit(samsung); error_rfkill: samsung_backlight_exit(samsung); @@ -1395,6 +1530,7 @@ static void __exit samsung_exit(void) samsung = platform_get_drvdata(samsung_platform_device); samsung_debugfs_exit(samsung); + samsung_leds_exit(samsung); samsung_rfkill_exit(samsung); samsung_backlight_exit(samsung); samsung_sysfs_exit(samsung); -- cgit v1.2.3 From 84d482f23095be2c2e3e2cf6fbb10856dc479fb3 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:09 +0100 Subject: samsung-laptop: add true rfkill support for swsmi The wireless status get and get commands seems to use one byte per device. First byte is for wlan and third is for bluetooh, we will have to find what the other are for. Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 207 +++++++++++++++++++++++++++++----- 1 file changed, 177 insertions(+), 30 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index fd0ebedc86e2..39fdabac195d 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -45,6 +45,9 @@ #define SABI_IFACE_COMPLETE 0x04 #define SABI_IFACE_DATA 0x05 +#define WL_STATUS_WLAN 0x0 +#define WL_STATUS_BT 0x2 + /* Structure get/set data using sabi */ struct sabi_data { union { @@ -113,6 +116,10 @@ struct sabi_commands { u16 get_usb_charge; u16 set_usb_charge; + /* the first byte is for bluetooth and the third one is for wlan */ + u16 get_wireless_status; + u16 set_wireless_status; + /* 0x81 to read, (0x82 | level << 8) to set, 0xaabb to enable */ u16 kbd_backlight; @@ -129,6 +136,7 @@ struct sabi_performance_level { }; struct sabi_config { + int sabi_version; const char *test_string; u16 main_function; const struct sabi_header_offsets header_offsets; @@ -140,6 +148,10 @@ struct sabi_config { static const struct sabi_config sabi_configs[] = { { + /* I don't know if it is really 2, but it it is + * less than 3 anyway */ + .sabi_version = 2, + .test_string = "SECLINUX", .main_function = 0x4c49, @@ -175,6 +187,9 @@ static const struct sabi_config sabi_configs[] = { .get_usb_charge = 0xFFFF, .set_usb_charge = 0xFFFF, + .get_wireless_status = 0xFFFF, + .set_wireless_status = 0xFFFF, + .kbd_backlight = 0xFFFF, .set_linux = 0x0a, @@ -195,6 +210,8 @@ static const struct sabi_config sabi_configs[] = { .max_brightness = 8, }, { + .sabi_version = 3, + .test_string = "SwSmi@", .main_function = 0x5843, @@ -230,6 +247,9 @@ static const struct sabi_config sabi_configs[] = { .get_usb_charge = 0x67, .set_usb_charge = 0x68, + .get_wireless_status = 0x69, + .set_wireless_status = 0x6a, + .kbd_backlight = 0x78, .set_linux = 0xff, @@ -285,6 +305,14 @@ struct samsung_laptop_debug { struct debugfs_blob_wrapper data_wrapper; }; +struct samsung_laptop; + +struct samsung_rfkill { + struct samsung_laptop *samsung; + struct rfkill *rfkill; + enum rfkill_type type; +}; + struct samsung_laptop { const struct sabi_config *config; @@ -296,7 +324,9 @@ struct samsung_laptop { struct platform_device *platform_device; struct backlight_device *backlight_device; - struct rfkill *rfk; + + struct samsung_rfkill wlan; + struct samsung_rfkill bluetooth; struct led_classdev kbd_led; int kbd_led_wk; @@ -498,26 +528,78 @@ static const struct backlight_ops backlight_ops = { .update_status = update_status, }; -static int rfkill_set(void *data, bool blocked) +static int seclinux_rfkill_set(void *data, bool blocked) { struct samsung_laptop *samsung = data; const struct sabi_commands *commands = &samsung->config->commands; - /* Do something with blocked...*/ - /* - * blocked == false is on - * blocked == true is off - */ - if (blocked) - sabi_set_commandb(samsung, commands->set_wireless_button, 0); + return sabi_set_commandb(samsung, commands->set_wireless_button, + !blocked); +} + +static struct rfkill_ops seclinux_rfkill_ops = { + .set_block = seclinux_rfkill_set, +}; + +static int swsmi_wireless_status(struct samsung_laptop *samsung, + struct sabi_data *data) +{ + const struct sabi_commands *commands = &samsung->config->commands; + + return sabi_command(samsung, commands->get_wireless_status, + NULL, data); +} + +static int swsmi_rfkill_set(void *priv, bool blocked) +{ + struct samsung_rfkill *srfkill = priv; + struct samsung_laptop *samsung = srfkill->samsung; + const struct sabi_commands *commands = &samsung->config->commands; + struct sabi_data data; + int ret, i; + + ret = swsmi_wireless_status(samsung, &data); + if (ret) + return ret; + + /* Don't set the state for non-present devices */ + for (i = 0; i < 4; i++) + if (data.data[i] == 0x02) + data.data[1] = 0; + + if (srfkill->type == RFKILL_TYPE_WLAN) + data.data[WL_STATUS_WLAN] = !blocked; + else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) + data.data[WL_STATUS_BT] = !blocked; + + return sabi_command(samsung, commands->set_wireless_status, + &data, &data); +} + +static void swsmi_rfkill_query(struct rfkill *rfkill, void *priv) +{ + struct samsung_rfkill *srfkill = priv; + struct samsung_laptop *samsung = srfkill->samsung; + struct sabi_data data; + int ret; + + ret = swsmi_wireless_status(samsung, &data); + if (ret) + return ; + + if (srfkill->type == RFKILL_TYPE_WLAN) + ret = data.data[WL_STATUS_WLAN]; + else if (srfkill->type == RFKILL_TYPE_BLUETOOTH) + ret = data.data[WL_STATUS_BT]; else - sabi_set_commandb(samsung, commands->set_wireless_button, 1); + return ; - return 0; + rfkill_set_sw_state(rfkill, !ret); } -static struct rfkill_ops rfkill_ops = { - .set_block = rfkill_set, +static struct rfkill_ops swsmi_rfkill_ops = { + .set_block = swsmi_rfkill_set, + .query = swsmi_rfkill_query, }; static ssize_t get_performance_level(struct device *dev, @@ -742,31 +824,96 @@ static int find_signature(void __iomem *memcheck, const char *testStr) static void samsung_rfkill_exit(struct samsung_laptop *samsung) { - if (samsung->rfk) { - rfkill_unregister(samsung->rfk); - rfkill_destroy(samsung->rfk); - samsung->rfk = NULL; + if (samsung->wlan.rfkill) { + rfkill_unregister(samsung->wlan.rfkill); + rfkill_destroy(samsung->wlan.rfkill); + samsung->wlan.rfkill = NULL; + } + if (samsung->bluetooth.rfkill) { + rfkill_unregister(samsung->bluetooth.rfkill); + rfkill_destroy(samsung->bluetooth.rfkill); + samsung->bluetooth.rfkill = NULL; } } -static int __init samsung_rfkill_init(struct samsung_laptop *samsung) +static int samsung_new_rfkill(struct samsung_laptop *samsung, + struct samsung_rfkill *arfkill, + const char *name, enum rfkill_type type, + const struct rfkill_ops *ops, + int blocked) { - int retval; + struct rfkill **rfkill = &arfkill->rfkill; + int ret; - samsung->rfk = rfkill_alloc("samsung-wifi", - &samsung->platform_device->dev, - RFKILL_TYPE_WLAN, - &rfkill_ops, samsung); - if (!samsung->rfk) - return -ENOMEM; + arfkill->type = type; + arfkill->samsung = samsung; - retval = rfkill_register(samsung->rfk); - if (retval) { - rfkill_destroy(samsung->rfk); - samsung->rfk = NULL; - return -ENODEV; + *rfkill = rfkill_alloc(name, &samsung->platform_device->dev, + type, ops, arfkill); + + if (!*rfkill) + return -EINVAL; + + if (blocked != -1) + rfkill_init_sw_state(*rfkill, blocked); + + ret = rfkill_register(*rfkill); + if (ret) { + rfkill_destroy(*rfkill); + *rfkill = NULL; + return ret; } + return 0; +} + +static int __init samsung_rfkill_init_seclinux(struct samsung_laptop *samsung) +{ + return samsung_new_rfkill(samsung, &samsung->wlan, "samsung-wlan", + RFKILL_TYPE_WLAN, &seclinux_rfkill_ops, -1); +} +static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) +{ + struct sabi_data data; + int ret; + + ret = swsmi_wireless_status(samsung, &data); + if (ret) + return ret; + + /* 0x02 seems to mean that the device is no present/available */ + + if (data.data[WL_STATUS_WLAN] != 0x02) + ret = samsung_new_rfkill(samsung, &samsung->wlan, + "samsung-wlan", + RFKILL_TYPE_WLAN, + &swsmi_rfkill_ops, + !data.data[WL_STATUS_WLAN]); + if (ret) + goto exit; + + if (data.data[WL_STATUS_BT] != 0x02) + ret = samsung_new_rfkill(samsung, &samsung->bluetooth, + "samsung-bluetooth", + RFKILL_TYPE_BLUETOOTH, + &swsmi_rfkill_ops, + !data.data[WL_STATUS_BT]); + if (ret) + goto exit; + +exit: + if (ret) + samsung_rfkill_exit(samsung); + + return ret; +} + +static int __init samsung_rfkill_init(struct samsung_laptop *samsung) +{ + if (samsung->config->sabi_version == 2) + return samsung_rfkill_init_seclinux(samsung); + if (samsung->config->sabi_version == 3) + return samsung_rfkill_init_swsmi(samsung); return 0; } -- cgit v1.2.3 From 3be324a94df0c3f032178d04549dbfbf6cccb09a Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:10 +0100 Subject: samsung-laptop: make the dmi check less strict This enable the driver for everything that look like a laptop and is from vendor "SAMSUNG ELECTRONICS CO., LTD.". Note that laptop supported by samsung-q10 seem to have a different vendor strict. Also remove every log output until we know that we have a SABI interface (except if the driver is forced to load, or debug is enabled). Keeping a whitelist of laptop with a model granularity is something that can't work without close vendor cooperation (and we don't have that). Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 235 +++------------------------------- 1 file changed, 15 insertions(+), 220 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 39fdabac195d..431f7dc2f222 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -1270,7 +1270,8 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) samsung->f0000_segment = ioremap_nocache(0xf0000, 0xffff); if (!samsung->f0000_segment) { - pr_err("Can't map the segment at 0xf0000\n"); + if (debug || force) + pr_err("Can't map the segment at 0xf0000\n"); ret = -EINVAL; goto exit; } @@ -1285,7 +1286,8 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) } if (loca == 0xffff) { - pr_err("This computer does not support SABI\n"); + if (debug || force) + pr_err("This computer does not support SABI\n"); ret = -ENODEV; goto exit; } @@ -1354,244 +1356,34 @@ static int __init samsung_platform_init(struct samsung_laptop *samsung) return 0; } -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 = "N510", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, - "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "N510"), - DMI_MATCH(DMI_BOARD_NAME, "N510"), - }, - .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 = "R410 Plus", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, - "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "R410P"), - DMI_MATCH(DMI_BOARD_NAME, "R460"), - }, - .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"), + DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */ }, - .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"), + DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */ }, - .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"), + DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */ }, - .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"), + DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, - .callback = dmi_check_cb, - }, - { - .ident = "N150/N210/N220/N230", - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, - "SAMSUNG ELECTRONICS CO., LTD."), - DMI_MATCH(DMI_PRODUCT_NAME, "N150/N210/N220/N230"), - DMI_MATCH(DMI_BOARD_NAME, "N150/N210/N220/N230"), - }, - .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 = "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", - .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, - }, - { - .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, }, { }, }; @@ -1616,12 +1408,9 @@ static int __init samsung_init(void) #ifdef CONFIG_ACPI /* Don't handle backlight here if the acpi video already handle it */ - if (acpi_video_backlight_support()) { - pr_info("Backlight controlled by ACPI video driver\n"); + if (acpi_video_backlight_support()) samsung->handle_backlight = false; - } #endif - ret = samsung_platform_init(samsung); if (ret) goto error_platform; @@ -1630,6 +1419,12 @@ static int __init samsung_init(void) if (ret) goto error_sabi; +#ifdef CONFIG_ACPI + /* Only log that if we are really on a sabi platform */ + if (acpi_video_backlight_support()) + pr_info("Backlight controlled by ACPI video driver\n"); +#endif + ret = samsung_sysfs_init(samsung); if (ret) goto error_sysfs; -- cgit v1.2.3 From 6f6ae06eb30d4710cd86a1782326702afa18a8f6 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:11 +0100 Subject: samsung-laptop: dump model and version informations We still need to figure out exactly what each of different fields represent, but they contain at least model and version informations. Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 42 +++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 431f7dc2f222..b41c7b4f9c71 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -303,6 +303,7 @@ struct samsung_laptop_debug { struct debugfs_blob_wrapper f0000_wrapper; struct debugfs_blob_wrapper data_wrapper; + struct debugfs_blob_wrapper sdiag_wrapper; }; struct samsung_laptop; @@ -337,6 +338,8 @@ struct samsung_laptop { bool handle_backlight; bool has_stepping_quirk; + + char sdiag[64]; }; @@ -1164,6 +1167,9 @@ static int samsung_debugfs_init(struct samsung_laptop *samsung) samsung->debug.data_wrapper.data = &samsung->debug.data; samsung->debug.data_wrapper.size = sizeof(samsung->debug.data); + samsung->debug.sdiag_wrapper.data = samsung->sdiag; + samsung->debug.sdiag_wrapper.size = strlen(samsung->sdiag); + dent = debugfs_create_u16("command", S_IRUGO | S_IWUSR, samsung->debug.root, &samsung->debug.command); if (!dent) @@ -1207,6 +1213,12 @@ static int samsung_debugfs_init(struct samsung_laptop *samsung) if (!dent) goto error_debugfs; + dent = debugfs_create_blob("sdiag", S_IRUGO | S_IWUSR, + samsung->debug.root, + &samsung->debug.sdiag_wrapper); + if (!dent) + goto error_debugfs; + return 0; error_debugfs: @@ -1259,6 +1271,34 @@ static __init void samsung_sabi_infos(struct samsung_laptop *samsung, int loca, printk(KERN_DEBUG " SABI pointer = 0x%08x\n", ifaceP); } +static void __init samsung_sabi_diag(struct samsung_laptop *samsung) +{ + int loca = find_signature(samsung->f0000_segment, "SDiaG@"); + int i; + + if (loca == 0xffff) + return ; + + /* Example: + * Ident: @SDiaG@686XX-N90X3A/966-SEC-07HL-S90X3A + * + * Product name: 90X3A + * BIOS Version: 07HL + */ + loca += 1; + for (i = 0; loca < 0xffff && i < sizeof(samsung->sdiag) - 1; loca++) { + char temp = readb(samsung->f0000_segment + loca); + + if (isalnum(temp) || temp == '/' || temp == '-') + samsung->sdiag[i++] = temp; + else + break ; + } + + if (debug && samsung->sdiag[0]) + pr_info("sdiag: %s", samsung->sdiag); +} + static int __init samsung_sabi_init(struct samsung_laptop *samsung) { const struct sabi_config *config = NULL; @@ -1276,6 +1316,8 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) goto exit; } + samsung_sabi_diag(samsung); + /* Try to find one of the signatures in memory to find the header */ for (i = 0; sabi_configs[i].test_string != 0; ++i) { samsung->config = &sabi_configs[i]; -- cgit v1.2.3 From 2e777187d53ff4366f0dac37ded20980370e580e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Sat, 26 Nov 2011 11:00:12 +0100 Subject: samsung-laptop: tweak traces - don't output error when probing features at load - print the SABI signature if samsung_sabi_init() succeed Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index b41c7b4f9c71..5047642d1662 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -366,10 +366,11 @@ static int sabi_command(struct samsung_laptop *samsung, u16 command, if (debug) { if (in) - pr_info("SABI 0x%04x {0x%08x, 0x%08x, 0x%04x, 0x%02x}", + pr_info("SABI command:0x%04x " + "data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", command, in->d0, in->d1, in->d2, in->d3); else - pr_info("SABI 0x%04x", command); + pr_info("SABI command:0x%04x", command); } /* enable memory to be able to write to it */ @@ -393,10 +394,17 @@ static int sabi_command(struct samsung_laptop *samsung, u16 command, /* see if the command actually succeeded */ complete = readb(samsung->sabi_iface + SABI_IFACE_COMPLETE); iface_data = readb(samsung->sabi_iface + SABI_IFACE_DATA); - if (complete != 0xaa || iface_data == 0xff) { + + /* iface_data = 0xFF happens when a command is not known + * so we only add a warning in debug mode since we will + * probably issue some unknown command at startup to find + * out which features are supported */ + if (complete != 0xaa || (iface_data == 0xff && debug)) pr_warn("SABI command 0x%04x failed with" " completion flag 0x%02x and interface data 0x%02x", command, complete, iface_data); + + if (complete != 0xaa || iface_data == 0xff) { ret = -EINVAL; goto exit; } @@ -409,7 +417,7 @@ static int sabi_command(struct samsung_laptop *samsung, u16 command, } if (debug && out) { - pr_info("SABI {0x%08x, 0x%08x, 0x%04x, 0x%02x}", + pr_info("SABI return data:{0x%08x, 0x%08x, 0x%04x, 0x%02x}", out->d0, out->d1, out->d2, out->d3); } @@ -1370,6 +1378,9 @@ static int __init samsung_sabi_init(struct samsung_laptop *samsung) if (samsung->handle_backlight) check_for_stepping_quirk(samsung); + pr_info("detected SABI interface: %s\n", + samsung->config->test_string); + exit: if (ret) samsung_sabi_exit(samsung); -- cgit v1.2.3 From 92304a4084911cf1eef1cd624f6bf6f207498847 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Mon, 5 Dec 2011 12:38:35 -0500 Subject: samsung-laptop: promote myself as maintainer of samsung-laptop Signed-off-by: Corentin Chary Acked-by: Greg Kroah-Hartman Signed-off-by: Matthew Garrett --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9c63a43ab63a..0fc08ab7ad06 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5712,6 +5712,12 @@ F: drivers/media/common/saa7146* F: drivers/media/video/*7146* F: include/media/*7146* +SAMSUNG LAPTOP DRIVER +M: Corentin Chary +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/x86/samsung-laptop.c + SAMSUNG AUDIO (ASoC) DRIVERS M: Sangbeom Kim L: alsa-devel@alsa-project.org (moderated for non-subscribers) -- cgit v1.2.3 From 6e71f38bdac9e7e3d598fdfe31020cdaa18b20d5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 29 Nov 2011 11:04:05 -0800 Subject: acer-wmi: Message logging neatening Use pr_warn not pr_warning. Coalesce formats. Signed-off-by: Joe Perches Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 1e5290b5396d..6cd2289dd5d3 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1006,7 +1006,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) return AE_ERROR; } if (obj->buffer.length != 8) { - pr_warning("Unknown buffer length %d\n", obj->buffer.length); + pr_warn("Unknown buffer length %d\n", obj->buffer.length); kfree(obj); return AE_ERROR; } @@ -1015,8 +1015,8 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) 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, + pr_warn("Get Current Device Status failed: 0x%x - 0x%x\n", + return_value.error_code, return_value.ec_return_value); return status; } @@ -1039,7 +1039,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) return AE_ERROR; } if (obj->buffer.length != 4) { - pr_warning("Unknown buffer length %d\n", obj->buffer.length); + pr_warn("Unknown buffer length %d\n", obj->buffer.length); kfree(obj); return AE_ERROR; } @@ -1048,8 +1048,8 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) 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, + pr_warn("Set Device Status failed: 0x%x - 0x%x\n", + return_value.error_code, return_value.ec_return_value); return status; @@ -1488,8 +1488,8 @@ static ssize_t show_bool_threeg(struct device *dev, u32 result; \ acpi_status status; - pr_info("This threeg sysfs will be removed in 2012" - " - used by: %s\n", current->comm); + pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n", + current->comm); status = get_u32(&result, ACER_CAP_THREEG); if (ACPI_SUCCESS(status)) return sprintf(buf, "%u\n", result); @@ -1501,8 +1501,8 @@ static ssize_t set_bool_threeg(struct device *dev, { u32 tmp = simple_strtoul(buf, NULL, 10); acpi_status status = set_u32(tmp, ACER_CAP_THREEG); - pr_info("This threeg sysfs will be removed in 2012" - " - used by: %s\n", current->comm); + pr_info("This threeg sysfs will be removed in 2012 - used by: %s\n", + current->comm); if (ACPI_FAILURE(status)) return -EINVAL; return count; @@ -1513,8 +1513,8 @@ static DEVICE_ATTR(threeg, S_IRUGO | S_IWUSR, show_bool_threeg, static ssize_t show_interface(struct device *dev, struct device_attribute *attr, char *buf) { - pr_info("This interface sysfs will be removed in 2012" - " - used by: %s\n", current->comm); + pr_info("This interface sysfs will be removed in 2012 - used by: %s\n", + current->comm); switch (interface->type) { case ACER_AMW0: return sprintf(buf, "AMW0\n"); @@ -1982,8 +1982,7 @@ static int __init acer_wmi_init(void) if (acpi_video_backlight_support()) { interface->capability &= ~ACER_CAP_BRIGHTNESS; - pr_info("Brightness must be controlled by " - "generic video driver\n"); + pr_info("Brightness must be controlled by generic video driver\n"); } if (wmi_has_guid(WMID_GUID3)) { @@ -2008,7 +2007,7 @@ static int __init acer_wmi_init(void) err = platform_driver_register(&acer_platform_driver); if (err) { - pr_err("Unable to register platform driver.\n"); + pr_err("Unable to register platform driver\n"); goto error_platform_register; } -- cgit v1.2.3 From eecc5bbc612a8b008c0ef442ac8445306fbe5277 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 29 Nov 2011 11:04:06 -0800 Subject: acerhdf: Message logging neatening Use pr_warn not pr_warning. Coalesce formats. Argument aligning. Remove superfluous parentheses. Signed-off-by: Joe Perches Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acerhdf.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index 760c6d7624fe..bc8384c6f3eb 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -244,12 +244,11 @@ static void acerhdf_change_fanstate(int state) unsigned char cmd; if (verbose) - pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ? - "OFF" : "ON"); + pr_notice("fan %s\n", state == ACERHDF_FAN_OFF ? "OFF" : "ON"); if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { pr_err("invalid fan state %d requested, setting to auto!\n", - state); + state); state = ACERHDF_FAN_AUTO; } @@ -264,19 +263,18 @@ static void acerhdf_check_param(struct thermal_zone_device *thermal) { if (fanon > ACERHDF_MAX_FANON) { pr_err("fanon temperature too high, set to %d\n", - ACERHDF_MAX_FANON); + ACERHDF_MAX_FANON); fanon = ACERHDF_MAX_FANON; } if (kernelmode && prev_interval != interval) { if (interval > ACERHDF_MAX_INTERVAL) { pr_err("interval too high, set to %d\n", - ACERHDF_MAX_INTERVAL); + ACERHDF_MAX_INTERVAL); interval = ACERHDF_MAX_INTERVAL; } if (verbose) - pr_notice("interval changed to: %d\n", - interval); + pr_notice("interval changed to: %d\n", interval); thermal->polling_delay = interval*1000; prev_interval = interval; } @@ -587,8 +585,8 @@ static int acerhdf_check_hardware(void) } if (!bios_cfg) { - pr_err("unknown (unsupported) BIOS version %s/%s/%s, " - "please report, aborting!\n", vendor, product, version); + pr_err("unknown (unsupported) BIOS version %s/%s/%s, please report, aborting!\n", + vendor, product, version); return -EINVAL; } @@ -598,8 +596,7 @@ static int acerhdf_check_hardware(void) */ if (!kernelmode) { pr_notice("Fan control off, to enable do:\n"); - pr_notice("echo -n \"enabled\" > " - "/sys/class/thermal/thermal_zone0/mode\n"); + pr_notice("echo -n \"enabled\" > /sys/class/thermal/thermal_zone0/mode\n"); } return 0; -- cgit v1.2.3 From e0ac913374247f000aa97fdd732dcaf0070dd466 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:31 +0100 Subject: asus-laptop: log unknown keys Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index b7944f903886..3976cfac5ffe 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1364,8 +1364,10 @@ err_btrfk: */ static void asus_input_notify(struct asus_laptop *asus, int event) { - if (asus->inputdev) - sparse_keymap_report_event(asus->inputdev, event, 1, true); + if (!asus->inputdev) + return ; + if (!sparse_keymap_report_event(asus->inputdev, event, 1, true)) + pr_info("Unknown key %x pressed\n", event); } static int asus_input_init(struct asus_laptop *asus) -- cgit v1.2.3 From ce6c468fd8f7f027953f9df97434423b8197009c Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:32 +0100 Subject: eeepc-laptop: log unknown keys Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-laptop.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index ea44abd8df48..6deb0d7f2263 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1251,6 +1251,14 @@ static void eeepc_input_exit(struct eeepc_laptop *eeepc) /* * ACPI driver */ +static void eeepc_input_notify(struct eeepc_laptop *eeepc, int event) +{ + if (!eeepc->inputdev) + return ; + if (!sparse_keymap_report_event(eeepc->inputdev, event, 1, true)) + pr_info("Unknown key %x pressed\n", event); +} + static void eeepc_acpi_notify(struct acpi_device *device, u32 event) { struct eeepc_laptop *eeepc = acpi_driver_data(device); @@ -1287,12 +1295,11 @@ static void eeepc_acpi_notify(struct acpi_device *device, u32 event) * event will be desired value (or else ignored) */ } - sparse_keymap_report_event(eeepc->inputdev, event, - 1, true); + eeepc_input_notify(eeepc, event); } } else { /* Everything else is a bona-fide keypress event */ - sparse_keymap_report_event(eeepc->inputdev, event, 1, true); + eeepc_input_notify(eeepc, event); } } -- cgit v1.2.3 From 40969c7dd6298718820e0818b5b5acef7b24923d Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:33 +0100 Subject: asus-laptop: cleanup rfkill code Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 117 +++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 65 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 3976cfac5ffe..db32d0337a7c 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -218,7 +218,7 @@ struct asus_led { /* * Same thing for rfkill */ -struct asus_pega_rfkill { +struct asus_rfkill { int control_id; /* type of control. Maps to PEGA_* values */ struct rfkill *rfkill; struct asus_laptop *asus; @@ -256,11 +256,10 @@ struct asus_laptop { int pega_acc_y; int pega_acc_z; - struct rfkill *gps_rfkill; - - struct asus_pega_rfkill wlanrfk; - struct asus_pega_rfkill btrfk; - struct asus_pega_rfkill wwanrfk; + struct asus_rfkill wlan; + struct asus_rfkill bluetooth; + struct asus_rfkill wwan; + struct asus_rfkill gps; acpi_handle handle; /* the handle of the hotk device */ u32 ledd_status; /* status of the LED display */ @@ -1228,7 +1227,7 @@ static ssize_t store_gps(struct device *dev, struct device_attribute *attr, ret = asus_gps_switch(asus, !!value); if (ret) return ret; - rfkill_set_sw_state(asus->gps_rfkill, !value); + rfkill_set_sw_state(asus->gps.rfkill, !value); return rv; } @@ -1246,13 +1245,22 @@ static const struct rfkill_ops asus_gps_rfkill_ops = { .set_block = asus_gps_rfkill_set, }; +static void asus_rfkill_terminate(struct asus_rfkill *rfk) +{ + if (!rfk->rfkill) + return ; + + rfkill_unregister(rfk->rfkill); + rfkill_destroy(rfk->rfkill); + rfk->rfkill = NULL; +} + static void asus_rfkill_exit(struct asus_laptop *asus) { - if (asus->gps_rfkill) { - rfkill_unregister(asus->gps_rfkill); - rfkill_destroy(asus->gps_rfkill); - asus->gps_rfkill = NULL; - } + asus_rfkill_terminate(&asus->wwan); + asus_rfkill_terminate(&asus->bluetooth); + asus_rfkill_terminate(&asus->wlan); + asus_rfkill_terminate(&asus->gps); } static int asus_rfkill_init(struct asus_laptop *asus) @@ -1264,16 +1272,16 @@ static int asus_rfkill_init(struct asus_laptop *asus) acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) return 0; - asus->gps_rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, + asus->gps.rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, RFKILL_TYPE_GPS, &asus_gps_rfkill_ops, asus); - if (!asus->gps_rfkill) + if (!asus->gps.rfkill) return -EINVAL; - result = rfkill_register(asus->gps_rfkill); + result = rfkill_register(asus->gps.rfkill); if (result) { - rfkill_destroy(asus->gps_rfkill); - asus->gps_rfkill = NULL; + rfkill_destroy(asus->gps.rfkill); + asus->gps.rfkill = NULL; } return result; @@ -1281,11 +1289,9 @@ static int asus_rfkill_init(struct asus_laptop *asus) 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); + struct asus_rfkill *rfk = data; + int ret = asus_pega_lucid_set(rfk->asus, rfk->control_id, !blocked); return ret; } @@ -1293,40 +1299,22 @@ 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) +static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *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) + rfk->control_id = controlid; + rfk->asus = asus; + rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, + rfkill_type, &pega_rfkill_ops, rfk); + if (!rfk->rfkill) return -EINVAL; - result = rfkill_register(pega_rfk->rfkill); + result = rfkill_register(rfk->rfkill); if (result) { - rfkill_destroy(pega_rfk->rfkill); - pega_rfk->rfkill = NULL; + rfkill_destroy(rfk->rfkill); + rfk->rfkill = NULL; } return result; @@ -1339,22 +1327,22 @@ static int pega_rfkill_init(struct asus_laptop *asus) if(!asus->is_pega_lucid) return -ENODEV; - ret = pega_rfkill_setup(asus, &asus->wlanrfk, "pega-wlan", PEGA_WLAN, RFKILL_TYPE_WLAN); + ret = pega_rfkill_setup(asus, &asus->wlan, "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); + goto exit; + + ret = pega_rfkill_setup(asus, &asus->bluetooth, "pega-bt", + PEGA_BLUETOOTH, RFKILL_TYPE_BLUETOOTH); if(ret) - goto err_wwanrfk; + goto exit; - pr_warn("Pega rfkill init succeeded\n"); - return 0; -err_wwanrfk: - pega_rfkill_terminate(&asus->btrfk); -err_btrfk: - pega_rfkill_terminate(&asus->wlanrfk); + ret = pega_rfkill_setup(asus, &asus->wwan, "pega-wwan", + PEGA_WWAN, RFKILL_TYPE_WWAN); + +exit: + if (ret) + asus_rfkill_exit(asus); return ret; } @@ -1377,7 +1365,7 @@ static int asus_input_init(struct asus_laptop *asus) input = input_allocate_device(); if (!input) { - pr_info("Unable to allocate input device\n"); + pr_warn("Unable to allocate input device\n"); return -ENOMEM; } input->name = "Asus Laptop extra buttons"; @@ -1392,7 +1380,7 @@ static int asus_input_init(struct asus_laptop *asus) } error = input_register_device(input); if (error) { - pr_info("Unable to register input device\n"); + pr_warn("Unable to register input device\n"); goto err_free_keymap; } @@ -1830,7 +1818,6 @@ static int asus_acpi_remove(struct acpi_device *device, int type) asus_led_exit(asus); asus_input_exit(asus); pega_accel_exit(asus); - pega_rfkill_exit(asus); asus_platform_exit(asus); kfree(asus->name); -- cgit v1.2.3 From 774b06780be20d07c5459becd6495c04523a93a2 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:34 +0100 Subject: asus-laptop: control how BLED and WLED should be exposed Let the user tells if BLED and WLED should be exposed as led or rfkill (the old sysfs are still here, but this adds a standard interface to control the device). For example on my A6JC, with WAPF=1, I would do: $ modprobe asus-laptop wled_type=led bluetooth_type=rfkill There is still no known way to automatically guess what BLED and WLED methods will control, it's why user information is needed. A userspace database could do that automatically, and maybe some DMI matching in the driver. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 149 +++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index db32d0337a7c..e416867c0725 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -81,6 +81,19 @@ static uint wapf = 1; module_param(wapf, uint, 0444); MODULE_PARM_DESC(wapf, "WAPF value"); +static char *wled_type = "unknown"; +static char *bled_type = "unknown"; + +module_param(wled_type, charp, 0444); +MODULE_PARM_DESC(wlan_status, "Set the wled type on boot " + "(unknown, led or rfkill). " + "default is unknown"); + +module_param(bled_type, charp, 0444); +MODULE_PARM_DESC(bled_type, "Set the bled type on boot " + "(unknown, led or rfkill). " + "default is unknown"); + static int wlan_status = 1; static int bluetooth_status = 1; static int wimax_status = -1; @@ -137,6 +150,11 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot " #define WM_RSTS 0x08 /* internal wimax */ #define WW_RSTS 0x20 /* internal wwan */ +/* WLED and BLED type */ +#define TYPE_UNKNOWN 0 +#define TYPE_LED 1 +#define TYPE_RFKILL 2 + /* LED */ #define METHOD_MLED "MLED" #define METHOD_TLED "TLED" @@ -219,7 +237,8 @@ struct asus_led { * Same thing for rfkill */ struct asus_rfkill { - int control_id; /* type of control. Maps to PEGA_* values */ + /* type of control. Maps to PEGA_* values or *_RSTS */ + int control_id; struct rfkill *rfkill; struct asus_laptop *asus; }; @@ -240,6 +259,8 @@ struct asus_laptop { struct key_entry *keymap; struct input_polled_dev *pega_accel_poll; + struct asus_led wled; + struct asus_led bled; struct asus_led mled; struct asus_led tled; struct asus_led rled; @@ -248,6 +269,8 @@ struct asus_laptop { struct asus_led kled; struct workqueue_struct *led_workqueue; + int wled_type; + int bled_type; int wireless_status; bool have_rsts; bool is_pega_lucid; @@ -600,6 +623,10 @@ static enum led_brightness asus_kled_cdev_get(struct led_classdev *led_cdev) static void asus_led_exit(struct asus_laptop *asus) { + if (!IS_ERR_OR_NULL(asus->wled.led.dev)) + led_classdev_unregister(&asus->wled.led); + if (!IS_ERR_OR_NULL(asus->bled.led.dev)) + led_classdev_unregister(&asus->bled.led); if (!IS_ERR_OR_NULL(asus->mled.led.dev)) led_classdev_unregister(&asus->mled.led); if (!IS_ERR_OR_NULL(asus->tled.led.dev)) @@ -641,7 +668,7 @@ static int asus_led_register(struct asus_laptop *asus, static int asus_led_init(struct asus_laptop *asus) { - int r; + int r = 0; /* * The Pegatron Lucid has no physical leds, but all methods are @@ -660,6 +687,16 @@ static int asus_led_init(struct asus_laptop *asus) if (!asus->led_workqueue) return -ENOMEM; + if (asus->wled_type == TYPE_LED) + r = asus_led_register(asus, &asus->wled, "asus::wlan", + METHOD_WLAN); + if (r) + goto error; + if (asus->bled_type == TYPE_LED) + r = asus_led_register(asus, &asus->bled, "asus::bluetooth", + METHOD_BLUETOOTH); + if (r) + goto error; r = asus_led_register(asus, &asus->mled, "asus::mail", METHOD_MLED); if (r) goto error; @@ -962,7 +999,7 @@ static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, return sysfs_acpi_set(asus, buf, count, METHOD_WLAN); } -/* +/*e * Bluetooth */ static int asus_bluetooth_set(struct asus_laptop *asus, int status) @@ -1245,6 +1282,23 @@ static const struct rfkill_ops asus_gps_rfkill_ops = { .set_block = asus_gps_rfkill_set, }; +static int asus_rfkill_set(void *data, bool blocked) +{ + struct asus_rfkill *rfk = data; + struct asus_laptop *asus = rfk->asus; + + if (rfk->control_id == WL_RSTS) + return asus_wlan_set(asus, !blocked); + else if (rfk->control_id == BT_RSTS) + return asus_bluetooth_set(asus, !blocked); + + return -EINVAL; +} + +static const struct rfkill_ops asus_rfkill_ops = { + .set_block = asus_rfkill_set, +}; + static void asus_rfkill_terminate(struct asus_rfkill *rfk) { if (!rfk->rfkill) @@ -1263,30 +1317,64 @@ static void asus_rfkill_exit(struct asus_laptop *asus) asus_rfkill_terminate(&asus->gps); } -static int asus_rfkill_init(struct asus_laptop *asus) +static int asus_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk, + const char *name, int control_id, int type, + const struct rfkill_ops *ops) { int result; - if (acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) || - acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) || - acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) - return 0; - - asus->gps.rfkill = rfkill_alloc("asus-gps", &asus->platform_device->dev, - RFKILL_TYPE_GPS, - &asus_gps_rfkill_ops, asus); - if (!asus->gps.rfkill) + rfk->control_id = control_id; + rfk->asus = asus; + rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, + type, ops, rfk); + if (!rfk->rfkill) return -EINVAL; - result = rfkill_register(asus->gps.rfkill); + result = rfkill_register(rfk->rfkill); if (result) { - rfkill_destroy(asus->gps.rfkill); - asus->gps.rfkill = NULL; + rfkill_destroy(rfk->rfkill); + rfk->rfkill = NULL; } return result; } +static int asus_rfkill_init(struct asus_laptop *asus) +{ + int result = 0; + + if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && + !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && + !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) + result = asus_rfkill_setup(asus, &asus->gps, "asus-gps", + -1, RFKILL_TYPE_GPS, + &asus_gps_rfkill_ops); + if (result) + goto exit; + + + if (asus->wled_type == TYPE_RFKILL) + result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan", + WL_RSTS, RFKILL_TYPE_WLAN, + &asus_rfkill_ops); + if (result) + goto exit; + + if (asus->bled_type == TYPE_RFKILL) + result = asus_rfkill_setup(asus, &asus->bluetooth, + "asus-bluetooth", BT_RSTS, + RFKILL_TYPE_BLUETOOTH, + &asus_rfkill_ops); + if (result) + goto exit; + +exit: + if (result) + asus_rfkill_exit(asus); + + return result; +} + static int pega_rfkill_set(void *data, bool blocked) { struct asus_rfkill *rfk = data; @@ -1302,22 +1390,8 @@ static const struct rfkill_ops pega_rfkill_ops = { static int pega_rfkill_setup(struct asus_laptop *asus, struct asus_rfkill *rfk, const char *name, int controlid, int rfkill_type) { - int result; - - rfk->control_id = controlid; - rfk->asus = asus; - rfk->rfkill = rfkill_alloc(name, &asus->platform_device->dev, - rfkill_type, &pega_rfkill_ops, rfk); - if (!rfk->rfkill) - return -EINVAL; - - result = rfkill_register(rfk->rfkill); - if (result) { - rfkill_destroy(rfk->rfkill); - rfk->rfkill = NULL; - } - - return result; + return asus_rfkill_setup(asus, rfk, name, controlid, rfkill_type, + &pega_rfkill_ops); } static int pega_rfkill_init(struct asus_laptop *asus) @@ -1678,7 +1752,16 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus) if (result) return result; - /* WLED and BLED are on by default */ + if (!strcmp(bled_type, "led")) + asus->bled_type = TYPE_LED; + else if (!strcmp(bled_type, "rfkill")) + asus->bled_type = TYPE_RFKILL; + + if (!strcmp(wled_type, "led")) + asus->wled_type = TYPE_LED; + else if (!strcmp(wled_type, "rfkill")) + asus->wled_type = TYPE_RFKILL; + if (bluetooth_status >= 0) asus_bluetooth_set(asus, !!bluetooth_status); -- cgit v1.2.3 From 3c8671ffd334cfb692089ec00141b56d5a796ae7 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:35 +0100 Subject: asus-laptop: add rfkill interfaces for wlan and wwan But don't try to do than on pegatron tablets to avoid any conflict. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index e416867c0725..547b9eba03ae 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -282,6 +282,7 @@ struct asus_laptop { struct asus_rfkill wlan; struct asus_rfkill bluetooth; struct asus_rfkill wwan; + struct asus_rfkill wimax; struct asus_rfkill gps; acpi_handle handle; /* the handle of the hotk device */ @@ -1291,6 +1292,10 @@ static int asus_rfkill_set(void *data, bool blocked) return asus_wlan_set(asus, !blocked); else if (rfk->control_id == BT_RSTS) return asus_bluetooth_set(asus, !blocked); + else if (rfk->control_id == WM_RSTS) + return asus_wimax_set(asus, !blocked); + else if (rfk->control_id == WW_RSTS) + return asus_wwan_set(asus, !blocked); return -EINVAL; } @@ -1343,6 +1348,9 @@ static int asus_rfkill_init(struct asus_laptop *asus) { int result = 0; + if (asus->is_pega_lucid) + return -ENODEV; + if (!acpi_check_handle(asus->handle, METHOD_GPS_ON, NULL) && !acpi_check_handle(asus->handle, METHOD_GPS_OFF, NULL) && !acpi_check_handle(asus->handle, METHOD_GPS_STATUS, NULL)) @@ -1368,6 +1376,20 @@ static int asus_rfkill_init(struct asus_laptop *asus) if (result) goto exit; + if (!acpi_check_handle(asus->handle, METHOD_WWAN, NULL)) + result = asus_rfkill_setup(asus, &asus->wwan, "asus-wwan", + WW_RSTS, RFKILL_TYPE_WWAN, + &asus_rfkill_ops); + if (result) + goto exit; + + if (!acpi_check_handle(asus->handle, METHOD_WIMAX, NULL)) + result = asus_rfkill_setup(asus, &asus->wimax, "asus-wimax", + WM_RSTS, RFKILL_TYPE_WIMAX, + &asus_rfkill_ops); + if (result) + goto exit; + exit: if (result) asus_rfkill_exit(asus); @@ -1859,7 +1881,7 @@ static int __devinit asus_acpi_add(struct acpi_device *device) goto fail_led; result = asus_rfkill_init(asus); - if (result) + if (result && result != -ENODEV) goto fail_rfkill; result = pega_accel_init(asus); -- cgit v1.2.3 From 26594dd47669ec213297b25fae20cdbb21f877b8 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:36 +0100 Subject: asus-laptop: check WLED and BLED presence before adding rfkill Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 547b9eba03ae..c1125b36d177 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -1361,14 +1361,16 @@ static int asus_rfkill_init(struct asus_laptop *asus) goto exit; - if (asus->wled_type == TYPE_RFKILL) + if (!acpi_check_handle(asus->handle, METHOD_WLAN, NULL) && + asus->wled_type == TYPE_RFKILL) result = asus_rfkill_setup(asus, &asus->wlan, "asus-wlan", WL_RSTS, RFKILL_TYPE_WLAN, &asus_rfkill_ops); if (result) goto exit; - if (asus->bled_type == TYPE_RFKILL) + if (!acpi_check_handle(asus->handle, METHOD_BLUETOOTH, NULL) && + asus->bled_type == TYPE_RFKILL) result = asus_rfkill_setup(asus, &asus->bluetooth, "asus-bluetooth", BT_RSTS, RFKILL_TYPE_BLUETOOTH, -- cgit v1.2.3 From 7ec48ceda25c6c16ab3f69b6c318d3d196f7abd0 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:37 +0100 Subject: platform/x86: drop deprecated asus_acpi driver asus_acpi only support old models, it has been deprecated since 2009 in favor of asus-laptop, it's not built by any (sane) distro, so it is time to say good bye. Thanks to Julien Lerouge and Karol Kozimor for the work they have done on it, I would never have wrote asus-laptop and other asus related drivers without asus_acpi. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- Documentation/laptops/asus-laptop.txt | 2 +- drivers/acpi/video_detect.c | 2 +- drivers/platform/x86/Kconfig | 43 +- drivers/platform/x86/Makefile | 1 - drivers/platform/x86/asus_acpi.c | 1513 --------------------------------- 5 files changed, 6 insertions(+), 1555 deletions(-) delete mode 100644 drivers/platform/x86/asus_acpi.c diff --git a/Documentation/laptops/asus-laptop.txt b/Documentation/laptops/asus-laptop.txt index 803e51f6768b..a1e04d679289 100644 --- a/Documentation/laptops/asus-laptop.txt +++ b/Documentation/laptops/asus-laptop.txt @@ -45,7 +45,7 @@ Status Usage ----- - Try "modprobe asus_acpi". Check your dmesg (simply type dmesg). You should + Try "modprobe asus-laptop". Check your dmesg (simply type dmesg). You should see some lines like this : Asus Laptop Extras version 0.42 diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index f3f0fe7e255a..45d8097ef4cf 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -23,7 +23,7 @@ * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) * are available, video.ko should be used to handle the device. * - * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi, + * Otherwise vendor specific drivers like thinkpad_acpi, asus-laptop, * sony_acpi,... can take care about backlight brightness. * * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 912ffef0f148..0b5519cda194 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -54,7 +54,6 @@ config ACERHDF config ASUS_LAPTOP tristate "Asus Laptop Extras" depends on ACPI - depends on !ACPI_ASUS select LEDS_CLASS select NEW_LEDS select BACKLIGHT_CLASS_DEVICE @@ -460,10 +459,9 @@ config INTEL_MENLOW If unsure, say N. config EEEPC_LAPTOP - tristate "Eee PC Hotkey Driver (EXPERIMENTAL)" + tristate "Eee PC Hotkey Driver" depends on ACPI depends on INPUT - depends on EXPERIMENTAL depends on RFKILL || RFKILL = n depends on HOTPLUG_PCI select BACKLIGHT_CLASS_DEVICE @@ -482,11 +480,10 @@ config EEEPC_LAPTOP doesn't work on your Eee PC, try eeepc-wmi instead. config ASUS_WMI - tristate "ASUS WMI Driver (EXPERIMENTAL)" + tristate "ASUS WMI Driver" 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 @@ -501,7 +498,7 @@ config ASUS_WMI be called asus-wmi. config ASUS_NB_WMI - tristate "Asus Notebook WMI Driver (EXPERIMENTAL)" + tristate "Asus Notebook WMI Driver" depends on ASUS_WMI ---help--- This is a driver for newer Asus notebooks. It adds extra features @@ -514,7 +511,7 @@ config ASUS_NB_WMI here. config EEEPC_WMI - tristate "Eee PC WMI Driver (EXPERIMENTAL)" + tristate "Eee PC WMI Driver" depends on ASUS_WMI ---help--- This is a driver for newer Eee PC laptops. It adds extra features @@ -559,38 +556,6 @@ config MSI_WMI To compile this driver as a module, choose M here: the module will be called msi-wmi. -config ACPI_ASUS - tristate "ASUS/Medion Laptop Extras (DEPRECATED)" - depends on ACPI - select BACKLIGHT_CLASS_DEVICE - ---help--- - This driver provides support for extra features of ACPI-compatible - ASUS laptops. As some of Medion laptops are made by ASUS, it may also - support some Medion laptops (such as 9675 for example). It makes all - the extra buttons generate standard ACPI events that go through - /proc/acpi/events, and (on some models) adds support for changing the - display brightness and output, switching the LCD backlight on and off, - and most importantly, allows you to blink those fancy LEDs intended - for reporting mail and wireless status. - - Note: display switching code is currently considered EXPERIMENTAL, - toying with these values may even lock your machine. - - All settings are changed via /proc/acpi/asus directory entries. Owner - and group for these entries can be set with asus_uid and asus_gid - parameters. - - More information and a userspace daemon for handling the extra buttons - at . - - If you have an ACPI-compatible ASUS laptop, say Y or M here. This - driver is still under development, so if your laptop is unsupported or - something works not quite as expected, please use the mailing list - available on the above page (acpi4asus-user@lists.sourceforge.net). - - NOTE: This driver is deprecated and will probably be removed soon, - use asus-laptop instead. - config TOPSTAR_LAPTOP tristate "Topstar Laptop Extras" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index d328f21e9fdd..3c07f8bfd42c 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -29,7 +29,6 @@ obj-$(CONFIG_PANASONIC_LAPTOP) += panasonic-laptop.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o -obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_TOSHIBA_BT_RFKILL) += toshiba_bluetooth.o diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c deleted file mode 100644 index 6f966d6c062b..000000000000 --- a/drivers/platform/x86/asus_acpi.c +++ /dev/null @@ -1,1513 +0,0 @@ -/* - * asus_acpi.c - Asus Laptop ACPI Extras - * - * - * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor - * - * 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 - * - * - * The development page for this driver is located at - * http://sourceforge.net/projects/acpi4asus/ - * - * Credits: - * Pontus Fuchs - Helper functions, cleanup - * Johann Wiesner - Small compile fixes - * John Belmonte - ACPI code for Toshiba laptop was a good starting point. - * �ic Burghard - LED display support for W1N - * - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ASUS_ACPI_VERSION "0.30" - -#define PROC_ASUS "asus" /* The directory */ -#define PROC_MLED "mled" -#define PROC_WLED "wled" -#define PROC_TLED "tled" -#define PROC_BT "bluetooth" -#define PROC_LEDD "ledd" -#define PROC_INFO "info" -#define PROC_LCD "lcd" -#define PROC_BRN "brn" -#define PROC_DISP "disp" - -#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver" -#define ACPI_HOTK_CLASS "hotkey" -#define ACPI_HOTK_DEVICE_NAME "Hotkey" - -/* - * Some events we use, same for all Asus - */ -#define BR_UP 0x10 -#define BR_DOWN 0x20 - -/* - * Flags for hotk status - */ -#define MLED_ON 0x01 /* Mail LED */ -#define WLED_ON 0x02 /* Wireless LED */ -#define TLED_ON 0x04 /* Touchpad LED */ -#define BT_ON 0x08 /* Internal Bluetooth */ - -MODULE_AUTHOR("Julien Lerouge, Karol Kozimor"); -MODULE_DESCRIPTION(ACPI_HOTK_NAME); -MODULE_LICENSE("GPL"); - -static uid_t asus_uid; -static gid_t asus_gid; -module_param(asus_uid, uint, 0); -MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus"); -module_param(asus_gid, uint, 0); -MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus"); - -/* For each model, all features implemented, - * those marked with R are relative to HOTK, A for absolute */ -struct model_data { - char *name; /* name of the laptop________________A */ - char *mt_mled; /* method to handle mled_____________R */ - char *mled_status; /* node to handle mled reading_______A */ - char *mt_wled; /* method to handle wled_____________R */ - char *wled_status; /* node to handle wled reading_______A */ - char *mt_tled; /* method to handle tled_____________R */ - char *tled_status; /* node to handle tled reading_______A */ - char *mt_ledd; /* method to handle LED display______R */ - char *mt_bt_switch; /* method to switch Bluetooth on/off_R */ - char *bt_status; /* no model currently supports this__? */ - char *mt_lcd_switch; /* method to turn LCD on/off_________A */ - char *lcd_status; /* node to read LCD panel state______A */ - char *brightness_up; /* method to set brightness up_______A */ - char *brightness_down; /* method to set brightness down ____A */ - char *brightness_set; /* method to set absolute brightness_R */ - char *brightness_get; /* method to get absolute brightness_R */ - char *brightness_status;/* node to get brightness____________A */ - char *display_set; /* method to set video output________R */ - char *display_get; /* method to get video output________R */ -}; - -/* - * This is the main structure, we can use it to store anything interesting - * about the hotk device - */ -struct asus_hotk { - struct acpi_device *device; /* the device we are in */ - acpi_handle handle; /* the handle of the hotk device */ - char status; /* status of the hotk, for LEDs */ - u32 ledd_status; /* status of the LED display */ - struct model_data *methods; /* methods available on the laptop */ - u8 brightness; /* brightness level */ - enum { - A1x = 0, /* A1340D, A1300F */ - A2x, /* A2500H */ - A4G, /* A4700G */ - D1x, /* D1 */ - L2D, /* L2000D */ - L3C, /* L3800C */ - L3D, /* L3400D */ - L3H, /* L3H, L2000E, L5D */ - L4R, /* L4500R */ - L5x, /* L5800C */ - L8L, /* L8400L */ - M1A, /* M1300A */ - M2E, /* M2400E, L4400L */ - M6N, /* M6800N, W3400N */ - M6R, /* M6700R, A3000G */ - P30, /* Samsung P30 */ - S1x, /* S1300A, but also L1400B and M2400A (L84F) */ - S2x, /* S200 (J1 reported), Victor MP-XP7210 */ - W1N, /* W1000N */ - W5A, /* W5A */ - W3V, /* W3030V */ - xxN, /* M2400N, M3700N, M5200N, M6800N, - S1300N, S5200N*/ - A4S, /* Z81sp */ - F3Sa, /* (Centrino) */ - R1F, - END_MODEL - } model; /* Models currently supported */ - u16 event_count[128]; /* Count for each event TODO make this better */ -}; - -/* Here we go */ -#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0." -#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0." -#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0." -#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0." -#define S1x_PREFIX "\\_SB.PCI0.PX40." -#define S2x_PREFIX A1x_PREFIX -#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0." - -static struct model_data model_conf[END_MODEL] = { - /* - * TODO I have seen a SWBX and AIBX method on some models, like L1400B, - * it seems to be a kind of switch, but what for ? - */ - - { - .name = "A1x", - .mt_mled = "MLED", - .mled_status = "\\MAIL", - .mt_lcd_switch = A1x_PREFIX "_Q10", - .lcd_status = "\\BKLI", - .brightness_up = A1x_PREFIX "_Q0E", - .brightness_down = A1x_PREFIX "_Q0F"}, - - { - .name = "A2x", - .mt_mled = "MLED", - .mt_wled = "WLED", - .wled_status = "\\SG66", - .mt_lcd_switch = "\\Q10", - .lcd_status = "\\BAOF", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "A4G", - .mt_mled = "MLED", -/* WLED present, but not controlled by ACPI */ - .mt_lcd_switch = xxN_PREFIX "_Q10", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\ADVG"}, - - { - .name = "D1x", - .mt_mled = "MLED", - .mt_lcd_switch = "\\Q0D", - .lcd_status = "\\GP11", - .brightness_up = "\\Q0C", - .brightness_down = "\\Q0B", - .brightness_status = "\\BLVL", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "L2D", - .mt_mled = "MLED", - .mled_status = "\\SGP6", - .mt_wled = "WLED", - .wled_status = "\\RCP3", - .mt_lcd_switch = "\\Q10", - .lcd_status = "\\SGP0", - .brightness_up = "\\Q0E", - .brightness_down = "\\Q0F", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "L3C", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_lcd_switch = L3C_PREFIX "_Q10", - .lcd_status = "\\GL32", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"}, - - { - .name = "L3D", - .mt_mled = "MLED", - .mled_status = "\\MALD", - .mt_wled = "WLED", - .mt_lcd_switch = "\\Q10", - .lcd_status = "\\BKLG", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "L3H", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_lcd_switch = "EHK", - .lcd_status = "\\_SB.PCI0.PM.PBC", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "L4R", - .mt_mled = "MLED", - .mt_wled = "WLED", - .wled_status = "\\_SB.PCI0.SBRG.SG13", - .mt_lcd_switch = xxN_PREFIX "_Q10", - .lcd_status = "\\_SB.PCI0.SBSM.SEO4", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"}, - - { - .name = "L5x", - .mt_mled = "MLED", -/* WLED present, but not controlled by ACPI */ - .mt_tled = "TLED", - .mt_lcd_switch = "\\Q0D", - .lcd_status = "\\BAOF", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "L8L" -/* No features, but at least support the hotkeys */ - }, - - { - .name = "M1A", - .mt_mled = "MLED", - .mt_lcd_switch = M1A_PREFIX "Q10", - .lcd_status = "\\PNOF", - .brightness_up = M1A_PREFIX "Q0E", - .brightness_down = M1A_PREFIX "Q0F", - .brightness_status = "\\BRIT", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "M2E", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_lcd_switch = "\\Q10", - .lcd_status = "\\GP06", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "M6N", - .mt_mled = "MLED", - .mt_wled = "WLED", - .wled_status = "\\_SB.PCI0.SBRG.SG13", - .mt_lcd_switch = xxN_PREFIX "_Q10", - .lcd_status = "\\_SB.BKLT", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\SSTE"}, - - { - .name = "M6R", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_lcd_switch = xxN_PREFIX "_Q10", - .lcd_status = "\\_SB.PCI0.SBSM.SEO4", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"}, - - { - .name = "P30", - .mt_wled = "WLED", - .mt_lcd_switch = P30_PREFIX "_Q0E", - .lcd_status = "\\BKLT", - .brightness_up = P30_PREFIX "_Q68", - .brightness_down = P30_PREFIX "_Q69", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\DNXT"}, - - { - .name = "S1x", - .mt_mled = "MLED", - .mled_status = "\\EMLE", - .mt_wled = "WLED", - .mt_lcd_switch = S1x_PREFIX "Q10", - .lcd_status = "\\PNOF", - .brightness_set = "SPLV", - .brightness_get = "GPLV"}, - - { - .name = "S2x", - .mt_mled = "MLED", - .mled_status = "\\MAIL", - .mt_lcd_switch = S2x_PREFIX "_Q10", - .lcd_status = "\\BKLI", - .brightness_up = S2x_PREFIX "_Q0B", - .brightness_down = S2x_PREFIX "_Q0A"}, - - { - .name = "W1N", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_ledd = "SLCM", - .mt_lcd_switch = xxN_PREFIX "_Q10", - .lcd_status = "\\BKLT", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\ADVG"}, - - { - .name = "W5A", - .mt_bt_switch = "BLED", - .mt_wled = "WLED", - .mt_lcd_switch = xxN_PREFIX "_Q10", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\ADVG"}, - - { - .name = "W3V", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_lcd_switch = xxN_PREFIX "_Q10", - .lcd_status = "\\BKLT", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB"}, - - { - .name = "xxN", - .mt_mled = "MLED", -/* WLED present, but not controlled by ACPI */ - .mt_lcd_switch = xxN_PREFIX "_Q10", - .lcd_status = "\\BKLT", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\ADVG"}, - - { - .name = "A4S", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .mt_bt_switch = "BLED", - .mt_wled = "WLED" - }, - - { - .name = "F3Sa", - .mt_bt_switch = "BLED", - .mt_wled = "WLED", - .mt_mled = "MLED", - .brightness_get = "GPLV", - .brightness_set = "SPLV", - .mt_lcd_switch = "\\_SB.PCI0.SBRG.EC0._Q10", - .lcd_status = "\\_SB.PCI0.SBRG.EC0.RPIN", - .display_get = "\\ADVG", - .display_set = "SDSP", - }, - { - .name = "R1F", - .mt_bt_switch = "BLED", - .mt_mled = "MLED", - .mt_wled = "WLED", - .mt_lcd_switch = "\\Q10", - .lcd_status = "\\GP06", - .brightness_set = "SPLV", - .brightness_get = "GPLV", - .display_set = "SDSP", - .display_get = "\\INFB" - } -}; - -/* procdir we use */ -static struct proc_dir_entry *asus_proc_dir; - -static struct backlight_device *asus_backlight_device; - -/* - * This header is made available to allow proper configuration given model, - * revision number , ... this info cannot go in struct asus_hotk because it is - * available before the hotk - */ -static struct acpi_table_header *asus_info; - -/* The actual device the driver binds to */ -static struct asus_hotk *hotk; - -/* - * The hotkey driver and autoloading declaration - */ -static int asus_hotk_add(struct acpi_device *device); -static int asus_hotk_remove(struct acpi_device *device, int type); -static void asus_hotk_notify(struct acpi_device *device, u32 event); - -static const struct acpi_device_id asus_device_ids[] = { - {"ATK0100", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, asus_device_ids); - -static struct acpi_driver asus_hotk_driver = { - .name = "asus_acpi", - .class = ACPI_HOTK_CLASS, - .owner = THIS_MODULE, - .ids = asus_device_ids, - .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, - .ops = { - .add = asus_hotk_add, - .remove = asus_hotk_remove, - .notify = asus_hotk_notify, - }, -}; - -/* - * This function evaluates an ACPI method, given an int as parameter, the - * method is searched within the scope of the handle, can be NULL. The output - * of the method is written is output, which can also be NULL - * - * returns 1 if write is successful, 0 else. - */ -static int write_acpi_int(acpi_handle handle, const char *method, int val, - struct acpi_buffer *output) -{ - struct acpi_object_list params; /* list of input parameters (int) */ - union acpi_object in_obj; /* the only param we use */ - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = val; - - status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); - return (status == AE_OK); -} - -static int read_acpi_int(acpi_handle handle, const char *method, int *val) -{ - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - status = acpi_evaluate_object(handle, (char *)method, NULL, &output); - *val = out_obj.integer.value; - return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); -} - -static int asus_info_proc_show(struct seq_file *m, void *v) -{ - int temp; - - seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n"); - seq_printf(m, "Model reference : %s\n", hotk->methods->name); - /* - * The SFUN method probably allows the original driver to get the list - * of features supported by a given model. For now, 0x0100 or 0x0800 - * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. - * The significance of others is yet to be found. - */ - if (read_acpi_int(hotk->handle, "SFUN", &temp)) - seq_printf(m, "SFUN value : 0x%04x\n", temp); - /* - * Another value for userspace: the ASYM method returns 0x02 for - * battery low and 0x04 for battery critical, its readings tend to be - * more accurate than those provided by _BST. - * Note: since not all the laptops provide this method, errors are - * silently ignored. - */ - if (read_acpi_int(hotk->handle, "ASYM", &temp)) - seq_printf(m, "ASYM value : 0x%04x\n", temp); - if (asus_info) { - seq_printf(m, "DSDT length : %d\n", asus_info->length); - seq_printf(m, "DSDT checksum : %d\n", asus_info->checksum); - seq_printf(m, "DSDT revision : %d\n", asus_info->revision); - seq_printf(m, "OEM id : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id); - seq_printf(m, "OEM table id : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id); - seq_printf(m, "OEM revision : 0x%x\n", asus_info->oem_revision); - seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id); - seq_printf(m, "ASL comp revision : 0x%x\n", asus_info->asl_compiler_revision); - } - - return 0; -} - -static int asus_info_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, asus_info_proc_show, NULL); -} - -static const struct file_operations asus_info_proc_fops = { - .owner = THIS_MODULE, - .open = asus_info_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* - * /proc handlers - * We write our info in page, we begin at offset off and cannot write more - * than count bytes. We set eof to 1 if we handle those 2 values. We return the - * number of bytes written in page - */ - -/* Generic LED functions */ -static int read_led(const char *ledname, int ledmask) -{ - if (ledname) { - int led_status; - - if (read_acpi_int(NULL, ledname, &led_status)) - return led_status; - else - pr_warn("Error reading LED status\n"); - } - return (hotk->status & ledmask) ? 1 : 0; -} - -static int parse_arg(const char __user *buf, unsigned long count, int *val) -{ - char s[32]; - if (!count) - return 0; - if (count > 31) - return -EINVAL; - if (copy_from_user(s, buf, count)) - return -EFAULT; - s[count] = 0; - if (sscanf(s, "%i", val) != 1) - return -EINVAL; - return count; -} - -/* FIXME: kill extraneous args so it can be called independently */ -static int -write_led(const char __user *buffer, unsigned long count, - char *ledname, int ledmask, int invert) -{ - int rv, value; - int led_out = 0; - - rv = parse_arg(buffer, count, &value); - if (rv > 0) - led_out = value ? 1 : 0; - - hotk->status = - (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask); - - if (invert) /* invert target value */ - led_out = !led_out; - - if (!write_acpi_int(hotk->handle, ledname, led_out, NULL)) - pr_warn("LED (%s) write failed\n", ledname); - - return rv; -} - -/* - * Proc handlers for MLED - */ -static int mled_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON)); - return 0; -} - -static int mled_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, mled_proc_show, NULL); -} - -static ssize_t mled_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1); -} - -static const struct file_operations mled_proc_fops = { - .owner = THIS_MODULE, - .open = mled_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = mled_proc_write, -}; - -/* - * Proc handlers for LED display - */ -static int ledd_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "0x%08x\n", hotk->ledd_status); - return 0; -} - -static int ledd_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, ledd_proc_show, NULL); -} - -static ssize_t ledd_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - int rv, value; - - rv = parse_arg(buffer, count, &value); - if (rv > 0) { - if (!write_acpi_int - (hotk->handle, hotk->methods->mt_ledd, value, NULL)) - pr_warn("LED display write failed\n"); - else - hotk->ledd_status = (u32) value; - } - return rv; -} - -static const struct file_operations ledd_proc_fops = { - .owner = THIS_MODULE, - .open = ledd_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = ledd_proc_write, -}; - -/* - * Proc handlers for WLED - */ -static int wled_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON)); - return 0; -} - -static int wled_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, wled_proc_show, NULL); -} - -static ssize_t wled_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0); -} - -static const struct file_operations wled_proc_fops = { - .owner = THIS_MODULE, - .open = wled_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = wled_proc_write, -}; - -/* - * Proc handlers for Bluetooth - */ -static int bluetooth_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON)); - return 0; -} - -static int bluetooth_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, bluetooth_proc_show, NULL); -} - -static ssize_t bluetooth_proc_write(struct file *file, - const char __user *buffer, size_t count, loff_t *pos) -{ - /* Note: mt_bt_switch controls both internal Bluetooth adapter's - presence and its LED */ - return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0); -} - -static const struct file_operations bluetooth_proc_fops = { - .owner = THIS_MODULE, - .open = bluetooth_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = bluetooth_proc_write, -}; - -/* - * Proc handlers for TLED - */ -static int tled_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON)); - return 0; -} - -static int tled_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, tled_proc_show, NULL); -} - -static ssize_t tled_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0); -} - -static const struct file_operations tled_proc_fops = { - .owner = THIS_MODULE, - .open = tled_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = tled_proc_write, -}; - -static int get_lcd_state(void) -{ - int lcd = 0; - - if (hotk->model == L3H) { - /* L3H and the like have to be handled differently */ - acpi_status status = 0; - struct acpi_object_list input; - union acpi_object mt_params[2]; - struct acpi_buffer output; - union acpi_object out_obj; - - input.count = 2; - input.pointer = mt_params; - /* Note: the following values are partly guessed up, but - otherwise they seem to work */ - mt_params[0].type = ACPI_TYPE_INTEGER; - mt_params[0].integer.value = 0x02; - mt_params[1].type = ACPI_TYPE_INTEGER; - mt_params[1].integer.value = 0x02; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - status = - acpi_evaluate_object(NULL, hotk->methods->lcd_status, - &input, &output); - if (status != AE_OK) - return -1; - if (out_obj.type == ACPI_TYPE_INTEGER) - /* That's what the AML code does */ - lcd = out_obj.integer.value >> 8; - } else if (hotk->model == F3Sa) { - unsigned long long tmp; - union acpi_object param; - struct acpi_object_list input; - acpi_status status; - - /* Read pin 11 */ - param.type = ACPI_TYPE_INTEGER; - param.integer.value = 0x11; - input.count = 1; - input.pointer = ¶m; - - status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status, - &input, &tmp); - if (status != AE_OK) - return -1; - - lcd = tmp; - } else { - /* We don't have to check anything if we are here */ - if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) - pr_warn("Error reading LCD status\n"); - - if (hotk->model == L2D) - lcd = ~lcd; - } - - return (lcd & 1); -} - -static int set_lcd_state(int value) -{ - int lcd = 0; - acpi_status status = 0; - - lcd = value ? 1 : 0; - if (lcd != get_lcd_state()) { - /* switch */ - if (hotk->model != L3H) { - status = - acpi_evaluate_object(NULL, - hotk->methods->mt_lcd_switch, - NULL, NULL); - } else { - /* L3H and the like must be handled differently */ - if (!write_acpi_int - (hotk->handle, hotk->methods->mt_lcd_switch, 0x07, - NULL)) - status = AE_ERROR; - /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress, - the exact behaviour is simulated here */ - } - if (ACPI_FAILURE(status)) - pr_warn("Error switching LCD\n"); - } - return 0; - -} - -static int lcd_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", get_lcd_state()); - return 0; -} - -static int lcd_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, lcd_proc_show, NULL); -} - -static ssize_t lcd_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - int rv, value; - - rv = parse_arg(buffer, count, &value); - if (rv > 0) - set_lcd_state(value); - return rv; -} - -static const struct file_operations lcd_proc_fops = { - .owner = THIS_MODULE, - .open = lcd_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = lcd_proc_write, -}; - -static int read_brightness(struct backlight_device *bd) -{ - int value; - - if (hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ - if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, - &value)) - pr_warn("Error reading brightness\n"); - } else if (hotk->methods->brightness_status) { /* For D1 for example */ - if (!read_acpi_int(NULL, hotk->methods->brightness_status, - &value)) - pr_warn("Error reading brightness\n"); - } else /* No GPLV method */ - value = hotk->brightness; - return value; -} - -/* - * Change the brightness level - */ -static int set_brightness(int value) -{ - acpi_status status = 0; - int ret = 0; - - /* SPLV laptop */ - if (hotk->methods->brightness_set) { - if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set, - value, NULL)) { - pr_warn("Error changing brightness\n"); - ret = -EIO; - } - goto out; - } - - /* No SPLV method if we are here, act as appropriate */ - value -= read_brightness(NULL); - while (value != 0) { - status = acpi_evaluate_object(NULL, (value > 0) ? - hotk->methods->brightness_up : - hotk->methods->brightness_down, - NULL, NULL); - (value > 0) ? value-- : value++; - if (ACPI_FAILURE(status)) { - pr_warn("Error changing brightness\n"); - ret = -EIO; - } - } -out: - return ret; -} - -static int set_brightness_status(struct backlight_device *bd) -{ - return set_brightness(bd->props.brightness); -} - -static int brn_proc_show(struct seq_file *m, void *v) -{ - seq_printf(m, "%d\n", read_brightness(NULL)); - return 0; -} - -static int brn_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, brn_proc_show, NULL); -} - -static ssize_t brn_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - int rv, value; - - rv = parse_arg(buffer, count, &value); - if (rv > 0) { - value = (0 < value) ? ((15 < value) ? 15 : value) : 0; - /* 0 <= value <= 15 */ - set_brightness(value); - } - return rv; -} - -static const struct file_operations brn_proc_fops = { - .owner = THIS_MODULE, - .open = brn_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = brn_proc_write, -}; - -static void set_display(int value) -{ - /* no sanity check needed for now */ - if (!write_acpi_int(hotk->handle, hotk->methods->display_set, - value, NULL)) - pr_warn("Error setting display\n"); - return; -} - -/* - * Now, *this* one could be more user-friendly, but so far, no-one has - * complained. The significance of bits is the same as in proc_write_disp() - */ -static int disp_proc_show(struct seq_file *m, void *v) -{ - int value = 0; - - if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value)) - pr_warn("Error reading display status\n"); - value &= 0x07; /* needed for some models, shouldn't hurt others */ - seq_printf(m, "%d\n", value); - return 0; -} - -static int disp_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, disp_proc_show, NULL); -} - -/* - * Experimental support for display switching. As of now: 1 should activate - * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination - * (bitwise) of these will suffice. I never actually tested 3 displays hooked - * up simultaneously, so be warned. See the acpi4asus README for more info. - */ -static ssize_t disp_proc_write(struct file *file, const char __user *buffer, - size_t count, loff_t *pos) -{ - int rv, value; - - rv = parse_arg(buffer, count, &value); - if (rv > 0) - set_display(value); - return rv; -} - -static const struct file_operations disp_proc_fops = { - .owner = THIS_MODULE, - .open = disp_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .write = disp_proc_write, -}; - -static int -asus_proc_add(char *name, const struct file_operations *proc_fops, umode_t mode, - struct acpi_device *device) -{ - struct proc_dir_entry *proc; - - proc = proc_create_data(name, mode, acpi_device_dir(device), - proc_fops, acpi_driver_data(device)); - if (!proc) { - pr_warn(" Unable to create %s fs entry\n", name); - return -1; - } - proc->uid = asus_uid; - proc->gid = asus_gid; - return 0; -} - -static int asus_hotk_add_fs(struct acpi_device *device) -{ - struct proc_dir_entry *proc; - umode_t mode; - - if ((asus_uid == 0) && (asus_gid == 0)) { - mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP; - } else { - mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; - pr_warn(" asus_uid and asus_gid parameters are " - "deprecated, use chown and chmod instead!\n"); - } - - acpi_device_dir(device) = asus_proc_dir; - if (!acpi_device_dir(device)) - return -ENODEV; - - proc = proc_create(PROC_INFO, mode, acpi_device_dir(device), - &asus_info_proc_fops); - if (proc) { - proc->uid = asus_uid; - proc->gid = asus_gid; - } else { - pr_warn(" Unable to create " PROC_INFO " fs entry\n"); - } - - if (hotk->methods->mt_wled) { - asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device); - } - - if (hotk->methods->mt_ledd) { - asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device); - } - - if (hotk->methods->mt_mled) { - asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device); - } - - if (hotk->methods->mt_tled) { - asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device); - } - - if (hotk->methods->mt_bt_switch) { - asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device); - } - - /* - * We need both read node and write method as LCD switch is also - * accessible from the keyboard - */ - if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) { - asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device); - } - - if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || - (hotk->methods->brightness_get && hotk->methods->brightness_set)) { - asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device); - } - - if (hotk->methods->display_set) { - asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device); - } - - return 0; -} - -static int asus_hotk_remove_fs(struct acpi_device *device) -{ - if (acpi_device_dir(device)) { - remove_proc_entry(PROC_INFO, acpi_device_dir(device)); - if (hotk->methods->mt_wled) - remove_proc_entry(PROC_WLED, acpi_device_dir(device)); - if (hotk->methods->mt_mled) - remove_proc_entry(PROC_MLED, acpi_device_dir(device)); - if (hotk->methods->mt_tled) - remove_proc_entry(PROC_TLED, acpi_device_dir(device)); - if (hotk->methods->mt_ledd) - remove_proc_entry(PROC_LEDD, acpi_device_dir(device)); - if (hotk->methods->mt_bt_switch) - remove_proc_entry(PROC_BT, acpi_device_dir(device)); - if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) - remove_proc_entry(PROC_LCD, acpi_device_dir(device)); - if ((hotk->methods->brightness_up - && hotk->methods->brightness_down) - || (hotk->methods->brightness_get - && hotk->methods->brightness_set)) - remove_proc_entry(PROC_BRN, acpi_device_dir(device)); - if (hotk->methods->display_set) - remove_proc_entry(PROC_DISP, acpi_device_dir(device)); - } - return 0; -} - -static void asus_hotk_notify(struct acpi_device *device, u32 event) -{ - /* TODO Find a better way to handle events count. */ - if (!hotk) - return; - - /* - * The BIOS *should* be sending us device events, but apparently - * Asus uses system events instead, so just ignore any device - * events we get. - */ - if (event > ACPI_MAX_SYS_NOTIFY) - return; - - if ((event & ~((u32) BR_UP)) < 16) - hotk->brightness = (event & ~((u32) BR_UP)); - else if ((event & ~((u32) BR_DOWN)) < 16) - hotk->brightness = (event & ~((u32) BR_DOWN)); - - acpi_bus_generate_proc_event(hotk->device, event, - hotk->event_count[event % 128]++); - - return; -} - -/* - * Match the model string to the list of supported models. Return END_MODEL if - * no match or model is NULL. - */ -static int asus_model_match(char *model) -{ - if (model == NULL) - return END_MODEL; - - if (strncmp(model, "L3D", 3) == 0) - return L3D; - else if (strncmp(model, "L2E", 3) == 0 || - strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0) - return L3H; - else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0) - return L3C; - else if (strncmp(model, "L8L", 3) == 0) - return L8L; - else if (strncmp(model, "L4R", 3) == 0) - return L4R; - else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0) - return M6N; - else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0) - return M6R; - else if (strncmp(model, "M2N", 3) == 0 || - strncmp(model, "M3N", 3) == 0 || - strncmp(model, "M5N", 3) == 0 || - strncmp(model, "S1N", 3) == 0 || - strncmp(model, "S5N", 3) == 0) - return xxN; - else if (strncmp(model, "M1", 2) == 0) - return M1A; - else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0) - return M2E; - else if (strncmp(model, "L2", 2) == 0) - return L2D; - else if (strncmp(model, "L8", 2) == 0) - return S1x; - else if (strncmp(model, "D1", 2) == 0) - return D1x; - else if (strncmp(model, "A1", 2) == 0) - return A1x; - else if (strncmp(model, "A2", 2) == 0) - return A2x; - else if (strncmp(model, "J1", 2) == 0) - return S2x; - else if (strncmp(model, "L5", 2) == 0) - return L5x; - else if (strncmp(model, "A4G", 3) == 0) - return A4G; - else if (strncmp(model, "W1N", 3) == 0) - return W1N; - else if (strncmp(model, "W3V", 3) == 0) - return W3V; - else if (strncmp(model, "W5A", 3) == 0) - return W5A; - else if (strncmp(model, "R1F", 3) == 0) - return R1F; - else if (strncmp(model, "A4S", 3) == 0) - return A4S; - else if (strncmp(model, "F3Sa", 4) == 0) - return F3Sa; - else - return END_MODEL; -} - -/* - * This function is used to initialize the hotk with right values. In this - * method, we can make all the detection we want, and modify the hotk struct - */ -static int asus_hotk_get_info(void) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *model = NULL; - int bsts_result; - char *string = NULL; - acpi_status status; - - /* - * Get DSDT headers early enough to allow for differentiating between - * models, but late enough to allow acpi_bus_register_driver() to fail - * before doing anything ACPI-specific. Should we encounter a machine, - * which needs special handling (i.e. its hotkey device has a different - * HID), this bit will be moved. A global variable asus_info contains - * the DSDT header. - */ - status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); - if (ACPI_FAILURE(status)) - pr_warn(" Couldn't get the DSDT table header\n"); - - /* We have to write 0 on init this far for all ASUS models */ - if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { - pr_err(" Hotkey initialization failed\n"); - return -ENODEV; - } - - /* This needs to be called for some laptops to init properly */ - if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result)) - pr_warn(" Error calling BSTS\n"); - else if (bsts_result) - pr_notice(" BSTS called, 0x%02x returned\n", bsts_result); - - /* - * Try to match the object returned by INIT to the specific model. - * Handle every possible object (or the lack of thereof) the DSDT - * writers might throw at us. When in trouble, we pass NULL to - * asus_model_match() and try something completely different. - */ - if (buffer.pointer) { - model = buffer.pointer; - switch (model->type) { - case ACPI_TYPE_STRING: - string = model->string.pointer; - break; - case ACPI_TYPE_BUFFER: - string = model->buffer.pointer; - break; - default: - kfree(model); - model = NULL; - break; - } - } - hotk->model = asus_model_match(string); - if (hotk->model == END_MODEL) { /* match failed */ - if (asus_info && - strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { - hotk->model = P30; - pr_notice(" Samsung P30 detected, supported\n"); - hotk->methods = &model_conf[hotk->model]; - kfree(model); - return 0; - } else { - hotk->model = M2E; - pr_notice(" unsupported model %s, trying default values\n", - string); - pr_notice(" send /proc/acpi/dsdt to the developers\n"); - kfree(model); - return -ENODEV; - } - } - hotk->methods = &model_conf[hotk->model]; - pr_notice(" %s model detected, supported\n", string); - - /* Sort of per-model blacklist */ - if (strncmp(string, "L2B", 3) == 0) - hotk->methods->lcd_status = NULL; - /* L2B is similar enough to L3C to use its settings, with this only - exception */ - else if (strncmp(string, "A3G", 3) == 0) - hotk->methods->lcd_status = "\\BLFG"; - /* A3G is like M6R */ - else if (strncmp(string, "S5N", 3) == 0 || - strncmp(string, "M5N", 3) == 0 || - strncmp(string, "W3N", 3) == 0) - hotk->methods->mt_mled = NULL; - /* S5N, M5N and W3N have no MLED */ - else if (strncmp(string, "L5D", 3) == 0) - hotk->methods->mt_wled = NULL; - /* L5D's WLED is not controlled by ACPI */ - else if (strncmp(string, "M2N", 3) == 0 || - strncmp(string, "W3V", 3) == 0 || - strncmp(string, "S1N", 3) == 0) - hotk->methods->mt_wled = "WLED"; - /* M2N, S1N and W3V have a usable WLED */ - else if (asus_info) { - if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) - hotk->methods->mled_status = NULL; - /* S1300A reports L84F, but L1400B too, account for that */ - } - - kfree(model); - - return 0; -} - -static int asus_hotk_check(void) -{ - int result = 0; - - result = acpi_bus_get_status(hotk->device); - if (result) - return result; - - if (hotk->device->status.present) { - result = asus_hotk_get_info(); - } else { - pr_err(" Hotkey device not present, aborting\n"); - return -EINVAL; - } - - return result; -} - -static int asus_hotk_found; - -static int asus_hotk_add(struct acpi_device *device) -{ - acpi_status status = AE_OK; - int result; - - pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION); - - hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); - if (!hotk) - return -ENOMEM; - - hotk->handle = device->handle; - strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_HOTK_CLASS); - device->driver_data = hotk; - hotk->device = device; - - result = asus_hotk_check(); - if (result) - goto end; - - result = asus_hotk_add_fs(device); - if (result) - goto end; - - /* For laptops without GPLV: init the hotk->brightness value */ - if ((!hotk->methods->brightness_get) - && (!hotk->methods->brightness_status) - && (hotk->methods->brightness_up && hotk->methods->brightness_down)) { - status = - acpi_evaluate_object(NULL, hotk->methods->brightness_down, - NULL, NULL); - if (ACPI_FAILURE(status)) - pr_warn(" Error changing brightness\n"); - else { - status = - acpi_evaluate_object(NULL, - hotk->methods->brightness_up, - NULL, NULL); - if (ACPI_FAILURE(status)) - pr_warn(" Strange, error changing brightness\n"); - } - } - - asus_hotk_found = 1; - - /* LED display is off by default */ - hotk->ledd_status = 0xFFF; - -end: - if (result) - kfree(hotk); - - return result; -} - -static int asus_hotk_remove(struct acpi_device *device, int type) -{ - asus_hotk_remove_fs(device); - - kfree(hotk); - - return 0; -} - -static const struct backlight_ops asus_backlight_data = { - .get_brightness = read_brightness, - .update_status = set_brightness_status, -}; - -static void asus_acpi_exit(void) -{ - if (asus_backlight_device) - backlight_device_unregister(asus_backlight_device); - - acpi_bus_unregister_driver(&asus_hotk_driver); - remove_proc_entry(PROC_ASUS, acpi_root_dir); - - return; -} - -static int __init asus_acpi_init(void) -{ - struct backlight_properties props; - int result; - - result = acpi_bus_register_driver(&asus_hotk_driver); - if (result < 0) - return result; - - asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); - if (!asus_proc_dir) { - pr_err("Unable to create /proc entry\n"); - acpi_bus_unregister_driver(&asus_hotk_driver); - return -ENODEV; - } - - /* - * This is a bit of a kludge. We only want this module loaded - * for ASUS systems, but there's currently no way to probe the - * ACPI namespace for ASUS HIDs. So we just return failure if - * we didn't find one, which will cause the module to be - * unloaded. - */ - if (!asus_hotk_found) { - acpi_bus_unregister_driver(&asus_hotk_driver); - remove_proc_entry(PROC_ASUS, acpi_root_dir); - return -ENODEV; - } - - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = 15; - asus_backlight_device = backlight_device_register("asus", NULL, NULL, - &asus_backlight_data, - &props); - if (IS_ERR(asus_backlight_device)) { - pr_err("Could not register asus backlight device\n"); - asus_backlight_device = NULL; - asus_acpi_exit(); - return -ENODEV; - } - - return 0; -} - -module_init(asus_acpi_init); -module_exit(asus_acpi_exit); -- cgit v1.2.3 From a2d5dd24af1308d35329d78e74a1a3a94a1c1344 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:38 +0100 Subject: asus-laptop: add some keys found on Lenovo SL500 Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-laptop.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index c1125b36d177..e38f91be0b10 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -297,6 +297,7 @@ static const struct key_entry asus_keymap[] = { {KE_KEY, 0x02, { KEY_SCREENLOCK } }, {KE_KEY, 0x05, { KEY_WLAN } }, {KE_KEY, 0x08, { KEY_F13 } }, + {KE_KEY, 0x09, { KEY_PROG2 } }, /* Dock */ {KE_KEY, 0x17, { KEY_ZOOM } }, {KE_KEY, 0x1f, { KEY_BATTERY } }, /* End of Lenovo SL Specific keycodes */ @@ -322,6 +323,8 @@ static const struct key_entry asus_keymap[] = { {KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, {KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, {KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ + {KE_KEY, 0x6C, { KEY_SLEEP } }, /* Suspend */ + {KE_KEY, 0x6D, { KEY_SLEEP } }, /* Hibernate */ {KE_KEY, 0x7E, { KEY_BLUETOOTH } }, {KE_KEY, 0x7D, { KEY_BLUETOOTH } }, {KE_KEY, 0x82, { KEY_CAMERA } }, -- cgit v1.2.3 From 20db88e32d139e7646c61b23b027a7471f343fae Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 15 Dec 2011 08:27:39 +0100 Subject: samsung-laptop: fix seclinux rfkill and us it as fallback Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 5047642d1662..7d7109fdbd63 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -541,7 +541,8 @@ static const struct backlight_ops backlight_ops = { static int seclinux_rfkill_set(void *data, bool blocked) { - struct samsung_laptop *samsung = data; + struct samsung_rfkill *srfkill = data; + struct samsung_laptop *samsung = srfkill->samsung; const struct sabi_commands *commands = &samsung->config->commands; return sabi_set_commandb(samsung, commands->set_wireless_button, @@ -889,8 +890,13 @@ static int __init samsung_rfkill_init_swsmi(struct samsung_laptop *samsung) int ret; ret = swsmi_wireless_status(samsung, &data); - if (ret) + if (ret) { + /* Some swsmi laptops use the old seclinux way to control + * wireless devices */ + if (ret == -EINVAL) + ret = samsung_rfkill_init_seclinux(samsung); return ret; + } /* 0x02 seems to mean that the device is no present/available */ -- cgit v1.2.3 From 3fca3d3d5075cd1365c763c6a62076f1ea726229 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Dec 2011 22:27:59 +0000 Subject: platform-x86: intel_mid_thermal: add msic_thermal alias In newer boards this device is called "msic_thermal" instead of "msic_sensor". To support both we add suitable alias for the driver. Signed-off-by: Mika Westerberg Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_mid_thermal.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index e4cc99209ff1..888b3af4877b 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -550,6 +550,7 @@ static int mid_thermal_remove(struct platform_device *pdev) static const struct platform_device_id therm_id_table[] = { { DRIVER_NAME, 1 }, + { "msic_thermal", 1 }, { } }; -- cgit v1.2.3 From 420138a7477eaebafddaefb7412736d924ca7d73 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Dec 2011 22:28:11 +0000 Subject: platform-x86: intel_mid_thermal: convert to use Intel MSIC API Intel MSIC MFD driver provides common register access interface to the devices in the MSIC die so we use that instead of SCU IPC. Signed-off-by: Mika Westerberg Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 2 +- drivers/platform/x86/intel_mid_thermal.c | 39 +++++++++++++++----------------- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0b5519cda194..747dfe7371a1 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -661,7 +661,7 @@ config INTEL_MID_POWER_BUTTON config INTEL_MFLD_THERMAL tristate "Thermal driver for Intel Medfield platform" - depends on INTEL_SCU_IPC && THERMAL + depends on MFD_INTEL_MSIC && THERMAL help Say Y here to enable thermal driver support for the Intel Medfield platform. diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 888b3af4877b..b07f93d64a91 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -33,18 +33,15 @@ #include #include #include - -#include +#include /* 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 @@ -75,8 +72,8 @@ #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 */ +#define ADC_CHNL_START_ADDR INTEL_MSIC_ADC1ADDR0 /* increments by 1 */ +#define ADC_DATA_START_ADDR INTEL_MSIC_ADC1SNS0H /* increments by 2 */ /* MSIC die attributes */ #define MSIC_DIE_ADC_MIN 488 @@ -189,17 +186,17 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) addr = td_info->chnl_addr; /* Enable the msic for conversion before reading */ - ret = intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL3, MSIC_ADCRRDATA_ENBL); + ret = intel_msic_reg_write(INTEL_MSIC_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); + ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCTHERM_ENBL); if (ret) return ret; /* Read the higher bits of data */ - ret = intel_scu_ipc_ioread8(addr, &data); + ret = intel_msic_reg_read(addr, &data); if (ret) return ret; @@ -207,7 +204,7 @@ static int mid_read_temp(struct thermal_zone_device *tzd, unsigned long *temp) adc_val = (data << 2); addr++; - ret = intel_scu_ipc_ioread8(addr, &data);/* Read lower bits */ + ret = intel_msic_reg_read(addr, &data);/* Read lower bits */ if (ret) return ret; @@ -235,7 +232,7 @@ static int configure_adc(int val) int ret; uint8_t data; - ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); + ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data); if (ret) return ret; @@ -246,7 +243,7 @@ static int configure_adc(int val) /* Just stop the ADC */ data &= (~MSIC_ADC_START); } - return intel_scu_ipc_iowrite8(MSIC_THERM_ADC1CNTL1, data); + return intel_msic_reg_write(INTEL_MSIC_ADC1CNTL1, data); } /** @@ -262,21 +259,21 @@ 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); + ret = intel_msic_reg_write(base_addr, SKIN_SENSOR0_CODE); if (ret) return ret; - ret = intel_scu_ipc_iowrite8(base_addr + 1, SKIN_SENSOR1_CODE); + ret = intel_msic_reg_write(base_addr + 1, SKIN_SENSOR1_CODE); if (ret) return ret; - ret = intel_scu_ipc_iowrite8(base_addr + 2, SYS_SENSOR_CODE); + ret = intel_msic_reg_write(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, + ret = intel_msic_reg_write(base_addr + 3, (MSIC_DIE_SENSOR_CODE | 0x10)); if (ret) return ret; @@ -295,11 +292,11 @@ static int reset_stopbit(uint16_t addr) { int ret; uint8_t data; - ret = intel_scu_ipc_ioread8(addr, &data); + ret = intel_msic_reg_read(addr, &data); if (ret) return ret; /* Set the stop bit to zero */ - return intel_scu_ipc_iowrite8(addr, (data & 0xEF)); + return intel_msic_reg_write(addr, (data & 0xEF)); } /** @@ -322,7 +319,7 @@ static int find_free_channel(void) uint8_t data; /* check whether ADC is enabled */ - ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL1, &data); + ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data); if (ret) return ret; @@ -331,7 +328,7 @@ static int find_free_channel(void) /* 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); + ret = intel_msic_reg_read(ADC_CHNL_START_ADDR + i, &data); if (ret) return ret; @@ -359,7 +356,7 @@ static int mid_initialize_adc(struct device *dev) * Ensure that adctherm is disabled before we * initialize the ADC */ - ret = intel_scu_ipc_ioread8(MSIC_THERM_ADC1CNTL3, &data); + ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL3, &data); if (ret) return ret; -- cgit v1.2.3 From 0266e49b3fd37065f9f90856c75f442c020bd96e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Dec 2011 22:28:24 +0000 Subject: platform-x86: intel_mid_thermal: turn off thermistor voltage by default Instead of complaining that the voltage is on, we can just ask the MSIC to turn the voltage off. This should save some power. Voltage for thermistors is turned on when ADC conversion is initiated. Signed-off-by: Mika Westerberg Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_mid_thermal.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index b07f93d64a91..acd7d2d3d912 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -360,8 +360,10 @@ static int mid_initialize_adc(struct device *dev) if (ret) return ret; - if (data & MSIC_ADCTHERM_MASK) - dev_warn(dev, "ADCTHERM already set"); + data &= ~MSIC_ADCTHERM_MASK; + ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, data); + if (ret) + return ret; /* Index of the first channel in which the stop bit is set */ channel_index = find_free_channel(); -- cgit v1.2.3 From 984165a37ca65d990419566d9af5dd247d03d2a0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 15 Dec 2011 22:28:37 +0000 Subject: x86, mrst: add msic_thermal platform support This will let the MSIC driver to create platform device for the thermal driver. Signed-off-by: Mika Westerberg Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- arch/x86/platform/mrst/mrst.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 475e2cd0f3c3..229b8bf42cd9 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -686,6 +686,11 @@ static void *msic_ocd_platform_data(void *info) return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); } +static void *msic_thermal_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); +} + static const struct devs_id __initconst device_ids[] = { {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, @@ -705,6 +710,7 @@ static const struct devs_id __initconst device_ids[] = { {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, + {"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, {}, }; -- cgit v1.2.3 From 38db157d7b52a34be3e059d0bfa8ea9c9086105f Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Mon, 9 Jan 2012 14:26:44 +0800 Subject: acer-wmi: remove useless input argument for internal wmi The "wmi_interface *iface" is a useless input argument for internal wmi get/set functions, remove it to clear up source code. Tested on Lenovo E520. Tested on Acer TravelMate 4750. Tested-by: mr.kobzar Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 6cd2289dd5d3..7203e060d64d 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -536,8 +536,7 @@ struct acpi_buffer *result) return status; } -static acpi_status AMW0_get_u32(u32 *value, u32 cap, -struct wmi_interface *iface) +static acpi_status AMW0_get_u32(u32 *value, u32 cap) { int err; u8 result; @@ -607,7 +606,7 @@ struct wmi_interface *iface) return AE_OK; } -static acpi_status AMW0_set_u32(u32 value, u32 cap, struct wmi_interface *iface) +static acpi_status AMW0_set_u32(u32 value, u32 cap) { struct wmab_args args; @@ -827,8 +826,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out) return status; } -static acpi_status WMID_get_u32(u32 *value, u32 cap, -struct wmi_interface *iface) +static acpi_status WMID_get_u32(u32 *value, u32 cap) { acpi_status status; u8 tmp; @@ -864,7 +862,7 @@ struct wmi_interface *iface) return status; } -static acpi_status WMID_set_u32(u32 value, u32 cap, struct wmi_interface *iface) +static acpi_status WMID_set_u32(u32 value, u32 cap) { u32 method_id = 0; char param; @@ -1154,15 +1152,15 @@ static acpi_status get_u32(u32 *value, u32 cap) switch (interface->type) { case ACER_AMW0: - status = AMW0_get_u32(value, cap, interface); + status = AMW0_get_u32(value, cap); break; case ACER_AMW0_V2: if (cap == ACER_CAP_MAILLED) { - status = AMW0_get_u32(value, cap, interface); + status = AMW0_get_u32(value, cap); break; } case ACER_WMID: - status = WMID_get_u32(value, cap, interface); + status = WMID_get_u32(value, cap); break; case ACER_WMID_v2: if (cap & (ACER_CAP_WIRELESS | @@ -1170,7 +1168,7 @@ static acpi_status get_u32(u32 *value, u32 cap) ACER_CAP_THREEG)) status = wmid_v2_get_u32(value, cap); else if (wmi_has_guid(WMID_GUID2)) - status = WMID_get_u32(value, cap, interface); + status = WMID_get_u32(value, cap); break; } @@ -1184,10 +1182,10 @@ static acpi_status set_u32(u32 value, u32 cap) if (interface->capability & cap) { switch (interface->type) { case ACER_AMW0: - return AMW0_set_u32(value, cap, interface); + return AMW0_set_u32(value, cap); case ACER_AMW0_V2: if (cap == ACER_CAP_MAILLED) - return AMW0_set_u32(value, cap, interface); + return AMW0_set_u32(value, cap); /* * On some models, some WMID methods don't toggle @@ -1197,21 +1195,21 @@ static acpi_status set_u32(u32 value, u32 cap) */ if (cap == ACER_CAP_WIRELESS || cap == ACER_CAP_BLUETOOTH) { - status = WMID_set_u32(value, cap, interface); + status = WMID_set_u32(value, cap); if (ACPI_FAILURE(status)) return status; - return AMW0_set_u32(value, cap, interface); + return AMW0_set_u32(value, cap); } case ACER_WMID: - return WMID_set_u32(value, cap, interface); + return WMID_set_u32(value, cap); 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); + return WMID_set_u32(value, cap); default: return AE_BAD_PARAMETER; } -- cgit v1.2.3 From 592b746c55b63770da25148ea3b56ed463a596b2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sun, 15 Jan 2012 14:28:20 +0300 Subject: toshiba_acpi: make one-bit bitfields unsigned This doesn't change how the code works, but it silences a Sparse complaint: drivers/platform/x86/toshiba_acpi.c:121:37: error: dubious one-bit signed bitfield Signed-off-by: Dan Carpenter Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index dcdc1f4a4624..eab5e11fb41f 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -118,10 +118,10 @@ struct toshiba_acpi_dev { int last_key_event; int key_event_valid; - int illumination_supported:1; - int video_supported:1; - int fan_supported:1; - int system_event_supported:1; + unsigned int illumination_supported:1; + unsigned int video_supported:1; + unsigned int fan_supported:1; + unsigned int system_event_supported:1; struct mutex mutex; }; -- cgit v1.2.3 From 3197059af0762c191af23c0ce3fd6f8311c564e7 Mon Sep 17 00:00:00 2001 From: Philip A. Prindeville Date: Sat, 14 Jan 2012 01:45:39 -0700 Subject: geos: Platform driver for Geos and Geos2 single-board computers. Trivial platform driver for Traverse Technologies Geos and Geos2 single-board computers. Uses SMBIOS to identify platform. Based on progressive revisions of the leds-net5501 driver that was rewritten by Ed Wildgoose as a platform driver. Supports GPIO-based LEDs (3) and 1 polled button which is typically used for a soft reset. Signed-off-by: Philip Prindeville Reviewed-by: Ed Wildgoose Acked-by: Andres Salomon Cc: Richard Purdie Cc: Andrew Morton Signed-off-by: Matthew Garrett --- arch/x86/Kconfig | 7 +++ arch/x86/platform/geode/Makefile | 1 + arch/x86/platform/geode/geos.c | 128 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 arch/x86/platform/geode/geos.c diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5bed94e189fa..3a38c4c1d359 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2133,6 +2133,13 @@ config ALIX Note: You have to set alix.force=1 for boards with Award BIOS. +config GEOS + bool "Traverse Technologies GEOS System Support (LEDS, GPIO, etc)" + select GPIOLIB + depends on DMI + ---help--- + This option enables system support for the Traverse Technologies GEOS. + endif # X86_32 config AMD_NB diff --git a/arch/x86/platform/geode/Makefile b/arch/x86/platform/geode/Makefile index 07c9cd05021a..d8ba5644f2f6 100644 --- a/arch/x86/platform/geode/Makefile +++ b/arch/x86/platform/geode/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_ALIX) += alix.o +obj-$(CONFIG_GEOS) += geos.o diff --git a/arch/x86/platform/geode/geos.c b/arch/x86/platform/geode/geos.c new file mode 100644 index 000000000000..c2e6d53558be --- /dev/null +++ b/arch/x86/platform/geode/geos.c @@ -0,0 +1,128 @@ +/* + * System Specific setup for Traverse Technologies GEOS. + * At the moment this means setup of GPIO control of LEDs. + * + * Copyright (C) 2008 Constantin Baranov + * Copyright (C) 2011 Ed Wildgoose + * and Philip Prindeville + * + * TODO: There are large similarities with leds-net5501.c + * by Alessandro Zummo + * In the future leds-net5501.c should be migrated over to platform + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct gpio_keys_button geos_gpio_buttons[] = { + { + .code = KEY_RESTART, + .gpio = 3, + .active_low = 1, + .desc = "Reset button", + .type = EV_KEY, + .wakeup = 0, + .debounce_interval = 100, + .can_disable = 0, + } +}; +static struct gpio_keys_platform_data geos_buttons_data = { + .buttons = geos_gpio_buttons, + .nbuttons = ARRAY_SIZE(geos_gpio_buttons), + .poll_interval = 20, +}; + +static struct platform_device geos_buttons_dev = { + .name = "gpio-keys-polled", + .id = 1, + .dev = { + .platform_data = &geos_buttons_data, + } +}; + +static struct gpio_led geos_leds[] = { + { + .name = "geos:1", + .gpio = 6, + .default_trigger = "default-on", + .active_low = 1, + }, + { + .name = "geos:2", + .gpio = 25, + .default_trigger = "default-off", + .active_low = 1, + }, + { + .name = "geos:3", + .gpio = 27, + .default_trigger = "default-off", + .active_low = 1, + }, +}; + +static struct gpio_led_platform_data geos_leds_data = { + .num_leds = ARRAY_SIZE(geos_leds), + .leds = geos_leds, +}; + +static struct platform_device geos_leds_dev = { + .name = "leds-gpio", + .id = -1, + .dev.platform_data = &geos_leds_data, +}; + +static struct __initdata platform_device *geos_devs[] = { + &geos_buttons_dev, + &geos_leds_dev, +}; + +static void __init register_geos(void) +{ + /* Setup LED control through leds-gpio driver */ + platform_add_devices(geos_devs, ARRAY_SIZE(geos_devs)); +} + +static int __init geos_init(void) +{ + const char *vendor, *product; + + if (!is_geode()) + return 0; + + vendor = dmi_get_system_info(DMI_SYS_VENDOR); + if (!vendor || strcmp(vendor, "Traverse Technologies")) + return 0; + + product = dmi_get_system_info(DMI_PRODUCT_NAME); + if (!product || strcmp(product, "Geos")) + return 0; + + printk(KERN_INFO "%s: system is recognized as \"%s %s\"\n", + KBUILD_MODNAME, vendor, product); + + register_geos(); + + return 0; +} + +module_init(geos_init); + +MODULE_AUTHOR("Philip Prindeville "); +MODULE_DESCRIPTION("Traverse Technologies Geos System Setup"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2d5de9e84928e35b4d9b46b4d8d5dcaac1cff1fa Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Tue, 17 Jan 2012 16:18:06 +0800 Subject: dell-laptop: touchpad LED should persist its status after S3 Touchpad LED will not turn on after S3, it will make the touchpad status doesn't consist with the LED. By adding one flag to let the LED device restore it's status. Signed-off-by: AceLan Kao Signed-off-by: Matthew Garrett --- drivers/platform/x86/dell-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 27600a11f3cc..a05fc9c955d8 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -641,6 +641,7 @@ static void touchpad_led_set(struct led_classdev *led_cdev, static struct led_classdev touchpad_led = { .name = "dell-laptop::touchpad", .brightness_set = touchpad_led_set, + .flags = LED_CORE_SUSPENDRESUME, }; static int __devinit touchpad_led_init(struct device *dev) -- cgit v1.2.3 From 67e1d34cd54cbf33f093f1dd53e7bda1124eb972 Mon Sep 17 00:00:00 2001 From: Merlin Schumacher Date: Tue, 24 Jan 2012 04:35:35 +0800 Subject: acer-wmi: support for P key on TM8372 BugLink: http://launchpad.net/bugs/865807 There is no entry for P key on TM8372, so when P key is pressed, only "acer_wmi: Unknown key number - 0x29" in dmesg. Signed-off-by: Merlin Schumacher Signed-off-by: Ike Panhc Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 7203e060d64d..286dd9caefcb 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -105,6 +105,7 @@ static const struct key_entry acer_wmi_keymap[] = { {KE_KEY, 0x22, {KEY_PROG2} }, /* Arcade */ {KE_KEY, 0x23, {KEY_PROG3} }, /* P_Key */ {KE_KEY, 0x24, {KEY_PROG4} }, /* Social networking_Key */ + {KE_KEY, 0x29, {KEY_PROG3} }, /* P_Key for TM8372 */ {KE_IGNORE, 0x41, {KEY_MUTE} }, {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} }, {KE_IGNORE, 0x43, {KEY_NEXTSONG} }, -- cgit v1.2.3 From ca1469f5f1bb04856dec9549c111b7aec59da71e Mon Sep 17 00:00:00 2001 From: Sergey Senozhatsky Date: Mon, 12 Mar 2012 13:07:13 +0300 Subject: acer-wmi: ignore missing Aspire 5741G keys (checkpatched) acer-wmi: ignore missing Aspire 5741G keys Ignore Aspire's 5741G: KEY_PREVIOUSSONG KEY_NEXTSONG KEY_PLAYPAUSE KEY_STOP KEY_VOLUMEDOWN Signed-off-by: Sergey Senozhatsky Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 286dd9caefcb..937ddeb0e977 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -108,11 +108,16 @@ static const struct key_entry acer_wmi_keymap[] = { {KE_KEY, 0x29, {KEY_PROG3} }, /* P_Key for TM8372 */ {KE_IGNORE, 0x41, {KEY_MUTE} }, {KE_IGNORE, 0x42, {KEY_PREVIOUSSONG} }, + {KE_IGNORE, 0x4d, {KEY_PREVIOUSSONG} }, {KE_IGNORE, 0x43, {KEY_NEXTSONG} }, + {KE_IGNORE, 0x4e, {KEY_NEXTSONG} }, {KE_IGNORE, 0x44, {KEY_PLAYPAUSE} }, + {KE_IGNORE, 0x4f, {KEY_PLAYPAUSE} }, {KE_IGNORE, 0x45, {KEY_STOP} }, + {KE_IGNORE, 0x50, {KEY_STOP} }, {KE_IGNORE, 0x48, {KEY_VOLUMEUP} }, {KE_IGNORE, 0x49, {KEY_VOLUMEDOWN} }, + {KE_IGNORE, 0x4a, {KEY_VOLUMEDOWN} }, {KE_IGNORE, 0x61, {KEY_SWITCHVIDEOMODE} }, {KE_IGNORE, 0x62, {KEY_BRIGHTNESSUP} }, {KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} }, -- cgit v1.2.3 From a938406b768936f6275b18360ecb44624a80deb1 Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Mon, 30 Jan 2012 23:00:11 +0100 Subject: hdaps: trivial fix for -Wuninitialized Trivial fix for some -Wuninitialized compiler warnings. Signed-off-by: Danny Kukawka Signed-off-by: Matthew Garrett --- drivers/platform/x86/hdaps.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c index 2fdacc23cf7b..7387f97a2941 100644 --- a/drivers/platform/x86/hdaps.c +++ b/drivers/platform/x86/hdaps.c @@ -379,7 +379,7 @@ static ssize_t hdaps_temp1_show(struct device *dev, int ret; ret = hdaps_readb_one(HDAPS_PORT_TEMP1, &temp); - if (ret < 0) + if (ret) return ret; return sprintf(buf, "%u\n", temp); @@ -392,7 +392,7 @@ static ssize_t hdaps_temp2_show(struct device *dev, int ret; ret = hdaps_readb_one(HDAPS_PORT_TEMP2, &temp); - if (ret < 0) + if (ret) return ret; return sprintf(buf, "%u\n", temp); -- cgit v1.2.3 From f39eaa674bc3747c94abadbc46b6d389953e64b8 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Thu, 26 Jan 2012 17:37:36 +0000 Subject: platform, x86: Kill off Moorestown All production devices operate in the Oaktrail configuration with legacy PC elements present and an ACPI BIOS. Continue stripping out the Moorestown elements from the tree leaving Medfield. Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 22 - drivers/platform/x86/Makefile | 1 - drivers/platform/x86/intel_rar_register.c | 669 ------------------------------ include/linux/rar_register.h | 60 --- 4 files changed, 752 deletions(-) delete mode 100644 drivers/platform/x86/intel_rar_register.c delete mode 100644 include/linux/rar_register.h diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 747dfe7371a1..2efd7af2a21e 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -666,28 +666,6 @@ config INTEL_MFLD_THERMAL 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 - default n - ---help--- - This driver allows other kernel drivers access to the - contents of the restricted access region control registers. - - The restricted access region control registers - (rar_registers) are used to pass address and - locking information on restricted access regions - to other drivers that use restricted access regions. - - The restricted access regions are regions of memory - on the Intel MID Platform that are not accessible to - the x86 processor, but are accessible to dedicated - processors on board peripheral devices. - - The purpose of the restricted access regions is to - protect sensitive data from compromise by unauthorized - programs running on the x86 processor. - config INTEL_IPS tristate "Intel Intelligent Power Sharing" depends on ACPI diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 3c07f8bfd42c..e072c3095915 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -35,7 +35,6 @@ 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_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 diff --git a/drivers/platform/x86/intel_rar_register.c b/drivers/platform/x86/intel_rar_register.c deleted file mode 100644 index c8a6aed45277..000000000000 --- a/drivers/platform/x86/intel_rar_register.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * rar_register.c - An Intel Restricted Access Region register driver - * - * Copyright(c) 2009 Intel Corporation. All rights reserved. - * - * 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. - * - * ------------------------------------------------------------------- - * 20091204 Mark Allyn - * Ossama Othman - * Cleanup per feedback from Alan Cox and Arjan Van De Ven - * - * 20090806 Ossama Othman - * Return zero high address if upper 22 bits is zero. - * Cleaned up checkpatch errors. - * Clarified that driver is dealing with bus addresses. - * - * 20090702 Ossama Othman - * Removed unnecessary include directives - * Cleaned up spinlocks. - * Cleaned up logging. - * Improved invalid parameter checks. - * Fixed and simplified RAR address retrieval and RAR locking - * code. - * - * 20090626 Mark Allyn - * Initial publish - */ - -#include -#include -#include -#include -#include -#include - -/* === Lincroft Message Bus Interface === */ -#define LNC_MCR_OFFSET 0xD0 /* Message Control Register */ -#define LNC_MDR_OFFSET 0xD4 /* Message Data Register */ - -/* Message Opcodes */ -#define LNC_MESSAGE_READ_OPCODE 0xD0 -#define LNC_MESSAGE_WRITE_OPCODE 0xE0 - -/* Message Write Byte Enables */ -#define LNC_MESSAGE_BYTE_WRITE_ENABLES 0xF - -/* B-unit Port */ -#define LNC_BUNIT_PORT 0x3 - -/* === Lincroft B-Unit Registers - Programmed by IA32 firmware === */ -#define LNC_BRAR0L 0x10 -#define LNC_BRAR0H 0x11 -#define LNC_BRAR1L 0x12 -#define LNC_BRAR1H 0x13 -/* Reserved for SeP */ -#define LNC_BRAR2L 0x14 -#define LNC_BRAR2H 0x15 - -/* Moorestown supports three restricted access regions. */ -#define MRST_NUM_RAR 3 - -/* RAR Bus Address Range */ -struct rar_addr { - dma_addr_t low; - dma_addr_t high; -}; - -/* - * We create one of these for each RAR - */ -struct client { - int (*callback)(unsigned long data); - unsigned long driver_priv; - bool busy; -}; - -static DEFINE_MUTEX(rar_mutex); -static DEFINE_MUTEX(lnc_reg_mutex); - -/* - * One per RAR device (currently only one device) - */ -struct rar_device { - struct rar_addr rar_addr[MRST_NUM_RAR]; - struct pci_dev *rar_dev; - bool registered; - bool allocated; - struct client client[MRST_NUM_RAR]; -}; - -/* Current platforms have only one rar_device for 3 rar regions */ -static struct rar_device my_rar_device; - -/* - * Abstract out multiple device support. Current platforms only - * have a single RAR device. - */ - -/** - * alloc_rar_device - return a new RAR structure - * - * Return a new (but not yet ready) RAR device object - */ -static struct rar_device *alloc_rar_device(void) -{ - if (my_rar_device.allocated) - return NULL; - my_rar_device.allocated = 1; - return &my_rar_device; -} - -/** - * free_rar_device - free a RAR object - * @rar: the RAR device being freed - * - * Release a RAR object and any attached resources - */ -static void free_rar_device(struct rar_device *rar) -{ - pci_dev_put(rar->rar_dev); - rar->allocated = 0; -} - -/** - * _rar_to_device - return the device handling this RAR - * @rar: RAR number - * @off: returned offset - * - * Internal helper for looking up RAR devices. This and alloc are the - * two functions that need touching to go to multiple RAR devices. - */ -static struct rar_device *_rar_to_device(int rar, int *off) -{ - if (rar >= 0 && rar < MRST_NUM_RAR) { - *off = rar; - return &my_rar_device; - } - return NULL; -} - -/** - * rar_to_device - return the device handling this RAR - * @rar: RAR number - * @off: returned offset - * - * Return the device this RAR maps to if one is present, otherwise - * returns NULL. Reports the offset relative to the base of this - * RAR device in off. - */ -static struct rar_device *rar_to_device(int rar, int *off) -{ - struct rar_device *rar_dev = _rar_to_device(rar, off); - if (rar_dev == NULL || !rar_dev->registered) - return NULL; - return rar_dev; -} - -/** - * rar_to_client - return the client handling this RAR - * @rar: RAR number - * - * Return the client this RAR maps to if a mapping is known, otherwise - * returns NULL. - */ -static struct client *rar_to_client(int rar) -{ - int idx; - struct rar_device *r = _rar_to_device(rar, &idx); - if (r != NULL) - return &r->client[idx]; - return NULL; -} - -/** - * rar_read_addr - retrieve a RAR mapping - * @pdev: PCI device for the RAR - * @offset: offset for message - * @addr: returned address - * - * Reads the address of a given RAR register. Returns 0 on success - * or an error code on failure. - */ -static int rar_read_addr(struct pci_dev *pdev, int offset, dma_addr_t *addr) -{ - /* - * ======== The Lincroft Message Bus Interface ======== - * Lincroft registers may be obtained via PCI from - * the host bridge using the Lincroft Message Bus - * Interface. That message bus interface is generally - * comprised of two registers: a control register (MCR, 0xDO) - * and a data register (MDR, 0xD4). - * - * The MCR (message control register) format is the following: - * 1. [31:24]: Opcode - * 2. [23:16]: Port - * 3. [15:8]: Register Offset - * 4. [7:4]: Byte Enables (use 0xF to set all of these bits - * to 1) - * 5. [3:0]: reserved - * - * Read (0xD0) and write (0xE0) opcodes are written to the - * control register when reading and writing to Lincroft - * registers, respectively. - * - * We're interested in registers found in the Lincroft - * B-unit. The B-unit port is 0x3. - * - * The six B-unit RAR register offsets we use are listed - * earlier in this file. - * - * Lastly writing to the MCR register requires the "Byte - * enables" bits to be set to 1. This may be achieved by - * writing 0xF at bit 4. - * - * The MDR (message data register) format is the following: - * 1. [31:0]: Read/Write Data - * - * Data being read from this register is only available after - * writing the appropriate control message to the MCR - * register. - * - * Data being written to this register must be written before - * writing the appropriate control message to the MCR - * register. - */ - - int result; - u32 addr32; - - /* Construct control message */ - u32 const message = - (LNC_MESSAGE_READ_OPCODE << 24) - | (LNC_BUNIT_PORT << 16) - | (offset << 8) - | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4); - - dev_dbg(&pdev->dev, "Offset for 'get' LNC MSG is %x\n", offset); - - /* - * We synchronize access to the Lincroft MCR and MDR registers - * until BOTH the command is issued through the MCR register - * and the corresponding data is read from the MDR register. - * Otherwise a race condition would exist between accesses to - * both registers. - */ - - mutex_lock(&lnc_reg_mutex); - - /* Send the control message */ - result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message); - if (!result) { - /* Read back the address as a 32bit value */ - result = pci_read_config_dword(pdev, LNC_MDR_OFFSET, &addr32); - *addr = (dma_addr_t)addr32; - } - mutex_unlock(&lnc_reg_mutex); - return result; -} - -/** - * rar_set_addr - Set a RAR mapping - * @pdev: PCI device for the RAR - * @offset: offset for message - * @addr: address to set - * - * Sets the address of a given RAR register. Returns 0 on success - * or an error code on failure. - */ -static int rar_set_addr(struct pci_dev *pdev, - int offset, - dma_addr_t addr) -{ - /* - * Data being written to this register must be written before - * writing the appropriate control message to the MCR - * register. - * See rar_get_addrs() for a description of the - * message bus interface being used here. - */ - - int result; - - /* Construct control message */ - u32 const message = (LNC_MESSAGE_WRITE_OPCODE << 24) - | (LNC_BUNIT_PORT << 16) - | (offset << 8) - | (LNC_MESSAGE_BYTE_WRITE_ENABLES << 4); - - /* - * We synchronize access to the Lincroft MCR and MDR registers - * until BOTH the command is issued through the MCR register - * and the corresponding data is read from the MDR register. - * Otherwise a race condition would exist between accesses to - * both registers. - */ - - mutex_lock(&lnc_reg_mutex); - - /* Send the control message */ - result = pci_write_config_dword(pdev, LNC_MDR_OFFSET, addr); - if (!result) - /* And address */ - result = pci_write_config_dword(pdev, LNC_MCR_OFFSET, message); - - mutex_unlock(&lnc_reg_mutex); - return result; -} - -/* - * rar_init_params - Initialize RAR parameters - * @rar: RAR device to initialise - * - * Initialize RAR parameters, such as bus addresses, etc. Returns 0 - * on success, or an error code on failure. - */ -static int init_rar_params(struct rar_device *rar) -{ - struct pci_dev *pdev = rar->rar_dev; - unsigned int i; - int result = 0; - int offset = 0x10; /* RAR 0 to 2 in order low/high/low/high/... */ - - /* Retrieve RAR start and end bus addresses. - * Access the RAR registers through the Lincroft Message Bus - * Interface on PCI device: 00:00.0 Host bridge. - */ - - for (i = 0; i < MRST_NUM_RAR; ++i) { - struct rar_addr *addr = &rar->rar_addr[i]; - - result = rar_read_addr(pdev, offset++, &addr->low); - if (result != 0) - return result; - - result = rar_read_addr(pdev, offset++, &addr->high); - if (result != 0) - return result; - - - /* - * Only the upper 22 bits of the RAR addresses are - * stored in their corresponding RAR registers so we - * must set the lower 10 bits accordingly. - - * The low address has its lower 10 bits cleared, and - * the high address has all its lower 10 bits set, - * e.g.: - * low = 0x2ffffc00 - */ - - addr->low &= (dma_addr_t)0xfffffc00u; - - /* - * Set bits 9:0 on uppser address if bits 31:10 are non - * zero; otherwize clear all bits - */ - - if ((addr->high & 0xfffffc00u) == 0) - addr->high = 0; - else - addr->high |= 0x3ffu; - } - /* Done accessing the device. */ - - if (result == 0) { - for (i = 0; i != MRST_NUM_RAR; ++i) { - /* - * "BRAR" refers to the RAR registers in the - * Lincroft B-unit. - */ - dev_info(&pdev->dev, "BRAR[%u] bus address range = " - "[%lx, %lx]\n", i, - (unsigned long)rar->rar_addr[i].low, - (unsigned long)rar->rar_addr[i].high); - } - } - return result; -} - -/** - * rar_get_address - get the bus address in a RAR - * @start: return value of start address of block - * @end: return value of end address of block - * - * The rar_get_address function is used by other device drivers - * to obtain RAR address information on a RAR. It takes three - * parameters: - * - * The function returns a 0 upon success or an error if there is no RAR - * facility on this system. - */ -int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end) -{ - int idx; - struct rar_device *rar = rar_to_device(rar_index, &idx); - - if (rar == NULL) { - WARN_ON(1); - return -ENODEV; - } - - *start = rar->rar_addr[idx].low; - *end = rar->rar_addr[idx].high; - return 0; -} -EXPORT_SYMBOL(rar_get_address); - -/** - * rar_lock - lock a RAR register - * @rar_index: RAR to lock (0-2) - * - * The rar_lock function is ued by other device drivers to lock an RAR. - * once a RAR is locked, it stays locked until the next system reboot. - * - * The function returns a 0 upon success or an error if there is no RAR - * facility on this system, or the locking fails - */ -int rar_lock(int rar_index) -{ - struct rar_device *rar; - int result; - int idx; - dma_addr_t low, high; - - rar = rar_to_device(rar_index, &idx); - - if (rar == NULL) { - WARN_ON(1); - return -EINVAL; - } - - low = rar->rar_addr[idx].low & 0xfffffc00u; - high = rar->rar_addr[idx].high & 0xfffffc00u; - - /* - * Only allow I/O from the graphics and Langwell; - * not from the x86 processor - */ - - if (rar_index == RAR_TYPE_VIDEO) { - low |= 0x00000009; - high |= 0x00000015; - } else if (rar_index == RAR_TYPE_AUDIO) { - /* Only allow I/O from Langwell; nothing from x86 */ - low |= 0x00000008; - high |= 0x00000018; - } else - /* Read-only from all agents */ - high |= 0x00000018; - - /* - * Now program the register using the Lincroft message - * bus interface. - */ - result = rar_set_addr(rar->rar_dev, - 2 * idx, low); - - if (result == 0) - result = rar_set_addr(rar->rar_dev, - 2 * idx + 1, high); - - return result; -} -EXPORT_SYMBOL(rar_lock); - -/** - * register_rar - register a RAR handler - * @num: RAR we wish to register for - * @callback: function to call when RAR support is available - * @data: data to pass to this function - * - * 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 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 - * a driver that do require a valid RAR address. One of those - * steps would be to call rar_get_address() - * - * This function return 0 on success or an error code on failure. - */ -int register_rar(int num, int (*callback)(unsigned long data), - unsigned long data) -{ - /* For now we hardcode a single RAR device */ - struct rar_device *rar; - struct client *c; - int idx; - int retval = 0; - - mutex_lock(&rar_mutex); - - /* Do we have a client mapping for this RAR number ? */ - c = rar_to_client(num); - if (c == NULL) { - retval = -ERANGE; - goto done; - } - /* Is it claimed ? */ - if (c->busy) { - retval = -EBUSY; - goto done; - } - c->busy = 1; - - /* See if we have a handler for this RAR yet, if we do then fire it */ - rar = rar_to_device(num, &idx); - - if (rar) { - /* - * if the driver already registered, then we can simply - * call the callback right now - */ - (*callback)(data); - goto done; - } - - /* Arrange to be called back when the hardware is found */ - c->callback = callback; - c->driver_priv = data; -done: - mutex_unlock(&rar_mutex); - return retval; -} -EXPORT_SYMBOL(register_rar); - -/** - * unregister_rar - release a RAR allocation - * @num: RAR number - * - * Releases a RAR allocation, or pending allocation. If a callback is - * pending then this function will either complete before the unregister - * returns or not at all. - */ - -void unregister_rar(int num) -{ - struct client *c; - - mutex_lock(&rar_mutex); - c = rar_to_client(num); - if (c == NULL || !c->busy) - WARN_ON(1); - else - c->busy = 0; - mutex_unlock(&rar_mutex); -} -EXPORT_SYMBOL(unregister_rar); - -/** - * rar_callback - Process callbacks - * @rar: new RAR device - * - * Process the callbacks for a newly found RAR device. - */ - -static void rar_callback(struct rar_device *rar) -{ - struct client *c = &rar->client[0]; - int i; - - mutex_lock(&rar_mutex); - - rar->registered = 1; /* Ensure no more callbacks queue */ - - for (i = 0; i < MRST_NUM_RAR; i++) { - if (c->callback && c->busy) { - c->callback(c->driver_priv); - c->callback = NULL; - } - c++; - } - mutex_unlock(&rar_mutex); -} - -/** - * rar_probe - PCI probe callback - * @dev: PCI device - * @id: matching entry in the match table - * - * A RAR device has been discovered. Initialise it and if successful - * process any pending callbacks that can now be completed. - */ -static int rar_probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - int error; - struct rar_device *rar; - - dev_dbg(&dev->dev, "PCI probe starting\n"); - - rar = alloc_rar_device(); - if (rar == NULL) - return -EBUSY; - - /* Enable the device */ - error = pci_enable_device(dev); - if (error) { - dev_err(&dev->dev, - "Error enabling RAR register PCI device\n"); - goto end_function; - } - - /* Fill in the rar_device structure */ - rar->rar_dev = pci_dev_get(dev); - pci_set_drvdata(dev, rar); - - /* - * Initialize the RAR parameters, which have to be retrieved - * via the message bus interface. - */ - error = init_rar_params(rar); - if (error) { - pci_disable_device(dev); - dev_err(&dev->dev, "Error retrieving RAR addresses\n"); - goto end_function; - } - /* now call anyone who has registered (using callbacks) */ - rar_callback(rar); - return 0; -end_function: - free_rar_device(rar); - return error; -} - -static DEFINE_PCI_DEVICE_TABLE(rar_pci_id_tbl) = { - { PCI_VDEVICE(INTEL, 0x4110) }, - { 0 } -}; - -MODULE_DEVICE_TABLE(pci, rar_pci_id_tbl); - -/* field for registering driver to PCI device */ -static struct pci_driver rar_pci_driver = { - .name = "rar_register_driver", - .id_table = rar_pci_id_tbl, - .probe = rar_probe, - /* Cannot be unplugged - no remove */ -}; - -static int __init rar_init_handler(void) -{ - return pci_register_driver(&rar_pci_driver); -} - -static void __exit rar_exit_handler(void) -{ - pci_unregister_driver(&rar_pci_driver); -} - -module_init(rar_init_handler); -module_exit(rar_exit_handler); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Intel Restricted Access Region Register Driver"); diff --git a/include/linux/rar_register.h b/include/linux/rar_register.h deleted file mode 100644 index 5c6118189363..000000000000 --- a/include/linux/rar_register.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2010 Intel Corporation. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU General - * Public License as published by the Free Software Foundation. - * - * 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. - * The full GNU General Public License is included in this - * distribution in the file called COPYING. - */ - - -#ifndef _RAR_REGISTER_H -#define _RAR_REGISTER_H - -#include - -/* following are used both in drivers as well as user space apps */ - -#define RAR_TYPE_VIDEO 0 -#define RAR_TYPE_AUDIO 1 -#define RAR_TYPE_IMAGE 2 -#define RAR_TYPE_DATA 3 - -#ifdef __KERNEL__ - -struct rar_device; - -#if defined(CONFIG_RAR_REGISTER) -int register_rar(int num, - int (*callback)(unsigned long data), unsigned long data); -void unregister_rar(int num); -int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end); -int rar_lock(int rar_index); -#else -extern void unregister_rar(int num) { } -extern int rar_lock(int rar_index) { return -EIO; } - -extern inline int register_rar(int num, - int (*callback)(unsigned long data), unsigned long data) -{ - return -ENODEV; -} - -extern int rar_get_address(int rar_index, dma_addr_t *start, dma_addr_t *end) -{ - return -ENODEV; -} -#endif /* RAR_REGISTER */ - -#endif /* __KERNEL__ */ -#endif /* _RAR_REGISTER_H */ -- cgit v1.2.3 From 7714567c87f43862d3d7049ed2907567be3e50c3 Mon Sep 17 00:00:00 2001 From: Michael Demeter Date: Thu, 26 Jan 2012 17:40:27 +0000 Subject: intel_mid_powerbtn: use MSIC read/write instead of ipc_scu In the 2.6.36 kernel we did not have the MSIC driver. Changed all ipc_scu_reads/writes to use the MSIC driver and defines. Added a fix from the 2.6.36 kernel where the SCU FW could send a power button interrupt to the IA32 FW and the kernel was not running yet. This resulted in the interrupt not getting cleared and the power button was ignored. this fix just clears the interrupt on start-up. Signed-off-by: Michael Demeter Reviewed-by: Mika Westerberg Revert style-only changes. Remove unused variable. Fix comment style.] Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Matthew Garrett --- drivers/platform/x86/intel_mid_powerbtn.c | 32 +++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index ff3de343b64c..0a3594c7e912 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -23,21 +23,27 @@ #include #include #include - -#include +#include #define DRIVER_NAME "msic_power_btn" -#define MSIC_PB_STATUS 0x3f #define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */ +/* + * MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask + * power button interrupt + */ +#define MSIC_PWRBTNM (1 << 0) + static irqreturn_t mfld_pb_isr(int irq, void *dev_id) { struct input_dev *input = dev_id; int ret; u8 pbstat; - ret = intel_scu_ipc_ioread8(MSIC_PB_STATUS, &pbstat); + ret = intel_msic_reg_read(INTEL_MSIC_PBSTATUS, &pbstat); + dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat); + if (ret < 0) { dev_err(input->dev.parent, "Read error %d while reading" " MSIC_PB_STATUS\n", ret); @@ -88,6 +94,24 @@ static int __devinit mfld_pb_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, input); + + /* + * SCU firmware might send power button interrupts to IA core before + * kernel boots and doesn't get EOI from IA core. The first bit of + * MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new + * power interrupt to Android kernel. Unmask the bit when probing + * power button in kernel. + * There is a very narrow race between irq handler and power button + * initialization. The race happens rarely. So we needn't worry + * about it. + */ + error = intel_msic_reg_update(INTEL_MSIC_IRQLVL1MSK, 0, MSIC_PWRBTNM); + if (error) { + dev_err(&pdev->dev, "Unable to clear power button interrupt, " + "error: %d\n", error); + goto err_free_irq; + } + return 0; err_free_irq: -- cgit v1.2.3 From 3e2abc5a35d25442821e1733687b7abbc83b5072 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Wed, 18 Jan 2012 13:44:08 -0600 Subject: ACPI: EC: Add ec_get_handle() toshiba_acpi needs to execute an AML method within the EC namespace to make hotkeys work on some platforms. Provide an interface to allow it to easily get a handle to the EC namespace for this purpose. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/acpi/ec.c | 10 ++++++++++ include/linux/acpi.h | 1 + 2 files changed, 11 insertions(+) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index b19a18dd994f..e37615f310d7 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -445,6 +445,16 @@ int ec_transaction(u8 command, EXPORT_SYMBOL(ec_transaction); +/* Get the handle to the EC device */ +acpi_handle ec_get_handle(void) +{ + if (!first_ec) + return NULL; + return first_ec->handle; +} + +EXPORT_SYMBOL(ec_get_handle); + void acpi_ec_block_transactions(void) { struct acpi_ec *ec = first_ec; diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 3f968665899b..f53fea61f40a 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -151,6 +151,7 @@ extern int ec_write(u8 addr, u8 val); extern int ec_transaction(u8 command, const u8 *wdata, unsigned wdata_len, u8 *rdata, unsigned rdata_len); +extern acpi_handle ec_get_handle(void); #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) -- cgit v1.2.3 From 29cd293f9f8cd76444657622980010b9364b1de6 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Wed, 18 Jan 2012 13:44:09 -0600 Subject: toshiba_acpi: Support alternate hotkey interfaces There are two types of problems that prevent hotkeys from working on many of the machines supported by toshiba_acpi. The first of these is the lack of a functioning SCI for hotkey events. For these machines it is possible to filter the Fn keypresses from the keyboard and generate a notification by executing the ACPI NTFY method. The second problem is a lack of support for HCI_SYSTEM_EVENT, which is used for reading the hotkey scancodes. On these machines the scancodes can be read by executing the ACPI NTFY method. This patch fixes both problems by installing an i8042 filter when the NTFY method is present to generate notifications and by detecting which of INFO or HCI_SYSTEM_EVENT is supported for reading scancodes. If neither method of reading scancodes is supported, the hotkey input device is not registered. Signed-off-by: Azael Avalos Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 231 ++++++++++++++++++++++++++++++------ 1 file changed, 195 insertions(+), 36 deletions(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index eab5e11fb41f..42d73b0f530a 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include @@ -61,6 +63,9 @@ MODULE_AUTHOR("John Belmonte"); MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); MODULE_LICENSE("GPL"); +/* Scan code for Fn key on TOS1900 models */ +#define TOS1900_FN_SCAN 0x6e + /* Toshiba ACPI method paths */ #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" @@ -95,6 +100,8 @@ MODULE_LICENSE("GPL"); #define HCI_WIRELESS 0x0056 /* field definitions */ +#define HCI_HOTKEY_DISABLE 0x0b +#define HCI_HOTKEY_ENABLE 0x09 #define HCI_LCD_BRIGHTNESS_BITS 3 #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) @@ -111,6 +118,7 @@ struct toshiba_acpi_dev { const char *method_hci; struct rfkill *bt_rfk; struct input_dev *hotkey_dev; + struct work_struct hotkey_work; struct backlight_device *backlight_dev; struct led_classdev led_dev; @@ -122,10 +130,14 @@ struct toshiba_acpi_dev { unsigned int video_supported:1; unsigned int fan_supported:1; unsigned int system_event_supported:1; + unsigned int ntfy_supported:1; + unsigned int info_supported:1; struct mutex mutex; }; +static struct toshiba_acpi_dev *toshiba_acpi; + static const struct acpi_device_id toshiba_device_ids[] = { {"TOS6200", 0}, {"TOS6208", 0}, @@ -847,10 +859,78 @@ static const struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; +static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, + struct serio *port) +{ + if (str & 0x20) + return false; + + if (unlikely(data == 0xe0)) + return false; + + if ((data & 0x7f) == TOS1900_FN_SCAN) { + schedule_work(&toshiba_acpi->hotkey_work); + return true; + } + + return false; +} + +static void toshiba_acpi_hotkey_work(struct work_struct *work) +{ + acpi_handle ec_handle = ec_get_handle(); + acpi_status status; + + if (!ec_handle) + return; + + status = acpi_evaluate_object(ec_handle, "NTFY", NULL, NULL); + if (ACPI_FAILURE(status)) + pr_err("ACPI NTFY method execution failed\n"); +} + +/* + * Returns hotkey scancode, or < 0 on failure. + */ +static int toshiba_acpi_query_hotkey(struct toshiba_acpi_dev *dev) +{ + struct acpi_buffer buf; + union acpi_object out_obj; + acpi_status status; + + buf.pointer = &out_obj; + buf.length = sizeof(out_obj); + + status = acpi_evaluate_object(dev->acpi_dev->handle, "INFO", + NULL, &buf); + if (ACPI_FAILURE(status) || out_obj.type != ACPI_TYPE_INTEGER) { + pr_err("ACPI INFO method execution failed\n"); + return -EIO; + } + + return out_obj.integer.value; +} + +static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev, + int scancode) +{ + if (scancode == 0x100) + return; + + /* act on key press; ignore key release */ + if (scancode & 0x80) + return; + + if (!sparse_keymap_report_event(dev->hotkey_dev, scancode, 1, true)) + pr_info("Unknown key %x\n", scancode); +} + static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) { acpi_status status; + acpi_handle ec_handle, handle; int error; + u32 hci_result; dev->hotkey_dev = input_allocate_device(); if (!dev->hotkey_dev) { @@ -866,21 +946,67 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) if (error) goto err_free_dev; + /* + * For some machines the SCI responsible for providing hotkey + * notification doesn't fire. We can trigger the notification + * whenever the Fn key is pressed using the NTFY method, if + * supported, so if it's present set up an i8042 key filter + * for this purpose. + */ + status = AE_ERROR; + ec_handle = ec_get_handle(); + if (ec_handle) + status = acpi_get_handle(ec_handle, "NTFY", &handle); + + if (ACPI_SUCCESS(status)) { + INIT_WORK(&dev->hotkey_work, toshiba_acpi_hotkey_work); + + error = i8042_install_filter(toshiba_acpi_i8042_filter); + if (error) { + pr_err("Error installing key filter\n"); + goto err_free_keymap; + } + + dev->ntfy_supported = 1; + } + + /* + * Determine hotkey query interface. Prefer using the INFO + * method when it is available. + */ + status = acpi_get_handle(dev->acpi_dev->handle, "INFO", &handle); + if (ACPI_SUCCESS(status)) { + dev->info_supported = 1; + } else { + hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); + if (hci_result == HCI_SUCCESS) + dev->system_event_supported = 1; + } + + if (!dev->info_supported && !dev->system_event_supported) { + pr_warn("No hotkey query interface found\n"); + goto err_remove_filter; + } + status = acpi_evaluate_object(dev->acpi_dev->handle, "ENAB", NULL, NULL); if (ACPI_FAILURE(status)) { pr_info("Unable to enable hotkeys\n"); error = -ENODEV; - goto err_free_keymap; + goto err_remove_filter; } error = input_register_device(dev->hotkey_dev); if (error) { pr_info("Unable to register input device\n"); - goto err_free_keymap; + goto err_remove_filter; } + hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &hci_result); return 0; + err_remove_filter: + if (dev->ntfy_supported) + i8042_remove_filter(toshiba_acpi_i8042_filter); err_free_keymap: sparse_keymap_free(dev->hotkey_dev); err_free_dev: @@ -895,6 +1021,11 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) remove_toshiba_proc_entries(dev); + if (dev->ntfy_supported) { + i8042_remove_filter(toshiba_acpi_i8042_filter); + cancel_work_sync(&dev->hotkey_work); + } + if (dev->hotkey_dev) { input_unregister_device(dev->hotkey_dev); sparse_keymap_free(dev->hotkey_dev); @@ -911,6 +1042,9 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) if (dev->illumination_supported) led_classdev_unregister(&dev->led_dev); + if (toshiba_acpi) + toshiba_acpi = NULL; + kfree(dev); return 0; @@ -936,12 +1070,14 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) { struct toshiba_acpi_dev *dev; const char *hci_method; - u32 hci_result; u32 dummy; bool bt_present; int ret = 0; struct backlight_properties props; + if (toshiba_acpi) + return -EBUSY; + pr_info("Toshiba Laptop ACPI Extras version %s\n", TOSHIBA_ACPI_VERSION); @@ -963,11 +1099,6 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) mutex_init(&dev->mutex); - /* enable event fifo */ - hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); - if (hci_result == HCI_SUCCESS) - dev->system_event_supported = 1; - props.type = BACKLIGHT_PLATFORM; props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; dev->backlight_dev = backlight_device_register("toshiba", @@ -1024,6 +1155,8 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) create_toshiba_proc_entries(dev); + toshiba_acpi = dev; + return 0; error: @@ -1036,40 +1169,64 @@ static void toshiba_acpi_notify(struct acpi_device *acpi_dev, u32 event) struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); u32 hci_result, value; int retries = 3; + int scancode; - if (!dev->system_event_supported || event != 0x80) + if (event != 0x80) return; - do { - hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); - switch (hci_result) { - case HCI_SUCCESS: - if (value == 0x100) - continue; - /* act on key press; ignore key release */ - if (value & 0x80) - continue; - - if (!sparse_keymap_report_event(dev->hotkey_dev, - value, 1, true)) { - pr_info("Unknown key %x\n", - value); + if (dev->info_supported) { + scancode = toshiba_acpi_query_hotkey(dev); + if (scancode < 0) + pr_err("Failed to query hotkey event\n"); + else if (scancode != 0) + toshiba_acpi_report_hotkey(dev, scancode); + } else if (dev->system_event_supported) { + do { + hci_read1(dev, HCI_SYSTEM_EVENT, &value, &hci_result); + switch (hci_result) { + case HCI_SUCCESS: + toshiba_acpi_report_hotkey(dev, (int)value); + break; + case HCI_NOT_SUPPORTED: + /* + * This is a workaround for an unresolved + * issue on some machines where system events + * sporadically become disabled. + */ + hci_write1(dev, HCI_SYSTEM_EVENT, 1, + &hci_result); + pr_notice("Re-enabled hotkeys\n"); + /* fall through */ + default: + retries--; + break; } - break; - case HCI_NOT_SUPPORTED: - /* This is a workaround for an unresolved issue on - * some machines where system events sporadically - * become disabled. */ - hci_write1(dev, HCI_SYSTEM_EVENT, 1, &hci_result); - pr_notice("Re-enabled hotkeys\n"); - /* fall through */ - default: - retries--; - break; - } - } while (retries && hci_result != HCI_EMPTY); + } while (retries && hci_result != HCI_EMPTY); + } } +static int toshiba_acpi_suspend(struct acpi_device *acpi_dev, + pm_message_t state) +{ + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + u32 result; + + if (dev->hotkey_dev) + hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_DISABLE, &result); + + return 0; +} + +static int toshiba_acpi_resume(struct acpi_device *acpi_dev) +{ + struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); + u32 result; + + if (dev->hotkey_dev) + hci_write1(dev, HCI_HOTKEY_EVENT, HCI_HOTKEY_ENABLE, &result); + + return 0; +} static struct acpi_driver toshiba_acpi_driver = { .name = "Toshiba ACPI driver", @@ -1080,6 +1237,8 @@ static struct acpi_driver toshiba_acpi_driver = { .add = toshiba_acpi_add, .remove = toshiba_acpi_remove, .notify = toshiba_acpi_notify, + .suspend = toshiba_acpi_suspend, + .resume = toshiba_acpi_resume, }, }; -- cgit v1.2.3 From af502837a08c8ca3495d3940d4c4eb4e19a3beaa Mon Sep 17 00:00:00 2001 From: Azael Avalos Date: Wed, 18 Jan 2012 13:44:10 -0600 Subject: toshiba_acpi: Support additional hotkey scancodes These scancodes are used by many of the models now supported with the addition of TOS1900 device support. Signed-off-by: Azael Avalos Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/toshiba_acpi.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 42d73b0f530a..e3e1fa6db004 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -150,6 +150,8 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { { KE_KEY, 0x101, { KEY_MUTE } }, { KE_KEY, 0x102, { KEY_ZOOMOUT } }, { KE_KEY, 0x103, { KEY_ZOOMIN } }, + { KE_KEY, 0x12c, { KEY_KBDILLUMTOGGLE } }, + { KE_KEY, 0x139, { KEY_ZOOMRESET } }, { KE_KEY, 0x13b, { KEY_COFFEE } }, { KE_KEY, 0x13c, { KEY_BATTERY } }, { KE_KEY, 0x13d, { KEY_SLEEP } }, @@ -158,7 +160,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { { KE_KEY, 0x140, { KEY_BRIGHTNESSDOWN } }, { KE_KEY, 0x141, { KEY_BRIGHTNESSUP } }, { KE_KEY, 0x142, { KEY_WLAN } }, - { KE_KEY, 0x143, { KEY_PROG1 } }, + { KE_KEY, 0x143, { KEY_TOUCHPAD_TOGGLE } }, { KE_KEY, 0x17f, { KEY_FN } }, { KE_KEY, 0xb05, { KEY_PROG2 } }, { KE_KEY, 0xb06, { KEY_WWW } }, @@ -168,6 +170,7 @@ static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { { KE_KEY, 0xb32, { KEY_NEXTSONG } }, { KE_KEY, 0xb33, { KEY_PLAYPAUSE } }, { KE_KEY, 0xb5a, { KEY_MEDIA } }, + { KE_IGNORE, 0x1430, { KEY_RESERVED } }, { KE_END, 0 }, }; -- cgit v1.2.3 From f11f999e989061952f1a27bd0c49645a46d13173 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Wed, 18 Jan 2012 13:44:11 -0600 Subject: toshiba_acpi: Refuse to load on machines with buggy INFO implementations Several Satellite models have a buggy implementation of the INFO method that causes ACPI exceptions when executed: ACPI Error: Result stack is empty! State=ffff88012d70f800 (20110413/dswstate-98) ACPI Exception: AE_AML_NO_RETURN_VALUE, Missing or null operand (20110413/dsutils-646) ACPI Exception: AE_AML_NO_RETURN_VALUE, While creating Arg 0 (20110413/dsutils-763) ACPI Error: Method parse/execution failed [\_SB_.VALZ.GETE] (Node ffff880131175eb0), AE_AML_NO_RETURN_VALUE (20110413/psparse-536) ACPI Error: Method parse/execution failed [\_SB_.VALZ.INFO] (Node ffff880131175ed8), AE_AML_NO_RETURN_VALUE (20110413/psparse-536) toshiba_acpi: ACPI INFO method execution failed toshiba_acpi: Failed to query hotkey event All known machines with this implementation also have a WMI interface with event GUID 59142400-C6A3-40FA-BADB-8A2652834100 which is not seen on any other models. Refuse to load toshiba_acpi on machines with this guid. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 1 + drivers/platform/x86/Makefile | 4 ++++ drivers/platform/x86/toshiba_acpi.c | 10 ++++++++++ 3 files changed, 15 insertions(+) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 2efd7af2a21e..ce10f0313961 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -569,6 +569,7 @@ config TOPSTAR_LAPTOP config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on ACPI + depends on ACPI_WMI select LEDS_CLASS select NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e072c3095915..dcfee6b2606d 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -30,7 +30,11 @@ obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o obj-$(CONFIG_ACPI_WMI) += wmi.o obj-$(CONFIG_MSI_WMI) += msi-wmi.o obj-$(CONFIG_TOPSTAR_LAPTOP) += topstar-laptop.o + +# toshiba_acpi must link after wmi to ensure that wmi devices are found +# before toshiba_acpi initializes 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 diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index e3e1fa6db004..ee79ce64d9df 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -63,6 +63,8 @@ MODULE_AUTHOR("John Belmonte"); MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); MODULE_LICENSE("GPL"); +#define TOSHIBA_WMI_EVENT_GUID "59142400-C6A3-40FA-BADB-8A2652834100" + /* Scan code for Fn key on TOS1900 models */ #define TOS1900_FN_SCAN 0x6e @@ -1249,6 +1251,14 @@ static int __init toshiba_acpi_init(void) { int ret; + /* + * Machines with this WMI guid aren't supported due to bugs in + * their AML. This check relies on wmi initializing before + * toshiba_acpi to guarantee guids have been identified. + */ + if (wmi_has_guid(TOSHIBA_WMI_EVENT_GUID)) + return -ENODEV; + toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); if (!toshiba_proc_dir) { pr_err("Unable to create proc dir " PROC_TOSHIBA "\n"); -- cgit v1.2.3 From 83e72dd97a25a831ff270ce4437416943a1e4b36 Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Fri, 16 Mar 2012 14:41:21 -0500 Subject: apple_bl: Add register/unregister functions Add functions to allow other modules to enable or disable apple_bl. This will be used by the gmux driver to disable apple_bl when the gmux is present, as it is a better and more reliable option for brightness control. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett --- drivers/video/backlight/apple_bl.c | 23 +++++++++++++++++++++-- include/linux/apple_bl.h | 26 ++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 include/linux/apple_bl.h diff --git a/drivers/video/backlight/apple_bl.c b/drivers/video/backlight/apple_bl.c index be98d152b7fd..a523b255e124 100644 --- a/drivers/video/backlight/apple_bl.c +++ b/drivers/video/backlight/apple_bl.c @@ -24,6 +24,7 @@ #include #include #include +#include static struct backlight_device *apple_backlight_device; @@ -221,14 +222,32 @@ static struct acpi_driver apple_bl_driver = { }, }; +static atomic_t apple_bl_registered = ATOMIC_INIT(0); + +int apple_bl_register(void) +{ + if (atomic_xchg(&apple_bl_registered, 1) == 0) + return acpi_bus_register_driver(&apple_bl_driver); + + return 0; +} +EXPORT_SYMBOL_GPL(apple_bl_register); + +void apple_bl_unregister(void) +{ + if (atomic_xchg(&apple_bl_registered, 0) == 1) + acpi_bus_unregister_driver(&apple_bl_driver); +} +EXPORT_SYMBOL_GPL(apple_bl_unregister); + static int __init apple_bl_init(void) { - return acpi_bus_register_driver(&apple_bl_driver); + return apple_bl_register(); } static void __exit apple_bl_exit(void) { - acpi_bus_unregister_driver(&apple_bl_driver); + apple_bl_unregister(); } module_init(apple_bl_init); diff --git a/include/linux/apple_bl.h b/include/linux/apple_bl.h new file mode 100644 index 000000000000..47bedc0eee69 --- /dev/null +++ b/include/linux/apple_bl.h @@ -0,0 +1,26 @@ +/* + * apple_bl exported symbols + */ + +#ifndef _LINUX_APPLE_BL_H +#define _LINUX_APPLE_BL_H + +#ifdef CONFIG_BACKLIGHT_APPLE + +extern int apple_bl_register(void); +extern void apple_bl_unregister(void); + +#else /* !CONFIG_BACKLIGHT_APPLE */ + +static inline int apple_bl_register(void) +{ + return 0; +} + +static inline void apple_bl_unregister(void) +{ +} + +#endif /* !CONFIG_BACKLIGHT_APPLE */ + +#endif /* _LINUX_APPLE_BL_H */ -- cgit v1.2.3 From 917ee75a59160fe3518c1672feb4562f11a18fbc Mon Sep 17 00:00:00 2001 From: Seth Forshee Date: Fri, 16 Mar 2012 14:41:22 -0500 Subject: platform/x86: Add driver for Apple gmux device Apple laptops with hybrid graphics have a device named gmux that controls the muxing of the LVDS panel between the GPUs as well as screen brightness. This driver adds support for the gmux device. Only backlight control is supported initially. Signed-off-by: Seth Forshee Signed-off-by: Matthew Garrett Tested-by: Grant Likely --- drivers/platform/x86/Kconfig | 10 ++ drivers/platform/x86/Makefile | 1 + drivers/platform/x86/apple-gmux.c | 244 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 drivers/platform/x86/apple-gmux.c diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index ce10f0313961..c5b4bfed7bb4 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -752,4 +752,14 @@ config SAMSUNG_Q10 This driver provides support for backlight control on Samsung Q10 and related laptops, including Dell Latitude X200. +config APPLE_GMUX + tristate "Apple Gmux Driver" + depends on PNP + select BACKLIGHT_CLASS_DEVICE + ---help--- + This driver provides support for the gmux device found on many + Apple laptops, which controls the display mux for the hybrid + graphics as well as the backlight. Currently only backlight + control is supported by the driver. + endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index dcfee6b2606d..bf7e4f935b17 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -49,3 +49,4 @@ obj-$(CONFIG_MXM_WMI) += mxm-wmi.o obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o obj-$(CONFIG_INTEL_OAKTRAIL) += intel_oaktrail.o obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o +obj-$(CONFIG_APPLE_GMUX) += apple-gmux.o diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c new file mode 100644 index 000000000000..8a582bdfdc76 --- /dev/null +++ b/drivers/platform/x86/apple-gmux.c @@ -0,0 +1,244 @@ +/* + * Gmux driver for Apple laptops + * + * Copyright (C) Canonical Ltd. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct apple_gmux_data { + unsigned long iostart; + unsigned long iolen; + + struct backlight_device *bdev; +}; + +/* + * gmux port offsets. Many of these are not yet used, but may be in the + * future, and it's useful to have them documented here anyhow. + */ +#define GMUX_PORT_VERSION_MAJOR 0x04 +#define GMUX_PORT_VERSION_MINOR 0x05 +#define GMUX_PORT_VERSION_RELEASE 0x06 +#define GMUX_PORT_SWITCH_DISPLAY 0x10 +#define GMUX_PORT_SWITCH_GET_DISPLAY 0x11 +#define GMUX_PORT_INTERRUPT_ENABLE 0x14 +#define GMUX_PORT_INTERRUPT_STATUS 0x16 +#define GMUX_PORT_SWITCH_DDC 0x28 +#define GMUX_PORT_SWITCH_EXTERNAL 0x40 +#define GMUX_PORT_SWITCH_GET_EXTERNAL 0x41 +#define GMUX_PORT_DISCRETE_POWER 0x50 +#define GMUX_PORT_MAX_BRIGHTNESS 0x70 +#define GMUX_PORT_BRIGHTNESS 0x74 + +#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4) + +#define GMUX_INTERRUPT_ENABLE 0xff +#define GMUX_INTERRUPT_DISABLE 0x00 + +#define GMUX_INTERRUPT_STATUS_ACTIVE 0 +#define GMUX_INTERRUPT_STATUS_DISPLAY (1 << 0) +#define GMUX_INTERRUPT_STATUS_POWER (1 << 2) +#define GMUX_INTERRUPT_STATUS_HOTPLUG (1 << 3) + +#define GMUX_BRIGHTNESS_MASK 0x00ffffff +#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK + +static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port) +{ + return inb(gmux_data->iostart + port); +} + +static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port, + u8 val) +{ + outb(val, gmux_data->iostart + port); +} + +static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port) +{ + return inl(gmux_data->iostart + port); +} + +static int gmux_get_brightness(struct backlight_device *bd) +{ + struct apple_gmux_data *gmux_data = bl_get_data(bd); + return gmux_read32(gmux_data, GMUX_PORT_BRIGHTNESS) & + GMUX_BRIGHTNESS_MASK; +} + +static int gmux_update_status(struct backlight_device *bd) +{ + struct apple_gmux_data *gmux_data = bl_get_data(bd); + u32 brightness = bd->props.brightness; + + /* + * Older gmux versions require writing out lower bytes first then + * setting the upper byte to 0 to flush the values. Newer versions + * accept a single u32 write, but the old method also works, so we + * just use the old method for all gmux versions. + */ + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness); + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8); + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16); + gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0); + + return 0; +} + +static const struct backlight_ops gmux_bl_ops = { + .get_brightness = gmux_get_brightness, + .update_status = gmux_update_status, +}; + +static int __devinit gmux_probe(struct pnp_dev *pnp, + const struct pnp_device_id *id) +{ + struct apple_gmux_data *gmux_data; + struct resource *res; + struct backlight_properties props; + struct backlight_device *bdev; + u8 ver_major, ver_minor, ver_release; + int ret = -ENXIO; + + gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL); + if (!gmux_data) + return -ENOMEM; + pnp_set_drvdata(pnp, gmux_data); + + res = pnp_get_resource(pnp, IORESOURCE_IO, 0); + if (!res) { + pr_err("Failed to find gmux I/O resource\n"); + goto err_free; + } + + gmux_data->iostart = res->start; + gmux_data->iolen = res->end - res->start; + + if (gmux_data->iolen < GMUX_MIN_IO_LEN) { + pr_err("gmux I/O region too small (%lu < %u)\n", + gmux_data->iolen, GMUX_MIN_IO_LEN); + goto err_free; + } + + if (!request_region(gmux_data->iostart, gmux_data->iolen, + "Apple gmux")) { + pr_err("gmux I/O already in use\n"); + goto err_free; + } + + /* + * On some machines the gmux is in ACPI even thought the machine + * doesn't really have a gmux. Check for invalid version information + * to detect this. + */ + ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR); + ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR); + ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE); + if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) { + pr_info("gmux device not present\n"); + ret = -ENODEV; + goto err_release; + } + + pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor, + ver_release); + + memset(&props, 0, sizeof(props)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS); + + /* + * Currently it's assumed that the maximum brightness is less than + * 2^24 for compatibility with old gmux versions. Cap the max + * brightness at this value, but print a warning if the hardware + * reports something higher so that it can be fixed. + */ + if (WARN_ON(props.max_brightness > GMUX_MAX_BRIGHTNESS)) + props.max_brightness = GMUX_MAX_BRIGHTNESS; + + bdev = backlight_device_register("gmux_backlight", &pnp->dev, + gmux_data, &gmux_bl_ops, &props); + if (IS_ERR(bdev)) { + ret = PTR_ERR(bdev); + goto err_release; + } + + gmux_data->bdev = bdev; + bdev->props.brightness = gmux_get_brightness(bdev); + backlight_update_status(bdev); + + /* + * The backlight situation on Macs is complicated. If the gmux is + * present it's the best choice, because it always works for + * backlight control and supports more levels than other options. + * Disable the other backlight choices. + */ + acpi_video_unregister(); + apple_bl_unregister(); + + return 0; + +err_release: + release_region(gmux_data->iostart, gmux_data->iolen); +err_free: + kfree(gmux_data); + return ret; +} + +static void __devexit gmux_remove(struct pnp_dev *pnp) +{ + struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); + + backlight_device_unregister(gmux_data->bdev); + release_region(gmux_data->iostart, gmux_data->iolen); + kfree(gmux_data); + + acpi_video_register(); + apple_bl_register(); +} + +static const struct pnp_device_id gmux_device_ids[] = { + {"APP000B", 0}, + {"", 0} +}; + +static struct pnp_driver gmux_pnp_driver = { + .name = "apple-gmux", + .probe = gmux_probe, + .remove = __devexit_p(gmux_remove), + .id_table = gmux_device_ids, +}; + +static int __init apple_gmux_init(void) +{ + return pnp_register_driver(&gmux_pnp_driver); +} + +static void __exit apple_gmux_exit(void) +{ + pnp_unregister_driver(&gmux_pnp_driver); +} + +module_init(apple_gmux_init); +module_exit(apple_gmux_exit); + +MODULE_AUTHOR("Seth Forshee "); +MODULE_DESCRIPTION("Apple Gmux Driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pnp, gmux_device_ids); -- cgit v1.2.3 From 34b6cfabd760d3a2784f0ae649eb5e390e0e53cc Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Mon, 19 Mar 2012 17:37:32 +0800 Subject: acer-wmi: Detect communication hot key number Currently we set a fixed hot key number to 0x01 for communction button, but, actually, the key number is different on each acer laptop and it was reported by SMBIOS. So, add this patch to get the communication hot key number from Acer OEM-specific SMBIOS Type AA. Tested on Acer TravelMate 4750 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 937ddeb0e977..cb7e841582d9 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -177,6 +177,11 @@ struct hotkey_function_type_aa { u8 length; u16 handle; u16 commun_func_bitmap; + u16 application_func_bitmap; + u16 media_func_bitmap; + u16 display_func_bitmap; + u16 others_func_bitmap; + u8 commun_fn_key_number; } __attribute__((packed)); /* @@ -213,6 +218,7 @@ static int force_series; static bool ec_raw_mode; static bool has_type_aa; static u16 commun_func_bitmap; +static u8 commun_fn_key_number; module_param(mailled, int, 0444); module_param(brightness, int, 0444); @@ -918,7 +924,7 @@ static acpi_status wmid3_get_device_status(u32 *value, u16 device) union acpi_object *obj; struct wmid3_gds_input_param params = { .function_num = 0x1, - .hotkey_number = 0x01, + .hotkey_number = commun_fn_key_number, .devices = device, }; struct acpi_buffer input = { @@ -987,7 +993,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) u16 devices; struct wmid3_gds_input_param params = { .function_num = 0x1, - .hotkey_number = 0x01, + .hotkey_number = commun_fn_key_number, .devices = commun_func_bitmap, }; struct acpi_buffer input = { @@ -1027,7 +1033,7 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) devices = return_value.devices; params.function_num = 0x2; - params.hotkey_number = 0x01; + params.hotkey_number = commun_fn_key_number; params.devices = (value) ? (devices | device) : (devices & ~device); status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); @@ -1100,6 +1106,8 @@ static void type_aa_dmi_decode(const struct dmi_header *header, void *dummy) interface->capability |= ACER_CAP_THREEG; if (type_aa->commun_func_bitmap & ACER_WMID3_GDS_BLUETOOTH) interface->capability |= ACER_CAP_BLUETOOTH; + + commun_fn_key_number = type_aa->commun_fn_key_number; } static acpi_status WMID_set_capabilities(void) -- cgit v1.2.3 From 996d23ba36a0e505744a047d2138e189c64c6619 Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Mon, 19 Mar 2012 17:37:33 +0800 Subject: acer-wmi: fix out of input parameter size when set The input parameter of set device status is different with get device status. There have volume value element for set status but don't need for get action. On Acer TravelMate 4750 creates field on volume value element even doesn't use it in DSDT. So, add this patch for separate input paramter between set device status with get status. Tested on Acer TravelMate 4750 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index cb7e841582d9..c31664438e76 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -159,7 +159,14 @@ struct lm_return_value { u16 reserved; } __attribute__((packed)); -struct wmid3_gds_input_param { /* Get Device Status input parameter */ +struct wmid3_gds_set_input_param { /* Set Device Status input parameter */ + u8 function_num; /* Function Number */ + u8 hotkey_number; /* Hotkey Number */ + u16 devices; /* Set Device */ + u8 volume_value; /* Volume Value */ +} __attribute__((packed)); + +struct wmid3_gds_get_input_param { /* Get Device Status input parameter */ u8 function_num; /* Function Number */ u8 hotkey_number; /* Hotkey Number */ u16 devices; /* Get Device */ @@ -922,13 +929,13 @@ 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 = { + struct wmid3_gds_get_input_param params = { .function_num = 0x1, .hotkey_number = commun_fn_key_number, .devices = device, }; struct acpi_buffer input = { - sizeof(struct wmid3_gds_input_param), + sizeof(struct wmid3_gds_get_input_param), ¶ms }; struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -991,19 +998,28 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) acpi_status status; union acpi_object *obj; u16 devices; - struct wmid3_gds_input_param params = { + struct wmid3_gds_get_input_param get_params = { .function_num = 0x1, .hotkey_number = commun_fn_key_number, .devices = commun_func_bitmap, }; - struct acpi_buffer input = { - sizeof(struct wmid3_gds_input_param), - ¶ms + struct acpi_buffer get_input = { + sizeof(struct wmid3_gds_get_input_param), + &get_params + }; + struct wmid3_gds_set_input_param set_params = { + .function_num = 0x2, + .hotkey_number = commun_fn_key_number, + .devices = commun_func_bitmap, + }; + struct acpi_buffer set_input = { + sizeof(struct wmid3_gds_set_input_param), + &set_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); + status = wmi_evaluate_method(WMID_GUID3, 0, 0x2, &get_input, &output); if (ACPI_FAILURE(status)) return status; @@ -1032,11 +1048,9 @@ static acpi_status wmid3_set_device_status(u32 value, u16 device) } devices = return_value.devices; - params.function_num = 0x2; - params.hotkey_number = commun_fn_key_number; - params.devices = (value) ? (devices | device) : (devices & ~device); + set_params.devices = (value) ? (devices | device) : (devices & ~device); - status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &input, &output2); + status = wmi_evaluate_method(WMID_GUID3, 0, 0x1, &set_input, &output2); if (ACPI_FAILURE(status)) return status; -- cgit v1.2.3 From c08f2086cd0838465394eb51c2649ce91fbb8cc3 Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Tue, 20 Mar 2012 11:32:49 +0800 Subject: acer-wmi: support Lenovo ideapad S205 Brazos wifi switch Vaclav found a new ideapad S205 Brazos machine that used the same EC register of wireless with S205 but has different product name. So, add this machine to quirk for support wireless rfkill. Tested on Lenovo ideapad S205 Brazos Tested-by: Vaclav Mocek Acked-by: Ike Panhc Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index c31664438e76..3b3cd070b9dc 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -485,6 +485,15 @@ static struct dmi_system_id acer_quirks[] = { }, .driver_data = &quirk_lenovo_ideapad_s205, }, + { + .callback = dmi_matched, + .ident = "Lenovo Ideapad S205 (Brazos)", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "Brazos"), + }, + .driver_data = &quirk_lenovo_ideapad_s205, + }, { .callback = dmi_matched, .ident = "Lenovo 3000 N200", -- cgit v1.2.3 From 9b05ea24375f23a209f768c9069d24ede938ce50 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:02 +0100 Subject: asus-nb-wmi: ignore useless keys Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-nb-wmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b0859d4183e8..350112736023 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -70,6 +70,8 @@ static const struct key_entry asus_nb_wmi_keymap[] = { { KE_KEY, 0x50, { KEY_EMAIL } }, { KE_KEY, 0x51, { KEY_WWW } }, { KE_KEY, 0x55, { KEY_CALC } }, + { KE_IGNORE, 0x57, }, /* Battery mode */ + { KE_IGNORE, 0x58, }, /* AC mode */ { KE_KEY, 0x5C, { KEY_F15 } }, /* Power Gear key */ { KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */ { KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */ -- cgit v1.2.3 From eb649a818abab4b0a8bb8b00d174ad9acf0a8a5a Mon Sep 17 00:00:00 2001 From: Chih-Wei Huang Date: Tue, 20 Mar 2012 09:53:03 +0100 Subject: eeepc-wmi: add extra keymaps for EP121 Signed-off-by: Chih-Wei Huang Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9f6e64302b45..1d91eb2ace0a 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -84,6 +84,9 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_KEY, 0xed, { KEY_CAMERA_DOWN } }, { KE_KEY, 0xee, { KEY_CAMERA_LEFT } }, { KE_KEY, 0xef, { KEY_CAMERA_RIGHT } }, + { KE_KEY, 0xf3, { KEY_MENU } }, + { KE_KEY, 0xf5, { KEY_HOMEPAGE } }, + { KE_KEY, 0xf6, { KEY_ESC } }, { KE_END, 0}, }; -- cgit v1.2.3 From c09b2237da24e9136fc8053e11244f52903e73e0 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:04 +0100 Subject: asus-wmi: on/off bit is not set when reading the value Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 72d731c21d45..2b883470a9d0 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -411,7 +411,7 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) if (retval >= 0) { if (level) - *level = retval & 0x80 ? retval & 0x7F : 0; + *level = retval & 0x7F; if (env) *env = (retval >> 8) & 0x7F; retval = 0; -- cgit v1.2.3 From 8522944085ffd83af129ffce06e3a9f34635391c Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 20 Mar 2012 09:53:05 +0100 Subject: drivers, samsung-laptop: fix initialization of sabi_data in sabi_set_commandb Fields d0, d1, d2, and d3 are members of an anonymous struct inside an anonymous union inside struct sabi_data. Initialization must be done by wrapping the anonymous union and structs with brackets to avoid a build error: drivers/platform/x86/samsung-laptop.c: In function ‘sabi_set_commandb’: drivers/platform/x86/samsung-laptop.c:433: error: unknown field ‘d0’ specified in initializer drivers/platform/x86/samsung-laptop.c:433: warning: missing braces around initializer drivers/platform/x86/samsung-laptop.c:433: warning: (near initialization for ‘in.’) ... Signed-off-by: David Rientjes Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 7d7109fdbd63..b7c67c87f47b 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -430,7 +430,7 @@ exit: static int sabi_set_commandb(struct samsung_laptop *samsung, u16 command, u8 data) { - struct sabi_data in = { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 }; + struct sabi_data in = { { { .d0 = 0, .d1 = 0, .d2 = 0, .d3 = 0 } } }; in.data[0] = data; return sabi_command(samsung, command, &in, NULL); -- cgit v1.2.3 From 82c333aaf4b2d6a83d9e8d6247584af8c18c4d7b Mon Sep 17 00:00:00 2001 From: David Rientjes Date: Tue, 20 Mar 2012 09:53:06 +0100 Subject: drivers, samsung-laptop: fix usage of isalnum linux/ctype.h is needed for isalnum() to avoid a build error: drivers/platform/x86/samsung-laptop.c: In function ‘samsung_sabi_diag’: drivers/platform/x86/samsung-laptop.c:1306: error: implicit declaration of function ‘isalnum’ Signed-off-by: David Rientjes Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index b7c67c87f47b..d1142e317433 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -25,6 +25,7 @@ #include #include #include +#include /* * This driver is needed because a number of Samsung laptops do not hook -- cgit v1.2.3 From bde9e5098c121cfca6d9c7e3a26e6ae44a3c9632 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 20 Mar 2012 09:53:07 +0100 Subject: samsung-laptop: cleanup return type: mode_t vs umode_t This function returns a umode_t (unsigned short) instead of mode_t which is an unsigned int on some architectures. Cleaning this up silences a compile warning: drivers/platform/x86/samsung-laptop.c:1108:2: warning: initialization from incompatible pointer type [enabled by default] Signed-off-by: Dan Carpenter Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index d1142e317433..4787afdf11dc 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -1087,7 +1087,7 @@ static int __init samsung_backlight_init(struct samsung_laptop *samsung) return 0; } -static mode_t samsung_sysfs_is_visible(struct kobject *kobj, +static umode_t samsung_sysfs_is_visible(struct kobject *kobj, struct attribute *attr, int idx) { struct device *dev = container_of(kobj, struct device, kobj); -- cgit v1.2.3 From c87992d1fa51a6a3d8f0e980ca4d2bdec7e78a17 Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Tue, 20 Mar 2012 09:53:08 +0100 Subject: asus-wmi: add scalar board brightness adj. support Some ASUS ET2012E/I All-in-One machines that use a scalar board to control the brightness, and they only accept brightness up and down command. So, I introduced a get_scalar_command() function to pass the command to the scalar board through WMI. Besides, we have to store the brightness value locally, for we need the old value to know the brightness value is increasing or decreasing. BTW, since there is no way to retrieve the actual brightness(it would be a fixed value), and the max brightness value would be fixed to 1, so we have to keep passing the brightness up/down command when we reached the max brightness value or 0. Signed-off-by: AceLan Kao Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-nb-wmi.c | 2 +- drivers/platform/x86/asus-wmi.c | 33 ++++++++++++--- drivers/platform/x86/asus-wmi.h | 10 ++++- drivers/platform/x86/eeepc-wmi.c | 85 +++++++++++++++++++++++++++----------- 4 files changed, 97 insertions(+), 33 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 350112736023..1aea6b8019be 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -101,7 +101,7 @@ static struct asus_wmi_driver asus_nb_wmi_driver = { .keymap = asus_nb_wmi_keymap, .input_name = "Asus WMI hotkeys", .input_phys = ASUS_NB_WMI_FILE "/input0", - .quirks = asus_nb_wmi_quirks, + .detect_quirks = asus_nb_wmi_quirks, }; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 2b883470a9d0..eb114f8d39e7 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -784,7 +784,8 @@ static int asus_new_rfkill(struct asus_wmi *asus, arfkill->dev_id = dev_id; arfkill->asus = asus; - if (dev_id == ASUS_WMI_DEVID_WLAN && asus->driver->hotplug_wireless) + if (dev_id == ASUS_WMI_DEVID_WLAN && + asus->driver->quirks->hotplug_wireless) *rfkill = rfkill_alloc(name, &asus->platform_device->dev, type, &asus_rfkill_wlan_ops, arfkill); else @@ -895,7 +896,7 @@ static int asus_wmi_rfkill_init(struct asus_wmi *asus) if (result && result != -ENODEV) goto exit; - if (!asus->driver->hotplug_wireless) + if (!asus->driver->quirks->hotplug_wireless) goto exit; result = asus_setup_pci_hotplug(asus); @@ -1116,13 +1117,33 @@ static int read_brightness(struct backlight_device *bd) return retval & ASUS_WMI_DSTS_BRIGHTNESS_MASK; } +static u32 get_scalar_command(struct backlight_device *bd) +{ + struct asus_wmi *asus = bl_get_data(bd); + u32 ctrl_param = 0; + + if ((asus->driver->brightness < bd->props.brightness) || + bd->props.brightness == bd->props.max_brightness) + ctrl_param = 0x00008001; + else if ((asus->driver->brightness > bd->props.brightness) || + bd->props.brightness == 0) + ctrl_param = 0x00008000; + + asus->driver->brightness = bd->props.brightness; + + return ctrl_param; +} + static int update_bl_status(struct backlight_device *bd) { struct asus_wmi *asus = bl_get_data(bd); u32 ctrl_param; int power, err; - ctrl_param = bd->props.brightness; + if (asus->driver->quirks->scalar_panel_brightness) + ctrl_param = get_scalar_command(bd); + else + ctrl_param = bd->props.brightness; err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, ctrl_param, NULL); @@ -1200,6 +1221,8 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) bd->props.power = power; backlight_update_status(bd); + asus->driver->brightness = bd->props.brightness; + return 0; } @@ -1622,8 +1645,8 @@ static int asus_wmi_add(struct platform_device *pdev) wdrv->platform_device = pdev; platform_set_drvdata(asus->platform_device, asus); - if (wdrv->quirks) - wdrv->quirks(asus->driver); + if (wdrv->detect_quirks) + wdrv->detect_quirks(asus->driver); err = asus_wmi_platform_init(asus); if (err) diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 8147c10161cc..ac7dd4eaebd0 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -35,9 +35,14 @@ struct module; struct key_entry; struct asus_wmi; +struct quirk_entry { + bool hotplug_wireless; + bool scalar_panel_brightness; +}; + struct asus_wmi_driver { - bool hotplug_wireless; int wapf; + int brightness; const char *name; struct module *owner; @@ -47,13 +52,14 @@ struct asus_wmi_driver { const struct key_entry *keymap; const char *input_name; const char *input_phys; + struct quirk_entry *quirks; /* Returns new code, value, and autorelease values in arguments. * Return ASUS_WMI_KEY_IGNORE in code if event should be ignored. */ void (*key_filter) (struct asus_wmi_driver *driver, int *code, unsigned int *value, bool *autorelease); int (*probe) (struct platform_device *device); - void (*quirks) (struct asus_wmi_driver *driver); + void (*detect_quirks) (struct asus_wmi_driver *driver); struct platform_driver platform_driver; struct platform_device *platform_device; diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 1d91eb2ace0a..67186e6ca28d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -48,6 +48,7 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); +static struct quirk_entry *quirks; static bool hotplug_wireless; module_param(hotplug_wireless, bool, 0444); @@ -90,6 +91,60 @@ static const struct key_entry eeepc_wmi_keymap[] = { { KE_END, 0}, }; +static struct quirk_entry quirk_asus_unknown = { +}; + +static struct quirk_entry quirk_asus_1000h = { + .hotplug_wireless = true, +}; + +static struct quirk_entry quirk_asus_et2012_type3 = { + .scalar_panel_brightness = true, +}; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + char *model; + quirks = dmi->driver_data; + + model = (char *)dmi->matches[1].substr; + if (unlikely(strncmp(model, "ET2012", 6) == 0)) { + const struct dmi_device *dev = NULL; + char oemstring[30]; + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, + dev))) { + if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) { + if (oemstring[18] == '3') + quirks = &quirk_asus_et2012_type3; + break; + } + } + } + return 1; +} + +static struct dmi_system_id asus_quirks[] = { + { + .callback = dmi_matched, + .ident = "ASUSTeK Computer INC. 1000H", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "1000H"), + }, + .driver_data = &quirk_asus_1000h, + }, + { + .callback = dmi_matched, + .ident = "ASUSTeK Computer INC. ET2012E/I", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ET2012"), + }, + .driver_data = &quirk_asus_unknown, + }, + {}, +}; + static void eeepc_wmi_key_filter(struct asus_wmi_driver *asus_wmi, int *code, unsigned int *value, bool *autorelease) { @@ -144,33 +199,13 @@ static int eeepc_wmi_probe(struct platform_device *pdev) return 0; } -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; driver->wapf = -1; - eeepc_dmi_check(driver); + driver->quirks = &quirk_asus_unknown; + driver->quirks->hotplug_wireless = hotplug_wireless; + dmi_check_system(asus_quirks); + driver->quirks = quirks; } static struct asus_wmi_driver asus_wmi_driver = { @@ -182,7 +217,7 @@ static struct asus_wmi_driver asus_wmi_driver = { .input_phys = EEEPC_WMI_FILE "/input0", .key_filter = eeepc_wmi_key_filter, .probe = eeepc_wmi_probe, - .quirks = eeepc_wmi_quirks, + .detect_quirks = eeepc_wmi_quirks, }; -- cgit v1.2.3 From 6e0044bedc1fc94a61cc32fa25dcab9a4e4a9218 Mon Sep 17 00:00:00 2001 From: AceLan Kao Date: Tue, 20 Mar 2012 09:53:09 +0100 Subject: asus-wmi: store backlight power status for AIO machine Due to some implementation reasons, ASUS ET2012 All-in-One machines can't report the correct backlight power status, it will always return 1. To track the backlight power status correctly, we have to store the status by ourselves. BTW, by the BIOS design, the backlight power will be turn on/off sequently, no matter what the value of the parameter will be. More over, the brightness adjustment command will turn on the backlight power. Those behaviors will make us fail to track the backlight power status. For example, While we are trying to turn on the backlight power, we will send out the brightness adjustment command and then trying to figure out if we have to turn on the backlight power, then send out the command. But, the real case is that, the backlight power turns on while sending the brightness adjustment command, and then we send out the command to turn on the backlight power, it actually will turn off the backlight power and the backlight power status we recorded becomes wrong. So, we have to seperate these two commands by a if statement. Signed-off-by: AceLan Kao Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 33 ++++++++++++++++++++------------- drivers/platform/x86/asus-wmi.h | 2 ++ drivers/platform/x86/eeepc-wmi.c | 15 ++++++++++++--- 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index eb114f8d39e7..c4ad76ee7b5f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1076,7 +1076,12 @@ static int asus_wmi_hwmon_init(struct asus_wmi *asus) */ static int read_backlight_power(struct asus_wmi *asus) { - int ret = asus_wmi_get_devstate_simple(asus, ASUS_WMI_DEVID_BACKLIGHT); + int ret; + if (asus->driver->quirks->store_backlight_power) + ret = !asus->driver->panel_power; + else + ret = asus_wmi_get_devstate_simple(asus, + ASUS_WMI_DEVID_BACKLIGHT); if (ret < 0) return ret; @@ -1138,24 +1143,23 @@ static int update_bl_status(struct backlight_device *bd) { struct asus_wmi *asus = bl_get_data(bd); u32 ctrl_param; - int power, err; - - if (asus->driver->quirks->scalar_panel_brightness) - ctrl_param = get_scalar_command(bd); - else - ctrl_param = bd->props.brightness; - - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, - ctrl_param, NULL); - - if (err < 0) - return err; + int power, err = 0; power = read_backlight_power(asus); if (power != -ENODEV && bd->props.power != power) { ctrl_param = !!(bd->props.power == FB_BLANK_UNBLANK); err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BACKLIGHT, ctrl_param, NULL); + if (asus->driver->quirks->store_backlight_power) + asus->driver->panel_power = bd->props.power; + } else { + if (asus->driver->quirks->scalar_panel_brightness) + ctrl_param = get_scalar_command(bd); + else + ctrl_param = bd->props.brightness; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, + ctrl_param, NULL); } return err; } @@ -1217,6 +1221,9 @@ static int asus_wmi_backlight_init(struct asus_wmi *asus) asus->backlight_device = bd; + if (asus->driver->quirks->store_backlight_power) + asus->driver->panel_power = power; + bd->props.brightness = read_brightness(bd); bd->props.power = power; backlight_update_status(bd); diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index ac7dd4eaebd0..35003e4f1316 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -38,11 +38,13 @@ struct asus_wmi; struct quirk_entry { bool hotplug_wireless; bool scalar_panel_brightness; + bool store_backlight_power; }; struct asus_wmi_driver { int wapf; int brightness; + int panel_power; const char *name; struct module *owner; diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 67186e6ca28d..9f8ccf9f590d 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include "asus-wmi.h" @@ -98,8 +99,13 @@ static struct quirk_entry quirk_asus_1000h = { .hotplug_wireless = true, }; +static struct quirk_entry quirk_asus_et2012_type1 = { + .store_backlight_power = true, +}; + static struct quirk_entry quirk_asus_et2012_type3 = { .scalar_panel_brightness = true, + .store_backlight_power = true, }; static int dmi_matched(const struct dmi_system_id *dmi) @@ -111,10 +117,12 @@ static int dmi_matched(const struct dmi_system_id *dmi) if (unlikely(strncmp(model, "ET2012", 6) == 0)) { const struct dmi_device *dev = NULL; char oemstring[30]; - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, - dev))) { + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, + NULL, dev))) { if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) { - if (oemstring[18] == '3') + if (oemstring[18] == '1') + quirks = &quirk_asus_et2012_type1; + else if (oemstring[18] == '3') quirks = &quirk_asus_et2012_type3; break; } @@ -202,6 +210,7 @@ static int eeepc_wmi_probe(struct platform_device *pdev) static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) { driver->wapf = -1; + driver->panel_power = FB_BLANK_UNBLANK; driver->quirks = &quirk_asus_unknown; driver->quirks->hotplug_wireless = hotplug_wireless; dmi_check_system(asus_quirks); -- cgit v1.2.3 From 6a2bcccdb3206950d28e343476d9050e23e79b7e Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:10 +0100 Subject: asus-wmi: move WAPF variable into quirks_entry Some models work better with different values of wapf, so move the variable into quriks_entry to make it more easy to give a specific value to different models. Based on original patch from AceLan Kao Cc: AceLan Kao Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-nb-wmi.c | 6 +++++- drivers/platform/x86/asus-wmi.c | 4 ++-- drivers/platform/x86/asus-wmi.h | 2 +- drivers/platform/x86/eeepc-wmi.c | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index 1aea6b8019be..b12038c492c7 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -51,9 +51,13 @@ static uint wapf; module_param(wapf, uint, 0444); MODULE_PARM_DESC(wapf, "WAPF value"); +static struct quirk_entry quirk_asus_unknown = { +}; + static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) { - driver->wapf = wapf; + driver->quirks = &quirk_asus_unknown; + driver->quirks->wapf = wapf; } static const struct key_entry asus_nb_wmi_keymap[] = { diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index c4ad76ee7b5f..ff9cfd83b09f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1471,9 +1471,9 @@ static int asus_wmi_platform_init(struct asus_wmi *asus) /* CWAP allow to define the behavior of the Fn+F2 key, * this method doesn't seems to be present on Eee PCs */ - if (asus->driver->wapf >= 0) + if (asus->driver->quirks->wapf >= 0) asus_wmi_set_devstate(ASUS_WMI_DEVID_CWAP, - asus->driver->wapf, NULL); + asus->driver->quirks->wapf, NULL); return asus_wmi_sysfs_init(asus->platform_device); } diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 35003e4f1316..d43b66742004 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -39,10 +39,10 @@ struct quirk_entry { bool hotplug_wireless; bool scalar_panel_brightness; bool store_backlight_power; + int wapf; }; struct asus_wmi_driver { - int wapf; int brightness; int panel_power; diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 9f8ccf9f590d..389ff888cb6c 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -209,10 +209,10 @@ static int eeepc_wmi_probe(struct platform_device *pdev) static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) { - driver->wapf = -1; driver->panel_power = FB_BLANK_UNBLANK; driver->quirks = &quirk_asus_unknown; driver->quirks->hotplug_wireless = hotplug_wireless; + driver->quirks->wapf = -1; dmi_check_system(asus_quirks); driver->quirks = quirks; } -- cgit v1.2.3 From fb05b9f53fa9131ae86eb8cc4fda20e943a86e36 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:11 +0100 Subject: asus-nb-wmi: set panel_power correctly Even if it's currently unused. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-nb-wmi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index b12038c492c7..99a30b513137 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "asus-wmi.h" @@ -58,6 +59,7 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver) { driver->quirks = &quirk_asus_unknown; driver->quirks->wapf = wapf; + driver->panel_power = FB_BLANK_UNBLANK; } static const struct key_entry asus_nb_wmi_keymap[] = { -- cgit v1.2.3 From c55d995dd3cebffdeb2b7eff8acc813c56d62c97 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:12 +0100 Subject: eeepc-wmi: refine quirks handling Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 389ff888cb6c..0bb0aaf43aba 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -49,7 +49,6 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("wmi:"EEEPC_WMI_EVENT_GUID); -static struct quirk_entry *quirks; static bool hotplug_wireless; module_param(hotplug_wireless, bool, 0444); @@ -108,6 +107,8 @@ static struct quirk_entry quirk_asus_et2012_type3 = { .store_backlight_power = true, }; +static struct quirk_entry *quirks; + static int dmi_matched(const struct dmi_system_id *dmi) { char *model; @@ -209,12 +210,14 @@ static int eeepc_wmi_probe(struct platform_device *pdev) static void eeepc_wmi_quirks(struct asus_wmi_driver *driver) { - driver->panel_power = FB_BLANK_UNBLANK; - driver->quirks = &quirk_asus_unknown; - driver->quirks->hotplug_wireless = hotplug_wireless; - driver->quirks->wapf = -1; + quirks = &quirk_asus_unknown; + quirks->hotplug_wireless = hotplug_wireless; + dmi_check_system(asus_quirks); + driver->quirks = quirks; + driver->quirks->wapf = -1; + driver->panel_power = FB_BLANK_UNBLANK; } static struct asus_wmi_driver asus_wmi_driver = { -- cgit v1.2.3 From 7a61d0207465e41b7dbe3a25f628ddef24572c6f Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:13 +0100 Subject: eeepc-wmi: split et2012 specific hacks Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/eeepc-wmi.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 0bb0aaf43aba..656761380342 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -109,26 +109,32 @@ static struct quirk_entry quirk_asus_et2012_type3 = { static struct quirk_entry *quirks; +static void et2012_quirks(void) +{ + const struct dmi_device *dev = NULL; + char oemstring[30]; + + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) { + if (oemstring[18] == '1') + quirks = &quirk_asus_et2012_type1; + else if (oemstring[18] == '3') + quirks = &quirk_asus_et2012_type3; + break; + } + } +} + static int dmi_matched(const struct dmi_system_id *dmi) { char *model; + quirks = dmi->driver_data; model = (char *)dmi->matches[1].substr; - if (unlikely(strncmp(model, "ET2012", 6) == 0)) { - const struct dmi_device *dev = NULL; - char oemstring[30]; - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, - NULL, dev))) { - if (sscanf(dev->name, "AEMS%24c", oemstring) == 1) { - if (oemstring[18] == '1') - quirks = &quirk_asus_et2012_type1; - else if (oemstring[18] == '3') - quirks = &quirk_asus_et2012_type3; - break; - } - } - } + if (unlikely(strncmp(model, "ET2012", 6) == 0)) + et2012_quirks(); + return 1; } -- cgit v1.2.3 From ade28abdcb474531bb7045c032a286812c7f6d2a Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Tue, 20 Mar 2012 09:53:14 +0100 Subject: asus-wmi: don't update power and brightness when using scalar But we can still do it on other boards, as this might happen if the backlight driver change when update_bl is called. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index ff9cfd83b09f..7d1684bdbf63 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1152,15 +1152,21 @@ static int update_bl_status(struct backlight_device *bd) ctrl_param, NULL); if (asus->driver->quirks->store_backlight_power) asus->driver->panel_power = bd->props.power; - } else { - if (asus->driver->quirks->scalar_panel_brightness) - ctrl_param = get_scalar_command(bd); - else - ctrl_param = bd->props.brightness; - err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, - ctrl_param, NULL); + /* When using scalar brightness, updating the brightness + * will mess with the backlight power */ + if (asus->driver->quirks->scalar_panel_brightness) + return err; } + + if (asus->driver->quirks->scalar_panel_brightness) + ctrl_param = get_scalar_command(bd); + else + ctrl_param = bd->props.brightness; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_BRIGHTNESS, + ctrl_param, NULL); + return err; } -- cgit v1.2.3 From 23b0531641c72c6a2f410af1c593293fa353884b Mon Sep 17 00:00:00 2001 From: Manoj Iyer Date: Fri, 9 Mar 2012 17:32:24 -0600 Subject: thinkpad-acpi: recognize Lenovo as version string in newer V-series BIOS The newer V series bios reports product version as 'Lenovo' instead of 'ThinkPad'. Recoginze this new string so that the module can load. Signed-off-by: Manoj Iyer Signed-off-by: Matthew Garrett Tested-by: James Ferguson Tested-by: Dennis Chua Tested-by: Ike Pan Acked-by: Henrique de Moraes Holschuh --- drivers/platform/x86/thinkpad_acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index ea0c6075b720..d68c0002f4a2 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -8658,7 +8658,7 @@ static int __must_check __init get_thinkpad_model_data( } s = dmi_get_system_info(DMI_PRODUCT_VERSION); - if (s && !strnicmp(s, "ThinkPad", 8)) { + if (s && !(strnicmp(s, "ThinkPad", 8) && strnicmp(s, "Lenovo", 6))) { tp->model_str = kstrdup(s, GFP_KERNEL); if (!tp->model_str) return -ENOMEM; -- cgit v1.2.3 From 5719b81988f3c24ff694dc3a37e35b35630a3966 Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Fri, 23 Mar 2012 12:36:44 +0800 Subject: acer-wmi: No wifi rfkill on Sony machines The wireless rfkill should charged by sony-laptop but not acer-wmi. So, add Sony's SNY5001 acpi device to blacklist in acer-wmi. Tested on Sony Vaio Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Mattia Dongili Cc: Dimitris N Tested-by: Dimitris N Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/acer-wmi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 3b3cd070b9dc..b253e219297e 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -719,6 +719,7 @@ static const struct acpi_device_id norfkill_ids[] = { { "VPC2004", 0}, { "IBM0068", 0}, { "LEN0068", 0}, + { "SNY5001", 0}, /* sony-laptop in charge */ { "", 0}, }; -- cgit v1.2.3 From a979e2e2af7d5b4bb3b20f6a716c627bb23a6753 Mon Sep 17 00:00:00 2001 From: Corentin Chary Date: Thu, 22 Mar 2012 14:08:19 +0100 Subject: samsung-laptop: unregister ACPI video module for some well known laptops On these laptops, the ACPI video is not functional, and very unlikely to be fixed by the vendor. Note that intel_backlight works for some of these laptops, and the backlight from samsung-laptop always work. The good news is that newer laptops have functional ACPI video device and won't end up growing this list. Signed-off-by: Corentin Chary Signed-off-by: Matthew Garrett --- drivers/platform/x86/samsung-laptop.c | 80 +++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index 4787afdf11dc..e2a34b42ddc1 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -26,6 +26,9 @@ #include #include #include +#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) +#include +#endif /* * This driver is needed because a number of Samsung laptops do not hook @@ -336,6 +339,7 @@ struct samsung_laptop { struct work_struct kbd_led_work; struct samsung_laptop_debug debug; + struct samsung_quirks *quirks; bool handle_backlight; bool has_stepping_quirk; @@ -343,7 +347,15 @@ struct samsung_laptop { char sdiag[64]; }; +struct samsung_quirks { + bool broken_acpi_video; +}; + +static struct samsung_quirks samsung_unknown = {}; +static struct samsung_quirks samsung_broken_acpi_video = { + .broken_acpi_video = true, +}; static bool force; module_param(force, bool, 0); @@ -1416,6 +1428,14 @@ static int __init samsung_platform_init(struct samsung_laptop *samsung) return 0; } +static struct samsung_quirks *quirks; + +static int __init samsung_dmi_matched(const struct dmi_system_id *d) +{ + quirks = d->driver_data; + return 0; +} + static struct dmi_system_id __initdata samsung_dmi_table[] = { { .matches = { @@ -1445,6 +1465,47 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = { DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */ }, }, + /* Specific DMI ids for laptop with quirks */ + { + .callback = samsung_dmi_matched, + .ident = "N150P", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), + DMI_MATCH(DMI_PRODUCT_NAME, "N150P"), + DMI_MATCH(DMI_BOARD_NAME, "N150P"), + }, + .driver_data = &samsung_broken_acpi_video, + }, + { + .callback = samsung_dmi_matched, + .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"), + }, + .driver_data = &samsung_broken_acpi_video, + }, + { + .callback = samsung_dmi_matched, + .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"), + }, + .driver_data = &samsung_broken_acpi_video, + }, + { + .callback = samsung_dmi_matched, + .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"), + }, + .driver_data = &samsung_broken_acpi_video, + }, { }, }; MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); @@ -1456,6 +1517,7 @@ static int __init samsung_init(void) struct samsung_laptop *samsung; int ret; + quirks = &samsung_unknown; if (!force && !dmi_check_system(samsung_dmi_table)) return -ENODEV; @@ -1465,12 +1527,21 @@ static int __init samsung_init(void) mutex_init(&samsung->sabi_mutex); samsung->handle_backlight = true; + samsung->quirks = quirks; -#ifdef CONFIG_ACPI + +#if (defined CONFIG_ACPI_VIDEO || defined CONFIG_ACPI_VIDEO_MODULE) /* Don't handle backlight here if the acpi video already handle it */ - if (acpi_video_backlight_support()) - samsung->handle_backlight = false; + if (acpi_video_backlight_support()) { + if (samsung->quirks->broken_acpi_video) { + pr_info("Disabling ACPI video driver\n"); + acpi_video_unregister(); + } else { + samsung->handle_backlight = false; + } + } #endif + ret = samsung_platform_init(samsung); if (ret) goto error_platform; @@ -1481,7 +1552,8 @@ static int __init samsung_init(void) #ifdef CONFIG_ACPI /* Only log that if we are really on a sabi platform */ - if (acpi_video_backlight_support()) + if (acpi_video_backlight_support() && + !samsung->quirks->broken_acpi_video) pr_info("Backlight controlled by ACPI video driver\n"); #endif -- cgit v1.2.3 From 41603e9783a24c8c7cce548c0819bdc9e46a585b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Fri, 23 Mar 2012 01:08:34 +0100 Subject: drivers/platform/x86/amilo-rfkill.c::amilo_rfkill_probe() avoid NULL deref In drivers/platform/x86/amilo-rfkill.c::amilo_rfkill_probe() the call to dmi_first_match() may fail and return NULL. If it does return NULL, then we'll be dereferencing a NULL pointer in the rfkill_alloc() call where we do 'system_id->driver_data' --> KABOOM! Avoid that problem by testing for a NULL return value from dmi_first_match() and bailing out if it fails. I was a bit uncertain about what to return in the failure case. In the end I settled for -ENXIO as the most logical error to return. Signed-off-by: Jesper Juhl Signed-off-by: Matthew Garrett --- drivers/platform/x86/amilo-rfkill.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c index 19170bb7700b..a514bf66fdd7 100644 --- a/drivers/platform/x86/amilo-rfkill.c +++ b/drivers/platform/x86/amilo-rfkill.c @@ -97,9 +97,12 @@ static struct rfkill *amilo_rfkill_dev; static int __devinit amilo_rfkill_probe(struct platform_device *device) { + int rc; const struct dmi_system_id *system_id = dmi_first_match(amilo_rfkill_id_table); - int rc; + + if (!system_id) + return -ENXIO; amilo_rfkill_dev = rfkill_alloc(KBUILD_MODNAME, &device->dev, RFKILL_TYPE_WLAN, -- cgit v1.2.3 From 86924de2a612b275a45e92ba80d6f47d4e97d620 Mon Sep 17 00:00:00 2001 From: Lee, Chun-Yi Date: Mon, 26 Mar 2012 15:47:58 -0400 Subject: acer-wmi: add quirk table for video backlight vendor mode There have some acer laptop have broken _BCM implemenation, the AML code wrote value to EC register but firmware didn't change brighenss. Fortunately, the brightness control works on those machines with vendor mode. So, add quirk table for video backlight vendor mode and unregister acpi video interface on those machines. Tested on Acer TravelMate 4750 Cc: Carlos Corbacho Cc: Matthew Garrett Cc: Dmitry Torokhov Cc: Corentin Chary Cc: Thomas Renninger Signed-off-by: Lee, Chun-Yi Signed-off-by: Matthew Garrett --- drivers/platform/x86/Kconfig | 4 ++++ drivers/platform/x86/acer-wmi.c | 29 +++++++++++++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c5b4bfed7bb4..2a262f5c5c0c 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -26,6 +26,10 @@ config ACER_WMI depends on RFKILL || RFKILL = n depends on ACPI_WMI select INPUT_SPARSEKMAP + # Acer WMI depends on ACPI_VIDEO when ACPI is enabled + # but for select to work, need to select ACPI_VIDEO's dependencies, ick + select VIDEO_OUTPUT_CONTROL if ACPI + select ACPI_VIDEO if ACPI ---help--- This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index b253e219297e..c1a3fd8e1243 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -43,6 +43,7 @@ #include #include +#include MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("Acer Laptop WMI Extras Driver"); @@ -506,6 +507,25 @@ static struct dmi_system_id acer_quirks[] = { {} }; +static int video_set_backlight_video_vendor(const struct dmi_system_id *d) +{ + interface->capability &= ~ACER_CAP_BRIGHTNESS; + pr_info("Brightness must be controlled by generic video driver\n"); + return 0; +} + +static const struct dmi_system_id video_vendor_dmi_table[] = { + { + .callback = video_set_backlight_video_vendor, + .ident = "Acer TravelMate 4750", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4750"), + }, + }, + {} +}; + /* Find which quirks are needed for a particular vendor/ model pair */ static void find_quirks(void) { @@ -2017,8 +2037,13 @@ static int __init acer_wmi_init(void) set_quirks(); if (acpi_video_backlight_support()) { - interface->capability &= ~ACER_CAP_BRIGHTNESS; - pr_info("Brightness must be controlled by generic video driver\n"); + if (dmi_check_system(video_vendor_dmi_table)) { + acpi_video_unregister(); + } else { + interface->capability &= ~ACER_CAP_BRIGHTNESS; + pr_info("Brightness must be controlled by " + "acpi video driver\n"); + } } if (wmi_has_guid(WMID_GUID3)) { -- cgit v1.2.3