Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (35 commits)
  Input: add driver for Synaptics I2C touchpad
  Input: synaptics - add support for reporting x/y resolution
  Input: ALPS - handle touchpoints buttons correctly
  Input: gpio-keys - change timer to workqueue
  Input: ads7846 - pin change interrupt support
  Input: add support for touchscreen on W90P910 ARM platform
  Input: appletouch - improve finger detection
  Input: wacom - clear Intuos4 wheel data when finger leaves proximity
  Input: ucb1400 - move static function from header into core
  Input: add driver for EETI touchpanels
  Input: ads7846 - more detailed model name in sysfs
  Input: ads7846 - support swapping x and y axes
  Input: ati_remote2 - use non-atomic bitops
  Input: introduce lm8323 keypad driver
  Input: psmouse - ESD workaround fix for OLPC XO touchpad
  Input: tsc2007 - make sure platform provides get_pendown_state()
  Input: uinput - flush all pending ff effects before destroying device
  Input: simplify name handling for certain input handles
  Input: serio - do not use deprecated dev.power.power_state
  Input: wacom - add support for Intuos4 tablets
  ...
This commit is contained in:
Linus Torvalds 2009-06-20 10:17:02 -07:00
commit 93db629495
52 changed files with 4256 additions and 244 deletions

View file

@ -278,7 +278,7 @@ struct input_event {
};
'time' is the timestamp, it returns the time at which the event happened.
Type is for example EV_REL for relative moment, REL_KEY for a keypress or
Type is for example EV_REL for relative moment, EV_KEY for a keypress or
release. More types are defined in include/linux/input.h.
'code' is event code, for example REL_X or KEY_BACKSPACE, again a complete

View file

@ -67,7 +67,12 @@ data with it.
struct rotary_encoder_platform_data is declared in
include/linux/rotary-encoder.h and needs to be filled with the number of
steps the encoder has and can carry information about externally inverted
signals (because of used invertig buffer or other reasons).
signals (because of an inverting buffer or other reasons). The encoder
can be set up to deliver input information as either an absolute or relative
axes. For relative axes the input event returns +/-1 for each step. For
absolute axes the position of the encoder can either roll over between zero
and the number of steps or will clamp at the maximum and zero depending on
the configuration.
Because GPIO to IRQ mapping is platform specific, this information must
be given in seperately to the driver. See the example below.
@ -85,6 +90,8 @@ be given in seperately to the driver. See the example below.
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
.steps = 24,
.axis = ABS_X,
.relative_axis = false,
.rollover = false,
.gpio_a = GPIO_ROTARY_A,
.gpio_b = GPIO_ROTARY_B,
.inverted_a = 0,

View file

@ -0,0 +1,42 @@
/*
* arch/arm/mach-ep93xx/include/mach/ep93xx_keypad.h
*/
#ifndef __ASM_ARCH_EP93XX_KEYPAD_H
#define __ASM_ARCH_EP93XX_KEYPAD_H
#define MAX_MATRIX_KEY_ROWS (8)
#define MAX_MATRIX_KEY_COLS (8)
/* flags for the ep93xx_keypad driver */
#define EP93XX_KEYPAD_DISABLE_3_KEY (1<<0) /* disable 3-key reset */
#define EP93XX_KEYPAD_DIAG_MODE (1<<1) /* diagnostic mode */
#define EP93XX_KEYPAD_BACK_DRIVE (1<<2) /* back driving mode */
#define EP93XX_KEYPAD_TEST_MODE (1<<3) /* scan only column 0 */
#define EP93XX_KEYPAD_KDIV (1<<4) /* 1/4 clock or 1/16 clock */
#define EP93XX_KEYPAD_AUTOREPEAT (1<<5) /* enable key autorepeat */
/**
* struct ep93xx_keypad_platform_data - platform specific device structure
* @matrix_key_rows: number of rows in the keypad matrix
* @matrix_key_cols: number of columns in the keypad matrix
* @matrix_key_map: array of keycodes defining the keypad matrix
* @matrix_key_map_size: ARRAY_SIZE(matrix_key_map)
* @debounce: debounce start count; terminal count is 0xff
* @prescale: row/column counter pre-scaler load value
* @flags: see above
*/
struct ep93xx_keypad_platform_data {
unsigned int matrix_key_rows;
unsigned int matrix_key_cols;
unsigned int *matrix_key_map;
int matrix_key_map_size;
unsigned int debounce;
unsigned int prescale;
unsigned int flags;
};
/* macro for creating the matrix_key_map table */
#define KEY(row, col, val) (((row) << 28) | ((col) << 24) | (val))
#endif /* __ASM_ARCH_EP93XX_KEYPAD_H */

View file

@ -1123,8 +1123,6 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
#define HW_RAW(dev) 0
#warning "Cannot generate rawmode keyboard for your architecture yet."
static int emulate_raw(struct vc_data *vc, unsigned int keycode, unsigned char up_flag)
{
if (keycode > 127)

View file

@ -25,7 +25,6 @@ struct evdev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct evdev_client *grab;
@ -609,7 +608,8 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
p, compat_mode);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0)))
return str_to_user(dev->name, _IOC_SIZE(cmd), p);
return str_to_user(dev_name(&evdev->dev),
_IOC_SIZE(cmd), p);
if (_IOC_NR(cmd) == _IOC_NR(EVIOCGPHYS(0)))
return str_to_user(dev->phys, _IOC_SIZE(cmd), p);
@ -626,8 +626,11 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
abs.maximum = dev->absmax[t];
abs.fuzz = dev->absfuzz[t];
abs.flat = dev->absflat[t];
abs.resolution = dev->absres[t];
if (copy_to_user(p, &abs, sizeof(struct input_absinfo)))
if (copy_to_user(p, &abs, min_t(size_t,
_IOC_SIZE(cmd),
sizeof(struct input_absinfo))))
return -EFAULT;
return 0;
@ -654,8 +657,9 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
t = _IOC_NR(cmd) & ABS_MAX;
if (copy_from_user(&abs, p,
sizeof(struct input_absinfo)))
if (copy_from_user(&abs, p, min_t(size_t,
_IOC_SIZE(cmd),
sizeof(struct input_absinfo))))
return -EFAULT;
/*
@ -670,6 +674,8 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
dev->absmax[t] = abs.maximum;
dev->absfuzz[t] = abs.fuzz;
dev->absflat[t] = abs.flat;
dev->absres[t] = _IOC_SIZE(cmd) < sizeof(struct input_absinfo) ?
0 : abs.resolution;
spin_unlock_irq(&dev->event_lock);
@ -807,16 +813,15 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
mutex_init(&evdev->mutex);
init_waitqueue_head(&evdev->wait);
snprintf(evdev->name, sizeof(evdev->name), "event%d", minor);
dev_set_name(&evdev->dev, "event%d", minor);
evdev->exist = 1;
evdev->minor = minor;
evdev->handle.dev = input_get_device(dev);
evdev->handle.name = evdev->name;
evdev->handle.name = dev_name(&evdev->dev);
evdev->handle.handler = handler;
evdev->handle.private = evdev;
dev_set_name(&evdev->dev, evdev->name);
evdev->dev.devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor);
evdev->dev.class = &input_class;
evdev->dev.parent = &dev->dev;

View file

@ -167,5 +167,6 @@ module_exit(fm801_gp_exit);
MODULE_DEVICE_TABLE(pci, fm801_gp_id_table);
MODULE_DESCRIPTION("FM801 gameport driver");
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
MODULE_LICENSE("GPL");

View file

@ -30,16 +30,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Generic gameport layer");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(__gameport_register_port);
EXPORT_SYMBOL(gameport_unregister_port);
EXPORT_SYMBOL(__gameport_register_driver);
EXPORT_SYMBOL(gameport_unregister_driver);
EXPORT_SYMBOL(gameport_open);
EXPORT_SYMBOL(gameport_close);
EXPORT_SYMBOL(gameport_set_phys);
EXPORT_SYMBOL(gameport_start_polling);
EXPORT_SYMBOL(gameport_stop_polling);
/*
* gameport_mutex protects entire gameport subsystem and is taken
* every time gameport port or driver registrered or unregistered.
@ -162,6 +152,7 @@ void gameport_start_polling(struct gameport *gameport)
spin_unlock(&gameport->timer_lock);
}
EXPORT_SYMBOL(gameport_start_polling);
void gameport_stop_polling(struct gameport *gameport)
{
@ -172,6 +163,7 @@ void gameport_stop_polling(struct gameport *gameport)
spin_unlock(&gameport->timer_lock);
}
EXPORT_SYMBOL(gameport_stop_polling);
static void gameport_run_poll_handler(unsigned long d)
{
@ -516,6 +508,7 @@ void gameport_set_phys(struct gameport *gameport, const char *fmt, ...)
vsnprintf(gameport->phys, sizeof(gameport->phys), fmt, args);
va_end(args);
}
EXPORT_SYMBOL(gameport_set_phys);
/*
* Prepare gameport port for registration.
@ -658,6 +651,7 @@ void __gameport_register_port(struct gameport *gameport, struct module *owner)
gameport_init_port(gameport);
gameport_queue_event(gameport, owner, GAMEPORT_REGISTER_PORT);
}
EXPORT_SYMBOL(__gameport_register_port);
/*
* Synchronously unregisters gameport port.
@ -669,6 +663,7 @@ void gameport_unregister_port(struct gameport *gameport)
gameport_destroy_port(gameport);
mutex_unlock(&gameport_mutex);
}
EXPORT_SYMBOL(gameport_unregister_port);
/*
@ -728,7 +723,7 @@ int __gameport_register_driver(struct gameport_driver *drv, struct module *owner
* Temporarily disable automatic binding because probing
* takes long time and we are better off doing it in kgameportd
*/
drv->ignore = 1;
drv->ignore = true;
error = driver_register(&drv->driver);
if (error) {
@ -741,7 +736,7 @@ int __gameport_register_driver(struct gameport_driver *drv, struct module *owner
/*
* Reset ignore flag and let kgameportd bind the driver to free ports
*/
drv->ignore = 0;
drv->ignore = false;
error = gameport_queue_event(drv, NULL, GAMEPORT_ATTACH_DRIVER);
if (error) {
driver_unregister(&drv->driver);
@ -750,6 +745,7 @@ int __gameport_register_driver(struct gameport_driver *drv, struct module *owner
return 0;
}
EXPORT_SYMBOL(__gameport_register_driver);
void gameport_unregister_driver(struct gameport_driver *drv)
{
@ -757,7 +753,7 @@ void gameport_unregister_driver(struct gameport_driver *drv)
mutex_lock(&gameport_mutex);
drv->ignore = 1; /* so gameport_find_driver ignores it */
drv->ignore = true; /* so gameport_find_driver ignores it */
gameport_remove_pending_events(drv);
start_over:
@ -774,6 +770,7 @@ start_over:
mutex_unlock(&gameport_mutex);
}
EXPORT_SYMBOL(gameport_unregister_driver);
static int gameport_bus_match(struct device *dev, struct device_driver *drv)
{
@ -812,6 +809,7 @@ int gameport_open(struct gameport *gameport, struct gameport_driver *drv, int mo
gameport_set_drv(gameport, drv);
return 0;
}
EXPORT_SYMBOL(gameport_open);
void gameport_close(struct gameport *gameport)
{
@ -822,6 +820,7 @@ void gameport_close(struct gameport *gameport)
if (gameport->close)
gameport->close(gameport);
}
EXPORT_SYMBOL(gameport_close);
static int __init gameport_init(void)
{

View file

@ -39,7 +39,6 @@ struct joydev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct list_head client_list;
@ -537,12 +536,14 @@ static int joydev_ioctl_common(struct joydev *joydev,
default:
if ((cmd & ~IOCSIZE_MASK) == JSIOCGNAME(0)) {
int len;
if (!dev->name)
const char *name = dev_name(&dev->dev);
if (!name)
return 0;
len = strlen(dev->name) + 1;
len = strlen(name) + 1;
if (len > _IOC_SIZE(cmd))
len = _IOC_SIZE(cmd);
if (copy_to_user(argp, dev->name, len))
if (copy_to_user(argp, name, len))
return -EFAULT;
return len;
}
@ -742,13 +743,13 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
mutex_init(&joydev->mutex);
init_waitqueue_head(&joydev->wait);
snprintf(joydev->name, sizeof(joydev->name), "js%d", minor);
dev_set_name(&joydev->dev, "js%d", minor);
joydev->exist = 1;
joydev->minor = minor;
joydev->exist = 1;
joydev->handle.dev = input_get_device(dev);
joydev->handle.name = joydev->name;
joydev->handle.name = dev_name(&joydev->dev);
joydev->handle.handler = handler;
joydev->handle.private = joydev;
@ -797,7 +798,6 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
}
}
dev_set_name(&joydev->dev, joydev->name);
joydev->dev.devt = MKDEV(INPUT_MAJOR, JOYDEV_MINOR_BASE + minor);
joydev->dev.class = &input_class;
joydev->dev.parent = &dev->dev;

View file

@ -250,6 +250,17 @@ config KEYBOARD_HP7XX
To compile this driver as a module, choose M here: the
module will be called jornada720_kbd.
config KEYBOARD_LM8323
tristate "LM8323 keypad chip"
depends on I2C
depends on LEDS_CLASS
help
If you say yes here you get support for the National Semiconductor
LM8323 keypad controller.
To compile this driver as a module, choose M here: the
module will be called lm8323.
config KEYBOARD_OMAP
tristate "TI OMAP keypad support"
depends on (ARCH_OMAP1 || ARCH_OMAP2)
@ -332,4 +343,14 @@ config KEYBOARD_SH_KEYSC
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
config KEYBOARD_EP93XX
tristate "EP93xx Matrix Keypad support"
depends on ARCH_EP93XX
help
Say Y here to enable the matrix keypad on the Cirrus EP93XX.
To compile this driver as a module, choose M here: the
module will be called ep93xx_keypad.
endif

View file

@ -18,6 +18,7 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
@ -28,3 +29,4 @@ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o

View file

@ -0,0 +1,470 @@
/*
* Driver for the Cirrus EP93xx matrix keypad controller.
*
* Copyright (c) 2008 H Hartley Sweeten <hsweeten@visionengravers.com>
*
* Based on the pxa27x matrix keypad controller by Rodolfo Giometti.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* NOTE:
*
* The 3-key reset is triggered by pressing the 3 keys in
* Row 0, Columns 2, 4, and 7 at the same time. This action can
* be disabled by setting the EP93XX_KEYPAD_DISABLE_3_KEY flag.
*
* Normal operation for the matrix does not autorepeat the key press.
* This action can be enabled by setting the EP93XX_KEYPAD_AUTOREPEAT
* flag.
*/
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
#include <mach/ep93xx_keypad.h>
/*
* Keypad Interface Register offsets
*/
#define KEY_INIT 0x00 /* Key Scan Initialization register */
#define KEY_DIAG 0x04 /* Key Scan Diagnostic register */
#define KEY_REG 0x08 /* Key Value Capture register */
/* Key Scan Initialization Register bit defines */
#define KEY_INIT_DBNC_MASK (0x00ff0000)
#define KEY_INIT_DBNC_SHIFT (16)
#define KEY_INIT_DIS3KY (1<<15)
#define KEY_INIT_DIAG (1<<14)
#define KEY_INIT_BACK (1<<13)
#define KEY_INIT_T2 (1<<12)
#define KEY_INIT_PRSCL_MASK (0x000003ff)
#define KEY_INIT_PRSCL_SHIFT (0)
/* Key Scan Diagnostic Register bit defines */
#define KEY_DIAG_MASK (0x0000003f)
#define KEY_DIAG_SHIFT (0)
/* Key Value Capture Register bit defines */
#define KEY_REG_K (1<<15)
#define KEY_REG_INT (1<<14)
#define KEY_REG_2KEYS (1<<13)
#define KEY_REG_1KEY (1<<12)
#define KEY_REG_KEY2_MASK (0x00000fc0)
#define KEY_REG_KEY2_SHIFT (6)
#define KEY_REG_KEY1_MASK (0x0000003f)
#define KEY_REG_KEY1_SHIFT (0)
#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off))
#define keypad_writel(v, off) __raw_writel((v), keypad->mmio_base + (off))
#define MAX_MATRIX_KEY_NUM (MAX_MATRIX_KEY_ROWS * MAX_MATRIX_KEY_COLS)
struct ep93xx_keypad {
struct ep93xx_keypad_platform_data *pdata;
struct clk *clk;
struct input_dev *input_dev;
void __iomem *mmio_base;
int irq;
int enabled;
int key1;
int key2;
unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM];
};
static void ep93xx_keypad_build_keycode(struct ep93xx_keypad *keypad)
{
struct ep93xx_keypad_platform_data *pdata = keypad->pdata;
struct input_dev *input_dev = keypad->input_dev;
int i;
for (i = 0; i < pdata->matrix_key_map_size; i++) {
unsigned int key = pdata->matrix_key_map[i];
int row = (key >> 28) & 0xf;
int col = (key >> 24) & 0xf;
int code = key & 0xffffff;
keypad->matrix_keycodes[(row << 3) + col] = code;
__set_bit(code, input_dev->keybit);
}
}
static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id)
{
struct ep93xx_keypad *keypad = dev_id;
struct input_dev *input_dev = keypad->input_dev;
unsigned int status = keypad_readl(KEY_REG);
int keycode, key1, key2;
keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT;
key1 = keypad->matrix_keycodes[keycode];
keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT;
key2 = keypad->matrix_keycodes[keycode];
if (status & KEY_REG_2KEYS) {
if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1)
input_report_key(input_dev, keypad->key1, 0);
if (keypad->key2 && key1 != keypad->key2 && key2 != keypad->key2)
input_report_key(input_dev, keypad->key2, 0);
input_report_key(input_dev, key1, 1);
input_report_key(input_dev, key2, 1);
keypad->key1 = key1;
keypad->key2 = key2;
} else if (status & KEY_REG_1KEY) {
if (keypad->key1 && key1 != keypad->key1)
input_report_key(input_dev, keypad->key1, 0);
if (keypad->key2 && key1 != keypad->key2)
input_report_key(input_dev, keypad->key2, 0);
input_report_key(input_dev, key1, 1);
keypad->key1 = key1;
keypad->key2 = 0;
} else {
input_report_key(input_dev, keypad->key1, 0);
input_report_key(input_dev, keypad->key2, 0);
keypad->key1 = keypad->key2 = 0;
}
input_sync(input_dev);
return IRQ_HANDLED;
}
static void ep93xx_keypad_config(struct ep93xx_keypad *keypad)
{
struct ep93xx_keypad_platform_data *pdata = keypad->pdata;
unsigned int val = 0;
clk_set_rate(keypad->clk, pdata->flags & EP93XX_KEYPAD_KDIV);
if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY)
val |= KEY_INIT_DIS3KY;
if (pdata->flags & EP93XX_KEYPAD_DIAG_MODE)
val |= KEY_INIT_DIAG;
if (pdata->flags & EP93XX_KEYPAD_BACK_DRIVE)
val |= KEY_INIT_BACK;
if (pdata->flags & EP93XX_KEYPAD_TEST_MODE)
val |= KEY_INIT_T2;
val |= ((pdata->debounce << KEY_INIT_DBNC_SHIFT) & KEY_INIT_DBNC_MASK);
val |= ((pdata->prescale << KEY_INIT_PRSCL_SHIFT) & KEY_INIT_PRSCL_MASK);
keypad_writel(val, KEY_INIT);
}
static int ep93xx_keypad_open(struct input_dev *pdev)
{
struct ep93xx_keypad *keypad = input_get_drvdata(pdev);
if (!keypad->enabled) {
ep93xx_keypad_config(keypad);
clk_enable(keypad->clk);
keypad->enabled = 1;
}
return 0;
}
static void ep93xx_keypad_close(struct input_dev *pdev)
{
struct ep93xx_keypad *keypad = input_get_drvdata(pdev);
if (keypad->enabled) {
clk_disable(keypad->clk);
keypad->enabled = 0;
}
}
#ifdef CONFIG_PM
/*
* NOTE: I don't know if this is correct, or will work on the ep93xx.
*
* None of the existing ep93xx drivers have power management support.
* But, this is basically what the pxa27x_keypad driver does.
*/
static int ep93xx_keypad_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
mutex_lock(&input_dev->mutex);
if (keypad->enabled) {
clk_disable(keypad->clk);
keypad->enabled = 0;
}
mutex_unlock(&input_dev->mutex);
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(keypad->irq);
return 0;
}
static int ep93xx_keypad_resume(struct platform_device *pdev)
{
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
struct input_dev *input_dev = keypad->input_dev;
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(keypad->irq);
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
if (!keypad->enabled) {
ep93xx_keypad_config(keypad);
clk_enable(keypad->clk);
keypad->enabled = 1;
}
}
mutex_unlock(&input_dev->mutex);
return 0;
}
#else /* !CONFIG_PM */
#define ep93xx_keypad_suspend NULL
#define ep93xx_keypad_resume NULL
#endif /* !CONFIG_PM */
static int __devinit ep93xx_keypad_probe(struct platform_device *pdev)
{
struct ep93xx_keypad *keypad;
struct ep93xx_keypad_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input_dev;
struct resource *res;
int irq, err, i, gpio;
if (!pdata ||
!pdata->matrix_key_rows ||
pdata->matrix_key_rows > MAX_MATRIX_KEY_ROWS ||
!pdata->matrix_key_cols ||
pdata->matrix_key_cols > MAX_MATRIX_KEY_COLS) {
dev_err(&pdev->dev, "invalid or missing platform data\n");
return -EINVAL;
}
keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL);
if (!keypad) {
dev_err(&pdev->dev, "failed to allocate driver data\n");
return -ENOMEM;
}
keypad->pdata = pdata;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "failed to get keypad irq\n");
err = -ENXIO;
goto failed_free;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get I/O memory\n");
err = -ENXIO;
goto failed_free;
}
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (!res) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
err = -EBUSY;
goto failed_free;
}
keypad->mmio_base = ioremap(res->start, resource_size(res));
if (keypad->mmio_base == NULL) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
err = -ENXIO;
goto failed_free_mem;
}
/* Request the needed GPIO's */
gpio = EP93XX_GPIO_LINE_ROW0;
for (i = 0; i < keypad->pdata->matrix_key_rows; i++, gpio++) {
err = gpio_request(gpio, pdev->name);
if (err) {
dev_err(&pdev->dev, "failed to request gpio-%d\n",
gpio);
goto failed_free_rows;
}
}
gpio = EP93XX_GPIO_LINE_COL0;
for (i = 0; i < keypad->pdata->matrix_key_cols; i++, gpio++) {
err = gpio_request(gpio, pdev->name);
if (err) {
dev_err(&pdev->dev, "failed to request gpio-%d\n",
gpio);
goto failed_free_cols;
}
}
keypad->clk = clk_get(&pdev->dev, "key_clk");
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
err = PTR_ERR(keypad->clk);
goto failed_free_io;
}
/* Create and register the input driver */
input_dev = input_allocate_device();
if (!input_dev) {
dev_err(&pdev->dev, "failed to allocate input device\n");
err = -ENOMEM;
goto failed_put_clk;
}
keypad->input_dev = input_dev;
input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
input_dev->open = ep93xx_keypad_open;
input_dev->close = ep93xx_keypad_close;
input_dev->dev.parent = &pdev->dev;
input_dev->keycode = keypad->matrix_keycodes;
input_dev->keycodesize = sizeof(keypad->matrix_keycodes[0]);
input_dev->keycodemax = ARRAY_SIZE(keypad->matrix_keycodes);
input_set_drvdata(input_dev, keypad);
input_dev->evbit[0] = BIT_MASK(EV_KEY);
if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT)
input_dev->evbit[0] |= BIT_MASK(EV_REP);
ep93xx_keypad_build_keycode(keypad);
platform_set_drvdata(pdev, keypad);
err = request_irq(irq, ep93xx_keypad_irq_handler, IRQF_DISABLED,
pdev->name, keypad);
if (err) {
dev_err(&pdev->dev, "failed to request IRQ\n");
goto failed_free_dev;
}
keypad->irq = irq;
/* Register the input device */
err = input_register_device(input_dev);
if (err) {
dev_err(&pdev->dev, "failed to register input device\n");
goto failed_free_irq;
}
device_init_wakeup(&pdev->dev, 1);
return 0;
failed_free_irq:
free_irq(irq, pdev);
platform_set_drvdata(pdev, NULL);
failed_free_dev:
input_free_device(input_dev);
failed_put_clk:
clk_put(keypad->clk);
failed_free_io:
i = keypad->pdata->matrix_key_cols - 1;
gpio = EP93XX_GPIO_LINE_COL0 + i;
failed_free_cols:
for ( ; i >= 0; i--, gpio--)
gpio_free(gpio);
i = keypad->pdata->matrix_key_rows - 1;
gpio = EP93XX_GPIO_LINE_ROW0 + i;
failed_free_rows:
for ( ; i >= 0; i--, gpio--)
gpio_free(gpio);
iounmap(keypad->mmio_base);
failed_free_mem:
release_mem_region(res->start, resource_size(res));
failed_free:
kfree(keypad);
return err;
}
static int __devexit ep93xx_keypad_remove(struct platform_device *pdev)
{
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
struct resource *res;
int i, gpio;
free_irq(keypad->irq, pdev);
platform_set_drvdata(pdev, NULL);
if (keypad->enabled)
clk_disable(keypad->clk);
clk_put(keypad->clk);
input_unregister_device(keypad->input_dev);
i = keypad->pdata->matrix_key_cols - 1;
gpio = EP93XX_GPIO_LINE_COL0 + i;
for ( ; i >= 0; i--, gpio--)
gpio_free(gpio);
i = keypad->pdata->matrix_key_rows - 1;
gpio = EP93XX_GPIO_LINE_ROW0 + i;
for ( ; i >= 0; i--, gpio--)
gpio_free(gpio);
iounmap(keypad->mmio_base);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
kfree(keypad);
return 0;
}
static struct platform_driver ep93xx_keypad_driver = {
.driver = {
.name = "ep93xx-keypad",
.owner = THIS_MODULE,
},
.probe = ep93xx_keypad_probe,
.remove = __devexit_p(ep93xx_keypad_remove),
.suspend = ep93xx_keypad_suspend,
.resume = ep93xx_keypad_resume,
};
static int __init ep93xx_keypad_init(void)
{
return platform_driver_register(&ep93xx_keypad_driver);
}
static void __exit ep93xx_keypad_exit(void)
{
platform_driver_unregister(&ep93xx_keypad_driver);
}
module_init(ep93xx_keypad_init);
module_exit(ep93xx_keypad_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
MODULE_DESCRIPTION("EP93xx Matrix Keypad Controller");
MODULE_ALIAS("platform:ep93xx-keypad");

View file

@ -22,13 +22,14 @@
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/gpio_keys.h>
#include <linux/workqueue.h>
#include <asm/gpio.h>
struct gpio_button_data {
struct gpio_keys_button *button;
struct input_dev *input;
struct timer_list timer;
struct delayed_work work;
};
struct gpio_keys_drvdata {
@ -36,8 +37,10 @@ struct gpio_keys_drvdata {
struct gpio_button_data data[0];
};
static void gpio_keys_report_event(struct gpio_button_data *bdata)
static void gpio_keys_report_event(struct work_struct *work)
{
struct gpio_button_data *bdata =
container_of(work, struct gpio_button_data, work.work);
struct gpio_keys_button *button = bdata->button;
struct input_dev *input = bdata->input;
unsigned int type = button->type ?: EV_KEY;
@ -47,25 +50,17 @@ static void gpio_keys_report_event(struct gpio_button_data *bdata)
input_sync(input);
}
static void gpio_check_button(unsigned long _data)
{
struct gpio_button_data *data = (struct gpio_button_data *)_data;
gpio_keys_report_event(data);
}
static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
struct gpio_button_data *bdata = dev_id;
struct gpio_keys_button *button = bdata->button;
unsigned long delay;
BUG_ON(irq != gpio_to_irq(button->gpio));
if (button->debounce_interval)
mod_timer(&bdata->timer,
jiffies + msecs_to_jiffies(button->debounce_interval));
else
gpio_keys_report_event(bdata);
delay = button->debounce_interval ?
msecs_to_jiffies(button->debounce_interval) : 0;
schedule_delayed_work(&bdata->work, delay);
return IRQ_HANDLED;
}
@ -112,8 +107,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
bdata->input = input;
bdata->button = button;
setup_timer(&bdata->timer,
gpio_check_button, (unsigned long)bdata);
INIT_DELAYED_WORK(&bdata->work, gpio_keys_report_event);
error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
if (error < 0) {
@ -142,8 +136,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
}
error = request_irq(irq, gpio_keys_isr,
IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
button->desc ? button->desc : "gpio_keys",
bdata);
if (error) {
@ -173,8 +166,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
fail2:
while (--i >= 0) {
free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
if (pdata->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
cancel_delayed_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
}
@ -198,8 +190,7 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev)
for (i = 0; i < pdata->nbuttons; i++) {
int irq = gpio_to_irq(pdata->buttons[i].gpio);
free_irq(irq, &ddata->data[i]);
if (pdata->buttons[i].debounce_interval)
del_timer_sync(&ddata->data[i].timer);
cancel_delayed_work_sync(&ddata->data[i].work);
gpio_free(pdata->buttons[i].gpio);
}

View file

@ -0,0 +1,878 @@
/*
* drivers/i2c/chips/lm8323.c
*
* Copyright (C) 2007-2009 Nokia Corporation
*
* Written by Daniel Stone <daniel.stone@nokia.com>
* Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
*
* Updated by Felipe Balbi <felipe.balbi@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation (version 2 of the License only).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/leds.h>
#include <linux/i2c/lm8323.h>
/* Commands to send to the chip. */
#define LM8323_CMD_READ_ID 0x80 /* Read chip ID. */
#define LM8323_CMD_WRITE_CFG 0x81 /* Set configuration item. */
#define LM8323_CMD_READ_INT 0x82 /* Get interrupt status. */
#define LM8323_CMD_RESET 0x83 /* Reset, same as external one */
#define LM8323_CMD_WRITE_PORT_SEL 0x85 /* Set GPIO in/out. */
#define LM8323_CMD_WRITE_PORT_STATE 0x86 /* Set GPIO pullup. */
#define LM8323_CMD_READ_PORT_SEL 0x87 /* Get GPIO in/out. */
#define LM8323_CMD_READ_PORT_STATE 0x88 /* Get GPIO pullup. */
#define LM8323_CMD_READ_FIFO 0x89 /* Read byte from FIFO. */
#define LM8323_CMD_RPT_READ_FIFO 0x8a /* Read FIFO (no increment). */
#define LM8323_CMD_SET_ACTIVE 0x8b /* Set active time. */
#define LM8323_CMD_READ_ERR 0x8c /* Get error status. */
#define LM8323_CMD_READ_ROTATOR 0x8e /* Read rotator status. */
#define LM8323_CMD_SET_DEBOUNCE 0x8f /* Set debouncing time. */
#define LM8323_CMD_SET_KEY_SIZE 0x90 /* Set keypad size. */
#define LM8323_CMD_READ_KEY_SIZE 0x91 /* Get keypad size. */
#define LM8323_CMD_READ_CFG 0x92 /* Get configuration item. */
#define LM8323_CMD_WRITE_CLOCK 0x93 /* Set clock config. */
#define LM8323_CMD_READ_CLOCK 0x94 /* Get clock config. */
#define LM8323_CMD_PWM_WRITE 0x95 /* Write PWM script. */
#define LM8323_CMD_START_PWM 0x96 /* Start PWM engine. */
#define LM8323_CMD_STOP_PWM 0x97 /* Stop PWM engine. */
/* Interrupt status. */
#define INT_KEYPAD 0x01 /* Key event. */
#define INT_ROTATOR 0x02 /* Rotator event. */
#define INT_ERROR 0x08 /* Error: use CMD_READ_ERR. */
#define INT_NOINIT 0x10 /* Lost configuration. */
#define INT_PWM1 0x20 /* PWM1 stopped. */
#define INT_PWM2 0x40 /* PWM2 stopped. */
#define INT_PWM3 0x80 /* PWM3 stopped. */
/* Errors (signalled by INT_ERROR, read with CMD_READ_ERR). */
#define ERR_BADPAR 0x01 /* Bad parameter. */
#define ERR_CMDUNK 0x02 /* Unknown command. */
#define ERR_KEYOVR 0x04 /* Too many keys pressed. */
#define ERR_FIFOOVER 0x40 /* FIFO overflow. */
/* Configuration keys (CMD_{WRITE,READ}_CFG). */
#define CFG_MUX1SEL 0x01 /* Select MUX1_OUT input. */
#define CFG_MUX1EN 0x02 /* Enable MUX1_OUT. */
#define CFG_MUX2SEL 0x04 /* Select MUX2_OUT input. */
#define CFG_MUX2EN 0x08 /* Enable MUX2_OUT. */
#define CFG_PSIZE 0x20 /* Package size (must be 0). */
#define CFG_ROTEN 0x40 /* Enable rotator. */
/* Clock settings (CMD_{WRITE,READ}_CLOCK). */
#define CLK_RCPWM_INTERNAL 0x00
#define CLK_RCPWM_EXTERNAL 0x03
#define CLK_SLOWCLKEN 0x08 /* Enable 32.768kHz clock. */
#define CLK_SLOWCLKOUT 0x40 /* Enable slow pulse output. */
/* The possible addresses corresponding to CONFIG1 and CONFIG2 pin wirings. */
#define LM8323_I2C_ADDR00 (0x84 >> 1) /* 1000 010x */
#define LM8323_I2C_ADDR01 (0x86 >> 1) /* 1000 011x */
#define LM8323_I2C_ADDR10 (0x88 >> 1) /* 1000 100x */
#define LM8323_I2C_ADDR11 (0x8A >> 1) /* 1000 101x */
/* Key event fifo length */
#define LM8323_FIFO_LEN 15
/* Commands for PWM engine; feed in with PWM_WRITE. */
/* Load ramp counter from duty cycle field (range 0 - 0xff). */
#define PWM_SET(v) (0x4000 | ((v) & 0xff))
/* Go to start of script. */
#define PWM_GOTOSTART 0x0000
/*
* Stop engine (generates interrupt). If reset is 1, clear the program
* counter, else leave it.
*/
#define PWM_END(reset) (0xc000 | (!!(reset) << 11))
/*
* Ramp. If s is 1, divide clock by 512, else divide clock by 16.
* Take t clock scales (up to 63) per step, for n steps (up to 126).
* If u is set, ramp up, else ramp down.
*/
#define PWM_RAMP(s, t, n, u) ((!!(s) << 14) | ((t) & 0x3f) << 8 | \
((n) & 0x7f) | ((u) ? 0 : 0x80))
/*
* Loop (i.e. jump back to pos) for a given number of iterations (up to 63).
* If cnt is zero, execute until PWM_END is encountered.
*/
#define PWM_LOOP(cnt, pos) (0xa000 | (((cnt) & 0x3f) << 7) | \
((pos) & 0x3f))
/*
* Wait for trigger. Argument is a mask of channels, shifted by the channel
* number, e.g. 0xa for channels 3 and 1. Note that channels are numbered
* from 1, not 0.
*/
#define PWM_WAIT_TRIG(chans) (0xe000 | (((chans) & 0x7) << 6))
/* Send trigger. Argument is same as PWM_WAIT_TRIG. */
#define PWM_SEND_TRIG(chans) (0xe000 | ((chans) & 0x7))
struct lm8323_pwm {
int id;
int fade_time;
int brightness;
int desired_brightness;
bool enabled;
bool running;
/* pwm lock */
struct mutex lock;
struct work_struct work;
struct led_classdev cdev;
struct lm8323_chip *chip;
};
struct lm8323_chip {
/* device lock */
struct mutex lock;
struct i2c_client *client;
struct work_struct work;
struct input_dev *idev;
bool kp_enabled;
bool pm_suspend;
unsigned keys_down;
char phys[32];
unsigned short keymap[LM8323_KEYMAP_SIZE];
int size_x;
int size_y;
int debounce_time;
int active_time;
struct lm8323_pwm pwm[LM8323_NUM_PWMS];
};
#define client_to_lm8323(c) container_of(c, struct lm8323_chip, client)
#define dev_to_lm8323(d) container_of(d, struct lm8323_chip, client->dev)
#define work_to_lm8323(w) container_of(w, struct lm8323_chip, work)
#define cdev_to_pwm(c) container_of(c, struct lm8323_pwm, cdev)
#define work_to_pwm(w) container_of(w, struct lm8323_pwm, work)
#define LM8323_MAX_DATA 8
/*
* To write, we just access the chip's address in write mode, and dump the
* command and data out on the bus. The command byte and data are taken as
* sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA.
*/
static int lm8323_write(struct lm8323_chip *lm, int len, ...)
{
int ret, i;
va_list ap;
u8 data[LM8323_MAX_DATA];
va_start(ap, len);
if (unlikely(len > LM8323_MAX_DATA)) {
dev_err(&lm->client->dev, "tried to send %d bytes\n", len);
va_end(ap);
return 0;
}
for (i = 0; i < len; i++)
data[i] = va_arg(ap, int);
va_end(ap);
/*
* If the host is asleep while we send the data, we can get a NACK
* back while it wakes up, so try again, once.
*/
ret = i2c_master_send(lm->client, data, len);
if (unlikely(ret == -EREMOTEIO))
ret = i2c_master_send(lm->client, data, len);
if (unlikely(ret != len))
dev_err(&lm->client->dev, "sent %d bytes of %d total\n",
len, ret);
return ret;
}
/*
* To read, we first send the command byte to the chip and end the transaction,
* then access the chip in read mode, at which point it will send the data.
*/
static int lm8323_read(struct lm8323_chip *lm, u8 cmd, u8 *buf, int len)
{
int ret;
/*
* If the host is asleep while we send the byte, we can get a NACK
* back while it wakes up, so try again, once.
*/
ret = i2c_master_send(lm->client, &cmd, 1);
if (unlikely(ret == -EREMOTEIO))
ret = i2c_master_send(lm->client, &cmd, 1);
if (unlikely(ret != 1)) {
dev_err(&lm->client->dev, "sending read cmd 0x%02x failed\n",
cmd);
return 0;
}
ret = i2c_master_recv(lm->client, buf, len);
if (unlikely(ret != len))
dev_err(&lm->client->dev, "wanted %d bytes, got %d\n",
len, ret);
return ret;
}
/*
* Set the chip active time (idle time before it enters halt).
*/
static void lm8323_set_active_time(struct lm8323_chip *lm, int time)
{
lm8323_write(lm, 2, LM8323_CMD_SET_ACTIVE, time >> 2);
}
/*
* The signals are AT-style: the low 7 bits are the keycode, and the top
* bit indicates the state (1 for down, 0 for up).
*/
static inline u8 lm8323_whichkey(u8 event)
{
return event & 0x7f;
}
static inline int lm8323_ispress(u8 event)
{
return (event & 0x80) ? 1 : 0;
}
static void process_keys(struct lm8323_chip *lm)
{
u8 event;
u8 key_fifo[LM8323_FIFO_LEN + 1];
int old_keys_down = lm->keys_down;
int ret;
int i = 0;
/*
* Read all key events from the FIFO at once. Next READ_FIFO clears the
* FIFO even if we didn't read all events previously.
*/
ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN);
if (ret < 0) {
dev_err(&lm->client->dev, "Failed reading fifo \n");
return;
}
key_fifo[ret] = 0;
while ((event = key_fifo[i++])) {
u8 key = lm8323_whichkey(event);
int isdown = lm8323_ispress(event);
unsigned short keycode = lm->keymap[key];
dev_vdbg(&lm->client->dev, "key 0x%02x %s\n",
key, isdown ? "down" : "up");
if (lm->kp_enabled) {
input_event(lm->idev, EV_MSC, MSC_SCAN, key);
input_report_key(lm->idev, keycode, isdown);
input_sync(lm->idev);
}
if (isdown)
lm->keys_down++;
else
lm->keys_down--;
}
/*
* Errata: We need to ensure that the chip never enters halt mode
* during a keypress, so set active time to 0. When it's released,
* we can enter halt again, so set the active time back to normal.
*/
if (!old_keys_down && lm->keys_down)
lm8323_set_active_time(lm, 0);
if (old_keys_down && !lm->keys_down)
lm8323_set_active_time(lm, lm->active_time);
}
static void lm8323_process_error(struct lm8323_chip *lm)
{
u8 error;
if (lm8323_read(lm, LM8323_CMD_READ_ERR, &error, 1) == 1) {
if (error & ERR_FIFOOVER)
dev_vdbg(&lm->client->dev, "fifo overflow!\n");
if (error & ERR_KEYOVR)
dev_vdbg(&lm->client->dev,
"more than two keys pressed\n");
if (error & ERR_CMDUNK)
dev_vdbg(&lm->client->dev,
"unknown command submitted\n");
if (error & ERR_BADPAR)
dev_vdbg(&lm->client->dev, "bad command parameter\n");
}
}
static void lm8323_reset(struct lm8323_chip *lm)
{
/* The docs say we must pass 0xAA as the data byte. */
lm8323_write(lm, 2, LM8323_CMD_RESET, 0xAA);
}
static int lm8323_configure(struct lm8323_chip *lm)
{
int keysize = (lm->size_x << 4) | lm->size_y;
int clock = (CLK_SLOWCLKEN | CLK_RCPWM_EXTERNAL);
int debounce = lm->debounce_time >> 2;
int active = lm->active_time >> 2;
/*
* Active time must be greater than the debounce time: if it's
* a close-run thing, give ourselves a 12ms buffer.
*/
if (debounce >= active)
active = debounce + 3;
lm8323_write(lm, 2, LM8323_CMD_WRITE_CFG, 0);
lm8323_write(lm, 2, LM8323_CMD_WRITE_CLOCK, clock);
lm8323_write(lm, 2, LM8323_CMD_SET_KEY_SIZE, keysize);
lm8323_set_active_time(lm, lm->active_time);
lm8323_write(lm, 2, LM8323_CMD_SET_DEBOUNCE, debounce);
lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_STATE, 0xff, 0xff);
lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_SEL, 0, 0);
/*
* Not much we can do about errors at this point, so just hope
* for the best.
*/
return 0;
}
static void pwm_done(struct lm8323_pwm *pwm)
{
mutex_lock(&pwm->lock);
pwm->running = false;
if (pwm->desired_brightness != pwm->brightness)
schedule_work(&pwm->work);
mutex_unlock(&pwm->lock);
}
/*
* Bottom half: handle the interrupt by posting key events, or dealing with
* errors appropriately.
*/
static void lm8323_work(struct work_struct *work)
{
struct lm8323_chip *lm = work_to_lm8323(work);
u8 ints;
int i;
mutex_lock(&lm->lock);
while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) {
if (likely(ints & INT_KEYPAD))
process_keys(lm);
if (ints & INT_ROTATOR) {
/* We don't currently support the rotator. */
dev_vdbg(&lm->client->dev, "rotator fired\n");
}
if (ints & INT_ERROR) {
dev_vdbg(&lm->client->dev, "error!\n");
lm8323_process_error(lm);
}
if (ints & INT_NOINIT) {
dev_err(&lm->client->dev, "chip lost config; "
"reinitialising\n");
lm8323_configure(lm);
}
for (i = 0; i < LM8323_NUM_PWMS; i++) {
if (ints & (1 << (INT_PWM1 + i))) {
dev_vdbg(&lm->client->dev,
"pwm%d engine completed\n", i);
pwm_done(&lm->pwm[i]);
}
}
}
mutex_unlock(&lm->lock);
}
/*
* We cannot use I2C in interrupt context, so we just schedule work.
*/
static irqreturn_t lm8323_irq(int irq, void *data)
{
struct lm8323_chip *lm = data;
schedule_work(&lm->work);
return IRQ_HANDLED;
}
/*
* Read the chip ID.
*/
static int lm8323_read_id(struct lm8323_chip *lm, u8 *buf)
{
int bytes;
bytes = lm8323_read(lm, LM8323_CMD_READ_ID, buf, 2);
if (unlikely(bytes != 2))
return -EIO;
return 0;
}
static void lm8323_write_pwm_one(struct lm8323_pwm *pwm, int pos, u16 cmd)
{
lm8323_write(pwm->chip, 4, LM8323_CMD_PWM_WRITE, (pos << 2) | pwm->id,
(cmd & 0xff00) >> 8, cmd & 0x00ff);
}
/*
* Write a script into a given PWM engine, concluding with PWM_END.
* If 'kill' is nonzero, the engine will be shut down at the end
* of the script, producing a zero output. Otherwise the engine
* will be kept running at the final PWM level indefinitely.
*/
static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill,
int len, const u16 *cmds)
{
int i;
for (i = 0; i < len; i++)
lm8323_write_pwm_one(pwm, i, cmds[i]);
lm8323_write_pwm_one(pwm, i++, PWM_END(kill));
lm8323_write(pwm->chip, 2, LM8323_CMD_START_PWM, pwm->id);
pwm->running = true;
}
static void lm8323_pwm_work(struct work_struct *work)
{
struct lm8323_pwm *pwm = work_to_pwm(work);
int div512, perstep, steps, hz, up, kill;
u16 pwm_cmds[3];
int num_cmds = 0;
mutex_lock(&pwm->lock);
/*
* Do nothing if we're already at the requested level,
* or previous setting is not yet complete. In the latter
* case we will be called again when the previous PWM script
* finishes.
*/
if (pwm->running || pwm->desired_brightness == pwm->brightness)
goto out;
kill = (pwm->desired_brightness == 0);
up = (pwm->desired_brightness > pwm->brightness);
steps = abs(pwm->desired_brightness - pwm->brightness);
/*
* Convert time (in ms) into a divisor (512 or 16 on a refclk of
* 32768Hz), and number of ticks per step.
*/
if ((pwm->fade_time / steps) > (32768 / 512)) {
div512 = 1;
hz = 32768 / 512;
} else {
div512 = 0;
hz = 32768 / 16;
}
perstep = (hz * pwm->fade_time) / (steps * 1000);
if (perstep == 0)
perstep = 1;
else if (perstep > 63)
perstep = 63;
while (steps) {
int s;
s = min(126, steps);
pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up);
steps -= s;
}
lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds);
pwm->brightness = pwm->desired_brightness;
out:
mutex_unlock(&pwm->lock);
}
static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
struct lm8323_chip *lm = pwm->chip;
mutex_lock(&pwm->lock);
pwm->desired_brightness = brightness;
mutex_unlock(&pwm->lock);
if (in_interrupt()) {
schedule_work(&pwm->work);
} else {
/*
* Schedule PWM work as usual unless we are going into suspend
*/
mutex_lock(&lm->lock);
if (likely(!lm->pm_suspend))
schedule_work(&pwm->work);
else
lm8323_pwm_work(&pwm->work);
mutex_unlock(&lm->lock);
}
}
static ssize_t lm8323_pwm_show_time(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
return sprintf(buf, "%d\n", pwm->fade_time);
}
static ssize_t lm8323_pwm_store_time(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
{
struct led_classdev *led_cdev = dev_get_drvdata(dev);
struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
int ret;
unsigned long time;
ret = strict_strtoul(buf, 10, &time);
/* Numbers only, please. */
if (ret)
return -EINVAL;
pwm->fade_time = time;
return strlen(buf);
}
static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time);
static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev,
const char *name)
{
struct lm8323_pwm *pwm;
BUG_ON(id > 3);
pwm = &lm->pwm[id - 1];
pwm->id = id;
pwm->fade_time = 0;
pwm->brightness = 0;
pwm->desired_brightness = 0;
pwm->running = false;
pwm->enabled = false;
INIT_WORK(&pwm->work, lm8323_pwm_work);
mutex_init(&pwm->lock);
pwm->chip = lm;
if (name) {
pwm->cdev.name = name;
pwm->cdev.brightness_set = lm8323_pwm_set_brightness;
if (led_classdev_register(dev, &pwm->cdev) < 0) {
dev_err(dev, "couldn't register PWM %d\n", id);
return -1;
}
if (device_create_file(pwm->cdev.dev,
&dev_attr_time) < 0) {
dev_err(dev, "couldn't register time attribute\n");
led_classdev_unregister(&pwm->cdev);
return -1;
}
pwm->enabled = true;
}
return 0;
}
static struct i2c_driver lm8323_i2c_driver;
static ssize_t lm8323_show_disable(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct lm8323_chip *lm = dev_get_drvdata(dev);
return sprintf(buf, "%u\n", !lm->kp_enabled);
}
static ssize_t lm8323_set_disable(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct lm8323_chip *lm = dev_get_drvdata(dev);
int ret;
unsigned long i;
ret = strict_strtoul(buf, 10, &i);
mutex_lock(&lm->lock);
lm->kp_enabled = !i;
mutex_unlock(&lm->lock);
return count;
}
static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable);
static int __devinit lm8323_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct lm8323_platform_data *pdata = client->dev.platform_data;
struct input_dev *idev;
struct lm8323_chip *lm;
int i, err;
unsigned long tmo;
u8 data[2];
if (!pdata || !pdata->size_x || !pdata->size_y) {
dev_err(&client->dev, "missing platform_data\n");
return -EINVAL;
}
if (pdata->size_x > 8) {
dev_err(&client->dev, "invalid x size %d specified\n",
pdata->size_x);
return -EINVAL;
}
if (pdata->size_y > 12) {
dev_err(&client->dev, "invalid y size %d specified\n",
pdata->size_y);
return -EINVAL;
}
lm = kzalloc(sizeof *lm, GFP_KERNEL);
idev = input_allocate_device();
if (!lm || !idev) {
err = -ENOMEM;
goto fail1;
}
i2c_set_clientdata(client, lm);
lm->client = client;
lm->idev = idev;
mutex_init(&lm->lock);
INIT_WORK(&lm->work, lm8323_work);
lm->size_x = pdata->size_x;
lm->size_y = pdata->size_y;
dev_vdbg(&client->dev, "Keypad size: %d x %d\n",
lm->size_x, lm->size_y);
lm->debounce_time = pdata->debounce_time;
lm->active_time = pdata->active_time;
lm8323_reset(lm);
/* Nothing's set up to service the IRQ yet, so just spin for max.
* 100ms until we can configure. */
tmo = jiffies + msecs_to_jiffies(100);
while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) {
if (data[0] & INT_NOINIT)
break;
if (time_after(jiffies, tmo)) {
dev_err(&client->dev,
"timeout waiting for initialisation\n");
break;
}
msleep(1);
}
lm8323_configure(lm);
/* If a true probe check the device */
if (lm8323_read_id(lm, data) != 0) {
dev_err(&client->dev, "device not found\n");
err = -ENODEV;
goto fail1;
}
for (i = 0; i < LM8323_NUM_PWMS; i++) {
err = init_pwm(lm, i + 1, &client->dev, pdata->pwm_names[i]);
if (err < 0)
goto fail2;
}
lm->kp_enabled = true;
err = device_create_file(&client->dev, &dev_attr_disable_kp);
if (err < 0)
goto fail2;
idev->name = pdata->name ? : "LM8323 keypad";
snprintf(lm->phys, sizeof(lm->phys),
"%s/input-kp", dev_name(&client->dev));
idev->phys = lm->phys;
idev->evbit[0] = BIT(EV_KEY) | BIT(EV_MSC);
__set_bit(MSC_SCAN, idev->mscbit);
for (i = 0; i < LM8323_KEYMAP_SIZE; i++) {
__set_bit(pdata->keymap[i], idev->keybit);
lm->keymap[i] = pdata->keymap[i];
}
__clear_bit(KEY_RESERVED, idev->keybit);
if (pdata->repeat)
__set_bit(EV_REP, idev->evbit);
err = input_register_device(idev);
if (err) {
dev_dbg(&client->dev, "error registering input device\n");
goto fail3;
}
err = request_irq(client->irq, lm8323_irq,
IRQF_TRIGGER_FALLING | IRQF_DISABLED,
"lm8323", lm);
if (err) {
dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
goto fail4;
}
device_init_wakeup(&client->dev, 1);
enable_irq_wake(client->irq);
return 0;
fail4:
input_unregister_device(idev);
idev = NULL;
fail3:
device_remove_file(&client->dev, &dev_attr_disable_kp);
fail2:
while (--i >= 0)
if (lm->pwm[i].enabled)
led_classdev_unregister(&lm->pwm[i].cdev);
fail1:
input_free_device(idev);
kfree(lm);
return err;
}
static int __devexit lm8323_remove(struct i2c_client *client)
{
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
disable_irq_wake(client->irq);
free_irq(client->irq, lm);
cancel_work_sync(&lm->work);
input_unregister_device(lm->idev);
device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
for (i = 0; i < 3; i++)
if (lm->pwm[i].enabled)
led_classdev_unregister(&lm->pwm[i].cdev);
kfree(lm);
return 0;
}
#ifdef CONFIG_PM
/*
* We don't need to explicitly suspend the chip, as it already switches off
* when there's no activity.
*/
static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
set_irq_wake(client->irq, 0);
disable_irq(client->irq);
mutex_lock(&lm->lock);
lm->pm_suspend = true;
mutex_unlock(&lm->lock);
for (i = 0; i < 3; i++)
if (lm->pwm[i].enabled)
led_classdev_suspend(&lm->pwm[i].cdev);
return 0;
}
static int lm8323_resume(struct i2c_client *client)
{
struct lm8323_chip *lm = i2c_get_clientdata(client);
int i;
mutex_lock(&lm->lock);
lm->pm_suspend = false;
mutex_unlock(&lm->lock);
for (i = 0; i < 3; i++)
if (lm->pwm[i].enabled)
led_classdev_resume(&lm->pwm[i].cdev);
enable_irq(client->irq);
set_irq_wake(client->irq, 1);
return 0;
}
#else
#define lm8323_suspend NULL
#define lm8323_resume NULL
#endif
static const struct i2c_device_id lm8323_id[] = {
{ "lm8323", 0 },
{ }
};
static struct i2c_driver lm8323_i2c_driver = {
.driver = {
.name = "lm8323",
},
.probe = lm8323_probe,
.remove = __devexit_p(lm8323_remove),
.suspend = lm8323_suspend,
.resume = lm8323_resume,
.id_table = lm8323_id,
};
MODULE_DEVICE_TABLE(i2c, lm8323_id);
static int __init lm8323_init(void)
{
return i2c_add_driver(&lm8323_i2c_driver);
}
module_init(lm8323_init);
static void __exit lm8323_exit(void)
{
i2c_del_driver(&lm8323_i2c_driver);
}
module_exit(lm8323_exit);
MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>");
MODULE_AUTHOR("Daniel Stone");
MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
MODULE_DESCRIPTION("LM8323 keypad driver");
MODULE_LICENSE("GPL");

View file

@ -193,6 +193,16 @@ config INPUT_CM109
To compile this driver as a module, choose M here: the module will be
called cm109.
config INPUT_TWL4030_PWRBUTTON
tristate "TWL4030 Power button Driver"
depends on TWL4030_CORE
help
Say Y here if you want to enable power key reporting via the
TWL4030 family of chips.
To compile this driver as a module, choose M here. The module will
be called twl4030_pwrbutton.
config INPUT_UINPUT
tristate "User level driver support"
help
@ -250,4 +260,13 @@ config INPUT_RB532_BUTTON
To compile this driver as a module, choose M here: the
module will be called rb532_button.
config INPUT_DM355EVM
tristate "TI DaVinci DM355 EVM Keypad and IR Remote"
depends on MFD_DM355EVM_MSP
help
Supports the pushbuttons and IR remote used with
the DM355 EVM board.
To compile this driver as a module, choose M here: the
module will be called dm355evm_keys.
endif

View file

@ -10,6 +10,7 @@ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
obj-$(CONFIG_INPUT_CM109) += cm109.o
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
@ -21,6 +22,7 @@ obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o

View file

@ -509,7 +509,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc
old_keycode = ar2->keycode[mode][index];
ar2->keycode[mode][index] = keycode;
set_bit(keycode, idev->keybit);
__set_bit(keycode, idev->keybit);
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
@ -518,7 +518,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc
}
}
clear_bit(old_keycode, idev->keybit);
__clear_bit(old_keycode, idev->keybit);
return 0;
}
@ -543,7 +543,7 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
for (mode = 0; mode < ATI_REMOTE2_MODES; mode++) {
for (index = 0; index < ARRAY_SIZE(ati_remote2_key_table); index++) {
ar2->keycode[mode][index] = ati_remote2_key_table[index].keycode;
set_bit(ar2->keycode[mode][index], idev->keybit);
__set_bit(ar2->keycode[mode][index], idev->keybit);
}
}
@ -554,11 +554,11 @@ static int ati_remote2_input_init(struct ati_remote2 *ar2)
ar2->keycode[ATI_REMOTE2_AUX3][index] = KEY_PROG3;
ar2->keycode[ATI_REMOTE2_AUX4][index] = KEY_PROG4;
ar2->keycode[ATI_REMOTE2_PC][index] = KEY_PC;
set_bit(KEY_PROG1, idev->keybit);
set_bit(KEY_PROG2, idev->keybit);
set_bit(KEY_PROG3, idev->keybit);
set_bit(KEY_PROG4, idev->keybit);
set_bit(KEY_PC, idev->keybit);
__set_bit(KEY_PROG1, idev->keybit);
__set_bit(KEY_PROG2, idev->keybit);
__set_bit(KEY_PROG3, idev->keybit);
__set_bit(KEY_PROG4, idev->keybit);
__set_bit(KEY_PC, idev->keybit);
idev->rep[REP_DELAY] = 250;
idev->rep[REP_PERIOD] = 33;

View file

@ -0,0 +1,329 @@
/*
* dm355evm_keys.c - support buttons and IR remote on DM355 EVM board
*
* Copyright (c) 2008 by David Brownell
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/i2c/dm355evm_msp.h>
/*
* The MSP430 firmware on the DM355 EVM monitors on-board pushbuttons
* and an IR receptor used for the remote control. When any key is
* pressed, or its autorepeat kicks in, an event is sent. This driver
* read those events from the small (32 event) queue and reports them.
*
* Because we communicate with the MSP430 using I2C, and all I2C calls
* in Linux sleep, we need to cons up a kind of threaded IRQ handler
* using a work_struct. The IRQ is active low, but we use it through
* the GPIO controller so we can trigger on falling edges.
*
* Note that physically there can only be one of these devices.
*
* This driver was tested with firmware revision A4.
*/
struct dm355evm_keys {
struct work_struct work;
struct input_dev *input;
struct device *dev;
int irq;
};
static irqreturn_t dm355evm_keys_irq(int irq, void *_keys)
{
struct dm355evm_keys *keys = _keys;
schedule_work(&keys->work);
return IRQ_HANDLED;
}
/* These initial keycodes can be remapped by dm355evm_setkeycode(). */
static struct {
u16 event;
u16 keycode;
} dm355evm_keys[] = {
/*
* Pushbuttons on the EVM board ... note that the labels for these
* are SW10/SW11/etc on the PC board. The left/right orientation
* comes only from the firmware's documentation, and presumes the
* power connector is immediately in front of you and the IR sensor
* is to the right. (That is, rotate the board counter-clockwise
* by 90 degrees from the SW10/etc and "DM355 EVM" labels.)
*/
{ 0x00d8, KEY_OK, }, /* SW12 */
{ 0x00b8, KEY_UP, }, /* SW13 */
{ 0x00e8, KEY_DOWN, }, /* SW11 */
{ 0x0078, KEY_LEFT, }, /* SW14 */
{ 0x00f0, KEY_RIGHT, }, /* SW10 */
/*
* IR buttons ... codes assigned to match the universal remote
* provided with the EVM (Philips PM4S) using DVD code 0020.
*
* These event codes match firmware documentation, but other
* remote controls could easily send more RC5-encoded events.
* The PM4S manual was used in several cases to help select
* a keycode reflecting the intended usage.
*
* RC5 codes are 14 bits, with two start bits (0x3 prefix)
* and a toggle bit (masked out below).
*/
{ 0x300c, KEY_POWER, }, /* NOTE: docs omit this */
{ 0x3000, KEY_NUMERIC_0, },
{ 0x3001, KEY_NUMERIC_1, },
{ 0x3002, KEY_NUMERIC_2, },
{ 0x3003, KEY_NUMERIC_3, },
{ 0x3004, KEY_NUMERIC_4, },
{ 0x3005, KEY_NUMERIC_5, },
{ 0x3006, KEY_NUMERIC_6, },
{ 0x3007, KEY_NUMERIC_7, },
{ 0x3008, KEY_NUMERIC_8, },
{ 0x3009, KEY_NUMERIC_9, },
{ 0x3022, KEY_ENTER, },
{ 0x30ec, KEY_MODE, }, /* "tv/vcr/..." */
{ 0x300f, KEY_SELECT, }, /* "info" */
{ 0x3020, KEY_CHANNELUP, }, /* "up" */
{ 0x302e, KEY_MENU, }, /* "in/out" */
{ 0x3011, KEY_VOLUMEDOWN, }, /* "left" */
{ 0x300d, KEY_MUTE, }, /* "ok" */
{ 0x3010, KEY_VOLUMEUP, }, /* "right" */
{ 0x301e, KEY_SUBTITLE, }, /* "cc" */
{ 0x3021, KEY_CHANNELDOWN, }, /* "down" */
{ 0x3022, KEY_PREVIOUS, },
{ 0x3026, KEY_SLEEP, },
{ 0x3172, KEY_REWIND, }, /* NOTE: docs wrongly say 0x30ca */
{ 0x3175, KEY_PLAY, },
{ 0x3174, KEY_FASTFORWARD, },
{ 0x3177, KEY_RECORD, },
{ 0x3176, KEY_STOP, },
{ 0x3169, KEY_PAUSE, },
};
static void dm355evm_keys_work(struct work_struct *work)
{
struct dm355evm_keys *keys;
int status;
keys = container_of(work, struct dm355evm_keys, work);
/* For simplicity we ignore INPUT_COUNT and just read
* events until we get the "queue empty" indicator.
* Reading INPUT_LOW decrements the count.
*/
for (;;) {
static u16 last_event;
u16 event;
int keycode;
int i;
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_HIGH);
if (status < 0) {
dev_dbg(keys->dev, "input high err %d\n",
status);
break;
}
event = status << 8;
status = dm355evm_msp_read(DM355EVM_MSP_INPUT_LOW);
if (status < 0) {
dev_dbg(keys->dev, "input low err %d\n",
status);
break;
}
event |= status;
if (event == 0xdead)
break;
/* Press and release a button: two events, same code.
* Press and hold (autorepeat), then release: N events
* (N > 2), same code. For RC5 buttons the toggle bits
* distinguish (for example) "1-autorepeat" from "1 1";
* but PCB buttons don't support that bit.
*
* So we must synthesize release events. We do that by
* mapping events to a press/release event pair; then
* to avoid adding extra events, skip the second event
* of each pair.
*/
if (event == last_event) {
last_event = 0;
continue;
}
last_event = event;
/* ignore the RC5 toggle bit */
event &= ~0x0800;
/* find the key, or leave it as unknown */
keycode = KEY_UNKNOWN;
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
if (dm355evm_keys[i].event != event)
continue;
keycode = dm355evm_keys[i].keycode;
break;
}
dev_dbg(keys->dev,
"input event 0x%04x--> keycode %d\n",
event, keycode);
/* report press + release */
input_report_key(keys->input, keycode, 1);
input_sync(keys->input);
input_report_key(keys->input, keycode, 0);
input_sync(keys->input);
}
}
static int dm355evm_setkeycode(struct input_dev *dev, int index, int keycode)
{
u16 old_keycode;
unsigned i;
if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
return -EINVAL;
old_keycode = dm355evm_keys[index].keycode;
dm355evm_keys[index].keycode = keycode;
set_bit(keycode, dev->keybit);
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++) {
if (dm355evm_keys[index].keycode == old_keycode)
goto done;
}
clear_bit(old_keycode, dev->keybit);
done:
return 0;
}
static int dm355evm_getkeycode(struct input_dev *dev, int index, int *keycode)
{
if (((unsigned)index) >= ARRAY_SIZE(dm355evm_keys))
return -EINVAL;
return dm355evm_keys[index].keycode;
}
/*----------------------------------------------------------------------*/
static int __devinit dm355evm_keys_probe(struct platform_device *pdev)
{
struct dm355evm_keys *keys;
struct input_dev *input;
int status;
int i;
/* allocate instance struct and input dev */
keys = kzalloc(sizeof *keys, GFP_KERNEL);
input = input_allocate_device();
if (!keys || !input) {
status = -ENOMEM;
goto fail1;
}
keys->dev = &pdev->dev;
keys->input = input;
INIT_WORK(&keys->work, dm355evm_keys_work);
/* set up "threaded IRQ handler" */
status = platform_get_irq(pdev, 0);
if (status < 0)
goto fail1;
keys->irq = status;
input_set_drvdata(input, keys);
input->name = "DM355 EVM Controls";
input->phys = "dm355evm/input0";
input->dev.parent = &pdev->dev;
input->id.bustype = BUS_I2C;
input->id.product = 0x0355;
input->id.version = dm355evm_msp_read(DM355EVM_MSP_FIRMREV);
input->evbit[0] = BIT(EV_KEY);
for (i = 0; i < ARRAY_SIZE(dm355evm_keys); i++)
__set_bit(dm355evm_keys[i].keycode, input->keybit);
input->setkeycode = dm355evm_setkeycode;
input->getkeycode = dm355evm_getkeycode;
/* REVISIT: flush the event queue? */
status = request_irq(keys->irq, dm355evm_keys_irq,
IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), keys);
if (status < 0)
goto fail1;
/* register */
status = input_register_device(input);
if (status < 0)
goto fail2;
platform_set_drvdata(pdev, keys);
return 0;
fail2:
free_irq(keys->irq, keys);
fail1:
input_free_device(input);
kfree(keys);
dev_err(&pdev->dev, "can't register, err %d\n", status);
return status;
}
static int __devexit dm355evm_keys_remove(struct platform_device *pdev)
{
struct dm355evm_keys *keys = platform_get_drvdata(pdev);
free_irq(keys->irq, keys);
input_unregister_device(keys->input);
kfree(keys);
return 0;
}
/* REVISIT: add suspend/resume when DaVinci supports it. The IRQ should
* be able to wake up the system. When device_may_wakeup(&pdev->dev), call
* enable_irq_wake() on suspend, and disable_irq_wake() on resume.
*/
/*
* I2C is used to talk to the MSP430, but this platform device is
* exposed by an MFD driver that manages I2C communications.
*/
static struct platform_driver dm355evm_keys_driver = {
.probe = dm355evm_keys_probe,
.remove = __devexit_p(dm355evm_keys_remove),
.driver = {
.owner = THIS_MODULE,
.name = "dm355evm_keys",
},
};
static int __init dm355evm_keys_init(void)
{
return platform_driver_register(&dm355evm_keys_driver);
}
module_init(dm355evm_keys_init);
static void __exit dm355evm_keys_exit(void)
{
platform_driver_unregister(&dm355evm_keys_driver);
}
module_exit(dm355evm_keys_exit);
MODULE_LICENSE("GPL");

View file

@ -26,13 +26,17 @@
#define DRV_NAME "rotary-encoder"
struct rotary_encoder {
unsigned int irq_a;
unsigned int irq_b;
unsigned int pos;
unsigned int armed;
unsigned int dir;
struct input_dev *input;
struct rotary_encoder_platform_data *pdata;
unsigned int axis;
unsigned int pos;
unsigned int irq_a;
unsigned int irq_b;
bool armed;
unsigned char dir; /* 0 - clockwise, 1 - CCW */
};
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
@ -53,21 +57,32 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
if (!encoder->armed)
break;
if (pdata->relative_axis) {
input_report_rel(encoder->input, pdata->axis,
encoder->dir ? -1 : 1);
} else {
unsigned int pos = encoder->pos;
if (encoder->dir) {
/* turning counter-clockwise */
encoder->pos += pdata->steps;
encoder->pos--;
encoder->pos %= pdata->steps;
if (pdata->rollover)
pos += pdata->steps;
if (pos)
pos--;
} else {
/* turning clockwise */
encoder->pos++;
encoder->pos %= pdata->steps;
if (pdata->rollover || pos < pdata->steps)
pos++;
}
if (pdata->rollover)
pos %= pdata->steps;
encoder->pos = pos;
input_report_abs(encoder->input, pdata->axis,
encoder->pos);
}
input_report_abs(encoder->input, pdata->axis, encoder->pos);
input_sync(encoder->input);
encoder->armed = 0;
encoder->armed = false;
break;
case 0x1:
@ -77,7 +92,7 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
break;
case 0x3:
encoder->armed = 1;
encoder->armed = true;
break;
}
@ -113,9 +128,15 @@ static int __devinit rotary_encoder_probe(struct platform_device *pdev)
input->name = pdev->name;
input->id.bustype = BUS_HOST;
input->dev.parent = &pdev->dev;
if (pdata->relative_axis) {
input->evbit[0] = BIT_MASK(EV_REL);
input->relbit[0] = BIT_MASK(pdata->axis);
} else {
input->evbit[0] = BIT_MASK(EV_ABS);
input_set_abs_params(encoder->input,
pdata->axis, 0, pdata->steps, 0, 1);
}
err = input_register_device(input);
if (err) {

View file

@ -0,0 +1,145 @@
/**
* twl4030-pwrbutton.c - TWL4030 Power Button Input Driver
*
* Copyright (C) 2008-2009 Nokia Corporation
*
* Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
* Several fixes by Felipe Balbi <felipe.balbi@nokia.com>
*
* This file is subject to the terms and conditions of the GNU General
* Public License. See the file "COPYING" in the main directory of this
* archive for more details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
#define PWR_PWRON_IRQ (1 << 0)
#define STS_HW_CONDITIONS 0xf
static irqreturn_t powerbutton_irq(int irq, void *_pwr)
{
struct input_dev *pwr = _pwr;
int err;
u8 value;
#ifdef CONFIG_LOCKDEP
/* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
* we don't want and can't tolerate since this is a threaded
* IRQ and can sleep due to the i2c reads it has to issue.
* Although it might be friendlier not to borrow this thread
* context...
*/
local_irq_enable();
#endif
err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
STS_HW_CONDITIONS);
if (!err) {
input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
input_sync(pwr);
} else {
dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading"
" TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err);
}
return IRQ_HANDLED;
}
static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev)
{
struct input_dev *pwr;
int irq = platform_get_irq(pdev, 0);
int err;
pwr = input_allocate_device();
if (!pwr) {
dev_dbg(&pdev->dev, "Can't allocate power button\n");
return -ENOMEM;
}
pwr->evbit[0] = BIT_MASK(EV_KEY);
pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
pwr->name = "twl4030_pwrbutton";
pwr->phys = "twl4030_pwrbutton/input0";
pwr->dev.parent = &pdev->dev;
err = request_irq(irq, powerbutton_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"twl4030_pwrbutton", pwr);
if (err < 0) {
dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
goto free_input_dev;
}
err = input_register_device(pwr);
if (err) {
dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
goto free_irq;
}
platform_set_drvdata(pdev, pwr);
return 0;
free_irq:
free_irq(irq, NULL);
free_input_dev:
input_free_device(pwr);
return err;
}
static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)
{
struct input_dev *pwr = platform_get_drvdata(pdev);
int irq = platform_get_irq(pdev, 0);
free_irq(irq, pwr);
input_unregister_device(pwr);
return 0;
}
struct platform_driver twl4030_pwrbutton_driver = {
.probe = twl4030_pwrbutton_probe,
.remove = __devexit_p(twl4030_pwrbutton_remove),
.driver = {
.name = "twl4030_pwrbutton",
.owner = THIS_MODULE,
},
};
static int __init twl4030_pwrbutton_init(void)
{
return platform_driver_register(&twl4030_pwrbutton_driver);
}
module_init(twl4030_pwrbutton_init);
static void __exit twl4030_pwrbutton_exit(void)
{
platform_driver_unregister(&twl4030_pwrbutton_driver);
}
module_exit(twl4030_pwrbutton_exit);
MODULE_ALIAS("platform:twl4030_pwrbutton");
MODULE_DESCRIPTION("Triton2 Power Button");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>");
MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");

View file

