Commit ba28f22e authored by Dmitry Torokhov's avatar Dmitry Torokhov

Merge branch 'next' into for-linus

parents 577c9c45 59cc1dd9
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 *));
......@@ -834,87 +835,64 @@ static void atkbd_disconnect(struct serio *serio)
}
/*
* Most special keys (Fn+F?) on Dell laptops do not generate release
* events so we have to do it ourselves.
* generate release events for the keycodes given in data
*/
static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd)
static void atkbd_apply_forced_release_keylist(struct atkbd* atkbd,
const void *data)
{
static const unsigned int forced_release_keys[] = {
0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93,
};
int i;
const unsigned int *keys = data;
unsigned 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);
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 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:
err2:
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);
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;
return 0;
}
#if defined(CONFIG_PARISC)
unregister_parisc_driver(&hil_driver);
#else
release_region(HILBASE+HIL_DATA, 2);
#endif
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;
......
/*
* 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);
/*
* 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)
......
/*
* 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
/*
* 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");
/*
* 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;
......
/*
* 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");
#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__ */
/* 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;
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment