diff options
Diffstat (limited to 'drivers/platform')
47 files changed, 2316 insertions, 419 deletions
diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 8390dca2b4e..69616aeaa96 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -1,3 +1,7 @@  if X86  source "drivers/platform/x86/Kconfig"  endif +if GOLDFISH +source "drivers/platform/goldfish/Kconfig" +endif + diff --git a/drivers/platform/Makefile b/drivers/platform/Makefile index b17c16ce54a..8a44a4cd6d1 100644 --- a/drivers/platform/Makefile +++ b/drivers/platform/Makefile @@ -4,3 +4,4 @@  obj-$(CONFIG_X86)		+= x86/  obj-$(CONFIG_OLPC)		+= olpc/ +obj-$(CONFIG_GOLDFISH)		+= goldfish/ diff --git a/drivers/platform/goldfish/Kconfig b/drivers/platform/goldfish/Kconfig new file mode 100644 index 00000000000..635ef25cc72 --- /dev/null +++ b/drivers/platform/goldfish/Kconfig @@ -0,0 +1,5 @@ +config GOLDFISH_PIPE +	tristate "Goldfish virtual device for QEMU pipes" +	---help--- +	  This is a virtual device to drive the QEMU pipe interface used by +	  the Goldfish Android Virtual Device. diff --git a/drivers/platform/goldfish/Makefile b/drivers/platform/goldfish/Makefile new file mode 100644 index 00000000000..a0022395eee --- /dev/null +++ b/drivers/platform/goldfish/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for Goldfish platform specific drivers +# +obj-$(CONFIG_GOLDFISH)	+=	pdev_bus.o +obj-$(CONFIG_GOLDFISH_PIPE)	+= goldfish_pipe.o diff --git a/drivers/platform/goldfish/goldfish_pipe.c b/drivers/platform/goldfish/goldfish_pipe.c new file mode 100644 index 00000000000..4f5aa831f54 --- /dev/null +++ b/drivers/platform/goldfish/goldfish_pipe.c @@ -0,0 +1,612 @@ +/* + * Copyright (C) 2011 Google, Inc. + * Copyright (C) 2012 Intel, Inc. + * Copyright (C) 2013 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +/* This source file contains the implementation of a special device driver + * that intends to provide a *very* fast communication channel between the + * guest system and the QEMU emulator. + * + * Usage from the guest is simply the following (error handling simplified): + * + *    int  fd = open("/dev/qemu_pipe",O_RDWR); + *    .... write() or read() through the pipe. + * + * This driver doesn't deal with the exact protocol used during the session. + * It is intended to be as simple as something like: + * + *    // do this _just_ after opening the fd to connect to a specific + *    // emulator service. + *    const char*  msg = "<pipename>"; + *    if (write(fd, msg, strlen(msg)+1) < 0) { + *       ... could not connect to <pipename> service + *       close(fd); + *    } + * + *    // after this, simply read() and write() to communicate with the + *    // service. Exact protocol details left as an exercise to the reader. + * + * This driver is very fast because it doesn't copy any data through + * intermediate buffers, since the emulator is capable of translating + * guest user addresses into host ones. + * + * Note that we must however ensure that each user page involved in the + * exchange is properly mapped during a transfer. + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/miscdevice.h> +#include <linux/platform_device.h> +#include <linux/poll.h> +#include <linux/sched.h> +#include <linux/bitops.h> +#include <linux/slab.h> +#include <linux/io.h> + +/* + * IMPORTANT: The following constants must match the ones used and defined + * in external/qemu/hw/goldfish_pipe.c in the Android source tree. + */ + +/* pipe device registers */ +#define PIPE_REG_COMMAND		0x00  /* write: value = command */ +#define PIPE_REG_STATUS			0x04  /* read */ +#define PIPE_REG_CHANNEL		0x08  /* read/write: channel id */ +#define PIPE_REG_SIZE			0x0c  /* read/write: buffer size */ +#define PIPE_REG_ADDRESS		0x10  /* write: physical address */ +#define PIPE_REG_WAKES			0x14  /* read: wake flags */ +#define PIPE_REG_PARAMS_ADDR_LOW	0x18  /* read/write: batch data address */ +#define PIPE_REG_PARAMS_ADDR_HIGH	0x1c  /* read/write: batch data address */ +#define PIPE_REG_ACCESS_PARAMS		0x20  /* write: batch access */ + +/* list of commands for PIPE_REG_COMMAND */ +#define CMD_OPEN			1  /* open new channel */ +#define CMD_CLOSE			2  /* close channel (from guest) */ +#define CMD_POLL			3  /* poll read/write status */ + +/* List of bitflags returned in status of CMD_POLL command */ +#define PIPE_POLL_IN			(1 << 0) +#define PIPE_POLL_OUT			(1 << 1) +#define PIPE_POLL_HUP			(1 << 2) + +/* The following commands are related to write operations */ +#define CMD_WRITE_BUFFER	4  /* send a user buffer to the emulator */ +#define CMD_WAKE_ON_WRITE	5  /* tell the emulator to wake us when writing +				     is possible */ + +/* The following commands are related to read operations, they must be + * listed in the same order than the corresponding write ones, since we + * will use (CMD_READ_BUFFER - CMD_WRITE_BUFFER) as a special offset + * in goldfish_pipe_read_write() below. + */ +#define CMD_READ_BUFFER        6  /* receive a user buffer from the emulator */ +#define CMD_WAKE_ON_READ       7  /* tell the emulator to wake us when reading +				   * is possible */ + +/* Possible status values used to signal errors - see goldfish_pipe_error_convert */ +#define PIPE_ERROR_INVAL       -1 +#define PIPE_ERROR_AGAIN       -2 +#define PIPE_ERROR_NOMEM       -3 +#define PIPE_ERROR_IO          -4 + +/* Bit-flags used to signal events from the emulator */ +#define PIPE_WAKE_CLOSED       (1 << 0)  /* emulator closed pipe */ +#define PIPE_WAKE_READ         (1 << 1)  /* pipe can now be read from */ +#define PIPE_WAKE_WRITE        (1 << 2)  /* pipe can now be written to */ + +struct access_params { +	u32 channel; +	u32 size; +	u32 address; +	u32 cmd; +	u32 result; +	/* reserved for future extension */ +	u32 flags; +}; + +/* The global driver data. Holds a reference to the i/o page used to + * communicate with the emulator, and a wake queue for blocked tasks + * waiting to be awoken. + */ +struct goldfish_pipe_dev { +	spinlock_t lock; +	unsigned char __iomem *base; +	struct access_params *aps; +	int irq; +}; + +static struct goldfish_pipe_dev   pipe_dev[1]; + +/* This data type models a given pipe instance */ +struct goldfish_pipe { +	struct goldfish_pipe_dev *dev; +	struct mutex lock; +	unsigned long flags; +	wait_queue_head_t wake_queue; +}; + + +/* Bit flags for the 'flags' field */ +enum { +	BIT_CLOSED_ON_HOST = 0,  /* pipe closed by host */ +	BIT_WAKE_ON_WRITE  = 1,  /* want to be woken on writes */ +	BIT_WAKE_ON_READ   = 2,  /* want to be woken on reads */ +}; + + +static u32 goldfish_cmd_status(struct goldfish_pipe *pipe, u32 cmd) +{  +	unsigned long flags; +	u32 status; +	struct goldfish_pipe_dev *dev = pipe->dev; + +	spin_lock_irqsave(&dev->lock, flags); +	writel((u32)pipe, dev->base + PIPE_REG_CHANNEL); +	writel(cmd, dev->base + PIPE_REG_COMMAND); +	status = readl(dev->base + PIPE_REG_STATUS); +	spin_unlock_irqrestore(&dev->lock, flags); +	return status; +} + +static void goldfish_cmd(struct goldfish_pipe *pipe, u32 cmd) +{  +	unsigned long flags; +	struct goldfish_pipe_dev *dev = pipe->dev; + +	spin_lock_irqsave(&dev->lock, flags); +	writel((u32)pipe, dev->base + PIPE_REG_CHANNEL); +	writel(cmd, dev->base + PIPE_REG_COMMAND); +	spin_unlock_irqrestore(&dev->lock, flags); +} + +/* This function converts an error code returned by the emulator through + * the PIPE_REG_STATUS i/o register into a valid negative errno value. + */ +static int goldfish_pipe_error_convert(int status) +{ +	switch (status) { +	case PIPE_ERROR_AGAIN: +		return -EAGAIN; +	case PIPE_ERROR_NOMEM: +		return -ENOMEM; +	case PIPE_ERROR_IO: +		return -EIO; +	default: +		return -EINVAL; +	} +} + +/* + * Notice: QEMU will return 0 for un-known register access, indicating + * param_acess is supported or not + */ +static int valid_batchbuffer_addr(struct goldfish_pipe_dev *dev, +				  struct access_params *aps) +{ +	u32 aph, apl; +	u64 paddr; +	aph = readl(dev->base + PIPE_REG_PARAMS_ADDR_HIGH); +	apl = readl(dev->base + PIPE_REG_PARAMS_ADDR_LOW); + +	paddr = ((u64)aph << 32) | apl; +	if (paddr != (__pa(aps))) +		return 0; +	return 1; +} + +/* 0 on success */ +static int setup_access_params_addr(struct platform_device *pdev, +					struct goldfish_pipe_dev *dev) +{ +	u64 paddr; +	struct access_params *aps; + +	aps = devm_kzalloc(&pdev->dev, sizeof(struct access_params), GFP_KERNEL); +	if (!aps) +		return -1; + +	/* FIXME */ +	paddr = __pa(aps); +	writel((u32)(paddr >> 32), dev->base + PIPE_REG_PARAMS_ADDR_HIGH); +	writel((u32)paddr, dev->base + PIPE_REG_PARAMS_ADDR_LOW); + +	if (valid_batchbuffer_addr(dev, aps)) { +		dev->aps = aps; +		return 0; +	} else +		return -1; +} + +/* A value that will not be set by qemu emulator */ +#define INITIAL_BATCH_RESULT (0xdeadbeaf) +static int access_with_param(struct goldfish_pipe_dev *dev, const int cmd, +				unsigned long address, unsigned long avail, +				struct goldfish_pipe *pipe, int *status) +{ +	struct access_params *aps = dev->aps; + +	if (aps == NULL) +		return -1; + +	aps->result = INITIAL_BATCH_RESULT; +	aps->channel = (unsigned long)pipe; +	aps->size = avail; +	aps->address = address; +	aps->cmd = cmd; +	writel(cmd, dev->base + PIPE_REG_ACCESS_PARAMS); +	/* +	 * If the aps->result has not changed, that means +	 * that the batch command failed +	 */ +	if (aps->result == INITIAL_BATCH_RESULT) +		return -1; +	*status = aps->result; +	return 0; +} + +/* This function is used for both reading from and writing to a given + * pipe. + */ +static ssize_t goldfish_pipe_read_write(struct file *filp, char __user *buffer, +				    size_t bufflen, int is_write) +{ +	unsigned long irq_flags; +	struct goldfish_pipe *pipe = filp->private_data; +	struct goldfish_pipe_dev *dev = pipe->dev; +	const int cmd_offset = is_write ? 0 +					: (CMD_READ_BUFFER - CMD_WRITE_BUFFER); +	unsigned long address, address_end; +	int ret = 0; + +	/* If the emulator already closed the pipe, no need to go further */ +	if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) +		return -EIO; + +	/* Null reads or writes succeeds */ +	if (unlikely(bufflen) == 0) +		return 0; + +	/* Check the buffer range for access */ +	if (!access_ok(is_write ? VERIFY_WRITE : VERIFY_READ, +			buffer, bufflen)) +		return -EFAULT; + +	/* Serialize access to the pipe */ +	if (mutex_lock_interruptible(&pipe->lock)) +		return -ERESTARTSYS; + +	address = (unsigned long)(void *)buffer; +	address_end = address + bufflen; + +	while (address < address_end) { +		unsigned long  page_end = (address & PAGE_MASK) + PAGE_SIZE; +		unsigned long  next     = page_end < address_end ? page_end +								 : address_end; +		unsigned long  avail    = next - address; +		int status, wakeBit; + +		/* Ensure that the corresponding page is properly mapped */ +		/* FIXME: this isn't safe or sufficient - use get_user_pages */ +		if (is_write) { +			char c; +			/* Ensure that the page is mapped and readable */ +			if (__get_user(c, (char __user *)address)) { +				if (!ret) +					ret = -EFAULT; +				break; +			} +		} else { +			/* Ensure that the page is mapped and writable */ +			if (__put_user(0, (char __user *)address)) { +				if (!ret) +					ret = -EFAULT; +				break; +			} +		} + +		/* Now, try to transfer the bytes in the current page */ +		spin_lock_irqsave(&dev->lock, irq_flags); +		if (access_with_param(dev, CMD_WRITE_BUFFER + cmd_offset, +				address, avail, pipe, &status)) { +			writel((u32)pipe, dev->base + PIPE_REG_CHANNEL); +			writel(avail, dev->base + PIPE_REG_SIZE); +			writel(address, dev->base + PIPE_REG_ADDRESS); +			writel(CMD_WRITE_BUFFER + cmd_offset, +					dev->base + PIPE_REG_COMMAND); +			status = readl(dev->base + PIPE_REG_STATUS); +		} +		spin_unlock_irqrestore(&dev->lock, irq_flags); + +		if (status > 0) { /* Correct transfer */ +			ret += status; +			address += status; +			continue; +		} + +		if (status == 0)  /* EOF */ +			break; + +		/* An error occured. If we already transfered stuff, just +		* return with its count. We expect the next call to return +		* an error code */ +		if (ret > 0) +			break; + +		/* If the error is not PIPE_ERROR_AGAIN, or if we are not in +		* non-blocking mode, just return the error code. +		*/ +		if (status != PIPE_ERROR_AGAIN || +			(filp->f_flags & O_NONBLOCK) != 0) { +			ret = goldfish_pipe_error_convert(status); +			break; +		} + +		/* We will have to wait until more data/space is available. +		* First, mark the pipe as waiting for a specific wake signal. +		*/ +		wakeBit = is_write ? BIT_WAKE_ON_WRITE : BIT_WAKE_ON_READ; +		set_bit(wakeBit, &pipe->flags); + +		/* Tell the emulator we're going to wait for a wake event */ +		goldfish_cmd(pipe, CMD_WAKE_ON_WRITE + cmd_offset); + +		/* Unlock the pipe, then wait for the wake signal */ +		mutex_unlock(&pipe->lock); + +		while (test_bit(wakeBit, &pipe->flags)) { +			if (wait_event_interruptible( +					pipe->wake_queue, +					!test_bit(wakeBit, &pipe->flags))) +				return -ERESTARTSYS; + +			if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) +				return -EIO; +		} + +		/* Try to re-acquire the lock */ +		if (mutex_lock_interruptible(&pipe->lock)) +			return -ERESTARTSYS; + +		/* Try the transfer again */ +		continue; +	} +	mutex_unlock(&pipe->lock); +	return ret; +} + +static ssize_t goldfish_pipe_read(struct file *filp, char __user *buffer, +			      size_t bufflen, loff_t *ppos) +{ +	return goldfish_pipe_read_write(filp, buffer, bufflen, 0); +} + +static ssize_t goldfish_pipe_write(struct file *filp, +				const char __user *buffer, size_t bufflen, +				loff_t *ppos) +{ +	return goldfish_pipe_read_write(filp, (char __user *)buffer, +								bufflen, 1); +} + + +static unsigned int goldfish_pipe_poll(struct file *filp, poll_table *wait) +{ +	struct goldfish_pipe *pipe = filp->private_data; +	unsigned int mask = 0; +	int status; + +	mutex_lock(&pipe->lock); + +	poll_wait(filp, &pipe->wake_queue, wait); + +	status = goldfish_cmd_status(pipe, CMD_POLL); + +	mutex_unlock(&pipe->lock); + +	if (status & PIPE_POLL_IN) +		mask |= POLLIN | POLLRDNORM; + +	if (status & PIPE_POLL_OUT) +		mask |= POLLOUT | POLLWRNORM; + +	if (status & PIPE_POLL_HUP) +		mask |= POLLHUP; + +	if (test_bit(BIT_CLOSED_ON_HOST, &pipe->flags)) +		mask |= POLLERR; + +	return mask; +} + +static irqreturn_t goldfish_pipe_interrupt(int irq, void *dev_id) +{ +	struct goldfish_pipe_dev *dev = dev_id; +	unsigned long irq_flags; +	int count = 0; + +	/* We're going to read from the emulator a list of (channel,flags) +	* pairs corresponding to the wake events that occured on each +	* blocked pipe (i.e. channel). +	*/ +	spin_lock_irqsave(&dev->lock, irq_flags); +	for (;;) { +		/* First read the channel, 0 means the end of the list */ +		struct goldfish_pipe *pipe; +		unsigned long wakes; +		unsigned long channel = readl(dev->base + PIPE_REG_CHANNEL); + +		if (channel == 0) +			break; + +		/* Convert channel to struct pipe pointer + read wake flags */ +		wakes = readl(dev->base + PIPE_REG_WAKES); +		pipe  = (struct goldfish_pipe *)(ptrdiff_t)channel; + +		/* Did the emulator just closed a pipe? */ +		if (wakes & PIPE_WAKE_CLOSED) { +			set_bit(BIT_CLOSED_ON_HOST, &pipe->flags); +			wakes |= PIPE_WAKE_READ | PIPE_WAKE_WRITE; +		} +		if (wakes & PIPE_WAKE_READ) +			clear_bit(BIT_WAKE_ON_READ, &pipe->flags); +		if (wakes & PIPE_WAKE_WRITE) +			clear_bit(BIT_WAKE_ON_WRITE, &pipe->flags); + +		wake_up_interruptible(&pipe->wake_queue); +		count++; +	} +	spin_unlock_irqrestore(&dev->lock, irq_flags); + +	return (count == 0) ? IRQ_NONE : IRQ_HANDLED; +} + +/** + *	goldfish_pipe_open	-	open a channel to the AVD + *	@inode: inode of device + *	@file: file struct of opener + * + *	Create a new pipe link between the emulator and the use application. + *	Each new request produces a new pipe. + * + *	Note: we use the pipe ID as a mux. All goldfish emulations are 32bit + *	right now so this is fine. A move to 64bit will need this addressing + */ +static int goldfish_pipe_open(struct inode *inode, struct file *file) +{ +	struct goldfish_pipe *pipe; +	struct goldfish_pipe_dev *dev = pipe_dev; +	int32_t status; + +	/* Allocate new pipe kernel object */ +	pipe = kzalloc(sizeof(*pipe), GFP_KERNEL); +	if (pipe == NULL) +		return -ENOMEM; + +	pipe->dev = dev; +	mutex_init(&pipe->lock); +	init_waitqueue_head(&pipe->wake_queue); + +	/* +	 * Now, tell the emulator we're opening a new pipe. We use the +	 * pipe object's address as the channel identifier for simplicity. +	 */ + +	status = goldfish_cmd_status(pipe, CMD_OPEN); +	if (status < 0) { +		kfree(pipe); +		return status; +	} + +	/* All is done, save the pipe into the file's private data field */ +	file->private_data = pipe; +	return 0; +} + +static int goldfish_pipe_release(struct inode *inode, struct file *filp) +{ +	struct goldfish_pipe *pipe = filp->private_data; + +	/* The guest is closing the channel, so tell the emulator right now */ +	goldfish_cmd(pipe, CMD_CLOSE); +	kfree(pipe); +	filp->private_data = NULL; +	return 0; +} + +static const struct file_operations goldfish_pipe_fops = { +	.owner = THIS_MODULE, +	.read = goldfish_pipe_read, +	.write = goldfish_pipe_write, +	.poll = goldfish_pipe_poll, +	.open = goldfish_pipe_open, +	.release = goldfish_pipe_release, +}; + +static struct miscdevice goldfish_pipe_device = { +	.minor = MISC_DYNAMIC_MINOR, +	.name = "goldfish_pipe", +	.fops = &goldfish_pipe_fops, +}; + +static int goldfish_pipe_probe(struct platform_device *pdev) +{ +	int err; +	struct resource *r; +	struct goldfish_pipe_dev *dev = pipe_dev; + +	/* not thread safe, but this should not happen */ +	WARN_ON(dev->base != NULL); + +	spin_lock_init(&dev->lock); + +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (r == NULL || resource_size(r) < PAGE_SIZE) { +		dev_err(&pdev->dev, "can't allocate i/o page\n"); +		return -EINVAL; +	} +	dev->base = devm_ioremap(&pdev->dev, r->start, PAGE_SIZE); +	if (dev->base == NULL) { +		dev_err(&pdev->dev, "ioremap failed\n"); +		return -EINVAL; +	} + +	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (r == NULL) { +		err = -EINVAL; +		goto error; +	} +	dev->irq = r->start; + +	err = devm_request_irq(&pdev->dev, dev->irq, goldfish_pipe_interrupt, +				IRQF_SHARED, "goldfish_pipe", dev); +	if (err) { +		dev_err(&pdev->dev, "unable to allocate IRQ\n"); +		goto error; +	} + +	err = misc_register(&goldfish_pipe_device); +	if (err) { +		dev_err(&pdev->dev, "unable to register device\n"); +		goto error; +	} +	setup_access_params_addr(pdev, dev); +	return 0; + +error: +	dev->base = NULL; +	return err; +} + +static int goldfish_pipe_remove(struct platform_device *pdev) +{ +	struct goldfish_pipe_dev *dev = pipe_dev; +	misc_deregister(&goldfish_pipe_device); +	dev->base = NULL; +	return 0; +} + +static struct platform_driver goldfish_pipe = { +	.probe = goldfish_pipe_probe, +	.remove = goldfish_pipe_remove, +	.driver = { +		.name = "goldfish_pipe" +	} +}; + +module_platform_driver(goldfish_pipe); +MODULE_AUTHOR("David Turner <digit@google.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/goldfish/pdev_bus.c b/drivers/platform/goldfish/pdev_bus.c new file mode 100644 index 00000000000..92cc4cfafde --- /dev/null +++ b/drivers/platform/goldfish/pdev_bus.c @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2011 Intel, Inc. + * Copyright (C) 2013 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/io.h> + +#define PDEV_BUS_OP_DONE        (0x00) +#define PDEV_BUS_OP_REMOVE_DEV  (0x04) +#define PDEV_BUS_OP_ADD_DEV     (0x08) + +#define PDEV_BUS_OP_INIT        (0x00) + +#define PDEV_BUS_OP             (0x00) +#define PDEV_BUS_GET_NAME       (0x04) +#define PDEV_BUS_NAME_LEN       (0x08) +#define PDEV_BUS_ID             (0x0c) +#define PDEV_BUS_IO_BASE        (0x10) +#define PDEV_BUS_IO_SIZE        (0x14) +#define PDEV_BUS_IRQ            (0x18) +#define PDEV_BUS_IRQ_COUNT      (0x1c) + +struct pdev_bus_dev { +	struct list_head list; +	struct platform_device pdev; +	struct resource resources[0]; +}; + +static void goldfish_pdev_worker(struct work_struct *work); + +static void __iomem *pdev_bus_base; +static unsigned long pdev_bus_addr; +static unsigned long pdev_bus_len; +static u32 pdev_bus_irq; +static LIST_HEAD(pdev_bus_new_devices); +static LIST_HEAD(pdev_bus_registered_devices); +static LIST_HEAD(pdev_bus_removed_devices); +static DECLARE_WORK(pdev_bus_worker, goldfish_pdev_worker); + + +static void goldfish_pdev_worker(struct work_struct *work) +{ +	int ret; +	struct pdev_bus_dev *pos, *n; + +	list_for_each_entry_safe(pos, n, &pdev_bus_removed_devices, list) { +		list_del(&pos->list); +		platform_device_unregister(&pos->pdev); +		kfree(pos); +	} +	list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { +		list_del(&pos->list); +		ret = platform_device_register(&pos->pdev); +		if (ret) +			pr_err("goldfish_pdev_worker failed to register device, %s\n", +								pos->pdev.name); +		list_add_tail(&pos->list, &pdev_bus_registered_devices); +	} +} + +static void goldfish_pdev_remove(void) +{ +	struct pdev_bus_dev *pos, *n; +	u32 base; + +	base = readl(pdev_bus_base + PDEV_BUS_IO_BASE); + +	list_for_each_entry_safe(pos, n, &pdev_bus_new_devices, list) { +		if (pos->resources[0].start == base) { +			list_del(&pos->list); +			kfree(pos); +			return; +		} +	} +	list_for_each_entry_safe(pos, n, &pdev_bus_registered_devices, list) { +		if (pos->resources[0].start == base) { +			list_del(&pos->list); +			list_add_tail(&pos->list, &pdev_bus_removed_devices); +			schedule_work(&pdev_bus_worker); +			return; +		} +	}; +	pr_err("goldfish_pdev_remove could not find device at %x\n", base); +} + +static int goldfish_new_pdev(void) +{ +	struct pdev_bus_dev *dev; +	u32 name_len; +	u32 irq = -1, irq_count; +	int resource_count = 2; +	u32 base; +	char *name; + +	base = readl(pdev_bus_base + PDEV_BUS_IO_BASE); + +	irq_count = readl(pdev_bus_base + PDEV_BUS_IRQ_COUNT); +	name_len = readl(pdev_bus_base + PDEV_BUS_NAME_LEN); +	if (irq_count) +		resource_count++; + +	dev = kzalloc(sizeof(*dev) + +		sizeof(struct resource) * resource_count + +		name_len + 1 + sizeof(*dev->pdev.dev.dma_mask), GFP_ATOMIC); +	if (dev == NULL) +		return -ENOMEM; + +	dev->pdev.num_resources = resource_count; +	dev->pdev.resource = (struct resource *)(dev + 1); +	dev->pdev.name = name = (char *)(dev->pdev.resource + resource_count); +	dev->pdev.dev.coherent_dma_mask = ~0; +	dev->pdev.dev.dma_mask = (void *)(dev->pdev.name + name_len + 1); +	*dev->pdev.dev.dma_mask = ~0; + +	writel((unsigned long)name, pdev_bus_base + PDEV_BUS_GET_NAME); +	name[name_len] = '\0'; +	dev->pdev.id = readl(pdev_bus_base + PDEV_BUS_ID); +	dev->pdev.resource[0].start = base; +	dev->pdev.resource[0].end = base + +				readl(pdev_bus_base + PDEV_BUS_IO_SIZE) - 1; +	dev->pdev.resource[0].flags = IORESOURCE_MEM; +	if (irq_count) { +		irq = readl(pdev_bus_base + PDEV_BUS_IRQ); +		dev->pdev.resource[1].start = irq; +		dev->pdev.resource[1].end = irq + irq_count - 1; +		dev->pdev.resource[1].flags = IORESOURCE_IRQ; +	} + +	pr_debug("goldfish_new_pdev %s at %x irq %d\n", name, base, irq); +	list_add_tail(&dev->list, &pdev_bus_new_devices); +	schedule_work(&pdev_bus_worker); + +	return 0; +} + +static irqreturn_t goldfish_pdev_bus_interrupt(int irq, void *dev_id) +{ +	irqreturn_t ret = IRQ_NONE; +	while (1) { +		u32 op = readl(pdev_bus_base + PDEV_BUS_OP); +		switch (op) { +		case PDEV_BUS_OP_DONE: +			return IRQ_NONE; + +		case PDEV_BUS_OP_REMOVE_DEV: +			goldfish_pdev_remove(); +			break; + +		case PDEV_BUS_OP_ADD_DEV: +			goldfish_new_pdev(); +			break; +		} +		ret = IRQ_HANDLED; +	} +	return ret; +} + +static int goldfish_pdev_bus_probe(struct platform_device *pdev) +{ +	int ret; +	struct resource *r; + +	r = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (r == NULL) +		return -EINVAL; + +	pdev_bus_addr = r->start; +	pdev_bus_len = resource_size(r); + +	if (request_mem_region(pdev_bus_addr, pdev_bus_len, "goldfish")) { +		dev_err(&pdev->dev, "unable to reserve Goldfish MMIO.\n"); +		return -EBUSY; +	} + +	pdev_bus_base = ioremap(pdev_bus_addr, pdev_bus_len); +	if (pdev_bus_base == NULL) { +		ret = -ENOMEM; +		dev_err(&pdev->dev, "unable to map Goldfish MMIO.\n"); +		goto free_resources; +	} + +	r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (r == NULL) { +		ret = -ENOENT; +		goto free_map; +	} + +	pdev_bus_irq = r->start; + +	ret = request_irq(pdev_bus_irq, goldfish_pdev_bus_interrupt, +				IRQF_SHARED, "goldfish_pdev_bus", pdev); +	if (ret) { +		dev_err(&pdev->dev, "unable to request Goldfish IRQ\n"); +		goto free_map; +	} + +	writel(PDEV_BUS_OP_INIT, pdev_bus_base + PDEV_BUS_OP); +	return 0; + +free_map: +	iounmap(pdev_bus_base); +free_resources: +	release_mem_region(pdev_bus_addr, pdev_bus_len); +	return ret; +} + +static int goldfish_pdev_bus_remove(struct platform_device *pdev) +{ +	iounmap(pdev_bus_base); +	free_irq(pdev_bus_irq, pdev); +	release_mem_region(pdev_bus_addr, pdev_bus_len); +	return 0; +} + +static struct platform_driver goldfish_pdev_bus_driver = { +	.probe = goldfish_pdev_bus_probe, +	.remove = goldfish_pdev_bus_remove, +	.driver = { +		.name = "goldfish_pdev_bus" +	} +}; + +module_platform_driver(goldfish_pdev_bus_driver); diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c86bae828c2..3338437b559 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -79,11 +79,21 @@ config ASUS_LAPTOP  	  If you have an ACPI-compatible ASUS laptop, say Y or M here. +config CHROMEOS_LAPTOP +	tristate "Chrome OS Laptop" +	depends on I2C +	depends on DMI +	---help--- +	  This driver instantiates i2c and smbus devices such as +	  light sensors and touchpads. + +	  If you have a supported Chromebook, choose Y or M here. +	  The module will be called chromeos_laptop. +  config DELL_LAPTOP -	tristate "Dell Laptop Extras (EXPERIMENTAL)" +	tristate "Dell Laptop Extras"  	depends on X86  	depends on DCDBAS -	depends on EXPERIMENTAL  	depends on BACKLIGHT_CLASS_DEVICE  	depends on RFKILL || RFKILL = n  	depends on SERIO_I8042 @@ -171,9 +181,8 @@ config AMILO_RFKILL  	  laptops.  config TC1100_WMI -	tristate "HP Compaq TC1100 Tablet WMI Extras (EXPERIMENTAL)" +	tristate "HP Compaq TC1100 Tablet WMI Extras"  	depends on !X86_64 -	depends on EXPERIMENTAL  	depends on ACPI  	depends on ACPI_WMI  	---help--- @@ -290,9 +299,11 @@ config IDEAPAD_LAPTOP  	depends on ACPI  	depends on RFKILL && INPUT  	depends on SERIO_I8042 +	depends on BACKLIGHT_CLASS_DEVICE  	select INPUT_SPARSEKMAP  	help -	  This is a driver for the rfkill switches on Lenovo IdeaPad netbooks. +	  This is a driver for Lenovo IdeaPad netbooks contains drivers for +	  rfkill switch, hotkey, fan control and backlight control.  config THINKPAD_ACPI  	tristate "ThinkPad ACPI Laptop Extras" diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index bf7e4f935b1..ace2b38942f 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -50,3 +50,4 @@ 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 +obj-$(CONFIG_CHROMEOS_LAPTOP)	+= chromeos_laptop.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 934d861a323..c9076bdaf2c 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -125,8 +125,11 @@ static const struct key_entry acer_wmi_keymap[] = {  	{KE_IGNORE, 0x63, {KEY_BRIGHTNESSDOWN} },  	{KE_KEY, 0x64, {KEY_SWITCHVIDEOMODE} },	/* Display Switch */  	{KE_IGNORE, 0x81, {KEY_SLEEP} }, -	{KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} },	/* Touch Pad On/Off */ +	{KE_KEY, 0x82, {KEY_TOUCHPAD_TOGGLE} },	/* Touch Pad Toggle */ +	{KE_KEY, KEY_TOUCHPAD_ON, {KEY_TOUCHPAD_ON} }, +	{KE_KEY, KEY_TOUCHPAD_OFF, {KEY_TOUCHPAD_OFF} },  	{KE_IGNORE, 0x83, {KEY_TOUCHPAD_TOGGLE} }, +	{KE_KEY, 0x85, {KEY_TOUCHPAD_TOGGLE} },  	{KE_END, 0}  }; @@ -147,6 +150,7 @@ struct event_return_value {  #define ACER_WMID3_GDS_THREEG		(1<<6)	/* 3G */  #define ACER_WMID3_GDS_WIMAX		(1<<7)	/* WiMAX */  #define ACER_WMID3_GDS_BLUETOOTH	(1<<11)	/* BT */ +#define ACER_WMID3_GDS_TOUCHPAD		(1<<1)	/* Touchpad */  struct lm_input_params {  	u8 function_num;        /* Function Number */ @@ -335,7 +339,7 @@ static struct quirk_entry quirk_lenovo_ideapad_s205 = {  };  /* The Aspire One has a dummy ACPI-WMI interface - disable it */ -static struct dmi_system_id __devinitdata acer_blacklist[] = { +static struct dmi_system_id acer_blacklist[] = {  	{  		.ident = "Acer Aspire One (SSD)",  		.matches = { @@ -507,6 +511,24 @@ static struct dmi_system_id acer_quirks[] = {  		},  		.driver_data = &quirk_fujitsu_amilo_li_1718,  	}, +	{ +		.callback = dmi_matched, +		.ident = "Lenovo Ideapad S205-10382JG", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "10382JG"), +		}, +		.driver_data = &quirk_lenovo_ideapad_s205, +	}, +	{ +		.callback = dmi_matched, +		.ident = "Lenovo Ideapad S205-1038DPG", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), +			DMI_MATCH(DMI_PRODUCT_NAME, "1038DPG"), +		}, +		.driver_data = &quirk_lenovo_ideapad_s205, +	},  	{}  }; @@ -875,7 +897,7 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)  	struct acpi_buffer input = { (acpi_size) sizeof(u32), (void *)(&in) };  	struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL };  	union acpi_object *obj; -	u32 tmp; +	u32 tmp = 0;  	acpi_status status;  	status = wmi_evaluate_method(WMID_GUID1, 1, method_id, &input, &result); @@ -884,14 +906,14 @@ WMI_execute_u32(u32 method_id, u32 in, u32 *out)  		return status;  	obj = (union acpi_object *) result.pointer; -	if (obj && obj->type == ACPI_TYPE_BUFFER && -		(obj->buffer.length == sizeof(u32) || -		obj->buffer.length == sizeof(u64))) { -		tmp = *((u32 *) obj->buffer.pointer); -	} else if (obj->type == ACPI_TYPE_INTEGER) { -		tmp = (u32) obj->integer.value; -	} else { -		tmp = 0; +	if (obj) { +		if (obj->type == ACPI_TYPE_BUFFER && +			(obj->buffer.length == sizeof(u32) || +			obj->buffer.length == sizeof(u64))) { +			tmp = *((u32 *) obj->buffer.pointer); +		} else if (obj->type == ACPI_TYPE_INTEGER) { +			tmp = (u32) obj->integer.value; +		}  	}  	if (out) @@ -1193,12 +1215,17 @@ static acpi_status WMID_set_capabilities(void)  		return status;  	obj = (union acpi_object *) out.pointer; -	if (obj && obj->type == ACPI_TYPE_BUFFER && -		(obj->buffer.length == sizeof(u32) || -		obj->buffer.length == sizeof(u64))) { -		devices = *((u32 *) obj->buffer.pointer); -	} else if (obj->type == ACPI_TYPE_INTEGER) { -		devices = (u32) obj->integer.value; +	if (obj) { +		if (obj->type == ACPI_TYPE_BUFFER && +			(obj->buffer.length == sizeof(u32) || +			obj->buffer.length == sizeof(u64))) { +			devices = *((u32 *) obj->buffer.pointer); +		} else if (obj->type == ACPI_TYPE_INTEGER) { +			devices = (u32) obj->integer.value; +		} else { +			kfree(out.pointer); +			return AE_ERROR; +		}  	} else {  		kfree(out.pointer);  		return AE_ERROR; @@ -1330,7 +1357,7 @@ static struct led_classdev mail_led = {  	.brightness_set = mail_led_set,  }; -static int __devinit acer_led_init(struct device *dev) +static int acer_led_init(struct device *dev)  {  	return led_classdev_register(dev, &mail_led);  } @@ -1372,7 +1399,7 @@ static const struct backlight_ops acer_bl_ops = {  	.update_status = update_bl_status,  }; -static int __devinit acer_backlight_init(struct device *dev) +static int acer_backlight_init(struct device *dev)  {  	struct backlight_properties props;  	struct backlight_device *bd; @@ -1676,6 +1703,7 @@ static void acer_wmi_notify(u32 value, void *context)  	acpi_status status;  	u16 device_state;  	const struct key_entry *key; +	u32 scancode;  	status = wmi_get_event_data(value, &response);  	if (status != AE_OK) { @@ -1712,6 +1740,7 @@ static void acer_wmi_notify(u32 value, void *context)  			pr_warn("Unknown key number - 0x%x\n",  				return_value.key_num);  		} else { +			scancode = return_value.key_num;  			switch (key->keycode) {  			case KEY_WLAN:  			case KEY_BLUETOOTH: @@ -1725,9 +1754,11 @@ static void acer_wmi_notify(u32 value, void *context)  					rfkill_set_sw_state(bluetooth_rfkill,  						!(device_state & ACER_WMID3_GDS_BLUETOOTH));  				break; +			case KEY_TOUCHPAD_TOGGLE: +				scancode = (device_state & ACER_WMID3_GDS_TOUCHPAD) ? +						KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF;  			} -			sparse_keymap_report_entry(acer_wmi_input_dev, key, -						   1, true); +			sparse_keymap_report_event(acer_wmi_input_dev, scancode, 1, true);  		}  		break;  	case WMID_ACCEL_EVENT: @@ -1946,12 +1977,14 @@ static u32 get_wmid_devices(void)  		return 0;  	obj = (union acpi_object *) out.pointer; -	if (obj && obj->type == ACPI_TYPE_BUFFER && -		(obj->buffer.length == sizeof(u32) || -		obj->buffer.length == sizeof(u64))) { -		devices = *((u32 *) obj->buffer.pointer); -	} else if (obj->type == ACPI_TYPE_INTEGER) { -		devices = (u32) obj->integer.value; +	if (obj) { +		if (obj->type == ACPI_TYPE_BUFFER && +			(obj->buffer.length == sizeof(u32) || +			obj->buffer.length == sizeof(u64))) { +			devices = *((u32 *) obj->buffer.pointer); +		} else if (obj->type == ACPI_TYPE_INTEGER) { +			devices = (u32) obj->integer.value; +		}  	}  	kfree(out.pointer); @@ -1961,7 +1994,7 @@ static u32 get_wmid_devices(void)  /*   * Platform device   */ -static int __devinit acer_platform_probe(struct platform_device *device) +static int acer_platform_probe(struct platform_device *device)  {  	int err; diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c index c2e3e63d2c1..f94467c0522 100644 --- a/drivers/platform/x86/acerhdf.c +++ b/drivers/platform/x86/acerhdf.c @@ -515,7 +515,7 @@ static int acerhdf_suspend(struct device *dev)  	return 0;  } -static int __devinit acerhdf_probe(struct platform_device *device) +static int acerhdf_probe(struct platform_device *device)  {  	return 0;  } diff --git a/drivers/platform/x86/amilo-rfkill.c b/drivers/platform/x86/amilo-rfkill.c index 1deca7f6c4e..6296f078b7b 100644 --- a/drivers/platform/x86/amilo-rfkill.c +++ b/drivers/platform/x86/amilo-rfkill.c @@ -74,7 +74,7 @@ static const struct rfkill_ops amilo_m7440_rfkill_ops = {  	.set_block = amilo_m7440_rfkill_set_block  }; -static const struct dmi_system_id __devinitconst amilo_rfkill_id_table[] = { +static const struct dmi_system_id amilo_rfkill_id_table[] = {  	{  		.matches = {  			DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), @@ -95,7 +95,7 @@ static const struct dmi_system_id __devinitconst amilo_rfkill_id_table[] = {  static struct platform_device *amilo_rfkill_pdev;  static struct rfkill *amilo_rfkill_dev; -static int __devinit amilo_rfkill_probe(struct platform_device *device) +static int amilo_rfkill_probe(struct platform_device *device)  {  	int rc;  	const struct dmi_system_id *system_id = diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index db8f63841b4..f74bfcbb7ba 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -411,8 +411,7 @@ static int gmux_resume(struct pnp_dev *pnp)  	return 0;  } -static int __devinit gmux_probe(struct pnp_dev *pnp, -				const struct pnp_device_id *id) +static int gmux_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)  {  	struct apple_gmux_data *gmux_data;  	struct resource *res; @@ -577,7 +576,7 @@ err_free:  	return ret;  } -static void __devexit gmux_remove(struct pnp_dev *pnp) +static void gmux_remove(struct pnp_dev *pnp)  {  	struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp); @@ -609,7 +608,7 @@ static const struct pnp_device_id gmux_device_ids[] = {  static struct pnp_driver gmux_pnp_driver = {  	.name		= "apple-gmux",  	.probe		= gmux_probe, -	.remove		= __devexit_p(gmux_remove), +	.remove		= gmux_remove,  	.id_table	= gmux_device_ids,  	.suspend	= gmux_suspend,  	.resume		= gmux_resume diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index 4b568df5664..0eea09c1c13 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -128,10 +128,12 @@ MODULE_PARM_DESC(als_status, "Set the ALS status on boot "  /*   * Some events we use, same for all Asus   */ -#define ATKD_BR_UP	0x10	/* (event & ~ATKD_BR_UP) = brightness level */ -#define ATKD_BR_DOWN	0x20	/* (event & ~ATKD_BR_DOWN) = britghness level */ -#define ATKD_BR_MIN	ATKD_BR_UP -#define ATKD_BR_MAX	(ATKD_BR_DOWN | 0xF)	/* 0x2f */ +#define ATKD_BRNUP_MIN		0x10 +#define ATKD_BRNUP_MAX		0x1f +#define ATKD_BRNDOWN_MIN	0x20 +#define ATKD_BRNDOWN_MAX	0x2f +#define ATKD_BRNDOWN		0x20 +#define ATKD_BRNUP		0x2f  #define ATKD_LCD_ON	0x33  #define ATKD_LCD_OFF	0x34 @@ -301,40 +303,65 @@ static const struct key_entry asus_keymap[] = {  	{KE_KEY, 0x17, { KEY_ZOOM } },  	{KE_KEY, 0x1f, { KEY_BATTERY } },  	/* End of Lenovo SL Specific keycodes */ +	{KE_KEY, ATKD_BRNDOWN, { KEY_BRIGHTNESSDOWN } }, +	{KE_KEY, ATKD_BRNUP, { KEY_BRIGHTNESSUP } },  	{KE_KEY, 0x30, { KEY_VOLUMEUP } },  	{KE_KEY, 0x31, { KEY_VOLUMEDOWN } },  	{KE_KEY, 0x32, { KEY_MUTE } }, -	{KE_KEY, 0x33, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x34, { KEY_SWITCHVIDEOMODE } }, +	{KE_KEY, 0x33, { KEY_DISPLAYTOGGLE } }, /* LCD on */ +	{KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */  	{KE_KEY, 0x40, { KEY_PREVIOUSSONG } },  	{KE_KEY, 0x41, { KEY_NEXTSONG } }, -	{KE_KEY, 0x43, { KEY_STOPCD } }, +	{KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */  	{KE_KEY, 0x45, { KEY_PLAYPAUSE } }, -	{KE_KEY, 0x4c, { KEY_MEDIA } }, +	{KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */  	{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_SCREENLOCK } },  /* Screenlock */ -	{KE_KEY, 0x5D, { KEY_WLAN } }, -	{KE_KEY, 0x5E, { KEY_WLAN } }, -	{KE_KEY, 0x5F, { KEY_WLAN } }, -	{KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, -	{KE_KEY, 0x6B, { KEY_F13 } }, /* Lock Touchpad */ +	{KE_KEY, 0x5D, { KEY_WLAN } }, /* WLAN Toggle */ +	{KE_KEY, 0x5E, { KEY_WLAN } }, /* WLAN Enable */ +	{KE_KEY, 0x5F, { KEY_WLAN } }, /* WLAN Disable */ +	{KE_KEY, 0x60, { KEY_TOUCHPAD_ON } }, +	{KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */ +	{KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */ +	{KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */ +	{KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */ +	{KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */ +	{KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */ +	{KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */ +	{KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, /* 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_IGNORE, 0x6E, },  /* Low Battery notification */ +	{KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ +	{KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */  	{KE_KEY, 0x82, { KEY_CAMERA } }, -	{KE_KEY, 0x88, { KEY_WLAN  } }, -	{KE_KEY, 0x8A, { KEY_PROG1 } }, +	{KE_KEY, 0x88, { KEY_RFKILL  } }, /* Radio Toggle Key */ +	{KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */ +	{KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */ +	{KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */ +	{KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */ +	{KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */ +	{KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */ +	{KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */ +	{KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ +	{KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */  	{KE_KEY, 0x95, { KEY_MEDIA } },  	{KE_KEY, 0x99, { KEY_PHONE } }, -	{KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, -	{KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, -	{KE_KEY, 0xb5, { KEY_CALC } }, +	{KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */ +	{KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */ +	{KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */ +	{KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */ +	{KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */ +	{KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */ +	{KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */ +	{KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */ +	{KE_KEY, 0xB5, { KEY_CALC } }, +	{KE_KEY, 0xC4, { KEY_KBDILLUMUP } }, +	{KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },  	{KE_END, 0},  }; @@ -860,8 +887,10 @@ static ssize_t show_infos(struct device *dev,  	/*  	 * The HWRS method return informations about the hardware.  	 * 0x80 bit is for WLAN, 0x100 for Bluetooth. +	 * 0x40 for WWAN, 0x10 for WIMAX.  	 * The significance of others is yet to be found. -	 * If we don't find the method, we assume the device are present. +	 * We don't currently use this for device detection, and it +	 * takes several seconds to run on some systems.  	 */  	rv = acpi_evaluate_integer(asus->handle, "HWRS", NULL, &temp);  	if (!ACPI_FAILURE(rv)) @@ -1519,15 +1548,19 @@ static void asus_acpi_notify(struct acpi_device *device, u32 event)  					dev_name(&asus->device->dev), event,  					count); -	/* Brightness events are special */ -	if (event >= ATKD_BR_MIN && event <= ATKD_BR_MAX) { +	if (event >= ATKD_BRNUP_MIN && event <= ATKD_BRNUP_MAX) +		event = ATKD_BRNUP; +	else if (event >= ATKD_BRNDOWN_MIN && +		 event <= ATKD_BRNDOWN_MAX) +		event = ATKD_BRNDOWN; -		/* Ignore them completely if the acpi video driver is used */ +	/* Brightness events are special */ +	if (event == ATKD_BRNDOWN || event == ATKD_BRNUP) {  		if (asus->backlight_device != NULL) {  			/* Update the backlight device. */  			asus_backlight_notify(asus); +			return ;  		} -		return ;  	}  	/* Accelerometer "coarse orientation change" event */ @@ -1682,7 +1715,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  {  	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };  	union acpi_object *model = NULL; -	unsigned long long bsts_result, hwrs_result; +	unsigned long long bsts_result;  	char *string = NULL;  	acpi_status status; @@ -1741,20 +1774,9 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  		return -ENOMEM;  	} -	if (*string) +	if (string)  		pr_notice("  %s model detected\n", string); -	/* -	 * The HWRS method return informations about the hardware. -	 * 0x80 bit is for WLAN, 0x100 for Bluetooth, -	 * 0x40 for WWAN, 0x10 for WIMAX. -	 * The significance of others is yet to be found. -	 */ -	status = -	    acpi_evaluate_integer(asus->handle, "HWRS", NULL, &hwrs_result); -	if (!ACPI_FAILURE(status)) -		pr_notice("  HWRS returned %x", (int)hwrs_result); -  	if (!acpi_check_handle(asus->handle, METHOD_WL_STATUS, NULL))  		asus->have_rsts = true; @@ -1763,7 +1785,7 @@ static int asus_laptop_get_info(struct asus_laptop *asus)  	return AE_OK;  } -static int __devinit asus_acpi_init(struct asus_laptop *asus) +static int asus_acpi_init(struct asus_laptop *asus)  {  	int result = 0; @@ -1823,7 +1845,7 @@ static int __devinit asus_acpi_init(struct asus_laptop *asus)  	return result;  } -static void __devinit asus_dmi_check(void) +static void asus_dmi_check(void)  {  	const char *model; @@ -1839,7 +1861,7 @@ static void __devinit asus_dmi_check(void)  static bool asus_device_present; -static int __devinit asus_acpi_add(struct acpi_device *device) +static int asus_acpi_add(struct acpi_device *device)  {  	struct asus_laptop *asus;  	int result; @@ -1919,7 +1941,7 @@ fail_platform:  	return result;  } -static int asus_acpi_remove(struct acpi_device *device, int type) +static int asus_acpi_remove(struct acpi_device *device)  {  	struct asus_laptop *asus = acpi_driver_data(device); diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index be790402e0f..210b5b87212 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -59,6 +59,17 @@ static struct quirk_entry quirk_asus_unknown = {  	.wapf = 0,  }; +/* + * For those machines that need software to control bt/wifi status + * and can't adjust brightness through ACPI interface + * and have duplicate events(ACPI and WMI) for display toggle + */ +static struct quirk_entry quirk_asus_x55u = { +	.wapf = 4, +	.wmi_backlight_power = true, +	.no_display_toggle = true, +}; +  static struct quirk_entry quirk_asus_x401u = {  	.wapf = 4,  }; @@ -77,6 +88,15 @@ static struct dmi_system_id asus_quirks[] = {  			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),  			DMI_MATCH(DMI_PRODUCT_NAME, "X401U"),  		}, +		.driver_data = &quirk_asus_x55u, +	}, +	{ +		.callback = dmi_matched, +		.ident = "ASUSTeK COMPUTER INC. X401A", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), +			DMI_MATCH(DMI_PRODUCT_NAME, "X401A"), +		},  		.driver_data = &quirk_asus_x401u,  	},  	{ @@ -95,6 +115,15 @@ static struct dmi_system_id asus_quirks[] = {  			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),  			DMI_MATCH(DMI_PRODUCT_NAME, "X501U"),  		}, +		.driver_data = &quirk_asus_x55u, +	}, +	{ +		.callback = dmi_matched, +		.ident = "ASUSTeK COMPUTER INC. X501A", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), +			DMI_MATCH(DMI_PRODUCT_NAME, "X501A"), +		},  		.driver_data = &quirk_asus_x401u,  	},  	{ @@ -131,7 +160,7 @@ static struct dmi_system_id asus_quirks[] = {  			DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),  			DMI_MATCH(DMI_PRODUCT_NAME, "X55U"),  		}, -		.driver_data = &quirk_asus_x401u, +		.driver_data = &quirk_asus_x55u,  	},  	{  		.callback = dmi_matched, @@ -161,6 +190,8 @@ static void asus_nb_wmi_quirks(struct asus_wmi_driver *driver)  }  static const struct key_entry asus_nb_wmi_keymap[] = { +	{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } }, +	{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },  	{ KE_KEY, 0x30, { KEY_VOLUMEUP } },  	{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } },  	{ KE_KEY, 0x32, { KEY_MUTE } }, @@ -168,9 +199,9 @@ static const struct key_entry asus_nb_wmi_keymap[] = {  	{ KE_KEY, 0x34, { KEY_DISPLAY_OFF } }, /* LCD off */  	{ KE_KEY, 0x40, { KEY_PREVIOUSSONG } },  	{ KE_KEY, 0x41, { KEY_NEXTSONG } }, -	{ KE_KEY, 0x43, { KEY_STOPCD } }, +	{ KE_KEY, 0x43, { KEY_STOPCD } }, /* Stop/Eject */  	{ KE_KEY, 0x45, { KEY_PLAYPAUSE } }, -	{ KE_KEY, 0x4c, { KEY_MEDIA } }, +	{ KE_KEY, 0x4c, { KEY_MEDIA } }, /* WMP Key */  	{ KE_KEY, 0x50, { KEY_EMAIL } },  	{ KE_KEY, 0x51, { KEY_WWW } },  	{ KE_KEY, 0x55, { KEY_CALC } }, @@ -180,25 +211,42 @@ static const struct key_entry asus_nb_wmi_keymap[] = {  	{ KE_KEY, 0x5D, { KEY_WLAN } }, /* Wireless console Toggle */  	{ KE_KEY, 0x5E, { KEY_WLAN } }, /* Wireless console Enable */  	{ KE_KEY, 0x5F, { KEY_WLAN } }, /* Wireless console Disable */ -	{ KE_KEY, 0x60, { KEY_SWITCHVIDEOMODE } }, -	{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, -	{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, -	{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, +	{ KE_KEY, 0x60, { KEY_TOUCHPAD_ON } }, +	{ KE_KEY, 0x61, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD only */ +	{ KE_KEY, 0x62, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT only */ +	{ KE_KEY, 0x63, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT */ +	{ KE_KEY, 0x64, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV */ +	{ KE_KEY, 0x65, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV */ +	{ KE_KEY, 0x66, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV */ +	{ KE_KEY, 0x67, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV */  	{ KE_KEY, 0x6B, { KEY_TOUCHPAD_TOGGLE } }, -	{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, -	{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, +	{ KE_IGNORE, 0x6E, },  /* Low Battery notification */ +	{ KE_KEY, 0x7D, { KEY_BLUETOOTH } }, /* Bluetooth Enable */ +	{ KE_KEY, 0x7E, { KEY_BLUETOOTH } }, /* Bluetooth Disable */  	{ KE_KEY, 0x82, { KEY_CAMERA } }, -	{ KE_KEY, 0x88, { KEY_RFKILL  } }, -	{ KE_KEY, 0x8A, { KEY_PROG1 } }, +	{ KE_KEY, 0x88, { KEY_RFKILL  } }, /* Radio Toggle Key */ +	{ KE_KEY, 0x8A, { KEY_PROG1 } }, /* Color enhancement mode */ +	{ KE_KEY, 0x8C, { KEY_SWITCHVIDEOMODE } }, /* SDSP DVI only */ +	{ KE_KEY, 0x8D, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + DVI */ +	{ KE_KEY, 0x8E, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + DVI */ +	{ KE_KEY, 0x8F, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + DVI */ +	{ KE_KEY, 0x90, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + DVI */ +	{ KE_KEY, 0x91, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + DVI */ +	{ KE_KEY, 0x92, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + DVI */ +	{ KE_KEY, 0x93, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + DVI */  	{ KE_KEY, 0x95, { KEY_MEDIA } },  	{ KE_KEY, 0x99, { KEY_PHONE } },  	{ KE_KEY, 0xA0, { KEY_SWITCHVIDEOMODE } }, /* SDSP HDMI only */  	{ KE_KEY, 0xA1, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + HDMI */  	{ KE_KEY, 0xA2, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + HDMI */  	{ KE_KEY, 0xA3, { KEY_SWITCHVIDEOMODE } }, /* SDSP TV + HDMI */ -	{ KE_KEY, 0xb5, { KEY_CALC } }, -	{ KE_KEY, 0xc4, { KEY_KBDILLUMUP } }, -	{ KE_KEY, 0xc5, { KEY_KBDILLUMDOWN } }, +	{ KE_KEY, 0xA4, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + HDMI */ +	{ KE_KEY, 0xA5, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + TV + HDMI */ +	{ KE_KEY, 0xA6, { KEY_SWITCHVIDEOMODE } }, /* SDSP CRT + TV + HDMI */ +	{ KE_KEY, 0xA7, { KEY_SWITCHVIDEOMODE } }, /* SDSP LCD + CRT + TV + HDMI */ +	{ KE_KEY, 0xB5, { KEY_CALC } }, +	{ KE_KEY, 0xC4, { KEY_KBDILLUMUP } }, +	{ KE_KEY, 0xC5, { KEY_KBDILLUMDOWN } },  	{ KE_END, 0},  }; diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f80ae4d10f6..c11b2426dac 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -187,6 +187,8 @@ struct asus_wmi {  	struct device *hwmon_device;  	struct platform_device *platform_device; +	struct led_classdev wlan_led; +	int wlan_led_wk;  	struct led_classdev tpd_led;  	int tpd_led_wk;  	struct led_classdev kbd_led; @@ -194,6 +196,7 @@ struct asus_wmi {  	struct workqueue_struct *led_workqueue;  	struct work_struct tpd_led_work;  	struct work_struct kbd_led_work; +	struct work_struct wlan_led_work;  	struct asus_rfkill wlan;  	struct asus_rfkill bluetooth; @@ -456,12 +459,65 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev)  	return value;  } +static int wlan_led_unknown_state(struct asus_wmi *asus) +{ +	u32 result; + +	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result); + +	return result & ASUS_WMI_DSTS_UNKNOWN_BIT; +} + +static int wlan_led_presence(struct asus_wmi *asus) +{ +	u32 result; + +	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result); + +	return result & ASUS_WMI_DSTS_PRESENCE_BIT; +} + +static void wlan_led_update(struct work_struct *work) +{ +	int ctrl_param; +	struct asus_wmi *asus; + +	asus = container_of(work, struct asus_wmi, wlan_led_work); + +	ctrl_param = asus->wlan_led_wk; +	asus_wmi_set_devstate(ASUS_WMI_DEVID_WIRELESS_LED, ctrl_param, NULL); +} + +static void wlan_led_set(struct led_classdev *led_cdev, +			 enum led_brightness value) +{ +	struct asus_wmi *asus; + +	asus = container_of(led_cdev, struct asus_wmi, wlan_led); + +	asus->wlan_led_wk = !!value; +	queue_work(asus->led_workqueue, &asus->wlan_led_work); +} + +static enum led_brightness wlan_led_get(struct led_classdev *led_cdev) +{ +	struct asus_wmi *asus; +	u32 result; + +	asus = container_of(led_cdev, struct asus_wmi, wlan_led); +	asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WIRELESS_LED, &result); + +	return result & ASUS_WMI_DSTS_BRIGHTNESS_MASK; +} +  static void asus_wmi_led_exit(struct asus_wmi *asus)  {  	if (!IS_ERR_OR_NULL(asus->kbd_led.dev))  		led_classdev_unregister(&asus->kbd_led);  	if (!IS_ERR_OR_NULL(asus->tpd_led.dev))  		led_classdev_unregister(&asus->tpd_led); +	if (!IS_ERR_OR_NULL(asus->wlan_led.dev)) +		led_classdev_unregister(&asus->wlan_led);  	if (asus->led_workqueue)  		destroy_workqueue(asus->led_workqueue);  } @@ -498,6 +554,23 @@ static int asus_wmi_led_init(struct asus_wmi *asus)  		rv = led_classdev_register(&asus->platform_device->dev,  					   &asus->kbd_led); +		if (rv) +			goto error; +	} + +	if (wlan_led_presence(asus)) { +		INIT_WORK(&asus->wlan_led_work, wlan_led_update); + +		asus->wlan_led.name = "asus::wlan"; +		asus->wlan_led.brightness_set = wlan_led_set; +		if (!wlan_led_unknown_state(asus)) +			asus->wlan_led.brightness_get = wlan_led_get; +		asus->wlan_led.flags = LED_CORE_SUSPENDRESUME; +		asus->wlan_led.max_brightness = 1; +		asus->wlan_led.default_trigger = "asus-wlan"; + +		rv = led_classdev_register(&asus->platform_device->dev, +					   &asus->wlan_led);  	}  error: @@ -813,6 +886,9 @@ static int asus_new_rfkill(struct asus_wmi *asus,  	if (!*rfkill)  		return -EINVAL; +	if (dev_id == ASUS_WMI_DEVID_WLAN) +		rfkill_set_led_trigger_name(*rfkill, "asus-wlan"); +  	rfkill_init_sw_state(*rfkill, !result);  	result = rfkill_register(*rfkill);  	if (result) { @@ -1265,6 +1341,18 @@ static void asus_wmi_backlight_exit(struct asus_wmi *asus)  	asus->backlight_device = NULL;  } +static int is_display_toggle(int code) +{ +	/* display toggle keys */ +	if ((code >= 0x61 && code <= 0x67) || +	    (code >= 0x8c && code <= 0x93) || +	    (code >= 0xa0 && code <= 0xa7) || +	    (code >= 0xd0 && code <= 0xd5)) +		return 1; + +	return 0; +} +  static void asus_wmi_notify(u32 value, void *context)  {  	struct asus_wmi *asus = context; @@ -1298,16 +1386,24 @@ static void asus_wmi_notify(u32 value, void *context)  	}  	if (code >= NOTIFY_BRNUP_MIN && code <= NOTIFY_BRNUP_MAX) -		code = NOTIFY_BRNUP_MIN; +		code = ASUS_WMI_BRN_UP;  	else if (code >= NOTIFY_BRNDOWN_MIN &&  		 code <= NOTIFY_BRNDOWN_MAX) -		code = NOTIFY_BRNDOWN_MIN; +		code = ASUS_WMI_BRN_DOWN; -	if (code == NOTIFY_BRNUP_MIN || code == NOTIFY_BRNDOWN_MIN) { -		if (!acpi_video_backlight_support()) +	if (code == ASUS_WMI_BRN_DOWN || code == ASUS_WMI_BRN_UP) { +		if (!acpi_video_backlight_support()) {  			asus_wmi_backlight_notify(asus, orig_code); -	} else if (!sparse_keymap_report_event(asus->inputdev, code, -					       key_value, autorelease)) +			goto exit; +		} +	} + +	if (is_display_toggle(code) && +	    asus->driver->quirks->no_display_toggle) +		goto exit; + +	if (!sparse_keymap_report_event(asus->inputdev, code, +					key_value, autorelease))  		pr_info("Unknown key %x pressed\n", code);  exit: diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h index 4c9bd38bb0a..4da4c8bafe7 100644 --- a/drivers/platform/x86/asus-wmi.h +++ b/drivers/platform/x86/asus-wmi.h @@ -30,6 +30,8 @@  #include <linux/platform_device.h>  #define ASUS_WMI_KEY_IGNORE (-1) +#define ASUS_WMI_BRN_DOWN	0x20 +#define ASUS_WMI_BRN_UP		0x2f  struct module;  struct key_entry; @@ -41,6 +43,13 @@ struct quirk_entry {  	bool store_backlight_power;  	bool wmi_backlight_power;  	int wapf; +	/* +	 * For machines with AMD graphic chips, it will send out WMI event +	 * and ACPI interrupt at the same time while hitting the hotkey. +	 * To simplify the problem, we just have to ignore the WMI event, +	 * and let the ACPI interrupt to send out the key event. +	 */ +	int no_display_toggle;  };  struct asus_wmi_driver { diff --git a/drivers/platform/x86/chromeos_laptop.c b/drivers/platform/x86/chromeos_laptop.c new file mode 100644 index 00000000000..93d66809355 --- /dev/null +++ b/drivers/platform/x86/chromeos_laptop.c @@ -0,0 +1,371 @@ +/* + *  chromeos_laptop.c - Driver to instantiate Chromebook i2c/smbus devices. + * + *  Author : Benson Leung <bleung@chromium.org> + * + *  Copyright (C) 2012 Google, Inc. + * + *  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 + * + */ + +#include <linux/dmi.h> +#include <linux/i2c.h> +#include <linux/module.h> + +#define ATMEL_TP_I2C_ADDR	0x4b +#define ATMEL_TP_I2C_BL_ADDR	0x25 +#define ATMEL_TS_I2C_ADDR	0x4a +#define ATMEL_TS_I2C_BL_ADDR	0x26 +#define CYAPA_TP_I2C_ADDR	0x67 +#define ISL_ALS_I2C_ADDR	0x44 +#define TAOS_ALS_I2C_ADDR	0x29 + +static struct i2c_client *als; +static struct i2c_client *tp; +static struct i2c_client *ts; + +const char *i2c_adapter_names[] = { +	"SMBus I801 adapter", +	"i915 gmbus vga", +	"i915 gmbus panel", +}; + +/* Keep this enum consistent with i2c_adapter_names */ +enum i2c_adapter_type { +	I2C_ADAPTER_SMBUS = 0, +	I2C_ADAPTER_VGADDC, +	I2C_ADAPTER_PANEL, +}; + +static struct i2c_board_info __initdata cyapa_device = { +	I2C_BOARD_INFO("cyapa", CYAPA_TP_I2C_ADDR), +	.flags		= I2C_CLIENT_WAKE, +}; + +static struct i2c_board_info __initdata isl_als_device = { +	I2C_BOARD_INFO("isl29018", ISL_ALS_I2C_ADDR), +}; + +static struct i2c_board_info __initdata tsl2583_als_device = { +	I2C_BOARD_INFO("tsl2583", TAOS_ALS_I2C_ADDR), +}; + +static struct i2c_board_info __initdata tsl2563_als_device = { +	I2C_BOARD_INFO("tsl2563", TAOS_ALS_I2C_ADDR), +}; + +static struct i2c_board_info __initdata atmel_224s_tp_device = { +	I2C_BOARD_INFO("atmel_mxt_tp", ATMEL_TP_I2C_ADDR), +	.platform_data = NULL, +	.flags		= I2C_CLIENT_WAKE, +}; + +static struct i2c_board_info __initdata atmel_1664s_device = { +	I2C_BOARD_INFO("atmel_mxt_ts", ATMEL_TS_I2C_ADDR), +	.platform_data = NULL, +	.flags		= I2C_CLIENT_WAKE, +}; + +static struct i2c_client __init *__add_probed_i2c_device( +		const char *name, +		int bus, +		struct i2c_board_info *info, +		const unsigned short *addrs) +{ +	const struct dmi_device *dmi_dev; +	const struct dmi_dev_onboard *dev_data; +	struct i2c_adapter *adapter; +	struct i2c_client *client; + +	if (bus < 0) +		return NULL; +	/* +	 * If a name is specified, look for irq platform information stashed +	 * in DMI_DEV_TYPE_DEV_ONBOARD by the Chrome OS custom system firmware. +	 */ +	if (name) { +		dmi_dev = dmi_find_device(DMI_DEV_TYPE_DEV_ONBOARD, name, NULL); +		if (!dmi_dev) { +			pr_err("%s failed to dmi find device %s.\n", +			       __func__, +			       name); +			return NULL; +		} +		dev_data = (struct dmi_dev_onboard *)dmi_dev->device_data; +		if (!dev_data) { +			pr_err("%s failed to get data from dmi for %s.\n", +			       __func__, name); +			return NULL; +		} +		info->irq = dev_data->instance; +	} + +	adapter = i2c_get_adapter(bus); +	if (!adapter) { +		pr_err("%s failed to get i2c adapter %d.\n", __func__, bus); +		return NULL; +	} + +	/* add the i2c device */ +	client = i2c_new_probed_device(adapter, info, addrs, NULL); +	if (!client) +		pr_err("%s failed to register device %d-%02x\n", +		       __func__, bus, info->addr); +	else +		pr_debug("%s added i2c device %d-%02x\n", +			 __func__, bus, info->addr); + +	i2c_put_adapter(adapter); +	return client; +} + +static int __init __find_i2c_adap(struct device *dev, void *data) +{ +	const char *name = data; +	static const char *prefix = "i2c-"; +	struct i2c_adapter *adapter; +	if (strncmp(dev_name(dev), prefix, strlen(prefix)) != 0) +		return 0; +	adapter = to_i2c_adapter(dev); +	return (strncmp(adapter->name, name, strlen(name)) == 0); +} + +static int __init find_i2c_adapter_num(enum i2c_adapter_type type) +{ +	struct device *dev = NULL; +	struct i2c_adapter *adapter; +	const char *name = i2c_adapter_names[type]; +	/* find the adapter by name */ +	dev = bus_find_device(&i2c_bus_type, NULL, (void *)name, +			      __find_i2c_adap); +	if (!dev) { +		pr_err("%s: i2c adapter %s not found on system.\n", __func__, +		       name); +		return -ENODEV; +	} +	adapter = to_i2c_adapter(dev); +	return adapter->nr; +} + +/* + * Takes a list of addresses in addrs as such : + * { addr1, ... , addrn, I2C_CLIENT_END }; + * add_probed_i2c_device will use i2c_new_probed_device + * and probe for devices at all of the addresses listed. + * Returns NULL if no devices found. + * See Documentation/i2c/instantiating-devices for more information. + */ +static __init struct i2c_client *add_probed_i2c_device( +		const char *name, +		enum i2c_adapter_type type, +		struct i2c_board_info *info, +		const unsigned short *addrs) +{ +	return __add_probed_i2c_device(name, +				       find_i2c_adapter_num(type), +				       info, +				       addrs); +} + +/* + * Probes for a device at a single address, the one provided by + * info->addr. + * Returns NULL if no device found. + */ +static __init struct i2c_client *add_i2c_device(const char *name, +						enum i2c_adapter_type type, +						struct i2c_board_info *info) +{ +	const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; +	return __add_probed_i2c_device(name, +				       find_i2c_adapter_num(type), +				       info, +				       addr_list); +} + + +static struct i2c_client __init *add_smbus_device(const char *name, +						  struct i2c_board_info *info) +{ +	return add_i2c_device(name, I2C_ADAPTER_SMBUS, info); +} + +static int __init setup_cyapa_smbus_tp(const struct dmi_system_id *id) +{ +	/* add cyapa touchpad on smbus */ +	tp = add_smbus_device("trackpad", &cyapa_device); +	return 0; +} + +static int __init setup_atmel_224s_tp(const struct dmi_system_id *id) +{ +	const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR, +					     ATMEL_TP_I2C_ADDR, +					     I2C_CLIENT_END }; + +	/* add atmel mxt touchpad on VGA DDC GMBus */ +	tp = add_probed_i2c_device("trackpad", I2C_ADAPTER_VGADDC, +				   &atmel_224s_tp_device, addr_list); +	return 0; +} + +static int __init setup_atmel_1664s_ts(const struct dmi_system_id *id) +{ +	const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, +					     ATMEL_TS_I2C_ADDR, +					     I2C_CLIENT_END }; + +	/* add atmel mxt touch device on PANEL GMBus */ +	ts = add_probed_i2c_device("touchscreen", I2C_ADAPTER_PANEL, +				   &atmel_1664s_device, addr_list); +	return 0; +} + + +static int __init setup_isl29018_als(const struct dmi_system_id *id) +{ +	/* add isl29018 light sensor */ +	als = add_smbus_device("lightsensor", &isl_als_device); +	return 0; +} + +static int __init setup_isl29023_als(const struct dmi_system_id *id) +{ +	/* add isl29023 light sensor on Panel GMBus */ +	als = add_i2c_device("lightsensor", I2C_ADAPTER_PANEL, +			     &isl_als_device); +	return 0; +} + +static int __init setup_tsl2583_als(const struct dmi_system_id *id) +{ +	/* add tsl2583 light sensor on smbus */ +	als = add_smbus_device(NULL, &tsl2583_als_device); +	return 0; +} + +static int __init setup_tsl2563_als(const struct dmi_system_id *id) +{ +	/* add tsl2563 light sensor on smbus */ +	als = add_smbus_device(NULL, &tsl2563_als_device); +	return 0; +} + +static struct dmi_system_id __initdata chromeos_laptop_dmi_table[] = { +	{ +		.ident = "Samsung Series 5 550 - Touchpad", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), +		}, +		.callback = setup_cyapa_smbus_tp, +	}, +	{ +		.ident = "Chromebook Pixel - Touchscreen", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Link"), +		}, +		.callback = setup_atmel_1664s_ts, +	}, +	{ +		.ident = "Chromebook Pixel - Touchpad", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Link"), +		}, +		.callback = setup_atmel_224s_tp, +	}, +	{ +		.ident = "Samsung Series 5 550 - Light Sensor", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Lumpy"), +		}, +		.callback = setup_isl29018_als, +	}, +	{ +		.ident = "Chromebook Pixel - Light Sensor", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), +			DMI_MATCH(DMI_PRODUCT_NAME, "Link"), +		}, +		.callback = setup_isl29023_als, +	}, +	{ +		.ident = "Acer C7 Chromebook - Touchpad", +		.matches = { +			DMI_MATCH(DMI_PRODUCT_NAME, "Parrot"), +		}, +		.callback = setup_cyapa_smbus_tp, +	}, +	{ +		.ident = "HP Pavilion 14 Chromebook - Touchpad", +		.matches = { +			DMI_MATCH(DMI_PRODUCT_NAME, "Butterfly"), +		}, +		.callback = setup_cyapa_smbus_tp, +	}, +	{ +		.ident = "Samsung Series 5 - Light Sensor", +		.matches = { +			DMI_MATCH(DMI_PRODUCT_NAME, "Alex"), +		}, +		.callback = setup_tsl2583_als, +	}, +	{ +		.ident = "Cr-48 - Light Sensor", +		.matches = { +			DMI_MATCH(DMI_PRODUCT_NAME, "Mario"), +		}, +		.callback = setup_tsl2563_als, +	}, +	{ +		.ident = "Acer AC700 - Light Sensor", +		.matches = { +			DMI_MATCH(DMI_PRODUCT_NAME, "ZGB"), +		}, +		.callback = setup_tsl2563_als, +	}, +	{ } +}; +MODULE_DEVICE_TABLE(dmi, chromeos_laptop_dmi_table); + +static int __init chromeos_laptop_init(void) +{ +	if (!dmi_check_system(chromeos_laptop_dmi_table)) { +		pr_debug("%s unsupported system.\n", __func__); +		return -ENODEV; +	} +	return 0; +} + +static void __exit chromeos_laptop_exit(void) +{ +	if (als) +		i2c_unregister_device(als); +	if (tp) +		i2c_unregister_device(tp); +	if (ts) +		i2c_unregister_device(ts); +} + +module_init(chromeos_laptop_init); +module_exit(chromeos_laptop_exit); + +MODULE_DESCRIPTION("Chrome OS Laptop driver"); +MODULE_AUTHOR("Benson Leung <bleung@chromium.org>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c index c87ff16873f..36e5e6c13db 100644 --- a/drivers/platform/x86/classmate-laptop.c +++ b/drivers/platform/x86/classmate-laptop.c @@ -432,7 +432,7 @@ failed_sensitivity:  	return error;  } -static int cmpc_accel_remove_v4(struct acpi_device *acpi, int type) +static int cmpc_accel_remove_v4(struct acpi_device *acpi)  {  	struct input_dev *inputdev;  	struct cmpc_accel *accel; @@ -668,7 +668,7 @@ failed_file:  	return error;  } -static int cmpc_accel_remove(struct acpi_device *acpi, int type) +static int cmpc_accel_remove(struct acpi_device *acpi)  {  	struct input_dev *inputdev;  	struct cmpc_accel *accel; @@ -753,7 +753,7 @@ static int cmpc_tablet_add(struct acpi_device *acpi)  					   cmpc_tablet_idev_init);  } -static int cmpc_tablet_remove(struct acpi_device *acpi, int type) +static int cmpc_tablet_remove(struct acpi_device *acpi)  {  	return cmpc_remove_acpi_notify_device(acpi);  } @@ -1000,7 +1000,7 @@ out_bd:  	return retval;  } -static int cmpc_ipml_remove(struct acpi_device *acpi, int type) +static int cmpc_ipml_remove(struct acpi_device *acpi)  {  	struct ipml200_dev *ipml; @@ -1079,7 +1079,7 @@ static int cmpc_keys_add(struct acpi_device *acpi)  					   cmpc_keys_idev_init);  } -static int cmpc_keys_remove(struct acpi_device *acpi, int type) +static int cmpc_keys_remove(struct acpi_device *acpi)  {  	return cmpc_remove_acpi_notify_device(acpi);  } diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index 1887e2f166a..475cc524251 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -713,15 +713,15 @@ static struct attribute_group compal_attribute_group = {  	.attrs = compal_attributes  }; -static int __devinit compal_probe(struct platform_device *); -static int __devexit compal_remove(struct platform_device *); +static int compal_probe(struct platform_device *); +static int compal_remove(struct platform_device *);  static struct platform_driver compal_driver = {  	.driver = {  		.name = DRIVER_NAME,  		.owner = THIS_MODULE,  	},  	.probe	= compal_probe, -	.remove	= __devexit_p(compal_remove) +	.remove	= compal_remove,  };  static enum power_supply_property compal_bat_properties[] = { @@ -1015,7 +1015,7 @@ err_backlight:  	return ret;  } -static int __devinit compal_probe(struct platform_device *pdev) +static int compal_probe(struct platform_device *pdev)  {  	int err;  	struct compal_data *data; @@ -1067,7 +1067,7 @@ static void __exit compal_cleanup(void)  	pr_info("Driver unloaded\n");  } -static int __devexit compal_remove(struct platform_device *pdev) +static int compal_remove(struct platform_device *pdev)  {  	struct compal_data *data; diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c index 927c33af67e..fa3ee620957 100644 --- a/drivers/platform/x86/dell-laptop.c +++ b/drivers/platform/x86/dell-laptop.c @@ -115,7 +115,7 @@ static const struct dmi_system_id dell_device_table[] __initconst = {  };  MODULE_DEVICE_TABLE(dmi, dell_device_table); -static struct dmi_system_id __devinitdata dell_quirks[] = { +static struct dmi_system_id dell_quirks[] = {  	{  		.callback = dmi_matched,  		.ident = "Dell Vostro V130", @@ -503,7 +503,7 @@ static struct led_classdev touchpad_led = {  	.flags = LED_CORE_SUSPENDRESUME,  }; -static int __devinit touchpad_led_init(struct device *dev) +static int touchpad_led_init(struct device *dev)  {  	return led_classdev_register(dev, &touchpad_led);  } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 5ca264179f4..5d26e70bed6 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -1007,7 +1007,7 @@ static int eeepc_get_fan_pwm(void)  static void eeepc_set_fan_pwm(int value)  { -	value = SENSORS_LIMIT(value, 0, 255); +	value = clamp_val(value, 0, 255);  	value = value * 100 / 255;  	ec_write(EEEPC_EC_FAN_PWM, value);  } @@ -1375,7 +1375,7 @@ static void cmsg_quirks(struct eeepc_laptop *eeepc)  	cmsg_quirk(eeepc, CM_ASL_TPD, "TPD");  } -static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc) +static int eeepc_acpi_init(struct eeepc_laptop *eeepc)  {  	unsigned int init_flags;  	int result; @@ -1407,7 +1407,7 @@ static int __devinit eeepc_acpi_init(struct eeepc_laptop *eeepc)  	return 0;  } -static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc) +static void eeepc_enable_camera(struct eeepc_laptop *eeepc)  {  	/*  	 * If the following call to set_acpi() fails, it's because there's no @@ -1419,7 +1419,7 @@ static void __devinit eeepc_enable_camera(struct eeepc_laptop *eeepc)  static bool eeepc_device_present; -static int __devinit eeepc_acpi_add(struct acpi_device *device) +static int eeepc_acpi_add(struct acpi_device *device)  {  	struct eeepc_laptop *eeepc;  	int result; @@ -1501,7 +1501,7 @@ fail_platform:  	return result;  } -static int eeepc_acpi_remove(struct acpi_device *device, int type) +static int eeepc_acpi_remove(struct acpi_device *device)  {  	struct eeepc_laptop *eeepc = acpi_driver_data(device); diff --git a/drivers/platform/x86/eeepc-wmi.c b/drivers/platform/x86/eeepc-wmi.c index 60cb76a5b51..af67e6e56eb 100644 --- a/drivers/platform/x86/eeepc-wmi.c +++ b/drivers/platform/x86/eeepc-wmi.c @@ -63,6 +63,8 @@ MODULE_PARM_DESC(hotplug_wireless,  #define HOME_RELEASE	0xe5  static const struct key_entry eeepc_wmi_keymap[] = { +	{ KE_KEY, ASUS_WMI_BRN_DOWN, { KEY_BRIGHTNESSDOWN } }, +	{ KE_KEY, ASUS_WMI_BRN_UP, { KEY_BRIGHTNESSUP } },  	/* Sleep already handled via generic ACPI code */  	{ KE_KEY, 0x30, { KEY_VOLUMEUP } },  	{ KE_KEY, 0x31, { KEY_VOLUMEDOWN } }, diff --git a/drivers/platform/x86/fujitsu-laptop.c b/drivers/platform/x86/fujitsu-laptop.c index c4c1a5444b3..1c9386e7c58 100644 --- a/drivers/platform/x86/fujitsu-laptop.c +++ b/drivers/platform/x86/fujitsu-laptop.c @@ -733,7 +733,7 @@ err_stop:  	return result;  } -static int acpi_fujitsu_remove(struct acpi_device *device, int type) +static int acpi_fujitsu_remove(struct acpi_device *device)  {  	struct fujitsu_t *fujitsu = acpi_driver_data(device);  	struct input_dev *input = fujitsu->input; @@ -938,7 +938,7 @@ err_stop:  	return result;  } -static int acpi_fujitsu_hotkey_remove(struct acpi_device *device, int type) +static int acpi_fujitsu_hotkey_remove(struct acpi_device *device)  {  	struct fujitsu_hotkey_t *fujitsu_hotkey = acpi_driver_data(device);  	struct input_dev *input = fujitsu_hotkey->input; diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index f77484528b1..570926c1001 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -192,8 +192,8 @@ static void fujitsu_reset(void)  	fujitsu_send_state();  } -static int __devinit input_fujitsu_setup(struct device *parent, -					 const char *name, const char *phys) +static int input_fujitsu_setup(struct device *parent, const char *name, +			       const char *phys)  {  	struct input_dev *idev;  	int error; @@ -277,21 +277,21 @@ static irqreturn_t fujitsu_interrupt(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static void __devinit fujitsu_dmi_common(const struct dmi_system_id *dmi) +static void fujitsu_dmi_common(const struct dmi_system_id *dmi)  {  	pr_info("%s\n", dmi->ident);  	memcpy(fujitsu.config.keymap, dmi->driver_data,  			sizeof(fujitsu.config.keymap));  } -static int __devinit fujitsu_dmi_lifebook(const struct dmi_system_id *dmi) +static int fujitsu_dmi_lifebook(const struct dmi_system_id *dmi)  {  	fujitsu_dmi_common(dmi);  	fujitsu.config.quirks |= INVERT_TABLET_MODE_BIT;  	return 1;  } -static int __devinit fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) +static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi)  {  	fujitsu_dmi_common(dmi);  	fujitsu.config.quirks |= FORCE_TABLET_MODE_IF_UNDOCK; @@ -366,8 +366,7 @@ static const struct dmi_system_id dmi_ids[] __initconst = {  	{ NULL }  }; -static acpi_status __devinit -fujitsu_walk_resources(struct acpi_resource *res, void *data) +static acpi_status fujitsu_walk_resources(struct acpi_resource *res, void *data)  {  	switch (res->type) {  	case ACPI_RESOURCE_TYPE_IRQ: @@ -390,7 +389,7 @@ fujitsu_walk_resources(struct acpi_resource *res, void *data)  	}  } -static int __devinit acpi_fujitsu_add(struct acpi_device *adev) +static int acpi_fujitsu_add(struct acpi_device *adev)  {  	acpi_status status;  	int error; @@ -432,7 +431,7 @@ static int __devinit acpi_fujitsu_add(struct acpi_device *adev)  	return 0;  } -static int __devexit acpi_fujitsu_remove(struct acpi_device *adev, int type) +static int acpi_fujitsu_remove(struct acpi_device *adev)  {  	free_irq(fujitsu.irq, fujitsu_interrupt);  	release_region(fujitsu.io_base, fujitsu.io_length); diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 387183a2d6d..45cacf79f3a 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -60,6 +60,7 @@ enum hp_wmi_radio {  	HPWMI_WIFI = 0,  	HPWMI_BLUETOOTH = 1,  	HPWMI_WWAN = 2, +	HPWMI_GPS = 3,  };  enum hp_wmi_event_ids { @@ -72,10 +73,6 @@ enum hp_wmi_event_ids {  	HPWMI_LOCK_SWITCH = 7,  }; -static int __devinit hp_wmi_bios_setup(struct platform_device *device); -static int __exit hp_wmi_bios_remove(struct platform_device *device); -static int hp_wmi_resume_handler(struct device *device); -  struct bios_args {  	u32 signature;  	u32 command; @@ -137,6 +134,7 @@ static const struct key_entry hp_wmi_keymap[] = {  	{ KE_KEY, 0x2142, { KEY_MEDIA } },  	{ KE_KEY, 0x213b, { KEY_INFO } },  	{ KE_KEY, 0x2169, { KEY_DIRECTION } }, +	{ KE_KEY, 0x216a, { KEY_SETUP } },  	{ KE_KEY, 0x231b, { KEY_HELP } },  	{ KE_END, 0 }  }; @@ -147,6 +145,7 @@ static struct platform_device *hp_wmi_platform_dev;  static struct rfkill *wifi_rfkill;  static struct rfkill *bluetooth_rfkill;  static struct rfkill *wwan_rfkill; +static struct rfkill *gps_rfkill;  struct rfkill2_device {  	u8 id; @@ -157,21 +156,6 @@ struct rfkill2_device {  static int rfkill2_count;  static struct rfkill2_device rfkill2[HPWMI_MAX_RFKILL2_DEVICES]; -static const struct dev_pm_ops hp_wmi_pm_ops = { -	.resume  = hp_wmi_resume_handler, -	.restore  = hp_wmi_resume_handler, -}; - -static struct platform_driver hp_wmi_driver = { -	.driver = { -		.name = "hp-wmi", -		.owner = THIS_MODULE, -		.pm = &hp_wmi_pm_ops, -	}, -	.probe = hp_wmi_bios_setup, -	.remove = hp_wmi_bios_remove, -}; -  /*   * hp_wmi_perform_query   * @@ -543,6 +527,10 @@ static void hp_wmi_notify(u32 value, void *context)  			rfkill_set_states(wwan_rfkill,  					  hp_wmi_get_sw_state(HPWMI_WWAN),  					  hp_wmi_get_hw_state(HPWMI_WWAN)); +		if (gps_rfkill) +			rfkill_set_states(gps_rfkill, +					  hp_wmi_get_sw_state(HPWMI_GPS), +					  hp_wmi_get_hw_state(HPWMI_GPS));  		break;  	case HPWMI_CPU_BATTERY_THROTTLE:  		pr_info("Unimplemented CPU throttle because of 3 Cell battery event detected\n"); @@ -619,7 +607,7 @@ static void cleanup_sysfs(struct platform_device *device)  	device_remove_file(&device->dev, &dev_attr_tablet);  } -static int __devinit hp_wmi_rfkill_setup(struct platform_device *device) +static int hp_wmi_rfkill_setup(struct platform_device *device)  {  	int err;  	int wireless = 0; @@ -670,7 +658,7 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)  					   (void *) HPWMI_WWAN);  		if (!wwan_rfkill) {  			err = -ENOMEM; -			goto register_bluetooth_error; +			goto register_gps_error;  		}  		rfkill_init_sw_state(wwan_rfkill,  				     hp_wmi_get_sw_state(HPWMI_WWAN)); @@ -681,10 +669,33 @@ static int __devinit hp_wmi_rfkill_setup(struct platform_device *device)  			goto register_wwan_err;  	} +	if (wireless & 0x8) { +		gps_rfkill = rfkill_alloc("hp-gps", &device->dev, +						RFKILL_TYPE_GPS, +						&hp_wmi_rfkill_ops, +						(void *) HPWMI_GPS); +		if (!gps_rfkill) { +			err = -ENOMEM; +			goto register_bluetooth_error; +		} +		rfkill_init_sw_state(gps_rfkill, +				     hp_wmi_get_sw_state(HPWMI_GPS)); +		rfkill_set_hw_state(bluetooth_rfkill, +				    hp_wmi_get_hw_state(HPWMI_GPS)); +		err = rfkill_register(gps_rfkill); +		if (err) +			goto register_gps_error; +	} +  	return 0;  register_wwan_err:  	rfkill_destroy(wwan_rfkill);  	wwan_rfkill = NULL; +	if (gps_rfkill) +		rfkill_unregister(gps_rfkill); +register_gps_error: +	rfkill_destroy(gps_rfkill); +	gps_rfkill = NULL;  	if (bluetooth_rfkill)  		rfkill_unregister(bluetooth_rfkill);  register_bluetooth_error: @@ -698,7 +709,7 @@ register_wifi_error:  	return err;  } -static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device) +static int hp_wmi_rfkill2_setup(struct platform_device *device)  {  	int err, i;  	struct bios_rfkill2_state state; @@ -729,6 +740,10 @@ static int __devinit hp_wmi_rfkill2_setup(struct platform_device *device)  			type = RFKILL_TYPE_WWAN;  			name = "hp-wwan";  			break; +		case HPWMI_GPS: +			type = RFKILL_TYPE_GPS; +			name = "hp-gps"; +			break;  		default:  			pr_warn("unknown device type 0x%x\n",  				state.device[i].radio_type); @@ -778,7 +793,7 @@ fail:  	return err;  } -static int __devinit hp_wmi_bios_setup(struct platform_device *device) +static int __init hp_wmi_bios_setup(struct platform_device *device)  {  	int err; @@ -786,6 +801,7 @@ static int __devinit hp_wmi_bios_setup(struct platform_device *device)  	wifi_rfkill = NULL;  	bluetooth_rfkill = NULL;  	wwan_rfkill = NULL; +	gps_rfkill = NULL;  	rfkill2_count = 0;  	if (hp_wmi_rfkill_setup(device)) @@ -835,6 +851,10 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)  		rfkill_unregister(wwan_rfkill);  		rfkill_destroy(wwan_rfkill);  	} +	if (gps_rfkill) { +		rfkill_unregister(gps_rfkill); +		rfkill_destroy(gps_rfkill); +	}  	return 0;  } @@ -870,51 +890,70 @@ static int hp_wmi_resume_handler(struct device *device)  		rfkill_set_states(wwan_rfkill,  				  hp_wmi_get_sw_state(HPWMI_WWAN),  				  hp_wmi_get_hw_state(HPWMI_WWAN)); +	if (gps_rfkill) +		rfkill_set_states(gps_rfkill, +				  hp_wmi_get_sw_state(HPWMI_GPS), +				  hp_wmi_get_hw_state(HPWMI_GPS));  	return 0;  } +static const struct dev_pm_ops hp_wmi_pm_ops = { +	.resume  = hp_wmi_resume_handler, +	.restore  = hp_wmi_resume_handler, +}; + +static struct platform_driver hp_wmi_driver = { +	.driver = { +		.name = "hp-wmi", +		.owner = THIS_MODULE, +		.pm = &hp_wmi_pm_ops, +	}, +	.remove = __exit_p(hp_wmi_bios_remove), +}; +  static int __init hp_wmi_init(void)  {  	int err;  	int event_capable = wmi_has_guid(HPWMI_EVENT_GUID);  	int bios_capable = wmi_has_guid(HPWMI_BIOS_GUID); +	if (!bios_capable && !event_capable) +		return -ENODEV; +  	if (event_capable) {  		err = hp_wmi_input_setup();  		if (err)  			return err; +		 +		//Enable magic for hotkeys that run on the SMBus +		ec_write(0xe6,0x6e);  	}  	if (bios_capable) { -		err = platform_driver_register(&hp_wmi_driver); -		if (err) -			goto err_driver_reg; -		hp_wmi_platform_dev = platform_device_alloc("hp-wmi", -1); -		if (!hp_wmi_platform_dev) { -			err = -ENOMEM; -			goto err_device_alloc; +		hp_wmi_platform_dev = +			platform_device_register_simple("hp-wmi", -1, NULL, 0); +		if (IS_ERR(hp_wmi_platform_dev)) { +			err = PTR_ERR(hp_wmi_platform_dev); +			goto err_destroy_input;  		} -		err = platform_device_add(hp_wmi_platform_dev); + +		err = platform_driver_probe(&hp_wmi_driver, hp_wmi_bios_setup);  		if (err) -			goto err_device_add; +			goto err_unregister_device;  	} -	if (!bios_capable && !event_capable) -		return -ENODEV; -  	return 0; -err_device_add: -	platform_device_put(hp_wmi_platform_dev); -err_device_alloc: -	platform_driver_unregister(&hp_wmi_driver); -err_driver_reg: +err_unregister_device: +	platform_device_unregister(hp_wmi_platform_dev); +err_destroy_input:  	if (event_capable)  		hp_wmi_input_destroy();  	return err;  } +module_init(hp_wmi_init);  static void __exit hp_wmi_exit(void)  { @@ -926,6 +965,4 @@ static void __exit hp_wmi_exit(void)  		platform_driver_unregister(&hp_wmi_driver);  	}  } - -module_init(hp_wmi_init);  module_exit(hp_wmi_exit); diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c index 18d74f29dcb..e64a7a870d4 100644 --- a/drivers/platform/x86/hp_accel.c +++ b/drivers/platform/x86/hp_accel.c @@ -337,7 +337,7 @@ static int lis3lv02d_add(struct acpi_device *device)  	return ret;  } -static int lis3lv02d_remove(struct acpi_device *device, int type) +static int lis3lv02d_remove(struct acpi_device *device)  {  	if (!device)  		return -EINVAL; diff --git a/drivers/platform/x86/ibm_rtl.c b/drivers/platform/x86/ibm_rtl.c index 7481146a5b4..97c2be195ef 100644 --- a/drivers/platform/x86/ibm_rtl.c +++ b/drivers/platform/x86/ibm_rtl.c @@ -244,7 +244,7 @@ static int __init ibm_rtl_init(void) {  	if (force)  		pr_warn("module loaded by force\n");  	/* first ensure that we are running on IBM HW */ -	else if (efi_enabled || !dmi_check_system(ibm_rtl_dmi_table)) +	else if (efi_enabled(EFI_BOOT) || !dmi_check_system(ibm_rtl_dmi_table))  		return -ENODEV;  	/* Get the address for the Extended BIOS Data Area */ diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c index 5ff4f2e314d..17f00b8dc5c 100644 --- a/drivers/platform/x86/ideapad-laptop.c +++ b/drivers/platform/x86/ideapad-laptop.c @@ -298,7 +298,7 @@ static const struct file_operations debugfs_cfg_fops = {  	.release = single_release,  }; -static int __devinit ideapad_debugfs_init(struct ideapad_private *priv) +static int ideapad_debugfs_init(struct ideapad_private *priv)  {  	struct dentry *node; @@ -468,8 +468,7 @@ static void ideapad_sync_rfk_state(struct ideapad_private *priv)  			rfkill_set_hw_state(priv->rfk[i], hw_blocked);  } -static int __devinit ideapad_register_rfkill(struct acpi_device *adevice, -					     int dev) +static int ideapad_register_rfkill(struct acpi_device *adevice, int dev)  {  	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);  	int ret; @@ -519,7 +518,7 @@ static void ideapad_unregister_rfkill(struct acpi_device *adevice, int dev)  /*   * Platform device   */ -static int __devinit ideapad_platform_init(struct ideapad_private *priv) +static int ideapad_platform_init(struct ideapad_private *priv)  {  	int result; @@ -569,7 +568,7 @@ static const struct key_entry ideapad_keymap[] = {  	{ KE_END, 0 },  }; -static int __devinit ideapad_input_init(struct ideapad_private *priv) +static int ideapad_input_init(struct ideapad_private *priv)  {  	struct input_dev *inputdev;  	int error; @@ -776,7 +775,7 @@ static void ideapad_sync_touchpad_state(struct acpi_device *adevice)  	}  } -static int __devinit ideapad_acpi_add(struct acpi_device *adevice) +static int ideapad_acpi_add(struct acpi_device *adevice)  {  	int ret, i;  	int cfg; @@ -835,7 +834,7 @@ platform_failed:  	return ret;  } -static int __devexit ideapad_acpi_remove(struct acpi_device *adevice, int type) +static int ideapad_acpi_remove(struct acpi_device *adevice)  {  	struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);  	int i; diff --git a/drivers/platform/x86/intel_menlow.c b/drivers/platform/x86/intel_menlow.c index 3271ac85115..d6cfc1558c2 100644 --- a/drivers/platform/x86/intel_menlow.c +++ b/drivers/platform/x86/intel_menlow.c @@ -200,7 +200,7 @@ static int intel_menlow_memory_add(struct acpi_device *device)  } -static int intel_menlow_memory_remove(struct acpi_device *device, int type) +static int intel_menlow_memory_remove(struct acpi_device *device)  {  	struct thermal_cooling_device *cdev = acpi_driver_data(device); diff --git a/drivers/platform/x86/intel_mid_powerbtn.c b/drivers/platform/x86/intel_mid_powerbtn.c index bcbad8452a6..f59683aa13d 100644 --- a/drivers/platform/x86/intel_mid_powerbtn.c +++ b/drivers/platform/x86/intel_mid_powerbtn.c @@ -56,7 +56,7 @@ static irqreturn_t mfld_pb_isr(int irq, void *dev_id)  	return IRQ_HANDLED;  } -static int __devinit mfld_pb_probe(struct platform_device *pdev) +static int mfld_pb_probe(struct platform_device *pdev)  {  	struct input_dev *input;  	int irq = platform_get_irq(pdev, 0); @@ -121,7 +121,7 @@ err_free_input:  	return error;  } -static int __devexit mfld_pb_remove(struct platform_device *pdev) +static int mfld_pb_remove(struct platform_device *pdev)  {  	struct input_dev *input = platform_get_drvdata(pdev);  	int irq = platform_get_irq(pdev, 0); @@ -139,7 +139,7 @@ static struct platform_driver mfld_pb_driver = {  		.owner = THIS_MODULE,  	},  	.probe	= mfld_pb_probe, -	.remove	= __devexit_p(mfld_pb_remove), +	.remove	= mfld_pb_remove,  };  module_platform_driver(mfld_pb_driver); diff --git a/drivers/platform/x86/intel_mid_thermal.c b/drivers/platform/x86/intel_mid_thermal.c index 93de09019d1..81c491e74b3 100644 --- a/drivers/platform/x86/intel_mid_thermal.c +++ b/drivers/platform/x86/intel_mid_thermal.c @@ -563,7 +563,7 @@ static struct platform_driver mid_thermal_driver = {  		.pm = &mid_thermal_pm,  	},  	.probe = mid_thermal_probe, -	.remove = __devexit_p(mid_thermal_remove), +	.remove = mid_thermal_remove,  	.id_table = therm_id_table,  }; diff --git a/drivers/platform/x86/intel_oaktrail.c b/drivers/platform/x86/intel_oaktrail.c index 79a0c2f6be5..f6f18cde0f1 100644 --- a/drivers/platform/x86/intel_oaktrail.c +++ b/drivers/platform/x86/intel_oaktrail.c @@ -278,12 +278,12 @@ static void oaktrail_backlight_exit(void)  		backlight_device_unregister(oaktrail_bl_device);  } -static int __devinit oaktrail_probe(struct platform_device *pdev) +static int oaktrail_probe(struct platform_device *pdev)  {  	return 0;  } -static int __devexit oaktrail_remove(struct platform_device *pdev) +static int oaktrail_remove(struct platform_device *pdev)  {  	return 0;  } @@ -294,7 +294,7 @@ static struct platform_driver oaktrail_driver = {  		.owner = THIS_MODULE,  	},  	.probe	= oaktrail_probe, -	.remove	= __devexit_p(oaktrail_remove) +	.remove	= oaktrail_remove,  };  static int dmi_check_cb(const struct dmi_system_id *id) diff --git a/drivers/platform/x86/intel_pmic_gpio.c b/drivers/platform/x86/intel_pmic_gpio.c index 1686c1e07d5..6f4b7289a05 100644 --- a/drivers/platform/x86/intel_pmic_gpio.c +++ b/drivers/platform/x86/intel_pmic_gpio.c @@ -230,7 +230,7 @@ static irqreturn_t pmic_irq_handler(int irq, void *data)  	return ret;  } -static int __devinit platform_pmic_gpio_probe(struct platform_device *pdev) +static int platform_pmic_gpio_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev;  	int irq = platform_get_irq(pdev, 0); diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c index 2111dbb7e1e..6b229387567 100644 --- a/drivers/platform/x86/msi-laptop.c +++ b/drivers/platform/x86/msi-laptop.c @@ -82,8 +82,19 @@  #define MSI_STANDARD_EC_SCM_LOAD_ADDRESS	0x2d  #define MSI_STANDARD_EC_SCM_LOAD_MASK		(1 << 0) -#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS	0xe4 +#define MSI_STANDARD_EC_FUNCTIONS_ADDRESS	0xe4 +/* Power LED is orange - Turbo mode */ +#define MSI_STANDARD_EC_TURBO_MASK		(1 << 1) +/* Power LED is green - ECO mode */ +#define MSI_STANDARD_EC_ECO_MASK		(1 << 3) +/* Touchpad is turned on */  #define MSI_STANDARD_EC_TOUCHPAD_MASK		(1 << 4) +/* If this bit != bit 1, turbo mode can't be toggled */ +#define MSI_STANDARD_EC_TURBO_COOLDOWN_MASK	(1 << 7) + +#define MSI_STANDARD_EC_FAN_ADDRESS		0x33 +/* If zero, fan rotates at maximal speed */ +#define MSI_STANDARD_EC_AUTOFAN_MASK		(1 << 0)  #ifdef CONFIG_PM_SLEEP  static int msi_laptop_resume(struct device *device); @@ -108,23 +119,38 @@ static const struct key_entry msi_laptop_keymap[] = {  static struct input_dev *msi_laptop_input_dev; -static bool old_ec_model;  static int wlan_s, bluetooth_s, threeg_s;  static int threeg_exists; - -/* Some MSI 3G netbook only have one fn key to control Wlan/Bluetooth/3G, - * those netbook will load the SCM (windows app) to disable the original - * Wlan/Bluetooth control by BIOS when user press fn key, then control - * Wlan/Bluetooth/3G by SCM (software control by OS). Without SCM, user - * cann't on/off 3G module on those 3G netbook. - * On Linux, msi-laptop driver will do the same thing to disable the - * original BIOS control, then might need use HAL or other userland - * application to do the software control that simulate with SCM. - * e.g. MSI N034 netbook - */ -static bool load_scm_model;  static struct rfkill *rfk_wlan, *rfk_bluetooth, *rfk_threeg; +/* MSI laptop quirks */ +struct quirk_entry { +	bool old_ec_model; + +	/* Some MSI 3G netbook only have one fn key to control +	 * Wlan/Bluetooth/3G, those netbook will load the SCM (windows app) to +	 * disable the original Wlan/Bluetooth control by BIOS when user press +	 * fn key, then control Wlan/Bluetooth/3G by SCM (software control by +	 * OS). Without SCM, user cann't on/off 3G module on those 3G netbook. +	 * On Linux, msi-laptop driver will do the same thing to disable the +	 * original BIOS control, then might need use HAL or other userland +	 * application to do the software control that simulate with SCM. +	 * e.g. MSI N034 netbook +	 */ +	bool load_scm_model; + +	/* Some MSI laptops need delay before reading from EC */ +	bool ec_delay; + +	/* Some MSI Wind netbooks (e.g. MSI Wind U100) need loading SCM to get +	 * some features working (e.g. ECO mode), but we cannot change +	 * Wlan/Bluetooth state in software and we can only read its state. +	 */ +	bool ec_read_only; +}; + +static struct quirk_entry *quirks; +  /* Hardware access */  static int set_lcd_level(int level) @@ -195,10 +221,13 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)  	if (sscanf(buf, "%i", &status) != 1 || (status < 0 || status > 1))  		return -EINVAL; +	if (quirks->ec_read_only) +		return -EOPNOTSUPP; +  	/* read current device state */  	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);  	if (result < 0) -		return -EINVAL; +		return result;  	if (!!(rdata & mask) != status) {  		/* reverse device bit */ @@ -209,7 +238,7 @@ static ssize_t set_device_state(const char *buf, size_t count, u8 mask)  		result = ec_write(MSI_STANDARD_EC_COMMAND_ADDRESS, wdata);  		if (result < 0) -			return -EINVAL; +			return result;  	}  	return count; @@ -222,7 +251,7 @@ static int get_wireless_state(int *wlan, int *bluetooth)  	result = ec_transaction(MSI_EC_COMMAND_WIRELESS, &wdata, 1, &rdata, 1);  	if (result < 0) -		return -1; +		return result;  	if (wlan)  		*wlan = !!(rdata & 8); @@ -240,7 +269,7 @@ static int get_wireless_state_ec_standard(void)  	result = ec_read(MSI_STANDARD_EC_COMMAND_ADDRESS, &rdata);  	if (result < 0) -		return -1; +		return result;  	wlan_s = !!(rdata & MSI_STANDARD_EC_WLAN_MASK); @@ -258,7 +287,7 @@ static int get_threeg_exists(void)  	result = ec_read(MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS, &rdata);  	if (result < 0) -		return -1; +		return result;  	threeg_exists = !!(rdata & MSI_STANDARD_EC_3G_MASK); @@ -291,9 +320,9 @@ static ssize_t show_wlan(struct device *dev,  	struct device_attribute *attr, char *buf)  { -	int ret, enabled; +	int ret, enabled = 0; -	if (old_ec_model) { +	if (quirks->old_ec_model) {  		ret = get_wireless_state(&enabled, NULL);  	} else {  		ret = get_wireless_state_ec_standard(); @@ -315,9 +344,9 @@ static ssize_t show_bluetooth(struct device *dev,  	struct device_attribute *attr, char *buf)  { -	int ret, enabled; +	int ret, enabled = 0; -	if (old_ec_model) { +	if (quirks->old_ec_model) {  		ret = get_wireless_state(NULL, &enabled);  	} else {  		ret = get_wireless_state_ec_standard(); @@ -342,8 +371,8 @@ static ssize_t show_threeg(struct device *dev,  	int ret;  	/* old msi ec not support 3G */ -	if (old_ec_model) -		return -1; +	if (quirks->old_ec_model) +		return -ENODEV;  	ret = get_wireless_state_ec_standard();  	if (ret < 0) @@ -417,18 +446,119 @@ static ssize_t store_auto_brightness(struct device *dev,  	return count;  } +static ssize_t show_touchpad(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	u8 rdata; +	int result; + +	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata); +	if (result < 0) +		return result; + +	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK)); +} + +static ssize_t show_turbo(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	u8 rdata; +	int result; + +	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata); +	if (result < 0) +		return result; + +	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_TURBO_MASK)); +} + +static ssize_t show_eco(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	u8 rdata; +	int result; + +	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata); +	if (result < 0) +		return result; + +	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_ECO_MASK)); +} + +static ssize_t show_turbo_cooldown(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	u8 rdata; +	int result; + +	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata); +	if (result < 0) +		return result; + +	return sprintf(buf, "%i\n", (!!(rdata & MSI_STANDARD_EC_TURBO_MASK)) | +		(!!(rdata & MSI_STANDARD_EC_TURBO_COOLDOWN_MASK) << 1)); +} + +static ssize_t show_auto_fan(struct device *dev, +	struct device_attribute *attr, char *buf) +{ + +	u8 rdata; +	int result; + +	result = ec_read(MSI_STANDARD_EC_FAN_ADDRESS, &rdata); +	if (result < 0) +		return result; + +	return sprintf(buf, "%i\n", !!(rdata & MSI_STANDARD_EC_AUTOFAN_MASK)); +} + +static ssize_t store_auto_fan(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ + +	int enable, result; + +	if (sscanf(buf, "%i", &enable) != 1 || (enable != (enable & 1))) +		return -EINVAL; + +	result = ec_write(MSI_STANDARD_EC_FAN_ADDRESS, enable); +	if (result < 0) +		return result; + +	return count; +} +  static DEVICE_ATTR(lcd_level, 0644, show_lcd_level, store_lcd_level);  static DEVICE_ATTR(auto_brightness, 0644, show_auto_brightness,  		   store_auto_brightness);  static DEVICE_ATTR(bluetooth, 0444, show_bluetooth, NULL);  static DEVICE_ATTR(wlan, 0444, show_wlan, NULL);  static DEVICE_ATTR(threeg, 0444, show_threeg, NULL); +static DEVICE_ATTR(touchpad, 0444, show_touchpad, NULL); +static DEVICE_ATTR(turbo_mode, 0444, show_turbo, NULL); +static DEVICE_ATTR(eco_mode, 0444, show_eco, NULL); +static DEVICE_ATTR(turbo_cooldown, 0444, show_turbo_cooldown, NULL); +static DEVICE_ATTR(auto_fan, 0644, show_auto_fan, store_auto_fan);  static struct attribute *msipf_attributes[] = { -	&dev_attr_lcd_level.attr, -	&dev_attr_auto_brightness.attr,  	&dev_attr_bluetooth.attr,  	&dev_attr_wlan.attr, +	&dev_attr_touchpad.attr, +	&dev_attr_turbo_mode.attr, +	&dev_attr_eco_mode.attr, +	&dev_attr_turbo_cooldown.attr, +	&dev_attr_auto_fan.attr, +	NULL +}; + +static struct attribute *msipf_old_attributes[] = { +	&dev_attr_lcd_level.attr, +	&dev_attr_auto_brightness.attr,  	NULL  }; @@ -436,6 +566,10 @@ static struct attribute_group msipf_attribute_group = {  	.attrs = msipf_attributes  }; +static struct attribute_group msipf_old_attribute_group = { +	.attrs = msipf_old_attributes +}; +  static struct platform_driver msipf_driver = {  	.driver = {  		.name = "msi-laptop-pf", @@ -448,9 +582,26 @@ static struct platform_device *msipf_device;  /* Initialization */ -static int dmi_check_cb(const struct dmi_system_id *id) +static struct quirk_entry quirk_old_ec_model = { +	.old_ec_model = true, +}; + +static struct quirk_entry quirk_load_scm_model = { +	.load_scm_model = true, +	.ec_delay = true, +}; + +static struct quirk_entry quirk_load_scm_ro_model = { +	.load_scm_model = true, +	.ec_read_only = true, +}; + +static int dmi_check_cb(const struct dmi_system_id *dmi)  { -	pr_info("Identified laptop model '%s'\n", id->ident); +	pr_info("Identified laptop model '%s'\n", dmi->ident); + +	quirks = dmi->driver_data; +  	return 1;  } @@ -464,6 +615,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {  			DMI_MATCH(DMI_CHASSIS_VENDOR,  				  "MICRO-STAR INT'L CO.,LTD")  		}, +		.driver_data = &quirk_old_ec_model,  		.callback = dmi_check_cb  	},  	{ @@ -474,6 +626,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {  			DMI_MATCH(DMI_PRODUCT_VERSION, "0581"),  			DMI_MATCH(DMI_BOARD_NAME, "MS-1058")  		}, +		.driver_data = &quirk_old_ec_model,  		.callback = dmi_check_cb  	},  	{ @@ -484,6 +637,7 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {  			DMI_MATCH(DMI_BOARD_VENDOR, "MSI"),  			DMI_MATCH(DMI_BOARD_NAME, "MS-1412")  		}, +		.driver_data = &quirk_old_ec_model,  		.callback = dmi_check_cb  	},  	{ @@ -495,12 +649,9 @@ static struct dmi_system_id __initdata msi_dmi_table[] = {  			DMI_MATCH(DMI_CHASSIS_VENDOR,  				  "MICRO-STAR INT'L CO.,LTD")  		}, +		.driver_data = &quirk_old_ec_model,  		.callback = dmi_check_cb  	}, -	{ } -}; - -static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {  	{  		.ident = "MSI N034",  		.matches = { @@ -510,6 +661,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {  			DMI_MATCH(DMI_CHASSIS_VENDOR,  			"MICRO-STAR INTERNATIONAL CO., LTD")  		}, +		.driver_data = &quirk_load_scm_model,  		.callback = dmi_check_cb  	},  	{ @@ -521,6 +673,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {  			DMI_MATCH(DMI_CHASSIS_VENDOR,  			"MICRO-STAR INTERNATIONAL CO., LTD")  		}, +		.driver_data = &quirk_load_scm_model,  		.callback = dmi_check_cb  	},  	{ @@ -530,6 +683,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {  				"MICRO-STAR INTERNATIONAL CO., LTD"),  			DMI_MATCH(DMI_PRODUCT_NAME, "MS-N014"),  		}, +		.driver_data = &quirk_load_scm_model,  		.callback = dmi_check_cb  	},  	{ @@ -539,6 +693,7 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {  				"Micro-Star International"),  			DMI_MATCH(DMI_PRODUCT_NAME, "CR620"),  		}, +		.driver_data = &quirk_load_scm_model,  		.callback = dmi_check_cb  	},  	{ @@ -548,6 +703,17 @@ static struct dmi_system_id __initdata msi_load_scm_models_dmi_table[] = {  				"Micro-Star International Co., Ltd."),  			DMI_MATCH(DMI_PRODUCT_NAME, "U270 series"),  		}, +		.driver_data = &quirk_load_scm_model, +		.callback = dmi_check_cb +	}, +	{ +		.ident = "MSI U90/U100", +		.matches = { +			DMI_MATCH(DMI_SYS_VENDOR, +				"MICRO-STAR INTERNATIONAL CO., LTD"), +			DMI_MATCH(DMI_PRODUCT_NAME, "U90/U100"), +		}, +		.driver_data = &quirk_load_scm_ro_model,  		.callback = dmi_check_cb  	},  	{ } @@ -560,32 +726,26 @@ static int rfkill_bluetooth_set(void *data, bool blocked)  	 * blocked == false is on  	 * blocked == true is off  	 */ -	if (blocked) -		set_device_state("0", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); -	else -		set_device_state("1", 0, MSI_STANDARD_EC_BLUETOOTH_MASK); +	int result = set_device_state(blocked ? "0" : "1", 0, +			MSI_STANDARD_EC_BLUETOOTH_MASK); -	return 0; +	return min(result, 0);  }  static int rfkill_wlan_set(void *data, bool blocked)  { -	if (blocked) -		set_device_state("0", 0, MSI_STANDARD_EC_WLAN_MASK); -	else -		set_device_state("1", 0, MSI_STANDARD_EC_WLAN_MASK); +	int result = set_device_state(blocked ? "0" : "1", 0, +			MSI_STANDARD_EC_WLAN_MASK); -	return 0; +	return min(result, 0);  }  static int rfkill_threeg_set(void *data, bool blocked)  { -	if (blocked) -		set_device_state("0", 0, MSI_STANDARD_EC_3G_MASK); -	else -		set_device_state("1", 0, MSI_STANDARD_EC_3G_MASK); +	int result = set_device_state(blocked ? "0" : "1", 0, +			MSI_STANDARD_EC_3G_MASK); -	return 0; +	return min(result, 0);  }  static const struct rfkill_ops rfkill_bluetooth_ops = { @@ -618,25 +778,34 @@ static void rfkill_cleanup(void)  	}  } +static bool msi_rfkill_set_state(struct rfkill *rfkill, bool blocked) +{ +	if (quirks->ec_read_only) +		return rfkill_set_hw_state(rfkill, blocked); +	else +		return rfkill_set_sw_state(rfkill, blocked); +} +  static void msi_update_rfkill(struct work_struct *ignored)  {  	get_wireless_state_ec_standard();  	if (rfk_wlan) -		rfkill_set_sw_state(rfk_wlan, !wlan_s); +		msi_rfkill_set_state(rfk_wlan, !wlan_s);  	if (rfk_bluetooth) -		rfkill_set_sw_state(rfk_bluetooth, !bluetooth_s); +		msi_rfkill_set_state(rfk_bluetooth, !bluetooth_s);  	if (rfk_threeg) -		rfkill_set_sw_state(rfk_threeg, !threeg_s); +		msi_rfkill_set_state(rfk_threeg, !threeg_s);  } -static DECLARE_DELAYED_WORK(msi_rfkill_work, msi_update_rfkill); +static DECLARE_DELAYED_WORK(msi_rfkill_dwork, msi_update_rfkill); +static DECLARE_WORK(msi_rfkill_work, msi_update_rfkill);  static void msi_send_touchpad_key(struct work_struct *ignored)  {  	u8 rdata;  	int result; -	result = ec_read(MSI_STANDARD_EC_TOUCHPAD_ADDRESS, &rdata); +	result = ec_read(MSI_STANDARD_EC_FUNCTIONS_ADDRESS, &rdata);  	if (result < 0)  		return; @@ -644,7 +813,8 @@ static void msi_send_touchpad_key(struct work_struct *ignored)  		(rdata & MSI_STANDARD_EC_TOUCHPAD_MASK) ?  		KEY_TOUCHPAD_ON : KEY_TOUCHPAD_OFF, 1, true);  } -static DECLARE_DELAYED_WORK(msi_touchpad_work, msi_send_touchpad_key); +static DECLARE_DELAYED_WORK(msi_touchpad_dwork, msi_send_touchpad_key); +static DECLARE_WORK(msi_touchpad_work, msi_send_touchpad_key);  static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,  				struct serio *port) @@ -662,14 +832,20 @@ static bool msi_laptop_i8042_filter(unsigned char data, unsigned char str,  		extended = false;  		switch (data) {  		case 0xE4: -			schedule_delayed_work(&msi_touchpad_work, -				round_jiffies_relative(0.5 * HZ)); +			if (quirks->ec_delay) { +				schedule_delayed_work(&msi_touchpad_dwork, +					round_jiffies_relative(0.5 * HZ)); +			} else +				schedule_work(&msi_touchpad_work);  			break;  		case 0x54:  		case 0x62:  		case 0x76: -			schedule_delayed_work(&msi_rfkill_work, -				round_jiffies_relative(0.5 * HZ)); +			if (quirks->ec_delay) { +				schedule_delayed_work(&msi_rfkill_dwork, +					round_jiffies_relative(0.5 * HZ)); +			} else +				schedule_work(&msi_rfkill_work);  			break;  		}  	} @@ -736,8 +912,11 @@ static int rfkill_init(struct platform_device *sdev)  	}  	/* schedule to run rfkill state initial */ -	schedule_delayed_work(&msi_rfkill_init, -				round_jiffies_relative(1 * HZ)); +	if (quirks->ec_delay) { +		schedule_delayed_work(&msi_rfkill_init, +			round_jiffies_relative(1 * HZ)); +	} else +		schedule_work(&msi_rfkill_work);  	return 0; @@ -761,7 +940,7 @@ static int msi_laptop_resume(struct device *device)  	u8 data;  	int result; -	if (!load_scm_model) +	if (!quirks->load_scm_model)  		return 0;  	/* set load SCM to disable hardware control by fn key */ @@ -819,13 +998,15 @@ static int __init load_scm_model_init(struct platform_device *sdev)  	u8 data;  	int result; -	/* allow userland write sysfs file  */ -	dev_attr_bluetooth.store = store_bluetooth; -	dev_attr_wlan.store = store_wlan; -	dev_attr_threeg.store = store_threeg; -	dev_attr_bluetooth.attr.mode |= S_IWUSR; -	dev_attr_wlan.attr.mode |= S_IWUSR; -	dev_attr_threeg.attr.mode |= S_IWUSR; +	if (!quirks->ec_read_only) { +		/* allow userland write sysfs file  */ +		dev_attr_bluetooth.store = store_bluetooth; +		dev_attr_wlan.store = store_wlan; +		dev_attr_threeg.store = store_threeg; +		dev_attr_bluetooth.attr.mode |= S_IWUSR; +		dev_attr_wlan.attr.mode |= S_IWUSR; +		dev_attr_threeg.attr.mode |= S_IWUSR; +	}  	/* disable hardware control by fn key */  	result = ec_read(MSI_STANDARD_EC_SCM_LOAD_ADDRESS, &data); @@ -874,21 +1055,22 @@ static int __init msi_init(void)  	if (acpi_disabled)  		return -ENODEV; -	if (force || dmi_check_system(msi_dmi_table)) -		old_ec_model = 1; +	dmi_check_system(msi_dmi_table); +	if (!quirks) +		/* quirks may be NULL if no match in DMI table */ +		quirks = &quirk_load_scm_model; +	if (force) +		quirks = &quirk_old_ec_model; -	if (!old_ec_model) +	if (!quirks->old_ec_model)  		get_threeg_exists(); -	if (!old_ec_model && dmi_check_system(msi_load_scm_models_dmi_table)) -		load_scm_model = 1; -  	if (auto_brightness < 0 || auto_brightness > 2)  		return -EINVAL;  	/* Register backlight stuff */ -	if (acpi_video_backlight_support()) { +	if (!quirks->old_ec_model || acpi_video_backlight_support()) {  		pr_info("Brightness ignored, must be controlled by ACPI video driver\n");  	} else {  		struct backlight_properties props; @@ -918,7 +1100,7 @@ static int __init msi_init(void)  	if (ret)  		goto fail_platform_device1; -	if (load_scm_model && (load_scm_model_init(msipf_device) < 0)) { +	if (quirks->load_scm_model && (load_scm_model_init(msipf_device) < 0)) {  		ret = -EINVAL;  		goto fail_platform_device1;  	} @@ -928,20 +1110,25 @@ static int __init msi_init(void)  	if (ret)  		goto fail_platform_device2; -	if (!old_ec_model) { +	if (!quirks->old_ec_model) {  		if (threeg_exists)  			ret = device_create_file(&msipf_device->dev,  						&dev_attr_threeg);  		if (ret)  			goto fail_platform_device2; -	} +	} else { +		ret = sysfs_create_group(&msipf_device->dev.kobj, +					 &msipf_old_attribute_group); +		if (ret) +			goto fail_platform_device2; -	/* Disable automatic brightness control by default because -	 * this module was probably loaded to do brightness control in -	 * software. */ +		/* Disable automatic brightness control by default because +		 * this module was probably loaded to do brightness control in +		 * software. */ -	if (auto_brightness != 2) -		set_auto_brightness(auto_brightness); +		if (auto_brightness != 2) +			set_auto_brightness(auto_brightness); +	}  	pr_info("driver " MSI_DRIVER_VERSION " successfully loaded\n"); @@ -949,9 +1136,10 @@ static int __init msi_init(void)  fail_platform_device2: -	if (load_scm_model) { +	if (quirks->load_scm_model) {  		i8042_remove_filter(msi_laptop_i8042_filter); -		cancel_delayed_work_sync(&msi_rfkill_work); +		cancel_delayed_work_sync(&msi_rfkill_dwork); +		cancel_work_sync(&msi_rfkill_work);  		rfkill_cleanup();  	}  	platform_device_del(msipf_device); @@ -973,23 +1161,26 @@ fail_backlight:  static void __exit msi_cleanup(void)  { -	if (load_scm_model) { +	if (quirks->load_scm_model) {  		i8042_remove_filter(msi_laptop_i8042_filter);  		msi_laptop_input_destroy(); -		cancel_delayed_work_sync(&msi_rfkill_work); +		cancel_delayed_work_sync(&msi_rfkill_dwork); +		cancel_work_sync(&msi_rfkill_work);  		rfkill_cleanup();  	}  	sysfs_remove_group(&msipf_device->dev.kobj, &msipf_attribute_group); -	if (!old_ec_model && threeg_exists) +	if (!quirks->old_ec_model && threeg_exists)  		device_remove_file(&msipf_device->dev, &dev_attr_threeg);  	platform_device_unregister(msipf_device);  	platform_driver_unregister(&msipf_driver);  	backlight_device_unregister(msibl_device); -	/* Enable automatic brightness control again */ -	if (auto_brightness != 2) -		set_auto_brightness(1); +	if (quirks->old_ec_model) { +		/* Enable automatic brightness control again */ +		if (auto_brightness != 2) +			set_auto_brightness(1); +	}  	pr_info("driver unloaded\n");  } @@ -1011,3 +1202,4 @@ MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N051:*");  MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnMS-N014:*");  MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnCR620:*");  MODULE_ALIAS("dmi:*:svnMicro-StarInternational*:pnU270series:*"); +MODULE_ALIAS("dmi:*:svnMICRO-STARINTERNATIONAL*:pnU90/U100:*"); diff --git a/drivers/platform/x86/msi-wmi.c b/drivers/platform/x86/msi-wmi.c index 2264331bd48..70222f265f6 100644 --- a/drivers/platform/x86/msi-wmi.c +++ b/drivers/platform/x86/msi-wmi.c @@ -34,29 +34,65 @@ MODULE_AUTHOR("Thomas Renninger <trenn@suse.de>");  MODULE_DESCRIPTION("MSI laptop WMI hotkeys driver");  MODULE_LICENSE("GPL"); -MODULE_ALIAS("wmi:551A1F84-FBDD-4125-91DB-3EA8F44F1D45"); -MODULE_ALIAS("wmi:B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2"); -  #define DRV_NAME "msi-wmi"  #define MSIWMI_BIOS_GUID "551A1F84-FBDD-4125-91DB-3EA8F44F1D45" -#define MSIWMI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" +#define MSIWMI_MSI_EVENT_GUID "B6F3EEF2-3D2F-49DC-9DE3-85BCE18C62F2" +#define MSIWMI_WIND_EVENT_GUID "5B3CC38A-40D9-7245-8AE6-1145B751BE3F" + +MODULE_ALIAS("wmi:" MSIWMI_BIOS_GUID); +MODULE_ALIAS("wmi:" MSIWMI_MSI_EVENT_GUID); +MODULE_ALIAS("wmi:" MSIWMI_WIND_EVENT_GUID); -#define SCANCODE_BASE 0xD0 -#define MSI_WMI_BRIGHTNESSUP   SCANCODE_BASE -#define MSI_WMI_BRIGHTNESSDOWN (SCANCODE_BASE + 1) -#define MSI_WMI_VOLUMEUP       (SCANCODE_BASE + 2) -#define MSI_WMI_VOLUMEDOWN     (SCANCODE_BASE + 3) -#define MSI_WMI_MUTE           (SCANCODE_BASE + 4) +enum msi_scancodes { +	/* Generic MSI keys (not present on MSI Wind) */ +	MSI_KEY_BRIGHTNESSUP	= 0xD0, +	MSI_KEY_BRIGHTNESSDOWN, +	MSI_KEY_VOLUMEUP, +	MSI_KEY_VOLUMEDOWN, +	MSI_KEY_MUTE, +	/* MSI Wind keys */ +	WIND_KEY_TOUCHPAD	= 0x08,	/* Fn+F3 touchpad toggle */ +	WIND_KEY_BLUETOOTH	= 0x56,	/* Fn+F11 Bluetooth toggle */ +	WIND_KEY_CAMERA,		/* Fn+F6 webcam toggle */ +	WIND_KEY_WLAN		= 0x5f,	/* Fn+F11 Wi-Fi toggle */ +	WIND_KEY_TURBO,			/* Fn+F10 turbo mode toggle */ +	WIND_KEY_ECO		= 0x69,	/* Fn+F10 ECO mode toggle */ +};  static struct key_entry msi_wmi_keymap[] = { -	{ KE_KEY, MSI_WMI_BRIGHTNESSUP,   {KEY_BRIGHTNESSUP} }, -	{ KE_KEY, MSI_WMI_BRIGHTNESSDOWN, {KEY_BRIGHTNESSDOWN} }, -	{ KE_KEY, MSI_WMI_VOLUMEUP,       {KEY_VOLUMEUP} }, -	{ KE_KEY, MSI_WMI_VOLUMEDOWN,     {KEY_VOLUMEDOWN} }, -	{ KE_KEY, MSI_WMI_MUTE,           {KEY_MUTE} }, -	{ KE_END, 0} +	{ KE_KEY, MSI_KEY_BRIGHTNESSUP,		{KEY_BRIGHTNESSUP} }, +	{ KE_KEY, MSI_KEY_BRIGHTNESSDOWN,	{KEY_BRIGHTNESSDOWN} }, +	{ KE_KEY, MSI_KEY_VOLUMEUP,		{KEY_VOLUMEUP} }, +	{ KE_KEY, MSI_KEY_VOLUMEDOWN,		{KEY_VOLUMEDOWN} }, +	{ KE_KEY, MSI_KEY_MUTE,			{KEY_MUTE} }, + +	/* These keys work without WMI. Ignore them to avoid double keycodes */ +	{ KE_IGNORE, WIND_KEY_TOUCHPAD,		{KEY_TOUCHPAD_TOGGLE} }, +	{ KE_IGNORE, WIND_KEY_BLUETOOTH,	{KEY_BLUETOOTH} }, +	{ KE_IGNORE, WIND_KEY_CAMERA,		{KEY_CAMERA} }, +	{ KE_IGNORE, WIND_KEY_WLAN,		{KEY_WLAN} }, + +	/* These are unknown WMI events found on MSI Wind */ +	{ KE_IGNORE, 0x00 }, +	{ KE_IGNORE, 0x62 }, +	{ KE_IGNORE, 0x63 }, + +	/* These are MSI Wind keys that should be handled via WMI */ +	{ KE_KEY, WIND_KEY_TURBO,		{KEY_PROG1} }, +	{ KE_KEY, WIND_KEY_ECO,			{KEY_PROG2} }, + +	{ KE_END, 0 } +}; + +static ktime_t last_pressed; + +static const struct { +	const char *guid; +	bool quirk_last_pressed; +} *event_wmi, event_wmis[] = { +	{ MSIWMI_MSI_EVENT_GUID, true }, +	{ MSIWMI_WIND_EVENT_GUID, false },  }; -static ktime_t last_pressed[ARRAY_SIZE(msi_wmi_keymap) - 1];  static struct backlight_device *backlight; @@ -149,7 +185,6 @@ static void msi_wmi_notify(u32 value, void *context)  	struct acpi_buffer response = { ACPI_ALLOCATE_BUFFER, NULL };  	static struct key_entry *key;  	union acpi_object *obj; -	ktime_t cur;  	acpi_status status;  	status = wmi_get_event_data(value, &response); @@ -165,39 +200,67 @@ static void msi_wmi_notify(u32 value, void *context)  		pr_debug("Eventcode: 0x%x\n", eventcode);  		key = sparse_keymap_entry_from_scancode(msi_wmi_input_dev,  				eventcode); -		if (key) { -			ktime_t diff; -			cur = ktime_get_real(); -			diff = ktime_sub(cur, last_pressed[key->code - -					SCANCODE_BASE]); -			/* Ignore event if the same event happened in a 50 ms +		if (!key) { +			pr_info("Unknown key pressed - %x\n", eventcode); +			goto msi_wmi_notify_exit; +		} + +		if (event_wmi->quirk_last_pressed) { +			ktime_t cur = ktime_get_real(); +			ktime_t diff = ktime_sub(cur, last_pressed); +			/* Ignore event if any event happened in a 50 ms  			   timeframe -> Key press may result in 10-20 GPEs */  			if (ktime_to_us(diff) < 1000 * 50) {  				pr_debug("Suppressed key event 0x%X - "  					 "Last press was %lld us ago\n",  					 key->code, ktime_to_us(diff)); -				return; +				goto msi_wmi_notify_exit;  			} -			last_pressed[key->code - SCANCODE_BASE] = cur; +			last_pressed = cur; +		} -			if (key->type == KE_KEY && -			/* Brightness is served via acpi video driver */ -			(!acpi_video_backlight_support() || -			(key->code != MSI_WMI_BRIGHTNESSUP && -			key->code != MSI_WMI_BRIGHTNESSDOWN))) { -				pr_debug("Send key: 0x%X - " -					 "Input layer keycode: %d\n", -					 key->code, key->keycode); -				sparse_keymap_report_entry(msi_wmi_input_dev, -						key, 1, true); -			} -		} else -			pr_info("Unknown key pressed - %x\n", eventcode); +		if (key->type == KE_KEY && +		/* Brightness is served via acpi video driver */ +		(backlight || +		(key->code != MSI_KEY_BRIGHTNESSUP && +		key->code != MSI_KEY_BRIGHTNESSDOWN))) { +			pr_debug("Send key: 0x%X - Input layer keycode: %d\n", +				 key->code, key->keycode); +			sparse_keymap_report_entry(msi_wmi_input_dev, key, 1, +						   true); +		}  	} else  		pr_info("Unknown event received\n"); + +msi_wmi_notify_exit:  	kfree(response.pointer);  } +static int __init msi_wmi_backlight_setup(void) +{ +	int err; +	struct backlight_properties props; + +	memset(&props, 0, sizeof(struct backlight_properties)); +	props.type = BACKLIGHT_PLATFORM; +	props.max_brightness = ARRAY_SIZE(backlight_map) - 1; +	backlight = backlight_device_register(DRV_NAME, NULL, NULL, +					      &msi_backlight_ops, +					      &props); +	if (IS_ERR(backlight)) +		return PTR_ERR(backlight); + +	err = bl_get(NULL); +	if (err < 0) { +		backlight_device_unregister(backlight); +		return err; +	} + +	backlight->props.brightness = err; + +	return 0; +} +  static int __init msi_wmi_input_setup(void)  {  	int err; @@ -219,7 +282,7 @@ static int __init msi_wmi_input_setup(void)  	if (err)  		goto err_free_keymap; -	memset(last_pressed, 0, sizeof(last_pressed)); +	last_pressed = ktime_set(0, 0);  	return 0; @@ -233,61 +296,66 @@ err_free_dev:  static int __init msi_wmi_init(void)  {  	int err; +	int i; -	if (!wmi_has_guid(MSIWMI_EVENT_GUID)) { -		pr_err("This machine doesn't have MSI-hotkeys through WMI\n"); -		return -ENODEV; -	} -	err = wmi_install_notify_handler(MSIWMI_EVENT_GUID, -			msi_wmi_notify, NULL); -	if (ACPI_FAILURE(err)) -		return -EINVAL; +	for (i = 0; i < ARRAY_SIZE(event_wmis); i++) { +		if (!wmi_has_guid(event_wmis[i].guid)) +			continue; -	err = msi_wmi_input_setup(); -	if (err) -		goto err_uninstall_notifier; +		err = msi_wmi_input_setup(); +		if (err) { +			pr_err("Unable to setup input device\n"); +			return err; +		} -	if (!acpi_video_backlight_support()) { -		struct backlight_properties props; -		memset(&props, 0, sizeof(struct backlight_properties)); -		props.type = BACKLIGHT_PLATFORM; -		props.max_brightness = ARRAY_SIZE(backlight_map) - 1; -		backlight = backlight_device_register(DRV_NAME, NULL, NULL, -						      &msi_backlight_ops, -						      &props); -		if (IS_ERR(backlight)) { -			err = PTR_ERR(backlight); +		err = wmi_install_notify_handler(event_wmis[i].guid, +			msi_wmi_notify, NULL); +		if (ACPI_FAILURE(err)) { +			pr_err("Unable to setup WMI notify handler\n");  			goto err_free_input;  		} -		err = bl_get(NULL); -		if (err < 0) -			goto err_free_backlight; +		pr_debug("Event handler installed\n"); +		event_wmi = &event_wmis[i]; +		break; +	} + +	if (wmi_has_guid(MSIWMI_BIOS_GUID) && !acpi_video_backlight_support()) { +		err = msi_wmi_backlight_setup(); +		if (err) { +			pr_err("Unable to setup backlight device\n"); +			goto err_uninstall_handler; +		} +		pr_debug("Backlight device created\n"); +	} -		backlight->props.brightness = err; +	if (!event_wmi && !backlight) { +		pr_err("This machine doesn't have neither MSI-hotkeys nor backlight through WMI\n"); +		return -ENODEV;  	} -	pr_debug("Event handler installed\n");  	return 0; -err_free_backlight: -	backlight_device_unregister(backlight); +err_uninstall_handler: +	if (event_wmi) +		wmi_remove_notify_handler(event_wmi->guid);  err_free_input: -	sparse_keymap_free(msi_wmi_input_dev); -	input_unregister_device(msi_wmi_input_dev); -err_uninstall_notifier: -	wmi_remove_notify_handler(MSIWMI_EVENT_GUID); +	if (event_wmi) { +		sparse_keymap_free(msi_wmi_input_dev); +		input_unregister_device(msi_wmi_input_dev); +	}  	return err;  }  static void __exit msi_wmi_exit(void)  { -	if (wmi_has_guid(MSIWMI_EVENT_GUID)) { -		wmi_remove_notify_handler(MSIWMI_EVENT_GUID); +	if (event_wmi) { +		wmi_remove_notify_handler(event_wmi->guid);  		sparse_keymap_free(msi_wmi_input_dev);  		input_unregister_device(msi_wmi_input_dev); -		backlight_device_unregister(backlight);  	} +	if (backlight) +		backlight_device_unregister(backlight);  }  module_init(msi_wmi_init); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 8e8caa767d6..4add9a31bf6 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -176,7 +176,7 @@ enum SINF_BITS { SINF_NUM_BATTERIES = 0,  /* R1 handles SINF_AC_CUR_BRIGHT as SINF_CUR_BRIGHT, doesn't know AC state */  static int acpi_pcc_hotkey_add(struct acpi_device *device); -static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type); +static int acpi_pcc_hotkey_remove(struct acpi_device *device);  static void acpi_pcc_hotkey_notify(struct acpi_device *device, u32 event);  static const struct acpi_device_id pcc_device_ids[] = { @@ -663,7 +663,7 @@ static int __init acpi_pcc_init(void)  	return 0;  } -static int acpi_pcc_hotkey_remove(struct acpi_device *device, int type) +static int acpi_pcc_hotkey_remove(struct acpi_device *device)  {  	struct pcc_acpi *pcc = acpi_driver_data(device); diff --git a/drivers/platform/x86/samsung-laptop.c b/drivers/platform/x86/samsung-laptop.c index dd90d15f521..d1f03005317 100644 --- a/drivers/platform/x86/samsung-laptop.c +++ b/drivers/platform/x86/samsung-laptop.c @@ -26,6 +26,7 @@  #include <linux/seq_file.h>  #include <linux/debugfs.h>  #include <linux/ctype.h> +#include <linux/efi.h>  #include <acpi/video.h>  /* @@ -1523,6 +1524,16 @@ static struct dmi_system_id __initdata samsung_dmi_table[] = {  		},  	 .driver_data = &samsung_broken_acpi_video,  	}, +	{ +	 .callback = samsung_dmi_matched, +	 .ident = "N250P", +	 .matches = { +		DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."), +		DMI_MATCH(DMI_PRODUCT_NAME, "N250P"), +		DMI_MATCH(DMI_BOARD_NAME, "N250P"), +		}, +	 .driver_data = &samsung_broken_acpi_video, +	},  	{ },  };  MODULE_DEVICE_TABLE(dmi, samsung_dmi_table); @@ -1534,6 +1545,9 @@ static int __init samsung_init(void)  	struct samsung_laptop *samsung;  	int ret; +	if (efi_enabled(EFI_BOOT)) +		return -ENODEV; +  	quirks = &samsung_unknown;  	if (!force && !dmi_check_system(samsung_dmi_table))  		return -ENODEV; diff --git a/drivers/platform/x86/samsung-q10.c b/drivers/platform/x86/samsung-q10.c index 1e54ae74274..5f770059fd4 100644 --- a/drivers/platform/x86/samsung-q10.c +++ b/drivers/platform/x86/samsung-q10.c @@ -77,7 +77,7 @@ static int samsungq10_resume(struct device *dev)  static SIMPLE_DEV_PM_OPS(samsungq10_pm_ops,  			  samsungq10_suspend, samsungq10_resume); -static int __devinit samsungq10_probe(struct platform_device *pdev) +static int samsungq10_probe(struct platform_device *pdev)  {  	struct backlight_properties props; @@ -99,7 +99,7 @@ static int __devinit samsungq10_probe(struct platform_device *pdev)  	return 0;  } -static int __devexit samsungq10_remove(struct platform_device *pdev) +static int samsungq10_remove(struct platform_device *pdev)  {  	struct backlight_device *bd = platform_get_drvdata(pdev); @@ -119,7 +119,7 @@ static struct platform_driver samsungq10_driver = {  		.pm	= &samsungq10_pm_ops,  	},  	.probe		= samsungq10_probe, -	.remove		= __devexit_p(samsungq10_remove), +	.remove		= samsungq10_remove,  };  static struct platform_device *samsungq10_device; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index daaddec68de..14d4dced1de 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -158,6 +158,11 @@ static void sony_nc_thermal_cleanup(struct platform_device *pd);  static int sony_nc_lid_resume_setup(struct platform_device *pd);  static void sony_nc_lid_resume_cleanup(struct platform_device *pd); +static int sony_nc_gfx_switch_setup(struct platform_device *pd, +		unsigned int handle); +static void sony_nc_gfx_switch_cleanup(struct platform_device *pd); +static int __sony_nc_gfx_switch_status_get(void); +  static int sony_nc_highspeed_charging_setup(struct platform_device *pd);  static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); @@ -786,28 +791,29 @@ static int sony_nc_int_call(acpi_handle handle, char *name, int *value,  static int sony_nc_buffer_call(acpi_handle handle, char *name, u64 *value,  		void *buffer, size_t buflen)  { +	int ret = 0;  	size_t len = len;  	union acpi_object *object = __call_snc_method(handle, name, value);  	if (!object)  		return -EINVAL; -	if (object->type == ACPI_TYPE_BUFFER) +	if (object->type == ACPI_TYPE_BUFFER) {  		len = MIN(buflen, object->buffer.length); +		memcpy(buffer, object->buffer.pointer, len); -	else if (object->type == ACPI_TYPE_INTEGER) +	} else if (object->type == ACPI_TYPE_INTEGER) {  		len = MIN(buflen, sizeof(object->integer.value)); +		memcpy(buffer, &object->integer.value, len); -	else { +	} else {  		pr_warn("Invalid acpi_object: expected 0x%x got 0x%x\n",  				ACPI_TYPE_BUFFER, object->type); -		kfree(object); -		return -EINVAL; +		ret = -EINVAL;  	} -	memcpy(buffer, object->buffer.pointer, len);  	kfree(object); -	return 0; +	return ret;  }  struct sony_nc_handles { @@ -1240,17 +1246,13 @@ static void sony_nc_notify(struct acpi_device *device, u32 event)  			/* Hybrid GFX switching */  			sony_call_snc_handle(handle, 0x0000, &result);  			dprintk("GFX switch event received (reason: %s)\n", -					(result & 0x01) ? -					"switch change" : "unknown"); - -			/* verify the switch state -			 * 1: discrete GFX -			 * 0: integrated GFX -			 */ -			sony_call_snc_handle(handle, 0x0100, &result); +					(result == 0x1) ? "switch change" : +					(result == 0x2) ? "output switch" : +					(result == 0x3) ? "output switch" : +					"");  			ev_type = GFX_SWITCH; -			real_ev = result & 0xff; +			real_ev = __sony_nc_gfx_switch_status_get();  			break;  		default: @@ -1349,6 +1351,13 @@ static void sony_nc_function_setup(struct acpi_device *device,  				pr_err("couldn't set up thermal profile function (%d)\n",  						result);  			break; +		case 0x0128: +		case 0x0146: +			result = sony_nc_gfx_switch_setup(pf_device, handle); +			if (result) +				pr_err("couldn't set up GFX Switch status (%d)\n", +						result); +			break;  		case 0x0131:  			result = sony_nc_highspeed_charging_setup(pf_device);  			if (result) @@ -1364,6 +1373,8 @@ static void sony_nc_function_setup(struct acpi_device *device,  			break;  		case 0x0137:  		case 0x0143: +		case 0x014b: +		case 0x014c:  			result = sony_nc_kbd_backlight_setup(pf_device, handle);  			if (result)  				pr_err("couldn't set up keyboard backlight function (%d)\n", @@ -1413,6 +1424,10 @@ static void sony_nc_function_cleanup(struct platform_device *pd)  		case 0x0122:  			sony_nc_thermal_cleanup(pd);  			break; +		case 0x0128: +		case 0x0146: +			sony_nc_gfx_switch_cleanup(pd); +			break;  		case 0x0131:  			sony_nc_highspeed_charging_cleanup(pd);  			break; @@ -1422,6 +1437,8 @@ static void sony_nc_function_cleanup(struct platform_device *pd)  			break;  		case 0x0137:  		case 0x0143: +		case 0x014b: +		case 0x014c:  			sony_nc_kbd_backlight_cleanup(pd);  			break;  		default: @@ -1466,6 +1483,8 @@ static void sony_nc_function_resume(void)  			break;  		case 0x0137:  		case 0x0143: +		case 0x014b: +		case 0x014c:  			sony_nc_kbd_backlight_resume();  			break;  		default: @@ -1533,7 +1552,7 @@ static int sony_nc_rfkill_set(void *data, bool blocked)  	int argument = sony_rfkill_address[(long) data] + 0x100;  	if (!blocked) -		argument |= 0x030000; +		argument |= 0x070000;  	return sony_call_snc_handle(sony_rfkill_handle, argument, &result);  } @@ -2332,7 +2351,7 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd)  	return 0;  liderror: -	for (; i > 0; i--) +	for (i--; i >= 0; i--)  		device_remove_file(&pd->dev, &lid_ctl->attrs[i]);  	kfree(lid_ctl); @@ -2354,6 +2373,97 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd)  	}  } +/* GFX Switch position */ +enum gfx_switch { +	SPEED, +	STAMINA, +	AUTO +}; +struct snc_gfx_switch_control { +	struct device_attribute attr; +	unsigned int handle; +}; +static struct snc_gfx_switch_control *gfxs_ctl; + +/* returns 0 for speed, 1 for stamina */ +static int __sony_nc_gfx_switch_status_get(void) +{ +	unsigned int result; + +	if (sony_call_snc_handle(gfxs_ctl->handle, 0x0100, &result)) +		return -EIO; + +	switch (gfxs_ctl->handle) { +	case 0x0146: +		/* 1: discrete GFX (speed) +		 * 0: integrated GFX (stamina) +		 */ +		return result & 0x1 ? SPEED : STAMINA; +		break; +	case 0x0128: +		/* it's a more elaborated bitmask, for now: +		 * 2: integrated GFX (stamina) +		 * 0: discrete GFX (speed) +		 */ +		dprintk("GFX Status: 0x%x\n", result); +		return result & 0x80 ? AUTO : +			result & 0x02 ? STAMINA : SPEED; +		break; +	} +	return -EINVAL; +} + +static ssize_t sony_nc_gfx_switch_status_show(struct device *dev, +				       struct device_attribute *attr, +				       char *buffer) +{ +	int pos = __sony_nc_gfx_switch_status_get(); + +	if (pos < 0) +		return pos; + +	return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina"); +} + +static int sony_nc_gfx_switch_setup(struct platform_device *pd, +		unsigned int handle) +{ +	unsigned int result; + +	gfxs_ctl = kzalloc(sizeof(struct snc_gfx_switch_control), GFP_KERNEL); +	if (!gfxs_ctl) +		return -ENOMEM; + +	gfxs_ctl->handle = handle; + +	sysfs_attr_init(&gfxs_ctl->attr.attr); +	gfxs_ctl->attr.attr.name = "gfx_switch_status"; +	gfxs_ctl->attr.attr.mode = S_IRUGO; +	gfxs_ctl->attr.show = sony_nc_gfx_switch_status_show; + +	result = device_create_file(&pd->dev, &gfxs_ctl->attr); +	if (result) +		goto gfxerror; + +	return 0; + +gfxerror: +	kfree(gfxs_ctl); +	gfxs_ctl = NULL; + +	return result; +} + +static void sony_nc_gfx_switch_cleanup(struct platform_device *pd) +{ +	if (gfxs_ctl) { +		device_remove_file(&pd->dev, &gfxs_ctl->attr); + +		kfree(gfxs_ctl); +		gfxs_ctl = NULL; +	} +} +  /* High speed charging function */  static struct device_attribute *hsc_handle; @@ -2532,6 +2642,8 @@ static void sony_nc_backlight_ng_read_limits(int handle,  		lvl_table_len = 9;  		break;  	case 0x143: +	case 0x14b: +	case 0x14c:  		lvl_table_len = 16;  		break;  	} @@ -2583,6 +2695,18 @@ static void sony_nc_backlight_setup(void)  		sony_nc_backlight_ng_read_limits(0x143, &sony_bl_props);  		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; +	} else if (sony_find_snc_handle(0x14b) >= 0) { +		ops = &sony_backlight_ng_ops; +		sony_bl_props.cmd_base = 0x3000; +		sony_nc_backlight_ng_read_limits(0x14b, &sony_bl_props); +		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; + +	} else if (sony_find_snc_handle(0x14c) >= 0) { +		ops = &sony_backlight_ng_ops; +		sony_bl_props.cmd_base = 0x3000; +		sony_nc_backlight_ng_read_limits(0x14c, &sony_bl_props); +		max_brightness = sony_bl_props.maxlvl - sony_bl_props.offset; +  	} else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT",  						&unused))) {  		ops = &sony_backlight_ops; @@ -2739,7 +2863,7 @@ outwalk:  	return result;  } -static int sony_nc_remove(struct acpi_device *device, int type) +static int sony_nc_remove(struct acpi_device *device)  {  	struct sony_nc_value *item; @@ -3565,7 +3689,7 @@ static ssize_t sonypi_misc_read(struct file *file, char __user *buf,  	}  	if (ret > 0) { -		struct inode *inode = file->f_path.dentry->d_inode; +		struct inode *inode = file_inode(file);  		inode->i_atime = current_fs_time(inode->i_sb);  	} @@ -4110,7 +4234,7 @@ found:   *  ACPI driver   *   *****************/ -static int sony_pic_remove(struct acpi_device *device, int type) +static int sony_pic_remove(struct acpi_device *device)  {  	struct sony_pic_ioport *io, *tmp_io;  	struct sony_pic_irq *irq, *tmp_irq; diff --git a/drivers/platform/x86/tc1100-wmi.c b/drivers/platform/x86/tc1100-wmi.c index e24f5ae475a..9b93fdb61ed 100644 --- a/drivers/platform/x86/tc1100-wmi.c +++ b/drivers/platform/x86/tc1100-wmi.c @@ -187,7 +187,7 @@ static int __init tc1100_probe(struct platform_device *device)  } -static int __devexit tc1100_remove(struct platform_device *device) +static int tc1100_remove(struct platform_device *device)  {  	sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); @@ -241,7 +241,7 @@ static struct platform_driver tc1100_driver = {  		.pm = &tc1100_pm_ops,  #endif  	}, -	.remove = __devexit_p(tc1100_remove), +	.remove = tc1100_remove,  };  static int __init tc1100_init(void) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 75dd651664a..9a907567f41 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -209,9 +209,8 @@ enum tpacpi_hkey_event_t {  	TP_HKEY_EV_ALARM_SENSOR_XHOT	= 0x6022, /* sensor critically hot */  	TP_HKEY_EV_THM_TABLE_CHANGED	= 0x6030, /* thermal table changed */ -	TP_HKEY_EV_UNK_6040		= 0x6040, /* Related to AC change? -						     some sort of APM hint, -						     W520 */ +	/* AC-related events */ +	TP_HKEY_EV_AC_CHANGED		= 0x6040, /* AC status changed */  	/* Misc */  	TP_HKEY_EV_RFKILL_CHANGED	= 0x7000, /* rfkill switch changed */ @@ -852,7 +851,7 @@ static ssize_t dispatch_proc_write(struct file *file,  			const char __user *userbuf,  			size_t count, loff_t *pos)  { -	struct ibm_struct *ibm = PDE(file->f_path.dentry->d_inode)->data; +	struct ibm_struct *ibm = PDE(file_inode(file))->data;  	char *kernbuf;  	int ret; @@ -3629,6 +3628,12 @@ static bool hotkey_notify_6xxx(const u32 hkey,  			 "a sensor reports something is extremely hot!\n");  		/* recommended action: immediate sleep/hibernate */  		break; +	case TP_HKEY_EV_AC_CHANGED: +		/* X120e, X121e, X220, X220i, X220t, X230, T420, T420s, W520: +		 * AC status changed; can be triggered by plugging or +		 * unplugging AC adapter, docking or undocking. */ + +		/* fallthrough */  	case TP_HKEY_EV_KEY_NUMLOCK:  	case TP_HKEY_EV_KEY_FN: @@ -4877,8 +4882,7 @@ static int __init light_init(struct ibm_init_struct *iibm)  static void light_exit(void)  {  	led_classdev_unregister(&tpacpi_led_thinklight.led_classdev); -	if (work_pending(&tpacpi_led_thinklight.work)) -		flush_workqueue(tpacpi_wq); +	flush_workqueue(tpacpi_wq);  }  static int light_read(struct seq_file *m) @@ -6732,7 +6736,7 @@ static int volume_alsa_mute_put(struct snd_kcontrol *kcontrol,  	return volume_alsa_set_mute(!ucontrol->value.integer.value[0]);  } -static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = { +static struct snd_kcontrol_new volume_alsa_control_vol = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  	.name = "Console Playback Volume",  	.index = 0, @@ -6741,7 +6745,7 @@ static struct snd_kcontrol_new volume_alsa_control_vol __devinitdata = {  	.get = volume_alsa_vol_get,  }; -static struct snd_kcontrol_new volume_alsa_control_mute __devinitdata = { +static struct snd_kcontrol_new volume_alsa_control_mute = {  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,  	.name = "Console Playback Switch",  	.index = 0, @@ -8575,7 +8579,8 @@ static bool __pure __init tpacpi_is_valid_fw_id(const char* const s,  	return s && strlen(s) >= 8 &&  		tpacpi_is_fw_digit(s[0]) &&  		tpacpi_is_fw_digit(s[1]) && -		s[2] == t && s[3] == 'T' && +		s[2] == t && +		(s[3] == 'T' || s[3] == 'N') &&  		tpacpi_is_fw_digit(s[4]) &&  		tpacpi_is_fw_digit(s[5]);  } @@ -8608,7 +8613,8 @@ static int __must_check __init get_thinkpad_model_data(  		return -ENOMEM;  	/* Really ancient ThinkPad 240X will fail this, which is fine */ -	if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E')) +	if (!(tpacpi_is_valid_fw_id(tp->bios_version_str, 'E') || +	      tpacpi_is_valid_fw_id(tp->bios_version_str, 'C')))  		return 0;  	tp->bios_model = tp->bios_version_str[0] diff --git a/drivers/platform/x86/topstar-laptop.c b/drivers/platform/x86/topstar-laptop.c index d727bfee89a..4ab618c63b4 100644 --- a/drivers/platform/x86/topstar-laptop.c +++ b/drivers/platform/x86/topstar-laptop.c @@ -157,7 +157,7 @@ add_err:  	return -ENODEV;  } -static int acpi_topstar_remove(struct acpi_device *device, int type) +static int acpi_topstar_remove(struct acpi_device *device)  {  	struct topstar_hkey *tps_hkey = acpi_driver_data(device); diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 5f1256d5e93..242abac62d8 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -150,7 +150,7 @@ static const struct acpi_device_id toshiba_device_ids[] = {  };  MODULE_DEVICE_TABLE(acpi, toshiba_device_ids); -static const struct key_entry toshiba_acpi_keymap[] __devinitconst = { +static const struct key_entry toshiba_acpi_keymap[] = {  	{ KE_KEY, 0x101, { KEY_MUTE } },  	{ KE_KEY, 0x102, { KEY_ZOOMOUT } },  	{ KE_KEY, 0x103, { KEY_ZOOMIN } }, @@ -583,7 +583,7 @@ static int set_lcd_status(struct backlight_device *bd)  static ssize_t lcd_proc_write(struct file *file, const char __user *buf,  			      size_t count, loff_t *pos)  { -	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; +	struct toshiba_acpi_dev *dev = PDE(file_inode(file))->data;  	char cmd[42];  	size_t len;  	int value; @@ -650,7 +650,7 @@ static int video_proc_open(struct inode *inode, struct file *file)  static ssize_t video_proc_write(struct file *file, const char __user *buf,  				size_t count, loff_t *pos)  { -	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; +	struct toshiba_acpi_dev *dev = PDE(file_inode(file))->data;  	char *cmd, *buffer;  	int ret;  	int value; @@ -750,7 +750,7 @@ static int fan_proc_open(struct inode *inode, struct file *file)  static ssize_t fan_proc_write(struct file *file, const char __user *buf,  			      size_t count, loff_t *pos)  { -	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; +	struct toshiba_acpi_dev *dev = PDE(file_inode(file))->data;  	char cmd[42];  	size_t len;  	int value; @@ -822,7 +822,7 @@ static int keys_proc_open(struct inode *inode, struct file *file)  static ssize_t keys_proc_write(struct file *file, const char __user *buf,  			       size_t count, loff_t *pos)  { -	struct toshiba_acpi_dev *dev = PDE(file->f_path.dentry->d_inode)->data; +	struct toshiba_acpi_dev *dev = PDE(file_inode(file))->data;  	char cmd[42];  	size_t len;  	int value; @@ -875,8 +875,7 @@ static const struct file_operations version_proc_fops = {  #define PROC_TOSHIBA		"toshiba" -static void __devinit -create_toshiba_proc_entries(struct toshiba_acpi_dev *dev) +static void create_toshiba_proc_entries(struct toshiba_acpi_dev *dev)  {  	if (dev->backlight_dev)  		proc_create_data("lcd", S_IRUGO | S_IWUSR, toshiba_proc_dir, @@ -979,7 +978,7 @@ static void toshiba_acpi_report_hotkey(struct toshiba_acpi_dev *dev,  		pr_info("Unknown key %x\n", scancode);  } -static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev) +static int toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)  {  	acpi_status status;  	acpi_handle ec_handle, handle; @@ -1069,7 +1068,7 @@ static int __devinit toshiba_acpi_setup_keyboard(struct toshiba_acpi_dev *dev)  	return error;  } -static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev) +static int toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)  {  	struct backlight_properties props;  	int brightness; @@ -1119,7 +1118,7 @@ static int __devinit toshiba_acpi_setup_backlight(struct toshiba_acpi_dev *dev)  	return 0;  } -static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type) +static int toshiba_acpi_remove(struct acpi_device *acpi_dev)  {  	struct toshiba_acpi_dev *dev = acpi_driver_data(acpi_dev); @@ -1154,7 +1153,7 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev, int type)  	return 0;  } -static const char * __devinit find_hci_method(acpi_handle handle) +static const char *find_hci_method(acpi_handle handle)  {  	acpi_status status;  	acpi_handle hci_handle; @@ -1170,7 +1169,7 @@ static const char * __devinit find_hci_method(acpi_handle handle)  	return NULL;  } -static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev) +static int toshiba_acpi_add(struct acpi_device *acpi_dev)  {  	struct toshiba_acpi_dev *dev;  	const char *hci_method; @@ -1251,7 +1250,7 @@ static int __devinit toshiba_acpi_add(struct acpi_device *acpi_dev)  	return 0;  error: -	toshiba_acpi_remove(acpi_dev, 0); +	toshiba_acpi_remove(acpi_dev);  	return ret;  } diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c index e95be0b7485..74dd01ae343 100644 --- a/drivers/platform/x86/toshiba_bluetooth.c +++ b/drivers/platform/x86/toshiba_bluetooth.c @@ -32,7 +32,7 @@ MODULE_LICENSE("GPL");  static int toshiba_bt_rfkill_add(struct acpi_device *device); -static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type); +static int toshiba_bt_rfkill_remove(struct acpi_device *device);  static void toshiba_bt_rfkill_notify(struct acpi_device *device, u32 event);  static const struct acpi_device_id bt_device_ids[] = { @@ -122,7 +122,7 @@ static int toshiba_bt_rfkill_add(struct acpi_device *device)  	return result;  } -static int toshiba_bt_rfkill_remove(struct acpi_device *device, int type) +static int toshiba_bt_rfkill_remove(struct acpi_device *device)  {  	/* clean up */  	return 0; diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 42a4dcc25f9..e4ac38aca58 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -92,7 +92,7 @@ module_param(debug_dump_wdg, bool, 0444);  MODULE_PARM_DESC(debug_dump_wdg,  		 "Dump available WMI interfaces [0/1]"); -static int acpi_wmi_remove(struct acpi_device *device, int type); +static int acpi_wmi_remove(struct acpi_device *device);  static int acpi_wmi_add(struct acpi_device *device);  static void acpi_wmi_notify(struct acpi_device *device, u32 event); @@ -917,7 +917,7 @@ static void acpi_wmi_notify(struct acpi_device *device, u32 event)  	}  } -static int acpi_wmi_remove(struct acpi_device *device, int type) +static int acpi_wmi_remove(struct acpi_device *device)  {  	acpi_remove_address_space_handler(device->handle,  				ACPI_ADR_SPACE_EC, &acpi_wmi_ec_space_handler); diff --git a/drivers/platform/x86/xo1-rfkill.c b/drivers/platform/x86/xo1-rfkill.c index 1da13ed34b0..4bd17248dfc 100644 --- a/drivers/platform/x86/xo1-rfkill.c +++ b/drivers/platform/x86/xo1-rfkill.c @@ -40,7 +40,7 @@ static const struct rfkill_ops rfkill_ops = {  	.set_block = rfkill_set_block,  }; -static int __devinit xo1_rfkill_probe(struct platform_device *pdev) +static int xo1_rfkill_probe(struct platform_device *pdev)  {  	struct rfkill *rfk;  	int r; @@ -60,7 +60,7 @@ static int __devinit xo1_rfkill_probe(struct platform_device *pdev)  	return 0;  } -static int __devexit xo1_rfkill_remove(struct platform_device *pdev) +static int xo1_rfkill_remove(struct platform_device *pdev)  {  	struct rfkill *rfk = platform_get_drvdata(pdev);  	rfkill_unregister(rfk); @@ -74,7 +74,7 @@ static struct platform_driver xo1_rfkill_driver = {  		.owner = THIS_MODULE,  	},  	.probe		= xo1_rfkill_probe, -	.remove		= __devexit_p(xo1_rfkill_remove), +	.remove		= xo1_rfkill_remove,  };  module_platform_driver(xo1_rfkill_driver); diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c index 16d340c3b85..4b1377bd594 100644 --- a/drivers/platform/x86/xo15-ebook.c +++ b/drivers/platform/x86/xo15-ebook.c @@ -150,7 +150,7 @@ static int ebook_switch_add(struct acpi_device *device)  	return error;  } -static int ebook_switch_remove(struct acpi_device *device, int type) +static int ebook_switch_remove(struct acpi_device *device)  {  	struct ebook_switch *button = acpi_driver_data(device);  |