mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 11:19:37 +00:00
Merge branch 'next' into for-linus
This commit is contained in:
commit
ba28f22e7c
28 changed files with 3066 additions and 204 deletions
101
Documentation/input/rotary-encoder.txt
Normal file
101
Documentation/input/rotary-encoder.txt
Normal file
|
@ -0,0 +1,101 @@
|
|||
rotary-encoder - a generic driver for GPIO connected devices
|
||||
Daniel Mack <daniel@caiaq.de>, Feb 2009
|
||||
|
||||
0. Function
|
||||
-----------
|
||||
|
||||
Rotary encoders are devices which are connected to the CPU or other
|
||||
peripherals with two wires. The outputs are phase-shifted by 90 degrees
|
||||
and by triggering on falling and rising edges, the turn direction can
|
||||
be determined.
|
||||
|
||||
The phase diagram of these two outputs look like this:
|
||||
|
||||
_____ _____ _____
|
||||
| | | | | |
|
||||
Channel A ____| |_____| |_____| |____
|
||||
|
||||
: : : : : : : : : : : :
|
||||
__ _____ _____ _____
|
||||
| | | | | | |
|
||||
Channel B |_____| |_____| |_____| |__
|
||||
|
||||
: : : : : : : : : : : :
|
||||
Event a b c d a b c d a b c d
|
||||
|
||||
|<-------->|
|
||||
one step
|
||||
|
||||
|
||||
For more information, please see
|
||||
http://en.wikipedia.org/wiki/Rotary_encoder
|
||||
|
||||
|
||||
1. Events / state machine
|
||||
-------------------------
|
||||
|
||||
a) Rising edge on channel A, channel B in low state
|
||||
This state is used to recognize a clockwise turn
|
||||
|
||||
b) Rising edge on channel B, channel A in high state
|
||||
When entering this state, the encoder is put into 'armed' state,
|
||||
meaning that there it has seen half the way of a one-step transition.
|
||||
|
||||
c) Falling edge on channel A, channel B in high state
|
||||
This state is used to recognize a counter-clockwise turn
|
||||
|
||||
d) Falling edge on channel B, channel A in low state
|
||||
Parking position. If the encoder enters this state, a full transition
|
||||
should have happend, unless it flipped back on half the way. The
|
||||
'armed' state tells us about that.
|
||||
|
||||
2. Platform requirements
|
||||
------------------------
|
||||
|
||||
As there is no hardware dependent call in this driver, the platform it is
|
||||
used with must support gpiolib. Another requirement is that IRQs must be
|
||||
able to fire on both edges.
|
||||
|
||||
|
||||
3. Board integration
|
||||
--------------------
|
||||
|
||||
To use this driver in your system, register a platform_device with the
|
||||
name 'rotary-encoder' and associate the IRQs and some specific platform
|
||||
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).
|
||||
|
||||
Because GPIO to IRQ mapping is platform specific, this information must
|
||||
be given in seperately to the driver. See the example below.
|
||||
|
||||
---------<snip>---------
|
||||
|
||||
/* board support file example */
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/rotary_encoder.h>
|
||||
|
||||
#define GPIO_ROTARY_A 1
|
||||
#define GPIO_ROTARY_B 2
|
||||
|
||||
static struct rotary_encoder_platform_data my_rotary_encoder_info = {
|
||||
.steps = 24,
|
||||
.axis = ABS_X,
|
||||
.gpio_a = GPIO_ROTARY_A,
|
||||
.gpio_b = GPIO_ROTARY_B,
|
||||
.inverted_a = 0,
|
||||
.inverted_b = 0,
|
||||
};
|
||||
|
||||
static struct platform_device rotary_encoder_device = {
|
||||
.name = "rotary-encoder",
|
||||
.id = 0,
|
||||
.dev = {
|
||||
.platform_data = &my_rotary_encoder_info,
|
||||
}
|
||||
};
|
||||
|
|
@ -80,6 +80,9 @@ struct rb532_gpio_reg {
|
|||
/* Compact Flash GPIO pin */
|
||||
#define CF_GPIO_NUM 13
|
||||
|
||||
/* S1 button GPIO (shared with UART0_SIN) */
|
||||
#define GPIO_BTN_S1 1
|
||||
|
||||
extern void rb532_gpio_set_ilevel(int bit, unsigned gpio);
|
||||
extern void rb532_gpio_set_istat(int bit, unsigned gpio);
|
||||
extern void rb532_gpio_set_func(unsigned gpio);
|
||||
|
|
|
@ -200,26 +200,9 @@ static struct platform_device rb532_led = {
|
|||
.id = -1,
|
||||
};
|
||||
|
||||
static struct gpio_keys_button rb532_gpio_btn[] = {
|
||||
{
|
||||
.gpio = 1,
|
||||
.code = BTN_0,
|
||||
.desc = "S1",
|
||||
.active_low = 1,
|
||||
}
|
||||
};
|
||||
|
||||
static struct gpio_keys_platform_data rb532_gpio_btn_data = {
|
||||
.buttons = rb532_gpio_btn,
|
||||
.nbuttons = ARRAY_SIZE(rb532_gpio_btn),
|
||||
};
|
||||
|
||||
static struct platform_device rb532_button = {
|
||||
.name = "gpio-keys",
|
||||
.name = "rb532-button",
|
||||
.id = -1,
|
||||
.dev = {
|
||||
.platform_data = &rb532_gpio_btn_data,
|
||||
}
|
||||
};
|
||||
|
||||
static struct resource rb532_wdt_res[] = {
|
||||
|
|
|
@ -132,6 +132,11 @@ static void input_start_autorepeat(struct input_dev *dev, int code)
|
|||
}
|
||||
}
|
||||
|
||||
static void input_stop_autorepeat(struct input_dev *dev)
|
||||
{
|
||||
del_timer(&dev->timer);
|
||||
}
|
||||
|
||||
#define INPUT_IGNORE_EVENT 0
|
||||
#define INPUT_PASS_TO_HANDLERS 1
|
||||
#define INPUT_PASS_TO_DEVICE 2
|
||||
|
@ -167,6 +172,8 @@ static void input_handle_event(struct input_dev *dev,
|
|||
__change_bit(code, dev->key);
|
||||
if (value)
|
||||
input_start_autorepeat(dev, code);
|
||||
else
|
||||
input_stop_autorepeat(dev);
|
||||
}
|
||||
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
|
@ -737,11 +744,11 @@ static inline void input_wakeup_procfs_readers(void)
|
|||
|
||||
static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
|
||||
{
|
||||
int state = input_devices_state;
|
||||
|
||||
poll_wait(file, &input_devices_poll_wait, wait);
|
||||
if (state != input_devices_state)
|
||||
if (file->f_version != input_devices_state) {
|
||||
file->f_version = input_devices_state;
|
||||
return POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -229,7 +229,8 @@ struct atkbd {
|
|||
/*
|
||||
* System-specific ketymap fixup routine
|
||||
*/
|
||||
static void (*atkbd_platform_fixup)(struct atkbd *);
|
||||
static void (*atkbd_platform_fixup)(struct atkbd *, const void *data);
|
||||
static void *atkbd_platform_fixup_data;
|
||||
|
||||
static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
|
||||
ssize_t (*handler)(struct atkbd *, char *));
|
||||
|
@ -833,88 +834,65 @@ static void atkbd_disconnect(struct serio *serio)
|
|||
kfree(atkbd);
|
||||
}
|
||||
|
||||
/*
|
||||
* generate release events for the keycodes given in data
|
||||
*/
|
||||
static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd,
|
||||
const void *data)
|
||||
{
|
||||
const unsigned int *keys = data;
|
||||
unsigned int i;
|
||||
|
||||
if (atkbd->set == 2)
|
||||
for (i = 0; keys[i] != -1U; i++)
|
||||
__set_bit(keys[i], atkbd->force_release_mask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Most special keys (Fn+F?) on Dell laptops do not generate release
|
||||
* events so we have to do it ourselves.
|
||||
*/
|
||||
static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd)
|
||||
{
|
||||
static const unsigned int forced_release_keys[] = {
|
||||
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (atkbd->set == 2)
|
||||
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||
__set_bit(forced_release_keys[i],
|
||||
atkbd->force_release_mask);
|
||||
}
|
||||
static unsigned int atkbd_dell_laptop_forced_release_keys[] = {
|
||||
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93, -1U
|
||||
};
|
||||
|
||||
/*
|
||||
* Perform fixup for HP system that doesn't generate release
|
||||
* for its video switch
|
||||
*/
|
||||
static void atkbd_hp_keymap_fixup(struct atkbd *atkbd)
|
||||
{
|
||||
static const unsigned int forced_release_keys[] = {
|
||||
0x94,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (atkbd->set == 2)
|
||||
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||
__set_bit(forced_release_keys[i],
|
||||
atkbd->force_release_mask);
|
||||
}
|
||||
static unsigned int atkbd_hp_forced_release_keys[] = {
|
||||
0x94, -1U
|
||||
};
|
||||
|
||||
/*
|
||||
* Inventec system with broken key release on volume keys
|
||||
*/
|
||||
static void atkbd_inventec_keymap_fixup(struct atkbd *atkbd)
|
||||
{
|
||||
const unsigned int forced_release_keys[] = {
|
||||
0xae, 0xb0,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (atkbd->set == 2)
|
||||
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||
__set_bit(forced_release_keys[i],
|
||||
atkbd->force_release_mask);
|
||||
}
|
||||
static unsigned int atkbd_inventec_forced_release_keys[] = {
|
||||
0xae, 0xb0, -1U
|
||||
};
|
||||
|
||||
/*
|
||||
* Perform fixup for HP Pavilion ZV6100 laptop that doesn't generate release
|
||||
* for its volume buttons
|
||||
*/
|
||||
static void atkbd_hp_zv6100_keymap_fixup(struct atkbd *atkbd)
|
||||
{
|
||||
const unsigned int forced_release_keys[] = {
|
||||
0xae, 0xb0,
|
||||
};
|
||||
int i;
|
||||
|
||||
if (atkbd->set == 2)
|
||||
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||
__set_bit(forced_release_keys[i],
|
||||
atkbd->force_release_mask);
|
||||
}
|
||||
static unsigned int atkbd_hp_zv6100_forced_release_keys[] = {
|
||||
0xae, 0xb0, -1U
|
||||
};
|
||||
|
||||
/*
|
||||
* Samsung NC10 with Fn+F? key release not working
|
||||
*/
|
||||
static void atkbd_samsung_keymap_fixup(struct atkbd *atkbd)
|
||||
{
|
||||
const unsigned int forced_release_keys[] = {
|
||||
0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9,
|
||||
};
|
||||
int i;
|
||||
static unsigned int atkbd_samsung_forced_release_keys[] = {
|
||||
0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9, -1U
|
||||
};
|
||||
|
||||
if (atkbd->set == 2)
|
||||
for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
|
||||
__set_bit(forced_release_keys[i],
|
||||
atkbd->force_release_mask);
|
||||
}
|
||||
/*
|
||||
* The volume up and volume down special keys on a Fujitsu Amilo PA 1510 laptop
|
||||
* do not generate release events so we have to do it ourselves.
|
||||
*/
|
||||
static unsigned int atkbd_amilo_pa1510_forced_release_keys[] = {
|
||||
0xb0, 0xae, -1U
|
||||
};
|
||||
|
||||
/*
|
||||
* atkbd_set_keycode_table() initializes keyboard's keycode table
|
||||
|
@ -967,7 +945,7 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
|
|||
* Perform additional fixups
|
||||
*/
|
||||
if (atkbd_platform_fixup)
|
||||
atkbd_platform_fixup(atkbd);
|
||||
atkbd_platform_fixup(atkbd, atkbd_platform_fixup_data);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1492,9 +1470,11 @@ static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf)
|
|||
return sprintf(buf, "%lu\n", atkbd->err_count);
|
||||
}
|
||||
|
||||
static int __init atkbd_setup_fixup(const struct dmi_system_id *id)
|
||||
static int __init atkbd_setup_forced_release(const struct dmi_system_id *id)
|
||||
{
|
||||
atkbd_platform_fixup = id->driver_data;
|
||||
atkbd_platform_fixup = atkbd_apply_forced_release_keylist;
|
||||
atkbd_platform_fixup_data = id->driver_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1505,8 +1485,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_dell_laptop_keymap_fixup,
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_dell_laptop_forced_release_keys,
|
||||
},
|
||||
{
|
||||
.ident = "Dell Laptop",
|
||||
|
@ -1514,8 +1494,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
||||
DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_dell_laptop_keymap_fixup,
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_dell_laptop_forced_release_keys,
|
||||
},
|
||||
{
|
||||
.ident = "HP 2133",
|
||||
|
@ -1523,8 +1503,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"),
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_hp_keymap_fixup,
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_hp_forced_release_keys,
|
||||
},
|
||||
{
|
||||
.ident = "HP Pavilion ZV6100",
|
||||
|
@ -1532,8 +1512,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion ZV6100"),
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_hp_zv6100_keymap_fixup,
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_hp_zv6100_forced_release_keys,
|
||||
},
|
||||
{
|
||||
.ident = "Inventec Symphony",
|
||||
|
@ -1541,8 +1521,8 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"),
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_inventec_keymap_fixup,
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_inventec_forced_release_keys,
|
||||
},
|
||||
{
|
||||
.ident = "Samsung NC10",
|
||||
|
@ -1550,8 +1530,17 @@ static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
|
||||
},
|
||||
.callback = atkbd_setup_fixup,
|
||||
.driver_data = atkbd_samsung_keymap_fixup,
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_samsung_forced_release_keys,
|
||||
},
|
||||
{
|
||||
.ident = "Fujitsu Amilo PA 1510",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 1510"),
|
||||
},
|
||||
.callback = atkbd_setup_forced_release,
|
||||
.driver_data = atkbd_amilo_pa1510_forced_release_keys,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
|
|
@ -211,8 +211,8 @@ static int __devinit bfin_kpad_probe(struct platform_device *pdev)
|
|||
|
||||
if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
|
||||
!pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
|
||||
printk(KERN_ERR DRV_NAME
|
||||
": Invalid Debounce/Columdrive Time from pdata\n");
|
||||
printk(KERN_WARNING DRV_NAME
|
||||
": Invalid Debounce/Columndrive Time in platform data\n");
|
||||
bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
|
||||
} else {
|
||||
bfin_write_KPAD_MSEL(
|
||||
|
|
|
@ -198,45 +198,28 @@ static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
|
|||
}
|
||||
|
||||
|
||||
/* initialise HIL */
|
||||
static int __init
|
||||
hil_keyb_init(void)
|
||||
/* initialize HIL */
|
||||
static int __devinit hil_keyb_init(void)
|
||||
{
|
||||
unsigned char c;
|
||||
unsigned int i, kbid;
|
||||
wait_queue_head_t hil_wait;
|
||||
int err;
|
||||
|
||||
if (hil_dev.dev) {
|
||||
if (hil_dev.dev)
|
||||
return -ENODEV; /* already initialized */
|
||||
}
|
||||
|
||||
init_waitqueue_head(&hil_wait);
|
||||
spin_lock_init(&hil_dev.lock);
|
||||
|
||||
hil_dev.dev = input_allocate_device();
|
||||
if (!hil_dev.dev)
|
||||
return -ENOMEM;
|
||||
|
||||
#if defined(CONFIG_HP300)
|
||||
if (!MACH_IS_HP300) {
|
||||
err = -ENODEV;
|
||||
goto err1;
|
||||
}
|
||||
if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
|
||||
printk(KERN_ERR "HIL: hardware register was not found\n");
|
||||
err = -ENODEV;
|
||||
goto err1;
|
||||
}
|
||||
if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
|
||||
printk(KERN_ERR "HIL: IOPORT region already used\n");
|
||||
err = -EIO;
|
||||
goto err1;
|
||||
}
|
||||
#endif
|
||||
|
||||
err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
|
||||
if (err) {
|
||||
printk(KERN_ERR "HIL: Can't get IRQ\n");
|
||||
goto err2;
|
||||
goto err1;
|
||||
}
|
||||
|
||||
/* Turn on interrupts */
|
||||
|
@ -246,11 +229,9 @@ hil_keyb_init(void)
|
|||
hil_dev.valid = 0; /* clear any pending data */
|
||||
hil_do(HIL_READKBDSADR, NULL, 0);
|
||||
|
||||
init_waitqueue_head(&hil_wait);
|
||||
wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
|
||||
if (!hil_dev.valid) {
|
||||
wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3 * HZ);
|
||||
if (!hil_dev.valid)
|
||||
printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
|
||||
}
|
||||
|
||||
c = hil_dev.c;
|
||||
hil_dev.valid = 0;
|
||||
|
@ -268,7 +249,7 @@ hil_keyb_init(void)
|
|||
|
||||
for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
|
||||
if (hphilkeyb_keycode[i] != KEY_RESERVED)
|
||||
set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
|
||||
__set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
|
||||
|
||||
hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
|
||||
|
@ -287,34 +268,45 @@ hil_keyb_init(void)
|
|||
err = input_register_device(hil_dev.dev);
|
||||
if (err) {
|
||||
printk(KERN_ERR "HIL: Can't register device\n");
|
||||
goto err3;
|
||||
goto err2;
|
||||
}
|
||||
|
||||
printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
|
||||
hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
|
||||
|
||||
return 0;
|
||||
|
||||
err3:
|
||||
hil_do(HIL_INTOFF, NULL, 0);
|
||||
disable_irq(HIL_IRQ);
|
||||
free_irq(HIL_IRQ, hil_dev.dev_id);
|
||||
err2:
|
||||
#if defined(CONFIG_HP300)
|
||||
release_region(HILBASE + HIL_DATA, 2);
|
||||
hil_do(HIL_INTOFF, NULL, 0);
|
||||
free_irq(HIL_IRQ, hil_dev.dev_id);
|
||||
err1:
|
||||
#endif
|
||||
input_free_device(hil_dev.dev);
|
||||
hil_dev.dev = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __devexit hil_keyb_exit(void)
|
||||
{
|
||||
if (HIL_IRQ)
|
||||
free_irq(HIL_IRQ, hil_dev.dev_id);
|
||||
|
||||
/* Turn off interrupts */
|
||||
hil_do(HIL_INTOFF, NULL, 0);
|
||||
|
||||
input_unregister_device(hil_dev.dev);
|
||||
hil_dev.dev = NULL;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PARISC)
|
||||
static int __init
|
||||
hil_init_chip(struct parisc_device *dev)
|
||||
static int __devinit hil_probe_chip(struct parisc_device *dev)
|
||||
{
|
||||
/* Only allow one HIL keyboard */
|
||||
if (hil_dev.dev)
|
||||
return -ENODEV;
|
||||
|
||||
if (!dev->irq) {
|
||||
printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa.start);
|
||||
printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%p\n",
|
||||
(void *)dev->hpa.start);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
|
@ -327,51 +319,79 @@ hil_init_chip(struct parisc_device *dev)
|
|||
return hil_keyb_init();
|
||||
}
|
||||
|
||||
static int __devexit hil_remove_chip(struct parisc_device *dev)
|
||||
{
|
||||
hil_keyb_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct parisc_device_id hil_tbl[] = {
|
||||
{ HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
|
||||
{ 0, }
|
||||
};
|
||||
|
||||
#if 0
|
||||
/* Disabled to avoid conflicts with the HP SDC HIL drivers */
|
||||
MODULE_DEVICE_TABLE(parisc, hil_tbl);
|
||||
#endif
|
||||
|
||||
static struct parisc_driver hil_driver = {
|
||||
.name = "hil",
|
||||
.id_table = hil_tbl,
|
||||
.probe = hil_init_chip,
|
||||
.name = "hil",
|
||||
.id_table = hil_tbl,
|
||||
.probe = hil_probe_chip,
|
||||
.remove = __devexit_p(hil_remove_chip),
|
||||
};
|
||||
#endif /* CONFIG_PARISC */
|
||||
|
||||
|
||||
static int __init hil_init(void)
|
||||
{
|
||||
#if defined(CONFIG_PARISC)
|
||||
return register_parisc_driver(&hil_driver);
|
||||
#else
|
||||
return hil_keyb_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void __exit hil_exit(void)
|
||||
{
|
||||
if (HIL_IRQ) {
|
||||
disable_irq(HIL_IRQ);
|
||||
free_irq(HIL_IRQ, hil_dev.dev_id);
|
||||
unregister_parisc_driver(&hil_driver);
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PARISC */
|
||||
|
||||
static int __init hil_init(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Only allow one HIL keyboard */
|
||||
if (hil_dev.dev)
|
||||
return -EBUSY;
|
||||
|
||||
if (!MACH_IS_HP300)
|
||||
return -ENODEV;
|
||||
|
||||
if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
|
||||
printk(KERN_ERR "HIL: hardware register was not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Turn off interrupts */
|
||||
hil_do(HIL_INTOFF, NULL, 0);
|
||||
if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
|
||||
printk(KERN_ERR "HIL: IOPORT region already used\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
input_unregister_device(hil_dev.dev);
|
||||
error = hil_keyb_init();
|
||||
if (error) {
|
||||
release_region(HILBASE + HIL_DATA, 2);
|
||||
return error;
|
||||
}
|
||||
|
||||
hil_dev.dev = NULL;
|
||||
|
||||
#if defined(CONFIG_PARISC)
|
||||
unregister_parisc_driver(&hil_driver);
|
||||
#else
|
||||
release_region(HILBASE+HIL_DATA, 2);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit hil_exit(void)
|
||||
{
|
||||
hil_keyb_exit();
|
||||
release_region(HILBASE + HIL_DATA, 2);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PARISC */
|
||||
|
||||
module_init(hil_init);
|
||||
module_exit(hil_exit);
|
||||
|
|
|
@ -227,4 +227,27 @@ config INPUT_PCF50633_PMU
|
|||
Say Y to include support for delivering PMU events via input
|
||||
layer on NXP PCF50633.
|
||||
|
||||
config INPUT_GPIO_ROTARY_ENCODER
|
||||
tristate "Rotary encoders connected to GPIO pins"
|
||||
depends on GPIOLIB && GENERIC_GPIO
|
||||
help
|
||||
Say Y here to add support for rotary encoders connected to GPIO lines.
|
||||
Check file:Documentation/incput/rotary_encoder.txt for more
|
||||
information.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rotary_encoder.
|
||||
|
||||
config INPUT_RB532_BUTTON
|
||||
tristate "Mikrotik Routerboard 532 button interface"
|
||||
depends on MIKROTIK_RB532
|
||||
depends on GPIOLIB && GENERIC_GPIO
|
||||
select INPUT_POLLDEV
|
||||
help
|
||||
Say Y here if you want support for the S1 button built into
|
||||
Mikrotik's Routerboard 532.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called rb532_button.
|
||||
|
||||
endif
|
||||
|
|
|
@ -4,21 +4,23 @@
|
|||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o
|
||||
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
|
||||
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
|
||||
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
|
||||
obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
|
||||
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
|
||||
obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
|
||||
obj-$(CONFIG_INPUT_APANEL) += apanel.o
|
||||
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
|
||||
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
|
||||
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
|
||||
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
|
||||
obj-$(CONFIG_INPUT_YEALINK) += yealink.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_HP_SDC_RTC) += hp_sdc_rtc.o
|
||||
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
|
||||
obj-$(CONFIG_INPUT_APANEL) += apanel.o
|
||||
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
||||
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
|
||||
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
|
||||
obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
|
||||
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
|
||||
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
|
||||
obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
|
||||
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_UINPUT) += uinput.o
|
||||
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
|
||||
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
|
||||
|
|
|
@ -31,12 +31,73 @@ MODULE_LICENSE("GPL");
|
|||
* newly configured "channel".
|
||||
*/
|
||||
|
||||
static unsigned int channel_mask = 0xFFFF;
|
||||
module_param(channel_mask, uint, 0644);
|
||||
enum {
|
||||
ATI_REMOTE2_MAX_CHANNEL_MASK = 0xFFFF,
|
||||
ATI_REMOTE2_MAX_MODE_MASK = 0x1F,
|
||||
};
|
||||
|
||||
static int ati_remote2_set_mask(const char *val,
|
||||
struct kernel_param *kp, unsigned int max)
|
||||
{
|
||||
unsigned long mask;
|
||||
int ret;
|
||||
|
||||
if (!val)
|
||||
return -EINVAL;
|
||||
|
||||
ret = strict_strtoul(val, 0, &mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (mask & ~max)
|
||||
return -EINVAL;
|
||||
|
||||
*(unsigned int *)kp->arg = mask;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_set_channel_mask(const char *val,
|
||||
struct kernel_param *kp)
|
||||
{
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_CHANNEL_MASK);
|
||||
}
|
||||
|
||||
static int ati_remote2_get_channel_mask(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
return sprintf(buffer, "0x%04x", *(unsigned int *)kp->arg);
|
||||
}
|
||||
|
||||
static int ati_remote2_set_mode_mask(const char *val, struct kernel_param *kp)
|
||||
{
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
return ati_remote2_set_mask(val, kp, ATI_REMOTE2_MAX_MODE_MASK);
|
||||
}
|
||||
|
||||
static int ati_remote2_get_mode_mask(char *buffer, struct kernel_param *kp)
|
||||
{
|
||||
pr_debug("%s()\n", __func__);
|
||||
|
||||
return sprintf(buffer, "0x%02x", *(unsigned int *)kp->arg);
|
||||
}
|
||||
|
||||
static unsigned int channel_mask = ATI_REMOTE2_MAX_CHANNEL_MASK;
|
||||
#define param_check_channel_mask(name, p) __param_check(name, p, unsigned int)
|
||||
#define param_set_channel_mask ati_remote2_set_channel_mask
|
||||
#define param_get_channel_mask ati_remote2_get_channel_mask
|
||||
module_param(channel_mask, channel_mask, 0644);
|
||||
MODULE_PARM_DESC(channel_mask, "Bitmask of channels to accept <15:Channel16>...<1:Channel2><0:Channel1>");
|
||||
|
||||
static unsigned int mode_mask = 0x1F;
|
||||
module_param(mode_mask, uint, 0644);
|
||||
static unsigned int mode_mask = ATI_REMOTE2_MAX_MODE_MASK;
|
||||
#define param_check_mode_mask(name, p) __param_check(name, p, unsigned int)
|
||||
#define param_set_mode_mask ati_remote2_set_mode_mask
|
||||
#define param_get_mode_mask ati_remote2_get_mode_mask
|
||||
module_param(mode_mask, mode_mask, 0644);
|
||||
MODULE_PARM_DESC(mode_mask, "Bitmask of modes to accept <4:PC><3:AUX4><2:AUX3><1:AUX2><0:AUX1>");
|
||||
|
||||
static struct usb_device_id ati_remote2_id_table[] = {
|
||||
|
@ -133,12 +194,18 @@ struct ati_remote2 {
|
|||
u16 keycode[ATI_REMOTE2_MODES][ARRAY_SIZE(ati_remote2_key_table)];
|
||||
|
||||
unsigned int flags;
|
||||
|
||||
unsigned int channel_mask;
|
||||
unsigned int mode_mask;
|
||||
};
|
||||
|
||||
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id);
|
||||
static void ati_remote2_disconnect(struct usb_interface *interface);
|
||||
static int ati_remote2_suspend(struct usb_interface *interface, pm_message_t message);
|
||||
static int ati_remote2_resume(struct usb_interface *interface);
|
||||
static int ati_remote2_reset_resume(struct usb_interface *interface);
|
||||
static int ati_remote2_pre_reset(struct usb_interface *interface);
|
||||
static int ati_remote2_post_reset(struct usb_interface *interface);
|
||||
|
||||
static struct usb_driver ati_remote2_driver = {
|
||||
.name = "ati_remote2",
|
||||
|
@ -147,6 +214,9 @@ static struct usb_driver ati_remote2_driver = {
|
|||
.id_table = ati_remote2_id_table,
|
||||
.suspend = ati_remote2_suspend,
|
||||
.resume = ati_remote2_resume,
|
||||
.reset_resume = ati_remote2_reset_resume,
|
||||
.pre_reset = ati_remote2_pre_reset,
|
||||
.post_reset = ati_remote2_post_reset,
|
||||
.supports_autosuspend = 1,
|
||||
};
|
||||
|
||||
|
@ -238,7 +308,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
|
|||
|
||||
channel = data[0] >> 4;
|
||||
|
||||
if (!((1 << channel) & channel_mask))
|
||||
if (!((1 << channel) & ar2->channel_mask))
|
||||
return;
|
||||
|
||||
mode = data[0] & 0x0F;
|
||||
|
@ -250,7 +320,7 @@ static void ati_remote2_input_mouse(struct ati_remote2 *ar2)
|
|||
return;
|
||||
}
|
||||
|
||||
if (!((1 << mode) & mode_mask))
|
||||
if (!((1 << mode) & ar2->mode_mask))
|
||||
return;
|
||||
|
||||
input_event(idev, EV_REL, REL_X, (s8) data[1]);
|
||||
|
@ -277,7 +347,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
|
||||
channel = data[0] >> 4;
|
||||
|
||||
if (!((1 << channel) & channel_mask))
|
||||
if (!((1 << channel) & ar2->channel_mask))
|
||||
return;
|
||||
|
||||
mode = data[0] & 0x0F;
|
||||
|
@ -305,7 +375,7 @@ static void ati_remote2_input_key(struct ati_remote2 *ar2)
|
|||
ar2->mode = mode;
|
||||
}
|
||||
|
||||
if (!((1 << mode) & mode_mask))
|
||||
if (!((1 << mode) & ar2->mode_mask))
|
||||
return;
|
||||
|
||||
index = ati_remote2_lookup(hw_code);
|
||||
|
@ -410,7 +480,7 @@ static int ati_remote2_getkeycode(struct input_dev *idev,
|
|||
int index, mode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
|
@ -427,7 +497,7 @@ static int ati_remote2_setkeycode(struct input_dev *idev, int scancode, int keyc
|
|||
int index, mode, old_keycode;
|
||||
|
||||
mode = scancode >> 8;
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & mode_mask))
|
||||
if (mode > ATI_REMOTE2_PC || !((1 << mode) & ar2->mode_mask))
|
||||
return -EINVAL;
|
||||
|
||||
index = ati_remote2_lookup(scancode & 0xFF);
|
||||
|
@ -550,7 +620,7 @@ static void ati_remote2_urb_cleanup(struct ati_remote2 *ar2)
|
|||
}
|
||||
}
|
||||
|
||||
static int ati_remote2_setup(struct ati_remote2 *ar2)
|
||||
static int ati_remote2_setup(struct ati_remote2 *ar2, unsigned int ch_mask)
|
||||
{
|
||||
int r, i, channel;
|
||||
|
||||
|
@ -565,8 +635,8 @@ static int ati_remote2_setup(struct ati_remote2 *ar2)
|
|||
|
||||
channel = 0;
|
||||
for (i = 0; i < 16; i++) {
|
||||
if ((1 << i) & channel_mask) {
|
||||
if (!(~(1 << i) & 0xFFFF & channel_mask))
|
||||
if ((1 << i) & ch_mask) {
|
||||
if (!(~(1 << i) & ch_mask))
|
||||
channel = i + 1;
|
||||
break;
|
||||
}
|
||||
|
@ -585,6 +655,99 @@ static int ati_remote2_setup(struct ati_remote2 *ar2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ati_remote2_show_channel_mask(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
|
||||
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
|
||||
|
||||
return sprintf(buf, "0x%04x\n", ar2->channel_mask);
|
||||
}
|
||||
|
||||
static ssize_t ati_remote2_store_channel_mask(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
|
||||
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
|
||||
unsigned long mask;
|
||||
int r;
|
||||
|
||||
if (strict_strtoul(buf, 0, &mask))
|
||||
return -EINVAL;
|
||||
|
||||
if (mask & ~ATI_REMOTE2_MAX_CHANNEL_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
r = usb_autopm_get_interface(ar2->intf[0]);
|
||||
if (r) {
|
||||
dev_err(&ar2->intf[0]->dev,
|
||||
"%s(): usb_autopm_get_interface() = %d\n", __func__, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (mask != ar2->channel_mask && !ati_remote2_setup(ar2, mask))
|
||||
ar2->channel_mask = mask;
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
usb_autopm_put_interface(ar2->intf[0]);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t ati_remote2_show_mode_mask(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
|
||||
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
|
||||
|
||||
return sprintf(buf, "0x%02x\n", ar2->mode_mask);
|
||||
}
|
||||
|
||||
static ssize_t ati_remote2_store_mode_mask(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct usb_device *udev = to_usb_device(dev);
|
||||
struct usb_interface *intf = usb_ifnum_to_if(udev, 0);
|
||||
struct ati_remote2 *ar2 = usb_get_intfdata(intf);
|
||||
unsigned long mask;
|
||||
|
||||
if (strict_strtoul(buf, 0, &mask))
|
||||
return -EINVAL;
|
||||
|
||||
if (mask & ~ATI_REMOTE2_MAX_MODE_MASK)
|
||||
return -EINVAL;
|
||||
|
||||
ar2->mode_mask = mask;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(channel_mask, 0644, ati_remote2_show_channel_mask,
|
||||
ati_remote2_store_channel_mask);
|
||||
|
||||
static DEVICE_ATTR(mode_mask, 0644, ati_remote2_show_mode_mask,
|
||||
ati_remote2_store_mode_mask);
|
||||
|
||||
static struct attribute *ati_remote2_attrs[] = {
|
||||
&dev_attr_channel_mask.attr,
|
||||
&dev_attr_mode_mask.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ati_remote2_attr_group = {
|
||||
.attrs = ati_remote2_attrs,
|
||||
};
|
||||
|
||||
static int ati_remote2_probe(struct usb_interface *interface, const struct usb_device_id *id)
|
||||
{
|
||||
struct usb_device *udev = interface_to_usbdev(interface);
|
||||
|
@ -615,7 +778,10 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
|
|||
if (r)
|
||||
goto fail2;
|
||||
|
||||
r = ati_remote2_setup(ar2);
|
||||
ar2->channel_mask = channel_mask;
|
||||
ar2->mode_mask = mode_mask;
|
||||
|
||||
r = ati_remote2_setup(ar2, ar2->channel_mask);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
|
@ -624,19 +790,24 @@ static int ati_remote2_probe(struct usb_interface *interface, const struct usb_d
|
|||
|
||||
strlcat(ar2->name, "ATI Remote Wonder II", sizeof(ar2->name));
|
||||
|
||||
r = ati_remote2_input_init(ar2);
|
||||
r = sysfs_create_group(&udev->dev.kobj, &ati_remote2_attr_group);
|
||||
if (r)
|
||||
goto fail2;
|
||||
|
||||
r = ati_remote2_input_init(ar2);
|
||||
if (r)
|
||||
goto fail3;
|
||||
|
||||
usb_set_intfdata(interface, ar2);
|
||||
|
||||
interface->needs_remote_wakeup = 1;
|
||||
|
||||
return 0;
|
||||
|
||||
fail3:
|
||||
sysfs_remove_group(&udev->dev.kobj, &ati_remote2_attr_group);
|
||||
fail2:
|
||||
ati_remote2_urb_cleanup(ar2);
|
||||
|
||||
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
|
||||
fail1:
|
||||
kfree(ar2);
|
||||
|
@ -657,6 +828,8 @@ static void ati_remote2_disconnect(struct usb_interface *interface)
|
|||
|
||||
input_unregister_device(ar2->idev);
|
||||
|
||||
sysfs_remove_group(&ar2->udev->dev.kobj, &ati_remote2_attr_group);
|
||||
|
||||
ati_remote2_urb_cleanup(ar2);
|
||||
|
||||
usb_driver_release_interface(&ati_remote2_driver, ar2->intf[1]);
|
||||
|
@ -715,6 +888,78 @@ static int ati_remote2_resume(struct usb_interface *interface)
|
|||
return r;
|
||||
}
|
||||
|
||||
static int ati_remote2_reset_resume(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
int r = 0;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return 0;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
r = ati_remote2_setup(ar2, ar2->channel_mask);
|
||||
if (r)
|
||||
goto out;
|
||||
|
||||
if (ar2->flags & ATI_REMOTE2_OPENED)
|
||||
r = ati_remote2_submit_urbs(ar2);
|
||||
|
||||
if (!r)
|
||||
ar2->flags &= ~ATI_REMOTE2_SUSPENDED;
|
||||
|
||||
out:
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ati_remote2_pre_reset(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return 0;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
mutex_lock(&ati_remote2_mutex);
|
||||
|
||||
if (ar2->flags == ATI_REMOTE2_OPENED)
|
||||
ati_remote2_kill_urbs(ar2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ati_remote2_post_reset(struct usb_interface *interface)
|
||||
{
|
||||
struct ati_remote2 *ar2;
|
||||
struct usb_host_interface *alt = interface->cur_altsetting;
|
||||
int r = 0;
|
||||
|
||||
if (alt->desc.bInterfaceNumber)
|
||||
return 0;
|
||||
|
||||
ar2 = usb_get_intfdata(interface);
|
||||
|
||||
dev_dbg(&ar2->intf[0]->dev, "%s()\n", __func__);
|
||||
|
||||
if (ar2->flags == ATI_REMOTE2_OPENED)
|
||||
r = ati_remote2_submit_urbs(ar2);
|
||||
|
||||
mutex_unlock(&ati_remote2_mutex);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int __init ati_remote2_init(void)
|
||||
{
|
||||
int r;
|
||||
|
|
120
drivers/input/misc/rb532_button.c
Normal file
120
drivers/input/misc/rb532_button.c
Normal file
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Support for the S1 button on Routerboard 532
|
||||
*
|
||||
* Copyright (C) 2009 Phil Sutter <n0-1@freewrt.org>
|
||||
*/
|
||||
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/mach-rc32434/gpio.h>
|
||||
#include <asm/mach-rc32434/rb.h>
|
||||
|
||||
#define DRV_NAME "rb532-button"
|
||||
|
||||
#define RB532_BTN_RATE 100 /* msec */
|
||||
#define RB532_BTN_KSYM BTN_0
|
||||
|
||||
/* The S1 button state is provided by GPIO pin 1. But as this
|
||||
* pin is also used for uart input as alternate function, the
|
||||
* operational modes must be switched first:
|
||||
* 1) disable uart using set_latch_u5()
|
||||
* 2) turn off alternate function implicitly through
|
||||
* gpio_direction_input()
|
||||
* 3) read the GPIO's current value
|
||||
* 4) undo step 2 by enabling alternate function (in this
|
||||
* mode the GPIO direction is fixed, so no change needed)
|
||||
* 5) turn on uart again
|
||||
* The GPIO value occurs to be inverted, so pin high means
|
||||
* button is not pressed.
|
||||
*/
|
||||
static bool rb532_button_pressed(void)
|
||||
{
|
||||
int val;
|
||||
|
||||
set_latch_u5(0, LO_FOFF);
|
||||
gpio_direction_input(GPIO_BTN_S1);
|
||||
|
||||
val = gpio_get_value(GPIO_BTN_S1);
|
||||
|
||||
rb532_gpio_set_func(GPIO_BTN_S1);
|
||||
set_latch_u5(LO_FOFF, 0);
|
||||
|
||||
return !val;
|
||||
}
|
||||
|
||||
static void rb532_button_poll(struct input_polled_dev *poll_dev)
|
||||
{
|
||||
input_report_key(poll_dev->input, RB532_BTN_KSYM,
|
||||
rb532_button_pressed());
|
||||
input_sync(poll_dev->input);
|
||||
}
|
||||
|
||||
static int __devinit rb532_button_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct input_polled_dev *poll_dev;
|
||||
int error;
|
||||
|
||||
poll_dev = input_allocate_polled_device();
|
||||
if (!poll_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
poll_dev->poll = rb532_button_poll;
|
||||
poll_dev->poll_interval = RB532_BTN_RATE;
|
||||
|
||||
poll_dev->input->name = "rb532 button";
|
||||
poll_dev->input->phys = "rb532/button0";
|
||||
poll_dev->input->id.bustype = BUS_HOST;
|
||||
poll_dev->input->dev.parent = &pdev->dev;
|
||||
|
||||
dev_set_drvdata(&pdev->dev, poll_dev);
|
||||
|
||||
input_set_capability(poll_dev->input, EV_KEY, RB532_BTN_KSYM);
|
||||
|
||||
error = input_register_polled_device(poll_dev);
|
||||
if (error) {
|
||||
input_free_polled_device(poll_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit rb532_button_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct input_polled_dev *poll_dev = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
input_unregister_polled_device(poll_dev);
|
||||
input_free_polled_device(poll_dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rb532_button_driver = {
|
||||
.probe = rb532_button_probe,
|
||||
.remove = __devexit_p(rb532_button_remove),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init rb532_button_init(void)
|
||||
{
|
||||
return platform_driver_register(&rb532_button_driver);
|
||||
}
|
||||
|
||||
static void __exit rb532_button_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rb532_button_driver);
|
||||
}
|
||||
|
||||
module_init(rb532_button_init);
|
||||
module_exit(rb532_button_exit);
|
||||
|
||||
MODULE_AUTHOR("Phil Sutter <n0-1@freewrt.org>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Support for S1 button on Routerboard 532");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
221
drivers/input/misc/rotary_encoder.c
Normal file
221
drivers/input/misc/rotary_encoder.c
Normal file
|
@ -0,0 +1,221 @@
|
|||
/*
|
||||
* rotary_encoder.c
|
||||
*
|
||||
* (c) 2009 Daniel Mack <daniel@caiaq.de>
|
||||
*
|
||||
* state machine code inspired by code from Tim Ruetz
|
||||
*
|
||||
* A generic driver for rotary encoders connected to GPIO lines.
|
||||
* See file:Documentation/input/rotary_encoder.txt for more information
|
||||
*
|
||||
* 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/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/rotary_encoder.h>
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct rotary_encoder *encoder = dev_id;
|
||||
struct rotary_encoder_platform_data *pdata = encoder->pdata;
|
||||
int a = !!gpio_get_value(pdata->gpio_a);
|
||||
int b = !!gpio_get_value(pdata->gpio_b);
|
||||
int state;
|
||||
|
||||
a ^= pdata->inverted_a;
|
||||
b ^= pdata->inverted_b;
|
||||
state = (a << 1) | b;
|
||||
|
||||
switch (state) {
|
||||
|
||||
case 0x0:
|
||||
if (!encoder->armed)
|
||||
break;
|
||||
|
||||
if (encoder->dir) {
|
||||
/* turning counter-clockwise */
|
||||
encoder->pos += pdata->steps;
|
||||
encoder->pos--;
|
||||
encoder->pos %= pdata->steps;
|
||||
} else {
|
||||
/* turning clockwise */
|
||||
encoder->pos++;
|
||||
encoder->pos %= pdata->steps;
|
||||
}
|
||||
|
||||
input_report_abs(encoder->input, pdata->axis, encoder->pos);
|
||||
input_sync(encoder->input);
|
||||
|
||||
encoder->armed = 0;
|
||||
break;
|
||||
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
if (encoder->armed)
|
||||
encoder->dir = state - 1;
|
||||
break;
|
||||
|
||||
case 0x3:
|
||||
encoder->armed = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit rotary_encoder_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct rotary_encoder *encoder;
|
||||
struct input_dev *input;
|
||||
int err;
|
||||
|
||||
if (!pdata || !pdata->steps) {
|
||||
dev_err(&pdev->dev, "invalid platform data\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!encoder || !input) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory for device\n");
|
||||
err = -ENOMEM;
|
||||
goto exit_free_mem;
|
||||
}
|
||||
|
||||
encoder->input = input;
|
||||
encoder->pdata = pdata;
|
||||
encoder->irq_a = gpio_to_irq(pdata->gpio_a);
|
||||
encoder->irq_b = gpio_to_irq(pdata->gpio_b);
|
||||
|
||||
/* create and register the input driver */
|
||||
input->name = pdev->name;
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &pdev->dev;
|
||||
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) {
|
||||
dev_err(&pdev->dev, "failed to register input device\n");
|
||||
goto exit_free_mem;
|
||||
}
|
||||
|
||||
/* request the GPIOs */
|
||||
err = gpio_request(pdata->gpio_a, DRV_NAME);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to request GPIO %d\n",
|
||||
pdata->gpio_a);
|
||||
goto exit_unregister_input;
|
||||
}
|
||||
|
||||
err = gpio_request(pdata->gpio_b, DRV_NAME);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to request GPIO %d\n",
|
||||
pdata->gpio_b);
|
||||
goto exit_free_gpio_a;
|
||||
}
|
||||
|
||||
/* request the IRQs */
|
||||
err = request_irq(encoder->irq_a, &rotary_encoder_irq,
|
||||
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
|
||||
DRV_NAME, encoder);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to request IRQ %d\n",
|
||||
encoder->irq_a);
|
||||
goto exit_free_gpio_b;
|
||||
}
|
||||
|
||||
err = request_irq(encoder->irq_b, &rotary_encoder_irq,
|
||||
IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
|
||||
DRV_NAME, encoder);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "unable to request IRQ %d\n",
|
||||
encoder->irq_b);
|
||||
goto exit_free_irq_a;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, encoder);
|
||||
|
||||
return 0;
|
||||
|
||||
exit_free_irq_a:
|
||||
free_irq(encoder->irq_a, encoder);
|
||||
exit_free_gpio_b:
|
||||
gpio_free(pdata->gpio_b);
|
||||
exit_free_gpio_a:
|
||||
gpio_free(pdata->gpio_a);
|
||||
exit_unregister_input:
|
||||
input_unregister_device(input);
|
||||
input = NULL; /* so we don't try to free it */
|
||||
exit_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(encoder);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit rotary_encoder_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rotary_encoder *encoder = platform_get_drvdata(pdev);
|
||||
struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
free_irq(encoder->irq_a, encoder);
|
||||
free_irq(encoder->irq_b, encoder);
|
||||
gpio_free(pdata->gpio_a);
|
||||
gpio_free(pdata->gpio_b);
|
||||
input_unregister_device(encoder->input);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
kfree(encoder);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rotary_encoder_driver = {
|
||||
.probe = rotary_encoder_probe,
|
||||
.remove = __devexit_p(rotary_encoder_remove),
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
static int __init rotary_encoder_init(void)
|
||||
{
|
||||
return platform_driver_register(&rotary_encoder_driver);
|
||||
}
|
||||
|
||||
static void __exit rotary_encoder_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rotary_encoder_driver);
|
||||
}
|
||||
|
||||
module_init(rotary_encoder_init);
|
||||
module_exit(rotary_encoder_exit);
|
||||
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DESCRIPTION("GPIO rotary encoder driver");
|
||||
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -292,4 +292,15 @@ config MOUSE_PXA930_TRKBALL
|
|||
help
|
||||
Say Y here to support PXA930 Trackball mouse.
|
||||
|
||||
config MOUSE_MAPLE
|
||||
tristate "Maple mouse (for the Dreamcast)"
|
||||
depends on MAPLE
|
||||
help
|
||||
This driver supports the Maple mouse on the SEGA Dreamcast.
|
||||
|
||||
Most Dreamcast users, who have a mouse, will say Y here.
|
||||
|
||||
To compile this driver as a module choose M here: the module will be
|
||||
called maplemouse.
|
||||
|
||||
endif
|
||||
|
|
|
@ -6,18 +6,19 @@
|
|||
|
||||
obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
|
||||
obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
|
||||
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
||||
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
|
||||
obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
|
||||
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
|
||||
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||
obj-$(CONFIG_MOUSE_HIL) += hil_ptr.o
|
||||
obj-$(CONFIG_MOUSE_INPORT) += inport.o
|
||||
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
|
||||
obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o
|
||||
obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o
|
||||
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_HIL) += hil_ptr.o
|
||||
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
||||
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
|
||||
|
||||
psmouse-objs := psmouse-base.o synaptics.o
|
||||
|
||||
|
|
|
@ -472,7 +472,7 @@ static enum hgpk_model_t hgpk_get_model(struct psmouse *psmouse)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
hgpk_dbg(psmouse, "ID: %02x %02x %02x", param[0], param[1], param[2]);
|
||||
hgpk_dbg(psmouse, "ID: %02x %02x %02x\n", param[0], param[1], param[2]);
|
||||
|
||||
/* HGPK signature: 0x67, 0x00, 0x<model> */
|
||||
if (param[0] != 0x67 || param[1] != 0x00)
|
||||
|
|
147
drivers/input/mouse/maplemouse.c
Normal file
147
drivers/input/mouse/maplemouse.c
Normal file
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* SEGA Dreamcast mouse driver
|
||||
* Based on drivers/usb/usbmouse.c
|
||||
*
|
||||
* Copyright Yaegashi Takeshi, 2001
|
||||
* Adrian McMenamin, 2008
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/maple.h>
|
||||
|
||||
MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
|
||||
MODULE_DESCRIPTION("SEGA Dreamcast mouse driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct dc_mouse {
|
||||
struct input_dev *dev;
|
||||
struct maple_device *mdev;
|
||||
};
|
||||
|
||||
static void dc_mouse_callback(struct mapleq *mq)
|
||||
{
|
||||
int buttons, relx, rely, relz;
|
||||
struct maple_device *mapledev = mq->dev;
|
||||
struct dc_mouse *mse = maple_get_drvdata(mapledev);
|
||||
struct input_dev *dev = mse->dev;
|
||||
unsigned char *res = mq->recvbuf;
|
||||
|
||||
buttons = ~res[8];
|
||||
relx = *(unsigned short *)(res + 12) - 512;
|
||||
rely = *(unsigned short *)(res + 14) - 512;
|
||||
relz = *(unsigned short *)(res + 16) - 512;
|
||||
|
||||
input_report_key(dev, BTN_LEFT, buttons & 4);
|
||||
input_report_key(dev, BTN_MIDDLE, buttons & 9);
|
||||
input_report_key(dev, BTN_RIGHT, buttons & 2);
|
||||
input_report_rel(dev, REL_X, relx);
|
||||
input_report_rel(dev, REL_Y, rely);
|
||||
input_report_rel(dev, REL_WHEEL, relz);
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static int dc_mouse_open(struct input_dev *dev)
|
||||
{
|
||||
struct dc_mouse *mse = dev->dev.platform_data;
|
||||
|
||||
maple_getcond_callback(mse->mdev, dc_mouse_callback, HZ/50,
|
||||
MAPLE_FUNC_MOUSE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dc_mouse_close(struct input_dev *dev)
|
||||
{
|
||||
struct dc_mouse *mse = dev->dev.platform_data;
|
||||
|
||||
maple_getcond_callback(mse->mdev, dc_mouse_callback, 0,
|
||||
MAPLE_FUNC_MOUSE);
|
||||
}
|
||||
|
||||
|
||||
static int __devinit probe_maple_mouse(struct device *dev)
|
||||
{
|
||||
struct maple_device *mdev = to_maple_dev(dev);
|
||||
struct maple_driver *mdrv = to_maple_driver(dev->driver);
|
||||
struct input_dev *input_dev;
|
||||
struct dc_mouse *mse;
|
||||
int error;
|
||||
|
||||
mse = kzalloc(sizeof(struct dc_mouse), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
|
||||
if (!mse || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
mse->dev = input_dev;
|
||||
mse->mdev = mdev;
|
||||
|
||||
input_set_drvdata(input_dev, mse);
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
|
||||
input_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
|
||||
BIT_MASK(BTN_RIGHT) | BIT_MASK(BTN_MIDDLE);
|
||||
input_dev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
|
||||
BIT_MASK(REL_WHEEL);
|
||||
input_dev->name = mdev->product_name;
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->open = dc_mouse_open;
|
||||
input_dev->close = dc_mouse_close;
|
||||
|
||||
mdev->driver = mdrv;
|
||||
maple_set_drvdata(mdev, mse);
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
input_free_device(input_dev);
|
||||
maple_set_drvdata(mdev, NULL);
|
||||
kfree(mse);
|
||||
mdev->driver = NULL;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit remove_maple_mouse(struct device *dev)
|
||||
{
|
||||
struct maple_device *mdev = to_maple_dev(dev);
|
||||
struct dc_mouse *mse = maple_get_drvdata(mdev);
|
||||
|
||||
mdev->callback = NULL;
|
||||
input_unregister_device(mse->dev);
|
||||
maple_set_drvdata(mdev, NULL);
|
||||
kfree(mse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct maple_driver dc_mouse_driver = {
|
||||
.function = MAPLE_FUNC_MOUSE,
|
||||
.drv = {
|
||||
.name = "Dreamcast_mouse",
|
||||
.probe = probe_maple_mouse,
|
||||
.remove = __devexit_p(remove_maple_mouse),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init dc_mouse_init(void)
|
||||
{
|
||||
return maple_driver_register(&dc_mouse_driver);
|
||||
}
|
||||
|
||||
static void __exit dc_mouse_exit(void)
|
||||
{
|
||||
maple_driver_unregister(&dc_mouse_driver);
|
||||
}
|
||||
|
||||
module_init(dc_mouse_init);
|
||||
module_exit(dc_mouse_exit);
|
|
@ -111,11 +111,8 @@ static int __init pc110pad_init(void)
|
|||
struct pci_dev *dev;
|
||||
int err;
|
||||
|
||||
dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, NULL);
|
||||
if (dev) {
|
||||
pci_dev_put(dev);
|
||||
if (!no_pci_devices())
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!request_region(pc110pad_io, 4, "pc110pad")) {
|
||||
printk(KERN_ERR "pc110pad: I/O area %#x-%#x in use.\n",
|
||||
|
|
|
@ -151,6 +151,14 @@ static struct dmi_system_id __initdata i8042_dmi_noloop_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
|
||||
},
|
||||
},
|
||||
{
|
||||
.ident = "HP DV9700",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
|
|
|
@ -29,6 +29,51 @@ config TOUCHSCREEN_ADS7846
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ads7846.
|
||||
|
||||
config TOUCHSCREEN_AD7877
|
||||
tristate "AD7877 based touchscreens"
|
||||
depends on SPI_MASTER
|
||||
help
|
||||
Say Y here if you have a touchscreen interface using the
|
||||
AD7877 controller, and your board-specific initialization
|
||||
code includes that in its table of SPI devices.
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7877.
|
||||
|
||||
config TOUCHSCREEN_AD7879_I2C
|
||||
tristate "AD7879 based touchscreens: AD7879-1 I2C Interface"
|
||||
depends on I2C
|
||||
select TOUCHSCREEN_AD7879
|
||||
help
|
||||
Say Y here if you have a touchscreen interface using the
|
||||
AD7879-1 controller, and your board-specific initialization
|
||||
code includes that in its table of I2C devices.
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7879.
|
||||
|
||||
config TOUCHSCREEN_AD7879_SPI
|
||||
tristate "AD7879 based touchscreens: AD7879 SPI Interface"
|
||||
depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n
|
||||
select TOUCHSCREEN_AD7879
|
||||
help
|
||||
Say Y here if you have a touchscreen interface using the
|
||||
AD7879 controller, and your board-specific initialization
|
||||
code includes that in its table of SPI devices.
|
||||
|
||||
If unsure, say N (but it's safe to say "Y").
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ad7879.
|
||||
|
||||
config TOUCHSCREEN_AD7879
|
||||
tristate
|
||||
default n
|
||||
|
||||
config TOUCHSCREEN_BITSY
|
||||
tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
|
||||
depends on SA1100_BITSY
|
||||
|
@ -308,6 +353,19 @@ config TOUCHSCREEN_WM97XX_MAINSTONE
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called mainstone-wm97xx.
|
||||
|
||||
config TOUCHSCREEN_WM97XX_ZYLONITE
|
||||
tristate "Zylonite accelerated touch"
|
||||
depends on TOUCHSCREEN_WM97XX && MACH_ZYLONITE
|
||||
select TOUCHSCREEN_WM9713
|
||||
help
|
||||
Say Y here for support for streaming mode with the touchscreen
|
||||
on Zylonite systems.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called zylonite-wm97xx.
|
||||
|
||||
config TOUCHSCREEN_USB_COMPOSITE
|
||||
tristate "USB Touchscreen Driver"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
wm97xx-ts-y := wm97xx-core.o
|
||||
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o
|
||||
|
@ -34,3 +36,4 @@ 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_MAINSTONE) += mainstone-wm97xx.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
|
||||
|
|
844
drivers/input/touchscreen/ad7877.c
Normal file
844
drivers/input/touchscreen/ad7877.c
Normal file
|
@ -0,0 +1,844 @@
|
|||
/*
|
||||
* Copyright (C) 2006-2008 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* Description: AD7877 based touchscreen, sensor (ADCs), DAC and GPIO driver
|
||||
* Based on: ads7846.c
|
||||
*
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* History:
|
||||
* Copyright (c) 2005 David Brownell
|
||||
* Copyright (c) 2006 Nokia Corporation
|
||||
* Various changes: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* Using code from:
|
||||
* - corgi_ts.c
|
||||
* Copyright (C) 2004-2005 Richard Purdie
|
||||
* - omap_ts.[hc], ads7846.h, ts_osk.c
|
||||
* Copyright (C) 2002 MontaVista Software
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
* Copyright (C) 2005 Dirk Behme
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/ad7877.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
|
||||
|
||||
#define MAX_SPI_FREQ_HZ 20000000
|
||||
#define MAX_12BIT ((1<<12)-1)
|
||||
|
||||
#define AD7877_REG_ZEROS 0
|
||||
#define AD7877_REG_CTRL1 1
|
||||
#define AD7877_REG_CTRL2 2
|
||||
#define AD7877_REG_ALERT 3
|
||||
#define AD7877_REG_AUX1HIGH 4
|
||||
#define AD7877_REG_AUX1LOW 5
|
||||
#define AD7877_REG_BAT1HIGH 6
|
||||
#define AD7877_REG_BAT1LOW 7
|
||||
#define AD7877_REG_BAT2HIGH 8
|
||||
#define AD7877_REG_BAT2LOW 9
|
||||
#define AD7877_REG_TEMP1HIGH 10
|
||||
#define AD7877_REG_TEMP1LOW 11
|
||||
#define AD7877_REG_SEQ0 12
|
||||
#define AD7877_REG_SEQ1 13
|
||||
#define AD7877_REG_DAC 14
|
||||
#define AD7877_REG_NONE1 15
|
||||
#define AD7877_REG_EXTWRITE 15
|
||||
#define AD7877_REG_XPLUS 16
|
||||
#define AD7877_REG_YPLUS 17
|
||||
#define AD7877_REG_Z2 18
|
||||
#define AD7877_REG_aux1 19
|
||||
#define AD7877_REG_aux2 20
|
||||
#define AD7877_REG_aux3 21
|
||||
#define AD7877_REG_bat1 22
|
||||
#define AD7877_REG_bat2 23
|
||||
#define AD7877_REG_temp1 24
|
||||
#define AD7877_REG_temp2 25
|
||||
#define AD7877_REG_Z1 26
|
||||
#define AD7877_REG_GPIOCTRL1 27
|
||||
#define AD7877_REG_GPIOCTRL2 28
|
||||
#define AD7877_REG_GPIODATA 29
|
||||
#define AD7877_REG_NONE2 30
|
||||
#define AD7877_REG_NONE3 31
|
||||
|
||||
#define AD7877_SEQ_YPLUS_BIT (1<<11)
|
||||
#define AD7877_SEQ_XPLUS_BIT (1<<10)
|
||||
#define AD7877_SEQ_Z2_BIT (1<<9)
|
||||
#define AD7877_SEQ_AUX1_BIT (1<<8)
|
||||
#define AD7877_SEQ_AUX2_BIT (1<<7)
|
||||
#define AD7877_SEQ_AUX3_BIT (1<<6)
|
||||
#define AD7877_SEQ_BAT1_BIT (1<<5)
|
||||
#define AD7877_SEQ_BAT2_BIT (1<<4)
|
||||
#define AD7877_SEQ_TEMP1_BIT (1<<3)
|
||||
#define AD7877_SEQ_TEMP2_BIT (1<<2)
|
||||
#define AD7877_SEQ_Z1_BIT (1<<1)
|
||||
|
||||
enum {
|
||||
AD7877_SEQ_YPOS = 0,
|
||||
AD7877_SEQ_XPOS = 1,
|
||||
AD7877_SEQ_Z2 = 2,
|
||||
AD7877_SEQ_AUX1 = 3,
|
||||
AD7877_SEQ_AUX2 = 4,
|
||||
AD7877_SEQ_AUX3 = 5,
|
||||
AD7877_SEQ_BAT1 = 6,
|
||||
AD7877_SEQ_BAT2 = 7,
|
||||
AD7877_SEQ_TEMP1 = 8,
|
||||
AD7877_SEQ_TEMP2 = 9,
|
||||
AD7877_SEQ_Z1 = 10,
|
||||
AD7877_NR_SENSE = 11,
|
||||
};
|
||||
|
||||
/* DAC Register Default RANGE 0 to Vcc, Volatge Mode, DAC On */
|
||||
#define AD7877_DAC_CONF 0x1
|
||||
|
||||
/* If gpio3 is set AUX3/GPIO3 acts as GPIO Output */
|
||||
#define AD7877_EXTW_GPIO_3_CONF 0x1C4
|
||||
#define AD7877_EXTW_GPIO_DATA 0x200
|
||||
|
||||
/* Control REG 2 */
|
||||
#define AD7877_TMR(x) ((x & 0x3) << 0)
|
||||
#define AD7877_REF(x) ((x & 0x1) << 2)
|
||||
#define AD7877_POL(x) ((x & 0x1) << 3)
|
||||
#define AD7877_FCD(x) ((x & 0x3) << 4)
|
||||
#define AD7877_PM(x) ((x & 0x3) << 6)
|
||||
#define AD7877_ACQ(x) ((x & 0x3) << 8)
|
||||
#define AD7877_AVG(x) ((x & 0x3) << 10)
|
||||
|
||||
/* Control REG 1 */
|
||||
#define AD7877_SER (1 << 11) /* non-differential */
|
||||
#define AD7877_DFR (0 << 11) /* differential */
|
||||
|
||||
#define AD7877_MODE_NOC (0) /* Do not convert */
|
||||
#define AD7877_MODE_SCC (1) /* Single channel conversion */
|
||||
#define AD7877_MODE_SEQ0 (2) /* Sequence 0 in Slave Mode */
|
||||
#define AD7877_MODE_SEQ1 (3) /* Sequence 1 in Master Mode */
|
||||
|
||||
#define AD7877_CHANADD(x) ((x&0xF)<<7)
|
||||
#define AD7877_READADD(x) ((x)<<2)
|
||||
#define AD7877_WRITEADD(x) ((x)<<12)
|
||||
|
||||
#define AD7877_READ_CHAN(x) (AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_SER | \
|
||||
AD7877_MODE_SCC | AD7877_CHANADD(AD7877_REG_ ## x) | \
|
||||
AD7877_READADD(AD7877_REG_ ## x))
|
||||
|
||||
#define AD7877_MM_SEQUENCE (AD7877_SEQ_YPLUS_BIT | AD7877_SEQ_XPLUS_BIT | \
|
||||
AD7877_SEQ_Z2_BIT | AD7877_SEQ_Z1_BIT)
|
||||
|
||||
/*
|
||||
* Non-touchscreen sensors only use single-ended conversions.
|
||||
*/
|
||||
|
||||
struct ser_req {
|
||||
u16 reset;
|
||||
u16 ref_on;
|
||||
u16 command;
|
||||
u16 sample;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[6];
|
||||
};
|
||||
|
||||
struct ad7877 {
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
|
||||
struct spi_device *spi;
|
||||
u16 model;
|
||||
u16 vref_delay_usecs;
|
||||
u16 x_plate_ohms;
|
||||
u16 pressure_max;
|
||||
|
||||
u16 cmd_crtl1;
|
||||
u16 cmd_crtl2;
|
||||
u16 cmd_dummy;
|
||||
u16 dac;
|
||||
|
||||
u8 stopacq_polarity;
|
||||
u8 first_conversion_delay;
|
||||
u8 acquisition_time;
|
||||
u8 averaging;
|
||||
u8 pen_down_acc_interval;
|
||||
|
||||
u16 conversion_data[AD7877_NR_SENSE];
|
||||
|
||||
struct spi_transfer xfer[AD7877_NR_SENSE + 2];
|
||||
struct spi_message msg;
|
||||
|
||||
struct mutex mutex;
|
||||
unsigned disabled:1; /* P: mutex */
|
||||
unsigned gpio3:1; /* P: mutex */
|
||||
unsigned gpio4:1; /* P: mutex */
|
||||
|
||||
spinlock_t lock;
|
||||
struct timer_list timer; /* P: lock */
|
||||
unsigned pending:1; /* P: lock */
|
||||
};
|
||||
|
||||
static int gpio3;
|
||||
module_param(gpio3, int, 0);
|
||||
MODULE_PARM_DESC(gpio3, "If gpio3 is set to 1 AUX3 acts as GPIO3");
|
||||
|
||||
/*
|
||||
* ad7877_read/write are only used for initial setup and for sysfs controls.
|
||||
* The main traffic is done using spi_async() in the interrupt handler.
|
||||
*/
|
||||
|
||||
static int ad7877_read(struct spi_device *spi, u16 reg)
|
||||
{
|
||||
struct ser_req *req;
|
||||
int status, ret;
|
||||
|
||||
req = kzalloc(sizeof *req, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&req->msg);
|
||||
|
||||
req->command = (u16) (AD7877_WRITEADD(AD7877_REG_CTRL1) |
|
||||
AD7877_READADD(reg));
|
||||
req->xfer[0].tx_buf = &req->command;
|
||||
req->xfer[0].len = 2;
|
||||
|
||||
req->xfer[1].rx_buf = &req->sample;
|
||||
req->xfer[1].len = 2;
|
||||
|
||||
spi_message_add_tail(&req->xfer[0], &req->msg);
|
||||
spi_message_add_tail(&req->xfer[1], &req->msg);
|
||||
|
||||
status = spi_sync(spi, &req->msg);
|
||||
ret = status ? : req->sample;
|
||||
|
||||
kfree(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7877_write(struct spi_device *spi, u16 reg, u16 val)
|
||||
{
|
||||
struct ser_req *req;
|
||||
int status;
|
||||
|
||||
req = kzalloc(sizeof *req, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&req->msg);
|
||||
|
||||
req->command = (u16) (AD7877_WRITEADD(reg) | (val & MAX_12BIT));
|
||||
req->xfer[0].tx_buf = &req->command;
|
||||
req->xfer[0].len = 2;
|
||||
|
||||
spi_message_add_tail(&req->xfer[0], &req->msg);
|
||||
|
||||
status = spi_sync(spi, &req->msg);
|
||||
|
||||
kfree(req);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static int ad7877_read_adc(struct spi_device *spi, unsigned command)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
struct ser_req *req;
|
||||
int status;
|
||||
int sample;
|
||||
int i;
|
||||
|
||||
req = kzalloc(sizeof *req, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&req->msg);
|
||||
|
||||
/* activate reference, so it has time to settle; */
|
||||
req->ref_on = AD7877_WRITEADD(AD7877_REG_CTRL2) |
|
||||
AD7877_POL(ts->stopacq_polarity) |
|
||||
AD7877_AVG(0) | AD7877_PM(2) | AD7877_TMR(0) |
|
||||
AD7877_ACQ(ts->acquisition_time) | AD7877_FCD(0);
|
||||
|
||||
req->reset = AD7877_WRITEADD(AD7877_REG_CTRL1) | AD7877_MODE_NOC;
|
||||
|
||||
req->command = (u16) command;
|
||||
|
||||
req->xfer[0].tx_buf = &req->reset;
|
||||
req->xfer[0].len = 2;
|
||||
|
||||
req->xfer[1].tx_buf = &req->ref_on;
|
||||
req->xfer[1].len = 2;
|
||||
req->xfer[1].delay_usecs = ts->vref_delay_usecs;
|
||||
|
||||
req->xfer[2].tx_buf = &req->command;
|
||||
req->xfer[2].len = 2;
|
||||
req->xfer[2].delay_usecs = ts->vref_delay_usecs;
|
||||
|
||||
req->xfer[3].rx_buf = &req->sample;
|
||||
req->xfer[3].len = 2;
|
||||
|
||||
req->xfer[4].tx_buf = &ts->cmd_crtl2; /*REF OFF*/
|
||||
req->xfer[4].len = 2;
|
||||
|
||||
req->xfer[5].tx_buf = &ts->cmd_crtl1; /*DEFAULT*/
|
||||
req->xfer[5].len = 2;
|
||||
|
||||
/* group all the transfers together, so we can't interfere with
|
||||
* reading touchscreen state; disable penirq while sampling
|
||||
*/
|
||||
for (i = 0; i < 6; i++)
|
||||
spi_message_add_tail(&req->xfer[i], &req->msg);
|
||||
|
||||
status = spi_sync(spi, &req->msg);
|
||||
sample = req->sample;
|
||||
|
||||
kfree(req);
|
||||
|
||||
return status ? : sample;
|
||||
}
|
||||
|
||||
static void ad7877_rx(struct ad7877 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
unsigned Rt;
|
||||
u16 x, y, z1, z2;
|
||||
|
||||
x = ts->conversion_data[AD7877_SEQ_XPOS] & MAX_12BIT;
|
||||
y = ts->conversion_data[AD7877_SEQ_YPOS] & MAX_12BIT;
|
||||
z1 = ts->conversion_data[AD7877_SEQ_Z1] & MAX_12BIT;
|
||||
z2 = ts->conversion_data[AD7877_SEQ_Z2] & MAX_12BIT;
|
||||
|
||||
/*
|
||||
* The samples processed here are already preprocessed by the AD7877.
|
||||
* The preprocessing function consists of an averaging filter.
|
||||
* The combination of 'first conversion delay' and averaging provides a robust solution,
|
||||
* discarding the spurious noise in the signal and keeping only the data of interest.
|
||||
* The size of the averaging filter is programmable. (dev.platform_data, see linux/spi/ad7877.h)
|
||||
* Other user-programmable conversion controls include variable acquisition time,
|
||||
* and first conversion delay. Up to 16 averages can be taken per conversion.
|
||||
*/
|
||||
|
||||
if (likely(x && z1)) {
|
||||
/* compute touch pressure resistance using equation #1 */
|
||||
Rt = (z2 - z1) * x * ts->x_plate_ohms;
|
||||
Rt /= z1;
|
||||
Rt = (Rt + 2047) >> 12;
|
||||
|
||||
input_report_abs(input_dev, ABS_X, x);
|
||||
input_report_abs(input_dev, ABS_Y, y);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, Rt);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ad7877_ts_event_release(struct ad7877 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
|
||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void ad7877_timer(unsigned long handle)
|
||||
{
|
||||
struct ad7877 *ts = (void *)handle;
|
||||
|
||||
ad7877_ts_event_release(ts);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7877_irq(int irq, void *handle)
|
||||
{
|
||||
struct ad7877 *ts = handle;
|
||||
unsigned long flags;
|
||||
int status;
|
||||
|
||||
/*
|
||||
* The repeated conversion sequencer controlled by TMR kicked off
|
||||
* too fast. We ignore the last and process the sample sequence
|
||||
* currently in the queue. It can't be older than 9.4ms, and we
|
||||
* need to avoid that ts->msg doesn't get issued twice while in work.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
if (!ts->pending) {
|
||||
ts->pending = 1;
|
||||
|
||||
status = spi_async(ts->spi, &ts->msg);
|
||||
if (status)
|
||||
dev_err(&ts->spi->dev, "spi_sync --> %d\n", status);
|
||||
}
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7877_callback(void *_ts)
|
||||
{
|
||||
struct ad7877 *ts = _ts;
|
||||
|
||||
spin_lock_irq(&ts->lock);
|
||||
|
||||
ad7877_rx(ts);
|
||||
ts->pending = 0;
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
|
||||
spin_unlock_irq(&ts->lock);
|
||||
}
|
||||
|
||||
static void ad7877_disable(struct ad7877 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (!ts->disabled) {
|
||||
ts->disabled = 1;
|
||||
disable_irq(ts->spi->irq);
|
||||
|
||||
/* Wait for spi_async callback */
|
||||
while (ts->pending)
|
||||
msleep(1);
|
||||
|
||||
if (del_timer_sync(&ts->timer))
|
||||
ad7877_ts_event_release(ts);
|
||||
}
|
||||
|
||||
/* we know the chip's in lowpower mode since we always
|
||||
* leave it that way after every request
|
||||
*/
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
|
||||
static void ad7877_enable(struct ad7877 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (ts->disabled) {
|
||||
ts->disabled = 0;
|
||||
enable_irq(ts->spi->irq);
|
||||
}
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
|
||||
#define SHOW(name) static ssize_t \
|
||||
name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct ad7877 *ts = dev_get_drvdata(dev); \
|
||||
ssize_t v = ad7877_read_adc(ts->spi, \
|
||||
AD7877_READ_CHAN(name)); \
|
||||
if (v < 0) \
|
||||
return v; \
|
||||
return sprintf(buf, "%u\n", (unsigned) v); \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);
|
||||
|
||||
SHOW(aux1)
|
||||
SHOW(aux2)
|
||||
SHOW(aux3)
|
||||
SHOW(bat1)
|
||||
SHOW(bat2)
|
||||
SHOW(temp1)
|
||||
SHOW(temp2)
|
||||
|
||||
static ssize_t ad7877_disable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->disabled);
|
||||
}
|
||||
|
||||
static ssize_t ad7877_disable_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val)
|
||||
ad7877_disable(ts);
|
||||
else
|
||||
ad7877_enable(ts);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(disable, 0664, ad7877_disable_show, ad7877_disable_store);
|
||||
|
||||
static ssize_t ad7877_dac_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->dac);
|
||||
}
|
||||
|
||||
static ssize_t ad7877_dac_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
ts->dac = val & 0xFF;
|
||||
ad7877_write(ts->spi, AD7877_REG_DAC, (ts->dac << 4) | AD7877_DAC_CONF);
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(dac, 0664, ad7877_dac_show, ad7877_dac_store);
|
||||
|
||||
static ssize_t ad7877_gpio3_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio3);
|
||||
}
|
||||
|
||||
static ssize_t ad7877_gpio3_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
ts->gpio3 = !!val;
|
||||
ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
|
||||
(ts->gpio4 << 4) | (ts->gpio3 << 5));
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(gpio3, 0664, ad7877_gpio3_show, ad7877_gpio3_store);
|
||||
|
||||
static ssize_t ad7877_gpio4_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio4);
|
||||
}
|
||||
|
||||
static ssize_t ad7877_gpio4_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
ts->gpio4 = !!val;
|
||||
ad7877_write(ts->spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_DATA |
|
||||
(ts->gpio4 << 4) | (ts->gpio3 << 5));
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(gpio4, 0664, ad7877_gpio4_show, ad7877_gpio4_store);
|
||||
|
||||
static struct attribute *ad7877_attributes[] = {
|
||||
&dev_attr_temp1.attr,
|
||||
&dev_attr_temp2.attr,
|
||||
&dev_attr_aux1.attr,
|
||||
&dev_attr_aux2.attr,
|
||||
&dev_attr_bat1.attr,
|
||||
&dev_attr_bat2.attr,
|
||||
&dev_attr_disable.attr,
|
||||
&dev_attr_dac.attr,
|
||||
&dev_attr_gpio4.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7877_attr_group = {
|
||||
.attrs = ad7877_attributes,
|
||||
};
|
||||
|
||||
static void ad7877_setup_ts_def_msg(struct spi_device *spi, struct ad7877 *ts)
|
||||
{
|
||||
struct spi_message *m;
|
||||
int i;
|
||||
|
||||
ts->cmd_crtl2 = AD7877_WRITEADD(AD7877_REG_CTRL2) |
|
||||
AD7877_POL(ts->stopacq_polarity) |
|
||||
AD7877_AVG(ts->averaging) | AD7877_PM(1) |
|
||||
AD7877_TMR(ts->pen_down_acc_interval) |
|
||||
AD7877_ACQ(ts->acquisition_time) |
|
||||
AD7877_FCD(ts->first_conversion_delay);
|
||||
|
||||
ad7877_write(spi, AD7877_REG_CTRL2, ts->cmd_crtl2);
|
||||
|
||||
ts->cmd_crtl1 = AD7877_WRITEADD(AD7877_REG_CTRL1) |
|
||||
AD7877_READADD(AD7877_REG_XPLUS-1) |
|
||||
AD7877_MODE_SEQ1 | AD7877_DFR;
|
||||
|
||||
ad7877_write(spi, AD7877_REG_CTRL1, ts->cmd_crtl1);
|
||||
|
||||
ts->cmd_dummy = 0;
|
||||
|
||||
m = &ts->msg;
|
||||
|
||||
spi_message_init(m);
|
||||
|
||||
m->complete = ad7877_callback;
|
||||
m->context = ts;
|
||||
|
||||
ts->xfer[0].tx_buf = &ts->cmd_crtl1;
|
||||
ts->xfer[0].len = 2;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[0], m);
|
||||
|
||||
ts->xfer[1].tx_buf = &ts->cmd_dummy; /* Send ZERO */
|
||||
ts->xfer[1].len = 2;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[1], m);
|
||||
|
||||
for (i = 0; i < 11; i++) {
|
||||
ts->xfer[i + 2].rx_buf = &ts->conversion_data[AD7877_SEQ_YPOS + i];
|
||||
ts->xfer[i + 2].len = 2;
|
||||
spi_message_add_tail(&ts->xfer[i + 2], m);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit ad7877_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7877 *ts;
|
||||
struct input_dev *input_dev;
|
||||
struct ad7877_platform_data *pdata = spi->dev.platform_data;
|
||||
int err;
|
||||
u16 verify;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_dbg(&spi->dev, "no IRQ?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_dbg(&spi->dev, "no platform data?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* don't exceed max specified SPI CLK frequency */
|
||||
if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
|
||||
dev_dbg(&spi->dev, "SPI CLK %d Hz?\n",spi->max_speed_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ts = kzalloc(sizeof(struct ad7877), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
dev_set_drvdata(&spi->dev, ts);
|
||||
ts->spi = spi;
|
||||
ts->input = input_dev;
|
||||
|
||||
setup_timer(&ts->timer, ad7877_timer, (unsigned long) ts);
|
||||
mutex_init(&ts->mutex);
|
||||
spin_lock_init(&ts->lock);
|
||||
|
||||
ts->model = pdata->model ? : 7877;
|
||||
ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100;
|
||||
ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
|
||||
ts->pressure_max = pdata->pressure_max ? : ~0;
|
||||
|
||||
ts->stopacq_polarity = pdata->stopacq_polarity;
|
||||
ts->first_conversion_delay = pdata->first_conversion_delay;
|
||||
ts->acquisition_time = pdata->acquisition_time;
|
||||
ts->averaging = pdata->averaging;
|
||||
ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
|
||||
|
||||
input_dev->name = "AD7877 Touchscreen";
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->dev.parent = &spi->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(ABS_X, input_dev->absbit);
|
||||
__set_bit(ABS_Y, input_dev->absbit);
|
||||
__set_bit(ABS_PRESSURE, input_dev->absbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
pdata->x_min ? : 0,
|
||||
pdata->x_max ? : MAX_12BIT,
|
||||
0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
pdata->y_min ? : 0,
|
||||
pdata->y_max ? : MAX_12BIT,
|
||||
0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
pdata->pressure_min, pdata->pressure_max, 0, 0);
|
||||
|
||||
ad7877_write(spi, AD7877_REG_SEQ1, AD7877_MM_SEQUENCE);
|
||||
|
||||
verify = ad7877_read(spi, AD7877_REG_SEQ1);
|
||||
|
||||
if (verify != AD7877_MM_SEQUENCE){
|
||||
dev_err(&spi->dev, "%s: Failed to probe %s\n",
|
||||
dev_name(&spi->dev), input_dev->name);
|
||||
err = -ENODEV;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
if (gpio3)
|
||||
ad7877_write(spi, AD7877_REG_EXTWRITE, AD7877_EXTW_GPIO_3_CONF);
|
||||
|
||||
ad7877_setup_ts_def_msg(spi, ts);
|
||||
|
||||
/* Request AD7877 /DAV GPIO interrupt */
|
||||
|
||||
err = request_irq(spi->irq, ad7877_irq, IRQF_TRIGGER_FALLING |
|
||||
IRQF_SAMPLE_RANDOM, spi->dev.driver->name, ts);
|
||||
if (err) {
|
||||
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
err = device_create_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
if (err)
|
||||
goto err_remove_attr_group;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr;
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_attr:
|
||||
device_remove_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
err_remove_attr_group:
|
||||
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
err_free_irq:
|
||||
free_irq(spi->irq, ts);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(ts);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit ad7877_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &ad7877_attr_group);
|
||||
device_remove_file(&spi->dev,
|
||||
gpio3 ? &dev_attr_gpio3 : &dev_attr_aux3);
|
||||
|
||||
ad7877_disable(ts);
|
||||
free_irq(ts->spi->irq, ts);
|
||||
|
||||
input_unregister_device(ts->input);
|
||||
kfree(ts);
|
||||
|
||||
dev_dbg(&spi->dev, "unregistered touchscreen\n");
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ad7877_suspend(struct spi_device *spi, pm_message_t message)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
ad7877_disable(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7877_resume(struct spi_device *spi)
|
||||
{
|
||||
struct ad7877 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
ad7877_enable(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ad7877_suspend NULL
|
||||
#define ad7877_resume NULL
|
||||
#endif
|
||||
|
||||
static struct spi_driver ad7877_driver = {
|
||||
.driver = {
|
||||
.name = "ad7877",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7877_probe,
|
||||
.remove = __devexit_p(ad7877_remove),
|
||||
.suspend = ad7877_suspend,
|
||||
.resume = ad7877_resume,
|
||||
};
|
||||
|
||||
static int __init ad7877_init(void)
|
||||
{
|
||||
return spi_register_driver(&ad7877_driver);
|
||||
}
|
||||
module_init(ad7877_init);
|
||||
|
||||
static void __exit ad7877_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ad7877_driver);
|
||||
}
|
||||
module_exit(ad7877_exit);
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("AD7877 touchscreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
782
drivers/input/touchscreen/ad7879.c
Normal file
782
drivers/input/touchscreen/ad7879.c
Normal file
|
@ -0,0 +1,782 @@
|
|||
/*
|
||||
* Copyright (C) 2008 Michael Hennerich, Analog Devices Inc.
|
||||
*
|
||||
* Description: AD7879 based touchscreen, and GPIO driver (I2C/SPI Interface)
|
||||
*
|
||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see the file COPYING, or write
|
||||
* to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* History:
|
||||
* Copyright (c) 2005 David Brownell
|
||||
* Copyright (c) 2006 Nokia Corporation
|
||||
* Various changes: Imre Deak <imre.deak@nokia.com>
|
||||
*
|
||||
* Using code from:
|
||||
* - corgi_ts.c
|
||||
* Copyright (C) 2004-2005 Richard Purdie
|
||||
* - omap_ts.[hc], ads7846.h, ts_osk.c
|
||||
* Copyright (C) 2002 MontaVista Software
|
||||
* Copyright (C) 2004 Texas Instruments
|
||||
* Copyright (C) 2005 Dirk Behme
|
||||
* - ad7877.c
|
||||
* Copyright (C) 2006-2008 Analog Devices Inc.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/i2c.h>
|
||||
|
||||
#include <linux/spi/ad7879.h>
|
||||
|
||||
#define AD7879_REG_ZEROS 0
|
||||
#define AD7879_REG_CTRL1 1
|
||||
#define AD7879_REG_CTRL2 2
|
||||
#define AD7879_REG_CTRL3 3
|
||||
#define AD7879_REG_AUX1HIGH 4
|
||||
#define AD7879_REG_AUX1LOW 5
|
||||
#define AD7879_REG_TEMP1HIGH 6
|
||||
#define AD7879_REG_TEMP1LOW 7
|
||||
#define AD7879_REG_XPLUS 8
|
||||
#define AD7879_REG_YPLUS 9
|
||||
#define AD7879_REG_Z1 10
|
||||
#define AD7879_REG_Z2 11
|
||||
#define AD7879_REG_AUXVBAT 12
|
||||
#define AD7879_REG_TEMP 13
|
||||
#define AD7879_REG_REVID 14
|
||||
|
||||
/* Control REG 1 */
|
||||
#define AD7879_TMR(x) ((x & 0xFF) << 0)
|
||||
#define AD7879_ACQ(x) ((x & 0x3) << 8)
|
||||
#define AD7879_MODE_NOC (0 << 10) /* Do not convert */
|
||||
#define AD7879_MODE_SCC (1 << 10) /* Single channel conversion */
|
||||
#define AD7879_MODE_SEQ0 (2 << 10) /* Sequence 0 in Slave Mode */
|
||||
#define AD7879_MODE_SEQ1 (3 << 10) /* Sequence 1 in Master Mode */
|
||||
#define AD7879_MODE_INT (1 << 15) /* PENIRQ disabled INT enabled */
|
||||
|
||||
/* Control REG 2 */
|
||||
#define AD7879_FCD(x) ((x & 0x3) << 0)
|
||||
#define AD7879_RESET (1 << 4)
|
||||
#define AD7879_MFS(x) ((x & 0x3) << 5)
|
||||
#define AD7879_AVG(x) ((x & 0x3) << 7)
|
||||
#define AD7879_SER (1 << 9) /* non-differential */
|
||||
#define AD7879_DFR (0 << 9) /* differential */
|
||||
#define AD7879_GPIOPOL (1 << 10)
|
||||
#define AD7879_GPIODIR (1 << 11)
|
||||
#define AD7879_GPIO_DATA (1 << 12)
|
||||
#define AD7879_GPIO_EN (1 << 13)
|
||||
#define AD7879_PM(x) ((x & 0x3) << 14)
|
||||
#define AD7879_PM_SHUTDOWN (0)
|
||||
#define AD7879_PM_DYN (1)
|
||||
#define AD7879_PM_FULLON (2)
|
||||
|
||||
/* Control REG 3 */
|
||||
#define AD7879_TEMPMASK_BIT (1<<15)
|
||||
#define AD7879_AUXVBATMASK_BIT (1<<14)
|
||||
#define AD7879_INTMODE_BIT (1<<13)
|
||||
#define AD7879_GPIOALERTMASK_BIT (1<<12)
|
||||
#define AD7879_AUXLOW_BIT (1<<11)
|
||||
#define AD7879_AUXHIGH_BIT (1<<10)
|
||||
#define AD7879_TEMPLOW_BIT (1<<9)
|
||||
#define AD7879_TEMPHIGH_BIT (1<<8)
|
||||
#define AD7879_YPLUS_BIT (1<<7)
|
||||
#define AD7879_XPLUS_BIT (1<<6)
|
||||
#define AD7879_Z1_BIT (1<<5)
|
||||
#define AD7879_Z2_BIT (1<<4)
|
||||
#define AD7879_AUX_BIT (1<<3)
|
||||
#define AD7879_VBAT_BIT (1<<2)
|
||||
#define AD7879_TEMP_BIT (1<<1)
|
||||
|
||||
enum {
|
||||
AD7879_SEQ_XPOS = 0,
|
||||
AD7879_SEQ_YPOS = 1,
|
||||
AD7879_SEQ_Z1 = 2,
|
||||
AD7879_SEQ_Z2 = 3,
|
||||
AD7879_NR_SENSE = 4,
|
||||
};
|
||||
|
||||
#define MAX_12BIT ((1<<12)-1)
|
||||
#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
|
||||
#define AD7879_DEVID 0x7A
|
||||
typedef struct spi_device bus_device;
|
||||
#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE)
|
||||
#define AD7879_DEVID 0x79
|
||||
typedef struct i2c_client bus_device;
|
||||
#endif
|
||||
|
||||
struct ad7879 {
|
||||
bus_device *bus;
|
||||
struct input_dev *input;
|
||||
struct work_struct work;
|
||||
struct timer_list timer;
|
||||
|
||||
struct mutex mutex;
|
||||
unsigned disabled:1; /* P: mutex */
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[AD7879_NR_SENSE + 1];
|
||||
u16 cmd;
|
||||
#endif
|
||||
u16 conversion_data[AD7879_NR_SENSE];
|
||||
char phys[32];
|
||||
u8 first_conversion_delay;
|
||||
u8 acquisition_time;
|
||||
u8 averaging;
|
||||
u8 pen_down_acc_interval;
|
||||
u8 median;
|
||||
u16 x_plate_ohms;
|
||||
u16 pressure_max;
|
||||
u16 gpio_init;
|
||||
u16 cmd_crtl1;
|
||||
u16 cmd_crtl2;
|
||||
u16 cmd_crtl3;
|
||||
unsigned gpio:1;
|
||||
};
|
||||
|
||||
static int ad7879_read(bus_device *, u8);
|
||||
static int ad7879_write(bus_device *, u8, u16);
|
||||
static void ad7879_collect(struct ad7879 *);
|
||||
|
||||
static void ad7879_report(struct ad7879 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
unsigned Rt;
|
||||
u16 x, y, z1, z2;
|
||||
|
||||
x = ts->conversion_data[AD7879_SEQ_XPOS] & MAX_12BIT;
|
||||
y = ts->conversion_data[AD7879_SEQ_YPOS] & MAX_12BIT;
|
||||
z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;
|
||||
z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT;
|
||||
|
||||
/*
|
||||
* The samples processed here are already preprocessed by the AD7879.
|
||||
* The preprocessing function consists of a median and an averaging filter.
|
||||
* The combination of these two techniques provides a robust solution,
|
||||
* discarding the spurious noise in the signal and keeping only the data of interest.
|
||||
* The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h)
|
||||
* Other user-programmable conversion controls include variable acquisition time,
|
||||
* and first conversion delay. Up to 16 averages can be taken per conversion.
|
||||
*/
|
||||
|
||||
if (likely(x && z1)) {
|
||||
/* compute touch pressure resistance using equation #1 */
|
||||
Rt = (z2 - z1) * x * ts->x_plate_ohms;
|
||||
Rt /= z1;
|
||||
Rt = (Rt + 2047) >> 12;
|
||||
|
||||
input_report_abs(input_dev, ABS_X, x);
|
||||
input_report_abs(input_dev, ABS_Y, y);
|
||||
input_report_abs(input_dev, ABS_PRESSURE, Rt);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void ad7879_work(struct work_struct *work)
|
||||
{
|
||||
struct ad7879 *ts = container_of(work, struct ad7879, work);
|
||||
|
||||
/* use keventd context to read the result registers */
|
||||
ad7879_collect(ts);
|
||||
ad7879_report(ts);
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
}
|
||||
|
||||
static void ad7879_ts_event_release(struct ad7879 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
|
||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void ad7879_timer(unsigned long handle)
|
||||
{
|
||||
struct ad7879 *ts = (void *)handle;
|
||||
|
||||
ad7879_ts_event_release(ts);
|
||||
}
|
||||
|
||||
static irqreturn_t ad7879_irq(int irq, void *handle)
|
||||
{
|
||||
struct ad7879 *ts = handle;
|
||||
|
||||
/* The repeated conversion sequencer controlled by TMR kicked off too fast.
|
||||
* We ignore the last and process the sample sequence currently in the queue.
|
||||
* It can't be older than 9.4ms
|
||||
*/
|
||||
|
||||
if (!work_pending(&ts->work))
|
||||
schedule_work(&ts->work);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void ad7879_setup(struct ad7879 *ts)
|
||||
{
|
||||
ts->cmd_crtl3 = AD7879_YPLUS_BIT |
|
||||
AD7879_XPLUS_BIT |
|
||||
AD7879_Z2_BIT |
|
||||
AD7879_Z1_BIT |
|
||||
AD7879_TEMPMASK_BIT |
|
||||
AD7879_AUXVBATMASK_BIT |
|
||||
AD7879_GPIOALERTMASK_BIT;
|
||||
|
||||
ts->cmd_crtl2 = AD7879_PM(AD7879_PM_DYN) | AD7879_DFR |
|
||||
AD7879_AVG(ts->averaging) |
|
||||
AD7879_MFS(ts->median) |
|
||||
AD7879_FCD(ts->first_conversion_delay) |
|
||||
ts->gpio_init;
|
||||
|
||||
ts->cmd_crtl1 = AD7879_MODE_INT | AD7879_MODE_SEQ1 |
|
||||
AD7879_ACQ(ts->acquisition_time) |
|
||||
AD7879_TMR(ts->pen_down_acc_interval);
|
||||
|
||||
ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2);
|
||||
ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3);
|
||||
ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1);
|
||||
}
|
||||
|
||||
static void ad7879_disable(struct ad7879 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (!ts->disabled) {
|
||||
|
||||
ts->disabled = 1;
|
||||
disable_irq(ts->bus->irq);
|
||||
|
||||
cancel_work_sync(&ts->work);
|
||||
|
||||
if (del_timer_sync(&ts->timer))
|
||||
ad7879_ts_event_release(ts);
|
||||
|
||||
ad7879_write(ts->bus, AD7879_REG_CTRL2,
|
||||
AD7879_PM(AD7879_PM_SHUTDOWN));
|
||||
}
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
|
||||
static void ad7879_enable(struct ad7879 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (ts->disabled) {
|
||||
ad7879_setup(ts);
|
||||
ts->disabled = 0;
|
||||
enable_irq(ts->bus->irq);
|
||||
}
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
|
||||
static ssize_t ad7879_disable_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->disabled);
|
||||
}
|
||||
|
||||
static ssize_t ad7879_disable_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val)
|
||||
ad7879_disable(ts);
|
||||
else
|
||||
ad7879_enable(ts);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(disable, 0664, ad7879_disable_show, ad7879_disable_store);
|
||||
|
||||
static ssize_t ad7879_gpio_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%u\n", ts->gpio);
|
||||
}
|
||||
|
||||
static ssize_t ad7879_gpio_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
||||
unsigned long val;
|
||||
int error;
|
||||
|
||||
error = strict_strtoul(buf, 10, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
ts->gpio = !!val;
|
||||
error = ad7879_write(ts->bus, AD7879_REG_CTRL2,
|
||||
ts->gpio ?
|
||||
ts->cmd_crtl2 & ~AD7879_GPIO_DATA :
|
||||
ts->cmd_crtl2 | AD7879_GPIO_DATA);
|
||||
mutex_unlock(&ts->mutex);
|
||||
|
||||
return error ? : count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(gpio, 0664, ad7879_gpio_show, ad7879_gpio_store);
|
||||
|
||||
static struct attribute *ad7879_attributes[] = {
|
||||
&dev_attr_disable.attr,
|
||||
&dev_attr_gpio.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static const struct attribute_group ad7879_attr_group = {
|
||||
.attrs = ad7879_attributes,
|
||||
};
|
||||
|
||||
static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)
|
||||
{
|
||||
struct input_dev *input_dev;
|
||||
struct ad7879_platform_data *pdata = bus->dev.platform_data;
|
||||
int err;
|
||||
u16 revid;
|
||||
|
||||
if (!bus->irq) {
|
||||
dev_err(&bus->dev, "no IRQ?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&bus->dev, "no platform data?\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->input = input_dev;
|
||||
|
||||
setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
|
||||
INIT_WORK(&ts->work, ad7879_work);
|
||||
mutex_init(&ts->mutex);
|
||||
|
||||
ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
|
||||
ts->pressure_max = pdata->pressure_max ? : ~0;
|
||||
|
||||
ts->first_conversion_delay = pdata->first_conversion_delay;
|
||||
ts->acquisition_time = pdata->acquisition_time;
|
||||
ts->averaging = pdata->averaging;
|
||||
ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
|
||||
ts->median = pdata->median;
|
||||
|
||||
if (pdata->gpio_output)
|
||||
ts->gpio_init = AD7879_GPIO_EN |
|
||||
(pdata->gpio_default ? 0 : AD7879_GPIO_DATA);
|
||||
else
|
||||
ts->gpio_init = AD7879_GPIO_EN | AD7879_GPIODIR;
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev));
|
||||
|
||||
input_dev->name = "AD7879 Touchscreen";
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->dev.parent = &bus->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(ABS_X, input_dev->absbit);
|
||||
__set_bit(ABS_Y, input_dev->absbit);
|
||||
__set_bit(ABS_PRESSURE, input_dev->absbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
pdata->x_min ? : 0,
|
||||
pdata->x_max ? : MAX_12BIT,
|
||||
0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
pdata->y_min ? : 0,
|
||||
pdata->y_max ? : MAX_12BIT,
|
||||
0, 0);
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||
pdata->pressure_min, pdata->pressure_max, 0, 0);
|
||||
|
||||
err = ad7879_write(bus, AD7879_REG_CTRL2, AD7879_RESET);
|
||||
|
||||
if (err < 0) {
|
||||
dev_err(&bus->dev, "Failed to write %s\n", input_dev->name);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
revid = ad7879_read(bus, AD7879_REG_REVID);
|
||||
|
||||
if ((revid & 0xFF) != AD7879_DEVID) {
|
||||
dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name);
|
||||
err = -ENODEV;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ad7879_setup(ts);
|
||||
|
||||
err = request_irq(bus->irq, ad7879_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_SAMPLE_RANDOM,
|
||||
bus->dev.driver->name, ts);
|
||||
|
||||
if (err) {
|
||||
dev_err(&bus->dev, "irq %d busy?\n", bus->irq);
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group);
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr;
|
||||
|
||||
dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n",
|
||||
revid >> 8, bus->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_attr:
|
||||
sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group);
|
||||
err_free_irq:
|
||||
free_irq(bus->irq, ts);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts)
|
||||
{
|
||||
ad7879_disable(ts);
|
||||
sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group);
|
||||
free_irq(ts->bus->irq, ts);
|
||||
input_unregister_device(ts->input);
|
||||
dev_dbg(&bus->dev, "unregistered touchscreen\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ad7879_suspend(bus_device *bus, pm_message_t message)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(&bus->dev);
|
||||
|
||||
ad7879_disable(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7879_resume(bus_device *bus)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(&bus->dev);
|
||||
|
||||
ad7879_enable(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ad7879_suspend NULL
|
||||
#define ad7879_resume NULL
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_TOUCHSCREEN_AD7879_SPI) || defined(CONFIG_TOUCHSCREEN_AD7879_SPI_MODULE)
|
||||
#define MAX_SPI_FREQ_HZ 5000000
|
||||
#define AD7879_CMD_MAGIC 0xE000
|
||||
#define AD7879_CMD_READ (1 << 10)
|
||||
#define AD7879_WRITECMD(reg) (AD7879_CMD_MAGIC | (reg & 0xF))
|
||||
#define AD7879_READCMD(reg) (AD7879_CMD_MAGIC | AD7879_CMD_READ | (reg & 0xF))
|
||||
|
||||
struct ser_req {
|
||||
u16 command;
|
||||
u16 data;
|
||||
struct spi_message msg;
|
||||
struct spi_transfer xfer[2];
|
||||
};
|
||||
|
||||
/*
|
||||
* ad7879_read/write are only used for initial setup and for sysfs controls.
|
||||
* The main traffic is done in ad7879_collect().
|
||||
*/
|
||||
|
||||
static int ad7879_read(struct spi_device *spi, u8 reg)
|
||||
{
|
||||
struct ser_req *req;
|
||||
int status, ret;
|
||||
|
||||
req = kzalloc(sizeof *req, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&req->msg);
|
||||
|
||||
req->command = (u16) AD7879_READCMD(reg);
|
||||
req->xfer[0].tx_buf = &req->command;
|
||||
req->xfer[0].len = 2;
|
||||
|
||||
req->xfer[1].rx_buf = &req->data;
|
||||
req->xfer[1].len = 2;
|
||||
|
||||
spi_message_add_tail(&req->xfer[0], &req->msg);
|
||||
spi_message_add_tail(&req->xfer[1], &req->msg);
|
||||
|
||||
status = spi_sync(spi, &req->msg);
|
||||
ret = status ? : req->data;
|
||||
|
||||
kfree(req);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ad7879_write(struct spi_device *spi, u8 reg, u16 val)
|
||||
{
|
||||
struct ser_req *req;
|
||||
int status;
|
||||
|
||||
req = kzalloc(sizeof *req, GFP_KERNEL);
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_message_init(&req->msg);
|
||||
|
||||
req->command = (u16) AD7879_WRITECMD(reg);
|
||||
req->xfer[0].tx_buf = &req->command;
|
||||
req->xfer[0].len = 2;
|
||||
|
||||
req->data = val;
|
||||
req->xfer[1].tx_buf = &req->data;
|
||||
req->xfer[1].len = 2;
|
||||
|
||||
spi_message_add_tail(&req->xfer[0], &req->msg);
|
||||
spi_message_add_tail(&req->xfer[1], &req->msg);
|
||||
|
||||
status = spi_sync(spi, &req->msg);
|
||||
|
||||
kfree(req);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void ad7879_collect(struct ad7879 *ts)
|
||||
{
|
||||
int status = spi_sync(ts->bus, &ts->msg);
|
||||
|
||||
if (status)
|
||||
dev_err(&ts->bus->dev, "spi_sync --> %d\n", status);
|
||||
}
|
||||
|
||||
static void ad7879_setup_ts_def_msg(struct ad7879 *ts)
|
||||
{
|
||||
struct spi_message *m;
|
||||
int i;
|
||||
|
||||
ts->cmd = (u16) AD7879_READCMD(AD7879_REG_XPLUS);
|
||||
|
||||
m = &ts->msg;
|
||||
spi_message_init(m);
|
||||
ts->xfer[0].tx_buf = &ts->cmd;
|
||||
ts->xfer[0].len = 2;
|
||||
|
||||
spi_message_add_tail(&ts->xfer[0], m);
|
||||
|
||||
for (i = 0; i < AD7879_NR_SENSE; i++) {
|
||||
ts->xfer[i + 1].rx_buf = &ts->conversion_data[i];
|
||||
ts->xfer[i + 1].len = 2;
|
||||
spi_message_add_tail(&ts->xfer[i + 1], m);
|
||||
}
|
||||
}
|
||||
|
||||
static int __devinit ad7879_probe(struct spi_device *spi)
|
||||
{
|
||||
struct ad7879 *ts;
|
||||
int error;
|
||||
|
||||
/* don't exceed max specified SPI CLK frequency */
|
||||
if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
|
||||
dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&spi->dev, ts);
|
||||
ts->bus = spi;
|
||||
|
||||
ad7879_setup_ts_def_msg(ts);
|
||||
|
||||
error = ad7879_construct(spi, ts);
|
||||
if (error) {
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(ts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit ad7879_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
ad7879_destroy(spi, ts);
|
||||
dev_set_drvdata(&spi->dev, NULL);
|
||||
kfree(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct spi_driver ad7879_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7879_probe,
|
||||
.remove = __devexit_p(ad7879_remove),
|
||||
.suspend = ad7879_suspend,
|
||||
.resume = ad7879_resume,
|
||||
};
|
||||
|
||||
static int __init ad7879_init(void)
|
||||
{
|
||||
return spi_register_driver(&ad7879_driver);
|
||||
}
|
||||
module_init(ad7879_init);
|
||||
|
||||
static void __exit ad7879_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ad7879_driver);
|
||||
}
|
||||
module_exit(ad7879_exit);
|
||||
|
||||
#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE)
|
||||
|
||||
/* All registers are word-sized.
|
||||
* AD7879 uses a high-byte first convention.
|
||||
*/
|
||||
static int ad7879_read(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return swab16(i2c_smbus_read_word_data(client, reg));
|
||||
}
|
||||
|
||||
static int ad7879_write(struct i2c_client *client, u8 reg, u16 val)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, swab16(val));
|
||||
}
|
||||
|
||||
static void ad7879_collect(struct ad7879 *ts)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AD7879_NR_SENSE; i++)
|
||||
ts->conversion_data[i] = ad7879_read(ts->bus,
|
||||
AD7879_REG_XPLUS + i);
|
||||
}
|
||||
|
||||
static int __devinit ad7879_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ad7879 *ts;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_err(&client->dev, "SMBUS Word Data not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
ts->bus = client;
|
||||
|
||||
error = ad7879_construct(client, ts);
|
||||
if (error) {
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(ts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit ad7879_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ad7879 *ts = dev_get_drvdata(&client->dev);
|
||||
|
||||
ad7879_destroy(client, ts);
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id ad7879_id[] = {
|
||||
{ "ad7879", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ad7879_id);
|
||||
|
||||
static struct i2c_driver ad7879_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7879_probe,
|
||||
.remove = __devexit_p(ad7879_remove),
|
||||
.suspend = ad7879_suspend,
|
||||
.resume = ad7879_resume,
|
||||
.id_table = ad7879_id,
|
||||
};
|
||||
|
||||
static int __init ad7879_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ad7879_driver);
|
||||
}
|
||||
module_init(ad7879_init);
|
||||
|
||||
static void __exit ad7879_exit(void)
|
||||
{
|
||||
i2c_del_driver(&ad7879_driver);
|
||||
}
|
||||
module_exit(ad7879_exit);
|
||||
#endif
|
||||
|
||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||
MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -162,6 +162,7 @@ static int wm97xx_acc_pen_down(struct wm97xx *wm)
|
|||
input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
|
||||
input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
|
||||
input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
|
||||
input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
|
||||
input_sync(wm->input_dev);
|
||||
reads++;
|
||||
} while (reads < cinfo[sp_idx].reads);
|
||||
|
@ -245,7 +246,7 @@ static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
|
|||
if (enable)
|
||||
enable_irq(wm->pen_irq);
|
||||
else
|
||||
disable_irq(wm->pen_irq);
|
||||
disable_irq_nosync(wm->pen_irq);
|
||||
}
|
||||
|
||||
static struct wm97xx_mach_ops mainstone_mach_ops = {
|
||||
|
|
|
@ -151,12 +151,14 @@ static void ucb1400_ts_evt_add(struct input_dev *idev, u16 pressure, u16 x, u16
|
|||
input_report_abs(idev, ABS_X, x);
|
||||
input_report_abs(idev, ABS_Y, y);
|
||||
input_report_abs(idev, ABS_PRESSURE, pressure);
|
||||
input_report_key(idev, BTN_TOUCH, 1);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
static void ucb1400_ts_event_release(struct input_dev *idev)
|
||||
{
|
||||
input_report_abs(idev, ABS_PRESSURE, 0);
|
||||
input_report_key(idev, BTN_TOUCH, 0);
|
||||
input_sync(idev);
|
||||
}
|
||||
|
||||
|
@ -377,7 +379,8 @@ static int ucb1400_ts_probe(struct platform_device *dev)
|
|||
ucb->ts_idev->id.product = ucb->id;
|
||||
ucb->ts_idev->open = ucb1400_ts_open;
|
||||
ucb->ts_idev->close = ucb1400_ts_close;
|
||||
ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS);
|
||||
ucb->ts_idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY);
|
||||
ucb->ts_idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
|
||||
ucb1400_adc_enable(ucb->ac97);
|
||||
x_res = ucb1400_ts_read_xres(ucb);
|
||||
|
|
|
@ -409,6 +409,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)
|
|||
wm->pen_is_down = 0;
|
||||
dev_dbg(wm->dev, "pen up\n");
|
||||
input_report_abs(wm->input_dev, ABS_PRESSURE, 0);
|
||||
input_report_key(wm->input_dev, BTN_TOUCH, 0);
|
||||
input_sync(wm->input_dev);
|
||||
} else if (!(rc & RC_AGAIN)) {
|
||||
/* We need high frequency updates only while
|
||||
|
@ -433,6 +434,7 @@ static int wm97xx_read_samples(struct wm97xx *wm)
|
|||
input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff);
|
||||
input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff);
|
||||
input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff);
|
||||
input_report_key(wm->input_dev, BTN_TOUCH, 1);
|
||||
input_sync(wm->input_dev);
|
||||
wm->pen_is_down = 1;
|
||||
wm->ts_reader_interval = wm->ts_reader_min_interval;
|
||||
|
@ -628,18 +630,21 @@ static int wm97xx_probe(struct device *dev)
|
|||
wm->input_dev->phys = "wm97xx";
|
||||
wm->input_dev->open = wm97xx_ts_input_open;
|
||||
wm->input_dev->close = wm97xx_ts_input_close;
|
||||
set_bit(EV_ABS, wm->input_dev->evbit);
|
||||
set_bit(ABS_X, wm->input_dev->absbit);
|
||||
set_bit(ABS_Y, wm->input_dev->absbit);
|
||||
set_bit(ABS_PRESSURE, wm->input_dev->absbit);
|
||||
|
||||
__set_bit(EV_ABS, wm->input_dev->evbit);
|
||||
__set_bit(EV_KEY, wm->input_dev->evbit);
|
||||
__set_bit(BTN_TOUCH, wm->input_dev->keybit);
|
||||
|
||||
input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1],
|
||||
abs_x[2], 0);
|
||||
input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1],
|
||||
abs_y[2], 0);
|
||||
input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1],
|
||||
abs_p[2], 0);
|
||||
|
||||
input_set_drvdata(wm->input_dev, wm);
|
||||
wm->input_dev->dev.parent = dev;
|
||||
|
||||
ret = input_register_device(wm->input_dev);
|
||||
if (ret < 0)
|
||||
goto dev_alloc_err;
|
||||
|
|
240
drivers/input/touchscreen/zylonite-wm97xx.c
Normal file
240
drivers/input/touchscreen/zylonite-wm97xx.c
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* zylonite-wm97xx.c -- Zylonite Continuous Touch screen driver
|
||||
*
|
||||
* Copyright 2004, 2007, 2008 Wolfson Microelectronics PLC.
|
||||
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
||||
* Parts Copyright : Ian Molton <spyro@f2s.com>
|
||||
* Andrew Zabolotny <zap@homelink.ru>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* Notes:
|
||||
* This is a wm97xx extended touch driver supporting interrupt driven
|
||||
* and continuous operation on Marvell Zylonite development systems
|
||||
* (which have a WM9713 on board).
|
||||
*/
|
||||
|
||||
#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/io.h>
|
||||
#include <linux/wm97xx.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/mfp.h>
|
||||
#include <mach/regs-ac97.h>
|
||||
|
||||
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[] = {
|
||||
{ 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 = 200;
|
||||
module_param(cont_rate, int, 0);
|
||||
MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)");
|
||||
|
||||
/*
|
||||
* 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");
|
||||
|
||||
|
||||
/* flush AC97 slot 5 FIFO machines */
|
||||
static void wm97xx_acc_pen_up(struct wm97xx *wm)
|
||||
{
|
||||
int i;
|
||||
|
||||
msleep(1);
|
||||
|
||||
for (i = 0; i < 16; i++)
|
||||
MODR;
|
||||
}
|
||||
|
||||
static int wm97xx_acc_pen_down(struct wm97xx *wm)
|
||||
{
|
||||
u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES;
|
||||
int reads = 0;
|
||||
static u16 last, tries;
|
||||
|
||||
/* When the AC97 queue has been drained we need to allow time
|
||||
* to buffer up samples otherwise we end up spinning polling
|
||||
* for samples. The controller can't have a suitably low
|
||||
* threashold set to use the notifications it gives.
|
||||
*/
|
||||
msleep(1);
|
||||
|
||||
if (tries > 5) {
|
||||
tries = 0;
|
||||
return RC_PENUP;
|
||||
}
|
||||
|
||||
x = MODR;
|
||||
if (x == last) {
|
||||
tries++;
|
||||
return RC_AGAIN;
|
||||
}
|
||||
last = x;
|
||||
do {
|
||||
if (reads)
|
||||
x = MODR;
|
||||
y = MODR;
|
||||
if (pressure)
|
||||
p = MODR;
|
||||
|
||||
/* are samples valid */
|
||||
if ((x & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_X ||
|
||||
(y & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_Y ||
|
||||
(p & WM97XX_ADCSRC_MASK) != WM97XX_ADCSEL_PRES)
|
||||
goto up;
|
||||
|
||||
/* coordinate is good */
|
||||
tries = 0;
|
||||
input_report_abs(wm->input_dev, ABS_X, x & 0xfff);
|
||||
input_report_abs(wm->input_dev, ABS_Y, y & 0xfff);
|
||||
input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff);
|
||||
input_report_key(wm->input_dev, BTN_TOUCH, (p != 0));
|
||||
input_sync(wm->input_dev);
|
||||
reads++;
|
||||
} while (reads < cinfo[sp_idx].reads);
|
||||
up:
|
||||
return RC_PENDOWN | RC_AGAIN;
|
||||
}
|
||||
|
||||
static int wm97xx_acc_startup(struct wm97xx *wm)
|
||||
{
|
||||
int idx;
|
||||
|
||||
/* check we have a codec */
|
||||
if (wm->ac97 == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
/* Go you big red fire engine */
|
||||
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->dev,
|
||||
"zylonite accelerated touchscreen driver, %d samples/sec\n",
|
||||
cinfo[sp_idx].speed);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wm97xx_irq_enable(struct wm97xx *wm, int enable)
|
||||
{
|
||||
if (enable)
|
||||
enable_irq(wm->pen_irq);
|
||||
else
|
||||
disable_irq_nosync(wm->pen_irq);
|
||||
}
|
||||
|
||||
static struct wm97xx_mach_ops zylonite_mach_ops = {
|
||||
.acc_enabled = 1,
|
||||
.acc_pen_up = wm97xx_acc_pen_up,
|
||||
.acc_pen_down = wm97xx_acc_pen_down,
|
||||
.acc_startup = wm97xx_acc_startup,
|
||||
.irq_enable = wm97xx_irq_enable,
|
||||
.irq_gpio = WM97XX_GPIO_2,
|
||||
};
|
||||
|
||||
static int zylonite_wm97xx_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct wm97xx *wm = platform_get_drvdata(pdev);
|
||||
int gpio_touch_irq;
|
||||
|
||||
if (cpu_is_pxa320())
|
||||
gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO15);
|
||||
else
|
||||
gpio_touch_irq = mfp_to_gpio(MFP_PIN_GPIO26);
|
||||
|
||||
wm->pen_irq = IRQ_GPIO(gpio_touch_irq);
|
||||
set_irq_type(IRQ_GPIO(gpio_touch_irq), IRQ_TYPE_EDGE_BOTH);
|
||||
|
||||
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_2, WM97XX_GPIO_OUT,
|
||||
WM97XX_GPIO_POL_HIGH,
|
||||
WM97XX_GPIO_NOTSTICKY,
|
||||
WM97XX_GPIO_NOWAKE);
|
||||
|
||||
return wm97xx_register_mach_ops(wm, &zylonite_mach_ops);
|
||||
}
|
||||
|
||||
static int zylonite_wm97xx_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct wm97xx *wm = platform_get_drvdata(pdev);
|
||||
|
||||
wm97xx_unregister_mach_ops(wm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver zylonite_wm97xx_driver = {
|
||||
.probe = zylonite_wm97xx_probe,
|
||||
.remove = zylonite_wm97xx_remove,
|
||||
.driver = {
|
||||
.name = "wm97xx-touch",
|
||||
},
|
||||
};
|
||||
|
||||
static int __init zylonite_wm97xx_init(void)
|
||||
{
|
||||
return platform_driver_register(&zylonite_wm97xx_driver);
|
||||
}
|
||||
|
||||
static void __exit zylonite_wm97xx_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&zylonite_wm97xx_driver);
|
||||
}
|
||||
|
||||
module_init(zylonite_wm97xx_init);
|
||||
module_exit(zylonite_wm97xx_exit);
|
||||
|
||||
/* Module information */
|
||||
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
|
||||
MODULE_DESCRIPTION("wm97xx continuous touch driver for Zylonite");
|
||||
MODULE_LICENSE("GPL");
|
13
include/linux/rotary_encoder.h
Normal file
13
include/linux/rotary_encoder.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef __ROTARY_ENCODER_H__
|
||||
#define __ROTARY_ENCODER_H__
|
||||
|
||||
struct rotary_encoder_platform_data {
|
||||
unsigned int steps;
|
||||
unsigned int axis;
|
||||
unsigned int gpio_a;
|
||||
unsigned int gpio_b;
|
||||
unsigned int inverted_a;
|
||||
unsigned int inverted_b;
|
||||
};
|
||||
|
||||
#endif /* __ROTARY_ENCODER_H__ */
|
35
include/linux/spi/ad7879.h
Normal file
35
include/linux/spi/ad7879.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* linux/spi/ad7879.h */
|
||||
|
||||
/* Touchscreen characteristics vary between boards and models. The
|
||||
* platform_data for the device's "struct device" holds this information.
|
||||
*
|
||||
* It's OK if the min/max values are zero.
|
||||
*/
|
||||
struct ad7879_platform_data {
|
||||
u16 model; /* 7879 */
|
||||
u16 x_plate_ohms;
|
||||
u16 x_min, x_max;
|
||||
u16 y_min, y_max;
|
||||
u16 pressure_min, pressure_max;
|
||||
|
||||
/* [0..255] 0=OFF Starts at 1=550us and goes
|
||||
* all the way to 9.440ms in steps of 35us.
|
||||
*/
|
||||
u8 pen_down_acc_interval;
|
||||
/* [0..15] Starts at 0=128us and goes all the
|
||||
* way to 4.096ms in steps of 128us.
|
||||
*/
|
||||
u8 first_conversion_delay;
|
||||
/* [0..3] 0 = 2us, 1 = 4us, 2 = 8us, 3 = 16us */
|
||||
u8 acquisition_time;
|
||||
/* [0..3] Average X middle samples 0 = 2, 1 = 4, 2 = 8, 3 = 16 */
|
||||
u8 averaging;
|
||||
/* [0..3] Perform X measurements 0 = OFF,
|
||||
* 1 = 4, 2 = 8, 3 = 16 (median > averaging)
|
||||
*/
|
||||
u8 median;
|
||||
/* 1 = AUX/VBAT/GPIO set to GPIO Output */
|
||||
u8 gpio_output;
|
||||
/* Initial GPIO pin state (valid if gpio_output = 1) */
|
||||
u8 gpio_default;
|
||||
};
|
Loading…
Reference in a new issue