@ -54,27 +54,28 @@ static int uinput_dev_event(struct input_dev *dev, unsigned int type, unsigned i
return 0;
}
/* Atomically allocate an ID for the given request. Returns 0 on success. */
static int uinput_request_alloc_id(struct uinput_device *udev, struct uinput_request *request)
{
/* Atomically allocate an ID for the given request. Returns 0 on success. */
int id;
int err = -1;
spin_lock(&udev->requests_lock);
for (id = 0; id < UINPUT_NUM_REQUESTS; id++)
for (id = 0; id < UINPUT_NUM_REQUESTS; id++) {
if (!udev->requests[id]) {
request->id = id;
udev->requests[id] = request;
err = 0;
break;
}
}
spin_unlock(&udev->requests_lock);
return err;
}
static struct uinput_request* uinput_request_find(struct uinput_device *udev, int id)
static struct uinput_request *uinput_request_find(struct uinput_device *udev, int id)
{
/* Find an input request, by ID. Returns NULL if the ID isn't valid. */
if (id >= UINPUT_NUM_REQUESTS || id < 0)
@ -99,14 +100,51 @@ static void uinput_request_done(struct uinput_device *udev, struct uinput_reques
complete(&request->done);
}
static int uinput_request_submit(struct input_dev *dev, struct uinput_request *request)
static int uinput_request_submit(struct uinput_device *udev, struct uinput_request *request)
{
/* Tell our userspace app about this new request by queueing an input event */
uinput_dev_event(dev, EV_UINPUT, request->code, request->id);
int retval;
/* Wait for the request to complete */
wait_for_completion(&request->done);
return request->retval;
retval = uinput_request_reserve_slot(udev, request);
if (retval)
return retval;
retval = mutex_lock_interruptible(&udev->mutex);
if (retval)
return retval;
if (udev->state != UIST_CREATED) {
retval = -ENODEV;
goto out;
}
/* Tell our userspace app about this new request by queueing an input event */
uinput_dev_event(udev->dev, EV_UINPUT, request->code, request->id);
out:
mutex_unlock(&udev->mutex);
return retval;
}
/*
* Fail all ouitstanding requests so handlers don't wait for the userspace
* to finish processing them.
*/
static void uinput_flush_requests(struct uinput_device *udev)
{
struct uinput_request *request;
int i;
spin_lock(&udev->requests_lock);
for (i = 0; i < UINPUT_NUM_REQUESTS; i++) {
request = udev->requests[i];
if (request) {
request->retval = -ENODEV;
uinput_request_done(udev, request);
}
}
spin_unlock(&udev->requests_lock);
}
static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
@ -126,6 +164,7 @@ static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
{
struct uinput_device *udev = input_get_drvdata(dev);
struct uinput_request request;
int retval;
@ -146,15 +185,18 @@ static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *eff
request.u.upload.effect = effect;
request.u.upload.old = old;
retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
if (!retval)
retval = uinput_request_submit(dev, &request);
retval = uinput_request_submit(udev, &request);
if (!retval) {
wait_for_completion(&request.done);
retval = request.retval;
}
return retval;
}
static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
{
struct uinput_device *udev = input_get_drvdata(dev);
struct uinput_request request;
int retval;
@ -166,9 +208,11 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
request.code = UI_FF_ERASE;
request.u.effect_id = effect_id;
retval = uinput_request_reserve_slot(input_get_drvdata(dev), &request);
if (!retval)
retval = uinput_request_submit(dev, &request);
retval = uinput_request_submit(udev, &request);
if (!retval) {
wait_for_completion(&request.done);
retval = request.retval;
}
return retval;
}
@ -176,20 +220,24 @@ static int uinput_dev_erase_effect(struct input_dev *dev, int effect_id)
static void uinput_destroy_device(struct uinput_device *udev)
{
const char *name, *phys;
struct input_dev *dev = udev->dev;
enum uinput_state old_state = udev->state;
if (udev->dev) {
name = udev->dev->name;
phys = udev->dev->phys;
if (udev->state == UIST_CREATED)
input_unregister_device(udev->dev);
else
input_free_device(udev->dev);
udev->state = UIST_NEW_DEVICE;
if (dev) {
name = dev->name;
phys = dev->phys;
if (old_state == UIST_CREATED) {
uinput_flush_requests(udev);
input_unregister_device(dev);
} else {
input_free_device(dev);
}
kfree(name);
kfree(phys);
udev->dev = NULL;
}
udev->state = UIST_NEW_DEVICE;
}
static int uinput_create_device(struct uinput_device *udev)

View file

@ -303,4 +303,22 @@ config MOUSE_MAPLE
To compile this driver as a module choose M here: the module will be
called maplemouse.
config MOUSE_SYNAPTICS_I2C
tristate "Synaptics I2C Touchpad support"
depends on I2C
help
This driver supports Synaptics I2C touchpad controller on eXeda
mobile device.
The device will not work the synaptics X11 driver because
(i) it reports only relative coordinates and has no capabilities
to report absolute coordinates
(ii) the eXeda device itself uses Xfbdev as X Server and it does
not allow using xf86-input-* drivers.
Say y here if you have eXeda device and want to use a Synaptics
I2C Touchpad.
To compile this driver as a module, choose M here: the
module will be called synaptics_i2c.
endif

View file

@ -18,6 +18,7 @@ obj-$(CONFIG_MOUSE_PS2) += psmouse.o
obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
obj-$(CONFIG_MOUSE_SERIAL) += sermouse.o
obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
psmouse-objs := psmouse-base.o synaptics.o

View file

@ -48,7 +48,7 @@ static const struct alps_model_info alps_model_data[] = {
{ { 0x63, 0x02, 0x3c }, 0x8f, 0x8f, ALPS_WHEEL }, /* Toshiba Satellite S2400-103 */
{ { 0x63, 0x02, 0x50 }, 0xef, 0xef, ALPS_FW_BK_1 }, /* NEC Versa L320 */
{ { 0x63, 0x02, 0x64 }, 0xf8, 0xf8, 0 },
{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS }, /* Dell Latitude D800 */
{ { 0x63, 0x03, 0xc8 }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D800 */
{ { 0x73, 0x00, 0x0a }, 0xf8, 0xf8, ALPS_DUALPOINT }, /* ThinkPad R61 8918-5QG */
{ { 0x73, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
{ { 0x73, 0x02, 0x14 }, 0xf8, 0xf8, ALPS_FW_BK_2 }, /* Ahtec Laptop */
@ -56,7 +56,7 @@ static const struct alps_model_info alps_model_data[] = {
{ { 0x22, 0x02, 0x0a }, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT },
{ { 0x22, 0x02, 0x14 }, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude D600 */
{ { 0x62, 0x02, 0x14 }, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT }, /* Dell Latitude E6500 */
{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 } /* Dell Vostro 1400 */
{ { 0x73, 0x02, 0x50 }, 0xcf, 0xcf, ALPS_FW_BK_1 }, /* Dell Vostro 1400 */
};
/*
@ -132,18 +132,23 @@ static void alps_process_packet(struct psmouse *psmouse)
ges = packet[2] & 1;
fin = packet[2] & 2;
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_MIDDLE, middle);
if ((priv->i->flags & ALPS_DUALPOINT) && z == 127) {
input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x));
input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y));
input_report_key(dev2, BTN_LEFT, left);
input_report_key(dev2, BTN_RIGHT, right);
input_report_key(dev2, BTN_MIDDLE, middle);
input_sync(dev);
input_sync(dev2);
return;
}
input_report_key(dev, BTN_LEFT, left);
input_report_key(dev, BTN_RIGHT, right);
input_report_key(dev, BTN_MIDDLE, middle);
/* Convert hardware tap to a reasonable Z value */
if (ges && !fin) z = 40;

View file

@ -361,7 +361,7 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
(!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
(*fingers)++;
is_increasing = 1;
} else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
is_increasing = 0;
}

View file

@ -159,7 +159,8 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
if (!dev2)
printk(KERN_WARNING "lifebook.c: got relative packet "
"but no relative device set up\n");
} else if (lifebook_use_6byte_proto) {
} else {
if (lifebook_use_6byte_proto) {
input_report_abs(dev1, ABS_X,
((packet[1] & 0x3f) << 6) | (packet[2] & 0x3f));
input_report_abs(dev1, ABS_Y,
@ -170,9 +171,9 @@ static psmouse_ret_t lifebook_process_byte(struct psmouse *psmouse)
input_report_abs(dev1, ABS_Y,
1024 - (packet[2] | ((packet[0] & 0xC0) << 2)));
}
input_report_key(dev1, BTN_TOUCH, packet[0] & 0x04);
input_sync(dev1);
}
if (dev2) {
if (relative_packet) {

View file

@ -327,7 +327,9 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
goto out;
}
if (psmouse->packet[1] == PSMOUSE_RET_ID) {
if (psmouse->packet[1] == PSMOUSE_RET_ID ||
(psmouse->type == PSMOUSE_HGPK &&
psmouse->packet[1] == PSMOUSE_RET_BAT)) {
__psmouse_set_state(psmouse, PSMOUSE_IGNORE);
serio_reconnect(serio);
goto out;

View file

@ -180,6 +180,29 @@ static int synaptics_identify(struct psmouse *psmouse)
return -1;
}
/*
* Read touchpad resolution
* Resolution is left zero if touchpad does not support the query
*/
static int synaptics_resolution(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char res[3];
if (SYN_ID_MAJOR(priv->identity) < 4)
return 0;
if (synaptics_send_cmd(psmouse, SYN_QUE_RESOLUTION, res))
return 0;
if ((res[0] != 0) && (res[1] & 0x80) && (res[2] != 0)) {
priv->x_res = res[0]; /* x resolution in units/mm */
priv->y_res = res[2]; /* y resolution in units/mm */
}
return 0;
}
static int synaptics_query_hardware(struct psmouse *psmouse)
{
if (synaptics_identify(psmouse))
@ -188,6 +211,8 @@ static int synaptics_query_hardware(struct psmouse *psmouse)
return -1;
if (synaptics_capability(psmouse))
return -1;
if (synaptics_resolution(psmouse))
return -1;
return 0;
}
@ -563,6 +588,9 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
clear_bit(EV_REL, dev->evbit);
clear_bit(REL_X, dev->relbit);
clear_bit(REL_Y, dev->relbit);
dev->absres[ABS_X] = priv->x_res;
dev->absres[ABS_Y] = priv->y_res;
}
static void synaptics_disconnect(struct psmouse *psmouse)

View file

@ -97,6 +97,8 @@ struct synaptics_data {
unsigned long int capabilities; /* Capabilities */
unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int identity; /* Identification */
int x_res; /* X resolution in units/mm */
int y_res; /* Y resolution in units/mm */
unsigned char pkt_type; /* packet type - old, new, etc */
unsigned char mode; /* current mode byte */

View file

@ -0,0 +1,676 @@
/*
* Synaptics touchpad with I2C interface
*
* Copyright (C) 2009 Compulab, Ltd.
* Mike Rapoport <mike@compulab.co.il>
* Igor Grinberg <grinberg@compulab.co.il>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
* more details.
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#define DRIVER_NAME "synaptics_i2c"
/* maximum product id is 15 characters */
#define PRODUCT_ID_LENGTH 15
#define REGISTER_LENGTH 8
/*
* after soft reset, we should wait for 1 ms
* before the device becomes operational
*/
#define SOFT_RESET_DELAY_MS 3
/* and after hard reset, we should wait for max 500ms */
#define HARD_RESET_DELAY_MS 500
/* Registers by SMBus address */
#define PAGE_SEL_REG 0xff
#define DEVICE_STATUS_REG 0x09
/* Registers by RMI address */
#define DEV_CONTROL_REG 0x0000
#define INTERRUPT_EN_REG 0x0001
#define ERR_STAT_REG 0x0002
#define INT_REQ_STAT_REG 0x0003
#define DEV_COMMAND_REG 0x0004
#define RMI_PROT_VER_REG 0x0200
#define MANUFACT_ID_REG 0x0201
#define PHYS_INT_VER_REG 0x0202
#define PROD_PROPERTY_REG 0x0203
#define INFO_QUERY_REG0 0x0204
#define INFO_QUERY_REG1 (INFO_QUERY_REG0 + 1)
#define INFO_QUERY_REG2 (INFO_QUERY_REG0 + 2)
#define INFO_QUERY_REG3 (INFO_QUERY_REG0 + 3)
#define PRODUCT_ID_REG0 0x0210
#define PRODUCT_ID_REG1 (PRODUCT_ID_REG0 + 1)
#define PRODUCT_ID_REG2 (PRODUCT_ID_REG0 + 2)
#define PRODUCT_ID_REG3 (PRODUCT_ID_REG0 + 3)
#define PRODUCT_ID_REG4 (PRODUCT_ID_REG0 + 4)
#define PRODUCT_ID_REG5 (PRODUCT_ID_REG0 + 5)
#define PRODUCT_ID_REG6 (PRODUCT_ID_REG0 + 6)
#define PRODUCT_ID_REG7 (PRODUCT_ID_REG0 + 7)
#define PRODUCT_ID_REG8 (PRODUCT_ID_REG0 + 8)
#define PRODUCT_ID_REG9 (PRODUCT_ID_REG0 + 9)
#define PRODUCT_ID_REG10 (PRODUCT_ID_REG0 + 10)
#define PRODUCT_ID_REG11 (PRODUCT_ID_REG0 + 11)
#define PRODUCT_ID_REG12 (PRODUCT_ID_REG0 + 12)
#define PRODUCT_ID_REG13 (PRODUCT_ID_REG0 + 13)
#define PRODUCT_ID_REG14 (PRODUCT_ID_REG0 + 14)
#define PRODUCT_ID_REG15 (PRODUCT_ID_REG0 + 15)
#define DATA_REG0 0x0400
#define ABS_PRESSURE_REG 0x0401
#define ABS_MSB_X_REG 0x0402
#define ABS_LSB_X_REG (ABS_MSB_X_REG + 1)
#define ABS_MSB_Y_REG 0x0404
#define ABS_LSB_Y_REG (ABS_MSB_Y_REG + 1)
#define REL_X_REG 0x0406
#define REL_Y_REG 0x0407
#define DEV_QUERY_REG0 0x1000
#define DEV_QUERY_REG1 (DEV_QUERY_REG0 + 1)
#define DEV_QUERY_REG2 (DEV_QUERY_REG0 + 2)
#define DEV_QUERY_REG3 (DEV_QUERY_REG0 + 3)
#define DEV_QUERY_REG4 (DEV_QUERY_REG0 + 4)
#define DEV_QUERY_REG5 (DEV_QUERY_REG0 + 5)
#define DEV_QUERY_REG6 (DEV_QUERY_REG0 + 6)
#define DEV_QUERY_REG7 (DEV_QUERY_REG0 + 7)
#define DEV_QUERY_REG8 (DEV_QUERY_REG0 + 8)
#define GENERAL_2D_CONTROL_REG 0x1041
#define SENSOR_SENSITIVITY_REG 0x1044
#define SENS_MAX_POS_MSB_REG 0x1046
#define SENS_MAX_POS_LSB_REG (SENS_MAX_POS_UPPER_REG + 1)
/* Register bits */
/* Device Control Register Bits */
#define REPORT_RATE_1ST_BIT 6
/* Interrupt Enable Register Bits (INTERRUPT_EN_REG) */
#define F10_ABS_INT_ENA 0
#define F10_REL_INT_ENA 1
#define F20_INT_ENA 2
/* Interrupt Request Register Bits (INT_REQ_STAT_REG | DEVICE_STATUS_REG) */
#define F10_ABS_INT_REQ 0
#define F10_REL_INT_REQ 1
#define F20_INT_REQ 2
/* Device Status Register Bits (DEVICE_STATUS_REG) */
#define STAT_CONFIGURED 6
#define STAT_ERROR 7
/* Device Command Register Bits (DEV_COMMAND_REG) */
#define RESET_COMMAND 0x01
#define REZERO_COMMAND 0x02
/* Data Register 0 Bits (DATA_REG0) */
#define GESTURE 3
/* Device Query Registers Bits */
/* DEV_QUERY_REG3 */
#define HAS_PALM_DETECT 1
#define HAS_MULTI_FING 2
#define HAS_SCROLLER 4
#define HAS_2D_SCROLL 5
/* General 2D Control Register Bits (GENERAL_2D_CONTROL_REG) */
#define NO_DECELERATION 1
#define REDUCE_REPORTING 3
#define NO_FILTER 5
/* Function Masks */
/* Device Control Register Masks (DEV_CONTROL_REG) */
#define REPORT_RATE_MSK 0xc0
#define SLEEP_MODE_MSK 0x07
/* Device Sleep Modes */
#define FULL_AWAKE 0x0
#define NORMAL_OP 0x1
#define LOW_PWR_OP 0x2
#define VERY_LOW_PWR_OP 0x3
#define SENS_SLEEP 0x4
#define SLEEP_MOD 0x5
#define DEEP_SLEEP 0x6
#define HIBERNATE 0x7
/* Interrupt Register Mask */
/* (INT_REQ_STAT_REG | DEVICE_STATUS_REG | INTERRUPT_EN_REG) */
#define INT_ENA_REQ_MSK 0x07
#define INT_ENA_ABS_MSK 0x01
#define INT_ENA_REL_MSK 0x02
#define INT_ENA_F20_MSK 0x04
/* Device Status Register Masks (DEVICE_STATUS_REG) */
#define CONFIGURED_MSK 0x40
#define ERROR_MSK 0x80
/* Data Register 0 Masks */
#define FINGER_WIDTH_MSK 0xf0
#define GESTURE_MSK 0x08
#define SENSOR_STATUS_MSK 0x07
/*
* MSB Position Register Masks
* ABS_MSB_X_REG | ABS_MSB_Y_REG | SENS_MAX_POS_MSB_REG |
* DEV_QUERY_REG3 | DEV_QUERY_REG5
*/
#define MSB_POSITION_MSK 0x1f
/* Device Query Registers Masks */
/* DEV_QUERY_REG2 */
#define NUM_EXTRA_POS_MSK 0x07
/* When in IRQ mode read the device every THREAD_IRQ_SLEEP_SECS */
#define THREAD_IRQ_SLEEP_SECS 2
#define THREAD_IRQ_SLEEP_MSECS (THREAD_IRQ_SLEEP_SECS * MSEC_PER_SEC)
/*
* When in Polling mode and no data received for NO_DATA_THRES msecs
* reduce the polling rate to NO_DATA_SLEEP_MSECS
*/
#define NO_DATA_THRES (MSEC_PER_SEC)
#define NO_DATA_SLEEP_MSECS (MSEC_PER_SEC / 4)
/* Control touchpad's No Deceleration option */
static int no_decel = 1;
module_param(no_decel, bool, 0644);
MODULE_PARM_DESC(no_decel, "No Deceleration. Default = 1 (on)");
/* Control touchpad's Reduced Reporting option */
static int reduce_report;
module_param(reduce_report, bool, 0644);
MODULE_PARM_DESC(reduce_report, "Reduced Reporting. Default = 0 (off)");
/* Control touchpad's No Filter option */
static int no_filter;
module_param(no_filter, bool, 0644);
MODULE_PARM_DESC(no_filter, "No Filter. Default = 0 (off)");
/*
* touchpad Attention line is Active Low and Open Drain,
* therefore should be connected to pulled up line
* and the irq configuration should be set to Falling Edge Trigger
*/
/* Control IRQ / Polling option */
static int polling_req;
module_param(polling_req, bool, 0444);
MODULE_PARM_DESC(polling_req, "Request Polling. Default = 0 (use irq)");
/* Control Polling Rate */
static int scan_rate = 80;
module_param(scan_rate, int, 0644);
MODULE_PARM_DESC(scan_rate, "Polling rate in times/sec. Default = 80");
/* The main device structure */
struct synaptics_i2c {
struct i2c_client *client;
struct input_dev *input;
struct delayed_work dwork;
int no_data_count;
int no_decel_param;
int reduce_report_param;
int no_filter_param;
int scan_rate_param;
int scan_ms;
};
static inline void set_scan_rate(struct synaptics_i2c *touch, int scan_rate)
{
touch->scan_ms = MSEC_PER_SEC / scan_rate;
touch->scan_rate_param = scan_rate;
}
/*
* Driver's initial design makes no race condition possible on i2c bus,
* so there is no need in any locking.
* Keep it in mind, while playing with the code.
*/
static s32 synaptics_i2c_reg_get(struct i2c_client *client, u16 reg)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (ret == 0)
ret = i2c_smbus_read_byte_data(client, reg & 0xff);
return ret;
}
static s32 synaptics_i2c_reg_set(struct i2c_client *client, u16 reg, u8 val)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (ret == 0)
ret = i2c_smbus_write_byte_data(client, reg & 0xff, val);
return ret;
}
static s32 synaptics_i2c_word_get(struct i2c_client *client, u16 reg)
{
int ret;
ret = i2c_smbus_write_byte_data(client, PAGE_SEL_REG, reg >> 8);
if (ret == 0)
ret = i2c_smbus_read_word_data(client, reg & 0xff);
return ret;
}
static int synaptics_i2c_config(struct i2c_client *client)
{
int ret, control;
u8 int_en;
/* set Report Rate to Device Highest (>=80) and Sleep to normal */
ret = synaptics_i2c_reg_set(client, DEV_CONTROL_REG, 0xc1);
if (ret)
return ret;
/* set Interrupt Disable to Func20 / Enable to Func10) */
int_en = (polling_req) ? 0 : INT_ENA_ABS_MSK | INT_ENA_REL_MSK;
ret = synaptics_i2c_reg_set(client, INTERRUPT_EN_REG, int_en);
if (ret)
return ret;
control = synaptics_i2c_reg_get(client, GENERAL_2D_CONTROL_REG);
/* No Deceleration */
control |= no_decel ? 1 << NO_DECELERATION : 0;
/* Reduced Reporting */
control |= reduce_report ? 1 << REDUCE_REPORTING : 0;
/* No Filter */
control |= no_filter ? 1 << NO_FILTER : 0;
ret = synaptics_i2c_reg_set(client, GENERAL_2D_CONTROL_REG, control);
if (ret)
return ret;
return 0;
}
static int synaptics_i2c_reset_config(struct i2c_client *client)
{
int ret;
/* Reset the Touchpad */
ret = synaptics_i2c_reg_set(client, DEV_COMMAND_REG, RESET_COMMAND);
if (ret) {
dev_err(&client->dev, "Unable to reset device\n");
} else {
msleep(SOFT_RESET_DELAY_MS);
ret = synaptics_i2c_config(client);
if (ret)
dev_err(&client->dev, "Unable to config device\n");
}
return ret;
}
static int synaptics_i2c_check_error(struct i2c_client *client)
{
int status, ret = 0;
status = i2c_smbus_read_byte_data(client, DEVICE_STATUS_REG) &
(CONFIGURED_MSK | ERROR_MSK);
if (status != CONFIGURED_MSK)
ret = synaptics_i2c_reset_config(client);
return ret;
}
static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
{
struct input_dev *input = touch->input;
int xy_delta, gesture;
s32 data;
s8 x_delta, y_delta;
/* Deal with spontanious resets and errors */
if (synaptics_i2c_check_error(touch->client))
return 0;
/* Get Gesture Bit */
data = synaptics_i2c_reg_get(touch->client, DATA_REG0);
gesture = (data >> GESTURE) & 0x1;
/*
* Get Relative axes. we have to get them in one shot,
* so we get 2 bytes starting from REL_X_REG.
*/
xy_delta = synaptics_i2c_word_get(touch->client, REL_X_REG) & 0xffff;
/* Separate X from Y */
x_delta = xy_delta & 0xff;
y_delta = (xy_delta >> REGISTER_LENGTH) & 0xff;
/* Report the button event */
input_report_key(input, BTN_LEFT, gesture);
/* Report the deltas */
input_report_rel(input, REL_X, x_delta);
input_report_rel(input, REL_Y, -y_delta);
input_sync(input);
return xy_delta || gesture;
}
static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
{
struct synaptics_i2c *touch = dev_id;
/*
* We want to have the work run immediately but it might have
* already been scheduled with a delay, that's why we have to
* cancel it first.
*/
cancel_delayed_work(&touch->dwork);
schedule_delayed_work(&touch->dwork, 0);
return IRQ_HANDLED;
}
static void synaptics_i2c_check_params(struct synaptics_i2c *touch)
{
bool reset = false;
if (scan_rate != touch->scan_rate_param)
set_scan_rate(touch, scan_rate);
if (no_decel != touch->no_decel_param) {
touch->no_decel_param = no_decel;
reset = true;
}
if (no_filter != touch->no_filter_param) {
touch->no_filter_param = no_filter;
reset = true;
}
if (reduce_report != touch->reduce_report_param) {
touch->reduce_report_param = reduce_report;
reset = true;
}
if (reset)
synaptics_i2c_reset_config(touch->client);
}
/* Control the Device polling rate / Work Handler sleep time */
unsigned long synaptics_i2c_adjust_delay(struct synaptics_i2c *touch,
bool have_data)
{
unsigned long delay, nodata_count_thres;
if (polling_req) {
delay = touch->scan_ms;
if (have_data) {
touch->no_data_count = 0;
} else {
nodata_count_thres = NO_DATA_THRES / touch->scan_ms;
if (touch->no_data_count < nodata_count_thres)
touch->no_data_count++;
else
delay = NO_DATA_SLEEP_MSECS;
}
return msecs_to_jiffies(delay);
} else {
delay = msecs_to_jiffies(THREAD_IRQ_SLEEP_MSECS);
return round_jiffies_relative(delay);
}
}
/* Work Handler */
static void synaptics_i2c_work_handler(struct work_struct *work)
{
bool have_data;
struct synaptics_i2c *touch =
container_of(work, struct synaptics_i2c, dwork.work);
unsigned long delay;
synaptics_i2c_check_params(touch);
have_data = synaptics_i2c_get_input(touch);
delay = synaptics_i2c_adjust_delay(touch, have_data);
/*
* While interrupt driven, there is no real need to poll the device.
* But touchpads are very sensitive, so there could be errors
* related to physical environment and the attention line isn't
* neccesarily asserted. In such case we can lose the touchpad.
* We poll the device once in THREAD_IRQ_SLEEP_SECS and
* if error is detected, we try to reset and reconfigure the touchpad.
*/
schedule_delayed_work(&touch->dwork, delay);
}
static int synaptics_i2c_open(struct input_dev *input)
{
struct synaptics_i2c *touch = input_get_drvdata(input);
int ret;
ret = synaptics_i2c_reset_config(touch->client);
if (ret)
return ret;
if (polling_req)
schedule_delayed_work(&touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
static void synaptics_i2c_close(struct input_dev *input)
{
struct synaptics_i2c *touch = input_get_drvdata(input);
if (!polling_req)
synaptics_i2c_reg_set(touch->client, INTERRUPT_EN_REG, 0);
cancel_delayed_work_sync(&touch->dwork);
/* Save some power */
synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP);
}
static void synaptics_i2c_set_input_params(struct synaptics_i2c *touch)
{
struct input_dev *input = touch->input;
input->name = touch->client->name;
input->phys = touch->client->adapter->name;
input->id.bustype = BUS_I2C;
input->id.version = synaptics_i2c_word_get(touch->client,
INFO_QUERY_REG0);
input->dev.parent = &touch->client->dev;
input->open = synaptics_i2c_open;
input->close = synaptics_i2c_close;
input_set_drvdata(input, touch);
/* Register the device as mouse */
__set_bit(EV_REL, input->evbit);
__set_bit(REL_X, input->relbit);
__set_bit(REL_Y, input->relbit);
/* Register device's buttons and keys */
__set_bit(EV_KEY, input->evbit);
__set_bit(BTN_LEFT, input->keybit);
}
struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *client)
{
struct synaptics_i2c *touch;
touch = kzalloc(sizeof(struct synaptics_i2c), GFP_KERNEL);
if (!touch)
return NULL;
touch->client = client;
touch->no_decel_param = no_decel;
touch->scan_rate_param = scan_rate;
set_scan_rate(touch, scan_rate);
INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
return touch;
}
static int __devinit synaptics_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int ret;
struct synaptics_i2c *touch;
touch = synaptics_i2c_touch_create(client);
if (!touch)
return -ENOMEM;
i2c_set_clientdata(client, touch);
ret = synaptics_i2c_reset_config(client);
if (ret)
goto err_mem_free;
if (client->irq < 1)
polling_req = 1;
touch->input = input_allocate_device();
if (!touch->input) {
ret = -ENOMEM;
goto err_mem_free;
}
synaptics_i2c_set_input_params(touch);
if (!polling_req) {
dev_dbg(&touch->client->dev,
"Requesting IRQ: %d\n", touch->client->irq);
ret = request_irq(touch->client->irq, synaptics_i2c_irq,
IRQF_DISABLED|IRQ_TYPE_EDGE_FALLING,
DRIVER_NAME, touch);
if (ret) {
dev_warn(&touch->client->dev,
"IRQ request failed: %d, "
"falling back to polling\n", ret);
polling_req = 1;
synaptics_i2c_reg_set(touch->client,
INTERRUPT_EN_REG, 0);
}
}
if (polling_req)
dev_dbg(&touch->client->dev,
"Using polling at rate: %d times/sec\n", scan_rate);
/* Register the device in input subsystem */
ret = input_register_device(touch->input);
if (ret) {
dev_err(&client->dev,
"Input device register failed: %d\n", ret);
goto err_input_free;
}
return 0;
err_input_free:
input_free_device(touch->input);
err_mem_free:
i2c_set_clientdata(client, NULL);
kfree(touch);
return ret;
}
static int __devexit synaptics_i2c_remove(struct i2c_client *client)
{
struct synaptics_i2c *touch = i2c_get_clientdata(client);
if (!polling_req)
free_irq(touch->client->irq, touch);
input_unregister_device(touch->input);
i2c_set_clientdata(client, NULL);
kfree(touch);
return 0;
}
#ifdef CONFIG_PM
static int synaptics_i2c_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct synaptics_i2c *touch = i2c_get_clientdata(client);
cancel_delayed_work_sync(&touch->dwork);
/* Save some power */
synaptics_i2c_reg_set(touch->client, DEV_CONTROL_REG, DEEP_SLEEP);
return 0;
}
static int synaptics_i2c_resume(struct i2c_client *client)
{
int ret;
struct synaptics_i2c *touch = i2c_get_clientdata(client);
ret = synaptics_i2c_reset_config(client);
if (ret)
return ret;
schedule_delayed_work(&touch->dwork,
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
return 0;
}
#else
#define synaptics_i2c_suspend NULL
#define synaptics_i2c_resume NULL
#endif
static const struct i2c_device_id synaptics_i2c_id_table[] = {
{ "synaptics_i2c", 0 },
{ },
};
MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
static struct i2c_driver synaptics_i2c_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
},
.probe = synaptics_i2c_probe,
.remove = __devexit_p(synaptics_i2c_remove),
.suspend = synaptics_i2c_suspend,
.resume = synaptics_i2c_resume,
.id_table = synaptics_i2c_id_table,
};
static int __init synaptics_i2c_init(void)
{
return i2c_add_driver(&synaptics_i2c_driver);
}
static void __exit synaptics_i2c_exit(void)
{
i2c_del_driver(&synaptics_i2c_driver);
}
module_init(synaptics_i2c_init);
module_exit(synaptics_i2c_exit);
MODULE_DESCRIPTION("Synaptics I2C touchpad driver");
MODULE_AUTHOR("Mike Rapoport, Igor Grinberg, Compulab");
MODULE_LICENSE("GPL");

View file

@ -60,7 +60,6 @@ struct mousedev {
int exist;
int open;
int minor;
char name[16];
struct input_handle handle;
wait_queue_head_t wait;
struct list_head client_list;
@ -863,19 +862,17 @@ static struct mousedev *mousedev_create(struct input_dev *dev,
init_waitqueue_head(&mousedev->wait);
if (minor == MOUSEDEV_MIX)
strlcpy(mousedev->name, "mice", sizeof(mousedev->name));
dev_set_name(&mousedev->dev, "mice");
else
snprintf(mousedev->name, sizeof(mousedev->name),
"mouse%d", minor);
dev_set_name(&mousedev->dev, "mouse%d", minor);
mousedev->minor = minor;
mousedev->exist = 1;
mousedev->handle.dev = input_get_device(dev);
mousedev->handle.name = mousedev->name;
mousedev->handle.name = dev_name(&mousedev->dev);
mousedev->handle.handler = handler;
mousedev->handle.private = mousedev;
dev_set_name(&mousedev->dev, mousedev->name);
mousedev->dev.class = &input_class;
if (dev)
mousedev->dev.parent = &dev->dev;

View file

@ -10,6 +10,7 @@
* the Free Software Foundation.
*/
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
@ -921,6 +922,9 @@ static void i8042_dritek_enable(void)
#endif
#ifdef CONFIG_PM
static bool i8042_suspended;
/*
* Here we try to restore the original BIOS settings. We only want to
* do that once, when we really suspend, not when we taking memory
@ -930,11 +934,9 @@ static void i8042_dritek_enable(void)
static int i8042_suspend(struct platform_device *dev, pm_message_t state)
{
if (dev->dev.power.power_state.event != state.event) {
if (state.event == PM_EVENT_SUSPEND)
if (!i8042_suspended && state.event == PM_EVENT_SUSPEND) {
i8042_controller_reset();
dev->dev.power.power_state = state;
i8042_suspended = true;
}
return 0;
@ -952,7 +954,7 @@ static int i8042_resume(struct platform_device *dev)
/*
* Do not bother with restoring state if we haven't suspened yet
*/
if (dev->dev.power.power_state.event == PM_EVENT_ON)
if (!i8042_suspended)
return 0;
error = i8042_controller_check();
@ -998,10 +1000,9 @@ static int i8042_resume(struct platform_device *dev)
if (i8042_ports[I8042_KBD_PORT_NO].serio)
i8042_enable_kbd_port();
i8042_suspended = false;
i8042_interrupt(0, NULL);
dev->dev.power.power_state = PMSG_ON;
return 0;
}
#endif /* CONFIG_PM */

View file

@ -41,17 +41,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION("Serio abstraction core");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(serio_interrupt);
EXPORT_SYMBOL(__serio_register_port);
EXPORT_SYMBOL(serio_unregister_port);
EXPORT_SYMBOL(serio_unregister_child_port);
EXPORT_SYMBOL(__serio_register_driver);
EXPORT_SYMBOL(serio_unregister_driver);
EXPORT_SYMBOL(serio_open);
EXPORT_SYMBOL(serio_close);
EXPORT_SYMBOL(serio_rescan);
EXPORT_SYMBOL(serio_reconnect);
/*
* serio_mutex protects entire serio subsystem and is taken every time
* serio port or driver registrered or unregistered.
@ -506,9 +495,9 @@ static ssize_t serio_set_bind_mode(struct device *dev, struct device_attribute *
retval = count;
if (!strncmp(buf, "manual", count)) {
serio->manual_bind = 1;
serio->manual_bind = true;
} else if (!strncmp(buf, "auto", count)) {
serio->manual_bind = 0;
serio->manual_bind = false;
} else {
retval = -EINVAL;
}
@ -581,7 +570,7 @@ static void serio_add_port(struct serio *serio)
"serio: device_add() failed for %s (%s), error: %d\n",
serio->phys, serio->name, error);
else {
serio->registered = 1;
serio->registered = true;
error = sysfs_create_group(&serio->dev.kobj, &serio_id_attr_group);
if (error)
printk(KERN_ERR
@ -617,7 +606,7 @@ static void serio_destroy_port(struct serio *serio)
if (serio->registered) {
sysfs_remove_group(&serio->dev.kobj, &serio_id_attr_group);
device_del(&serio->dev);
serio->registered = 0;
serio->registered = false;
}
list_del_init(&serio->node);
@ -692,11 +681,13 @@ void serio_rescan(struct serio *serio)
{
serio_queue_event(serio, NULL, SERIO_RESCAN_PORT);
}
EXPORT_SYMBOL(serio_rescan);
void serio_reconnect(struct serio *serio)
{
serio_queue_event(serio, NULL, SERIO_RECONNECT_CHAIN);
}
EXPORT_SYMBOL(serio_reconnect);
/*
* Submits register request to kseriod for subsequent execution.
@ -707,6 +698,7 @@ void __serio_register_port(struct serio *serio, struct module *owner)
serio_init_port(serio);
serio_queue_event(serio, owner, SERIO_REGISTER_PORT);
}
EXPORT_SYMBOL(__serio_register_port);
/*
* Synchronously unregisters serio port.
@ -718,6 +710,7 @@ void serio_unregister_port(struct serio *serio)
serio_destroy_port(serio);
mutex_unlock(&serio_mutex);
}
EXPORT_SYMBOL(serio_unregister_port);
/*
* Safely unregisters child port if one is present.
@ -731,6 +724,7 @@ void serio_unregister_child_port(struct serio *serio)
}
mutex_unlock(&serio_mutex);
}
EXPORT_SYMBOL(serio_unregister_child_port);
/*
@ -756,9 +750,9 @@ static ssize_t serio_driver_set_bind_mode(struct device_driver *drv, const char
retval = count;
if (!strncmp(buf, "manual", count)) {
serio_drv->manual_bind = 1;
serio_drv->manual_bind = true;
} else if (!strncmp(buf, "auto", count)) {
serio_drv->manual_bind = 0;
serio_drv->manual_bind = false;
} else {
retval = -EINVAL;
}
@ -818,7 +812,7 @@ static void serio_attach_driver(struct serio_driver *drv)
int __serio_register_driver(struct serio_driver *drv, struct module *owner, const char *mod_name)
{
int manual_bind = drv->manual_bind;
bool manual_bind = drv->manual_bind;
int error;
drv->driver.bus = &serio_bus;
@ -829,7 +823,7 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons
* Temporarily disable automatic binding because probing
* takes long time and we are better off doing it in kseriod
*/
drv->manual_bind = 1;
drv->manual_bind = true;
error = driver_register(&drv->driver);
if (error) {
@ -844,7 +838,7 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons
* driver to free ports
*/
if (!manual_bind) {
drv->manual_bind = 0;
drv->manual_bind = false;
error = serio_queue_event(drv, NULL, SERIO_ATTACH_DRIVER);
if (error) {
driver_unregister(&drv->driver);
@ -854,6 +848,7 @@ int __serio_register_driver(struct serio_driver *drv, struct module *owner, cons
return 0;
}
EXPORT_SYMBOL(__serio_register_driver);
void serio_unregister_driver(struct serio_driver *drv)
{
@ -861,7 +856,7 @@ void serio_unregister_driver(struct serio_driver *drv)
mutex_lock(&serio_mutex);
drv->manual_bind = 1; /* so serio_find_driver ignores it */
drv->manual_bind = true; /* so serio_find_driver ignores it */
serio_remove_pending_events(drv);
start_over:
@ -877,6 +872,7 @@ start_over:
driver_unregister(&drv->driver);
mutex_unlock(&serio_mutex);
}
EXPORT_SYMBOL(serio_unregister_driver);
static void serio_set_drv(struct serio *serio, struct serio_driver *drv)
{
@ -937,11 +933,11 @@ static int serio_uevent(struct device *dev, struct kobj_uevent_env *env)
#ifdef CONFIG_PM
static int serio_suspend(struct device *dev, pm_message_t state)
{
if (dev->power.power_state.event != state.event) {
if (state.event == PM_EVENT_SUSPEND)
serio_cleanup(to_serio_port(dev));
struct serio *serio = to_serio_port(dev);
dev->power.power_state = state;
if (!serio->suspended && state.event == PM_EVENT_SUSPEND) {
serio_cleanup(serio);
serio->suspended = true;
}
return 0;
@ -949,14 +945,15 @@ static int serio_suspend(struct device *dev, pm_message_t state)
static int serio_resume(struct device *dev)
{
struct serio *serio = to_serio_port(dev);
/*
* Driver reconnect can take a while, so better let kseriod
* deal with it.
*/
if (dev->power.power_state.event != PM_EVENT_ON) {
dev->power.power_state = PMSG_ON;
serio_queue_event(to_serio_port(dev), NULL,
SERIO_RECONNECT_PORT);
if (serio->suspended) {
serio->suspended = false;
serio_queue_event(serio, NULL, SERIO_RECONNECT_PORT);
}
return 0;
@ -974,6 +971,7 @@ int serio_open(struct serio *serio, struct serio_driver *drv)
}
return 0;
}
EXPORT_SYMBOL(serio_open);
/* called from serio_driver->connect/disconnect methods under serio_mutex */
void serio_close(struct serio *serio)
@ -983,6 +981,7 @@ void serio_close(struct serio *serio)
serio_set_drv(serio, NULL);
}
EXPORT_SYMBOL(serio_close);
irqreturn_t serio_interrupt(struct serio *serio,
unsigned char data, unsigned int dfl)
@ -1003,6 +1002,7 @@ irqreturn_t serio_interrupt(struct serio *serio,
return ret;
}
EXPORT_SYMBOL(serio_interrupt);
static struct bus_type serio_bus = {
.name = "serio",

View file

@ -1050,4 +1050,5 @@ static void __exit gtco_exit(void)
module_init(gtco_init);
module_exit(gtco_exit);
MODULE_DESCRIPTION("GTCO digitizer USB driver");
MODULE_LICENSE("GPL");

View file

@ -68,6 +68,7 @@
* v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX
* v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A)
* v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28
* v1.51 (pc) - Added support for Intuos4
*/
/*
@ -88,7 +89,7 @@
/*
* Version Information
*/
#define DRIVER_VERSION "v1.50"
#define DRIVER_VERSION "v1.51"
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
#define DRIVER_LICENSE "GPL"
@ -128,6 +129,8 @@ extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac
extern void input_dev_i3s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
extern void input_dev_mo(struct input_dev *input_dev, struct wacom_wac *wacom_wac);

View file

@ -229,6 +229,19 @@ void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
input_set_abs_params(input_dev, ABS_RY, 0, 4096, 0, 0);
}
void input_dev_i4s(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_DIGI)] |= BIT_MASK(BTN_TOOL_FINGER);
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_0) | BIT_MASK(BTN_1) | BIT_MASK(BTN_2) | BIT_MASK(BTN_3);
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_4) | BIT_MASK(BTN_5) | BIT_MASK(BTN_6);
input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0);
}
void input_dev_i4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_7) | BIT_MASK(BTN_8);
}
void input_dev_bee(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
{
input_dev->keybit[BIT_WORD(BTN_MISC)] |= BIT_MASK(BTN_8) | BIT_MASK(BTN_9);

View file

@ -283,9 +283,10 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
int idx;
int idx = 0;
/* tool number */
if (wacom->features->type == INTUOS)
idx = data[1] & 0x01;
/* Enter report */
@ -299,6 +300,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
switch (wacom->id[idx]) {
case 0x812: /* Inking pen */
case 0x801: /* Intuos3 Inking pen */
case 0x20802: /* Intuos4 Classic Pen */
case 0x012:
wacom->tool[idx] = BTN_TOOL_PENCIL;
break;
@ -308,6 +310,9 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
case 0x823: /* Intuos3 Grip Pen */
case 0x813: /* Intuos3 Classic Pen */
case 0x885: /* Intuos3 Marker Pen */
case 0x802: /* Intuos4 Grip Pen Eraser */
case 0x804: /* Intuos4 Marker Pen */
case 0x40802: /* Intuos4 Classic Pen */
case 0x022:
wacom->tool[idx] = BTN_TOOL_PEN;
break;
@ -319,10 +324,12 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
case 0x09c:
case 0x094:
case 0x017: /* Intuos3 2D Mouse */
case 0x806: /* Intuos4 Mouse */
wacom->tool[idx] = BTN_TOOL_MOUSE;
break;
case 0x096: /* Lens cursor */
case 0x097: /* Intuos3 Lens cursor */
case 0x006: /* Intuos4 Lens cursor */
wacom->tool[idx] = BTN_TOOL_LENS;
break;
case 0x82a: /* Eraser */
@ -333,12 +340,17 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
case 0x82b: /* Intuos3 Grip Pen Eraser */
case 0x81b: /* Intuos3 Classic Pen Eraser */
case 0x91b: /* Intuos3 Airbrush Eraser */
case 0x80c: /* Intuos4 Marker Pen Eraser */
case 0x80a: /* Intuos4 Grip Pen Eraser */
case 0x4080a: /* Intuos4 Classic Pen Eraser */
case 0x90a: /* Intuos4 Airbrush Eraser */
wacom->tool[idx] = BTN_TOOL_RUBBER;
break;
case 0xd12:
case 0x912:
case 0x112:
case 0x913: /* Intuos3 Airbrush */
case 0x902: /* Intuos4 Airbrush */
wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
break;
default: /* Unknown tool */
@ -349,9 +361,15 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
/* Exit report */
if ((data[1] & 0xfe) == 0x80) {
/*
* Reset all states otherwise we lose the initial states
* when in-prox next time
*/
wacom_report_abs(wcombo, ABS_X, 0);
wacom_report_abs(wcombo, ABS_Y, 0);
wacom_report_abs(wcombo, ABS_DISTANCE, 0);
wacom_report_abs(wcombo, ABS_TILT_X, 0);
wacom_report_abs(wcombo, ABS_TILT_Y, 0);
if (wacom->tool[idx] >= BTN_TOOL_MOUSE) {
wacom_report_key(wcombo, BTN_LEFT, 0);
wacom_report_key(wcombo, BTN_MIDDLE, 0);
@ -362,8 +380,6 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
wacom_report_abs(wcombo, ABS_RZ, 0);
} else {
wacom_report_abs(wcombo, ABS_PRESSURE, 0);
wacom_report_abs(wcombo, ABS_TILT_X, 0);
wacom_report_abs(wcombo, ABS_TILT_Y, 0);
wacom_report_key(wcombo, BTN_STYLUS, 0);
wacom_report_key(wcombo, BTN_STYLUS2, 0);
wacom_report_key(wcombo, BTN_TOUCH, 0);
@ -372,6 +388,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
wacom_report_key(wcombo, wacom->tool[idx], 0);
wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
wacom->id[idx] = 0;
return 2;
}
return 0;
@ -385,6 +402,8 @@ static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
/* general pen packet */
if ((data[1] & 0xb8) == 0xa0) {
t = (data[6] << 2) | ((data[7] >> 6) & 3);
if (wacom->features->type >= INTUOS4S && wacom->features->type <= INTUOS4L)
t = (t << 1) | (data[1] & 1);
wacom_report_abs(wcombo, ABS_PRESSURE, t);
wacom_report_abs(wcombo, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
@ -409,7 +428,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
{
unsigned char *data = wacom->data;
unsigned int t;
int idx, result;
int idx = 0, result;
if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
@ -417,6 +436,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
}
/* tool number */
if (wacom->features->type == INTUOS)
idx = data[1] & 0x01;
/* pad packets. Works as a second tool and is always in prox */
@ -425,6 +445,32 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
if (wacom->tool[1] != BTN_TOOL_FINGER)
wacom->tool[1] = BTN_TOOL_FINGER;
if (wacom->features->type >= INTUOS4S && wacom->features->type <= INTUOS4L) {
wacom_report_key(wcombo, BTN_0, (data[2] & 0x01));
wacom_report_key(wcombo, BTN_1, (data[3] & 0x01));
wacom_report_key(wcombo, BTN_2, (data[3] & 0x02));
wacom_report_key(wcombo, BTN_3, (data[3] & 0x04));
wacom_report_key(wcombo, BTN_4, (data[3] & 0x08));
wacom_report_key(wcombo, BTN_5, (data[3] & 0x10));
wacom_report_key(wcombo, BTN_6, (data[3] & 0x20));
if (data[1] & 0x80) {
wacom_report_abs(wcombo, ABS_WHEEL, (data[1] & 0x7f));
} else {
/* Out of proximity, clear wheel value. */
wacom_report_abs(wcombo, ABS_WHEEL, 0);
}
if (wacom->features->type != INTUOS4S) {
wacom_report_key(wcombo, BTN_7, (data[3] & 0x40));
wacom_report_key(wcombo, BTN_8, (data[3] & 0x80));
}
if (data[1] | (data[2] & 0x01) | data[3]) {
wacom_report_key(wcombo, wacom->tool[1], 1);
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
} else {
wacom_report_key(wcombo, wacom->tool[1], 0);
wacom_report_abs(wcombo, ABS_MISC, 0);
}
} else {
wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
@ -439,11 +485,14 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
if ((data[5] & 0x1f) | (data[6] & 0x1f) | (data[1] & 0x1f) |
data[2] | (data[3] & 0x1f) | data[4])
data[2] | (data[3] & 0x1f) | data[4]) {
wacom_report_key(wcombo, wacom->tool[1], 1);
else
wacom_report_key(wcombo, wacom->tool[1], 0);
wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID);
} else {
wacom_report_key(wcombo, wacom->tool[1], 0);
wacom_report_abs(wcombo, ABS_MISC, 0);
}
}
wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
return 1;
}
@ -453,10 +502,16 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
if (result)
return result-1;
/* Only large I3 and I1 & I2 support Lense Cursor */
/* don't proceed if we don't know the ID */
if (!wacom->id[idx])
return 0;
/* Only large Intuos support Lense Cursor */
if ((wacom->tool[idx] == BTN_TOOL_LENS)
&& ((wacom->features->type == INTUOS3)
|| (wacom->features->type == INTUOS3S)))
|| (wacom->features->type == INTUOS3S)
|| (wacom->features->type == INTUOS4)
|| (wacom->features->type == INTUOS4S)))
return 0;
/* Cintiq doesn't send data when RDY bit isn't set */
@ -476,8 +531,8 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
/* process general packets */
wacom_intuos_general(wacom, wcombo);
/* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
/* 4D mouse, 2D mouse, marker pen rotation, tilt mouse, or Lens cursor packets */
if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0 || (data[1] & 0xbc) == 0xac) {
if (data[1] & 0x02) {
/* Rotation packet */
@ -506,6 +561,20 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
} else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
/* I4 mouse */
if (wacom->features->type >= INTUOS4S && wacom->features->type <= INTUOS4L) {
wacom_report_key(wcombo, BTN_LEFT, data[6] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[6] & 0x02);
wacom_report_key(wcombo, BTN_RIGHT, data[6] & 0x04);
wacom_report_rel(wcombo, REL_WHEEL, ((data[7] & 0x80) >> 7)
- ((data[7] & 0x40) >> 6));
wacom_report_key(wcombo, BTN_SIDE, data[6] & 0x08);
wacom_report_key(wcombo, BTN_EXTRA, data[6] & 0x10);
wacom_report_abs(wcombo, ABS_TILT_X,
((data[7] << 1) & 0x7e) | (data[8] >> 7));
wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
} else {
/* 2D mouse packet */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
@ -518,8 +587,10 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
}
} else if (wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L) {
}
} else if ((wacom->features->type < INTUOS3S || wacom->features->type == INTUOS3L ||
wacom->features->type == INTUOS4L) &&
wacom->tool[idx] == BTN_TOOL_LENS) {
/* Lens cursor packets */
wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
@ -581,6 +652,7 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, void *wcombo)
}
} else if (touchOut || !prox) { /* force touch out-prox */
wacom_report_abs(wcombo, ABS_MISC, TOUCH_DEVICE_ID);
wacom_report_key(wcombo, wacom->tool[1], 0);
wacom_report_key(wcombo, BTN_TOUCH, 0);
touchOut = 0;
touchInProx = 1;
@ -669,6 +741,9 @@ int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
case INTUOS3S:
case INTUOS3:
case INTUOS3L:
case INTUOS4S:
case INTUOS4:
case INTUOS4L:
case CINTIQ:
case WACOM_BEE:
return wacom_intuos_irq(wacom_wac, wcombo);
@ -706,6 +781,14 @@ void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_w
case INTUOS:
input_dev_i(input_dev, wacom_wac);
break;
case INTUOS4:
case INTUOS4L:
input_dev_i4(input_dev, wacom_wac);
/* fall through */
case INTUOS4S:
input_dev_i4s(input_dev, wacom_wac);
input_dev_i(input_dev, wacom_wac);
break;
case PL:
case PTU:
case TABLETPC:
@ -766,6 +849,10 @@ static struct wacom_features wacom_features[] = {
{ "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 63, INTUOS3L },
{ "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 },
{ "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S },
{ "Wacom Intuos4 4x6", 10, 31496, 19685, 2047, 63, INTUOS4S },
{ "Wacom Intuos4 6x9", 10, 44704, 27940, 2047, 63, INTUOS4 },
{ "Wacom Intuos4 8x13", 10, 65024, 40640, 2047, 63, INTUOS4L },
{ "Wacom Intuos4 12x19", 10, 97536, 60960, 2047, 63, INTUOS4L },
{ "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ },
{ "Wacom Cintiq 20WSX", 10, 86680, 54180, 1023, 63, WACOM_BEE },
{ "Wacom Cintiq 12WX", 10, 53020, 33440, 1023, 63, WACOM_BEE },
@ -825,6 +912,10 @@ static struct usb_device_id wacom_ids[] = {
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB7) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB8) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB9) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xBA) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xBB) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC5) },
{ USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC6) },

