Merge branch 'next' into for-linus
This commit is contained in:
Коммит
d01d0756f7
|
@ -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.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
Protocol Usage
|
||||
--------------
|
||||
|
||||
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
|
||||
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.
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
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
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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);
|
||||
do {
|
||||
client->buffer[client->head++] = *event;
|
||||
client->head &= EVDEV_BUFFER_SIZE - 1;
|
||||
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);
|
||||
|
|
|
@ -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 (is_event_supported(code, dev->absbit, ABS_MAX))
|
||||
disposition = input_handle_abs_event(dev, code, &value);
|
||||
|
||||
if (test_bit(code, input_abs_bypass)) {
|
||||
disposition = INPUT_PASS_TO_HANDLERS;
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -527,13 +546,31 @@ 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;
|
||||
|
@ -1517,6 +1556,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
|
||||
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,6 +288,7 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
|||
{
|
||||
struct input_dev *dev = xpad->dev;
|
||||
|
||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||
/* left stick */
|
||||
input_report_abs(dev, ABS_X,
|
||||
(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||
|
@ -291,6 +300,7 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
|||
(__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,6 +390,7 @@ 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);
|
||||
|
||||
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||
/* left stick */
|
||||
input_report_abs(dev, ABS_X,
|
||||
(__s16) le16_to_cpup((__le16 *)(data + 6)));
|
||||
|
@ -390,6 +402,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
|
|||
(__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);
|
||||
|
||||
/* set up standard buttons and axes */
|
||||
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 */
|
||||
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
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;
|
||||
|
||||
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,25 +225,58 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
|
||||
{
|
||||
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||
unsigned int gpio;
|
||||
int i;
|
||||
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
|
@ -243,18 +285,9 @@ static int matrix_keypad_resume(struct device *dev)
|
|||
{
|
||||
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;
|
||||
int i;
|
||||
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
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];
|
||||
|
||||
disable_irq_wake(gpio_to_irq(gpio));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
matrix_keypad_disable_wakeup(keypad);
|
||||
|
||||
matrix_keypad_start(keypad->input_dev);
|
||||
|
||||
|
@ -296,19 +329,33 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,
|
|||
gpio_direction_input(pdata->row_gpios[i]);
|
||||
}
|
||||
|
||||
if (pdata->clustered_irq > 0) {
|
||||
err = request_irq(pdata->clustered_irq,
|
||||
matrix_keypad_interrupt,
|
||||
pdata->clustered_irq_flags,
|
||||
"matrix-keypad", keypad);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"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,
|
||||
IRQF_TRIGGER_RISING |
|
||||
IRQF_TRIGGER_FALLING,
|
||||
"matrix-keypad", keypad);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to acquire interrupt for GPIO line %i\n",
|
||||
"Unable to acquire interrupt "
|
||||
"for GPIO line %i\n",
|
||||
pdata->row_gpios[i]);
|
||||
goto err_free_irqs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* initialized as disabled - enabled by input->open */
|
||||
disable_row_irqs(keypad);
|
||||
|
@ -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++) {
|
||||
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);
|
||||
gpio_free(pdata->row_gpios[i]);
|
||||
}
|
||||
|
||||
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]);
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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");
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -502,7 +502,9 @@ static void synaptics_process_packet(struct psmouse *psmouse)
|
|||
}
|
||||
input_report_abs(dev, ABS_PRESSURE, hw.z);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,6 +1017,7 @@ static void i8042_controller_reset(void)
|
|||
* Reset the controller if requested.
|
||||
*/
|
||||
|
||||
if (i8042_reset)
|
||||
i8042_controller_selftest();
|
||||
|
||||
/*
|
||||
|
@ -1093,24 +1091,12 @@ 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;
|
||||
|
||||
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;
|
||||
|
||||
if (i8042_reset) {
|
||||
error = i8042_controller_selftest();
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i8042_controller_init();
|
||||
if (error)
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -43,6 +43,7 @@ enum {
|
|||
WACOM_G4,
|
||||
PTU,
|
||||
PL,
|
||||
DTU,
|
||||
INTUOS,
|
||||
INTUOS3S,
|
||||
INTUOS3,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -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");
|
|
@ -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 void ad7879_report(struct ad7879 *ts)
|
||||
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 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);
|
||||
|
||||
if (!ts->disabled) {
|
||||
|
||||
ts->disabled = 1;
|
||||
disable_irq(ts->bus->irq);
|
||||
|
||||
cancel_work_sync(&ts->work);
|
||||
disable_irq(ts->irq);
|
||||
|
||||
if (del_timer_sync(&ts->timer))
|
||||
ad7879_ts_event_release(ts);
|
||||
|
||||
ad7879_write(ts->bus, AD7879_REG_CTRL2,
|
||||
AD7879_PM(AD7879_PM_SHUTDOWN));
|
||||
}
|
||||
|
||||
mutex_unlock(&ts->mutex);
|
||||
ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN));
|
||||
}
|
||||
|
||||
static void ad7879_enable(struct ad7879 *ts)
|
||||
{
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
if (ts->disabled) {
|
||||
ad7879_setup(ts);
|
||||
ts->disabled = 0;
|
||||
enable_irq(ts->bus->irq);
|
||||
static int ad7879_open(struct input_dev *input)
|
||||
{
|
||||
struct ad7879 *ts = input_get_drvdata(input);
|
||||
|
||||
/* protected by input->mutex */
|
||||
if (!ts->disabled && !ts->suspended)
|
||||
__ad7879_enable(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ad7879_close(struct input_dev* input)
|
||||
{
|
||||
struct ad7879 *ts = input_get_drvdata(input);
|
||||
|
||||
/* protected by input->mutex */
|
||||
if (!ts->disabled && !ts->suspended)
|
||||
__ad7879_disable(ts);
|
||||
}
|
||||
|
||||
void ad7879_suspend(struct ad7879 *ts)
|
||||
{
|
||||
mutex_lock(&ts->input->mutex);
|
||||
|
||||
if (!ts->suspended && !ts->disabled && ts->input->users)
|
||||
__ad7879_disable(ts);
|
||||
|
||||
ts->suspended = true;
|
||||
|
||||
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;
|
||||
kfree(ts);
|
||||
err_out:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL(ad7879_probe);
|
||||
|
||||
static int __devexit ad7879_destroy(bus_device *bus, struct ad7879 *ts)
|
||||
void ad7879_remove(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);
|
||||
ad7879_gpio_remove(ts);
|
||||
sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
|
||||
free_irq(ts->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;
|
||||
}
|
||||
|
||||
static struct spi_driver ad7879_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ad7879_probe,
|
||||
.remove = __devexit_p(ad7879_remove),
|
||||
.suspend = ad7879_suspend,
|
||||
.resume = ad7879_resume,
|
||||
};
|
||||
|
||||
static int __init ad7879_init(void)
|
||||
{
|
||||
return spi_register_driver(&ad7879_driver);
|
||||
}
|
||||
module_init(ad7879_init);
|
||||
|
||||
static void __exit ad7879_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ad7879_driver);
|
||||
}
|
||||
module_exit(ad7879_exit);
|
||||
|
||||
#elif defined(CONFIG_TOUCHSCREEN_AD7879_I2C) || defined(CONFIG_TOUCHSCREEN_AD7879_I2C_MODULE)
|
||||
|
||||
/* All registers are word-sized.
|
||||
* AD7879 uses a high-byte first convention.
|
||||
*/
|
||||
static int ad7879_read(struct i2c_client *client, u8 reg)
|
||||
{
|
||||
return swab16(i2c_smbus_read_word_data(client, reg));
|
||||
}
|
||||
|
||||
static int ad7879_write(struct i2c_client *client, u8 reg, u16 val)
|
||||
{
|
||||
return i2c_smbus_write_word_data(client, reg, swab16(val));
|
||||
}
|
||||
|
||||
static void ad7879_collect(struct ad7879 *ts)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < AD7879_NR_SENSE; i++)
|
||||
ts->conversion_data[i] = ad7879_read(ts->bus,
|
||||
AD7879_REG_XPLUS + i);
|
||||
}
|
||||
|
||||
static int __devinit ad7879_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct ad7879 *ts;
|
||||
int error;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_WORD_DATA)) {
|
||||
dev_err(&client->dev, "SMBUS Word Data not Supported\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ts = kzalloc(sizeof(struct ad7879), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
i2c_set_clientdata(client, ts);
|
||||
ts->bus = client;
|
||||
|
||||
error = ad7879_construct(client, ts);
|
||||
if (error)
|
||||
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);
|
||||
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");
|
||||
|
|
|
@ -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
|
|
@ -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.
|
||||
*/
|
||||
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.
|
||||
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) {
|
||||
|
@ -881,9 +945,10 @@ 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;
|
||||
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;
|
||||
|
||||
|
@ -1008,6 +1073,15 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
|
||||
spi_message_init(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;
|
||||
|
@ -1018,6 +1092,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
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,6 +1118,16 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
m++;
|
||||
spi_message_init(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);
|
||||
|
@ -1054,6 +1139,7 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
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,6 +1230,14 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
m++;
|
||||
spi_message_init(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;
|
||||
|
@ -1153,6 +1247,8 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||
x++;
|
||||
x->rx_buf = &packet->dummy;
|
||||
x->len = 2;
|
||||
}
|
||||
|
||||
CS_CHANGE(*x);
|
||||
spi_message_add_tail(x, m);
|
||||
|
||||
|
@ -1174,18 +1270,23 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
err = ads784x_hwmon_register(spi, ts);
|
||||
if (err)
|
||||
|
@ -1196,6 +1297,9 @@ 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.
|
||||
*/
|
||||
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);
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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;
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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)
|
||||
|
||||
|
@ -1047,6 +1056,14 @@ struct ff_effect {
|
|||
#include <linux/timer.h>
|
||||
#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
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче