diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-08-02 18:35:17 -0700 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2010-08-02 18:35:17 -0700 |
commit | d01d0756f75e7a5b4b43764ad45b83c4340f11d6 (patch) | |
tree | 90db2ff7ccb35a8fdcf98366e6404afe1f845bc4 | |
parent | b326b853dca2f410b254198ee89abad71a2f4668 (diff) | |
parent | 0d87c7228a49e8342d60dd552892e470e0b291fa (diff) | |
download | kernel_samsung_tuna-d01d0756f75e7a5b4b43764ad45b83c4340f11d6.zip kernel_samsung_tuna-d01d0756f75e7a5b4b43764ad45b83c4340f11d6.tar.gz kernel_samsung_tuna-d01d0756f75e7a5b4b43764ad45b83c4340f11d6.tar.bz2 |
Merge branch 'next' into for-linus
56 files changed, 6495 insertions, 983 deletions
diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt index c0fc1c7..bdcba154 100644 --- a/Documentation/input/multi-touch-protocol.txt +++ b/Documentation/input/multi-touch-protocol.txt @@ -6,31 +6,149 @@ Multi-touch (MT) Protocol Introduction ------------ -In order to utilize the full power of the new multi-touch devices, a way to -report detailed finger data to user space is needed. This document -describes the multi-touch (MT) protocol which allows kernel drivers to -report details for an arbitrary number of fingers. +In order to utilize the full power of the new multi-touch and multi-user +devices, a way to report detailed data from multiple contacts, i.e., +objects in direct contact with the device surface, is needed. This +document describes the multi-touch (MT) protocol which allows kernel +drivers to report details for an arbitrary number of contacts. + +The protocol is divided into two types, depending on the capabilities of the +hardware. For devices handling anonymous contacts (type A), the protocol +describes how to send the raw data for all contacts to the receiver. For +devices capable of tracking identifiable contacts (type B), the protocol +describes how to send updates for individual contacts via event slots. + + +Protocol Usage +-------------- + +Contact details are sent sequentially as separate packets of ABS_MT +events. Only the ABS_MT events are recognized as part of a contact +packet. Since these events are ignored by current single-touch (ST) +applications, the MT protocol can be implemented on top of the ST protocol +in an existing driver. + +Drivers for type A devices separate contact packets by calling +input_mt_sync() at the end of each packet. This generates a SYN_MT_REPORT +event, which instructs the receiver to accept the data for the current +contact and prepare to receive another. + +Drivers for type B devices separate contact packets by calling +input_mt_slot(), with a slot as argument, at the beginning of each packet. +This generates an ABS_MT_SLOT event, which instructs the receiver to +prepare for updates of the given slot. + +All drivers mark the end of a multi-touch transfer by calling the usual +input_sync() function. This instructs the receiver to act upon events +accumulated since last EV_SYN/SYN_REPORT and prepare to receive a new set +of events/packets. + +The main difference between the stateless type A protocol and the stateful +type B slot protocol lies in the usage of identifiable contacts to reduce +the amount of data sent to userspace. The slot protocol requires the use of +the ABS_MT_TRACKING_ID, either provided by the hardware or computed from +the raw data [5]. + +For type A devices, the kernel driver should generate an arbitrary +enumeration of the full set of anonymous contacts currently on the +surface. The order in which the packets appear in the event stream is not +important. Event filtering and finger tracking is left to user space [3]. + +For type B devices, the kernel driver should associate a slot with each +identified contact, and use that slot to propagate changes for the contact. +Creation, replacement and destruction of contacts is achieved by modifying +the ABS_MT_TRACKING_ID of the associated slot. A non-negative tracking id +is interpreted as a contact, and the value -1 denotes an unused slot. A +tracking id not previously present is considered new, and a tracking id no +longer present is considered removed. Since only changes are propagated, +the full state of each initiated contact has to reside in the receiving +end. Upon receiving an MT event, one simply updates the appropriate +attribute of the current slot. + + +Protocol Example A +------------------ + +Here is what a minimal event sequence for a two-contact touch would look +like for a type A device: + + ABS_MT_POSITION_X x[0] + ABS_MT_POSITION_Y y[0] + SYN_MT_REPORT + ABS_MT_POSITION_X x[1] + ABS_MT_POSITION_Y y[1] + SYN_MT_REPORT + SYN_REPORT +The sequence after moving one of the contacts looks exactly the same; the +raw data for all present contacts are sent between every synchronization +with SYN_REPORT. -Usage ------ +Here is the sequence after lifting the first contact: + + ABS_MT_POSITION_X x[1] + ABS_MT_POSITION_Y y[1] + SYN_MT_REPORT + SYN_REPORT + +And here is the sequence after lifting the second contact: + + SYN_MT_REPORT + SYN_REPORT + +If the driver reports one of BTN_TOUCH or ABS_PRESSURE in addition to the +ABS_MT events, the last SYN_MT_REPORT event may be omitted. Otherwise, the +last SYN_REPORT will be dropped by the input core, resulting in no +zero-contact event reaching userland. -Anonymous finger details are sent sequentially as separate packets of ABS -events. Only the ABS_MT events are recognized as part of a finger -packet. The end of a packet is marked by calling the input_mt_sync() -function, which generates a SYN_MT_REPORT event. This instructs the -receiver to accept the data for the current finger and prepare to receive -another. The end of a multi-touch transfer is marked by calling the usual -input_sync() function. This instructs the receiver to act upon events -accumulated since last EV_SYN/SYN_REPORT and prepare to receive a new -set of events/packets. + +Protocol Example B +------------------ + +Here is what a minimal event sequence for a two-contact touch would look +like for a type B device: + + ABS_MT_SLOT 0 + ABS_MT_TRACKING_ID 45 + ABS_MT_POSITION_X x[0] + ABS_MT_POSITION_Y y[0] + ABS_MT_SLOT 1 + ABS_MT_TRACKING_ID 46 + ABS_MT_POSITION_X x[1] + ABS_MT_POSITION_Y y[1] + SYN_REPORT + +Here is the sequence after moving contact 45 in the x direction: + + ABS_MT_SLOT 0 + ABS_MT_POSITION_X x[0] + SYN_REPORT + +Here is the sequence after lifting the contact in slot 0: + + ABS_MT_TRACKING_ID -1 + SYN_REPORT + +The slot being modified is already 0, so the ABS_MT_SLOT is omitted. The +message removes the association of slot 0 with contact 45, thereby +destroying contact 45 and freeing slot 0 to be reused for another contact. + +Finally, here is the sequence after lifting the second contact: + + ABS_MT_SLOT 1 + ABS_MT_TRACKING_ID -1 + SYN_REPORT + + +Event Usage +----------- A set of ABS_MT events with the desired properties is defined. The events are divided into categories, to allow for partial implementation. The minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which -allows for multiple fingers to be tracked. If the device supports it, the +allows for multiple contacts to be tracked. If the device supports it, the ABS_MT_TOUCH_MAJOR and ABS_MT_WIDTH_MAJOR may be used to provide the size -of the contact area and approaching finger, respectively. +of the contact area and approaching contact, respectively. The TOUCH and WIDTH parameters have a geometrical interpretation; imagine looking through a window at someone gently holding a finger against the @@ -41,56 +159,26 @@ ABS_MT_TOUCH_MAJOR, the diameter of the outer region is ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger harder against the glass. The inner region will increase, and in general, the ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than -unity, is related to the finger pressure. For pressure-based devices, +unity, is related to the contact pressure. For pressure-based devices, ABS_MT_PRESSURE may be used to provide the pressure on the contact area instead. -In addition to the MAJOR parameters, the oval shape of the finger can be +In addition to the MAJOR parameters, the oval shape of the contact can be described by adding the MINOR parameters, such that MAJOR and MINOR are the major and minor axis of an ellipse. Finally, the orientation of the oval shape can be describe with the ORIENTATION parameter. The ABS_MT_TOOL_TYPE may be used to specify whether the touching tool is a -finger or a pen or something else. Devices with more granular information +contact or a pen or something else. Devices with more granular information may specify general shapes as blobs, i.e., as a sequence of rectangular shapes grouped together by an ABS_MT_BLOB_ID. Finally, for the few devices that currently support it, the ABS_MT_TRACKING_ID event may be used to -report finger tracking from hardware [5]. +report contact tracking from hardware [5]. -Here is what a minimal event sequence for a two-finger touch would look -like: - - ABS_MT_POSITION_X - ABS_MT_POSITION_Y - SYN_MT_REPORT - ABS_MT_POSITION_X - ABS_MT_POSITION_Y - SYN_MT_REPORT - SYN_REPORT - -Here is the sequence after lifting one of the fingers: - - ABS_MT_POSITION_X - ABS_MT_POSITION_Y - SYN_MT_REPORT - SYN_REPORT - -And here is the sequence after lifting the remaining finger: - - SYN_MT_REPORT - SYN_REPORT - -If the driver reports one of BTN_TOUCH or ABS_PRESSURE in addition to the -ABS_MT events, the last SYN_MT_REPORT event may be omitted. Otherwise, the -last SYN_REPORT will be dropped by the input core, resulting in no -zero-finger event reaching userland. Event Semantics --------------- -The word "contact" is used to describe a tool which is in direct contact -with the surface. A finger, a pen or a rubber all classify as contacts. - ABS_MT_TOUCH_MAJOR The length of the major axis of the contact. The length should be given in @@ -157,15 +245,16 @@ MT_TOOL_PEN [2]. ABS_MT_BLOB_ID The BLOB_ID groups several packets together into one arbitrarily shaped -contact. This is a low-level anonymous grouping, and should not be confused -with the high-level trackingID [5]. Most kernel drivers will not have blob -capability, and can safely omit the event. +contact. This is a low-level anonymous grouping for type A devices, and +should not be confused with the high-level trackingID [5]. Most type A +devices do not have blob capability, so drivers can safely omit this event. ABS_MT_TRACKING_ID The TRACKING_ID identifies an initiated contact throughout its life cycle -[5]. There are currently only a few devices that support it, so this event -should normally be omitted. +[5]. This event is mandatory for type B devices. The value range of the +TRACKING_ID should be large enough to ensure unique identification of a +contact maintained over an extended period of time. Event Computation @@ -192,20 +281,11 @@ finger along the X axis (1). Finger Tracking --------------- -The kernel driver should generate an arbitrary enumeration of the set of -anonymous contacts currently on the surface. The order in which the packets -appear in the event stream is not important. - The process of finger tracking, i.e., to assign a unique trackingID to each -initiated contact on the surface, is left to user space; preferably the -multi-touch X driver [3]. In that driver, the trackingID stays the same and -unique until the contact vanishes (when the finger leaves the surface). The -problem of assigning a set of anonymous fingers to a set of identified -fingers is a euclidian bipartite matching problem at each event update, and -relies on a sufficiently rapid update rate. - -There are a few devices that support trackingID in hardware. User space can -make use of these native identifiers to reduce bandwidth and cpu usage. +initiated contact on the surface, is a Euclidian Bipartite Matching +problem. At each event synchronization, the set of actual contacts is +matched to the set of contacts from the previous synchronization. A full +implementation can be found in [3]. Gestures diff --git a/arch/arm/plat-samsung/include/plat/keypad.h b/arch/arm/plat-samsung/include/plat/keypad.h new file mode 100644 index 0000000..3a70c12 --- /dev/null +++ b/arch/arm/plat-samsung/include/plat/keypad.h @@ -0,0 +1,43 @@ +/* + * Samsung Platform - Keypad platform data definitions + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __PLAT_SAMSUNG_KEYPAD_H +#define __PLAT_SAMSUNG_KEYPAD_H + +#include <linux/input/matrix_keypad.h> + +#define SAMSUNG_MAX_ROWS 8 +#define SAMSUNG_MAX_COLS 8 + +/** + * struct samsung_keypad_platdata - Platform device data for Samsung Keypad. + * @keymap_data: pointer to &matrix_keymap_data. + * @rows: number of keypad row supported. + * @cols: number of keypad col supported. + * @no_autorepeat: disable key autorepeat. + * @wakeup: controls whether the device should be set up as wakeup source. + * @cfg_gpio: configure the GPIO. + * + * Initialisation data specific to either the machine or the platform + * for the device driver to use or call-back when configuring gpio. + */ +struct samsung_keypad_platdata { + const struct matrix_keymap_data *keymap_data; + unsigned int rows; + unsigned int cols; + bool no_autorepeat; + bool wakeup; + + void (*cfg_gpio)(unsigned int rows, unsigned int cols); +}; + +#endif /* __PLAT_SAMSUNG_KEYPAD_H */ diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 54109dc..25be210 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -1315,10 +1315,14 @@ static bool kbd_match(struct input_handler *handler, struct input_dev *dev) if (test_bit(EV_SND, dev->evbit)) return true; - if (test_bit(EV_KEY, dev->evbit)) + if (test_bit(EV_KEY, dev->evbit)) { for (i = KEY_RESERVED; i < BTN_MISC; i++) if (test_bit(i, dev->keybit)) return true; + for (i = KEY_BRL_DOT1; i <= KEY_BRL_DOT10; i++) + if (test_bit(i, dev->keybit)) + return true; + } return false; } diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index aa0f7dc..a35f8be 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1568,6 +1568,7 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) }, { HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) }, { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) }, + { HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0001) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) }, { HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 6af77ed..fad4cb0 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -197,6 +197,7 @@ #define USB_VENDOR_ID_ETT 0x0664 #define USB_DEVICE_ID_TC5UH 0x0309 +#define USB_DEVICE_ID_TC4UM 0x0306 #define USB_VENDOR_ID_EZKEY 0x0518 #define USB_DEVICE_ID_BTC_8193 0x0002 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 7a0d2e4..69d152e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -534,6 +534,9 @@ mapped: input_set_abs_params(input, usage->code, a, b, (b - a) >> 8, (b - a) >> 4); else input_set_abs_params(input, usage->code, a, b, 0, 0); + /* use a larger default input buffer for MT devices */ + if (usage->code == ABS_MT_POSITION_X && input->hint_events_per_packet == 0) + input_set_events_per_packet(input, 60); } if (usage->type == EV_ABS && diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c index 2ee6c7a..054edf3 100644 --- a/drivers/input/evdev.c +++ b/drivers/input/evdev.c @@ -10,7 +10,8 @@ #define EVDEV_MINOR_BASE 64 #define EVDEV_MINORS 32 -#define EVDEV_BUFFER_SIZE 64 +#define EVDEV_MIN_BUFFER_SIZE 64U +#define EVDEV_BUF_PACKETS 8 #include <linux/poll.h> #include <linux/sched.h> @@ -23,7 +24,6 @@ #include "input-compat.h" struct evdev { - int exist; int open; int minor; struct input_handle handle; @@ -33,16 +33,18 @@ struct evdev { spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + bool exist; }; struct evdev_client { - struct input_event buffer[EVDEV_BUFFER_SIZE]; int head; int tail; spinlock_t buffer_lock; /* protects access to buffer, head and tail */ struct fasync_struct *fasync; struct evdev *evdev; struct list_head node; + int bufsize; + struct input_event buffer[]; }; static struct evdev *evdev_table[EVDEV_MINORS]; @@ -52,11 +54,15 @@ static void evdev_pass_event(struct evdev_client *client, struct input_event *event) { /* - * Interrupts are disabled, just acquire the lock + * Interrupts are disabled, just acquire the lock. + * Make sure we don't leave with the client buffer + * "empty" by having client->head == client->tail. */ spin_lock(&client->buffer_lock); - client->buffer[client->head++] = *event; - client->head &= EVDEV_BUFFER_SIZE - 1; + do { + client->buffer[client->head++] = *event; + client->head &= client->bufsize - 1; + } while (client->head == client->tail); spin_unlock(&client->buffer_lock); if (event->type == EV_SYN) @@ -242,11 +248,21 @@ static int evdev_release(struct inode *inode, struct file *file) return 0; } +static unsigned int evdev_compute_buffer_size(struct input_dev *dev) +{ + unsigned int n_events = + max(dev->hint_events_per_packet * EVDEV_BUF_PACKETS, + EVDEV_MIN_BUFFER_SIZE); + + return roundup_pow_of_two(n_events); +} + static int evdev_open(struct inode *inode, struct file *file) { struct evdev *evdev; struct evdev_client *client; int i = iminor(inode) - EVDEV_MINOR_BASE; + unsigned int bufsize; int error; if (i >= EVDEV_MINORS) @@ -263,12 +279,17 @@ static int evdev_open(struct inode *inode, struct file *file) if (!evdev) return -ENODEV; - client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL); + bufsize = evdev_compute_buffer_size(evdev->handle.dev); + + client = kzalloc(sizeof(struct evdev_client) + + bufsize * sizeof(struct input_event), + GFP_KERNEL); if (!client) { error = -ENOMEM; goto err_put_evdev; } + client->bufsize = bufsize; spin_lock_init(&client->buffer_lock); client->evdev = evdev; evdev_attach_client(evdev, client); @@ -334,7 +355,7 @@ static int evdev_fetch_next_event(struct evdev_client *client, have_event = client->head != client->tail; if (have_event) { *event = client->buffer[client->tail++]; - client->tail &= EVDEV_BUFFER_SIZE - 1; + client->tail &= client->bufsize - 1; } spin_unlock_irq(&client->buffer_lock); @@ -382,10 +403,15 @@ static unsigned int evdev_poll(struct file *file, poll_table *wait) { struct evdev_client *client = file->private_data; struct evdev *evdev = client->evdev; + unsigned int mask; poll_wait(file, &evdev->wait, wait); - return ((client->head == client->tail) ? 0 : (POLLIN | POLLRDNORM)) | - (evdev->exist ? 0 : (POLLHUP | POLLERR)); + + mask = evdev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; + if (client->head != client->tail) + mask |= POLLIN | POLLRDNORM; + + return mask; } #ifdef CONFIG_COMPAT @@ -665,6 +691,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd, sizeof(struct input_absinfo)))) return -EFAULT; + /* We can't change number of reserved MT slots */ + if (t == ABS_MT_SLOT) + return -EINVAL; + /* * Take event lock to ensure that we are not * changing device parameters in the middle @@ -768,7 +798,7 @@ static void evdev_remove_chrdev(struct evdev *evdev) static void evdev_mark_dead(struct evdev *evdev) { mutex_lock(&evdev->mutex); - evdev->exist = 0; + evdev->exist = false; mutex_unlock(&evdev->mutex); } @@ -817,7 +847,7 @@ static int evdev_connect(struct input_handler *handler, struct input_dev *dev, init_waitqueue_head(&evdev->wait); dev_set_name(&evdev->dev, "event%d", minor); - evdev->exist = 1; + evdev->exist = true; evdev->minor = minor; evdev->handle.dev = input_get_device(dev); diff --git a/drivers/input/input.c b/drivers/input/input.c index 9c79bd5..e1243b4 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -33,25 +33,6 @@ MODULE_LICENSE("GPL"); #define INPUT_DEVICES 256 -/* - * EV_ABS events which should not be cached are listed here. - */ -static unsigned int input_abs_bypass_init_data[] __initdata = { - ABS_MT_TOUCH_MAJOR, - ABS_MT_TOUCH_MINOR, - ABS_MT_WIDTH_MAJOR, - ABS_MT_WIDTH_MINOR, - ABS_MT_ORIENTATION, - ABS_MT_POSITION_X, - ABS_MT_POSITION_Y, - ABS_MT_TOOL_TYPE, - ABS_MT_BLOB_ID, - ABS_MT_TRACKING_ID, - ABS_MT_PRESSURE, - 0 -}; -static unsigned long input_abs_bypass[BITS_TO_LONGS(ABS_CNT)]; - static LIST_HEAD(input_dev_list); static LIST_HEAD(input_handler_list); @@ -181,6 +162,56 @@ static void input_stop_autorepeat(struct input_dev *dev) #define INPUT_PASS_TO_DEVICE 2 #define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE) +static int input_handle_abs_event(struct input_dev *dev, + unsigned int code, int *pval) +{ + bool is_mt_event; + int *pold; + + if (code == ABS_MT_SLOT) { + /* + * "Stage" the event; we'll flush it later, when we + * get actiual touch data. + */ + if (*pval >= 0 && *pval < dev->mtsize) + dev->slot = *pval; + + return INPUT_IGNORE_EVENT; + } + + is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + + if (!is_mt_event) { + pold = &dev->abs[code]; + } else if (dev->mt) { + struct input_mt_slot *mtslot = &dev->mt[dev->slot]; + pold = &mtslot->abs[code - ABS_MT_FIRST]; + } else { + /* + * Bypass filtering for multitouch events when + * not employing slots. + */ + pold = NULL; + } + + if (pold) { + *pval = input_defuzz_abs_event(*pval, *pold, + dev->absfuzz[code]); + if (*pold == *pval) + return INPUT_IGNORE_EVENT; + + *pold = *pval; + } + + /* Flush pending "slot" event */ + if (is_mt_event && dev->slot != dev->abs[ABS_MT_SLOT]) { + dev->abs[ABS_MT_SLOT] = dev->slot; + input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot); + } + + return INPUT_PASS_TO_HANDLERS; +} + static void input_handle_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { @@ -196,12 +227,12 @@ static void input_handle_event(struct input_dev *dev, case SYN_REPORT: if (!dev->sync) { - dev->sync = 1; + dev->sync = true; disposition = INPUT_PASS_TO_HANDLERS; } break; case SYN_MT_REPORT: - dev->sync = 0; + dev->sync = false; disposition = INPUT_PASS_TO_HANDLERS; break; } @@ -233,21 +264,9 @@ static void input_handle_event(struct input_dev *dev, break; case EV_ABS: - if (is_event_supported(code, dev->absbit, ABS_MAX)) { - - if (test_bit(code, input_abs_bypass)) { - disposition = INPUT_PASS_TO_HANDLERS; - break; - } + if (is_event_supported(code, dev->absbit, ABS_MAX)) + disposition = input_handle_abs_event(dev, code, &value); - value = input_defuzz_abs_event(value, - dev->abs[code], dev->absfuzz[code]); - - if (dev->abs[code] != value) { - dev->abs[code] = value; - disposition = INPUT_PASS_TO_HANDLERS; - } - } break; case EV_REL: @@ -298,7 +317,7 @@ static void input_handle_event(struct input_dev *dev, } if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN) - dev->sync = 0; + dev->sync = false; if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event) dev->event(dev, type, code, value); @@ -528,12 +547,30 @@ void input_close_device(struct input_handle *handle) EXPORT_SYMBOL(input_close_device); /* + * Simulate keyup events for all keys that are marked as pressed. + * The function must be called with dev->event_lock held. + */ +static void input_dev_release_keys(struct input_dev *dev) +{ + int code; + + if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { + for (code = 0; code <= KEY_MAX; code++) { + if (is_event_supported(code, dev->keybit, KEY_MAX) && + __test_and_clear_bit(code, dev->key)) { + input_pass_event(dev, EV_KEY, code, 0); + } + } + input_pass_event(dev, EV_SYN, SYN_REPORT, 1); + } +} + +/* * Prepare device for unregistering */ static void input_disconnect_device(struct input_dev *dev) { struct input_handle *handle; - int code; /* * Mark device as going away. Note that we take dev->mutex here @@ -552,15 +589,7 @@ static void input_disconnect_device(struct input_dev *dev) * generate events even after we done here but they will not * reach any handlers. */ - if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) { - for (code = 0; code <= KEY_MAX; code++) { - if (is_event_supported(code, dev->keybit, KEY_MAX) && - __test_and_clear_bit(code, dev->key)) { - input_pass_event(dev, EV_KEY, code, 0); - } - } - input_pass_event(dev, EV_SYN, SYN_REPORT, 1); - } + input_dev_release_keys(dev); list_for_each_entry(handle, &dev->h_list, d_node) handle->open = 0; @@ -684,7 +713,7 @@ int input_set_keycode(struct input_dev *dev, unsigned int scancode, unsigned int keycode) { unsigned long flags; - int old_keycode; + unsigned int old_keycode; int retval; if (keycode > KEY_MAX) @@ -1278,6 +1307,7 @@ static void input_dev_release(struct device *device) struct input_dev *dev = to_input_dev(device); input_ff_destroy(dev); + input_mt_destroy_slots(dev); kfree(dev); module_put(THIS_MODULE); @@ -1433,6 +1463,15 @@ static int input_dev_resume(struct device *dev) mutex_lock(&input_dev->mutex); input_dev_reset(input_dev, true); + + /* + * Keys that have been pressed at suspend time are unlikely + * to be still pressed when we resume. + */ + spin_lock_irq(&input_dev->event_lock); + input_dev_release_keys(input_dev); + spin_unlock_irq(&input_dev->event_lock); + mutex_unlock(&input_dev->mutex); return 0; @@ -1518,6 +1557,45 @@ void input_free_device(struct input_dev *dev) EXPORT_SYMBOL(input_free_device); /** + * input_mt_create_slots() - create MT input slots + * @dev: input device supporting MT events and finger tracking + * @num_slots: number of slots used by the device + * + * This function allocates all necessary memory for MT slot handling + * in the input device, and adds ABS_MT_SLOT to the device capabilities. + */ +int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots) +{ + if (!num_slots) + return 0; + + dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL); + if (!dev->mt) + return -ENOMEM; + + dev->mtsize = num_slots; + input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0); + + return 0; +} +EXPORT_SYMBOL(input_mt_create_slots); + +/** + * input_mt_destroy_slots() - frees the MT slots of the input device + * @dev: input device with allocated MT slots + * + * This function is only needed in error path as the input core will + * automatically free the MT slots when the device is destroyed. + */ +void input_mt_destroy_slots(struct input_dev *dev) +{ + kfree(dev->mt); + dev->mt = NULL; + dev->mtsize = 0; +} +EXPORT_SYMBOL(input_mt_destroy_slots); + +/** * input_set_capability - mark device as capable of a certain event * @dev: device that is capable of emitting or accepting event * @type: type of the event (EV_KEY, EV_REL, etc...) @@ -1926,20 +2004,10 @@ static const struct file_operations input_fops = { .open = input_open_file, }; -static void __init input_init_abs_bypass(void) -{ - const unsigned int *p; - - for (p = input_abs_bypass_init_data; *p; p++) - input_abs_bypass[BIT_WORD(*p)] |= BIT_MASK(*p); -} - static int __init input_init(void) { int err; - input_init_abs_bypass(); - err = class_register(&input_class); if (err) { printk(KERN_ERR "input: unable to register input_dev class\n"); diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c index 34157bb..6383458 100644 --- a/drivers/input/joydev.c +++ b/drivers/input/joydev.c @@ -37,7 +37,6 @@ MODULE_LICENSE("GPL"); #define JOYDEV_BUFFER_SIZE 64 struct joydev { - int exist; int open; int minor; struct input_handle handle; @@ -46,6 +45,7 @@ struct joydev { spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + bool exist; struct js_corr corr[ABS_CNT]; struct JS_DATA_SAVE_TYPE glue; @@ -760,7 +760,7 @@ static void joydev_remove_chrdev(struct joydev *joydev) static void joydev_mark_dead(struct joydev *joydev) { mutex_lock(&joydev->mutex); - joydev->exist = 0; + joydev->exist = false; mutex_unlock(&joydev->mutex); } @@ -817,10 +817,9 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev, init_waitqueue_head(&joydev->wait); dev_set_name(&joydev->dev, "js%d", minor); - joydev->exist = 1; + joydev->exist = true; joydev->minor = minor; - joydev->exist = 1; joydev->handle.dev = input_get_device(dev); joydev->handle.name = dev_name(&joydev->dev); joydev->handle.handler = handler; diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index a2b426d..269a846 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -9,6 +9,7 @@ * 2005 Dominic Cerquetti <binary1230@yahoo.com> * 2006 Adam Buchbinder <adam.buchbinder@gmail.com> * 2007 Jan Kratochvil <honza@jikos.cz> + * 2010 Christoph Fritz <chf.fritz@googlemail.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -88,6 +89,9 @@ but we map them to axes when possible to simplify things */ #define MAP_DPAD_TO_BUTTONS (1 << 0) #define MAP_TRIGGERS_TO_BUTTONS (1 << 1) +#define MAP_STICKS_TO_NULL (1 << 2) +#define DANCEPAD_MAP_CONFIG (MAP_DPAD_TO_BUTTONS | \ + MAP_TRIGGERS_TO_BUTTONS | MAP_STICKS_TO_NULL) #define XTYPE_XBOX 0 #define XTYPE_XBOX360 1 @@ -102,6 +106,10 @@ static int triggers_to_buttons; module_param(triggers_to_buttons, bool, S_IRUGO); MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads"); +static int sticks_to_null; +module_param(sticks_to_null, bool, S_IRUGO); +MODULE_PARM_DESC(sticks_to_null, "Do not map sticks at all for unknown pads"); + static const struct xpad_device { u16 idVendor; u16 idProduct; @@ -114,7 +122,7 @@ static const struct xpad_device { { 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX }, { 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX }, { 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W }, - { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, + { 0x0c12, 0x8809, "RedOctane Xbox Dance Pad", DANCEPAD_MAP_CONFIG, XTYPE_XBOX }, { 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX }, { 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 }, { 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX }, @@ -159,7 +167,7 @@ static const struct xpad_device { /* buttons shared with xbox and xbox360 */ static const signed short xpad_common_btn[] = { BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */ - BTN_START, BTN_BACK, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ + BTN_START, BTN_SELECT, BTN_THUMBL, BTN_THUMBR, /* start/back/sticks */ -1 /* terminating entry */ }; @@ -169,10 +177,10 @@ static const signed short xpad_btn[] = { -1 /* terminating entry */ }; -/* used when dpad is mapped to nuttons */ +/* used when dpad is mapped to buttons */ static const signed short xpad_btn_pad[] = { - BTN_LEFT, BTN_RIGHT, /* d-pad left, right */ - BTN_0, BTN_1, /* d-pad up, down (XXX names??) */ + BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */ + BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */ -1 /* terminating entry */ }; @@ -280,17 +288,19 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d { struct input_dev *dev = xpad->dev; - /* left stick */ - input_report_abs(dev, ABS_X, - (__s16) le16_to_cpup((__le16 *)(data + 12))); - input_report_abs(dev, ABS_Y, - ~(__s16) le16_to_cpup((__le16 *)(data + 14))); - - /* right stick */ - input_report_abs(dev, ABS_RX, - (__s16) le16_to_cpup((__le16 *)(data + 16))); - input_report_abs(dev, ABS_RY, - ~(__s16) le16_to_cpup((__le16 *)(data + 18))); + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + /* left stick */ + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 12))); + input_report_abs(dev, ABS_Y, + ~(__s16) le16_to_cpup((__le16 *)(data + 14))); + + /* right stick */ + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 16))); + input_report_abs(dev, ABS_RY, + ~(__s16) le16_to_cpup((__le16 *)(data + 18))); + } /* triggers left/right */ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { @@ -303,10 +313,11 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { - input_report_key(dev, BTN_LEFT, data[2] & 0x04); - input_report_key(dev, BTN_RIGHT, data[2] & 0x08); - input_report_key(dev, BTN_0, data[2] & 0x01); /* up */ - input_report_key(dev, BTN_1, data[2] & 0x02); /* down */ + /* dpad as buttons (left, right, up, down) */ + input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); + input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); + input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); + input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); } else { input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); @@ -316,7 +327,7 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d /* start/back buttons and stick press left/right */ input_report_key(dev, BTN_START, data[2] & 0x10); - input_report_key(dev, BTN_BACK, data[2] & 0x20); + input_report_key(dev, BTN_SELECT, data[2] & 0x20); input_report_key(dev, BTN_THUMBL, data[2] & 0x40); input_report_key(dev, BTN_THUMBR, data[2] & 0x80); @@ -350,11 +361,11 @@ static void xpad360_process_packet(struct usb_xpad *xpad, /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { - /* dpad as buttons (right, left, down, up) */ - input_report_key(dev, BTN_LEFT, data[2] & 0x04); - input_report_key(dev, BTN_RIGHT, data[2] & 0x08); - input_report_key(dev, BTN_0, data[2] & 0x01); /* up */ - input_report_key(dev, BTN_1, data[2] & 0x02); /* down */ + /* dpad as buttons (left, right, up, down) */ + input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04); + input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08); + input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01); + input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02); } else { input_report_abs(dev, ABS_HAT0X, !!(data[2] & 0x08) - !!(data[2] & 0x04)); @@ -364,7 +375,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad, /* start/back buttons */ input_report_key(dev, BTN_START, data[2] & 0x10); - input_report_key(dev, BTN_BACK, data[2] & 0x20); + input_report_key(dev, BTN_SELECT, data[2] & 0x20); /* stick press left/right */ input_report_key(dev, BTN_THUMBL, data[2] & 0x40); @@ -379,17 +390,19 @@ static void xpad360_process_packet(struct usb_xpad *xpad, input_report_key(dev, BTN_TR, data[3] & 0x02); input_report_key(dev, BTN_MODE, data[3] & 0x04); - /* left stick */ - input_report_abs(dev, ABS_X, - (__s16) le16_to_cpup((__le16 *)(data + 6))); - input_report_abs(dev, ABS_Y, - ~(__s16) le16_to_cpup((__le16 *)(data + 8))); - - /* right stick */ - input_report_abs(dev, ABS_RX, - (__s16) le16_to_cpup((__le16 *)(data + 10))); - input_report_abs(dev, ABS_RY, - ~(__s16) le16_to_cpup((__le16 *)(data + 12))); + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + /* left stick */ + input_report_abs(dev, ABS_X, + (__s16) le16_to_cpup((__le16 *)(data + 6))); + input_report_abs(dev, ABS_Y, + ~(__s16) le16_to_cpup((__le16 *)(data + 8))); + + /* right stick */ + input_report_abs(dev, ABS_RX, + (__s16) le16_to_cpup((__le16 *)(data + 10))); + input_report_abs(dev, ABS_RY, + ~(__s16) le16_to_cpup((__le16 *)(data + 12))); + } /* triggers left/right */ if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) { @@ -815,6 +828,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id xpad->mapping |= MAP_DPAD_TO_BUTTONS; if (triggers_to_buttons) xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS; + if (sticks_to_null) + xpad->mapping |= MAP_STICKS_TO_NULL; } xpad->dev = input_dev; @@ -831,16 +846,20 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id input_dev->open = xpad_open; input_dev->close = xpad_close; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->evbit[0] = BIT_MASK(EV_KEY); + + if (!(xpad->mapping & MAP_STICKS_TO_NULL)) { + input_dev->evbit[0] |= BIT_MASK(EV_ABS); + /* set up axes */ + for (i = 0; xpad_abs[i] >= 0; i++) + xpad_set_up_abs(input_dev, xpad_abs[i]); + } - /* set up standard buttons and axes */ + /* set up standard buttons */ for (i = 0; xpad_common_btn[i] >= 0; i++) __set_bit(xpad_common_btn[i], input_dev->keybit); - for (i = 0; xpad_abs[i] >= 0; i++) - xpad_set_up_abs(input_dev, xpad_abs[i]); - - /* Now set up model-specific ones */ + /* set up model-specific ones */ if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) { for (i = 0; xpad360_btn[i] >= 0; i++) __set_bit(xpad360_btn[i], input_dev->keybit); diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index c96ccc1..dd4937f 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -297,6 +297,18 @@ config KEYBOARD_MAX7359 To compile this driver as a module, choose M here: the module will be called max7359_keypad. +config KEYBOARD_MCS + tristate "MELFAS MCS Touchkey" + depends on I2C + help + Say Y here if you have the MELFAS MCS5000/5080 touchkey controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mcs_touchkey. + config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC @@ -342,6 +354,15 @@ config KEYBOARD_PXA930_ROTARY To compile this driver as a module, choose M here: the module will be called pxa930_rotary. +config KEYBOARD_SAMSUNG + tristate "Samsung keypad support" + depends on SAMSUNG_DEV_KEYPAD + help + Say Y here if you want to use the Samsung keypad. + + To compile this driver as a module, choose M here: the + module will be called samsung-keypad. + config KEYBOARD_STOWAWAY tristate "Stowaway keyboard" select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 4596d0c..1a66d5f 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -26,12 +26,14 @@ obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o +obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.o +obj-$(CONFIG_KEYBOARD_SAMSUNG) += samsung-keypad.o obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c index 744600e..d6918cb 100644 --- a/drivers/input/keyboard/adp5588-keys.c +++ b/drivers/input/keyboard/adp5588-keys.c @@ -19,6 +19,7 @@ #include <linux/platform_device.h> #include <linux/input.h> #include <linux/i2c.h> +#include <linux/gpio.h> #include <linux/slab.h> #include <linux/i2c/adp5588.h> @@ -54,6 +55,10 @@ #define KEYP_MAX_EVENT 10 +#define MAXGPIO 18 +#define ADP_BANK(offs) ((offs) >> 3) +#define ADP_BIT(offs) (1u << ((offs) & 0x7)) + /* * Early pre 4.0 Silicon required to delay readout by at least 25ms, * since the Event Counter Register updated 25ms after the interrupt @@ -67,6 +72,16 @@ struct adp5588_kpad { struct delayed_work work; unsigned long delay; unsigned short keycode[ADP5588_KEYMAPSIZE]; + const struct adp5588_gpi_map *gpimap; + unsigned short gpimapsize; +#ifdef CONFIG_GPIOLIB + unsigned char gpiomap[MAXGPIO]; + bool export_gpio; + struct gpio_chip gc; + struct mutex gpio_lock; /* Protect cached dir, dat_out */ + u8 dat_out[3]; + u8 dir[3]; +#endif }; static int adp5588_read(struct i2c_client *client, u8 reg) @@ -84,12 +99,222 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val) return i2c_smbus_write_byte_data(client, reg, val); } +#ifdef CONFIG_GPIOLIB +static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + return !!(adp5588_read(kpad->client, GPIO_DAT_STAT1 + bank) & bit); +} + +static void adp5588_gpio_set_value(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + + mutex_lock(&kpad->gpio_lock); + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, + kpad->dat_out[bank]); + + mutex_unlock(&kpad->gpio_lock); +} + +static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] &= ~bit; + ret = adp5588_write(kpad->client, GPIO_DIR1 + bank, kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int adp5588_gpio_direction_output(struct gpio_chip *chip, + unsigned off, int val) +{ + struct adp5588_kpad *kpad = container_of(chip, struct adp5588_kpad, gc); + unsigned int bank = ADP_BANK(kpad->gpiomap[off]); + unsigned int bit = ADP_BIT(kpad->gpiomap[off]); + int ret; + + mutex_lock(&kpad->gpio_lock); + + kpad->dir[bank] |= bit; + + if (val) + kpad->dat_out[bank] |= bit; + else + kpad->dat_out[bank] &= ~bit; + + ret = adp5588_write(kpad->client, GPIO_DAT_OUT1 + bank, + kpad->dat_out[bank]); + ret |= adp5588_write(kpad->client, GPIO_DIR1 + bank, + kpad->dir[bank]); + + mutex_unlock(&kpad->gpio_lock); + + return ret; +} + +static int __devinit adp5588_build_gpiomap(struct adp5588_kpad *kpad, + const struct adp5588_kpad_platform_data *pdata) +{ + bool pin_used[MAXGPIO]; + int n_unused = 0; + int i; + + memset(pin_used, 0, sizeof(pin_used)); + + for (i = 0; i < pdata->rows; i++) + pin_used[i] = true; + + for (i = 0; i < pdata->cols; i++) + pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true; + + for (i = 0; i < kpad->gpimapsize; i++) + pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true; + + for (i = 0; i < MAXGPIO; i++) + if (!pin_used[i]) + kpad->gpiomap[n_unused++] = i; + + return n_unused; +} + +static int __devinit adp5588_gpio_add(struct adp5588_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev->platform_data; + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int i, error; + + if (!gpio_data) + return 0; + + kpad->gc.ngpio = adp5588_build_gpiomap(kpad, pdata); + if (kpad->gc.ngpio == 0) { + dev_info(dev, "No unused gpios left to export\n"); + return 0; + } + + kpad->export_gpio = true; + + kpad->gc.direction_input = adp5588_gpio_direction_input; + kpad->gc.direction_output = adp5588_gpio_direction_output; + kpad->gc.get = adp5588_gpio_get_value; + kpad->gc.set = adp5588_gpio_set_value; + kpad->gc.can_sleep = 1; + + kpad->gc.base = gpio_data->gpio_start; + kpad->gc.label = kpad->client->name; + kpad->gc.owner = THIS_MODULE; + + mutex_init(&kpad->gpio_lock); + + error = gpiochip_add(&kpad->gc); + if (error) { + dev_err(dev, "gpiochip_add failed, err: %d\n", error); + return error; + } + + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + kpad->dat_out[i] = adp5588_read(kpad->client, + GPIO_DAT_OUT1 + i); + kpad->dir[i] = adp5588_read(kpad->client, GPIO_DIR1 + i); + } + + if (gpio_data->setup) { + error = gpio_data->setup(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "setup failed, %d\n", error); + } + + return 0; +} + +static void __devexit adp5588_gpio_remove(struct adp5588_kpad *kpad) +{ + struct device *dev = &kpad->client->dev; + const struct adp5588_kpad_platform_data *pdata = dev->platform_data; + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; + int error; + + if (!kpad->export_gpio) + return; + + if (gpio_data->teardown) { + error = gpio_data->teardown(kpad->client, + kpad->gc.base, kpad->gc.ngpio, + gpio_data->context); + if (error) + dev_warn(dev, "teardown failed %d\n", error); + } + + error = gpiochip_remove(&kpad->gc); + if (error) + dev_warn(dev, "gpiochip_remove failed %d\n", error); +} +#else +static inline int adp5588_gpio_add(struct adp5588_kpad *kpad) +{ + return 0; +} + +static inline void adp5588_gpio_remove(struct adp5588_kpad *kpad) +{ +} +#endif + +static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt) +{ + int i, j; + + for (i = 0; i < ev_cnt; i++) { + int key = adp5588_read(kpad->client, Key_EVENTA + i); + int key_val = key & KEY_EV_MASK; + + if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) { + for (j = 0; j < kpad->gpimapsize; j++) { + if (key_val == kpad->gpimap[j].pin) { + input_report_switch(kpad->input, + kpad->gpimap[j].sw_evt, + key & KEY_EV_PRESSED); + break; + } + } + } else { + input_report_key(kpad->input, + kpad->keycode[key_val - 1], + key & KEY_EV_PRESSED); + } + } +} + static void adp5588_work(struct work_struct *work) { struct adp5588_kpad *kpad = container_of(work, struct adp5588_kpad, work.work); struct i2c_client *client = kpad->client; - int i, key, status, ev_cnt; + int status, ev_cnt; status = adp5588_read(client, INT_STAT); @@ -99,12 +324,7 @@ static void adp5588_work(struct work_struct *work) if (status & KE_INT) { ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC; if (ev_cnt) { - for (i = 0; i < ev_cnt; i++) { - key = adp5588_read(client, Key_EVENTA + i); - input_report_key(kpad->input, - kpad->keycode[(key & KEY_EV_MASK) - 1], - key & KEY_EV_PRESSED); - } + adp5588_report_events(kpad, ev_cnt); input_sync(kpad->input); } } @@ -128,8 +348,10 @@ static irqreturn_t adp5588_irq(int irq, void *handle) static int __devinit adp5588_setup(struct i2c_client *client) { - struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data; int i, ret; + unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0; ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows)); ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF); @@ -144,6 +366,32 @@ static int __devinit adp5588_setup(struct i2c_client *client) for (i = 0; i < KEYP_MAX_EVENT; i++) ret |= adp5588_read(client, Key_EVENTA); + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin <= GPI_PIN_ROW_END) { + evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE)); + } else { + evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF); + evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8); + } + } + + if (pdata->gpimapsize) { + ret |= adp5588_write(client, GPI_EM1, evt_mode1); + ret |= adp5588_write(client, GPI_EM2, evt_mode2); + ret |= adp5588_write(client, GPI_EM3, evt_mode3); + } + + if (gpio_data) { + for (i = 0; i <= ADP_BANK(MAXGPIO); i++) { + int pull_mask = gpio_data->pullup_dis_mask; + + ret |= adp5588_write(client, GPIO_PULL1 + i, + (pull_mask >> (8 * i)) & 0xFF); + } + } + ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT | OVR_FLOW_INT | K_LCK_INT | GPI_INT | KE_INT); /* Status is W1C */ @@ -158,11 +406,49 @@ static int __devinit adp5588_setup(struct i2c_client *client) return 0; } +static void __devinit adp5588_report_switch_state(struct adp5588_kpad *kpad) +{ + int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1); + int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2); + int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3); + int gpi_stat_tmp, pin_loc; + int i; + + for (i = 0; i < kpad->gpimapsize; i++) { + unsigned short pin = kpad->gpimap[i].pin; + + if (pin <= GPI_PIN_ROW_END) { + gpi_stat_tmp = gpi_stat1; + pin_loc = pin - GPI_PIN_ROW_BASE; + } else if ((pin - GPI_PIN_COL_BASE) < 8) { + gpi_stat_tmp = gpi_stat2; + pin_loc = pin - GPI_PIN_COL_BASE; + } else { + gpi_stat_tmp = gpi_stat3; + pin_loc = pin - GPI_PIN_COL_BASE - 8; + } + + if (gpi_stat_tmp < 0) { + dev_err(&kpad->client->dev, + "Can't read GPIO_DAT_STAT switch %d default to OFF\n", + pin); + gpi_stat_tmp = 0; + } + + input_report_switch(kpad->input, + kpad->gpimap[i].sw_evt, + !(gpi_stat_tmp & (1 << pin_loc))); + } + + input_sync(kpad->input); +} + + static int __devinit adp5588_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adp5588_kpad *kpad; - struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; + const struct adp5588_kpad_platform_data *pdata = client->dev.platform_data; struct input_dev *input; unsigned int revid; int ret, i; @@ -189,6 +475,37 @@ static int __devinit adp5588_probe(struct i2c_client *client, return -EINVAL; } + if (!pdata->gpimap && pdata->gpimapsize) { + dev_err(&client->dev, "invalid gpimap from pdata\n"); + return -EINVAL; + } + + if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) { + dev_err(&client->dev, "invalid gpimapsize\n"); + return -EINVAL; + } + + for (i = 0; i < pdata->gpimapsize; i++) { + unsigned short pin = pdata->gpimap[i].pin; + + if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) { + dev_err(&client->dev, "invalid gpi pin data\n"); + return -EINVAL; + } + + if (pin <= GPI_PIN_ROW_END) { + if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) { + dev_err(&client->dev, "invalid gpi row data\n"); + return -EINVAL; + } + } else { + if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) { + dev_err(&client->dev, "invalid gpi col data\n"); + return -EINVAL; + } + } + } + if (!client->irq) { dev_err(&client->dev, "no IRQ?\n"); return -EINVAL; @@ -233,6 +550,9 @@ static int __devinit adp5588_probe(struct i2c_client *client, memcpy(kpad->keycode, pdata->keymap, pdata->keymapsize * input->keycodesize); + kpad->gpimap = pdata->gpimap; + kpad->gpimapsize = pdata->gpimapsize; + /* setup input device */ __set_bit(EV_KEY, input->evbit); @@ -243,6 +563,11 @@ static int __devinit adp5588_probe(struct i2c_client *client, __set_bit(kpad->keycode[i] & KEY_MAX, input->keybit); __clear_bit(KEY_RESERVED, input->keybit); + if (kpad->gpimapsize) + __set_bit(EV_SW, input->evbit); + for (i = 0; i < kpad->gpimapsize; i++) + __set_bit(kpad->gpimap[i].sw_evt, input->swbit); + error = input_register_device(input); if (error) { dev_err(&client->dev, "unable to register input device\n"); @@ -261,6 +586,13 @@ static int __devinit adp5588_probe(struct i2c_client *client, if (error) goto err_free_irq; + if (kpad->gpimapsize) + adp5588_report_switch_state(kpad); + + error = adp5588_gpio_add(kpad); + if (error) + goto err_free_irq; + device_init_wakeup(&client->dev, 1); i2c_set_clientdata(client, kpad); @@ -287,6 +619,7 @@ static int __devexit adp5588_remove(struct i2c_client *client) free_irq(client->irq, kpad); cancel_delayed_work_sync(&kpad->work); input_unregister_device(kpad->input); + adp5588_gpio_remove(kpad); kfree(kpad); return 0; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index b8213fd..a9fd147 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -31,6 +31,7 @@ struct gpio_button_data { struct input_dev *input; struct timer_list timer; struct work_struct work; + int timer_debounce; /* in msecs */ bool disabled; }; @@ -109,7 +110,7 @@ static void gpio_keys_disable_button(struct gpio_button_data *bdata) * Disable IRQ and possible debouncing timer. */ disable_irq(gpio_to_irq(bdata->button->gpio)); - if (bdata->button->debounce_interval) + if (bdata->timer_debounce) del_timer_sync(&bdata->timer); bdata->disabled = true; @@ -347,9 +348,9 @@ static irqreturn_t gpio_keys_isr(int irq, void *dev_id) BUG_ON(irq != gpio_to_irq(button->gpio)); - if (button->debounce_interval) + if (bdata->timer_debounce) mod_timer(&bdata->timer, - jiffies + msecs_to_jiffies(button->debounce_interval)); + jiffies + msecs_to_jiffies(bdata->timer_debounce)); else schedule_work(&bdata->work); @@ -383,6 +384,14 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev, goto fail3; } + if (button->debounce_interval) { + error = gpio_set_debounce(button->gpio, + button->debounce_interval * 1000); + /* use timer if gpiolib doesn't provide debounce */ + if (error < 0) + bdata->timer_debounce = button->debounce_interval; + } + irq = gpio_to_irq(button->gpio); if (irq < 0) { error = irq; @@ -498,7 +507,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev) fail2: while (--i >= 0) { free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]); - if (pdata->buttons[i].debounce_interval) + if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); @@ -526,7 +535,7 @@ static int __devexit gpio_keys_remove(struct platform_device *pdev) for (i = 0; i < pdata->nbuttons; i++) { int irq = gpio_to_irq(pdata->buttons[i].gpio); free_irq(irq, &ddata->data[i]); - if (pdata->buttons[i].debounce_interval) + if (ddata->data[i].timer_debounce) del_timer_sync(&ddata->data[i].timer); cancel_work_sync(&ddata->data[i].work); gpio_free(pdata->buttons[i].gpio); diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c index 40b032f..f7c2a16 100644 --- a/drivers/input/keyboard/lm8323.c +++ b/drivers/input/keyboard/lm8323.c @@ -642,6 +642,7 @@ static int __devinit lm8323_probe(struct i2c_client *client, struct lm8323_platform_data *pdata = client->dev.platform_data; struct input_dev *idev; struct lm8323_chip *lm; + int pwm; int i, err; unsigned long tmo; u8 data[2]; @@ -710,8 +711,9 @@ static int __devinit lm8323_probe(struct i2c_client *client, goto fail1; } - for (i = 0; i < LM8323_NUM_PWMS; i++) { - err = init_pwm(lm, i + 1, &client->dev, pdata->pwm_names[i]); + for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) { + err = init_pwm(lm, pwm + 1, &client->dev, + pdata->pwm_names[pwm]); if (err < 0) goto fail2; } @@ -764,9 +766,9 @@ fail4: fail3: device_remove_file(&client->dev, &dev_attr_disable_kp); fail2: - while (--i >= 0) - if (lm->pwm[i].enabled) - led_classdev_unregister(&lm->pwm[i].cdev); + while (--pwm >= 0) + if (lm->pwm[pwm].enabled) + led_classdev_unregister(&lm->pwm[pwm].cdev); fail1: input_free_device(idev); kfree(lm); diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index b443e08..b02e426 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -37,6 +37,7 @@ struct matrix_keypad { spinlock_t lock; bool scan_pending; bool stopped; + bool gpio_all_disabled; }; /* @@ -87,8 +88,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad) const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; - for (i = 0; i < pdata->num_row_gpios; i++) - enable_irq(gpio_to_irq(pdata->row_gpios[i])); + if (pdata->clustered_irq > 0) + enable_irq(pdata->clustered_irq); + else { + for (i = 0; i < pdata->num_row_gpios; i++) + enable_irq(gpio_to_irq(pdata->row_gpios[i])); + } } static void disable_row_irqs(struct matrix_keypad *keypad) @@ -96,8 +101,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad) const struct matrix_keypad_platform_data *pdata = keypad->pdata; int i; - for (i = 0; i < pdata->num_row_gpios; i++) - disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); + if (pdata->clustered_irq > 0) + disable_irq_nosync(pdata->clustered_irq); + else { + for (i = 0; i < pdata->num_row_gpios; i++) + disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); + } } /* @@ -216,45 +225,69 @@ static void matrix_keypad_stop(struct input_dev *dev) } #ifdef CONFIG_PM -static int matrix_keypad_suspend(struct device *dev) +static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) { - struct platform_device *pdev = to_platform_device(dev); - struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; + unsigned int gpio; int i; - matrix_keypad_stop(keypad->input_dev); + if (pdata->clustered_irq > 0) { + if (enable_irq_wake(pdata->clustered_irq) == 0) + keypad->gpio_all_disabled = true; + } else { - if (device_may_wakeup(&pdev->dev)) { for (i = 0; i < pdata->num_row_gpios; i++) { if (!test_bit(i, keypad->disabled_gpios)) { - unsigned int gpio = pdata->row_gpios[i]; + gpio = pdata->row_gpios[i]; if (enable_irq_wake(gpio_to_irq(gpio)) == 0) __set_bit(i, keypad->disabled_gpios); } } } - - return 0; } -static int matrix_keypad_resume(struct device *dev) +static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) { - struct platform_device *pdev = to_platform_device(dev); - struct matrix_keypad *keypad = platform_get_drvdata(pdev); const struct matrix_keypad_platform_data *pdata = keypad->pdata; + unsigned int gpio; int i; - if (device_may_wakeup(&pdev->dev)) { + if (pdata->clustered_irq > 0) { + if (keypad->gpio_all_disabled) { + disable_irq_wake(pdata->clustered_irq); + keypad->gpio_all_disabled = false; + } + } else { for (i = 0; i < pdata->num_row_gpios; i++) { if (test_and_clear_bit(i, keypad->disabled_gpios)) { - unsigned int gpio = pdata->row_gpios[i]; - + gpio = pdata->row_gpios[i]; disable_irq_wake(gpio_to_irq(gpio)); } } } +} + +static int matrix_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + + matrix_keypad_stop(keypad->input_dev); + + if (device_may_wakeup(&pdev->dev)) + matrix_keypad_enable_wakeup(keypad); + + return 0; +} + +static int matrix_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct matrix_keypad *keypad = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + matrix_keypad_disable_wakeup(keypad); matrix_keypad_start(keypad->input_dev); @@ -296,17 +329,31 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev, gpio_direction_input(pdata->row_gpios[i]); } - for (i = 0; i < pdata->num_row_gpios; i++) { - err = request_irq(gpio_to_irq(pdata->row_gpios[i]), + if (pdata->clustered_irq > 0) { + err = request_irq(pdata->clustered_irq, matrix_keypad_interrupt, - IRQF_DISABLED | - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + pdata->clustered_irq_flags, "matrix-keypad", keypad); if (err) { dev_err(&pdev->dev, - "Unable to acquire interrupt for GPIO line %i\n", - pdata->row_gpios[i]); - goto err_free_irqs; + "Unable to acquire clustered interrupt\n"); + goto err_free_rows; + } + } else { + for (i = 0; i < pdata->num_row_gpios; i++) { + err = request_irq(gpio_to_irq(pdata->row_gpios[i]), + matrix_keypad_interrupt, + IRQF_DISABLED | + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "matrix-keypad", keypad); + if (err) { + dev_err(&pdev->dev, + "Unable to acquire interrupt " + "for GPIO line %i\n", + pdata->row_gpios[i]); + goto err_free_irqs; + } } } @@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev) device_init_wakeup(&pdev->dev, 0); - for (i = 0; i < pdata->num_row_gpios; i++) { - free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); - gpio_free(pdata->row_gpios[i]); + if (pdata->clustered_irq > 0) { + free_irq(pdata->clustered_irq, keypad); + } else { + for (i = 0; i < pdata->num_row_gpios; i++) + free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); } + for (i = 0; i < pdata->num_row_gpios; i++) + gpio_free(pdata->row_gpios[i]); + for (i = 0; i < pdata->num_col_gpios; i++) gpio_free(pdata->col_gpios[i]); diff --git a/drivers/input/keyboard/mcs_touchkey.c b/drivers/input/keyboard/mcs_touchkey.c new file mode 100644 index 0000000..63b849d --- /dev/null +++ b/drivers/input/keyboard/mcs_touchkey.c @@ -0,0 +1,239 @@ +/* + * mcs_touchkey.c - Touchkey driver for MELFAS MCS5000/5080 controller + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: HeungJun Kim <riverful.kim@samsung.com> + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c/mcs.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/slab.h> + +/* MCS5000 Touchkey */ +#define MCS5000_TOUCHKEY_STATUS 0x04 +#define MCS5000_TOUCHKEY_STATUS_PRESS 7 +#define MCS5000_TOUCHKEY_FW 0x0a +#define MCS5000_TOUCHKEY_BASE_VAL 0x61 + +/* MCS5080 Touchkey */ +#define MCS5080_TOUCHKEY_STATUS 0x00 +#define MCS5080_TOUCHKEY_STATUS_PRESS 3 +#define MCS5080_TOUCHKEY_FW 0x01 +#define MCS5080_TOUCHKEY_BASE_VAL 0x1 + +enum mcs_touchkey_type { + MCS5000_TOUCHKEY, + MCS5080_TOUCHKEY, +}; + +struct mcs_touchkey_chip { + unsigned int status_reg; + unsigned int pressbit; + unsigned int press_invert; + unsigned int baseval; +}; + +struct mcs_touchkey_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct mcs_touchkey_chip chip; + unsigned int key_code; + unsigned int key_val; + unsigned short keycodes[]; +}; + +static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id) +{ + struct mcs_touchkey_data *data = dev_id; + struct mcs_touchkey_chip *chip = &data->chip; + struct i2c_client *client = data->client; + struct input_dev *input = data->input_dev; + unsigned int key_val; + unsigned int pressed; + int val; + + val = i2c_smbus_read_byte_data(client, chip->status_reg); + if (val < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", val); + goto out; + } + + pressed = (val & (1 << chip->pressbit)) >> chip->pressbit; + if (chip->press_invert) + pressed ^= chip->press_invert; + + /* key_val is 0 when released, so we should use key_val of press. */ + if (pressed) { + key_val = val & (0xff >> (8 - chip->pressbit)); + if (!key_val) + goto out; + key_val -= chip->baseval; + data->key_code = data->keycodes[key_val]; + data->key_val = key_val; + } + + input_event(input, EV_MSC, MSC_SCAN, data->key_val); + input_report_key(input, data->key_code, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code, + pressed ? "pressed" : "released"); + + out: + return IRQ_HANDLED; +} + +static int __devinit mcs_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct mcs_platform_data *pdata; + struct mcs_touchkey_data *data; + struct input_dev *input_dev; + unsigned int fw_reg; + int fw_ver; + int error; + int i; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct mcs_touchkey_data) + + sizeof(data->keycodes[0]) * (pdata->key_maxval + 1), + GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + + if (id->driver_data == MCS5000_TOUCHKEY) { + data->chip.status_reg = MCS5000_TOUCHKEY_STATUS; + data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS; + data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL; + fw_reg = MCS5000_TOUCHKEY_FW; + } else { + data->chip.status_reg = MCS5080_TOUCHKEY_STATUS; + data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS; + data->chip.press_invert = 1; + data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL; + fw_reg = MCS5080_TOUCHKEY_FW; + } + + fw_ver = i2c_smbus_read_byte_data(client, fw_reg); + if (fw_ver < 0) { + error = fw_ver; + dev_err(&client->dev, "i2c read error[%d]\n", error); + goto err_free_mem; + } + dev_info(&client->dev, "Firmware version: %d\n", fw_ver); + + input_dev->name = "MELPAS MCS Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + input_dev->keycode = data->keycodes; + input_dev->keycodesize = sizeof(data->keycodes[0]); + input_dev->keycodemax = pdata->key_maxval + 1; + + for (i = 0; i < pdata->keymap_size; i++) { + unsigned int val = MCS_KEY_VAL(pdata->keymap[i]); + unsigned int code = MCS_KEY_CODE(pdata->keymap[i]); + + data->keycodes[val] = code; + __set_bit(code, input_dev->keybit); + } + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, data); + + if (pdata->cfg_pin) + pdata->cfg_pin(); + + error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt, + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + i2c_set_clientdata(client, data); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mcs_touchkey_remove(struct i2c_client *client) +{ + struct mcs_touchkey_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +static const struct i2c_device_id mcs_touchkey_id[] = { + { "mcs5000_touchkey", MCS5000_TOUCHKEY }, + { "mcs5080_touchkey", MCS5080_TOUCHKEY }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id); + +static struct i2c_driver mcs_touchkey_driver = { + .driver = { + .name = "mcs_touchkey", + .owner = THIS_MODULE, + }, + .probe = mcs_touchkey_probe, + .remove = __devexit_p(mcs_touchkey_remove), + .id_table = mcs_touchkey_id, +}; + +static int __init mcs_touchkey_init(void) +{ + return i2c_add_driver(&mcs_touchkey_driver); +} + +static void __exit mcs_touchkey_exit(void) +{ + i2c_del_driver(&mcs_touchkey_driver); +} + +module_init(mcs_touchkey_init); +module_exit(mcs_touchkey_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>"); +MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c new file mode 100644 index 0000000..f689f49 --- /dev/null +++ b/drivers/input/keyboard/samsung-keypad.c @@ -0,0 +1,491 @@ +/* + * Samsung keypad driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * Author: Donghwa Lee <dh09.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <plat/keypad.h> + +#define SAMSUNG_KEYIFCON 0x00 +#define SAMSUNG_KEYIFSTSCLR 0x04 +#define SAMSUNG_KEYIFCOL 0x08 +#define SAMSUNG_KEYIFROW 0x0c +#define SAMSUNG_KEYIFFC 0x10 + +/* SAMSUNG_KEYIFCON */ +#define SAMSUNG_KEYIFCON_INT_F_EN (1 << 0) +#define SAMSUNG_KEYIFCON_INT_R_EN (1 << 1) +#define SAMSUNG_KEYIFCON_DF_EN (1 << 2) +#define SAMSUNG_KEYIFCON_FC_EN (1 << 3) +#define SAMSUNG_KEYIFCON_WAKEUPEN (1 << 4) + +/* SAMSUNG_KEYIFSTSCLR */ +#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK (0xff << 0) +#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK (0xff << 8) +#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET 8 +#define S5PV210_KEYIFSTSCLR_P_INT_MASK (0x3fff << 0) +#define S5PV210_KEYIFSTSCLR_R_INT_MASK (0x3fff << 16) +#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET 16 + +/* SAMSUNG_KEYIFCOL */ +#define SAMSUNG_KEYIFCOL_MASK (0xff << 0) +#define S5PV210_KEYIFCOLEN_MASK (0xff << 8) + +/* SAMSUNG_KEYIFROW */ +#define SAMSUNG_KEYIFROW_MASK (0xff << 0) +#define S5PV210_KEYIFROW_MASK (0x3fff << 0) + +/* SAMSUNG_KEYIFFC */ +#define SAMSUNG_KEYIFFC_MASK (0x3ff << 0) + +enum samsung_keypad_type { + KEYPAD_TYPE_SAMSUNG, + KEYPAD_TYPE_S5PV210, +}; + +struct samsung_keypad { + struct input_dev *input_dev; + struct clk *clk; + void __iomem *base; + wait_queue_head_t wait; + bool stopped; + int irq; + unsigned int row_shift; + unsigned int rows; + unsigned int cols; + unsigned int row_state[SAMSUNG_MAX_COLS]; + unsigned short keycodes[]; +}; + +static int samsung_keypad_is_s5pv210(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + enum samsung_keypad_type type = + platform_get_device_id(pdev)->driver_data; + + return type == KEYPAD_TYPE_S5PV210; +} + +static void samsung_keypad_scan(struct samsung_keypad *keypad, + unsigned int *row_state) +{ + struct device *dev = keypad->input_dev->dev.parent; + unsigned int col; + unsigned int val; + + for (col = 0; col < keypad->cols; col++) { + if (samsung_keypad_is_s5pv210(dev)) { + val = S5PV210_KEYIFCOLEN_MASK; + val &= ~(1 << col) << 8; + } else { + val = SAMSUNG_KEYIFCOL_MASK; + val &= ~(1 << col); + } + + writel(val, keypad->base + SAMSUNG_KEYIFCOL); + mdelay(1); + + val = readl(keypad->base + SAMSUNG_KEYIFROW); + row_state[col] = ~val & ((1 << keypad->rows) - 1); + } + + /* KEYIFCOL reg clear */ + writel(0, keypad->base + SAMSUNG_KEYIFCOL); +} + +static bool samsung_keypad_report(struct samsung_keypad *keypad, + unsigned int *row_state) +{ + struct input_dev *input_dev = keypad->input_dev; + unsigned int changed; + unsigned int pressed; + unsigned int key_down = 0; + unsigned int val; + unsigned int col, row; + + for (col = 0; col < keypad->cols; col++) { + changed = row_state[col] ^ keypad->row_state[col]; + key_down |= row_state[col]; + if (!changed) + continue; + + for (row = 0; row < keypad->rows; row++) { + if (!(changed & (1 << row))) + continue; + + pressed = row_state[col] & (1 << row); + + dev_dbg(&keypad->input_dev->dev, + "key %s, row: %d, col: %d\n", + pressed ? "pressed" : "released", row, col); + + val = MATRIX_SCAN_CODE(row, col, keypad->row_shift); + + input_event(input_dev, EV_MSC, MSC_SCAN, val); + input_report_key(input_dev, + keypad->keycodes[val], pressed); + } + input_sync(keypad->input_dev); + } + + memcpy(keypad->row_state, row_state, sizeof(keypad->row_state)); + + return key_down; +} + +static irqreturn_t samsung_keypad_irq(int irq, void *dev_id) +{ + struct samsung_keypad *keypad = dev_id; + unsigned int row_state[SAMSUNG_MAX_COLS]; + unsigned int val; + bool key_down; + + do { + val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR); + /* Clear interrupt. */ + writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + + samsung_keypad_scan(keypad, row_state); + + key_down = samsung_keypad_report(keypad, row_state); + if (key_down) + wait_event_timeout(keypad->wait, keypad->stopped, + msecs_to_jiffies(50)); + + } while (key_down && !keypad->stopped); + + return IRQ_HANDLED; +} + +static void samsung_keypad_start(struct samsung_keypad *keypad) +{ + unsigned int val; + + /* Tell IRQ thread that it may poll the device. */ + keypad->stopped = false; + + clk_enable(keypad->clk); + + /* Enable interrupt bits. */ + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN; + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + /* KEYIFCOL reg clear. */ + writel(0, keypad->base + SAMSUNG_KEYIFCOL); +} + +static void samsung_keypad_stop(struct samsung_keypad *keypad) +{ + unsigned int val; + + /* Signal IRQ thread to stop polling and disable the handler. */ + keypad->stopped = true; + wake_up(&keypad->wait); + disable_irq(keypad->irq); + + /* Clear interrupt. */ + writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR); + + /* Disable interrupt bits. */ + val = readl(keypad->base + SAMSUNG_KEYIFCON); + val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN); + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); + + /* + * Now that chip should not generate interrupts we can safely + * re-enable the handler. + */ + enable_irq(keypad->irq); +} + +static int samsung_keypad_open(struct input_dev *input_dev) +{ + struct samsung_keypad *keypad = input_get_drvdata(input_dev); + + samsung_keypad_start(keypad); + + return 0; +} + +static void samsung_keypad_close(struct input_dev *input_dev) +{ + struct samsung_keypad *keypad = input_get_drvdata(input_dev); + + samsung_keypad_stop(keypad); +} + +static int __devinit samsung_keypad_probe(struct platform_device *pdev) +{ + const struct samsung_keypad_platdata *pdata; + const struct matrix_keymap_data *keymap_data; + struct samsung_keypad *keypad; + struct resource *res; + struct input_dev *input_dev; + unsigned int row_shift; + unsigned int keymap_size; + int error; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; + } + + keymap_data = pdata->keymap_data; + if (!keymap_data) { + dev_err(&pdev->dev, "no keymap data defined\n"); + return -EINVAL; + } + + if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS) + return -EINVAL; + + if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS) + return -EINVAL; + + /* initialize the gpio */ + if (pdata->cfg_gpio) + pdata->cfg_gpio(pdata->rows, pdata->cols); + + row_shift = get_count_order(pdata->cols); + keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]); + + keypad = kzalloc(sizeof(*keypad) + keymap_size, GFP_KERNEL); + input_dev = input_allocate_device(); + if (!keypad || !input_dev) { + error = -ENOMEM; + goto err_free_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + error = -ENODEV; + goto err_free_mem; + } + + keypad->base = ioremap(res->start, resource_size(res)); + if (!keypad->base) { + error = -EBUSY; + goto err_free_mem; + } + + keypad->clk = clk_get(&pdev->dev, "keypad"); + if (IS_ERR(keypad->clk)) { + dev_err(&pdev->dev, "failed to get keypad clk\n"); + error = PTR_ERR(keypad->clk); + goto err_unmap_base; + } + + keypad->input_dev = input_dev; + keypad->row_shift = row_shift; + keypad->rows = pdata->rows; + keypad->cols = pdata->cols; + init_waitqueue_head(&keypad->wait); + + input_dev->name = pdev->name; + input_dev->id.bustype = BUS_HOST; + input_dev->dev.parent = &pdev->dev; + input_set_drvdata(input_dev, keypad); + + input_dev->open = samsung_keypad_open; + input_dev->close = samsung_keypad_close; + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + if (!pdata->no_autorepeat) + input_dev->evbit[0] |= BIT_MASK(EV_REP); + + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_dev->keycode = keypad->keycodes; + input_dev->keycodesize = sizeof(keypad->keycodes[0]); + input_dev->keycodemax = pdata->rows << row_shift; + + matrix_keypad_build_keymap(keymap_data, row_shift, + input_dev->keycode, input_dev->keybit); + + keypad->irq = platform_get_irq(pdev, 0); + if (keypad->irq < 0) { + error = keypad->irq; + goto err_put_clk; + } + + error = request_threaded_irq(keypad->irq, NULL, samsung_keypad_irq, + IRQF_ONESHOT, dev_name(&pdev->dev), keypad); + if (error) { + dev_err(&pdev->dev, "failed to register keypad interrupt\n"); + goto err_put_clk; + } + + error = input_register_device(keypad->input_dev); + if (error) + goto err_free_irq; + + device_init_wakeup(&pdev->dev, pdata->wakeup); + platform_set_drvdata(pdev, keypad); + return 0; + +err_free_irq: + free_irq(keypad->irq, keypad); +err_put_clk: + clk_put(keypad->clk); +err_unmap_base: + iounmap(keypad->base); +err_free_mem: + input_free_device(input_dev); + kfree(keypad); + + return error; +} + +static int __devexit samsung_keypad_remove(struct platform_device *pdev) +{ + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + platform_set_drvdata(pdev, NULL); + + input_unregister_device(keypad->input_dev); + + /* + * It is safe to free IRQ after unregistering device because + * samsung_keypad_close will shut off interrupts. + */ + free_irq(keypad->irq, keypad); + + clk_put(keypad->clk); + + iounmap(keypad->base); + kfree(keypad); + + return 0; +} + +#ifdef CONFIG_PM +static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad, + bool enable) +{ + struct device *dev = keypad->input_dev->dev.parent; + unsigned int val; + + clk_enable(keypad->clk); + + val = readl(keypad->base + SAMSUNG_KEYIFCON); + if (enable) { + val |= SAMSUNG_KEYIFCON_WAKEUPEN; + if (device_may_wakeup(dev)) + enable_irq_wake(keypad->irq); + } else { + val &= ~SAMSUNG_KEYIFCON_WAKEUPEN; + if (device_may_wakeup(dev)) + disable_irq_wake(keypad->irq); + } + writel(val, keypad->base + SAMSUNG_KEYIFCON); + + clk_disable(keypad->clk); +} + +static int samsung_keypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + samsung_keypad_stop(keypad); + + samsung_keypad_toggle_wakeup(keypad, true); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int samsung_keypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct samsung_keypad *keypad = platform_get_drvdata(pdev); + struct input_dev *input_dev = keypad->input_dev; + + mutex_lock(&input_dev->mutex); + + samsung_keypad_toggle_wakeup(keypad, false); + + if (input_dev->users) + samsung_keypad_start(keypad); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static const struct dev_pm_ops samsung_keypad_pm_ops = { + .suspend = samsung_keypad_suspend, + .resume = samsung_keypad_resume, +}; +#endif + +static struct platform_device_id samsung_keypad_driver_ids[] = { + { + .name = "samsung-keypad", + .driver_data = KEYPAD_TYPE_SAMSUNG, + }, { + .name = "s5pv210-keypad", + .driver_data = KEYPAD_TYPE_S5PV210, + }, + { }, +}; +MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids); + +static struct platform_driver samsung_keypad_driver = { + .probe = samsung_keypad_probe, + .remove = __devexit_p(samsung_keypad_remove), + .driver = { + .name = "samsung-keypad", + .owner = THIS_MODULE, +#ifdef CONFIG_PM + .pm = &samsung_keypad_pm_ops, +#endif + }, + .id_table = samsung_keypad_driver_ids, +}; + +static int __init samsung_keypad_init(void) +{ + return platform_driver_register(&samsung_keypad_driver); +} +module_init(samsung_keypad_init); + +static void __exit samsung_keypad_exit(void) +{ + platform_driver_unregister(&samsung_keypad_driver); +} +module_exit(samsung_keypad_exit); + +MODULE_DESCRIPTION("Samsung keypad driver"); +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:samsung-keypad"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c44b9ea..b49e233 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -327,6 +327,17 @@ config INPUT_PCF8574 To compile this driver as a module, choose M here: the module will be called pcf8574_keypad. +config INPUT_PWM_BEEPER + tristate "PWM beeper support" + depends on HAVE_PWM + help + Say Y here to get support for PWM based beeper devices. + + If unsure, say N. + + To compile this driver as a module, choose M here: the module will be + called pwm-beeper. + config INPUT_GPIO_ROTARY_ENCODER tristate "Rotary encoders connected to GPIO pins" depends on GPIOLIB && GENERIC_GPIO @@ -390,4 +401,41 @@ config INPUT_PCAP To compile this driver as a module, choose M here: the module will be called pcap_keys. +config INPUT_ADXL34X + tristate "Analog Devices ADXL34x Three-Axis Digital Accelerometer" + default n + help + Say Y here if you have a Accelerometer interface using the + ADXL345/6 controller, and your board-specific initialization + code includes that in its table of devices. + + This driver can use either I2C or SPI communication to the + ADXL345/6 controller. Select the appropriate method for + your system. + + 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 adxl34x. + +config INPUT_ADXL34X_I2C + tristate "support I2C bus connection" + depends on INPUT_ADXL34X && I2C + default y + help + Say Y here if you have ADXL345/6 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called adxl34x-i2c. + +config INPUT_ADXL34X_SPI + tristate "support SPI bus connection" + depends on INPUT_ADXL34X && SPI + default y + help + Say Y here if you have ADXL345/6 hooked to a SPI bus. + + To compile this driver as a module, choose M here: the + module will be called adxl34x-spi. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 71fe57d..19ccca7 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -8,6 +8,9 @@ obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o obj-$(CONFIG_INPUT_AD714X) += ad714x.o obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.o +obj-$(CONFIG_INPUT_ADXL34X) += adxl34x.o +obj-$(CONFIG_INPUT_ADXL34X_I2C) += adxl34x-i2c.o +obj-$(CONFIG_INPUT_ADXL34X_SPI) += adxl34x-spi.o obj-$(CONFIG_INPUT_APANEL) += apanel.o obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o @@ -26,6 +29,7 @@ obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o +obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.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 diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c new file mode 100644 index 0000000..0779724 --- /dev/null +++ b/drivers/input/misc/adxl34x-i2c.c @@ -0,0 +1,163 @@ +/* + * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> /* BUS_I2C */ +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> +#include "adxl34x.h" + +static int adxl34x_smbus_read(struct device *dev, unsigned char reg) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int adxl34x_smbus_write(struct device *dev, + unsigned char reg, unsigned char val) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int adxl34x_smbus_read_block(struct device *dev, + unsigned char reg, int count, + void *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_read_i2c_block_data(client, reg, count, buf); +} + +static int adxl34x_i2c_read_block(struct device *dev, + unsigned char reg, int count, + void *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_master_send(client, ®, 1); + if (ret < 0) + return ret; + + ret = i2c_master_recv(client, buf, count); + if (ret < 0) + return ret; + + if (ret != count) + return -EIO; + + return 0; +} + +static const struct adxl34x_bus_ops adxl34x_smbus_bops = { + .bustype = BUS_I2C, + .write = adxl34x_smbus_write, + .read = adxl34x_smbus_read, + .read_block = adxl34x_smbus_read_block, +}; + +static const struct adxl34x_bus_ops adxl34x_i2c_bops = { + .bustype = BUS_I2C, + .write = adxl34x_smbus_write, + .read = adxl34x_smbus_read, + .read_block = adxl34x_i2c_read_block, +}; + +static int __devinit adxl34x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adxl34x *ac; + int error; + + error = i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_BYTE_DATA); + if (!error) { + dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); + return -EIO; + } + + ac = adxl34x_probe(&client->dev, client->irq, false, + i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_I2C_BLOCK) ? + &adxl34x_smbus_bops : &adxl34x_i2c_bops); + if (IS_ERR(ac)) + return PTR_ERR(ac); + + i2c_set_clientdata(client, ac); + + return 0; +} + +static int __devexit adxl34x_i2c_remove(struct i2c_client *client) +{ + struct adxl34x *ac = i2c_get_clientdata(client); + + return adxl34x_remove(ac); +} + +#ifdef CONFIG_PM +static int adxl34x_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + struct adxl34x *ac = i2c_get_clientdata(client); + + adxl34x_suspend(ac); + + return 0; +} + +static int adxl34x_i2c_resume(struct i2c_client *client) +{ + struct adxl34x *ac = i2c_get_clientdata(client); + + adxl34x_resume(ac); + + return 0; +} +#else +# define adxl34x_i2c_suspend NULL +# define adxl34x_i2c_resume NULL +#endif + +static const struct i2c_device_id adxl34x_id[] = { + { "adxl34x", 0 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, adxl34x_id); + +static struct i2c_driver adxl34x_driver = { + .driver = { + .name = "adxl34x", + .owner = THIS_MODULE, + }, + .probe = adxl34x_i2c_probe, + .remove = __devexit_p(adxl34x_i2c_remove), + .suspend = adxl34x_i2c_suspend, + .resume = adxl34x_i2c_resume, + .id_table = adxl34x_id, +}; + +static int __init adxl34x_i2c_init(void) +{ + return i2c_add_driver(&adxl34x_driver); +} +module_init(adxl34x_i2c_init); + +static void __exit adxl34x_i2c_exit(void) +{ + i2c_del_driver(&adxl34x_driver); +} +module_exit(adxl34x_i2c_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/adxl34x-spi.c b/drivers/input/misc/adxl34x-spi.c new file mode 100644 index 0000000..782de9e --- /dev/null +++ b/drivers/input/misc/adxl34x-spi.c @@ -0,0 +1,145 @@ +/* + * ADLX345/346 Three-Axis Digital Accelerometers (SPI Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> /* BUS_SPI */ +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include "adxl34x.h" + +#define MAX_SPI_FREQ_HZ 5000000 +#define MAX_FREQ_NO_FIFODELAY 1500000 +#define ADXL34X_CMD_MULTB (1 << 6) +#define ADXL34X_CMD_READ (1 << 7) +#define ADXL34X_WRITECMD(reg) (reg & 0x3F) +#define ADXL34X_READCMD(reg) (ADXL34X_CMD_READ | (reg & 0x3F)) +#define ADXL34X_READMB_CMD(reg) (ADXL34X_CMD_READ | ADXL34X_CMD_MULTB \ + | (reg & 0x3F)) + +static int adxl34x_spi_read(struct device *dev, unsigned char reg) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char cmd; + + cmd = ADXL34X_READCMD(reg); + + return spi_w8r8(spi, cmd); +} + +static int adxl34x_spi_write(struct device *dev, + unsigned char reg, unsigned char val) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + buf[0] = ADXL34X_WRITECMD(reg); + buf[1] = val; + + return spi_write(spi, buf, sizeof(buf)); +} + +static int adxl34x_spi_read_block(struct device *dev, + unsigned char reg, int count, + void *buf) +{ + struct spi_device *spi = to_spi_device(dev); + ssize_t status; + + reg = ADXL34X_READMB_CMD(reg); + status = spi_write_then_read(spi, ®, 1, buf, count); + + return (status < 0) ? status : 0; +} + +static const struct adxl34x_bus_ops adx134x_spi_bops = { + .bustype = BUS_SPI, + .write = adxl34x_spi_write, + .read = adxl34x_spi_read, + .read_block = adxl34x_spi_read_block, +}; + +static int __devinit adxl34x_spi_probe(struct spi_device *spi) +{ + struct adxl34x *ac; + + /* 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 too fast\n", spi->max_speed_hz); + return -EINVAL; + } + + ac = adxl34x_probe(&spi->dev, spi->irq, + spi->max_speed_hz > MAX_FREQ_NO_FIFODELAY, + &adx134x_spi_bops); + + if (IS_ERR(ac)) + return PTR_ERR(ac); + + spi_set_drvdata(spi, ac); + + return 0; +} + +static int __devexit adxl34x_spi_remove(struct spi_device *spi) +{ + struct adxl34x *ac = dev_get_drvdata(&spi->dev); + + return adxl34x_remove(ac); +} + +#ifdef CONFIG_PM +static int adxl34x_spi_suspend(struct spi_device *spi, pm_message_t message) +{ + struct adxl34x *ac = dev_get_drvdata(&spi->dev); + + adxl34x_suspend(ac); + + return 0; +} + +static int adxl34x_spi_resume(struct spi_device *spi) +{ + struct adxl34x *ac = dev_get_drvdata(&spi->dev); + + adxl34x_resume(ac); + + return 0; +} +#else +# define adxl34x_spi_suspend NULL +# define adxl34x_spi_resume NULL +#endif + +static struct spi_driver adxl34x_driver = { + .driver = { + .name = "adxl34x", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = adxl34x_spi_probe, + .remove = __devexit_p(adxl34x_spi_remove), + .suspend = adxl34x_spi_suspend, + .resume = adxl34x_spi_resume, +}; + +static int __init adxl34x_spi_init(void) +{ + return spi_register_driver(&adxl34x_driver); +} +module_init(adxl34x_spi_init); + +static void __exit adxl34x_spi_exit(void) +{ + spi_unregister_driver(&adxl34x_driver); +} +module_exit(adxl34x_spi_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer SPI Bus Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c new file mode 100644 index 0000000..e2ca017 --- /dev/null +++ b/drivers/input/misc/adxl34x.c @@ -0,0 +1,915 @@ +/* + * ADXL345/346 Three-Axis Digital Accelerometers + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#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/input/adxl34x.h> + +#include "adxl34x.h" + +/* ADXL345/6 Register Map */ +#define DEVID 0x00 /* R Device ID */ +#define THRESH_TAP 0x1D /* R/W Tap threshold */ +#define OFSX 0x1E /* R/W X-axis offset */ +#define OFSY 0x1F /* R/W Y-axis offset */ +#define OFSZ 0x20 /* R/W Z-axis offset */ +#define DUR 0x21 /* R/W Tap duration */ +#define LATENT 0x22 /* R/W Tap latency */ +#define WINDOW 0x23 /* R/W Tap window */ +#define THRESH_ACT 0x24 /* R/W Activity threshold */ +#define THRESH_INACT 0x25 /* R/W Inactivity threshold */ +#define TIME_INACT 0x26 /* R/W Inactivity time */ +#define ACT_INACT_CTL 0x27 /* R/W Axis enable control for activity and */ + /* inactivity detection */ +#define THRESH_FF 0x28 /* R/W Free-fall threshold */ +#define TIME_FF 0x29 /* R/W Free-fall time */ +#define TAP_AXES 0x2A /* R/W Axis control for tap/double tap */ +#define ACT_TAP_STATUS 0x2B /* R Source of tap/double tap */ +#define BW_RATE 0x2C /* R/W Data rate and power mode control */ +#define POWER_CTL 0x2D /* R/W Power saving features control */ +#define INT_ENABLE 0x2E /* R/W Interrupt enable control */ +#define INT_MAP 0x2F /* R/W Interrupt mapping control */ +#define INT_SOURCE 0x30 /* R Source of interrupts */ +#define DATA_FORMAT 0x31 /* R/W Data format control */ +#define DATAX0 0x32 /* R X-Axis Data 0 */ +#define DATAX1 0x33 /* R X-Axis Data 1 */ +#define DATAY0 0x34 /* R Y-Axis Data 0 */ +#define DATAY1 0x35 /* R Y-Axis Data 1 */ +#define DATAZ0 0x36 /* R Z-Axis Data 0 */ +#define DATAZ1 0x37 /* R Z-Axis Data 1 */ +#define FIFO_CTL 0x38 /* R/W FIFO control */ +#define FIFO_STATUS 0x39 /* R FIFO status */ +#define TAP_SIGN 0x3A /* R Sign and source for tap/double tap */ +/* Orientation ADXL346 only */ +#define ORIENT_CONF 0x3B /* R/W Orientation configuration */ +#define ORIENT 0x3C /* R Orientation status */ + +/* DEVIDs */ +#define ID_ADXL345 0xE5 +#define ID_ADXL346 0xE6 + +/* INT_ENABLE/INT_MAP/INT_SOURCE Bits */ +#define DATA_READY (1 << 7) +#define SINGLE_TAP (1 << 6) +#define DOUBLE_TAP (1 << 5) +#define ACTIVITY (1 << 4) +#define INACTIVITY (1 << 3) +#define FREE_FALL (1 << 2) +#define WATERMARK (1 << 1) +#define OVERRUN (1 << 0) + +/* ACT_INACT_CONTROL Bits */ +#define ACT_ACDC (1 << 7) +#define ACT_X_EN (1 << 6) +#define ACT_Y_EN (1 << 5) +#define ACT_Z_EN (1 << 4) +#define INACT_ACDC (1 << 3) +#define INACT_X_EN (1 << 2) +#define INACT_Y_EN (1 << 1) +#define INACT_Z_EN (1 << 0) + +/* TAP_AXES Bits */ +#define SUPPRESS (1 << 3) +#define TAP_X_EN (1 << 2) +#define TAP_Y_EN (1 << 1) +#define TAP_Z_EN (1 << 0) + +/* ACT_TAP_STATUS Bits */ +#define ACT_X_SRC (1 << 6) +#define ACT_Y_SRC (1 << 5) +#define ACT_Z_SRC (1 << 4) +#define ASLEEP (1 << 3) +#define TAP_X_SRC (1 << 2) +#define TAP_Y_SRC (1 << 1) +#define TAP_Z_SRC (1 << 0) + +/* BW_RATE Bits */ +#define LOW_POWER (1 << 4) +#define RATE(x) ((x) & 0xF) + +/* POWER_CTL Bits */ +#define PCTL_LINK (1 << 5) +#define PCTL_AUTO_SLEEP (1 << 4) +#define PCTL_MEASURE (1 << 3) +#define PCTL_SLEEP (1 << 2) +#define PCTL_WAKEUP(x) ((x) & 0x3) + +/* DATA_FORMAT Bits */ +#define SELF_TEST (1 << 7) +#define SPI (1 << 6) +#define INT_INVERT (1 << 5) +#define FULL_RES (1 << 3) +#define JUSTIFY (1 << 2) +#define RANGE(x) ((x) & 0x3) +#define RANGE_PM_2g 0 +#define RANGE_PM_4g 1 +#define RANGE_PM_8g 2 +#define RANGE_PM_16g 3 + +/* + * Maximum value our axis may get in full res mode for the input device + * (signed 13 bits) + */ +#define ADXL_FULLRES_MAX_VAL 4096 + +/* + * Maximum value our axis may get in fixed res mode for the input device + * (signed 10 bits) + */ +#define ADXL_FIXEDRES_MAX_VAL 512 + +/* FIFO_CTL Bits */ +#define FIFO_MODE(x) (((x) & 0x3) << 6) +#define FIFO_BYPASS 0 +#define FIFO_FIFO 1 +#define FIFO_STREAM 2 +#define FIFO_TRIGGER 3 +#define TRIGGER (1 << 5) +#define SAMPLES(x) ((x) & 0x1F) + +/* FIFO_STATUS Bits */ +#define FIFO_TRIG (1 << 7) +#define ENTRIES(x) ((x) & 0x3F) + +/* TAP_SIGN Bits ADXL346 only */ +#define XSIGN (1 << 6) +#define YSIGN (1 << 5) +#define ZSIGN (1 << 4) +#define XTAP (1 << 3) +#define YTAP (1 << 2) +#define ZTAP (1 << 1) + +/* ORIENT_CONF ADXL346 only */ +#define ORIENT_DEADZONE(x) (((x) & 0x7) << 4) +#define ORIENT_DIVISOR(x) ((x) & 0x7) + +/* ORIENT ADXL346 only */ +#define ADXL346_2D_VALID (1 << 6) +#define ADXL346_2D_ORIENT(x) (((x) & 0x3) >> 4) +#define ADXL346_3D_VALID (1 << 3) +#define ADXL346_3D_ORIENT(x) ((x) & 0x7) +#define ADXL346_2D_PORTRAIT_POS 0 /* +X */ +#define ADXL346_2D_PORTRAIT_NEG 1 /* -X */ +#define ADXL346_2D_LANDSCAPE_POS 2 /* +Y */ +#define ADXL346_2D_LANDSCAPE_NEG 3 /* -Y */ + +#define ADXL346_3D_FRONT 3 /* +X */ +#define ADXL346_3D_BACK 4 /* -X */ +#define ADXL346_3D_RIGHT 2 /* +Y */ +#define ADXL346_3D_LEFT 5 /* -Y */ +#define ADXL346_3D_TOP 1 /* +Z */ +#define ADXL346_3D_BOTTOM 6 /* -Z */ + +#undef ADXL_DEBUG + +#define ADXL_X_AXIS 0 +#define ADXL_Y_AXIS 1 +#define ADXL_Z_AXIS 2 + +#define AC_READ(ac, reg) ((ac)->bops->read((ac)->dev, reg)) +#define AC_WRITE(ac, reg, val) ((ac)->bops->write((ac)->dev, reg, val)) + +struct axis_triple { + int x; + int y; + int z; +}; + +struct adxl34x { + struct device *dev; + struct input_dev *input; + struct mutex mutex; /* reentrant protection for struct */ + struct adxl34x_platform_data pdata; + struct axis_triple swcal; + struct axis_triple hwcal; + struct axis_triple saved; + char phys[32]; + unsigned orient2d_saved; + unsigned orient3d_saved; + bool disabled; /* P: mutex */ + bool opened; /* P: mutex */ + bool suspended; /* P: mutex */ + bool fifo_delay; + int irq; + unsigned model; + unsigned int_mask; + + const struct adxl34x_bus_ops *bops; +}; + +static const struct adxl34x_platform_data adxl34x_default_init = { + .tap_threshold = 35, + .tap_duration = 3, + .tap_latency = 20, + .tap_window = 20, + .tap_axis_control = ADXL_TAP_X_EN | ADXL_TAP_Y_EN | ADXL_TAP_Z_EN, + .act_axis_control = 0xFF, + .activity_threshold = 6, + .inactivity_threshold = 4, + .inactivity_time = 3, + .free_fall_threshold = 8, + .free_fall_time = 0x20, + .data_rate = 8, + .data_range = ADXL_FULL_RES, + + .ev_type = EV_ABS, + .ev_code_x = ABS_X, /* EV_REL */ + .ev_code_y = ABS_Y, /* EV_REL */ + .ev_code_z = ABS_Z, /* EV_REL */ + + .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */ + .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK, + .fifo_mode = FIFO_STREAM, + .watermark = 0, +}; + +static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis) +{ + short buf[3]; + + ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf); + + mutex_lock(&ac->mutex); + ac->saved.x = (s16) le16_to_cpu(buf[0]); + axis->x = ac->saved.x; + + ac->saved.y = (s16) le16_to_cpu(buf[1]); + axis->y = ac->saved.y; + + ac->saved.z = (s16) le16_to_cpu(buf[2]); + axis->z = ac->saved.z; + mutex_unlock(&ac->mutex); +} + +static void adxl34x_service_ev_fifo(struct adxl34x *ac) +{ + struct adxl34x_platform_data *pdata = &ac->pdata; + struct axis_triple axis; + + adxl34x_get_triple(ac, &axis); + + input_event(ac->input, pdata->ev_type, pdata->ev_code_x, + axis.x - ac->swcal.x); + input_event(ac->input, pdata->ev_type, pdata->ev_code_y, + axis.y - ac->swcal.y); + input_event(ac->input, pdata->ev_type, pdata->ev_code_z, + axis.z - ac->swcal.z); +} + +static void adxl34x_report_key_single(struct input_dev *input, int key) +{ + input_report_key(input, key, true); + input_sync(input); + input_report_key(input, key, false); +} + +static void adxl34x_send_key_events(struct adxl34x *ac, + struct adxl34x_platform_data *pdata, int status, int press) +{ + int i; + + for (i = ADXL_X_AXIS; i <= ADXL_Z_AXIS; i++) { + if (status & (1 << (ADXL_Z_AXIS - i))) + input_report_key(ac->input, + pdata->ev_code_tap[i], press); + } +} + +static void adxl34x_do_tap(struct adxl34x *ac, + struct adxl34x_platform_data *pdata, int status) +{ + adxl34x_send_key_events(ac, pdata, status, true); + input_sync(ac->input); + adxl34x_send_key_events(ac, pdata, status, false); +} + +static irqreturn_t adxl34x_irq(int irq, void *handle) +{ + struct adxl34x *ac = handle; + struct adxl34x_platform_data *pdata = &ac->pdata; + int int_stat, tap_stat, samples, orient, orient_code; + + /* + * ACT_TAP_STATUS should be read before clearing the interrupt + * Avoid reading ACT_TAP_STATUS in case TAP detection is disabled + */ + + if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN)) + tap_stat = AC_READ(ac, ACT_TAP_STATUS); + else + tap_stat = 0; + + int_stat = AC_READ(ac, INT_SOURCE); + + if (int_stat & FREE_FALL) + adxl34x_report_key_single(ac->input, pdata->ev_code_ff); + + if (int_stat & OVERRUN) + dev_dbg(ac->dev, "OVERRUN\n"); + + if (int_stat & (SINGLE_TAP | DOUBLE_TAP)) { + adxl34x_do_tap(ac, pdata, tap_stat); + + if (int_stat & DOUBLE_TAP) + adxl34x_do_tap(ac, pdata, tap_stat); + } + + if (pdata->ev_code_act_inactivity) { + if (int_stat & ACTIVITY) + input_report_key(ac->input, + pdata->ev_code_act_inactivity, 1); + if (int_stat & INACTIVITY) + input_report_key(ac->input, + pdata->ev_code_act_inactivity, 0); + } + + /* + * ORIENTATION SENSING ADXL346 only + */ + if (pdata->orientation_enable) { + orient = AC_READ(ac, ORIENT); + if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) && + (orient & ADXL346_2D_VALID)) { + + orient_code = ADXL346_2D_ORIENT(orient); + /* Report orientation only when it changes */ + if (ac->orient2d_saved != orient_code) { + ac->orient2d_saved = orient_code; + adxl34x_report_key_single(ac->input, + pdata->ev_codes_orient_2d[orient_code]); + } + } + + if ((pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) && + (orient & ADXL346_3D_VALID)) { + + orient_code = ADXL346_3D_ORIENT(orient) - 1; + /* Report orientation only when it changes */ + if (ac->orient3d_saved != orient_code) { + ac->orient3d_saved = orient_code; + adxl34x_report_key_single(ac->input, + pdata->ev_codes_orient_3d[orient_code]); + } + } + } + + if (int_stat & (DATA_READY | WATERMARK)) { + + if (pdata->fifo_mode) + samples = ENTRIES(AC_READ(ac, FIFO_STATUS)) + 1; + else + samples = 1; + + for (; samples > 0; samples--) { + adxl34x_service_ev_fifo(ac); + /* + * To ensure that the FIFO has + * completely popped, there must be at least 5 us between + * the end of reading the data registers, signified by the + * transition to register 0x38 from 0x37 or the CS pin + * going high, and the start of new reads of the FIFO or + * reading the FIFO_STATUS register. For SPI operation at + * 1.5 MHz or lower, the register addressing portion of the + * transmission is sufficient delay to ensure the FIFO has + * completely popped. It is necessary for SPI operation + * greater than 1.5 MHz to de-assert the CS pin to ensure a + * total of 5 us, which is at most 3.4 us at 5 MHz + * operation. + */ + if (ac->fifo_delay && (samples > 1)) + udelay(3); + } + } + + input_sync(ac->input); + + return IRQ_HANDLED; +} + +static void __adxl34x_disable(struct adxl34x *ac) +{ + /* + * A '0' places the ADXL34x into standby mode + * with minimum power consumption. + */ + AC_WRITE(ac, POWER_CTL, 0); +} + +static void __adxl34x_enable(struct adxl34x *ac) +{ + AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE); +} + +void adxl34x_suspend(struct adxl34x *ac) +{ + mutex_lock(&ac->mutex); + + if (!ac->suspended && !ac->disabled && ac->opened) + __adxl34x_disable(ac); + + ac->suspended = true; + + mutex_unlock(&ac->mutex); +} +EXPORT_SYMBOL_GPL(adxl34x_suspend); + +void adxl34x_resume(struct adxl34x *ac) +{ + mutex_lock(&ac->mutex); + + if (ac->suspended && !ac->disabled && ac->opened) + __adxl34x_enable(ac); + + ac->suspended = false; + + mutex_unlock(&ac->mutex); +} +EXPORT_SYMBOL_GPL(adxl34x_resume); + +static ssize_t adxl34x_disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", ac->disabled); +} + +static ssize_t adxl34x_disable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + + if (!ac->suspended && ac->opened) { + if (val) { + if (!ac->disabled) + __adxl34x_disable(ac); + } else { + if (ac->disabled) + __adxl34x_enable(ac); + } + } + + ac->disabled = !!val; + + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(disable, 0664, adxl34x_disable_show, adxl34x_disable_store); + +static ssize_t adxl34x_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + ssize_t count; + + mutex_lock(&ac->mutex); + count = sprintf(buf, "%d,%d,%d\n", + ac->hwcal.x * 4 + ac->swcal.x, + ac->hwcal.y * 4 + ac->swcal.y, + ac->hwcal.z * 4 + ac->swcal.z); + mutex_unlock(&ac->mutex); + + return count; +} + +static ssize_t adxl34x_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + /* + * Hardware offset calibration has a resolution of 15.6 mg/LSB. + * We use HW calibration and handle the remaining bits in SW. (4mg/LSB) + */ + + mutex_lock(&ac->mutex); + ac->hwcal.x -= (ac->saved.x / 4); + ac->swcal.x = ac->saved.x % 4; + + ac->hwcal.y -= (ac->saved.y / 4); + ac->swcal.y = ac->saved.y % 4; + + ac->hwcal.z -= (ac->saved.z / 4); + ac->swcal.z = ac->saved.z % 4; + + AC_WRITE(ac, OFSX, (s8) ac->hwcal.x); + AC_WRITE(ac, OFSY, (s8) ac->hwcal.y); + AC_WRITE(ac, OFSZ, (s8) ac->hwcal.z); + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(calibrate, 0664, + adxl34x_calibrate_show, adxl34x_calibrate_store); + +static ssize_t adxl34x_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", RATE(ac->pdata.data_rate)); +} + +static ssize_t adxl34x_rate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + + ac->pdata.data_rate = RATE(val); + AC_WRITE(ac, BW_RATE, + ac->pdata.data_rate | + (ac->pdata.low_power_mode ? LOW_POWER : 0)); + + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(rate, 0664, adxl34x_rate_show, adxl34x_rate_store); + +static ssize_t adxl34x_autosleep_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + + return sprintf(buf, "%u\n", + ac->pdata.power_mode & (PCTL_AUTO_SLEEP | PCTL_LINK) ? 1 : 0); +} + +static ssize_t adxl34x_autosleep_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned long val; + int error; + + error = strict_strtoul(buf, 10, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + + if (val) + ac->pdata.power_mode |= (PCTL_AUTO_SLEEP | PCTL_LINK); + else + ac->pdata.power_mode &= ~(PCTL_AUTO_SLEEP | PCTL_LINK); + + if (!ac->disabled && !ac->suspended && ac->opened) + AC_WRITE(ac, POWER_CTL, ac->pdata.power_mode | PCTL_MEASURE); + + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(autosleep, 0664, + adxl34x_autosleep_show, adxl34x_autosleep_store); + +static ssize_t adxl34x_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + ssize_t count; + + mutex_lock(&ac->mutex); + count = sprintf(buf, "(%d, %d, %d)\n", + ac->saved.x, ac->saved.y, ac->saved.z); + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(position, S_IRUGO, adxl34x_position_show, NULL); + +#ifdef ADXL_DEBUG +static ssize_t adxl34x_write_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct adxl34x *ac = dev_get_drvdata(dev); + unsigned long val; + int error; + + /* + * This allows basic ADXL register write access for debug purposes. + */ + error = strict_strtoul(buf, 16, &val); + if (error) + return error; + + mutex_lock(&ac->mutex); + AC_WRITE(ac, val >> 8, val & 0xFF); + mutex_unlock(&ac->mutex); + + return count; +} + +static DEVICE_ATTR(write, 0664, NULL, adxl34x_write_store); +#endif + +static struct attribute *adxl34x_attributes[] = { + &dev_attr_disable.attr, + &dev_attr_calibrate.attr, + &dev_attr_rate.attr, + &dev_attr_autosleep.attr, + &dev_attr_position.attr, +#ifdef ADXL_DEBUG + &dev_attr_write.attr, +#endif + NULL +}; + +static const struct attribute_group adxl34x_attr_group = { + .attrs = adxl34x_attributes, +}; + +static int adxl34x_input_open(struct input_dev *input) +{ + struct adxl34x *ac = input_get_drvdata(input); + + mutex_lock(&ac->mutex); + + if (!ac->suspended && !ac->disabled) + __adxl34x_enable(ac); + + ac->opened = true; + + mutex_unlock(&ac->mutex); + + return 0; +} + +static void adxl34x_input_close(struct input_dev *input) +{ + struct adxl34x *ac = input_get_drvdata(input); + + mutex_lock(&ac->mutex); + + if (!ac->suspended && !ac->disabled) + __adxl34x_disable(ac); + + ac->opened = false; + + mutex_unlock(&ac->mutex); +} + +struct adxl34x *adxl34x_probe(struct device *dev, int irq, + bool fifo_delay_default, + const struct adxl34x_bus_ops *bops) +{ + struct adxl34x *ac; + struct input_dev *input_dev; + const struct adxl34x_platform_data *pdata; + int err, range, i; + unsigned char revid; + + if (!irq) { + dev_err(dev, "no IRQ?\n"); + err = -ENODEV; + goto err_out; + } + + ac = kzalloc(sizeof(*ac), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ac || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ac->fifo_delay = fifo_delay_default; + + pdata = dev->platform_data; + if (!pdata) { + dev_dbg(dev, + "No platfrom data: Using default initialization\n"); + pdata = &adxl34x_default_init; + } + + ac->pdata = *pdata; + pdata = &ac->pdata; + + ac->input = input_dev; + ac->disabled = true; + ac->dev = dev; + ac->irq = irq; + ac->bops = bops; + + mutex_init(&ac->mutex); + + input_dev->name = "ADXL34x accelerometer"; + revid = ac->bops->read(dev, DEVID); + + switch (revid) { + case ID_ADXL345: + ac->model = 345; + break; + case ID_ADXL346: + ac->model = 346; + break; + default: + dev_err(dev, "Failed to probe %s\n", input_dev->name); + err = -ENODEV; + goto err_free_mem; + } + + snprintf(ac->phys, sizeof(ac->phys), "%s/input0", dev_name(dev)); + + input_dev->phys = ac->phys; + input_dev->dev.parent = dev; + input_dev->id.product = ac->model; + input_dev->id.bustype = bops->bustype; + input_dev->open = adxl34x_input_open; + input_dev->close = adxl34x_input_close; + + input_set_drvdata(input_dev, ac); + + __set_bit(ac->pdata.ev_type, input_dev->evbit); + + if (ac->pdata.ev_type == EV_REL) { + __set_bit(REL_X, input_dev->relbit); + __set_bit(REL_Y, input_dev->relbit); + __set_bit(REL_Z, input_dev->relbit); + } else { + /* EV_ABS */ + __set_bit(ABS_X, input_dev->absbit); + __set_bit(ABS_Y, input_dev->absbit); + __set_bit(ABS_Z, input_dev->absbit); + + if (pdata->data_range & FULL_RES) + range = ADXL_FULLRES_MAX_VAL; /* Signed 13-bit */ + else + range = ADXL_FIXEDRES_MAX_VAL; /* Signed 10-bit */ + + input_set_abs_params(input_dev, ABS_X, -range, range, 3, 3); + input_set_abs_params(input_dev, ABS_Y, -range, range, 3, 3); + input_set_abs_params(input_dev, ABS_Z, -range, range, 3, 3); + } + + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(pdata->ev_code_tap[ADXL_X_AXIS], input_dev->keybit); + __set_bit(pdata->ev_code_tap[ADXL_Y_AXIS], input_dev->keybit); + __set_bit(pdata->ev_code_tap[ADXL_Z_AXIS], input_dev->keybit); + + if (pdata->ev_code_ff) { + ac->int_mask = FREE_FALL; + __set_bit(pdata->ev_code_ff, input_dev->keybit); + } + + if (pdata->ev_code_act_inactivity) + __set_bit(pdata->ev_code_act_inactivity, input_dev->keybit); + + ac->int_mask |= ACTIVITY | INACTIVITY; + + if (pdata->watermark) { + ac->int_mask |= WATERMARK; + if (!FIFO_MODE(pdata->fifo_mode)) + ac->pdata.fifo_mode |= FIFO_STREAM; + } else { + ac->int_mask |= DATA_READY; + } + + if (pdata->tap_axis_control & (TAP_X_EN | TAP_Y_EN | TAP_Z_EN)) + ac->int_mask |= SINGLE_TAP | DOUBLE_TAP; + + if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS) + ac->fifo_delay = false; + + ac->bops->write(dev, POWER_CTL, 0); + + err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + dev_name(dev), ac); + if (err) { + dev_err(dev, "irq %d busy?\n", ac->irq); + goto err_free_mem; + } + + err = sysfs_create_group(&dev->kobj, &adxl34x_attr_group); + if (err) + goto err_free_irq; + + err = input_register_device(input_dev); + if (err) + goto err_remove_attr; + + AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); + AC_WRITE(ac, OFSX, pdata->x_axis_offset); + ac->hwcal.x = pdata->x_axis_offset; + AC_WRITE(ac, OFSY, pdata->y_axis_offset); + ac->hwcal.y = pdata->y_axis_offset; + AC_WRITE(ac, OFSZ, pdata->z_axis_offset); + ac->hwcal.z = pdata->z_axis_offset; + AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); + AC_WRITE(ac, DUR, pdata->tap_duration); + AC_WRITE(ac, LATENT, pdata->tap_latency); + AC_WRITE(ac, WINDOW, pdata->tap_window); + AC_WRITE(ac, THRESH_ACT, pdata->activity_threshold); + AC_WRITE(ac, THRESH_INACT, pdata->inactivity_threshold); + AC_WRITE(ac, TIME_INACT, pdata->inactivity_time); + AC_WRITE(ac, THRESH_FF, pdata->free_fall_threshold); + AC_WRITE(ac, TIME_FF, pdata->free_fall_time); + AC_WRITE(ac, TAP_AXES, pdata->tap_axis_control); + AC_WRITE(ac, ACT_INACT_CTL, pdata->act_axis_control); + AC_WRITE(ac, BW_RATE, RATE(ac->pdata.data_rate) | + (pdata->low_power_mode ? LOW_POWER : 0)); + AC_WRITE(ac, DATA_FORMAT, pdata->data_range); + AC_WRITE(ac, FIFO_CTL, FIFO_MODE(pdata->fifo_mode) | + SAMPLES(pdata->watermark)); + + if (pdata->use_int2) { + /* Map all INTs to INT2 */ + AC_WRITE(ac, INT_MAP, ac->int_mask | OVERRUN); + } else { + /* Map all INTs to INT1 */ + AC_WRITE(ac, INT_MAP, 0); + } + + if (ac->model == 346 && ac->pdata.orientation_enable) { + AC_WRITE(ac, ORIENT_CONF, + ORIENT_DEADZONE(ac->pdata.deadzone_angle) | + ORIENT_DIVISOR(ac->pdata.divisor_length)); + + ac->orient2d_saved = 1234; + ac->orient3d_saved = 1234; + + if (pdata->orientation_enable & ADXL_EN_ORIENTATION_3D) + for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_3d); i++) + __set_bit(pdata->ev_codes_orient_3d[i], + input_dev->keybit); + + if (pdata->orientation_enable & ADXL_EN_ORIENTATION_2D) + for (i = 0; i < ARRAY_SIZE(pdata->ev_codes_orient_2d); i++) + __set_bit(pdata->ev_codes_orient_2d[i], + input_dev->keybit); + } else { + ac->pdata.orientation_enable = 0; + } + + AC_WRITE(ac, INT_ENABLE, ac->int_mask | OVERRUN); + + ac->pdata.power_mode &= (PCTL_AUTO_SLEEP | PCTL_LINK); + + return ac; + + err_remove_attr: + sysfs_remove_group(&dev->kobj, &adxl34x_attr_group); + err_free_irq: + free_irq(ac->irq, ac); + err_free_mem: + input_free_device(input_dev); + kfree(ac); + err_out: + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(adxl34x_probe); + +int adxl34x_remove(struct adxl34x *ac) +{ + sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group); + free_irq(ac->irq, ac); + input_unregister_device(ac->input); + dev_dbg(ac->dev, "unregistered accelerometer\n"); + kfree(ac); + + return 0; +} +EXPORT_SYMBOL_GPL(adxl34x_remove); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/adxl34x.h b/drivers/input/misc/adxl34x.h new file mode 100644 index 0000000..bbbc80f --- /dev/null +++ b/drivers/input/misc/adxl34x.h @@ -0,0 +1,30 @@ +/* + * ADXL345/346 Three-Axis Digital Accelerometers (I2C/SPI Interface) + * + * Enter bugs at http://blackfin.uclinux.org/ + * + * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. + * Licensed under the GPL-2 or later. + */ + +#ifndef _ADXL34X_H_ +#define _ADXL34X_H_ + +struct device; +struct adxl34x; + +struct adxl34x_bus_ops { + u16 bustype; + int (*read)(struct device *, unsigned char); + int (*read_block)(struct device *, unsigned char, int, void *); + int (*write)(struct device *, unsigned char, unsigned char); +}; + +void adxl34x_suspend(struct adxl34x *ac); +void adxl34x_resume(struct adxl34x *ac); +struct adxl34x *adxl34x_probe(struct device *dev, int irq, + bool fifo_delay_default, + const struct adxl34x_bus_ops *bops); +int adxl34x_remove(struct adxl34x *ac); + +#endif diff --git a/drivers/input/misc/atlas_btns.c b/drivers/input/misc/atlas_btns.c index dfaa9a0..601f737 100644 --- a/drivers/input/misc/atlas_btns.c +++ b/drivers/input/misc/atlas_btns.c @@ -21,6 +21,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -60,12 +62,11 @@ static acpi_status acpi_atlas_button_handler(u32 function, input_report_key(input_dev, atlas_keymap[code], key_down); input_sync(input_dev); - status = 0; + status = AE_OK; } else { - printk(KERN_WARNING "atlas: shrugged on unexpected function" - ":function=%x,address=%lx,value=%x\n", + pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n", function, (unsigned long)address, (u32)*value); - status = -EINVAL; + status = AE_BAD_PARAMETER; } return status; @@ -79,7 +80,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) input_dev = input_allocate_device(); if (!input_dev) { - printk(KERN_ERR "atlas: unable to allocate input device\n"); + pr_err("unable to allocate input device\n"); return -ENOMEM; } @@ -102,7 +103,7 @@ static int atlas_acpi_button_add(struct acpi_device *device) err = input_register_device(input_dev); if (err) { - printk(KERN_ERR "atlas: couldn't register input device\n"); + pr_err("couldn't register input device\n"); input_free_device(input_dev); return err; } @@ -112,12 +113,12 @@ static int atlas_acpi_button_add(struct acpi_device *device) 0x81, &acpi_atlas_button_handler, &acpi_atlas_button_setup, device); if (ACPI_FAILURE(status)) { - printk(KERN_ERR "Atlas: Error installing addr spc handler\n"); + pr_err("error installing addr spc handler\n"); input_unregister_device(input_dev); - status = -EINVAL; + err = -EINVAL; } - return status; + return err; } static int atlas_acpi_button_remove(struct acpi_device *device, int type) @@ -126,14 +127,12 @@ static int atlas_acpi_button_remove(struct acpi_device *device, int type) status = acpi_remove_address_space_handler(device->handle, 0x81, &acpi_atlas_button_handler); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR "Atlas: Error removing addr spc handler\n"); - status = -EINVAL; - } + if (ACPI_FAILURE(status)) + pr_err("error removing addr spc handler\n"); input_unregister_device(input_dev); - return status; + return 0; } static const struct acpi_device_id atlas_device_ids[] = { @@ -145,6 +144,7 @@ MODULE_DEVICE_TABLE(acpi, atlas_device_ids); static struct acpi_driver atlas_acpi_driver = { .name = ACPI_ATLAS_NAME, .class = ACPI_ATLAS_CLASS, + .owner = THIS_MODULE, .ids = atlas_device_ids, .ops = { .add = atlas_acpi_button_add, @@ -154,18 +154,10 @@ static struct acpi_driver atlas_acpi_driver = { static int __init atlas_acpi_init(void) { - int result; - if (acpi_disabled) return -ENODEV; - result = acpi_bus_register_driver(&atlas_acpi_driver); - if (result < 0) { - printk(KERN_ERR "Atlas ACPI: Unable to register driver\n"); - return -ENODEV; - } - - return 0; + return acpi_bus_register_driver(&atlas_acpi_driver); } static void __exit atlas_acpi_exit(void) diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c new file mode 100644 index 0000000..57c294f --- /dev/null +++ b/drivers/input/misc/pwm-beeper.c @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> + * PWM beeper driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/slab.h> + +struct pwm_beeper { + struct input_dev *input; + struct pwm_device *pwm; + unsigned long period; +}; + +#define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) + +static int pwm_beeper_event(struct input_dev *input, + unsigned int type, unsigned int code, int value) +{ + int ret = 0; + struct pwm_beeper *beeper = input_get_drvdata(input); + unsigned long period; + + if (type != EV_SND || value < 0) + return -EINVAL; + + switch (code) { + case SND_BELL: + value = value ? 1000 : 0; + break; + case SND_TONE: + break; + default: + return -EINVAL; + } + + if (value == 0) { + pwm_config(beeper->pwm, 0, 0); + pwm_disable(beeper->pwm); + } else { + period = HZ_TO_NANOSECONDS(value); + ret = pwm_config(beeper->pwm, period / 2, period); + if (ret) + return ret; + ret = pwm_enable(beeper->pwm); + if (ret) + return ret; + beeper->period = period; + } + + return 0; +} + +static int __devinit pwm_beeper_probe(struct platform_device *pdev) +{ + unsigned long pwm_id = (unsigned long)pdev->dev.platform_data; + struct pwm_beeper *beeper; + int error; + + beeper = kzalloc(sizeof(*beeper), GFP_KERNEL); + if (!beeper) + return -ENOMEM; + + beeper->pwm = pwm_request(pwm_id, "pwm beeper"); + + if (IS_ERR(beeper->pwm)) { + error = PTR_ERR(beeper->pwm); + dev_err(&pdev->dev, "Failed to request pwm device: %d\n", error); + goto err_free; + } + + beeper->input = input_allocate_device(); + if (!beeper->input) { + dev_err(&pdev->dev, "Failed to allocate input device\n"); + error = -ENOMEM; + goto err_pwm_free; + } + beeper->input->dev.parent = &pdev->dev; + + beeper->input->name = "pwm-beeper"; + beeper->input->phys = "pwm/input0"; + beeper->input->id.bustype = BUS_HOST; + beeper->input->id.vendor = 0x001f; + beeper->input->id.product = 0x0001; + beeper->input->id.version = 0x0100; + + beeper->input->evbit[0] = BIT(EV_SND); + beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); + + beeper->input->event = pwm_beeper_event; + + input_set_drvdata(beeper->input, beeper); + + error = input_register_device(beeper->input); + if (error) { + dev_err(&pdev->dev, "Failed to register input device: %d\n", error); + goto err_input_free; + } + + platform_set_drvdata(pdev, beeper); + + return 0; + +err_input_free: + input_free_device(beeper->input); +err_pwm_free: + pwm_free(beeper->pwm); +err_free: + kfree(beeper); + + return error; +} + +static int __devexit pwm_beeper_remove(struct platform_device *pdev) +{ + struct pwm_beeper *beeper = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + input_unregister_device(beeper->input); + + pwm_disable(beeper->pwm); + pwm_free(beeper->pwm); + + kfree(beeper); + + return 0; +} + +#ifdef CONFIG_PM +static int pwm_beeper_suspend(struct device *dev) +{ + struct pwm_beeper *beeper = dev_get_drvdata(dev); + + if (beeper->period) + pwm_disable(beeper->pwm); + + return 0; +} + +static int pwm_beeper_resume(struct device *dev) +{ + struct pwm_beeper *beeper = dev_get_drvdata(dev); + + if (beeper->period) { + pwm_config(beeper->pwm, beeper->period / 2, beeper->period); + pwm_enable(beeper->pwm); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, + pwm_beeper_suspend, pwm_beeper_resume); + +#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops) +#else +#define PWM_BEEPER_PM_OPS NULL +#endif + +static struct platform_driver pwm_beeper_driver = { + .probe = pwm_beeper_probe, + .remove = __devexit_p(pwm_beeper_remove), + .driver = { + .name = "pwm-beeper", + .owner = THIS_MODULE, + .pm = PWM_BEEPER_PM_OPS, + }, +}; + +static int __init pwm_beeper_init(void) +{ + return platform_driver_register(&pwm_beeper_driver); +} +module_init(pwm_beeper_init); + +static void __exit pwm_beeper_exit(void) +{ + platform_driver_unregister(&pwm_beeper_driver); +} +module_exit(pwm_beeper_exit); + +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("PWM beeper driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:pwm-beeper"); diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c index e9069b8..f16972b 100644 --- a/drivers/input/misc/twl4030-pwrbutton.c +++ b/drivers/input/misc/twl4030-pwrbutton.c @@ -52,7 +52,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr) return IRQ_HANDLED; } -static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev) +static int __init twl4030_pwrbutton_probe(struct platform_device *pdev) { struct input_dev *pwr; int irq = platform_get_irq(pdev, 0); @@ -95,7 +95,7 @@ free_input_dev: return err; } -static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev) +static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev) { struct input_dev *pwr = platform_get_drvdata(pdev); int irq = platform_get_irq(pdev, 0); @@ -106,9 +106,8 @@ static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev) return 0; } -struct platform_driver twl4030_pwrbutton_driver = { - .probe = twl4030_pwrbutton_probe, - .remove = __devexit_p(twl4030_pwrbutton_remove), +static struct platform_driver twl4030_pwrbutton_driver = { + .remove = __exit_p(twl4030_pwrbutton_remove), .driver = { .name = "twl4030_pwrbutton", .owner = THIS_MODULE, @@ -117,7 +116,8 @@ struct platform_driver twl4030_pwrbutton_driver = { static int __init twl4030_pwrbutton_init(void) { - return platform_driver_register(&twl4030_pwrbutton_driver); + return platform_driver_probe(&twl4030_pwrbutton_driver, + twl4030_pwrbutton_probe); } module_init(twl4030_pwrbutton_init); diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index 4dac8b7..12501de 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -1347,7 +1347,7 @@ static int __init wb_module_init(void) err = map_bios(); if (err) - return err; + goto err_free_keymap; err = platform_driver_register(&wistron_driver); if (err) @@ -1371,6 +1371,8 @@ static int __init wb_module_init(void) platform_driver_unregister(&wistron_driver); err_unmap_bios: unmap_bios(); + err_free_keymap: + kfree(keymap); return err; } diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c index 6dedded..ea67c49 100644 --- a/drivers/input/mouse/bcm5974.c +++ b/drivers/input/mouse/bcm5974.c @@ -312,6 +312,8 @@ static void setup_events_to_report(struct input_dev *input_dev, __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); __set_bit(BTN_LEFT, input_dev->keybit); + + input_set_events_per_packet(input_dev, 60); } /* report button data as logical button state */ @@ -580,23 +582,30 @@ exit: */ static int bcm5974_start_traffic(struct bcm5974 *dev) { - if (bcm5974_wellspring_mode(dev, true)) { + int error; + + error = bcm5974_wellspring_mode(dev, true); + if (error) { dprintk(1, "bcm5974: mode switch failed\n"); - goto error; + goto err_out; } - if (usb_submit_urb(dev->bt_urb, GFP_KERNEL)) - goto error; + error = usb_submit_urb(dev->bt_urb, GFP_KERNEL); + if (error) + goto err_reset_mode; - if (usb_submit_urb(dev->tp_urb, GFP_KERNEL)) + error = usb_submit_urb(dev->tp_urb, GFP_KERNEL); + if (error) goto err_kill_bt; return 0; err_kill_bt: usb_kill_urb(dev->bt_urb); -error: - return -EIO; +err_reset_mode: + bcm5974_wellspring_mode(dev, false); +err_out: + return error; } static void bcm5974_pause_traffic(struct bcm5974 *dev) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 705589d..8c32440 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -502,7 +502,9 @@ static void synaptics_process_packet(struct psmouse *psmouse) } input_report_abs(dev, ABS_PRESSURE, hw.z); - input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); + if (SYN_CAP_PALMDETECT(priv->capabilities)) + input_report_abs(dev, ABS_TOOL_WIDTH, finger_width); + input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1); input_report_key(dev, BTN_LEFT, hw.left); input_report_key(dev, BTN_RIGHT, hw.right); @@ -602,7 +604,9 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) input_set_abs_params(dev, ABS_Y, YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0); input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); - __set_bit(ABS_TOOL_WIDTH, dev->absbit); + + if (SYN_CAP_PALMDETECT(priv->capabilities)) + input_set_abs_params(dev, ABS_TOOL_WIDTH, 0, 15, 0, 0); __set_bit(EV_KEY, dev->evbit); __set_bit(BTN_TOUCH, dev->keybit); diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c index f34b22b..d8f68f7 100644 --- a/drivers/input/mousedev.c +++ b/drivers/input/mousedev.c @@ -57,7 +57,6 @@ struct mousedev_hw_data { }; struct mousedev { - int exist; int open; int minor; struct input_handle handle; @@ -66,6 +65,7 @@ struct mousedev { spinlock_t client_lock; /* protects client_list */ struct mutex mutex; struct device dev; + bool exist; struct list_head mixdev_node; int mixdev_open; @@ -765,10 +765,15 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait) { struct mousedev_client *client = file->private_data; struct mousedev *mousedev = client->mousedev; + unsigned int mask; poll_wait(file, &mousedev->wait, wait); - return ((client->ready || client->buffer) ? (POLLIN | POLLRDNORM) : 0) | - (mousedev->exist ? 0 : (POLLHUP | POLLERR)); + + mask = mousedev->exist ? POLLOUT | POLLWRNORM : POLLHUP | POLLERR; + if (client->ready || client->buffer) + mask |= POLLIN | POLLRDNORM; + + return mask; } static const struct file_operations mousedev_fops = { @@ -802,7 +807,7 @@ static void mousedev_remove_chrdev(struct mousedev *mousedev) static void mousedev_mark_dead(struct mousedev *mousedev) { mutex_lock(&mousedev->mutex); - mousedev->exist = 0; + mousedev->exist = false; mutex_unlock(&mousedev->mutex); } @@ -862,7 +867,7 @@ static struct mousedev *mousedev_create(struct input_dev *dev, dev_set_name(&mousedev->dev, "mouse%d", minor); mousedev->minor = minor; - mousedev->exist = 1; + mousedev->exist = true; mousedev->handle.dev = input_get_device(dev); mousedev->handle.name = dev_name(&mousedev->dev); mousedev->handle.handler = handler; diff --git a/drivers/input/serio/i8042-ppcio.h b/drivers/input/serio/i8042-ppcio.h index 2906e1b..f708c75 100644 --- a/drivers/input/serio/i8042-ppcio.h +++ b/drivers/input/serio/i8042-ppcio.h @@ -52,81 +52,6 @@ static inline void i8042_platform_exit(void) { } -#elif defined(CONFIG_SPRUCE) - -#define I8042_KBD_IRQ 22 -#define I8042_AUX_IRQ 21 - -#define I8042_KBD_PHYS_DESC "spruceps2/serio0" -#define I8042_AUX_PHYS_DESC "spruceps2/serio1" -#define I8042_MUX_PHYS_DESC "spruceps2/serio%d" - -#define I8042_COMMAND_REG 0xff810000 -#define I8042_DATA_REG 0xff810001 - -static inline int i8042_read_data(void) -{ - unsigned long kbd_data; - - __raw_writel(0x00000088, 0xff500008); - eieio(); - - __raw_writel(0x03000000, 0xff50000c); - eieio(); - - asm volatile("lis 7,0xff88 \n\ - lswi 6,7,0x8 \n\ - mr %0,6" - : "=r" (kbd_data) :: "6", "7"); - - __raw_writel(0x00000000, 0xff50000c); - eieio(); - - return (unsigned char)(kbd_data >> 24); -} - -static inline int i8042_read_status(void) -{ - unsigned long kbd_status; - - __raw_writel(0x00000088, 0xff500008); - eieio(); - - __raw_writel(0x03000000, 0xff50000c); - eieio(); - - asm volatile("lis 7,0xff88 \n\ - ori 7,7,0x8 \n\ - lswi 6,7,0x8 \n\ - mr %0,6" - : "=r" (kbd_status) :: "6", "7"); - - __raw_writel(0x00000000, 0xff50000c); - eieio(); - - return (unsigned char)(kbd_status >> 24); -} - -static inline void i8042_write_data(int val) -{ - *((unsigned char *)0xff810000) = (char)val; -} - -static inline void i8042_write_command(int val) -{ - *((unsigned char *)0xff810001) = (char)val; -} - -static inline int i8042_platform_init(void) -{ - i8042_reset = 1; - return 0; -} - -static inline void i8042_platform_exit(void) -{ -} - #else #include "i8042-io.h" diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 6440a8f..258b98b 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -861,9 +861,6 @@ static int i8042_controller_selftest(void) unsigned char param; int i = 0; - if (!i8042_reset) - return 0; - /* * We try this 5 times; on some really fragile systems this does not * take the first time... @@ -1020,7 +1017,8 @@ static void i8042_controller_reset(void) * Reset the controller if requested. */ - i8042_controller_selftest(); + if (i8042_reset) + i8042_controller_selftest(); /* * Restore the original control register setting. @@ -1094,23 +1092,11 @@ static void i8042_dritek_enable(void) #ifdef CONFIG_PM /* - * Here we try to restore the original BIOS settings to avoid - * upsetting it. - */ - -static int i8042_pm_reset(struct device *dev) -{ - i8042_controller_reset(); - - return 0; -} - -/* * Here we try to reset everything back to a state we had * before suspending. */ -static int i8042_pm_restore(struct device *dev) +static int i8042_controller_resume(bool force_reset) { int error; @@ -1118,9 +1104,11 @@ static int i8042_pm_restore(struct device *dev) if (error) return error; - error = i8042_controller_selftest(); - if (error) - return error; + if (i8042_reset || force_reset) { + error = i8042_controller_selftest(); + if (error) + return error; + } /* * Restore original CTR value and disable all ports @@ -1162,6 +1150,28 @@ static int i8042_pm_restore(struct device *dev) return 0; } +/* + * Here we try to restore the original BIOS settings to avoid + * upsetting it. + */ + +static int i8042_pm_reset(struct device *dev) +{ + i8042_controller_reset(); + + return 0; +} + +static int i8042_pm_resume(struct device *dev) +{ + /* + * On resume from S2R we always try to reset the controller + * to bring it in a sane state. (In case of S2D we expect + * BIOS to reset the controller for us.) + */ + return i8042_controller_resume(true); +} + static int i8042_pm_thaw(struct device *dev) { i8042_interrupt(0, NULL); @@ -1169,9 +1179,14 @@ static int i8042_pm_thaw(struct device *dev) return 0; } +static int i8042_pm_restore(struct device *dev) +{ + return i8042_controller_resume(false); +} + static const struct dev_pm_ops i8042_pm_ops = { .suspend = i8042_pm_reset, - .resume = i8042_pm_restore, + .resume = i8042_pm_resume, .thaw = i8042_pm_thaw, .poweroff = i8042_pm_reset, .restore = i8042_pm_restore, @@ -1389,9 +1404,11 @@ static int __init i8042_probe(struct platform_device *dev) i8042_platform_device = dev; - error = i8042_controller_selftest(); - if (error) - return error; + if (i8042_reset) { + error = i8042_controller_selftest(); + if (error) + return error; + } error = i8042_controller_init(); if (error) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 415f630..ce0b460 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -158,6 +158,39 @@ static int wacom_ptu_irq(struct wacom_wac *wacom) return 1; } +static int wacom_dtu_irq(struct wacom_wac *wacom) +{ + struct wacom_features *features = &wacom->features; + char *data = wacom->data; + struct input_dev *input = wacom->input; + int prox = data[1] & 0x20, pressure; + + dbg("wacom_dtu_irq: received report #%d", data[0]); + + if (prox) { + /* Going into proximity select tool */ + wacom->tool[0] = (data[1] & 0x0c) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN; + if (wacom->tool[0] == BTN_TOOL_PEN) + wacom->id[0] = STYLUS_DEVICE_ID; + else + wacom->id[0] = ERASER_DEVICE_ID; + } + input_report_key(input, BTN_STYLUS, data[1] & 0x02); + input_report_key(input, BTN_STYLUS2, data[1] & 0x10); + input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2])); + input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4])); + pressure = ((data[7] & 0x01) << 8) | data[6]; + if (pressure < 0) + pressure = features->pressure_max + pressure + 1; + input_report_abs(input, ABS_PRESSURE, pressure); + input_report_key(input, BTN_TOUCH, data[1] & 0x05); + if (!prox) /* out-prox */ + wacom->id[0] = 0; + input_report_key(input, wacom->tool[0], prox); + input_report_abs(input, ABS_MISC, wacom->id[0]); + return 1; +} + static int wacom_graphire_irq(struct wacom_wac *wacom) { struct wacom_features *features = &wacom->features; @@ -845,6 +878,10 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) sync = wacom_ptu_irq(wacom_wac); break; + case DTU: + sync = wacom_dtu_irq(wacom_wac); + break; + case INTUOS: case INTUOS3S: case INTUOS3: @@ -1030,6 +1067,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev, case PL: case PTU: + case DTU: __set_bit(BTN_TOOL_PEN, input_dev->keybit); __set_bit(BTN_STYLUS, input_dev->keybit); __set_bit(BTN_STYLUS2, input_dev->keybit); @@ -1155,6 +1193,10 @@ static const struct wacom_features wacom_features_0xC6 = { "Wacom Cintiq 12WX", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, 63, WACOM_BEE }; static const struct wacom_features wacom_features_0xC7 = { "Wacom DTU1931", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, 0, PL }; +static const struct wacom_features wacom_features_0xCE = + { "Wacom DTU2231", WACOM_PKGLEN_GRAPHIRE, 47864, 27011, 511, 0, DTU }; +static const struct wacom_features wacom_features_0xF0 = + { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, 0, DTU }; static const struct wacom_features wacom_features_0xCC = { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, 63, WACOM_21UX2 }; static const struct wacom_features wacom_features_0x90 = @@ -1234,6 +1276,8 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0xC5) }, { USB_DEVICE_WACOM(0xC6) }, { USB_DEVICE_WACOM(0xC7) }, + { USB_DEVICE_WACOM(0xCE) }, + { USB_DEVICE_WACOM(0xF0) }, { USB_DEVICE_WACOM(0xCC) }, { USB_DEVICE_WACOM(0x90) }, { USB_DEVICE_WACOM(0x93) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 854b920..99e1a54 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -43,6 +43,7 @@ enum { WACOM_G4, PTU, PL, + DTU, INTUOS, INTUOS3S, INTUOS3, diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3b9d5e2..61f3518 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -55,37 +55,36 @@ config TOUCHSCREEN_AD7877 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 +config TOUCHSCREEN_AD7879 + tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface" help - Say Y here if you have a touchscreen interface using the - AD7879-1/AD7889-1 controller, and your board-specific - initialization code includes that in its table of I2C devices. + Say Y here if you want to support a touchscreen interface using + the AD7879-1/AD7889-1 controller. - If unsure, say N (but it's safe to say "Y"). + You should select a bus connection too. To compile this driver as a module, choose M here: the module will be called ad7879. +config TOUCHSCREEN_AD7879_I2C + tristate "support I2C bus connection" + depends on TOUCHSCREEN_AD7879 && I2C + help + Say Y here if you have AD7879-1/AD7889-1 hooked to an I2C bus. + + To compile this driver as a module, choose M here: the + module will be called ad7879-i2c. + config TOUCHSCREEN_AD7879_SPI - tristate "AD7879 based touchscreens: AD7879 SPI Interface" - depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n - select TOUCHSCREEN_AD7879 + tristate "support SPI bus connection" + depends on TOUCHSCREEN_AD7879 && SPI_MASTER help - Say Y here if you have a touchscreen interface using the - AD7879/AD7889 controller, and your board-specific initialization - code includes that in its table of SPI devices. + Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus. 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 + module will be called ad7879-spi. config TOUCHSCREEN_BITSY tristate "Compaq iPAQ H3600 (Bitsy) touchscreen" @@ -99,6 +98,20 @@ config TOUCHSCREEN_BITSY To compile this driver as a module, choose M here: the module will be called h3600_ts_input. +config TOUCHSCREEN_CY8CTMG110 + tristate "cy8ctmg110 touchscreen" + depends on I2C + depends on GPIOLIB + + help + Say Y here if you have a cy8ctmg110 capacitive touchscreen on + an AAVA device. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called cy8ctmg110_ts. + config TOUCHSCREEN_DA9034 tristate "Touchscreen support for Dialog Semiconductor DA9034" depends on PMIC_DA903X @@ -292,6 +305,18 @@ config TOUCHSCREEN_PENMOUNT To compile this driver as a module, choose M here: the module will be called penmount. +config TOUCHSCREEN_QT602240 + tristate "QT602240 I2C Touchscreen" + depends on I2C + help + Say Y here if you have the AT42QT602240/ATMXT224 I2C touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called qt602240_ts. + config TOUCHSCREEN_MIGOR tristate "Renesas MIGO-R touchscreen" depends on SH_MIGOR && I2C @@ -540,9 +565,9 @@ config TOUCHSCREEN_USB_ZYTRONIC bool "Zytronic controller" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE -config TOUCHSCREEN_USB_ETT_TC5UH +config TOUCHSCREEN_USB_ETT_TC45USB default y - bool "ET&T TC5UH touchscreen controler support" if EMBEDDED + bool "ET&T USB series TC4UM/TC5UH touchscreen controler support" if EMBEDDED depends on TOUCHSCREEN_USB_COMPOSITE config TOUCHSCREEN_USB_NEXIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 497964a..bd6f30b4 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -9,9 +9,13 @@ wm97xx-ts-y := wm97xx-core.o obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o +obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o +obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.o @@ -30,6 +34,7 @@ obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o +obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o @@ -38,7 +43,6 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o -obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c new file mode 100644 index 0000000..d82a38e --- /dev/null +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -0,0 +1,143 @@ +/* + * AD7879-1/AD7889-1 touchscreen (I2C bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> /* BUS_I2C */ +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/types.h> + +#include "ad7879.h" + +#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */ + +#ifdef CONFIG_PM +static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_suspend(ts); + + return 0; +} + +static int ad7879_i2c_resume(struct i2c_client *client) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_resume(ts); + + return 0; +} +#else +# define ad7879_i2c_suspend NULL +# define ad7879_i2c_resume NULL +#endif + +/* All registers are word-sized. + * AD7879 uses a high-byte first convention. + */ +static int ad7879_i2c_read(struct device *dev, u8 reg) +{ + struct i2c_client *client = to_i2c_client(dev); + + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +static int ad7879_i2c_multi_read(struct device *dev, + u8 first_reg, u8 count, u16 *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + u8 idx; + + i2c_smbus_read_i2c_block_data(client, first_reg, count * 2, (u8 *)buf); + + for (idx = 0; idx < count; ++idx) + buf[idx] = swab16(buf[idx]); + + return 0; +} + +static int ad7879_i2c_write(struct device *dev, u8 reg, u16 val) +{ + struct i2c_client *client = to_i2c_client(dev); + + return i2c_smbus_write_word_data(client, reg, swab16(val)); +} + +static const struct ad7879_bus_ops ad7879_i2c_bus_ops = { + .bustype = BUS_I2C, + .read = ad7879_i2c_read, + .multi_read = ad7879_i2c_multi_read, + .write = ad7879_i2c_write, +}; + +static int __devinit ad7879_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7879 *ts; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + dev_err(&client->dev, "SMBUS Word Data not Supported\n"); + return -EIO; + } + + ts = ad7879_probe(&client->dev, AD7879_DEVID, client->irq, + &ad7879_i2c_bus_ops); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + i2c_set_clientdata(client, ts); + + return 0; +} + +static int __devexit ad7879_i2c_remove(struct i2c_client *client) +{ + struct ad7879 *ts = i2c_get_clientdata(client); + + ad7879_remove(ts); + + return 0; +} + +static const struct i2c_device_id ad7879_id[] = { + { "ad7879", 0 }, + { "ad7889", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ad7879_id); + +static struct i2c_driver ad7879_i2c_driver = { + .driver = { + .name = "ad7879", + .owner = THIS_MODULE, + }, + .probe = ad7879_i2c_probe, + .remove = __devexit_p(ad7879_i2c_remove), + .suspend = ad7879_i2c_suspend, + .resume = ad7879_i2c_resume, + .id_table = ad7879_id, +}; + +static int __init ad7879_i2c_init(void) +{ + return i2c_add_driver(&ad7879_i2c_driver); +} +module_init(ad7879_i2c_init); + +static void __exit ad7879_i2c_exit(void) +{ + i2c_del_driver(&ad7879_i2c_driver); +} +module_exit(ad7879_i2c_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("AD7879(-1) touchscreen I2C bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("i2c:ad7879"); diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c new file mode 100644 index 0000000..59c6e68 --- /dev/null +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -0,0 +1,198 @@ +/* + * AD7879/AD7889 touchscreen (SPI bus) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <linux/input.h> /* BUS_SPI */ +#include <linux/spi/spi.h> + +#include "ad7879.h" + +#define AD7879_DEVID 0x7A /* AD7879/AD7889 */ + +#define MAX_SPI_FREQ_HZ 5000000 +#define AD7879_CMD_MAGIC 0xE000 +#define AD7879_CMD_READ (1 << 10) +#define AD7879_CMD(reg) (AD7879_CMD_MAGIC | ((reg) & 0xF)) +#define AD7879_WRITECMD(reg) (AD7879_CMD(reg)) +#define AD7879_READCMD(reg) (AD7879_CMD(reg) | AD7879_CMD_READ) + +#ifdef CONFIG_PM +static int ad7879_spi_suspend(struct spi_device *spi, pm_message_t message) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_suspend(ts); + + return 0; +} + +static int ad7879_spi_resume(struct spi_device *spi) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_resume(ts); + + return 0; +} +#else +# define ad7879_spi_suspend NULL +# define ad7879_spi_resume NULL +#endif + +/* + * ad7879_read/write are only used for initial setup and for sysfs controls. + * The main traffic is done in ad7879_collect(). + */ + +static int ad7879_spi_xfer(struct spi_device *spi, + u16 cmd, u8 count, u16 *tx_buf, u16 *rx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfers; + void *spi_data; + u16 *command; + u16 *_rx_buf = _rx_buf; /* shut gcc up */ + u8 idx; + int ret; + + xfers = spi_data = kzalloc(sizeof(*xfers) * (count + 2), GFP_KERNEL); + if (!spi_data) + return -ENOMEM; + + spi_message_init(&msg); + + command = spi_data; + command[0] = cmd; + if (count == 1) { + /* ad7879_spi_{read,write} gave us buf on stack */ + command[1] = *tx_buf; + tx_buf = &command[1]; + _rx_buf = rx_buf; + rx_buf = &command[2]; + } + + ++xfers; + xfers[0].tx_buf = command; + xfers[0].len = 2; + spi_message_add_tail(&xfers[0], &msg); + ++xfers; + + for (idx = 0; idx < count; ++idx) { + if (rx_buf) + xfers[idx].rx_buf = &rx_buf[idx]; + if (tx_buf) + xfers[idx].tx_buf = &tx_buf[idx]; + xfers[idx].len = 2; + spi_message_add_tail(&xfers[idx], &msg); + } + + ret = spi_sync(spi, &msg); + + if (count == 1) + _rx_buf[0] = command[2]; + + kfree(spi_data); + + return ret; +} + +static int ad7879_spi_multi_read(struct device *dev, + u8 first_reg, u8 count, u16 *buf) +{ + struct spi_device *spi = to_spi_device(dev); + + return ad7879_spi_xfer(spi, AD7879_READCMD(first_reg), count, NULL, buf); +} + +static int ad7879_spi_read(struct device *dev, u8 reg) +{ + struct spi_device *spi = to_spi_device(dev); + u16 ret, dummy; + + return ad7879_spi_xfer(spi, AD7879_READCMD(reg), 1, &dummy, &ret) ? : ret; +} + +static int ad7879_spi_write(struct device *dev, u8 reg, u16 val) +{ + struct spi_device *spi = to_spi_device(dev); + u16 dummy; + + return ad7879_spi_xfer(spi, AD7879_WRITECMD(reg), 1, &val, &dummy); +} + +static const struct ad7879_bus_ops ad7879_spi_bus_ops = { + .bustype = BUS_SPI, + .read = ad7879_spi_read, + .multi_read = ad7879_spi_multi_read, + .write = ad7879_spi_write, +}; + +static int __devinit ad7879_spi_probe(struct spi_device *spi) +{ + struct ad7879 *ts; + int err; + + /* 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; + } + + spi->bits_per_word = 16; + err = spi_setup(spi); + if (err) { + dev_dbg(&spi->dev, "spi master doesn't support 16 bits/word\n"); + return err; + } + + ts = ad7879_probe(&spi->dev, AD7879_DEVID, spi->irq, &ad7879_spi_bus_ops); + if (IS_ERR(ts)) + return PTR_ERR(ts); + + spi_set_drvdata(spi, ts); + + return 0; +} + +static int __devexit ad7879_spi_remove(struct spi_device *spi) +{ + struct ad7879 *ts = spi_get_drvdata(spi); + + ad7879_remove(ts); + spi_set_drvdata(spi, NULL); + + return 0; +} + +static struct spi_driver ad7879_spi_driver = { + .driver = { + .name = "ad7879", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = ad7879_spi_probe, + .remove = __devexit_p(ad7879_spi_remove), + .suspend = ad7879_spi_suspend, + .resume = ad7879_spi_resume, +}; + +static int __init ad7879_spi_init(void) +{ + return spi_register_driver(&ad7879_spi_driver); +} +module_init(ad7879_spi_init); + +static void __exit ad7879_spi_exit(void) +{ + spi_unregister_driver(&ad7879_spi_driver); +} +module_exit(ad7879_spi_exit); + +MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); +MODULE_DESCRIPTION("AD7879(-1) touchscreen SPI bus driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 4b32fb4..ba6f0bd 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -1,25 +1,9 @@ /* - * Copyright (C) 2008-2009 Michael Hennerich, Analog Devices Inc. + * AD7879/AD7889 based touchscreen and GPIO driver * - * Description: AD7879/AD7889 based touchscreen, and GPIO driver - * (I2C/SPI Interface) + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. * - * 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 + * Licensed under the GPL-2 or later. * * History: * Copyright (c) 2005 David Brownell @@ -44,12 +28,12 @@ #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/gpio.h> #include <linux/spi/ad7879.h> +#include "ad7879.h" #define AD7879_REG_ZEROS 0 #define AD7879_REG_CTRL1 1 @@ -120,30 +104,19 @@ enum { #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; + const struct ad7879_bus_ops *bops; + + struct device *dev; struct input_dev *input; - struct work_struct work; struct timer_list timer; #ifdef CONFIG_GPIOLIB struct gpio_chip gc; -#endif 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 + unsigned int irq; + bool disabled; /* P: input->mutex */ + bool suspended; /* P: input->mutex */ u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -158,11 +131,22 @@ struct ad7879 { u16 cmd_crtl3; }; -static int ad7879_read(bus_device *, u8); -static int ad7879_write(bus_device *, u8, u16); -static void ad7879_collect(struct ad7879 *); +static int ad7879_read(struct ad7879 *ts, u8 reg) +{ + return ts->bops->read(ts->dev, reg); +} + +static int ad7879_multi_read(struct ad7879 *ts, u8 first_reg, u8 count, u16 *buf) +{ + return ts->bops->multi_read(ts->dev, first_reg, count, buf); +} -static void ad7879_report(struct ad7879 *ts) +static int ad7879_write(struct ad7879 *ts, u8 reg, u16 val) +{ + return ts->bops->write(ts->dev, reg, val); +} + +static int ad7879_report(struct ad7879 *ts) { struct input_dev *input_dev = ts->input; unsigned Rt; @@ -175,12 +159,14 @@ static void ad7879_report(struct ad7879 *ts) /* * 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. + * 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)) { @@ -189,21 +175,17 @@ static void ad7879_report(struct ad7879 *ts) Rt /= z1; Rt = (Rt + 2047) >> 12; + if (!timer_pending(&ts->timer)) + input_report_key(input_dev, BTN_TOUCH, 1); + 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); + return 0; } -} - -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); + return -EINVAL; } static void ad7879_ts_event_release(struct ad7879 *ts) @@ -211,6 +193,7 @@ 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_report_key(input_dev, BTN_TOUCH, 0); input_sync(input_dev); } @@ -225,56 +208,98 @@ 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 - */ + ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data); - if (!work_pending(&ts->work)) - schedule_work(&ts->work); + if (!ad7879_report(ts)) + mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT); return IRQ_HANDLED; } -static void ad7879_setup(struct ad7879 *ts) +static void __ad7879_enable(struct ad7879 *ts) { - 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); + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3); + ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1); + + enable_irq(ts->irq); } -static void ad7879_disable(struct ad7879 *ts) +static void __ad7879_disable(struct ad7879 *ts) { - mutex_lock(&ts->mutex); + disable_irq(ts->irq); + + if (del_timer_sync(&ts->timer)) + ad7879_ts_event_release(ts); + + ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN)); +} - if (!ts->disabled) { - ts->disabled = 1; - disable_irq(ts->bus->irq); +static int ad7879_open(struct input_dev *input) +{ + struct ad7879 *ts = input_get_drvdata(input); - cancel_work_sync(&ts->work); + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_enable(ts); - if (del_timer_sync(&ts->timer)) - ad7879_ts_event_release(ts); + return 0; +} - ad7879_write(ts->bus, AD7879_REG_CTRL2, - AD7879_PM(AD7879_PM_SHUTDOWN)); - } +static void ad7879_close(struct input_dev* input) +{ + struct ad7879 *ts = input_get_drvdata(input); - mutex_unlock(&ts->mutex); + /* protected by input->mutex */ + if (!ts->disabled && !ts->suspended) + __ad7879_disable(ts); } -static void ad7879_enable(struct ad7879 *ts) +void ad7879_suspend(struct ad7879 *ts) { - mutex_lock(&ts->mutex); + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && !ts->disabled && ts->input->users) + __ad7879_disable(ts); + + ts->suspended = true; - if (ts->disabled) { - ad7879_setup(ts); - ts->disabled = 0; - enable_irq(ts->bus->irq); + mutex_unlock(&ts->input->mutex); +} +EXPORT_SYMBOL(ad7879_suspend); + +void ad7879_resume(struct ad7879 *ts) +{ + mutex_lock(&ts->input->mutex); + + if (ts->suspended && !ts->disabled && ts->input->users) + __ad7879_enable(ts); + + ts->suspended = false; + + mutex_unlock(&ts->input->mutex); +} +EXPORT_SYMBOL(ad7879_resume); + +static void ad7879_toggle(struct ad7879 *ts, bool disable) +{ + mutex_lock(&ts->input->mutex); + + if (!ts->suspended && ts->input->users != 0) { + + if (disable) { + if (ts->disabled) + __ad7879_enable(ts); + } else { + if (!ts->disabled) + __ad7879_disable(ts); + } } - mutex_unlock(&ts->mutex); + ts->disabled = disable; + + mutex_unlock(&ts->input->mutex); } static ssize_t ad7879_disable_show(struct device *dev, @@ -297,10 +322,7 @@ static ssize_t ad7879_disable_store(struct device *dev, if (error) return error; - if (val) - ad7879_disable(ts); - else - ad7879_enable(ts); + ad7879_toggle(ts, val); return count; } @@ -325,7 +347,7 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip, mutex_lock(&ts->mutex); ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL; - err = ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); return err; @@ -345,7 +367,7 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - err = ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + err = ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); return err; @@ -357,7 +379,7 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio) u16 val; mutex_lock(&ts->mutex); - val = ad7879_read(ts->bus, AD7879_REG_CTRL2); + val = ad7879_read(ts, AD7879_REG_CTRL2); mutex_unlock(&ts->mutex); return !!(val & AD7879_GPIO_DATA); @@ -374,16 +396,17 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip, else ts->cmd_crtl2 &= ~AD7879_GPIO_DATA; - ad7879_write(ts->bus, AD7879_REG_CTRL2, ts->cmd_crtl2); + ad7879_write(ts, AD7879_REG_CTRL2, ts->cmd_crtl2); mutex_unlock(&ts->mutex); } -static int __devinit ad7879_gpio_add(struct device *dev) +static int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) { - struct ad7879 *ts = dev_get_drvdata(dev); - struct ad7879_platform_data *pdata = dev->platform_data; int ret = 0; + mutex_init(&ts->mutex); + if (pdata->gpio_export) { ts->gc.direction_input = ad7879_gpio_direction_input; ts->gc.direction_output = ad7879_gpio_direction_output; @@ -394,72 +417,75 @@ static int __devinit ad7879_gpio_add(struct device *dev) ts->gc.ngpio = 1; ts->gc.label = "AD7879-GPIO"; ts->gc.owner = THIS_MODULE; - ts->gc.dev = dev; + ts->gc.dev = ts->dev; ret = gpiochip_add(&ts->gc); if (ret) - dev_err(dev, "failed to register gpio %d\n", + dev_err(ts->dev, "failed to register gpio %d\n", ts->gc.base); } return ret; } -/* - * We mark ad7879_gpio_remove inline so there is a chance the code - * gets discarded when not needed. We can't do __devinit/__devexit - * markup since it is used in both probe and remove methods. - */ -static inline void ad7879_gpio_remove(struct device *dev) +static void ad7879_gpio_remove(struct ad7879 *ts) { - struct ad7879 *ts = dev_get_drvdata(dev); - struct ad7879_platform_data *pdata = dev->platform_data; + const struct ad7879_platform_data *pdata = ts->dev->platform_data; int ret; if (pdata->gpio_export) { ret = gpiochip_remove(&ts->gc); if (ret) - dev_err(dev, "failed to remove gpio %d\n", + dev_err(ts->dev, "failed to remove gpio %d\n", ts->gc.base); } } #else -static inline int ad7879_gpio_add(struct device *dev) +static inline int ad7879_gpio_add(struct ad7879 *ts, + const struct ad7879_platform_data *pdata) { return 0; } -static inline void ad7879_gpio_remove(struct device *dev) +static inline void ad7879_gpio_remove(struct ad7879 *ts) { } #endif -static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, + const struct ad7879_bus_ops *bops) { + struct ad7879_platform_data *pdata = dev->platform_data; + 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 (!irq) { + dev_err(dev, "no IRQ?\n"); + err = -EINVAL; + goto err_out; } if (!pdata) { - dev_err(&bus->dev, "no platform data?\n"); - return -ENODEV; + dev_err(dev, "no platform data?\n"); + err = -EINVAL; + goto err_out; } + ts = kzalloc(sizeof(*ts), GFP_KERNEL); input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + ts->bops = bops; + ts->dev = dev; ts->input = input_dev; + ts->irq = irq; 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; @@ -470,17 +496,26 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) ts->pen_down_acc_interval = pdata->pen_down_acc_interval; ts->median = pdata->median; - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&bus->dev)); + snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "AD7879 Touchscreen"; input_dev->phys = ts->phys; - input_dev->dev.parent = &bus->dev; + input_dev->dev.parent = dev; + input_dev->id.bustype = bops->bustype; + + input_dev->open = ad7879_open; + input_dev->close = ad7879_close; + + input_set_drvdata(input_dev, ts); __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); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + input_set_abs_params(input_dev, ABS_X, pdata->x_min ? : 0, pdata->x_max ? : MAX_12BIT, @@ -492,17 +527,18 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) 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); - + err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { - dev_err(&bus->dev, "Failed to write %s\n", input_dev->name); + dev_err(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); + revid = ad7879_read(ts, AD7879_REG_REVID); + input_dev->id.product = (revid & 0xff); + input_dev->id.version = revid >> 8; + if (input_dev->id.product != devid) { + dev_err(dev, "Failed to probe %s (%x vs %x)\n", + input_dev->name, devid, revid); err = -ENODEV; goto err_free_mem; } @@ -524,21 +560,21 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) AD7879_ACQ(ts->acquisition_time) | AD7879_TMR(ts->pen_down_acc_interval); - ad7879_setup(ts); - - err = request_irq(bus->irq, ad7879_irq, - IRQF_TRIGGER_FALLING, bus->dev.driver->name, ts); - + err = request_threaded_irq(ts->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING, + dev_name(dev), ts); if (err) { - dev_err(&bus->dev, "irq %d busy?\n", bus->irq); + dev_err(dev, "irq %d busy?\n", ts->irq); goto err_free_mem; } - err = sysfs_create_group(&bus->dev.kobj, &ad7879_attr_group); + __ad7879_disable(ts); + + err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) goto err_free_irq; - err = ad7879_gpio_add(&bus->dev); + err = ad7879_gpio_add(ts, pdata); if (err) goto err_remove_attr; @@ -546,321 +582,32 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts) if (err) goto err_remove_gpio; - dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n", - revid >> 8, bus->irq); - - return 0; + return ts; err_remove_gpio: - ad7879_gpio_remove(&bus->dev); + ad7879_gpio_remove(ts); err_remove_attr: - sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group); + sysfs_remove_group(&dev->kobj, &ad7879_attr_group); err_free_irq: - free_irq(bus->irq, ts); + free_irq(ts->irq, ts); err_free_mem: input_free_device(input_dev); - - return err; -} - -static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts) -{ - ad7879_gpio_remove(&bus->dev); - 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 error; -} - -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; +err_out: + return ERR_PTR(err); } +EXPORT_SYMBOL(ad7879_probe); -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) +void ad7879_remove(struct ad7879 *ts) { - 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) - kfree(ts); - - return error; -} - -static int __devexit ad7879_remove(struct i2c_client *client) -{ - struct ad7879 *ts = dev_get_drvdata(&client->dev); - - ad7879_destroy(client, ts); + ad7879_gpio_remove(ts); + sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); + free_irq(ts->irq, ts); + input_unregister_device(ts->input); kfree(ts); - - return 0; -} - -static const struct i2c_device_id ad7879_id[] = { - { "ad7879", 0 }, - { "ad7889", 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 +EXPORT_SYMBOL(ad7879_remove); MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("spi:ad7879"); diff --git a/drivers/input/touchscreen/ad7879.h b/drivers/input/touchscreen/ad7879.h new file mode 100644 index 0000000..6b45a27 --- /dev/null +++ b/drivers/input/touchscreen/ad7879.h @@ -0,0 +1,30 @@ +/* + * AD7879/AD7889 touchscreen (bus interfaces) + * + * Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef _AD7879_H_ +#define _AD7879_H_ + +#include <linux/types.h> + +struct ad7879; +struct device; + +struct ad7879_bus_ops { + u16 bustype; + int (*read)(struct device *dev, u8 reg); + int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf); + int (*write)(struct device *dev, u8 reg, u16 val); +}; + +void ad7879_suspend(struct ad7879 *); +void ad7879_resume(struct ad7879 *); +struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned irq, + const struct ad7879_bus_ops *bops); +void ad7879_remove(struct ad7879 *); + +#endif diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index a9fdf55..1603193 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -68,6 +68,8 @@ struct ts_event { u16 y; u16 z1, z2; int ignore; + u8 x_buf[3]; + u8 y_buf[3]; }; /* @@ -79,6 +81,8 @@ struct ads7846_packet { u8 read_x, read_y, read_z1, read_z2, pwrdown; u16 dummy; /* for the pwrdown read */ struct ts_event tc; + /* for ads7845 with mpc5121 psc spi we use 3-byte buffers */ + u8 read_x_cmd[3], read_y_cmd[3], pwrdown_cmd[3]; }; struct ads7846 { @@ -207,6 +211,14 @@ struct ser_req { struct spi_transfer xfer[6]; }; +struct ads7845_ser_req { + u8 command[3]; + u8 pwrdown[3]; + u8 sample[3]; + struct spi_message msg; + struct spi_transfer xfer[2]; +}; + static void ads7846_enable(struct ads7846 *ts); static void ads7846_disable(struct ads7846 *ts); @@ -287,6 +299,41 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) return status; } +static int ads7845_read12_ser(struct device *dev, unsigned command) +{ + struct spi_device *spi = to_spi_device(dev); + struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7845_ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); + int status; + + if (!req) + return -ENOMEM; + + spi_message_init(&req->msg); + + req->command[0] = (u8) command; + req->xfer[0].tx_buf = req->command; + req->xfer[0].rx_buf = req->sample; + req->xfer[0].len = 3; + spi_message_add_tail(&req->xfer[0], &req->msg); + + ts->irq_disabled = 1; + disable_irq(spi->irq); + status = spi_sync(spi, &req->msg); + ts->irq_disabled = 0; + enable_irq(spi->irq); + + if (status == 0) { + /* BE12 value, then padding */ + status = be16_to_cpu(*((u16 *)&req->sample[1])); + status = status >> 3; + status &= 0x0fff; + } + + kfree(req); + return status; +} + #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) #define SHOW(name, var, adjust) static ssize_t \ @@ -540,10 +587,17 @@ static void ads7846_rx(void *ads) /* ads7846_rx_val() did in-place conversion (including byteswap) from * on-the-wire format as part of debouncing to get stable readings. */ - x = packet->tc.x; - y = packet->tc.y; - z1 = packet->tc.z1; - z2 = packet->tc.z2; + if (ts->model == 7845) { + x = *(u16 *)packet->tc.x_buf; + y = *(u16 *)packet->tc.y_buf; + z1 = 0; + z2 = 0; + } else { + x = packet->tc.x; + y = packet->tc.y; + z1 = packet->tc.z1; + z2 = packet->tc.z2; + } /* range filtering */ if (x == MAX_12BIT) @@ -551,6 +605,12 @@ static void ads7846_rx(void *ads) if (ts->model == 7843) { Rt = ts->pressure_max / 2; + } else if (ts->model == 7845) { + if (get_pendown_state(ts)) + Rt = ts->pressure_max / 2; + else + Rt = 0; + dev_vdbg(&ts->spi->dev, "x/y: %d/%d, PD %d\n", x, y, Rt); } else if (likely(x && z1)) { /* compute touch pressure resistance using equation #2 */ Rt = z2; @@ -671,10 +731,14 @@ static void ads7846_rx_val(void *ads) m = &ts->msg[ts->msg_idx]; t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; - * built from two 8 bit values written msb-first. - */ - val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; + if (ts->model == 7845) { + val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3; + } else { + /* adjust: on-wire is a must-ignore bit, a BE12 value, then + * padding; built from two 8 bit values written msb-first. + */ + val = be16_to_cpup((__be16 *)t->rx_buf) >> 3; + } action = ts->filter(ts->filter_data, ts->msg_idx, &val); switch (action) { @@ -878,14 +942,15 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts) static int __devinit ads7846_probe(struct spi_device *spi) { - struct ads7846 *ts; - struct ads7846_packet *packet; - struct input_dev *input_dev; - struct ads7846_platform_data *pdata = spi->dev.platform_data; - struct spi_message *m; - struct spi_transfer *x; - int vref; - int err; + struct ads7846 *ts; + struct ads7846_packet *packet; + struct input_dev *input_dev; + const struct ads7846_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; + struct spi_transfer *x; + unsigned long irq_flags; + int vref; + int err; if (!spi->irq) { dev_dbg(&spi->dev, "no IRQ?\n"); @@ -1008,16 +1073,26 @@ static int __devinit ads7846_probe(struct spi_device *spi) spi_message_init(m); - /* y- still on; turn on only y+ (and ADC) */ - packet->read_y = READ_Y(vref); - x->tx_buf = &packet->read_y; - x->len = 1; - spi_message_add_tail(x, m); + if (ts->model == 7845) { + packet->read_y_cmd[0] = READ_Y(vref); + packet->read_y_cmd[1] = 0; + packet->read_y_cmd[2] = 0; + x->tx_buf = &packet->read_y_cmd[0]; + x->rx_buf = &packet->tc.y_buf[0]; + x->len = 3; + spi_message_add_tail(x, m); + } else { + /* y- still on; turn on only y+ (and ADC) */ + packet->read_y = READ_Y(vref); + x->tx_buf = &packet->read_y; + x->len = 1; + spi_message_add_tail(x, m); - x++; - x->rx_buf = &packet->tc.y; - x->len = 2; - spi_message_add_tail(x, m); + x++; + x->rx_buf = &packet->tc.y; + x->len = 2; + spi_message_add_tail(x, m); + } /* the first sample after switching drivers can be low quality; * optionally discard it, using a second one after the signals @@ -1043,17 +1118,28 @@ static int __devinit ads7846_probe(struct spi_device *spi) m++; spi_message_init(m); - /* turn y- off, x+ on, then leave in lowpower */ - x++; - packet->read_x = READ_X(vref); - x->tx_buf = &packet->read_x; - x->len = 1; - spi_message_add_tail(x, m); + if (ts->model == 7845) { + x++; + packet->read_x_cmd[0] = READ_X(vref); + packet->read_x_cmd[1] = 0; + packet->read_x_cmd[2] = 0; + x->tx_buf = &packet->read_x_cmd[0]; + x->rx_buf = &packet->tc.x_buf[0]; + x->len = 3; + spi_message_add_tail(x, m); + } else { + /* turn y- off, x+ on, then leave in lowpower */ + x++; + packet->read_x = READ_X(vref); + x->tx_buf = &packet->read_x; + x->len = 1; + spi_message_add_tail(x, m); - x++; - x->rx_buf = &packet->tc.x; - x->len = 2; - spi_message_add_tail(x, m); + x++; + x->rx_buf = &packet->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + } /* ... maybe discard first sample ... */ if (pdata->settle_delay_usecs) { @@ -1144,15 +1230,25 @@ static int __devinit ads7846_probe(struct spi_device *spi) m++; spi_message_init(m); - x++; - packet->pwrdown = PWRDOWN; - x->tx_buf = &packet->pwrdown; - x->len = 1; - spi_message_add_tail(x, m); + if (ts->model == 7845) { + x++; + packet->pwrdown_cmd[0] = PWRDOWN; + packet->pwrdown_cmd[1] = 0; + packet->pwrdown_cmd[2] = 0; + x->tx_buf = &packet->pwrdown_cmd[0]; + x->len = 3; + } else { + x++; + packet->pwrdown = PWRDOWN; + x->tx_buf = &packet->pwrdown; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &packet->dummy; + x->len = 2; + } - x++; - x->rx_buf = &packet->dummy; - x->len = 2; CS_CHANGE(*x); spi_message_add_tail(x, m); @@ -1174,17 +1270,22 @@ static int __devinit ads7846_probe(struct spi_device *spi) goto err_put_regulator; } - if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING, - spi->dev.driver->name, ts)) { + irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING; + + err = request_irq(spi->irq, ads7846_irq, irq_flags, + spi->dev.driver->name, ts); + + if (err && !pdata->irq_flags) { dev_info(&spi->dev, "trying pin change workaround on irq %d\n", spi->irq); err = request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, spi->dev.driver->name, ts); - if (err) { - dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); - goto err_disable_regulator; - } + } + + if (err) { + dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); + goto err_disable_regulator; } err = ads784x_hwmon_register(spi, ts); @@ -1196,8 +1297,11 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ - (void) ads7846_read12_ser(&spi->dev, - READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); + if (ts->model == 7845) + ads7845_read12_ser(&spi->dev, PWRDOWN); + else + (void) ads7846_read12_ser(&spi->dev, + READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); if (err) diff --git a/drivers/input/touchscreen/cy8ctmg110_ts.c b/drivers/input/touchscreen/cy8ctmg110_ts.c new file mode 100644 index 0000000..4eb7df0 --- /dev/null +++ b/drivers/input/touchscreen/cy8ctmg110_ts.c @@ -0,0 +1,363 @@ +/* + * Driver for cypress touch screen controller + * + * Copyright (c) 2009 Aava Mobile + * + * Some cleanups by Alan Cox <alan@linux.intel.com> + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/input.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <linux/input/cy8ctmg110_pdata.h> + +#define CY8CTMG110_DRIVER_NAME "cy8ctmg110" + +/* Touch coordinates */ +#define CY8CTMG110_X_MIN 0 +#define CY8CTMG110_Y_MIN 0 +#define CY8CTMG110_X_MAX 759 +#define CY8CTMG110_Y_MAX 465 + + +/* cy8ctmg110 register definitions */ +#define CY8CTMG110_TOUCH_WAKEUP_TIME 0 +#define CY8CTMG110_TOUCH_SLEEP_TIME 2 +#define CY8CTMG110_TOUCH_X1 3 +#define CY8CTMG110_TOUCH_Y1 5 +#define CY8CTMG110_TOUCH_X2 7 +#define CY8CTMG110_TOUCH_Y2 9 +#define CY8CTMG110_FINGERS 11 +#define CY8CTMG110_GESTURE 12 +#define CY8CTMG110_REG_MAX 13 + + +/* + * The touch driver structure. + */ +struct cy8ctmg110 { + struct input_dev *input; + char phys[32]; + struct i2c_client *client; + int reset_pin; + int irq_pin; +}; + +/* + * cy8ctmg110_power is the routine that is called when touch hardware + * will powered off or on. + */ +static void cy8ctmg110_power(struct cy8ctmg110 *ts, bool poweron) +{ + if (ts->reset_pin) + gpio_direction_output(ts->reset_pin, 1 - poweron); +} + +static int cy8ctmg110_write_regs(struct cy8ctmg110 *tsc, unsigned char reg, + unsigned char len, unsigned char *value) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + unsigned char i2c_data[6]; + + BUG_ON(len > 5); + + i2c_data[0] = reg; + memcpy(i2c_data + 1, value, len); + + ret = i2c_master_send(client, i2c_data, len + 1); + if (ret != 1) { + dev_err(&client->dev, "i2c write data cmd failed\n"); + return ret; + } + + return 0; +} + +static int cy8ctmg110_read_regs(struct cy8ctmg110 *tsc, + unsigned char *data, unsigned char len, unsigned char cmd) +{ + struct i2c_client *client = tsc->client; + unsigned int ret; + struct i2c_msg msg[2] = { + /* first write slave position to i2c devices */ + { client->addr, 0, 1, &cmd }, + /* Second read data from position */ + { client->addr, I2C_M_RD, len, data } + }; + + ret = i2c_transfer(client->adapter, msg, 2); + if (ret < 0) + return ret; + + return 0; +} + +static int cy8ctmg110_touch_pos(struct cy8ctmg110 *tsc) +{ + struct input_dev *input = tsc->input; + unsigned char reg_p[CY8CTMG110_REG_MAX]; + int x, y; + + memset(reg_p, 0, CY8CTMG110_REG_MAX); + + /* Reading coordinates */ + if (cy8ctmg110_read_regs(tsc, reg_p, 9, CY8CTMG110_TOUCH_X1) != 0) + return -EIO; + + y = reg_p[2] << 8 | reg_p[3]; + x = reg_p[0] << 8 | reg_p[1]; + + /* Number of touch */ + if (reg_p[8] == 0) { + input_report_key(input, BTN_TOUCH, 0); + } else { + input_report_key(input, BTN_TOUCH, 1); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + } + + input_sync(input); + + return 0; +} + +static int cy8ctmg110_set_sleepmode(struct cy8ctmg110 *ts, bool sleep) +{ + unsigned char reg_p[3]; + + if (sleep) { + reg_p[0] = 0x00; + reg_p[1] = 0xff; + reg_p[2] = 5; + } else { + reg_p[0] = 0x10; + reg_p[1] = 0xff; + reg_p[2] = 0; + } + + return cy8ctmg110_write_regs(ts, CY8CTMG110_TOUCH_WAKEUP_TIME, 3, reg_p); +} + +static irqreturn_t cy8ctmg110_irq_thread(int irq, void *dev_id) +{ + struct cy8ctmg110 *tsc = dev_id; + + cy8ctmg110_touch_pos(tsc); + + return IRQ_HANDLED; +} + +static int __devinit cy8ctmg110_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct cy8ctmg110_pdata *pdata = client->dev.platform_data; + struct cy8ctmg110 *ts; + struct input_dev *input_dev; + int err; + + /* No pdata no way forward */ + if (pdata == NULL) { + dev_err(&client->dev, "no pdata\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -EIO; + + ts = kzalloc(sizeof(struct cy8ctmg110), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts || !input_dev) { + err = -ENOMEM; + goto err_free_mem; + } + + ts->client = client; + ts->input = input_dev; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + input_dev->name = CY8CTMG110_DRIVER_NAME " Touchscreen"; + input_dev->phys = ts->phys; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + input_set_abs_params(input_dev, ABS_X, + CY8CTMG110_X_MIN, CY8CTMG110_X_MAX, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + CY8CTMG110_Y_MIN, CY8CTMG110_Y_MAX, 0, 0); + + if (ts->reset_pin) { + err = gpio_request(ts->reset_pin, NULL); + if (err) { + dev_err(&client->dev, + "Unable to request GPIO pin %d.\n", + ts->reset_pin); + goto err_free_mem; + } + } + + cy8ctmg110_power(ts, true); + cy8ctmg110_set_sleepmode(ts, false); + + err = gpio_request(ts->irq_pin, "touch_irq_key"); + if (err < 0) { + dev_err(&client->dev, + "Failed to request GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_shutoff_device; + } + + err = gpio_direction_input(ts->irq_pin); + if (err < 0) { + dev_err(&client->dev, + "Failed to configure input direction for GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_free_irq_gpio; + } + + client->irq = gpio_to_irq(ts->irq_pin); + if (client->irq < 0) { + err = client->irq; + dev_err(&client->dev, + "Unable to get irq number for GPIO %d, error %d\n", + ts->irq_pin, err); + goto err_free_irq_gpio; + } + + err = request_threaded_irq(client->irq, NULL, cy8ctmg110_irq_thread, + IRQF_TRIGGER_RISING, "touch_reset_key", ts); + if (err < 0) { + dev_err(&client->dev, + "irq %d busy? error %d\n", client->irq, err); + goto err_free_irq_gpio; + } + + err = input_register_device(input_dev); + if (err) + goto err_free_irq; + + i2c_set_clientdata(client, ts); + device_init_wakeup(&client->dev, 1); + return 0; + +err_free_irq: + free_irq(client->irq, ts); +err_free_irq_gpio: + gpio_free(ts->irq_pin); +err_shutoff_device: + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + if (ts->reset_pin) + gpio_free(ts->reset_pin); +err_free_mem: + input_free_device(input_dev); + kfree(ts); + return err; +} + +#ifdef CONFIG_PM +static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + else { + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + } + return 0; +} + +static int cy8ctmg110_resume(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + else { + cy8ctmg110_power(ts, true); + cy8ctmg110_set_sleepmode(ts, false); + } + return 0; +} +#endif + +static int __devexit cy8ctmg110_remove(struct i2c_client *client) +{ + struct cy8ctmg110 *ts = i2c_get_clientdata(client); + + cy8ctmg110_set_sleepmode(ts, true); + cy8ctmg110_power(ts, false); + + free_irq(client->irq, ts); + input_unregister_device(ts->input); + gpio_free(ts->irq_pin); + if (ts->reset_pin) + gpio_free(ts->reset_pin); + kfree(ts); + + return 0; +} + +static struct i2c_device_id cy8ctmg110_idtable[] = { + { CY8CTMG110_DRIVER_NAME, 1 }, + { } +}; + +MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable); + +static struct i2c_driver cy8ctmg110_driver = { + .driver = { + .owner = THIS_MODULE, + .name = CY8CTMG110_DRIVER_NAME, + }, + .id_table = cy8ctmg110_idtable, + .probe = cy8ctmg110_probe, + .remove = __devexit_p(cy8ctmg110_remove), +#ifdef CONFIG_PM + .suspend = cy8ctmg110_suspend, + .resume = cy8ctmg110_resume, +#endif +}; + +static int __init cy8ctmg110_init(void) +{ + return i2c_add_driver(&cy8ctmg110_driver); +} + +static void __exit cy8ctmg110_exit(void) +{ + i2c_del_driver(&cy8ctmg110_driver); +} + +module_init(cy8ctmg110_init); +module_exit(cy8ctmg110_exit); + +MODULE_AUTHOR("Samuli Konttila <samuli.konttila@aavamobile.com>"); +MODULE_DESCRIPTION("cy8ctmg110 TouchScreen Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/mcs5000_ts.c b/drivers/input/touchscreen/mcs5000_ts.c index 1fb0c2f..6ee9940 100644 --- a/drivers/input/touchscreen/mcs5000_ts.c +++ b/drivers/input/touchscreen/mcs5000_ts.c @@ -16,7 +16,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/i2c.h> -#include <linux/i2c/mcs5000_ts.h> +#include <linux/i2c/mcs.h> #include <linux/interrupt.h> #include <linux/input.h> #include <linux/irq.h> @@ -105,7 +105,7 @@ enum mcs5000_ts_read_offset { struct mcs5000_ts_data { struct i2c_client *client; struct input_dev *input_dev; - const struct mcs5000_ts_platform_data *platform_data; + const struct mcs_platform_data *platform_data; }; static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) @@ -164,7 +164,7 @@ static irqreturn_t mcs5000_ts_interrupt(int irq, void *dev_id) static void mcs5000_ts_phys_init(struct mcs5000_ts_data *data) { - const struct mcs5000_ts_platform_data *platform_data = + const struct mcs_platform_data *platform_data = data->platform_data; struct i2c_client *client = data->client; diff --git a/drivers/input/touchscreen/qt602240_ts.c b/drivers/input/touchscreen/qt602240_ts.c new file mode 100644 index 0000000..66b26ad --- /dev/null +++ b/drivers/input/touchscreen/qt602240_ts.c @@ -0,0 +1,1401 @@ +/* + * AT42QT602240/ATMXT224 Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/i2c/qt602240_ts.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/slab.h> + +/* Version */ +#define QT602240_VER_20 20 +#define QT602240_VER_21 21 +#define QT602240_VER_22 22 + +/* Slave addresses */ +#define QT602240_APP_LOW 0x4a +#define QT602240_APP_HIGH 0x4b +#define QT602240_BOOT_LOW 0x24 +#define QT602240_BOOT_HIGH 0x25 + +/* Firmware */ +#define QT602240_FW_NAME "qt602240.fw" + +/* Registers */ +#define QT602240_FAMILY_ID 0x00 +#define QT602240_VARIANT_ID 0x01 +#define QT602240_VERSION 0x02 +#define QT602240_BUILD 0x03 +#define QT602240_MATRIX_X_SIZE 0x04 +#define QT602240_MATRIX_Y_SIZE 0x05 +#define QT602240_OBJECT_NUM 0x06 +#define QT602240_OBJECT_START 0x07 + +#define QT602240_OBJECT_SIZE 6 + +/* Object types */ +#define QT602240_DEBUG_DIAGNOSTIC 37 +#define QT602240_GEN_MESSAGE 5 +#define QT602240_GEN_COMMAND 6 +#define QT602240_GEN_POWER 7 +#define QT602240_GEN_ACQUIRE 8 +#define QT602240_TOUCH_MULTI 9 +#define QT602240_TOUCH_KEYARRAY 15 +#define QT602240_TOUCH_PROXIMITY 23 +#define QT602240_PROCI_GRIPFACE 20 +#define QT602240_PROCG_NOISE 22 +#define QT602240_PROCI_ONETOUCH 24 +#define QT602240_PROCI_TWOTOUCH 27 +#define QT602240_SPT_COMMSCONFIG 18 /* firmware ver 21 over */ +#define QT602240_SPT_GPIOPWM 19 +#define QT602240_SPT_SELFTEST 25 +#define QT602240_SPT_CTECONFIG 28 +#define QT602240_SPT_USERDATA 38 /* firmware ver 21 over */ + +/* QT602240_GEN_COMMAND field */ +#define QT602240_COMMAND_RESET 0 +#define QT602240_COMMAND_BACKUPNV 1 +#define QT602240_COMMAND_CALIBRATE 2 +#define QT602240_COMMAND_REPORTALL 3 +#define QT602240_COMMAND_DIAGNOSTIC 5 + +/* QT602240_GEN_POWER field */ +#define QT602240_POWER_IDLEACQINT 0 +#define QT602240_POWER_ACTVACQINT 1 +#define QT602240_POWER_ACTV2IDLETO 2 + +/* QT602240_GEN_ACQUIRE field */ +#define QT602240_ACQUIRE_CHRGTIME 0 +#define QT602240_ACQUIRE_TCHDRIFT 2 +#define QT602240_ACQUIRE_DRIFTST 3 +#define QT602240_ACQUIRE_TCHAUTOCAL 4 +#define QT602240_ACQUIRE_SYNC 5 +#define QT602240_ACQUIRE_ATCHCALST 6 +#define QT602240_ACQUIRE_ATCHCALSTHR 7 + +/* QT602240_TOUCH_MULTI field */ +#define QT602240_TOUCH_CTRL 0 +#define QT602240_TOUCH_XORIGIN 1 +#define QT602240_TOUCH_YORIGIN 2 +#define QT602240_TOUCH_XSIZE 3 +#define QT602240_TOUCH_YSIZE 4 +#define QT602240_TOUCH_BLEN 6 +#define QT602240_TOUCH_TCHTHR 7 +#define QT602240_TOUCH_TCHDI 8 +#define QT602240_TOUCH_ORIENT 9 +#define QT602240_TOUCH_MOVHYSTI 11 +#define QT602240_TOUCH_MOVHYSTN 12 +#define QT602240_TOUCH_NUMTOUCH 14 +#define QT602240_TOUCH_MRGHYST 15 +#define QT602240_TOUCH_MRGTHR 16 +#define QT602240_TOUCH_AMPHYST 17 +#define QT602240_TOUCH_XRANGE_LSB 18 +#define QT602240_TOUCH_XRANGE_MSB 19 +#define QT602240_TOUCH_YRANGE_LSB 20 +#define QT602240_TOUCH_YRANGE_MSB 21 +#define QT602240_TOUCH_XLOCLIP 22 +#define QT602240_TOUCH_XHICLIP 23 +#define QT602240_TOUCH_YLOCLIP 24 +#define QT602240_TOUCH_YHICLIP 25 +#define QT602240_TOUCH_XEDGECTRL 26 +#define QT602240_TOUCH_XEDGEDIST 27 +#define QT602240_TOUCH_YEDGECTRL 28 +#define QT602240_TOUCH_YEDGEDIST 29 +#define QT602240_TOUCH_JUMPLIMIT 30 /* firmware ver 22 over */ + +/* QT602240_PROCI_GRIPFACE field */ +#define QT602240_GRIPFACE_CTRL 0 +#define QT602240_GRIPFACE_XLOGRIP 1 +#define QT602240_GRIPFACE_XHIGRIP 2 +#define QT602240_GRIPFACE_YLOGRIP 3 +#define QT602240_GRIPFACE_YHIGRIP 4 +#define QT602240_GRIPFACE_MAXTCHS 5 +#define QT602240_GRIPFACE_SZTHR1 7 +#define QT602240_GRIPFACE_SZTHR2 8 +#define QT602240_GRIPFACE_SHPTHR1 9 +#define QT602240_GRIPFACE_SHPTHR2 10 +#define QT602240_GRIPFACE_SUPEXTTO 11 + +/* QT602240_PROCI_NOISE field */ +#define QT602240_NOISE_CTRL 0 +#define QT602240_NOISE_OUTFLEN 1 +#define QT602240_NOISE_GCAFUL_LSB 3 +#define QT602240_NOISE_GCAFUL_MSB 4 +#define QT602240_NOISE_GCAFLL_LSB 5 +#define QT602240_NOISE_GCAFLL_MSB 6 +#define QT602240_NOISE_ACTVGCAFVALID 7 +#define QT602240_NOISE_NOISETHR 8 +#define QT602240_NOISE_FREQHOPSCALE 10 +#define QT602240_NOISE_FREQ0 11 +#define QT602240_NOISE_FREQ1 12 +#define QT602240_NOISE_FREQ2 13 +#define QT602240_NOISE_FREQ3 14 +#define QT602240_NOISE_FREQ4 15 +#define QT602240_NOISE_IDLEGCAFVALID 16 + +/* QT602240_SPT_COMMSCONFIG */ +#define QT602240_COMMS_CTRL 0 +#define QT602240_COMMS_CMD 1 + +/* QT602240_SPT_CTECONFIG field */ +#define QT602240_CTE_CTRL 0 +#define QT602240_CTE_CMD 1 +#define QT602240_CTE_MODE 2 +#define QT602240_CTE_IDLEGCAFDEPTH 3 +#define QT602240_CTE_ACTVGCAFDEPTH 4 +#define QT602240_CTE_VOLTAGE 5 /* firmware ver 21 over */ + +#define QT602240_VOLTAGE_DEFAULT 2700000 +#define QT602240_VOLTAGE_STEP 10000 + +/* Define for QT602240_GEN_COMMAND */ +#define QT602240_BOOT_VALUE 0xa5 +#define QT602240_BACKUP_VALUE 0x55 +#define QT602240_BACKUP_TIME 25 /* msec */ +#define QT602240_RESET_TIME 65 /* msec */ + +#define QT602240_FWRESET_TIME 175 /* msec */ + +/* Command to unlock bootloader */ +#define QT602240_UNLOCK_CMD_MSB 0xaa +#define QT602240_UNLOCK_CMD_LSB 0xdc + +/* Bootloader mode status */ +#define QT602240_WAITING_BOOTLOAD_CMD 0xc0 /* valid 7 6 bit only */ +#define QT602240_WAITING_FRAME_DATA 0x80 /* valid 7 6 bit only */ +#define QT602240_FRAME_CRC_CHECK 0x02 +#define QT602240_FRAME_CRC_FAIL 0x03 +#define QT602240_FRAME_CRC_PASS 0x04 +#define QT602240_APP_CRC_FAIL 0x40 /* valid 7 8 bit only */ +#define QT602240_BOOT_STATUS_MASK 0x3f + +/* Touch status */ +#define QT602240_SUPPRESS (1 << 1) +#define QT602240_AMP (1 << 2) +#define QT602240_VECTOR (1 << 3) +#define QT602240_MOVE (1 << 4) +#define QT602240_RELEASE (1 << 5) +#define QT602240_PRESS (1 << 6) +#define QT602240_DETECT (1 << 7) + +/* Touchscreen absolute values */ +#define QT602240_MAX_XC 0x3ff +#define QT602240_MAX_YC 0x3ff +#define QT602240_MAX_AREA 0xff + +#define QT602240_MAX_FINGER 10 + +/* Initial register values recommended from chip vendor */ +static const u8 init_vals_ver_20[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x08, 0x05, 0x05, 0x00, 0x00, 0x00, 0x05, 0x14, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x11, 0x0a, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x64, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x00, 0x64, 0x64, 0x64, 0x64, 0x00, 0x00, 0x1e, 0x14, 0x04, + 0x1e, 0x00, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x19, 0x00, 0xe7, 0xff, 0x04, 0x32, 0x00, + 0x01, 0x0a, 0x0f, 0x14, 0x00, 0x00, 0xe8, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x04, 0x08, +}; + +static const u8 init_vals_ver_21[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, + 0x0f, 0x0a, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +static const u8 init_vals_ver_22[] = { + /* QT602240_GEN_COMMAND(6) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_GEN_POWER(7) */ + 0x20, 0xff, 0x32, + /* QT602240_GEN_ACQUIRE(8) */ + 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x09, 0x23, + /* QT602240_TOUCH_MULTI(9) */ + 0x00, 0x00, 0x00, 0x13, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x01, 0x01, 0x0e, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_TOUCH_KEYARRAY(15) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, + /* QT602240_SPT_GPIOPWM(19) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_GRIPFACE(20) */ + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x28, 0x04, + 0x0f, 0x0a, + /* QT602240_PROCG_NOISE(22) */ + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, + 0x00, 0x05, 0x0f, 0x19, 0x23, 0x2d, 0x03, + /* QT602240_TOUCH_PROXIMITY(23) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_ONETOUCH(24) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_SELFTEST(25) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + /* QT602240_PROCI_TWOTOUCH(27) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* QT602240_SPT_CTECONFIG(28) */ + 0x00, 0x00, 0x00, 0x08, 0x10, 0x00, +}; + +struct qt602240_info { + u8 family_id; + u8 variant_id; + u8 version; + u8 build; + u8 matrix_xsize; + u8 matrix_ysize; + u8 object_num; +}; + +struct qt602240_object { + u8 type; + u16 start_address; + u8 size; + u8 instances; + u8 num_report_ids; + + /* to map object and message */ + u8 max_reportid; +}; + +struct qt602240_message { + u8 reportid; + u8 message[7]; + u8 checksum; +}; + +struct qt602240_finger { + int status; + int x; + int y; + int area; +}; + +/* Each client has this additional data */ +struct qt602240_data { + struct i2c_client *client; + struct input_dev *input_dev; + const struct qt602240_platform_data *pdata; + struct qt602240_object *object_table; + struct qt602240_info info; + struct qt602240_finger finger[QT602240_MAX_FINGER]; + unsigned int irq; +}; + +static bool qt602240_object_readable(unsigned int type) +{ + switch (type) { + case QT602240_GEN_MESSAGE: + case QT602240_GEN_COMMAND: + case QT602240_GEN_POWER: + case QT602240_GEN_ACQUIRE: + case QT602240_TOUCH_MULTI: + case QT602240_TOUCH_KEYARRAY: + case QT602240_TOUCH_PROXIMITY: + case QT602240_PROCI_GRIPFACE: + case QT602240_PROCG_NOISE: + case QT602240_PROCI_ONETOUCH: + case QT602240_PROCI_TWOTOUCH: + case QT602240_SPT_COMMSCONFIG: + case QT602240_SPT_GPIOPWM: + case QT602240_SPT_SELFTEST: + case QT602240_SPT_CTECONFIG: + case QT602240_SPT_USERDATA: + return true; + default: + return false; + } +} + +static bool qt602240_object_writable(unsigned int type) +{ + switch (type) { + case QT602240_GEN_COMMAND: + case QT602240_GEN_POWER: + case QT602240_GEN_ACQUIRE: + case QT602240_TOUCH_MULTI: + case QT602240_TOUCH_KEYARRAY: + case QT602240_TOUCH_PROXIMITY: + case QT602240_PROCI_GRIPFACE: + case QT602240_PROCG_NOISE: + case QT602240_PROCI_ONETOUCH: + case QT602240_PROCI_TWOTOUCH: + case QT602240_SPT_GPIOPWM: + case QT602240_SPT_SELFTEST: + case QT602240_SPT_CTECONFIG: + return true; + default: + return false; + } +} + +static void qt602240_dump_message(struct device *dev, + struct qt602240_message *message) +{ + dev_dbg(dev, "reportid:\t0x%x\n", message->reportid); + dev_dbg(dev, "message1:\t0x%x\n", message->message[0]); + dev_dbg(dev, "message2:\t0x%x\n", message->message[1]); + dev_dbg(dev, "message3:\t0x%x\n", message->message[2]); + dev_dbg(dev, "message4:\t0x%x\n", message->message[3]); + dev_dbg(dev, "message5:\t0x%x\n", message->message[4]); + dev_dbg(dev, "message6:\t0x%x\n", message->message[5]); + dev_dbg(dev, "message7:\t0x%x\n", message->message[6]); + dev_dbg(dev, "checksum:\t0x%x\n", message->checksum); +} + +static int qt602240_check_bootloader(struct i2c_client *client, + unsigned int state) +{ + u8 val; + +recheck: + if (i2c_master_recv(client, &val, 1) != 1) { + dev_err(&client->dev, "%s: i2c recv failed\n", __func__); + return -EIO; + } + + switch (state) { + case QT602240_WAITING_BOOTLOAD_CMD: + case QT602240_WAITING_FRAME_DATA: + val &= ~QT602240_BOOT_STATUS_MASK; + break; + case QT602240_FRAME_CRC_PASS: + if (val == QT602240_FRAME_CRC_CHECK) + goto recheck; + break; + default: + return -EINVAL; + } + + if (val != state) { + dev_err(&client->dev, "Unvalid bootloader mode state\n"); + return -EINVAL; + } + + return 0; +} + +static int qt602240_unlock_bootloader(struct i2c_client *client) +{ + u8 buf[2]; + + buf[0] = QT602240_UNLOCK_CMD_LSB; + buf[1] = QT602240_UNLOCK_CMD_MSB; + + if (i2c_master_send(client, buf, 2) != 2) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_fw_write(struct i2c_client *client, + const u8 *data, unsigned int frame_size) +{ + if (i2c_master_send(client, data, frame_size) != frame_size) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int __qt602240_read_reg(struct i2c_client *client, + u16 reg, u16 len, void *val) +{ + struct i2c_msg xfer[2]; + u8 buf[2]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + + /* Write register */ + xfer[0].addr = client->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + if (i2c_transfer(client->adapter, xfer, 2) != 2) { + dev_err(&client->dev, "%s: i2c transfer failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_read_reg(struct i2c_client *client, u16 reg, u8 *val) +{ + return __qt602240_read_reg(client, reg, 1, val); +} + +static int qt602240_write_reg(struct i2c_client *client, u16 reg, u8 val) +{ + u8 buf[3]; + + buf[0] = reg & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = val; + + if (i2c_master_send(client, buf, 3) != 3) { + dev_err(&client->dev, "%s: i2c send failed\n", __func__); + return -EIO; + } + + return 0; +} + +static int qt602240_read_object_table(struct i2c_client *client, + u16 reg, u8 *object_buf) +{ + return __qt602240_read_reg(client, reg, QT602240_OBJECT_SIZE, + object_buf); +} + +static struct qt602240_object * +qt602240_get_object(struct qt602240_data *data, u8 type) +{ + struct qt602240_object *object; + int i; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + if (object->type == type) + return object; + } + + dev_err(&data->client->dev, "Invalid object type\n"); + return NULL; +} + +static int qt602240_read_message(struct qt602240_data *data, + struct qt602240_message *message) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, QT602240_GEN_MESSAGE); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __qt602240_read_reg(data->client, reg, + sizeof(struct qt602240_message), message); +} + +static int qt602240_read_object(struct qt602240_data *data, + u8 type, u8 offset, u8 *val) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return __qt602240_read_reg(data->client, reg + offset, 1, val); +} + +static int qt602240_write_object(struct qt602240_data *data, + u8 type, u8 offset, u8 val) +{ + struct qt602240_object *object; + u16 reg; + + object = qt602240_get_object(data, type); + if (!object) + return -EINVAL; + + reg = object->start_address; + return qt602240_write_reg(data->client, reg + offset, val); +} + +static void qt602240_input_report(struct qt602240_data *data, int single_id) +{ + struct qt602240_finger *finger = data->finger; + struct input_dev *input_dev = data->input_dev; + int status = finger[single_id].status; + int finger_num = 0; + int id; + + for (id = 0; id < QT602240_MAX_FINGER; id++) { + if (!finger[id].status) + continue; + + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, + finger[id].status != QT602240_RELEASE ? + finger[id].area : 0); + input_report_abs(input_dev, ABS_MT_POSITION_X, + finger[id].x); + input_report_abs(input_dev, ABS_MT_POSITION_Y, + finger[id].y); + input_mt_sync(input_dev); + + if (finger[id].status == QT602240_RELEASE) + finger[id].status = 0; + else + finger_num++; + } + + input_report_key(input_dev, BTN_TOUCH, finger_num > 0); + + if (status != QT602240_RELEASE) { + input_report_abs(input_dev, ABS_X, finger[single_id].x); + input_report_abs(input_dev, ABS_Y, finger[single_id].y); + } + + input_sync(input_dev); +} + +static void qt602240_input_touchevent(struct qt602240_data *data, + struct qt602240_message *message, int id) +{ + struct qt602240_finger *finger = data->finger; + struct device *dev = &data->client->dev; + u8 status = message->message[0]; + int x; + int y; + int area; + + /* Check the touch is present on the screen */ + if (!(status & QT602240_DETECT)) { + if (status & QT602240_RELEASE) { + dev_dbg(dev, "[%d] released\n", id); + + finger[id].status = QT602240_RELEASE; + qt602240_input_report(data, id); + } + return; + } + + /* Check only AMP detection */ + if (!(status & (QT602240_PRESS | QT602240_MOVE))) + return; + + x = (message->message[1] << 2) | ((message->message[3] & ~0x3f) >> 6); + y = (message->message[2] << 2) | ((message->message[3] & ~0xf3) >> 2); + area = message->message[4]; + + dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id, + status & QT602240_MOVE ? "moved" : "pressed", + x, y, area); + + finger[id].status = status & QT602240_MOVE ? + QT602240_MOVE : QT602240_PRESS; + finger[id].x = x; + finger[id].y = y; + finger[id].area = area; + + qt602240_input_report(data, id); +} + +static irqreturn_t qt602240_interrupt(int irq, void *dev_id) +{ + struct qt602240_data *data = dev_id; + struct qt602240_message message; + struct qt602240_object *object; + struct device *dev = &data->client->dev; + int id; + u8 reportid; + u8 max_reportid; + u8 min_reportid; + + do { + if (qt602240_read_message(data, &message)) { + dev_err(dev, "Failed to read message\n"); + goto end; + } + + reportid = message.reportid; + + /* whether reportid is thing of QT602240_TOUCH_MULTI */ + object = qt602240_get_object(data, QT602240_TOUCH_MULTI); + if (!object) + goto end; + + max_reportid = object->max_reportid; + min_reportid = max_reportid - object->num_report_ids + 1; + id = reportid - min_reportid; + + if (reportid >= min_reportid && reportid <= max_reportid) + qt602240_input_touchevent(data, &message, id); + else + qt602240_dump_message(dev, &message); + } while (reportid != 0xff); + +end: + return IRQ_HANDLED; +} + +static int qt602240_check_reg_init(struct qt602240_data *data) +{ + struct qt602240_object *object; + struct device *dev = &data->client->dev; + int index = 0; + int i, j; + u8 version = data->info.version; + u8 *init_vals; + + switch (version) { + case QT602240_VER_20: + init_vals = (u8 *)init_vals_ver_20; + break; + case QT602240_VER_21: + init_vals = (u8 *)init_vals_ver_21; + break; + case QT602240_VER_22: + init_vals = (u8 *)init_vals_ver_22; + break; + default: + dev_err(dev, "Firmware version %d doesn't support\n", version); + return -EINVAL; + } + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + if (!qt602240_object_writable(object->type)) + continue; + + for (j = 0; j < object->size + 1; j++) + qt602240_write_object(data, object->type, j, + init_vals[index + j]); + + index += object->size + 1; + } + + return 0; +} + +static int qt602240_check_matrix_size(struct qt602240_data *data) +{ + const struct qt602240_platform_data *pdata = data->pdata; + struct device *dev = &data->client->dev; + int mode = -1; + int error; + u8 val; + + dev_dbg(dev, "Number of X lines: %d\n", pdata->x_line); + dev_dbg(dev, "Number of Y lines: %d\n", pdata->y_line); + + switch (pdata->x_line) { + case 0 ... 15: + if (pdata->y_line <= 14) + mode = 0; + break; + case 16: + if (pdata->y_line <= 12) + mode = 1; + if (pdata->y_line == 13 || pdata->y_line == 14) + mode = 0; + break; + case 17: + if (pdata->y_line <= 11) + mode = 2; + if (pdata->y_line == 12 || pdata->y_line == 13) + mode = 1; + break; + case 18: + if (pdata->y_line <= 10) + mode = 3; + if (pdata->y_line == 11 || pdata->y_line == 12) + mode = 2; + break; + case 19: + if (pdata->y_line <= 9) + mode = 4; + if (pdata->y_line == 10 || pdata->y_line == 11) + mode = 3; + break; + case 20: + mode = 4; + } + + if (mode < 0) { + dev_err(dev, "Invalid X/Y lines\n"); + return -EINVAL; + } + + error = qt602240_read_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_MODE, &val); + if (error) + return error; + + if (mode == val) + return 0; + + /* Change the CTE configuration */ + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_CTRL, 1); + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_MODE, mode); + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_CTRL, 0); + + return 0; +} + +static int qt602240_make_highchg(struct qt602240_data *data) +{ + struct device *dev = &data->client->dev; + int count = 10; + int error; + u8 val; + + /* Read dummy message to make high CHG pin */ + do { + error = qt602240_read_object(data, QT602240_GEN_MESSAGE, 0, &val); + if (error) + return error; + } while ((val != 0xff) && --count); + + if (!count) { + dev_err(dev, "CHG pin isn't cleared\n"); + return -EBUSY; + } + + return 0; +} + +static void qt602240_handle_pdata(struct qt602240_data *data) +{ + const struct qt602240_platform_data *pdata = data->pdata; + u8 voltage; + + /* Set touchscreen lines */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_XSIZE, + pdata->x_line); + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_YSIZE, + pdata->y_line); + + /* Set touchscreen orient */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, QT602240_TOUCH_ORIENT, + pdata->orient); + + /* Set touchscreen burst length */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_BLEN, pdata->blen); + + /* Set touchscreen threshold */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_TCHTHR, pdata->threshold); + + /* Set touchscreen resolution */ + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_XRANGE_LSB, (pdata->x_size - 1) & 0xff); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_XRANGE_MSB, (pdata->x_size - 1) >> 8); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_YRANGE_LSB, (pdata->y_size - 1) & 0xff); + qt602240_write_object(data, QT602240_TOUCH_MULTI, + QT602240_TOUCH_YRANGE_MSB, (pdata->y_size - 1) >> 8); + + /* Set touchscreen voltage */ + if (data->info.version >= QT602240_VER_21 && pdata->voltage) { + if (pdata->voltage < QT602240_VOLTAGE_DEFAULT) { + voltage = (QT602240_VOLTAGE_DEFAULT - pdata->voltage) / + QT602240_VOLTAGE_STEP; + voltage = 0xff - voltage + 1; + } else + voltage = (pdata->voltage - QT602240_VOLTAGE_DEFAULT) / + QT602240_VOLTAGE_STEP; + + qt602240_write_object(data, QT602240_SPT_CTECONFIG, + QT602240_CTE_VOLTAGE, voltage); + } +} + +static int qt602240_get_info(struct qt602240_data *data) +{ + struct i2c_client *client = data->client; + struct qt602240_info *info = &data->info; + int error; + u8 val; + + error = qt602240_read_reg(client, QT602240_FAMILY_ID, &val); + if (error) + return error; + info->family_id = val; + + error = qt602240_read_reg(client, QT602240_VARIANT_ID, &val); + if (error) + return error; + info->variant_id = val; + + error = qt602240_read_reg(client, QT602240_VERSION, &val); + if (error) + return error; + info->version = val; + + error = qt602240_read_reg(client, QT602240_BUILD, &val); + if (error) + return error; + info->build = val; + + error = qt602240_read_reg(client, QT602240_OBJECT_NUM, &val); + if (error) + return error; + info->object_num = val; + + return 0; +} + +static int qt602240_get_object_table(struct qt602240_data *data) +{ + int error; + int i; + u16 reg; + u8 reportid = 0; + u8 buf[QT602240_OBJECT_SIZE]; + + for (i = 0; i < data->info.object_num; i++) { + struct qt602240_object *object = data->object_table + i; + + reg = QT602240_OBJECT_START + QT602240_OBJECT_SIZE * i; + error = qt602240_read_object_table(data->client, reg, buf); + if (error) + return error; + + object->type = buf[0]; + object->start_address = (buf[2] << 8) | buf[1]; + object->size = buf[3]; + object->instances = buf[4]; + object->num_report_ids = buf[5]; + + if (object->num_report_ids) { + reportid += object->num_report_ids * + (object->instances + 1); + object->max_reportid = reportid; + } + } + + return 0; +} + +static int qt602240_initialize(struct qt602240_data *data) +{ + struct i2c_client *client = data->client; + struct qt602240_info *info = &data->info; + int error; + u8 val; + + error = qt602240_get_info(data); + if (error) + return error; + + data->object_table = kcalloc(info->object_num, + sizeof(struct qt602240_data), + GFP_KERNEL); + if (!data->object_table) { + dev_err(&client->dev, "Failed to allocate memory\n"); + return -ENOMEM; + } + + /* Get object table information */ + error = qt602240_get_object_table(data); + if (error) + return error; + + /* Check register init values */ + error = qt602240_check_reg_init(data); + if (error) + return error; + + /* Check X/Y matrix size */ + error = qt602240_check_matrix_size(data); + if (error) + return error; + + error = qt602240_make_highchg(data); + if (error) + return error; + + qt602240_handle_pdata(data); + + /* Backup to memory */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_BACKUPNV, + QT602240_BACKUP_VALUE); + msleep(QT602240_BACKUP_TIME); + + /* Soft reset */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, 1); + msleep(QT602240_RESET_TIME); + + /* Update matrix size at info struct */ + error = qt602240_read_reg(client, QT602240_MATRIX_X_SIZE, &val); + if (error) + return error; + info->matrix_xsize = val; + + error = qt602240_read_reg(client, QT602240_MATRIX_Y_SIZE, &val); + if (error) + return error; + info->matrix_ysize = val; + + dev_info(&client->dev, + "Family ID: %d Variant ID: %d Version: %d Build: %d\n", + info->family_id, info->variant_id, info->version, + info->build); + + dev_info(&client->dev, + "Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n", + info->matrix_xsize, info->matrix_ysize, + info->object_num); + + return 0; +} + +static ssize_t qt602240_object_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + struct qt602240_object *object; + int count = 0; + int i, j; + int error; + u8 val; + + for (i = 0; i < data->info.object_num; i++) { + object = data->object_table + i; + + count += sprintf(buf + count, + "Object Table Element %d(Type %d)\n", + i + 1, object->type); + + if (!qt602240_object_readable(object->type)) { + count += sprintf(buf + count, "\n"); + continue; + } + + for (j = 0; j < object->size + 1; j++) { + error = qt602240_read_object(data, + object->type, j, &val); + if (error) + return error; + + count += sprintf(buf + count, + " Byte %d: 0x%x (%d)\n", j, val, val); + } + + count += sprintf(buf + count, "\n"); + } + + return count; +} + +static int qt602240_load_fw(struct device *dev, const char *fn) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; + const struct firmware *fw = NULL; + unsigned int frame_size; + unsigned int pos = 0; + int ret; + + ret = request_firmware(&fw, fn, dev); + if (ret) { + dev_err(dev, "Unable to open firmware %s\n", fn); + return ret; + } + + /* Change to the bootloader mode */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, QT602240_BOOT_VALUE); + msleep(QT602240_RESET_TIME); + + /* Change to slave address of bootloader */ + if (client->addr == QT602240_APP_LOW) + client->addr = QT602240_BOOT_LOW; + else + client->addr = QT602240_BOOT_HIGH; + + ret = qt602240_check_bootloader(client, QT602240_WAITING_BOOTLOAD_CMD); + if (ret) + goto out; + + /* Unlock bootloader */ + qt602240_unlock_bootloader(client); + + while (pos < fw->size) { + ret = qt602240_check_bootloader(client, + QT602240_WAITING_FRAME_DATA); + if (ret) + goto out; + + frame_size = ((*(fw->data + pos) << 8) | *(fw->data + pos + 1)); + + /* We should add 2 at frame size as the the firmware data is not + * included the CRC bytes. + */ + frame_size += 2; + + /* Write one frame to device */ + qt602240_fw_write(client, fw->data + pos, frame_size); + + ret = qt602240_check_bootloader(client, + QT602240_FRAME_CRC_PASS); + if (ret) + goto out; + + pos += frame_size; + + dev_dbg(dev, "Updated %d bytes / %zd bytes\n", pos, fw->size); + } + +out: + release_firmware(fw); + + /* Change to slave address of application */ + if (client->addr == QT602240_BOOT_LOW) + client->addr = QT602240_APP_LOW; + else + client->addr = QT602240_APP_HIGH; + + return ret; +} + +static ssize_t qt602240_update_fw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct qt602240_data *data = dev_get_drvdata(dev); + unsigned int version; + int error; + + if (sscanf(buf, "%u", &version) != 1) { + dev_err(dev, "Invalid values\n"); + return -EINVAL; + } + + if (data->info.version < QT602240_VER_21 || version < QT602240_VER_21) { + dev_err(dev, "FW update supported starting with version 21\n"); + return -EINVAL; + } + + disable_irq(data->irq); + + error = qt602240_load_fw(dev, QT602240_FW_NAME); + if (error) { + dev_err(dev, "The firmware update failed(%d)\n", error); + count = error; + } else { + dev_dbg(dev, "The firmware update succeeded\n"); + + /* Wait for reset */ + msleep(QT602240_FWRESET_TIME); + + kfree(data->object_table); + data->object_table = NULL; + + qt602240_initialize(data); + } + + enable_irq(data->irq); + + return count; +} + +static DEVICE_ATTR(object, 0444, qt602240_object_show, NULL); +static DEVICE_ATTR(update_fw, 0664, NULL, qt602240_update_fw_store); + +static struct attribute *qt602240_attrs[] = { + &dev_attr_object.attr, + &dev_attr_update_fw.attr, + NULL +}; + +static const struct attribute_group qt602240_attr_group = { + .attrs = qt602240_attrs, +}; + +static void qt602240_start(struct qt602240_data *data) +{ + /* Touch enable */ + qt602240_write_object(data, + QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0x83); +} + +static void qt602240_stop(struct qt602240_data *data) +{ + /* Touch disable */ + qt602240_write_object(data, + QT602240_TOUCH_MULTI, QT602240_TOUCH_CTRL, 0); +} + +static int qt602240_input_open(struct input_dev *dev) +{ + struct qt602240_data *data = input_get_drvdata(dev); + + qt602240_start(data); + + return 0; +} + +static void qt602240_input_close(struct input_dev *dev) +{ + struct qt602240_data *data = input_get_drvdata(dev); + + qt602240_stop(data); +} + +static int __devinit qt602240_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct qt602240_data *data; + struct input_dev *input_dev; + int error; + + if (!client->dev.platform_data) + return -EINVAL; + + data = kzalloc(sizeof(struct qt602240_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + input_dev->name = "AT42QT602240/ATMXT224 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = qt602240_input_open; + input_dev->close = qt602240_input_close; + + __set_bit(EV_ABS, input_dev->evbit); + __set_bit(EV_KEY, input_dev->evbit); + __set_bit(BTN_TOUCH, input_dev->keybit); + + /* For single touch */ + input_set_abs_params(input_dev, ABS_X, + 0, QT602240_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_Y, + 0, QT602240_MAX_YC, 0, 0); + + /* For multi touch */ + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, QT602240_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, QT602240_MAX_XC, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, QT602240_MAX_YC, 0, 0); + + input_set_drvdata(input_dev, data); + + data->client = client; + data->input_dev = input_dev; + data->pdata = client->dev.platform_data; + data->irq = client->irq; + + i2c_set_clientdata(client, data); + + error = qt602240_initialize(data); + if (error) + goto err_free_object; + + error = request_threaded_irq(client->irq, NULL, qt602240_interrupt, + IRQF_TRIGGER_FALLING, client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_object; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + + error = sysfs_create_group(&client->dev.kobj, &qt602240_attr_group); + if (error) + goto err_unregister_device; + + return 0; + +err_unregister_device: + input_unregister_device(input_dev); + input_dev = NULL; +err_free_irq: + free_irq(client->irq, data); +err_free_object: + kfree(data->object_table); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit qt602240_remove(struct i2c_client *client) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &qt602240_attr_group); + free_irq(data->irq, data); + input_unregister_device(data->input_dev); + kfree(data->object_table); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int qt602240_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + qt602240_stop(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int qt602240_resume(struct i2c_client *client) +{ + struct qt602240_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + + /* Soft reset */ + qt602240_write_object(data, QT602240_GEN_COMMAND, + QT602240_COMMAND_RESET, 1); + + msleep(QT602240_RESET_TIME); + + mutex_lock(&input_dev->mutex); + + if (input_dev->users) + qt602240_start(data); + + mutex_unlock(&input_dev->mutex); + + return 0; +} +#else +#define qt602240_suspend NULL +#define qt602240_resume NULL +#endif + +static const struct i2c_device_id qt602240_id[] = { + { "qt602240_ts", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qt602240_id); + +static struct i2c_driver qt602240_driver = { + .driver = { + .name = "qt602240_ts", + .owner = THIS_MODULE, + }, + .probe = qt602240_probe, + .remove = __devexit_p(qt602240_remove), + .suspend = qt602240_suspend, + .resume = qt602240_resume, + .id_table = qt602240_id, +}; + +static int __init qt602240_init(void) +{ + return i2c_add_driver(&qt602240_driver); +} + +static void __exit qt602240_exit(void) +{ + i2c_del_driver(&qt602240_driver); +} + +module_init(qt602240_init); +module_exit(qt602240_exit); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>"); +MODULE_DESCRIPTION("AT42QT602240/ATMXT224 Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/tps6507x-ts.c b/drivers/input/touchscreen/tps6507x-ts.c index 5b70a14..a644d18 100644 --- a/drivers/input/touchscreen/tps6507x-ts.c +++ b/drivers/input/touchscreen/tps6507x-ts.c @@ -355,9 +355,6 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev) struct tps6507x_ts *tsc = tps6507x_dev->ts; struct input_dev *input_dev = tsc->input_dev; - if (!tsc) - return 0; - cancel_delayed_work_sync(&tsc->work); destroy_workqueue(tsc->wq); diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c index 567d572..f45f80f 100644 --- a/drivers/input/touchscreen/usbtouchscreen.c +++ b/drivers/input/touchscreen/usbtouchscreen.c @@ -95,6 +95,7 @@ struct usbtouch_device_info { int (*get_pkt_len) (unsigned char *pkt, int len); int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt); + int (*alloc) (struct usbtouch_usb *usbtouch); int (*init) (struct usbtouch_usb *usbtouch); void (*exit) (struct usbtouch_usb *usbtouch); }; @@ -135,7 +136,7 @@ enum { DEVTYPE_JASTEC, DEVTYPE_E2I, DEVTYPE_ZYTRONIC, - DEVTYPE_TC5UH, + DEVTYPE_TC45USB, DEVTYPE_NEXIO, }; @@ -222,8 +223,11 @@ static const struct usb_device_id usbtouch_devices[] = { {USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC}, #endif -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH - {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH}, +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB + /* TC5UH */ + {USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB}, + /* TC4UM */ + {USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB}, #endif #ifdef CONFIG_TOUCHSCREEN_USB_NEXIO @@ -507,7 +511,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch) int ret = -ENOMEM; unsigned char *buf; - buf = kmalloc(2, GFP_KERNEL); + buf = kmalloc(2, GFP_NOIO); if (!buf) goto err_nobuf; /* reset */ @@ -574,10 +578,10 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt) #endif /***************************************************************************** - * ET&T TC5UH part + * ET&T TC5UH/TC4UM part */ -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH -static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt) +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB +static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt) { dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1]; dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3]; @@ -732,11 +736,43 @@ static void nexio_ack_complete(struct urb *urb) { } +static int nexio_alloc(struct usbtouch_usb *usbtouch) +{ + struct nexio_priv *priv; + int ret = -ENOMEM; + + usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); + if (!usbtouch->priv) + goto out_buf; + + priv = usbtouch->priv; + + priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), + GFP_KERNEL); + if (!priv->ack_buf) + goto err_priv; + + priv->ack = usb_alloc_urb(0, GFP_KERNEL); + if (!priv->ack) { + dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); + goto err_ack_buf; + } + + return 0; + +err_ack_buf: + kfree(priv->ack_buf); +err_priv: + kfree(priv); +out_buf: + return ret; +} + static int nexio_init(struct usbtouch_usb *usbtouch) { struct usb_device *dev = interface_to_usbdev(usbtouch->interface); struct usb_host_interface *interface = usbtouch->interface->cur_altsetting; - struct nexio_priv *priv; + struct nexio_priv *priv = usbtouch->priv; int ret = -ENOMEM; int actual_len, i; unsigned char *buf; @@ -755,7 +791,7 @@ static int nexio_init(struct usbtouch_usb *usbtouch) if (!input_ep || !output_ep) return -ENXIO; - buf = kmalloc(NEXIO_BUFSIZE, GFP_KERNEL); + buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO); if (!buf) goto out_buf; @@ -787,11 +823,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch) switch (buf[0]) { case 0x83: /* firmware version */ if (!firmware_ver) - firmware_ver = kstrdup(&buf[2], GFP_KERNEL); + firmware_ver = kstrdup(&buf[2], GFP_NOIO); break; case 0x84: /* device name */ if (!device_name) - device_name = kstrdup(&buf[2], GFP_KERNEL); + device_name = kstrdup(&buf[2], GFP_NOIO); break; } } @@ -802,36 +838,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch) kfree(firmware_ver); kfree(device_name); - /* prepare ACK URB */ - ret = -ENOMEM; - - usbtouch->priv = kmalloc(sizeof(struct nexio_priv), GFP_KERNEL); - if (!usbtouch->priv) - goto out_buf; - - priv = usbtouch->priv; - - priv->ack_buf = kmemdup(nexio_ack_pkt, sizeof(nexio_ack_pkt), - GFP_KERNEL); - if (!priv->ack_buf) - goto err_priv; - - priv->ack = usb_alloc_urb(0, GFP_KERNEL); - if (!priv->ack) { - dbg("%s - usb_alloc_urb failed: usbtouch->ack", __func__); - goto err_ack_buf; - } - usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep), priv->ack_buf, sizeof(nexio_ack_pkt), nexio_ack_complete, usbtouch); ret = 0; - goto out_buf; -err_ack_buf: - kfree(priv->ack_buf); -err_priv: - kfree(priv); out_buf: kfree(buf); return ret; @@ -849,29 +860,32 @@ static void nexio_exit(struct usbtouch_usb *usbtouch) static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) { - int x, y, begin_x, begin_y, end_x, end_y, w, h, ret; struct nexio_touch_packet *packet = (void *) pkt; struct nexio_priv *priv = usbtouch->priv; + unsigned int data_len = be16_to_cpu(packet->data_len); + unsigned int x_len = be16_to_cpu(packet->x_len); + unsigned int y_len = be16_to_cpu(packet->y_len); + int x, y, begin_x, begin_y, end_x, end_y, w, h, ret; /* got touch data? */ if ((pkt[0] & 0xe0) != 0xe0) return 0; - if (be16_to_cpu(packet->data_len) > 0xff) - packet->data_len = cpu_to_be16(be16_to_cpu(packet->data_len) - 0x100); - if (be16_to_cpu(packet->x_len) > 0xff) - packet->x_len = cpu_to_be16(be16_to_cpu(packet->x_len) - 0x80); + if (data_len > 0xff) + data_len -= 0x100; + if (x_len > 0xff) + x_len -= 0x80; /* send ACK */ ret = usb_submit_urb(priv->ack, GFP_ATOMIC); if (!usbtouch->type->max_xc) { - usbtouch->type->max_xc = 2 * be16_to_cpu(packet->x_len); - input_set_abs_params(usbtouch->input, ABS_X, 0, - 2 * be16_to_cpu(packet->x_len), 0, 0); - usbtouch->type->max_yc = 2 * be16_to_cpu(packet->y_len); - input_set_abs_params(usbtouch->input, ABS_Y, 0, - 2 * be16_to_cpu(packet->y_len), 0, 0); + usbtouch->type->max_xc = 2 * x_len; + input_set_abs_params(usbtouch->input, ABS_X, + 0, usbtouch->type->max_xc, 0, 0); + usbtouch->type->max_yc = 2 * y_len; + input_set_abs_params(usbtouch->input, ABS_Y, + 0, usbtouch->type->max_yc, 0, 0); } /* * The device reports state of IR sensors on X and Y axes. @@ -881,22 +895,21 @@ static int nexio_read_data(struct usbtouch_usb *usbtouch, unsigned char *pkt) * it's disabled (and untested) here as there's no X driver for that. */ begin_x = end_x = begin_y = end_y = -1; - for (x = 0; x < be16_to_cpu(packet->x_len); x++) { + for (x = 0; x < x_len; x++) { if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) { begin_x = x; continue; } if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) { end_x = x - 1; - for (y = be16_to_cpu(packet->x_len); - y < be16_to_cpu(packet->data_len); y++) { + for (y = x_len; y < data_len; y++) { if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) { - begin_y = y - be16_to_cpu(packet->x_len); + begin_y = y - x_len; continue; } if (end_y == -1 && begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) { - end_y = y - 1 - be16_to_cpu(packet->x_len); + end_y = y - 1 - x_len; w = end_x - begin_x; h = end_y - begin_y; #if 0 @@ -1104,14 +1117,14 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { }, #endif -#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH - [DEVTYPE_TC5UH] = { +#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB + [DEVTYPE_TC45USB] = { .min_xc = 0x0, .max_xc = 0x0fff, .min_yc = 0x0, .max_yc = 0x0fff, .rept_size = 5, - .read_data = tc5uh_read_data, + .read_data = tc45usb_read_data, }, #endif @@ -1120,6 +1133,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = { .rept_size = 1024, .irq_always = true, .read_data = nexio_read_data, + .alloc = nexio_alloc, .init = nexio_init, .exit = nexio_exit, }, @@ -1263,6 +1277,7 @@ static void usbtouch_irq(struct urb *urb) usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length); exit: + usb_mark_last_busy(interface_to_usbdev(usbtouch->interface)); retval = usb_submit_urb(urb, GFP_ATOMIC); if (retval) err("%s - usb_submit_urb failed with result: %d", @@ -1272,25 +1287,89 @@ exit: static int usbtouch_open(struct input_dev *input) { struct usbtouch_usb *usbtouch = input_get_drvdata(input); + int r; usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface); + r = usb_autopm_get_interface(usbtouch->interface) ? -EIO : 0; + if (r < 0) + goto out; + if (!usbtouch->type->irq_always) { - if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) - return -EIO; + if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) { + r = -EIO; + goto out_put; + } } - return 0; + usbtouch->interface->needs_remote_wakeup = 1; +out_put: + usb_autopm_put_interface(usbtouch->interface); +out: + return r; } static void usbtouch_close(struct input_dev *input) { struct usbtouch_usb *usbtouch = input_get_drvdata(input); + int r; if (!usbtouch->type->irq_always) usb_kill_urb(usbtouch->irq); + r = usb_autopm_get_interface(usbtouch->interface); + usbtouch->interface->needs_remote_wakeup = 0; + if (!r) + usb_autopm_put_interface(usbtouch->interface); } +static int usbtouch_suspend +(struct usb_interface *intf, pm_message_t message) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + + usb_kill_urb(usbtouch->irq); + + return 0; +} + +static int usbtouch_resume(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct input_dev *input = usbtouch->input; + int result = 0; + + mutex_lock(&input->mutex); + if (input->users || usbtouch->type->irq_always) + result = usb_submit_urb(usbtouch->irq, GFP_NOIO); + mutex_unlock(&input->mutex); + + return result; +} + +static int usbtouch_reset_resume(struct usb_interface *intf) +{ + struct usbtouch_usb *usbtouch = usb_get_intfdata(intf); + struct input_dev *input = usbtouch->input; + int err = 0; + + /* reinit the device */ + if (usbtouch->type->init) { + err = usbtouch->type->init(usbtouch); + if (err) { + dbg("%s - type->init() failed, err: %d", + __func__, err); + return err; + } + } + + /* restart IO if needed */ + mutex_lock(&input->mutex); + if (input->users) + err = usb_submit_urb(usbtouch->irq, GFP_NOIO); + mutex_unlock(&input->mutex); + + return err; +} static void usbtouch_free_buffers(struct usb_device *udev, struct usbtouch_usb *usbtouch) @@ -1411,12 +1490,21 @@ static int usbtouch_probe(struct usb_interface *intf, usbtouch->irq->transfer_dma = usbtouch->data_dma; usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - /* device specific init */ + /* device specific allocations */ + if (type->alloc) { + err = type->alloc(usbtouch); + if (err) { + dbg("%s - type->alloc() failed, err: %d", __func__, err); + goto out_free_urb; + } + } + + /* device specific initialisation*/ if (type->init) { err = type->init(usbtouch); if (err) { dbg("%s - type->init() failed, err: %d", __func__, err); - goto out_free_urb; + goto out_do_exit; } } @@ -1429,8 +1517,11 @@ static int usbtouch_probe(struct usb_interface *intf, usb_set_intfdata(intf, usbtouch); if (usbtouch->type->irq_always) { + /* this can't fail */ + usb_autopm_get_interface(intf); err = usb_submit_urb(usbtouch->irq, GFP_KERNEL); if (err) { + usb_autopm_put_interface(intf); err("%s - usb_submit_urb failed with result: %d", __func__, err); goto out_unregister_input; @@ -1481,7 +1572,11 @@ static struct usb_driver usbtouch_driver = { .name = "usbtouchscreen", .probe = usbtouch_probe, .disconnect = usbtouch_disconnect, + .suspend = usbtouch_suspend, + .resume = usbtouch_resume, + .reset_resume = usbtouch_reset_resume, .id_table = usbtouch_devices, + .supports_autosuspend = 1, }; static int __init usbtouch_init(void) diff --git a/include/linux/i2c/adp5588.h b/include/linux/i2c/adp5588.h index 02c9af3..269181b 100644 --- a/include/linux/i2c/adp5588.h +++ b/include/linux/i2c/adp5588.h @@ -78,6 +78,40 @@ #define ADP5588_KEYMAPSIZE 80 +#define GPI_PIN_ROW0 97 +#define GPI_PIN_ROW1 98 +#define GPI_PIN_ROW2 99 +#define GPI_PIN_ROW3 100 +#define GPI_PIN_ROW4 101 +#define GPI_PIN_ROW5 102 +#define GPI_PIN_ROW6 103 +#define GPI_PIN_ROW7 104 +#define GPI_PIN_COL0 105 +#define GPI_PIN_COL1 106 +#define GPI_PIN_COL2 107 +#define GPI_PIN_COL3 108 +#define GPI_PIN_COL4 109 +#define GPI_PIN_COL5 110 +#define GPI_PIN_COL6 111 +#define GPI_PIN_COL7 112 +#define GPI_PIN_COL8 113 +#define GPI_PIN_COL9 114 + +#define GPI_PIN_ROW_BASE GPI_PIN_ROW0 +#define GPI_PIN_ROW_END GPI_PIN_ROW7 +#define GPI_PIN_COL_BASE GPI_PIN_COL0 +#define GPI_PIN_COL_END GPI_PIN_COL9 + +#define GPI_PIN_BASE GPI_PIN_ROW_BASE +#define GPI_PIN_END GPI_PIN_COL_END + +#define ADP5588_GPIMAPSIZE_MAX (GPI_PIN_END - GPI_PIN_BASE + 1) + +struct adp5588_gpi_map { + unsigned short pin; + unsigned short sw_evt; +}; + struct adp5588_kpad_platform_data { int rows; /* Number of rows */ int cols; /* Number of columns */ @@ -87,6 +121,9 @@ struct adp5588_kpad_platform_data { unsigned en_keylock:1; /* Enable Key Lock feature */ unsigned short unlock_key1; /* Unlock Key 1 */ unsigned short unlock_key2; /* Unlock Key 2 */ + const struct adp5588_gpi_map *gpimap; + unsigned short gpimapsize; + const struct adp5588_gpio_platform_data *gpio_data; }; struct adp5588_gpio_platform_data { diff --git a/include/linux/i2c/mcs.h b/include/linux/i2c/mcs.h new file mode 100644 index 0000000..725ae7c --- /dev/null +++ b/include/linux/i2c/mcs.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2009 - 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * Author: HeungJun Kim <riverful.kim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#ifndef __LINUX_MCS_H +#define __LINUX_MCS_H + +#define MCS_KEY_MAP(v, c) ((((v) & 0xff) << 16) | ((c) & 0xffff)) +#define MCS_KEY_VAL(v) (((v) >> 16) & 0xff) +#define MCS_KEY_CODE(v) ((v) & 0xffff) + +struct mcs_platform_data { + void (*cfg_pin)(void); + + /* touchscreen */ + unsigned int x_size; + unsigned int y_size; + + /* touchkey */ + const u32 *keymap; + unsigned int keymap_size; + unsigned int key_maxval; + bool no_autorepeat; +}; + +#endif /* __LINUX_MCS_H */ diff --git a/include/linux/i2c/mcs5000_ts.h b/include/linux/i2c/mcs5000_ts.h deleted file mode 100644 index 5a117b5..0000000 --- a/include/linux/i2c/mcs5000_ts.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * mcs5000_ts.h - * - * Copyright (C) 2009 Samsung Electronics Co.Ltd - * Author: Joonyoung Shim <jy0922.shim@samsung.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#ifndef __LINUX_MCS5000_TS_H -#define __LINUX_MCS5000_TS_H - -/* platform data for the MELFAS MCS-5000 touchscreen driver */ -struct mcs5000_ts_platform_data { - void (*cfg_pin)(void); - int x_size; - int y_size; -}; - -#endif /* __LINUX_MCS5000_TS_H */ diff --git a/include/linux/i2c/qt602240_ts.h b/include/linux/i2c/qt602240_ts.h new file mode 100644 index 0000000..c5033e1 --- /dev/null +++ b/include/linux/i2c/qt602240_ts.h @@ -0,0 +1,38 @@ +/* + * AT42QT602240/ATMXT224 Touchscreen driver + * + * Copyright (C) 2010 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim <jy0922.shim@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef __LINUX_QT602240_TS_H +#define __LINUX_QT602240_TS_H + +/* Orient */ +#define QT602240_NORMAL 0x0 +#define QT602240_DIAGONAL 0x1 +#define QT602240_HORIZONTAL_FLIP 0x2 +#define QT602240_ROTATED_90_COUNTER 0x3 +#define QT602240_VERTICAL_FLIP 0x4 +#define QT602240_ROTATED_90 0x5 +#define QT602240_ROTATED_180 0x6 +#define QT602240_DIAGONAL_COUNTER 0x7 + +/* The platform data for the AT42QT602240/ATMXT224 touchscreen driver */ +struct qt602240_platform_data { + unsigned int x_line; + unsigned int y_line; + unsigned int x_size; + unsigned int y_size; + unsigned int blen; + unsigned int threshold; + unsigned int voltage; + unsigned char orient; +}; + +#endif /* __LINUX_QT602240_TS_H */ diff --git a/include/linux/input.h b/include/linux/input.h index 6fcc910..339d043 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -691,9 +691,12 @@ struct input_absinfo { #define ABS_TILT_X 0x1a #define ABS_TILT_Y 0x1b #define ABS_TOOL_WIDTH 0x1c + #define ABS_VOLUME 0x20 + #define ABS_MISC 0x28 +#define ABS_MT_SLOT 0x2f /* MT slot being modified */ #define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ #define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ @@ -706,6 +709,12 @@ struct input_absinfo { #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ +#ifdef __KERNEL__ +/* Implementation details, userspace should not care about these */ +#define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR +#define ABS_MT_LAST ABS_MT_PRESSURE +#endif + #define ABS_MAX 0x3f #define ABS_CNT (ABS_MAX+1) @@ -1048,6 +1057,14 @@ struct ff_effect { #include <linux/mod_devicetable.h> /** + * struct input_mt_slot - represents the state of an input MT slot + * @abs: holds current values of ABS_MT axes for this slot + */ +struct input_mt_slot { + int abs[ABS_MT_LAST - ABS_MT_FIRST + 1]; +}; + +/** * struct input_dev - represents an input device * @name: name of the device * @phys: physical path to the device in the system hierarchy @@ -1063,6 +1080,10 @@ struct ff_effect { * @sndbit: bitmap of sound effects supported by the device * @ffbit: bitmap of force feedback effects supported by the device * @swbit: bitmap of switches present on the device + * @hint_events_per_packet: average number of events generated by the + * device in a packet (between EV_SYN/SYN_REPORT events). Used by + * event handlers to estimate size of the buffer needed to hold + * events. * @keycodemax: size of keycode table * @keycodesize: size of elements in keycode table * @keycode: map of scancodes to keycodes for this device @@ -1078,9 +1099,12 @@ struct ff_effect { * @repeat_key: stores key code of the last key pressed; used to implement * software autorepeat * @timer: timer for software autorepeat - * @sync: set to 1 when there were no new events since last EV_SYNC * @abs: current values for reports from absolute axes * @rep: current values for autorepeat parameters (delay, rate) + * @mt: pointer to array of struct input_mt_slot holding current values + * of tracked contacts + * @mtsize: number of MT slots the device uses + * @slot: MT slot currently being transmitted * @key: reflects current state of device's keys/buttons * @led: reflects current state of device's LEDs * @snd: reflects current state of sound effects @@ -1119,6 +1143,7 @@ struct ff_effect { * last user closes the device * @going_away: marks devices that are in a middle of unregistering and * causes input_open_device*() fail with -ENODEV. + * @sync: set to %true when there were no new events since last EV_SYN * @dev: driver model's view of this device * @h_list: list of input handles associated with the device. When * accessing the list dev->mutex must be held @@ -1140,6 +1165,8 @@ struct input_dev { unsigned long ffbit[BITS_TO_LONGS(FF_CNT)]; unsigned long swbit[BITS_TO_LONGS(SW_CNT)]; + unsigned int hint_events_per_packet; + unsigned int keycodemax; unsigned int keycodesize; void *keycode; @@ -1153,11 +1180,13 @@ struct input_dev { unsigned int repeat_key; struct timer_list timer; - int sync; - int abs[ABS_CNT]; int rep[REP_MAX + 1]; + struct input_mt_slot *mt; + int mtsize; + int slot; + unsigned long key[BITS_TO_LONGS(KEY_CNT)]; unsigned long led[BITS_TO_LONGS(LED_CNT)]; unsigned long snd[BITS_TO_LONGS(SND_CNT)]; @@ -1182,6 +1211,8 @@ struct input_dev { unsigned int users; bool going_away; + bool sync; + struct device dev; struct list_head h_list; @@ -1406,8 +1437,28 @@ static inline void input_mt_sync(struct input_dev *dev) input_event(dev, EV_SYN, SYN_MT_REPORT, 0); } +static inline void input_mt_slot(struct input_dev *dev, int slot) +{ + input_event(dev, EV_ABS, ABS_MT_SLOT, slot); +} + void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code); +/** + * input_set_events_per_packet - tell handlers about the driver event rate + * @dev: the input device used by the driver + * @n_events: the average number of events between calls to input_sync() + * + * If the event rate sent from a device is unusually large, use this + * function to set the expected event rate. This will allow handlers + * to set up an appropriate buffer size for the event stream, in order + * to minimize information loss. + */ +static inline void input_set_events_per_packet(struct input_dev *dev, int n_events) +{ + dev->hint_events_per_packet = n_events; +} + static inline void input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat) { dev->absmin[axis] = min; @@ -1485,5 +1536,8 @@ int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file); int input_ff_create_memless(struct input_dev *dev, void *data, int (*play_effect)(struct input_dev *, void *, struct ff_effect *)); +int input_mt_create_slots(struct input_dev *dev, unsigned int num_slots); +void input_mt_destroy_slots(struct input_dev *dev); + #endif #endif diff --git a/include/linux/input/adxl34x.h b/include/linux/input/adxl34x.h new file mode 100644 index 0000000..df00d99 --- /dev/null +++ b/include/linux/input/adxl34x.h @@ -0,0 +1,349 @@ +/* + * include/linux/input/adxl34x.h + * + * Digital Accelerometer characteristics are highly application specific + * and may vary between boards and models. The platform_data for the + * device's "struct device" holds this information. + * + * Copyright 2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#ifndef __LINUX_INPUT_ADXL34X_H__ +#define __LINUX_INPUT_ADXL34X_H__ + +struct adxl34x_platform_data { + + /* + * X,Y,Z Axis Offset: + * offer user offset adjustments in twoscompliment + * form with a scale factor of 15.6 mg/LSB (i.e. 0x7F = +2 g) + */ + + s8 x_axis_offset; + s8 y_axis_offset; + s8 z_axis_offset; + + /* + * TAP_X/Y/Z Enable: Setting TAP_X, Y, or Z Enable enables X, + * Y, or Z participation in Tap detection. A '0' excludes the + * selected axis from participation in Tap detection. + * Setting the SUPPRESS bit suppresses Double Tap detection if + * acceleration greater than tap_threshold is present between + * taps. + */ + +#define ADXL_SUPPRESS (1 << 3) +#define ADXL_TAP_X_EN (1 << 2) +#define ADXL_TAP_Y_EN (1 << 1) +#define ADXL_TAP_Z_EN (1 << 0) + + u8 tap_axis_control; + + /* + * tap_threshold: + * holds the threshold value for tap detection/interrupts. + * The data format is unsigned. The scale factor is 62.5 mg/LSB + * (i.e. 0xFF = +16 g). A zero value may result in undesirable + * behavior if Tap/Double Tap is enabled. + */ + + u8 tap_threshold; + + /* + * tap_duration: + * is an unsigned time value representing the maximum + * time that an event must be above the tap_threshold threshold + * to qualify as a tap event. The scale factor is 625 us/LSB. A zero + * value will prevent Tap/Double Tap functions from working. + */ + + u8 tap_duration; + + /* + * tap_latency: + * is an unsigned time value representing the wait time + * from the detection of a tap event to the opening of the time + * window tap_window for a possible second tap event. The scale + * factor is 1.25 ms/LSB. A zero value will disable the Double Tap + * function. + */ + + u8 tap_latency; + + /* + * tap_window: + * is an unsigned time value representing the amount + * of time after the expiration of tap_latency during which a second + * tap can begin. The scale factor is 1.25 ms/LSB. A zero value will + * disable the Double Tap function. + */ + + u8 tap_window; + + /* + * act_axis_control: + * X/Y/Z Enable: A '1' enables X, Y, or Z participation in activity + * or inactivity detection. A '0' excludes the selected axis from + * participation. If all of the axes are excluded, the function is + * disabled. + * AC/DC: A '0' = DC coupled operation and a '1' = AC coupled + * operation. In DC coupled operation, the current acceleration is + * compared with activity_threshold and inactivity_threshold directly + * to determine whether activity or inactivity is detected. In AC + * coupled operation for activity detection, the acceleration value + * at the start of activity detection is taken as a reference value. + * New samples of acceleration are then compared to this + * reference value and if the magnitude of the difference exceeds + * activity_threshold the device will trigger an activity interrupt. In + * AC coupled operation for inactivity detection, a reference value + * is used again for comparison and is updated whenever the + * device exceeds the inactivity threshold. Once the reference + * value is selected, the device compares the magnitude of the + * difference between the reference value and the current + * acceleration with inactivity_threshold. If the difference is below + * inactivity_threshold for a total of inactivity_time, the device is + * considered inactive and the inactivity interrupt is triggered. + */ + +#define ADXL_ACT_ACDC (1 << 7) +#define ADXL_ACT_X_EN (1 << 6) +#define ADXL_ACT_Y_EN (1 << 5) +#define ADXL_ACT_Z_EN (1 << 4) +#define ADXL_INACT_ACDC (1 << 3) +#define ADXL_INACT_X_EN (1 << 2) +#define ADXL_INACT_Y_EN (1 << 1) +#define ADXL_INACT_Z_EN (1 << 0) + + u8 act_axis_control; + + /* + * activity_threshold: + * holds the threshold value for activity detection. + * The data format is unsigned. The scale factor is + * 62.5 mg/LSB. A zero value may result in undesirable behavior if + * Activity interrupt is enabled. + */ + + u8 activity_threshold; + + /* + * inactivity_threshold: + * holds the threshold value for inactivity + * detection. The data format is unsigned. The scale + * factor is 62.5 mg/LSB. A zero value may result in undesirable + * behavior if Inactivity interrupt is enabled. + */ + + u8 inactivity_threshold; + + /* + * inactivity_time: + * is an unsigned time value representing the + * amount of time that acceleration must be below the value in + * inactivity_threshold for inactivity to be declared. The scale factor + * is 1 second/LSB. Unlike the other interrupt functions, which + * operate on unfiltered data, the inactivity function operates on the + * filtered output data. At least one output sample must be + * generated for the inactivity interrupt to be triggered. This will + * result in the function appearing un-responsive if the + * inactivity_time register is set with a value less than the time + * constant of the Output Data Rate. A zero value will result in an + * interrupt when the output data is below inactivity_threshold. + */ + + u8 inactivity_time; + + /* + * free_fall_threshold: + * holds the threshold value for Free-Fall detection. + * The data format is unsigned. The root-sum-square(RSS) value + * of all axes is calculated and compared to the value in + * free_fall_threshold to determine if a free fall event may be + * occurring. The scale factor is 62.5 mg/LSB. A zero value may + * result in undesirable behavior if Free-Fall interrupt is + * enabled. Values between 300 and 600 mg (0x05 to 0x09) are + * recommended. + */ + + u8 free_fall_threshold; + + /* + * free_fall_time: + * is an unsigned time value representing the minimum + * time that the RSS value of all axes must be less than + * free_fall_threshold to generate a Free-Fall interrupt. The + * scale factor is 5 ms/LSB. A zero value may result in + * undesirable behavior if Free-Fall interrupt is enabled. + * Values between 100 to 350 ms (0x14 to 0x46) are recommended. + */ + + u8 free_fall_time; + + /* + * data_rate: + * Selects device bandwidth and output data rate. + * RATE = 3200 Hz / (2^(15 - x)). Default value is 0x0A, or 100 Hz + * Output Data Rate. An Output Data Rate should be selected that + * is appropriate for the communication protocol and frequency + * selected. Selecting too high of an Output Data Rate with a low + * communication speed will result in samples being discarded. + */ + + u8 data_rate; + + /* + * data_range: + * FULL_RES: When this bit is set with the device is + * in Full-Resolution Mode, where the output resolution increases + * with RANGE to maintain a 4 mg/LSB scale factor. When this + * bit is cleared the device is in 10-bit Mode and RANGE determine the + * maximum g-Range and scale factor. + */ + +#define ADXL_FULL_RES (1 << 3) +#define ADXL_RANGE_PM_2g 0 +#define ADXL_RANGE_PM_4g 1 +#define ADXL_RANGE_PM_8g 2 +#define ADXL_RANGE_PM_16g 3 + + u8 data_range; + + /* + * low_power_mode: + * A '0' = Normal operation and a '1' = Reduced + * power operation with somewhat higher noise. + */ + + u8 low_power_mode; + + /* + * power_mode: + * LINK: A '1' with both the activity and inactivity functions + * enabled will delay the start of the activity function until + * inactivity is detected. Once activity is detected, inactivity + * detection will begin and prevent the detection of activity. This + * bit serially links the activity and inactivity functions. When '0' + * the inactivity and activity functions are concurrent. Additional + * information can be found in the Application section under Link + * Mode. + * AUTO_SLEEP: A '1' sets the ADXL34x to switch to Sleep Mode + * when inactivity (acceleration has been below inactivity_threshold + * for at least inactivity_time) is detected and the LINK bit is set. + * A '0' disables automatic switching to Sleep Mode. See SLEEP + * for further description. + */ + +#define ADXL_LINK (1 << 5) +#define ADXL_AUTO_SLEEP (1 << 4) + + u8 power_mode; + + /* + * fifo_mode: + * BYPASS The FIFO is bypassed + * FIFO FIFO collects up to 32 values then stops collecting data + * STREAM FIFO holds the last 32 data values. Once full, the FIFO's + * oldest data is lost as it is replaced with newer data + * + * DEFAULT should be ADXL_FIFO_STREAM + */ + +#define ADXL_FIFO_BYPASS 0 +#define ADXL_FIFO_FIFO 1 +#define ADXL_FIFO_STREAM 2 + + u8 fifo_mode; + + /* + * watermark: + * The Watermark feature can be used to reduce the interrupt load + * of the system. The FIFO fills up to the value stored in watermark + * [1..32] and then generates an interrupt. + * A '0' disables the watermark feature. + */ + + u8 watermark; + + u32 ev_type; /* EV_ABS or EV_REL */ + + u32 ev_code_x; /* ABS_X,Y,Z or REL_X,Y,Z */ + u32 ev_code_y; /* ABS_X,Y,Z or REL_X,Y,Z */ + u32 ev_code_z; /* ABS_X,Y,Z or REL_X,Y,Z */ + + /* + * A valid BTN or KEY Code; use tap_axis_control to disable + * event reporting + */ + + u32 ev_code_tap[3]; /* EV_KEY {X-Axis, Y-Axis, Z-Axis} */ + + /* + * A valid BTN or KEY Code for Free-Fall or Activity enables + * input event reporting. A '0' disables the Free-Fall or + * Activity reporting. + */ + + u32 ev_code_ff; /* EV_KEY */ + u32 ev_code_act_inactivity; /* EV_KEY */ + + /* + * Use ADXL34x INT2 instead of INT1 + */ + u8 use_int2; + + /* + * ADXL346 only ORIENTATION SENSING feature + * The orientation function of the ADXL346 reports both 2-D and + * 3-D orientation concurrently. + */ + +#define ADXL_EN_ORIENTATION_2D 1 +#define ADXL_EN_ORIENTATION_3D 2 +#define ADXL_EN_ORIENTATION_2D_3D 3 + + u8 orientation_enable; + + /* + * The width of the deadzone region between two or more + * orientation positions is determined by setting the Deadzone + * value. The deadzone region size can be specified with a + * resolution of 3.6deg. The deadzone angle represents the total + * angle where the orientation is considered invalid. + */ + +#define ADXL_DEADZONE_ANGLE_0p0 0 /* !!!0.0 [deg] */ +#define ADXL_DEADZONE_ANGLE_3p6 1 /* 3.6 [deg] */ +#define ADXL_DEADZONE_ANGLE_7p2 2 /* 7.2 [deg] */ +#define ADXL_DEADZONE_ANGLE_10p8 3 /* 10.8 [deg] */ +#define ADXL_DEADZONE_ANGLE_14p4 4 /* 14.4 [deg] */ +#define ADXL_DEADZONE_ANGLE_18p0 5 /* 18.0 [deg] */ +#define ADXL_DEADZONE_ANGLE_21p6 6 /* 21.6 [deg] */ +#define ADXL_DEADZONE_ANGLE_25p2 7 /* 25.2 [deg] */ + + u8 deadzone_angle; + + /* + * To eliminate most human motion such as walking or shaking, + * a Divisor value should be selected to effectively limit the + * orientation bandwidth. Set the depth of the filter used to + * low-pass filter the measured acceleration for stable + * orientation sensing + */ + +#define ADXL_LP_FILTER_DIVISOR_2 0 +#define ADXL_LP_FILTER_DIVISOR_4 1 +#define ADXL_LP_FILTER_DIVISOR_8 2 +#define ADXL_LP_FILTER_DIVISOR_16 3 +#define ADXL_LP_FILTER_DIVISOR_32 4 +#define ADXL_LP_FILTER_DIVISOR_64 5 +#define ADXL_LP_FILTER_DIVISOR_128 6 +#define ADXL_LP_FILTER_DIVISOR_256 7 + + u8 divisor_length; + + u32 ev_codes_orient_2d[4]; /* EV_KEY {+X, -X, +Y, -Y} */ + u32 ev_codes_orient_3d[6]; /* EV_KEY {+Z, +Y, +X, -X, -Y, -Z} */ +}; +#endif diff --git a/include/linux/input/cy8ctmg110_pdata.h b/include/linux/input/cy8ctmg110_pdata.h new file mode 100644 index 0000000..09522cb --- /dev/null +++ b/include/linux/input/cy8ctmg110_pdata.h @@ -0,0 +1,10 @@ +#ifndef _LINUX_CY8CTMG110_PDATA_H +#define _LINUX_CY8CTMG110_PDATA_H + +struct cy8ctmg110_pdata +{ + int reset_pin; /* Reset pin is wired to this GPIO (optional) */ + int irq_pin; /* IRQ pin is wired to this GPIO */ +}; + +#endif diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index c964cd7..80352ad 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -41,6 +41,9 @@ struct matrix_keymap_data { * @col_scan_delay_us: delay, measured in microseconds, that is * needed before we can keypad after activating column gpio * @debounce_ms: debounce interval in milliseconds + * @clustered_irq: may be specified if interrupts of all row/column GPIOs + * are bundled to one single irq + * @clustered_irq_flags: flags that are needed for the clustered irq * @active_low: gpio polarity * @wakeup: controls whether the device should be set up as wakeup * source @@ -63,6 +66,9 @@ struct matrix_keypad_platform_data { /* key debounce interval in milli-second */ unsigned int debounce_ms; + unsigned int clustered_irq; + unsigned int clustered_irq_flags; + bool active_low; bool wakeup; bool no_autorepeat; diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index b4ae570..92bd083 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -48,11 +48,12 @@ struct ads7846_platform_data { * state if get_pendown_state == NULL */ int (*get_pendown_state)(void); - int (*filter_init) (struct ads7846_platform_data *pdata, + int (*filter_init) (const struct ads7846_platform_data *pdata, void **filter_data); int (*filter) (void *filter_data, int data_idx, int *val); void (*filter_cleanup)(void *filter_data); void (*wait_for_sync)(void); bool wakeup; + unsigned long irq_flags; }; |