View file

@ -25,6 +25,9 @@ enum {
INTUOS3S,
INTUOS3,
INTUOS3L,
INTUOS4S,
INTUOS4,
INTUOS4L,
CINTIQ,
WACOM_BEE,
WACOM_MO,

View file

@ -111,6 +111,15 @@ config TOUCHSCREEN_DA9034
Say Y here to enable the support for the touchscreen found
on Dialog Semiconductor DA9034 PMIC.
config TOUCHSCREEN_EETI
tristate "EETI touchscreen panel support"
depends on I2C
help
Say Y here to enable support for I2C connected EETI touch panels.
To compile this driver as a module, choose M here: the
module will be called eeti_ts.
config TOUCHSCREEN_FUJITSU
tristate "Fujitsu serial touchscreen"
select SERIO
@ -341,6 +350,21 @@ config TOUCHSCREEN_WM9713
Say Y here to enable support for the Wolfson Microelectronics
WM9713 touchscreen controller.
config TOUCHSCREEN_WM97XX_ATMEL
tristate "WM97xx Atmel accelerated touch"
depends on TOUCHSCREEN_WM97XX && (AVR32 || ARCH_AT91)
help
Say Y here for support for streaming mode with WM97xx touchscreens
on Atmel AT91 or AVR32 systems with an AC97C module.
Be aware that this will use channel B in the controller for
streaming data, this must not conflict with other AC97C drivers.
If unsure, say N.
To compile this driver as a module, choose M here: the module will
be called atmel-wm97xx.
config TOUCHSCREEN_WM97XX_MAINSTONE
tristate "WM97xx Mainstone accelerated touch"
depends on TOUCHSCREEN_WM97XX && ARCH_PXA
@ -466,4 +490,12 @@ config TOUCHSCREEN_TSC2007
To compile this driver as a module, choose M here: the
module will be called tsc2007.
config TOUCHSCREEN_W90X900
tristate "W90P910 touchscreen driver"
help
Say Y here if you have a W90P910 based touchscreen.
To compile this driver as a module, choose M here: the
module will be called w90p910_ts.
endif

View file

@ -13,6 +13,7 @@ obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o
obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
@ -35,5 +36,7 @@ obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o

View file

@ -83,6 +83,7 @@ struct ads7846_packet {
struct ads7846 {
struct input_dev *input;
char phys[32];
char name[32];
struct spi_device *spi;
@ -97,6 +98,8 @@ struct ads7846 {
u16 x_plate_ohms;
u16 pressure_max;
bool swap_xy;
struct ads7846_packet *packet;
struct spi_transfer xfer[18];
@ -599,6 +602,10 @@ static void ads7846_rx(void *ads)
dev_dbg(&ts->spi->dev, "DOWN\n");
#endif
}
if (ts->swap_xy)
swap(x, y);
input_report_abs(input, ABS_X, x);
input_report_abs(input, ABS_Y, y);
input_report_abs(input, ABS_PRESSURE, Rt);
@ -917,6 +924,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
ts->spi = spi;
ts->input = input_dev;
ts->vref_mv = pdata->vref_mv;
ts->swap_xy = pdata->swap_xy;
hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
ts->timer.function = ads7846_timer;
@ -958,8 +966,9 @@ static int __devinit ads7846_probe(struct spi_device *spi)
ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
input_dev->name = "ADS784x Touchscreen";
input_dev->name = ts->name;
input_dev->phys = ts->phys;
input_dev->dev.parent = &spi->dev;
@ -1141,10 +1150,16 @@ static int __devinit ads7846_probe(struct spi_device *spi)
if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING,
spi->dev.driver->name, ts)) {
dev_info(&spi->dev,
"trying pin change workaround on irq %d\n", spi->irq);
err = request_irq(spi->irq, ads7846_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
spi->dev.driver->name, ts);
if (err) {
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
err = -EBUSY;
goto err_free_gpio;
}
}
err = ads784x_hwmon_register(spi, ts);
if (err)

View file

@ -0,0 +1,446 @@
/*
* Atmel AT91 and AVR32 continuous touch screen driver for Wolfson WM97xx AC97
* codecs.
*
* Copyright (C) 2008 - 2009 Atmel Corporation
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/wm97xx.h>
#include <linux/timer.h>
#include <linux/gpio.h>
#include <linux/io.h>
#define AC97C_ICA 0x10
#define AC97C_CBRHR 0x30
#define AC97C_CBSR 0x38
#define AC97C_CBMR 0x3c
#define AC97C_IER 0x54
#define AC97C_IDR 0x58
#define AC97C_RXRDY (1 << 4)
#define AC97C_OVRUN (1 << 5)
#define AC97C_CMR_SIZE_20 (0 << 16)
#define AC97C_CMR_SIZE_18 (1 << 16)
#define AC97C_CMR_SIZE_16 (2 << 16)
#define AC97C_CMR_SIZE_10 (3 << 16)
#define AC97C_CMR_CEM_LITTLE (1 << 18)
#define AC97C_CMR_CEM_BIG (0 << 18)
#define AC97C_CMR_CENA (1 << 21)
#define AC97C_INT_CBEVT (1 << 4)
#define AC97C_SR_CAEVT (1 << 3)
#define AC97C_CH_MASK(slot) \
(0x7 << (3 * (slot - 3)))
#define AC97C_CH_ASSIGN(slot, channel) \
(AC97C_CHANNEL_##channel << (3 * (slot - 3)))
#define AC97C_CHANNEL_NONE 0x0
#define AC97C_CHANNEL_B 0x2
#define ac97c_writel(chip, reg, val) \
__raw_writel((val), (chip)->regs + AC97C_##reg)
#define ac97c_readl(chip, reg) \
__raw_readl((chip)->regs + AC97C_##reg)
#ifdef CONFIG_CPU_AT32AP700X
#define ATMEL_WM97XX_AC97C_IOMEM (0xfff02800)
#define ATMEL_WM97XX_AC97C_IRQ (29)
#define ATMEL_WM97XX_GPIO_DEFAULT (32+16) /* Pin 16 on port B. */
#else
#error Unkown CPU, this driver only supports AT32AP700X CPUs.
#endif
struct continuous {
u16 id; /* codec id */
u8 code; /* continuous code */
u8 reads; /* number of coord reads per read cycle */
u32 speed; /* number of coords per second */
};
#define WM_READS(sp) ((sp / HZ) + 1)
static const struct continuous cinfo[] = {
{WM9705_ID2, 0, WM_READS(94), 94},
{WM9705_ID2, 1, WM_READS(188), 188},
{WM9705_ID2, 2, WM_READS(375), 375},
{WM9705_ID2, 3, WM_READS(750), 750},
{WM9712_ID2, 0, WM_READS(94), 94},
{WM9712_ID2, 1, WM_READS(188), 188},
{WM9712_ID2, 2, WM_READS(375), 375},
{WM9712_ID2, 3, WM_READS(750), 750},
{WM9713_ID2, 0, WM_READS(94), 94},
{WM9713_ID2, 1, WM_READS(120), 120},
{WM9713_ID2, 2, WM_READS(154), 154},
{WM9713_ID2, 3, WM_READS(188), 188},
};
/* Continuous speed index. */
static int sp_idx;
/*
* Pen sampling frequency (Hz) in continuous mode.
*/
static int cont_rate = 188;
module_param(cont_rate, int, 0);
MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
/*
* Pen down detection.
*
* This driver can either poll or use an interrupt to indicate a pen down
* event. If the irq request fails then it will fall back to polling mode.
*/
static int pen_int = 1;
module_param(pen_int, int, 0);
MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)");
/*
* Pressure readback.
*
* Set to 1 to read back pen down pressure.
*/
static int pressure;
module_param(pressure, int, 0);
MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)");
/*
* AC97 touch data slot.
*
* Touch screen readback data ac97 slot.
*/
static int ac97_touch_slot = 5;
module_param(ac97_touch_slot, int, 0);
MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number");
/*
* GPIO line number.
*
* Set to GPIO number where the signal from the WM97xx device is hooked up.
*/
static int atmel_gpio_line = ATMEL_WM97XX_GPIO_DEFAULT;
module_param(atmel_gpio_line, int, 0);
MODULE_PARM_DESC(atmel_gpio_line, "GPIO line number connected to WM97xx");
struct atmel_wm97xx {
struct wm97xx *wm;
struct timer_list pen_timer;
void __iomem *regs;
unsigned long ac97c_irq;
unsigned long gpio_pen;
unsigned long gpio_irq;
unsigned short x;
unsigned short y;
};
static irqreturn_t atmel_wm97xx_channel_b_interrupt(int irq, void *dev_id)
{
struct atmel_wm97xx *atmel_wm97xx = dev_id;
struct wm97xx *wm = atmel_wm97xx->wm;
int status = ac97c_readl(atmel_wm97xx, CBSR);
irqreturn_t retval = IRQ_NONE;
if (status & AC97C_OVRUN) {
dev_dbg(&wm->touch_dev->dev, "AC97C overrun\n");
ac97c_readl(atmel_wm97xx, CBRHR);
retval = IRQ_HANDLED;
} else if (status & AC97C_RXRDY) {
u16 data;
u16 value;
u16 source;
u16 pen_down;
data = ac97c_readl(atmel_wm97xx, CBRHR);
value = data & 0x0fff;
source = data & WM97XX_ADCSRC_MASK;
pen_down = (data & WM97XX_PEN_DOWN) >> 8;
if (source == WM97XX_ADCSEL_X)
atmel_wm97xx->x = value;
if (source == WM97XX_ADCSEL_Y)
atmel_wm97xx->y = value;
if (!pressure && source == WM97XX_ADCSEL_Y) {
input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
input_report_key(wm->input_dev, BTN_TOUCH, pen_down);
input_sync(wm->input_dev);
} else if (pressure && source == WM97XX_ADCSEL_PRES) {
input_report_abs(wm->input_dev, ABS_X, atmel_wm97xx->x);
input_report_abs(wm->input_dev, ABS_Y, atmel_wm97xx->y);
input_report_abs(wm->input_dev, ABS_PRESSURE, value);
input_report_key(wm->input_dev, BTN_TOUCH, value);
input_sync(wm->input_dev);
}
retval = IRQ_HANDLED;
}
return retval;
}
static void atmel_wm97xx_acc_pen_up(struct wm97xx *wm)
{
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
struct input_dev *input_dev = wm->input_dev;
int pen_down = gpio_get_value(atmel_wm97xx->gpio_pen);
if (pen_down != 0) {
mod_timer(&atmel_wm97xx->pen_timer,
jiffies + msecs_to_jiffies(1));
} else {
if (pressure)
input_report_abs(input_dev, ABS_PRESSURE, 0);
input_report_key(input_dev, BTN_TOUCH, 0);
input_sync(input_dev);
}
}
static void atmel_wm97xx_pen_timer(unsigned long data)
{
atmel_wm97xx_acc_pen_up((struct wm97xx *)data);
}
static int atmel_wm97xx_acc_startup(struct wm97xx *wm)
{
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(wm->touch_dev);
int idx = 0;
if (wm->ac97 == NULL)
return -ENODEV;
for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) {
if (wm->id != cinfo[idx].id)
continue;
sp_idx = idx;
if (cont_rate <= cinfo[idx].speed)
break;
}
wm->acc_rate = cinfo[sp_idx].code;
wm->acc_slot = ac97_touch_slot;
dev_info(&wm->touch_dev->dev, "atmel accelerated touchscreen driver, "
"%d samples/sec\n", cinfo[sp_idx].speed);
if (pen_int) {
unsigned long reg;
wm->pen_irq = atmel_wm97xx->gpio_irq;
switch (wm->id) {
case WM9712_ID2: /* Fall through. */
case WM9713_ID2:
/*
* Use GPIO 13 (PEN_DOWN) to assert GPIO line 3
* (PENDOWN).
*/
wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN,
WM97XX_GPIO_POL_HIGH,
WM97XX_GPIO_STICKY,
WM97XX_GPIO_WAKE);
wm97xx_config_gpio(wm, WM97XX_GPIO_3, WM97XX_GPIO_OUT,
WM97XX_GPIO_POL_HIGH,
WM97XX_GPIO_NOTSTICKY,
WM97XX_GPIO_NOWAKE);
case WM9705_ID2: /* Fall through. */
/*
* Enable touch data slot in AC97 controller channel B.
*/
reg = ac97c_readl(atmel_wm97xx, ICA);
reg &= ~AC97C_CH_MASK(wm->acc_slot);
reg |= AC97C_CH_ASSIGN(wm->acc_slot, B);
ac97c_writel(atmel_wm97xx, ICA, reg);
/*
* Enable channel and interrupt for RXRDY and OVERRUN.
*/
ac97c_writel(atmel_wm97xx, CBMR, AC97C_CMR_CENA
| AC97C_CMR_CEM_BIG
| AC97C_CMR_SIZE_16
| AC97C_OVRUN
| AC97C_RXRDY);
/* Dummy read to empty RXRHR. */
ac97c_readl(atmel_wm97xx, CBRHR);
/*
* Enable interrupt for channel B in the AC97
* controller.
*/
ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
break;
default:
dev_err(&wm->touch_dev->dev, "pen down irq not "
"supported on this device\n");
pen_int = 0;
break;
}
}
return 0;
}
static void atmel_wm97xx_acc_shutdown(struct wm97xx *wm)
{
if (pen_int) {
struct atmel_wm97xx *atmel_wm97xx =
platform_get_drvdata(wm->touch_dev);
unsigned long ica;
switch (wm->id & 0xffff) {
case WM9705_ID2: /* Fall through. */
case WM9712_ID2: /* Fall through. */
case WM9713_ID2:
/* Disable slot and turn off channel B interrupts. */
ica = ac97c_readl(atmel_wm97xx, ICA);
ica &= ~AC97C_CH_MASK(wm->acc_slot);
ac97c_writel(atmel_wm97xx, ICA, ica);
ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
ac97c_writel(atmel_wm97xx, CBMR, 0);
wm->pen_irq = 0;
break;
default:
dev_err(&wm->touch_dev->dev, "unknown codec\n");
break;
}
}
}
static void atmel_wm97xx_irq_enable(struct wm97xx *wm, int enable)
{
/* Intentionally left empty. */
}
static struct wm97xx_mach_ops atmel_mach_ops = {
.acc_enabled = 1,
.acc_pen_up = atmel_wm97xx_acc_pen_up,
.acc_startup = atmel_wm97xx_acc_startup,
.acc_shutdown = atmel_wm97xx_acc_shutdown,
.irq_enable = atmel_wm97xx_irq_enable,
.irq_gpio = WM97XX_GPIO_3,
};
static int __init atmel_wm97xx_probe(struct platform_device *pdev)
{
struct wm97xx *wm = platform_get_drvdata(pdev);
struct atmel_wm97xx *atmel_wm97xx;
int ret;
atmel_wm97xx = kzalloc(sizeof(struct atmel_wm97xx), GFP_KERNEL);
if (!atmel_wm97xx) {
dev_dbg(&pdev->dev, "out of memory\n");
return -ENOMEM;
}
atmel_wm97xx->wm = wm;
atmel_wm97xx->regs = (void *)ATMEL_WM97XX_AC97C_IOMEM;
atmel_wm97xx->ac97c_irq = ATMEL_WM97XX_AC97C_IRQ;
atmel_wm97xx->gpio_pen = atmel_gpio_line;
atmel_wm97xx->gpio_irq = gpio_to_irq(atmel_wm97xx->gpio_pen);
setup_timer(&atmel_wm97xx->pen_timer, atmel_wm97xx_pen_timer,
(unsigned long)wm);
ret = request_irq(atmel_wm97xx->ac97c_irq,
atmel_wm97xx_channel_b_interrupt,
IRQF_SHARED, "atmel-wm97xx-ch-b", atmel_wm97xx);
if (ret) {
dev_dbg(&pdev->dev, "could not request ac97c irq\n");
goto err;
}
platform_set_drvdata(pdev, atmel_wm97xx);
ret = wm97xx_register_mach_ops(wm, &atmel_mach_ops);
if (ret)
goto err_irq;
return ret;
err_irq:
free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
err:
platform_set_drvdata(pdev, NULL);
kfree(atmel_wm97xx);
return ret;
}
static int __exit atmel_wm97xx_remove(struct platform_device *pdev)
{
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
struct wm97xx *wm = atmel_wm97xx->wm;
ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
free_irq(atmel_wm97xx->ac97c_irq, atmel_wm97xx);
del_timer_sync(&atmel_wm97xx->pen_timer);
wm97xx_unregister_mach_ops(wm);
platform_set_drvdata(pdev, NULL);
kfree(atmel_wm97xx);
return 0;
}
#ifdef CONFIG_PM
static int atmel_wm97xx_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
ac97c_writel(atmel_wm97xx, IDR, AC97C_INT_CBEVT);
disable_irq(atmel_wm97xx->gpio_irq);
del_timer_sync(&atmel_wm97xx->pen_timer);
return 0;
}
static int atmel_wm97xx_resume(struct platform_device *pdev)
{
struct atmel_wm97xx *atmel_wm97xx = platform_get_drvdata(pdev);
struct wm97xx *wm = atmel_wm97xx->wm;
if (wm->input_dev->users) {
enable_irq(atmel_wm97xx->gpio_irq);
ac97c_writel(atmel_wm97xx, IER, AC97C_INT_CBEVT);
}
return 0;
}
#else
#define atmel_wm97xx_suspend NULL
#define atmel_wm97xx_resume NULL
#endif
static struct platform_driver atmel_wm97xx_driver = {
.remove = __exit_p(atmel_wm97xx_remove),
.driver = {
.name = "wm97xx-touch",
},
.suspend = atmel_wm97xx_suspend,
.resume = atmel_wm97xx_resume,
};
static int __init atmel_wm97xx_init(void)
{
return platform_driver_probe(&atmel_wm97xx_driver, atmel_wm97xx_probe);
}
module_init(atmel_wm97xx_init);
static void __exit atmel_wm97xx_exit(void)
{
platform_driver_unregister(&atmel_wm97xx_driver);
}
module_exit(atmel_wm97xx_exit);
MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
MODULE_DESCRIPTION("wm97xx continuous touch driver for Atmel AT91 and AVR32");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,286 @@
/*
* Touch Screen driver for EETI's I2C connected touch screen panels
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* See EETI's software guide for the protocol specification:
* http://home.eeti.com.tw/web20/eg/guide.htm
*
* Based on migor_ts.c
* Copyright (c) 2008 Magnus Damm
* Copyright (c) 2007 Ujjwal Pande <ujjwal@kenati.com>
*
* This file 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 file 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/timer.h>
#include <linux/gpio.h>
static int flip_x;
module_param(flip_x, bool, 0644);
MODULE_PARM_DESC(flip_x, "flip x coordinate");
static int flip_y;
module_param(flip_y, bool, 0644);
MODULE_PARM_DESC(flip_y, "flip y coordinate");
struct eeti_ts_priv {
struct i2c_client *client;
struct input_dev *input;
struct work_struct work;
struct mutex mutex;
int irq;
};
#define EETI_TS_BITDEPTH (11)
#define EETI_MAXVAL ((1 << (EETI_TS_BITDEPTH + 1)) - 1)
#define REPORT_BIT_PRESSED (1 << 0)
#define REPORT_BIT_AD0 (1 << 1)
#define REPORT_BIT_AD1 (1 << 2)
#define REPORT_BIT_HAS_PRESSURE (1 << 6)
#define REPORT_RES_BITS(v) (((v) >> 1) + EETI_TS_BITDEPTH)
static void eeti_ts_read(struct work_struct *work)
{
char buf[6];
unsigned int x, y, res, pressed, to = 100;
struct eeti_ts_priv *priv =
container_of(work, struct eeti_ts_priv, work);
mutex_lock(&priv->mutex);
while (!gpio_get_value(irq_to_gpio(priv->irq)) && --to)
i2c_master_recv(priv->client, buf, sizeof(buf));
if (!to) {
dev_err(&priv->client->dev,
"unable to clear IRQ - line stuck?\n");
goto out;
}
/* drop non-report packets */
if (!(buf[0] & 0x80))
goto out;
pressed = buf[0] & REPORT_BIT_PRESSED;
res = REPORT_RES_BITS(buf[0] & (REPORT_BIT_AD0 | REPORT_BIT_AD1));
x = buf[2] | (buf[1] << 8);
y = buf[4] | (buf[3] << 8);
/* fix the range to 11 bits */
x >>= res - EETI_TS_BITDEPTH;
y >>= res - EETI_TS_BITDEPTH;
if (flip_x)
x = EETI_MAXVAL - x;
if (flip_y)
y = EETI_MAXVAL - y;
if (buf[0] & REPORT_BIT_HAS_PRESSURE)
input_report_abs(priv->input, ABS_PRESSURE, buf[5]);
input_report_abs(priv->input, ABS_X, x);
input_report_abs(priv->input, ABS_Y, y);
input_report_key(priv->input, BTN_TOUCH, !!pressed);
input_sync(priv->input);
out:
mutex_unlock(&priv->mutex);
}
static irqreturn_t eeti_ts_isr(int irq, void *dev_id)
{
struct eeti_ts_priv *priv = dev_id;
/* postpone I2C transactions as we are atomic */
schedule_work(&priv->work);
return IRQ_HANDLED;
}
static int eeti_ts_open(struct input_dev *dev)
{
struct eeti_ts_priv *priv = input_get_drvdata(dev);
enable_irq(priv->irq);
/* Read the events once to arm the IRQ */
eeti_ts_read(&priv->work);
return 0;
}
static void eeti_ts_close(struct input_dev *dev)
{
struct eeti_ts_priv *priv = input_get_drvdata(dev);
disable_irq(priv->irq);
cancel_work_sync(&priv->work);
}
static int __devinit eeti_ts_probe(struct i2c_client *client,
const struct i2c_device_id *idp)
{
struct eeti_ts_priv *priv;
struct input_dev *input;
int err = -ENOMEM;
/* In contrast to what's described in the datasheet, there seems
* to be no way of probing the presence of that device using I2C
* commands. So we need to blindly believe it is there, and wait
* for interrupts to occur. */
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&client->dev, "failed to allocate driver data\n");
goto err0;
}
mutex_init(&priv->mutex);
input = input_allocate_device();
if (!input) {
dev_err(&client->dev, "Failed to allocate input device.\n");
goto err1;
}
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input, ABS_X, 0, EETI_MAXVAL, 0, 0);
input_set_abs_params(input, ABS_Y, 0, EETI_MAXVAL, 0, 0);
input_set_abs_params(input, ABS_PRESSURE, 0, 0xff, 0, 0);
input->name = client->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
input->open = eeti_ts_open;
input->close = eeti_ts_close;
priv->client = client;
priv->input = input;
priv->irq = client->irq;
INIT_WORK(&priv->work, eeti_ts_read);
i2c_set_clientdata(client, priv);
input_set_drvdata(input, priv);
err = input_register_device(input);
if (err)
goto err1;
err = request_irq(priv->irq, eeti_ts_isr, IRQF_TRIGGER_FALLING,
client->name, priv);
if (err) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
goto err2;
}
/* Disable the irq for now. It will be enabled once the input device
* is opened. */
disable_irq(priv->irq);
device_init_wakeup(&client->dev, 0);
return 0;
err2:
input_unregister_device(input);
input = NULL; /* so we dont try to free it below */
err1:
input_free_device(input);
i2c_set_clientdata(client, NULL);
kfree(priv);
err0:
return err;
}
static int __devexit eeti_ts_remove(struct i2c_client *client)
{
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
free_irq(priv->irq, priv);
input_unregister_device(priv->input);
i2c_set_clientdata(client, NULL);
kfree(priv);
return 0;
}
#ifdef CONFIG_PM
static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)
{
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
enable_irq_wake(priv->irq);
return 0;
}
static int eeti_ts_resume(struct i2c_client *client)
{
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
if (device_may_wakeup(&client->dev))
disable_irq_wake(priv->irq);
return 0;
}
#else
#define eeti_ts_suspend NULL
#define eeti_ts_resume NULL
#endif
static const struct i2c_device_id eeti_ts_id[] = {
{ "eeti_ts", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, eeti_ts_id);
static struct i2c_driver eeti_ts_driver = {
.driver = {
.name = "eeti_ts",
},
.probe = eeti_ts_probe,
.remove = __devexit_p(eeti_ts_remove),
.suspend = eeti_ts_suspend,
.resume = eeti_ts_resume,
.id_table = eeti_ts_id,
};
static int __init eeti_ts_init(void)
{
return i2c_add_driver(&eeti_ts_driver);
}
static void __exit eeti_ts_exit(void)
{
i2c_del_driver(&eeti_ts_driver);
}
MODULE_DESCRIPTION("EETI Touchscreen driver");
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_LICENSE("GPL");
module_init(eeti_ts_init);
module_exit(eeti_ts_exit);

View file

@ -257,7 +257,7 @@ static int tsc2007_probe(struct i2c_client *client,
struct input_dev *input_dev;
int err;
if (!pdata) {
if (!pdata || !pdata->get_pendown_state) {
dev_err(&client->dev, "platform data is required!\n");
return -EINVAL;
}

View file

@ -0,0 +1,350 @@
/*
* Copyright (c) 2008 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation;version 2 of the License.
*
*/
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/input.h>
#include <linux/interrupt.h>
/* ADC controller bit defines */
#define ADC_DELAY 0xf00
#define ADC_DOWN 0x01
#define ADC_TSC_Y (0x01 << 8)
#define ADC_TSC_X (0x00 << 8)
#define TSC_FOURWIRE (~(0x03 << 1))
#define ADC_CLK_EN (0x01 << 28) /* ADC clock enable */
#define ADC_READ_CON (0x01 << 12)
#define ADC_CONV (0x01 << 13)
#define ADC_SEMIAUTO (0x01 << 14)
#define ADC_WAITTRIG (0x03 << 14)
#define ADC_RST1 (0x01 << 16)
#define ADC_RST0 (0x00 << 16)
#define ADC_EN (0x01 << 17)
#define ADC_INT (0x01 << 18)
#define WT_INT (0x01 << 20)
#define ADC_INT_EN (0x01 << 21)
#define LVD_INT_EN (0x01 << 22)
#define WT_INT_EN (0x01 << 23)
#define ADC_DIV (0x04 << 1) /* div = 6 */
enum ts_state {
TS_WAIT_NEW_PACKET, /* We are waiting next touch report */
TS_WAIT_X_COORD, /* We are waiting for ADC to report X coord */
TS_WAIT_Y_COORD, /* We are waiting for ADC to report Y coord */
TS_IDLE, /* Input device is closed, don't do anything */
};
struct w90p910_ts {
struct input_dev *input;
struct timer_list timer;
int irq_num;
void __iomem *clocken;
void __iomem *ts_reg;
spinlock_t lock;
enum ts_state state;
};
static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down)
{
struct input_dev *dev = w90p910_ts->input;
if (down) {
input_report_abs(dev, ABS_X,
__raw_readl(w90p910_ts->ts_reg + 0x0c));
input_report_abs(dev, ABS_Y,
__raw_readl(w90p910_ts->ts_reg + 0x10));
}
input_report_key(dev, BTN_TOUCH, down);
input_sync(dev);
}
static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts)
{
unsigned long ctlreg;
__raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04);
ctlreg = __raw_readl(w90p910_ts->ts_reg);
ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN);
ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
__raw_writel(ctlreg, w90p910_ts->ts_reg);
w90p910_ts->state = TS_WAIT_X_COORD;
}
static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts)
{
unsigned long ctlreg;
__raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04);
ctlreg = __raw_readl(w90p910_ts->ts_reg);
ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN);
ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
__raw_writel(ctlreg, w90p910_ts->ts_reg);
w90p910_ts->state = TS_WAIT_Y_COORD;
}
static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts)
{
unsigned long ctlreg;
ctlreg = __raw_readl(w90p910_ts->ts_reg);
ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV);
ctlreg |= ADC_WAITTRIG | WT_INT_EN;
__raw_writel(ctlreg, w90p910_ts->ts_reg);
w90p910_ts->state = TS_WAIT_NEW_PACKET;
}
static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id)
{
struct w90p910_ts *w90p910_ts = dev_id;
unsigned long flags;
spin_lock_irqsave(&w90p910_ts->lock, flags);
switch (w90p910_ts->state) {
case TS_WAIT_NEW_PACKET:
/*
* The controller only generates interrupts when pen
* is down.
*/
del_timer(&w90p910_ts->timer);
w90p910_prepare_x_reading(w90p910_ts);
break;
case TS_WAIT_X_COORD:
w90p910_prepare_y_reading(w90p910_ts);
break;
case TS_WAIT_Y_COORD:
w90p910_report_event(w90p910_ts, true);
w90p910_prepare_next_packet(w90p910_ts);
mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100));
break;
case TS_IDLE:
break;
}
spin_unlock_irqrestore(&w90p910_ts->lock, flags);
return IRQ_HANDLED;
}
static void w90p910_check_pen_up(unsigned long data)
{
struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data;
unsigned long flags;
spin_lock_irqsave(&w90p910_ts->lock, flags);
if (w90p910_ts->state == TS_WAIT_NEW_PACKET &&
!(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) {
w90p910_report_event(w90p910_ts, false);
}
spin_unlock_irqrestore(&w90p910_ts->lock, flags);
}
static int w90p910_open(struct input_dev *dev)
{
struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
unsigned long val;
/* enable the ADC clock */
val = __raw_readl(w90p910_ts->clocken);
__raw_writel(val | ADC_CLK_EN, w90p910_ts->clocken);
__raw_writel(ADC_RST1, w90p910_ts->ts_reg);
msleep(1);
__raw_writel(ADC_RST0, w90p910_ts->ts_reg);
msleep(1);
/* set delay and screen type */
val = __raw_readl(w90p910_ts->ts_reg + 0x04);
__raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04);
__raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08);
w90p910_ts->state = TS_WAIT_NEW_PACKET;
wmb();
/* set trigger mode */
val = __raw_readl(w90p910_ts->ts_reg);
val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN;
__raw_writel(val, w90p910_ts->ts_reg);
return 0;
}
static void w90p910_close(struct input_dev *dev)
{
struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
unsigned long val;
/* disable trigger mode */
spin_lock_irq(&w90p910_ts->lock);
w90p910_ts->state = TS_IDLE;
val = __raw_readl(w90p910_ts->ts_reg);
val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN);
__raw_writel(val, w90p910_ts->ts_reg);
spin_unlock_irq(&w90p910_ts->lock);
/* Now that interrupts are shut off we can safely delete timer */
del_timer_sync(&w90p910_ts->timer);
/* stop the ADC clock */
val = __raw_readl(w90p910_ts->clocken);
__raw_writel(val & ~ADC_CLK_EN, w90p910_ts->clocken);
}
static int __devinit w90x900ts_probe(struct platform_device *pdev)
{
struct w90p910_ts *w90p910_ts;
struct input_dev *input_dev;
struct resource *res;
int err;
w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL);
input_dev = input_allocate_device();
if (!w90p910_ts || !input_dev) {
err = -ENOMEM;
goto fail1;
}
w90p910_ts->input = input_dev;
w90p910_ts->state = TS_IDLE;
spin_lock_init(&w90p910_ts->lock);
setup_timer(&w90p910_ts->timer, w90p910_check_pen_up,
(unsigned long)&w90p910_ts);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
err = -ENXIO;
goto fail1;
}
if (!request_mem_region(res->start, res->end - res->start + 1,
pdev->name)) {
err = -EBUSY;
goto fail1;
}
w90p910_ts->ts_reg = ioremap(res->start, res->end - res->start + 1);
if (!w90p910_ts->ts_reg) {
err = -ENOMEM;
goto fail2;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
if (!res) {
err = -ENXIO;
goto fail3;
}
w90p910_ts->clocken = (void __iomem *)res->start;
input_dev->name = "W90P910 TouchScreen";
input_dev->phys = "w90p910ts/event0";
input_dev->id.bustype = BUS_HOST;
input_dev->id.vendor = 0x0005;
input_dev->id.product = 0x0001;
input_dev->id.version = 0x0100;
input_dev->dev.parent = &pdev->dev;
input_dev->open = w90p910_open;
input_dev->close = w90p910_close;
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0);
input_set_drvdata(input_dev, w90p910_ts);
w90p910_ts->irq_num = platform_get_irq(pdev, 0);
if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
IRQF_DISABLED, "w90p910ts", w90p910_ts)) {
err = -EBUSY;
goto fail3;
}
err = input_register_device(w90p910_ts->input);
if (err)
goto fail4;
platform_set_drvdata(pdev, w90p910_ts);
return 0;
fail4: free_irq(w90p910_ts->irq_num, w90p910_ts);
fail3: iounmap(w90p910_ts->ts_reg);
fail2: release_mem_region(res->start, res->end - res->start + 1);
fail1: input_free_device(input_dev);
kfree(w90p910_ts);
return err;
}
static int __devexit w90x900ts_remove(struct platform_device *pdev)
{
struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);
struct resource *res;
free_irq(w90p910_ts->irq_num, w90p910_ts);
del_timer_sync(&w90p910_ts->timer);
iounmap(w90p910_ts->ts_reg);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start + 1);
input_unregister_device(w90p910_ts->input);
kfree(w90p910_ts);
platform_set_drvdata(pdev, NULL);
return 0;
}
static struct platform_driver w90x900ts_driver = {
.probe = w90x900ts_probe,
.remove = __devexit_p(w90x900ts_remove),
.driver = {
.name = "w90x900-ts",
.owner = THIS_MODULE,
},
};
static int __init w90x900ts_init(void)
{
return platform_driver_register(&w90x900ts_driver);
}
static void __exit w90x900ts_exit(void)
{
platform_driver_unregister(&w90x900ts_driver);
}
module_init(w90x900ts_init);
module_exit(w90x900ts_exit);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("w90p910 touch screen driver!");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:w90p910-ts");

View file

@ -23,6 +23,26 @@
#include <linux/module.h>
#include <linux/ucb1400.h>
unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel,
int adcsync)
{
unsigned int val;
if (adcsync)
adc_channel |= UCB_ADC_SYNC_ENA;
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel |
UCB_ADC_START);
while (!((val = ucb1400_reg_read(ac97, UCB_ADC_DATA))
& UCB_ADC_DAT_VALID))
schedule_timeout_uninterruptible(1);
return val & UCB_ADC_DAT_MASK;
}
EXPORT_SYMBOL_GPL(ucb1400_adc_read);
static int ucb1400_core_probe(struct device *dev)
{
int err;

View file

@ -11,6 +11,7 @@
#ifdef __KERNEL__
#include <asm/io.h>
#include <linux/types.h>
#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/device.h>
@ -62,7 +63,7 @@ struct gameport_driver {
struct device_driver driver;
unsigned int ignore;
bool ignore;
};
#define to_gameport_driver(d) container_of(d, struct gameport_driver, driver)

View file

@ -0,0 +1,46 @@
/*
* lm8323.h - Configuration for LM8323 keypad driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation (version 2 of the License only).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __LINUX_LM8323_H
#define __LINUX_LM8323_H
#include <linux/types.h>
/*
* Largest keycode that the chip can send, plus one,
* so keys can be mapped directly at the index of the
* LM8323 keycode instead of subtracting one.
*/
#define LM8323_KEYMAP_SIZE (0x7f + 1)
#define LM8323_NUM_PWMS 3
struct lm8323_platform_data {
int debounce_time; /* Time to watch for key bouncing, in ms. */
int active_time; /* Idle time until sleep, in ms. */
int size_x;
int size_y;
bool repeat;
const unsigned short *keymap;
const char *pwm_names[LM8323_NUM_PWMS];
const char *name; /* Device name. */
};
#endif /* __LINUX_LM8323_H */

View file

@ -53,6 +53,7 @@ struct input_absinfo {
__s32 maximum;
__s32 fuzz;
__s32 flat;
__s32 resolution;
};
#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */
@ -1109,6 +1110,7 @@ struct input_dev {
int absmin[ABS_MAX + 1];
int absfuzz[ABS_MAX + 1];
int absflat[ABS_MAX + 1];
int absres[ABS_MAX + 1];
int (*open)(struct input_dev *dev);
void (*close)(struct input_dev *dev);

View file

@ -8,6 +8,8 @@ struct rotary_encoder_platform_data {
unsigned int gpio_b;
unsigned int inverted_a;
unsigned int inverted_b;
bool relative_axis;
bool rollover;
};
#endif /* __ROTARY_ENCODER_H__ */

View file

@ -15,6 +15,7 @@
#ifdef __KERNEL__
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/spinlock.h>
@ -28,7 +29,10 @@ struct serio {
char name[32];
char phys[32];
unsigned int manual_bind;
bool manual_bind;
bool registered; /* port has been fully registered with driver core */
bool suspended; /* port is suspended */
struct serio_device_id id;
@ -47,7 +51,6 @@ struct serio {
struct mutex drv_mutex; /* protects serio->drv so attributes can pin driver */
struct device dev;
unsigned int registered; /* port has been fully registered with driver core */
struct list_head node;
};
@ -58,7 +61,7 @@ struct serio_driver {
char *description;
struct serio_device_id *id_table;
unsigned int manual_bind;
bool manual_bind;
void (*write_wakeup)(struct serio *);
irqreturn_t (*interrupt)(struct serio *, unsigned char, unsigned int);

View file

@ -17,6 +17,7 @@ struct ads7846_platform_data {
u16 vref_mv; /* external vref value, milliVolts */
bool keep_vref_on; /* set to keep vref on for differential
* measurements as well */
bool swap_xy; /* swap x and y axes */
/* Settling time of the analog signals; a function of Vcc and the
* capacitance on the X/Y drivers. If set to non-zero, two samples

View file

@ -134,28 +134,13 @@ static inline void ucb1400_adc_enable(struct snd_ac97 *ac97)
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA);
}
static unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel,
int adcsync)
{
unsigned int val;
if (adcsync)
adc_channel |= UCB_ADC_SYNC_ENA;
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel);
ucb1400_reg_write(ac97, UCB_ADC_CR, UCB_ADC_ENA | adc_channel |
UCB_ADC_START);
while (!((val = ucb1400_reg_read(ac97, UCB_ADC_DATA))
& UCB_ADC_DAT_VALID))
schedule_timeout_uninterruptible(1);
return val & UCB_ADC_DAT_MASK;
}
static inline void ucb1400_adc_disable(struct snd_ac97 *ac97)
{
ucb1400_reg_write(ac97, UCB_ADC_CR, 0);
}
unsigned int ucb1400_adc_read(struct snd_ac97 *ac97, u16 adc_channel,
int adcsync);
#endif