Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (57 commits) Input: adp5588-keypad - fix NULL dereference in adp5588_gpio_add() Input: cy8ctmg110 - capacitive touchscreen support Input: keyboard - also match braille-only keyboards Input: adp5588-keys - export unused GPIO pins Input: xpad - add product ID for Hori Fighting Stick EX2 Input: adxl34x - fix leak and use after free Input: samsung-keypad - Add samsung keypad driver Input: i8042 - reset keyboard controller wehen resuming from S2R Input: synaptics - set min/max for finger width Input: synaptics - only report width on hardware that supports it Input: evdev - signal that device is writable in evdev_poll() Input: mousedev - signal that device is writable in mousedev_poll() Input: change input handlers to use bool when possible Input: document the MT event slot protocol Input: introduce MT event slots Input: usbtouchscreen - implement reset_resume Input: usbtouchscreen - implement runtime power management Input: usbtouchscreen - implement basic suspend/resume Input: Add ATMEL QT602240 touchscreen driver Input: fix signedness warning in input_set_keycode() ...
This commit is contained in:
commit
fe445c6e2c
@ -6,31 +6,149 @@ Multi-touch (MT) Protocol
|
|||||||
Introduction
|
Introduction
|
||||||
------------
|
------------
|
||||||
|
|
||||||
In order to utilize the full power of the new multi-touch devices, a way to
|
In order to utilize the full power of the new multi-touch and multi-user
|
||||||
report detailed finger data to user space is needed. This document
|
devices, a way to report detailed data from multiple contacts, i.e.,
|
||||||
describes the multi-touch (MT) protocol which allows kernel drivers to
|
objects in direct contact with the device surface, is needed. This
|
||||||
report details for an arbitrary number of fingers.
|
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
|
Contact details are sent sequentially as separate packets of ABS_MT
|
||||||
events. Only the ABS_MT events are recognized as part of a finger
|
events. Only the ABS_MT events are recognized as part of a contact
|
||||||
packet. The end of a packet is marked by calling the input_mt_sync()
|
packet. Since these events are ignored by current single-touch (ST)
|
||||||
function, which generates a SYN_MT_REPORT event. This instructs the
|
applications, the MT protocol can be implemented on top of the ST protocol
|
||||||
receiver to accept the data for the current finger and prepare to receive
|
in an existing driver.
|
||||||
another. The end of a multi-touch transfer is marked by calling the usual
|
|
||||||
|
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
|
input_sync() function. This instructs the receiver to act upon events
|
||||||
accumulated since last EV_SYN/SYN_REPORT and prepare to receive a new
|
accumulated since last EV_SYN/SYN_REPORT and prepare to receive a new set
|
||||||
set of events/packets.
|
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
|
A set of ABS_MT events with the desired properties is defined. The events
|
||||||
are divided into categories, to allow for partial implementation. The
|
are divided into categories, to allow for partial implementation. The
|
||||||
minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which
|
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
|
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
|
The TOUCH and WIDTH parameters have a geometrical interpretation; imagine
|
||||||
looking through a window at someone gently holding a finger against the
|
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
|
ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger harder
|
||||||
against the glass. The inner region will increase, and in general, the
|
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
|
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
|
ABS_MT_PRESSURE may be used to provide the pressure on the contact area
|
||||||
instead.
|
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
|
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
|
major and minor axis of an ellipse. Finally, the orientation of the oval
|
||||||
shape can be describe with the ORIENTATION parameter.
|
shape can be describe with the ORIENTATION parameter.
|
||||||
|
|
||||||
The ABS_MT_TOOL_TYPE may be used to specify whether the touching tool is a
|
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
|
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
|
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
|
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
|
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
|
ABS_MT_TOUCH_MAJOR
|
||||||
|
|
||||||
The length of the major axis of the contact. The length should be given in
|
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
|
ABS_MT_BLOB_ID
|
||||||
|
|
||||||
The BLOB_ID groups several packets together into one arbitrarily shaped
|
The BLOB_ID groups several packets together into one arbitrarily shaped
|
||||||
contact. This is a low-level anonymous grouping, and should not be confused
|
contact. This is a low-level anonymous grouping for type A devices, and
|
||||||
with the high-level trackingID [5]. Most kernel drivers will not have blob
|
should not be confused with the high-level trackingID [5]. Most type A
|
||||||
capability, and can safely omit the event.
|
devices do not have blob capability, so drivers can safely omit this event.
|
||||||
|
|
||||||
ABS_MT_TRACKING_ID
|
ABS_MT_TRACKING_ID
|
||||||
|
|
||||||
The TRACKING_ID identifies an initiated contact throughout its life cycle
|
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
|
[5]. This event is mandatory for type B devices. The value range of the
|
||||||
should normally be omitted.
|
TRACKING_ID should be large enough to ensure unique identification of a
|
||||||
|
contact maintained over an extended period of time.
|
||||||
|
|
||||||
|
|
||||||
Event Computation
|
Event Computation
|
||||||
@ -192,20 +281,11 @@ finger along the X axis (1).
|
|||||||
Finger Tracking
|
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
|
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
|
initiated contact on the surface, is a Euclidian Bipartite Matching
|
||||||
multi-touch X driver [3]. In that driver, the trackingID stays the same and
|
problem. At each event synchronization, the set of actual contacts is
|
||||||
unique until the contact vanishes (when the finger leaves the surface). The
|
matched to the set of contacts from the previous synchronization. A full
|
||||||
problem of assigning a set of anonymous fingers to a set of identified
|
implementation can be found in [3].
|
||||||
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.
|
|
||||||
|
|
||||||
|
|
||||||
Gestures
|
Gestures
|
||||||
|
43
arch/arm/plat-samsung/include/plat/keypad.h
Normal file
43
arch/arm/plat-samsung/include/plat/keypad.h
Normal file
@ -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))
|
if (test_bit(EV_SND, dev->evbit))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (test_bit(EV_KEY, dev->evbit))
|
if (test_bit(EV_KEY, dev->evbit)) {
|
||||||
for (i = KEY_RESERVED; i < BTN_MISC; i++)
|
for (i = KEY_RESERVED; i < BTN_MISC; i++)
|
||||||
if (test_bit(i, dev->keybit))
|
if (test_bit(i, dev->keybit))
|
||||||
return true;
|
return true;
|
||||||
|
for (i = KEY_BRL_DOT1; i <= KEY_BRL_DOT10; i++)
|
||||||
|
if (test_bit(i, dev->keybit))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1586,6 +1586,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_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_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_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, 0x0001) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0002) },
|
||||||
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) },
|
{ HID_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH, 0x0003) },
|
||||||
|
@ -198,6 +198,7 @@
|
|||||||
|
|
||||||
#define USB_VENDOR_ID_ETT 0x0664
|
#define USB_VENDOR_ID_ETT 0x0664
|
||||||
#define USB_DEVICE_ID_TC5UH 0x0309
|
#define USB_DEVICE_ID_TC5UH 0x0309
|
||||||
|
#define USB_DEVICE_ID_TC4UM 0x0306
|
||||||
|
|
||||||
#define USB_VENDOR_ID_EZKEY 0x0518
|
#define USB_VENDOR_ID_EZKEY 0x0518
|
||||||
#define USB_DEVICE_ID_BTC_8193 0x0002
|
#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);
|
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);
|
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 &&
|
if (usage->type == EV_ABS &&
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
|
|
||||||
#define EVDEV_MINOR_BASE 64
|
#define EVDEV_MINOR_BASE 64
|
||||||
#define EVDEV_MINORS 32
|
#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/poll.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
@ -23,7 +24,6 @@
|
|||||||
#include "input-compat.h"
|
#include "input-compat.h"
|
||||||
|
|
||||||
struct evdev {
|
struct evdev {
|
||||||
int exist;
|
|
||||||
int open;
|
int open;
|
||||||
int minor;
|
int minor;
|
||||||
struct input_handle handle;
|
struct input_handle handle;
|
||||||
@ -33,16 +33,18 @@ struct evdev {
|
|||||||
spinlock_t client_lock; /* protects client_list */
|
spinlock_t client_lock; /* protects client_list */
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
bool exist;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct evdev_client {
|
struct evdev_client {
|
||||||
struct input_event buffer[EVDEV_BUFFER_SIZE];
|
|
||||||
int head;
|
int head;
|
||||||
int tail;
|
int tail;
|
||||||
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
spinlock_t buffer_lock; /* protects access to buffer, head and tail */
|
||||||
struct fasync_struct *fasync;
|
struct fasync_struct *fasync;
|
||||||
struct evdev *evdev;
|
struct evdev *evdev;
|
||||||
struct list_head node;
|
struct list_head node;
|
||||||
|
int bufsize;
|
||||||
|
struct input_event buffer[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct evdev *evdev_table[EVDEV_MINORS];
|
static struct evdev *evdev_table[EVDEV_MINORS];
|
||||||
@ -52,11 +54,15 @@ static void evdev_pass_event(struct evdev_client *client,
|
|||||||
struct input_event *event)
|
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);
|
spin_lock(&client->buffer_lock);
|
||||||
client->buffer[client->head++] = *event;
|
do {
|
||||||
client->head &= EVDEV_BUFFER_SIZE - 1;
|
client->buffer[client->head++] = *event;
|
||||||
|
client->head &= client->bufsize - 1;
|
||||||
|
} while (client->head == client->tail);
|
||||||
spin_unlock(&client->buffer_lock);
|
spin_unlock(&client->buffer_lock);
|
||||||
|
|
||||||
if (event->type == EV_SYN)
|
if (event->type == EV_SYN)
|
||||||
@ -242,11 +248,21 @@ static int evdev_release(struct inode *inode, struct file *file)
|
|||||||
return 0;
|
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)
|
static int evdev_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
struct evdev *evdev;
|
struct evdev *evdev;
|
||||||
struct evdev_client *client;
|
struct evdev_client *client;
|
||||||
int i = iminor(inode) - EVDEV_MINOR_BASE;
|
int i = iminor(inode) - EVDEV_MINOR_BASE;
|
||||||
|
unsigned int bufsize;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (i >= EVDEV_MINORS)
|
if (i >= EVDEV_MINORS)
|
||||||
@ -263,12 +279,17 @@ static int evdev_open(struct inode *inode, struct file *file)
|
|||||||
if (!evdev)
|
if (!evdev)
|
||||||
return -ENODEV;
|
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) {
|
if (!client) {
|
||||||
error = -ENOMEM;
|
error = -ENOMEM;
|
||||||
goto err_put_evdev;
|
goto err_put_evdev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
client->bufsize = bufsize;
|
||||||
spin_lock_init(&client->buffer_lock);
|
spin_lock_init(&client->buffer_lock);
|
||||||
client->evdev = evdev;
|
client->evdev = evdev;
|
||||||
evdev_attach_client(evdev, client);
|
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;
|
have_event = client->head != client->tail;
|
||||||
if (have_event) {
|
if (have_event) {
|
||||||
*event = client->buffer[client->tail++];
|
*event = client->buffer[client->tail++];
|
||||||
client->tail &= EVDEV_BUFFER_SIZE - 1;
|
client->tail &= client->bufsize - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_unlock_irq(&client->buffer_lock);
|
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_client *client = file->private_data;
|
||||||
struct evdev *evdev = client->evdev;
|
struct evdev *evdev = client->evdev;
|
||||||
|
unsigned int mask;
|
||||||
|
|
||||||
poll_wait(file, &evdev->wait, wait);
|
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
|
#ifdef CONFIG_COMPAT
|
||||||
@ -665,6 +691,10 @@ static long evdev_do_ioctl(struct file *file, unsigned int cmd,
|
|||||||
sizeof(struct input_absinfo))))
|
sizeof(struct input_absinfo))))
|
||||||
return -EFAULT;
|
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
|
* Take event lock to ensure that we are not
|
||||||
* changing device parameters in the middle
|
* 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)
|
static void evdev_mark_dead(struct evdev *evdev)
|
||||||
{
|
{
|
||||||
mutex_lock(&evdev->mutex);
|
mutex_lock(&evdev->mutex);
|
||||||
evdev->exist = 0;
|
evdev->exist = false;
|
||||||
mutex_unlock(&evdev->mutex);
|
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);
|
init_waitqueue_head(&evdev->wait);
|
||||||
|
|
||||||
dev_set_name(&evdev->dev, "event%d", minor);
|
dev_set_name(&evdev->dev, "event%d", minor);
|
||||||
evdev->exist = 1;
|
evdev->exist = true;
|
||||||
evdev->minor = minor;
|
evdev->minor = minor;
|
||||||
|
|
||||||
evdev->handle.dev = input_get_device(dev);
|
evdev->handle.dev = input_get_device(dev);
|
||||||
|
@ -33,25 +33,6 @@ MODULE_LICENSE("GPL");
|
|||||||
|
|
||||||
#define INPUT_DEVICES 256
|
#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_dev_list);
|
||||||
static LIST_HEAD(input_handler_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_DEVICE 2
|
||||||
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
|
#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,
|
static void input_handle_event(struct input_dev *dev,
|
||||||
unsigned int type, unsigned int code, int value)
|
unsigned int type, unsigned int code, int value)
|
||||||
{
|
{
|
||||||
@ -196,12 +227,12 @@ static void input_handle_event(struct input_dev *dev,
|
|||||||
|
|
||||||
case SYN_REPORT:
|
case SYN_REPORT:
|
||||||
if (!dev->sync) {
|
if (!dev->sync) {
|
||||||
dev->sync = 1;
|
dev->sync = true;
|
||||||
disposition = INPUT_PASS_TO_HANDLERS;
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SYN_MT_REPORT:
|
case SYN_MT_REPORT:
|
||||||
dev->sync = 0;
|
dev->sync = false;
|
||||||
disposition = INPUT_PASS_TO_HANDLERS;
|
disposition = INPUT_PASS_TO_HANDLERS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -233,21 +264,9 @@ static void input_handle_event(struct input_dev *dev,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case EV_ABS:
|
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;
|
break;
|
||||||
|
|
||||||
case EV_REL:
|
case EV_REL:
|
||||||
@ -298,7 +317,7 @@ static void input_handle_event(struct input_dev *dev,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
|
||||||
dev->sync = 0;
|
dev->sync = false;
|
||||||
|
|
||||||
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
|
||||||
dev->event(dev, type, code, value);
|
dev->event(dev, type, code, value);
|
||||||
@ -527,13 +546,31 @@ void input_close_device(struct input_handle *handle)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_close_device);
|
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
|
* Prepare device for unregistering
|
||||||
*/
|
*/
|
||||||
static void input_disconnect_device(struct input_dev *dev)
|
static void input_disconnect_device(struct input_dev *dev)
|
||||||
{
|
{
|
||||||
struct input_handle *handle;
|
struct input_handle *handle;
|
||||||
int code;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mark device as going away. Note that we take dev->mutex here
|
* 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
|
* generate events even after we done here but they will not
|
||||||
* reach any handlers.
|
* reach any handlers.
|
||||||
*/
|
*/
|
||||||
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
|
input_dev_release_keys(dev);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
list_for_each_entry(handle, &dev->h_list, d_node)
|
list_for_each_entry(handle, &dev->h_list, d_node)
|
||||||
handle->open = 0;
|
handle->open = 0;
|
||||||
@ -684,7 +713,7 @@ int input_set_keycode(struct input_dev *dev,
|
|||||||
unsigned int scancode, unsigned int keycode)
|
unsigned int scancode, unsigned int keycode)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int old_keycode;
|
unsigned int old_keycode;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
if (keycode > KEY_MAX)
|
if (keycode > KEY_MAX)
|
||||||
@ -1278,6 +1307,7 @@ static void input_dev_release(struct device *device)
|
|||||||
struct input_dev *dev = to_input_dev(device);
|
struct input_dev *dev = to_input_dev(device);
|
||||||
|
|
||||||
input_ff_destroy(dev);
|
input_ff_destroy(dev);
|
||||||
|
input_mt_destroy_slots(dev);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
|
|
||||||
module_put(THIS_MODULE);
|
module_put(THIS_MODULE);
|
||||||
@ -1433,6 +1463,15 @@ static int input_dev_resume(struct device *dev)
|
|||||||
|
|
||||||
mutex_lock(&input_dev->mutex);
|
mutex_lock(&input_dev->mutex);
|
||||||
input_dev_reset(input_dev, true);
|
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);
|
mutex_unlock(&input_dev->mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -1517,6 +1556,45 @@ void input_free_device(struct input_dev *dev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(input_free_device);
|
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
|
* input_set_capability - mark device as capable of a certain event
|
||||||
* @dev: device that is capable of emitting or accepting 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,
|
.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)
|
static int __init input_init(void)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
input_init_abs_bypass();
|
|
||||||
|
|
||||||
err = class_register(&input_class);
|
err = class_register(&input_class);
|
||||||
if (err) {
|
if (err) {
|
||||||
printk(KERN_ERR "input: unable to register input_dev class\n");
|
printk(KERN_ERR "input: unable to register input_dev class\n");
|
||||||
|
@ -37,7 +37,6 @@ MODULE_LICENSE("GPL");
|
|||||||
#define JOYDEV_BUFFER_SIZE 64
|
#define JOYDEV_BUFFER_SIZE 64
|
||||||
|
|
||||||
struct joydev {
|
struct joydev {
|
||||||
int exist;
|
|
||||||
int open;
|
int open;
|
||||||
int minor;
|
int minor;
|
||||||
struct input_handle handle;
|
struct input_handle handle;
|
||||||
@ -46,6 +45,7 @@ struct joydev {
|
|||||||
spinlock_t client_lock; /* protects client_list */
|
spinlock_t client_lock; /* protects client_list */
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
bool exist;
|
||||||
|
|
||||||
struct js_corr corr[ABS_CNT];
|
struct js_corr corr[ABS_CNT];
|
||||||
struct JS_DATA_SAVE_TYPE glue;
|
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)
|
static void joydev_mark_dead(struct joydev *joydev)
|
||||||
{
|
{
|
||||||
mutex_lock(&joydev->mutex);
|
mutex_lock(&joydev->mutex);
|
||||||
joydev->exist = 0;
|
joydev->exist = false;
|
||||||
mutex_unlock(&joydev->mutex);
|
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);
|
init_waitqueue_head(&joydev->wait);
|
||||||
|
|
||||||
dev_set_name(&joydev->dev, "js%d", minor);
|
dev_set_name(&joydev->dev, "js%d", minor);
|
||||||
joydev->exist = 1;
|
joydev->exist = true;
|
||||||
joydev->minor = minor;
|
joydev->minor = minor;
|
||||||
|
|
||||||
joydev->exist = 1;
|
|
||||||
joydev->handle.dev = input_get_device(dev);
|
joydev->handle.dev = input_get_device(dev);
|
||||||
joydev->handle.name = dev_name(&joydev->dev);
|
joydev->handle.name = dev_name(&joydev->dev);
|
||||||
joydev->handle.handler = handler;
|
joydev->handle.handler = handler;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
* 2005 Dominic Cerquetti <binary1230@yahoo.com>
|
* 2005 Dominic Cerquetti <binary1230@yahoo.com>
|
||||||
* 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
|
* 2006 Adam Buchbinder <adam.buchbinder@gmail.com>
|
||||||
* 2007 Jan Kratochvil <honza@jikos.cz>
|
* 2007 Jan Kratochvil <honza@jikos.cz>
|
||||||
|
* 2010 Christoph Fritz <chf.fritz@googlemail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License as
|
* 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 */
|
but we map them to axes when possible to simplify things */
|
||||||
#define MAP_DPAD_TO_BUTTONS (1 << 0)
|
#define MAP_DPAD_TO_BUTTONS (1 << 0)
|
||||||
#define MAP_TRIGGERS_TO_BUTTONS (1 << 1)
|
#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_XBOX 0
|
||||||
#define XTYPE_XBOX360 1
|
#define XTYPE_XBOX360 1
|
||||||
@ -102,6 +106,10 @@ static int triggers_to_buttons;
|
|||||||
module_param(triggers_to_buttons, bool, S_IRUGO);
|
module_param(triggers_to_buttons, bool, S_IRUGO);
|
||||||
MODULE_PARM_DESC(triggers_to_buttons, "Map triggers to buttons rather than axes for unknown pads");
|
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 {
|
static const struct xpad_device {
|
||||||
u16 idVendor;
|
u16 idVendor;
|
||||||
u16 idProduct;
|
u16 idProduct;
|
||||||
@ -114,7 +122,7 @@ static const struct xpad_device {
|
|||||||
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },
|
{ 0x045e, 0x0285, "Microsoft X-Box pad (Japan)", 0, XTYPE_XBOX },
|
||||||
{ 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },
|
{ 0x045e, 0x0287, "Microsoft Xbox Controller S", 0, XTYPE_XBOX },
|
||||||
{ 0x045e, 0x0719, "Xbox 360 Wireless Receiver", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360W },
|
{ 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 },
|
{ 0x044f, 0x0f07, "Thrustmaster, Inc. Controller", 0, XTYPE_XBOX },
|
||||||
{ 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },
|
{ 0x046d, 0xc242, "Logitech Chillstream Controller", 0, XTYPE_XBOX360 },
|
||||||
{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX },
|
{ 0x046d, 0xca84, "Logitech Xbox Cordless Controller", 0, XTYPE_XBOX },
|
||||||
@ -151,6 +159,7 @@ static const struct xpad_device {
|
|||||||
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
|
{ 0x045e, 0x028e, "Microsoft X-Box 360 pad", 0, XTYPE_XBOX360 },
|
||||||
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x1bad, 0x0003, "Harmonix Rock Band Drumkit", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
{ 0x0f0d, 0x0016, "Hori Real Arcade Pro.EX", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
|
{ 0x0f0d, 0x000d, "Hori Fighting Stick EX2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 },
|
||||||
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
{ 0xffff, 0xffff, "Chinese-made Xbox Controller", 0, XTYPE_XBOX },
|
||||||
{ 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
|
{ 0x0000, 0x0000, "Generic X-Box pad", 0, XTYPE_UNKNOWN }
|
||||||
};
|
};
|
||||||
@ -158,7 +167,7 @@ static const struct xpad_device {
|
|||||||
/* buttons shared with xbox and xbox360 */
|
/* buttons shared with xbox and xbox360 */
|
||||||
static const signed short xpad_common_btn[] = {
|
static const signed short xpad_common_btn[] = {
|
||||||
BTN_A, BTN_B, BTN_X, BTN_Y, /* "analog" buttons */
|
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 */
|
-1 /* terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -168,10 +177,10 @@ static const signed short xpad_btn[] = {
|
|||||||
-1 /* terminating entry */
|
-1 /* terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* used when dpad is mapped to nuttons */
|
/* used when dpad is mapped to buttons */
|
||||||
static const signed short xpad_btn_pad[] = {
|
static const signed short xpad_btn_pad[] = {
|
||||||
BTN_LEFT, BTN_RIGHT, /* d-pad left, right */
|
BTN_TRIGGER_HAPPY1, BTN_TRIGGER_HAPPY2, /* d-pad left, right */
|
||||||
BTN_0, BTN_1, /* d-pad up, down (XXX names??) */
|
BTN_TRIGGER_HAPPY3, BTN_TRIGGER_HAPPY4, /* d-pad up, down */
|
||||||
-1 /* terminating entry */
|
-1 /* terminating entry */
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -279,17 +288,19 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
|||||||
{
|
{
|
||||||
struct input_dev *dev = xpad->dev;
|
struct input_dev *dev = xpad->dev;
|
||||||
|
|
||||||
/* left stick */
|
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||||
input_report_abs(dev, ABS_X,
|
/* left stick */
|
||||||
(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
input_report_abs(dev, ABS_X,
|
||||||
input_report_abs(dev, ABS_Y,
|
(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||||
~(__s16) le16_to_cpup((__le16 *)(data + 14)));
|
input_report_abs(dev, ABS_Y,
|
||||||
|
~(__s16) le16_to_cpup((__le16 *)(data + 14)));
|
||||||
|
|
||||||
/* right stick */
|
/* right stick */
|
||||||
input_report_abs(dev, ABS_RX,
|
input_report_abs(dev, ABS_RX,
|
||||||
(__s16) le16_to_cpup((__le16 *)(data + 16)));
|
(__s16) le16_to_cpup((__le16 *)(data + 16)));
|
||||||
input_report_abs(dev, ABS_RY,
|
input_report_abs(dev, ABS_RY,
|
||||||
~(__s16) le16_to_cpup((__le16 *)(data + 18)));
|
~(__s16) le16_to_cpup((__le16 *)(data + 18)));
|
||||||
|
}
|
||||||
|
|
||||||
/* triggers left/right */
|
/* triggers left/right */
|
||||||
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
||||||
@ -302,10 +313,11 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d
|
|||||||
|
|
||||||
/* digital pad */
|
/* digital pad */
|
||||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||||
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
/* dpad as buttons (left, right, up, down) */
|
||||||
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
|
||||||
input_report_key(dev, BTN_0, data[2] & 0x01); /* up */
|
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
|
||||||
input_report_key(dev, BTN_1, data[2] & 0x02); /* down */
|
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
|
||||||
|
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
|
||||||
} else {
|
} else {
|
||||||
input_report_abs(dev, ABS_HAT0X,
|
input_report_abs(dev, ABS_HAT0X,
|
||||||
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||||
@ -315,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 */
|
/* start/back buttons and stick press left/right */
|
||||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
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_THUMBL, data[2] & 0x40);
|
||||||
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
input_report_key(dev, BTN_THUMBR, data[2] & 0x80);
|
||||||
|
|
||||||
@ -349,11 +361,11 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
|
|||||||
|
|
||||||
/* digital pad */
|
/* digital pad */
|
||||||
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
if (xpad->mapping & MAP_DPAD_TO_BUTTONS) {
|
||||||
/* dpad as buttons (right, left, down, up) */
|
/* dpad as buttons (left, right, up, down) */
|
||||||
input_report_key(dev, BTN_LEFT, data[2] & 0x04);
|
input_report_key(dev, BTN_TRIGGER_HAPPY1, data[2] & 0x04);
|
||||||
input_report_key(dev, BTN_RIGHT, data[2] & 0x08);
|
input_report_key(dev, BTN_TRIGGER_HAPPY2, data[2] & 0x08);
|
||||||
input_report_key(dev, BTN_0, data[2] & 0x01); /* up */
|
input_report_key(dev, BTN_TRIGGER_HAPPY3, data[2] & 0x01);
|
||||||
input_report_key(dev, BTN_1, data[2] & 0x02); /* down */
|
input_report_key(dev, BTN_TRIGGER_HAPPY4, data[2] & 0x02);
|
||||||
} else {
|
} else {
|
||||||
input_report_abs(dev, ABS_HAT0X,
|
input_report_abs(dev, ABS_HAT0X,
|
||||||
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
!!(data[2] & 0x08) - !!(data[2] & 0x04));
|
||||||
@ -363,7 +375,7 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
|
|||||||
|
|
||||||
/* start/back buttons */
|
/* start/back buttons */
|
||||||
input_report_key(dev, BTN_START, data[2] & 0x10);
|
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 */
|
/* stick press left/right */
|
||||||
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
input_report_key(dev, BTN_THUMBL, data[2] & 0x40);
|
||||||
@ -378,17 +390,19 @@ static void xpad360_process_packet(struct usb_xpad *xpad,
|
|||||||
input_report_key(dev, BTN_TR, data[3] & 0x02);
|
input_report_key(dev, BTN_TR, data[3] & 0x02);
|
||||||
input_report_key(dev, BTN_MODE, data[3] & 0x04);
|
input_report_key(dev, BTN_MODE, data[3] & 0x04);
|
||||||
|
|
||||||
/* left stick */
|
if (!(xpad->mapping & MAP_STICKS_TO_NULL)) {
|
||||||
input_report_abs(dev, ABS_X,
|
/* left stick */
|
||||||
(__s16) le16_to_cpup((__le16 *)(data + 6)));
|
input_report_abs(dev, ABS_X,
|
||||||
input_report_abs(dev, ABS_Y,
|
(__s16) le16_to_cpup((__le16 *)(data + 6)));
|
||||||
~(__s16) le16_to_cpup((__le16 *)(data + 8)));
|
input_report_abs(dev, ABS_Y,
|
||||||
|
~(__s16) le16_to_cpup((__le16 *)(data + 8)));
|
||||||
|
|
||||||
/* right stick */
|
/* right stick */
|
||||||
input_report_abs(dev, ABS_RX,
|
input_report_abs(dev, ABS_RX,
|
||||||
(__s16) le16_to_cpup((__le16 *)(data + 10)));
|
(__s16) le16_to_cpup((__le16 *)(data + 10)));
|
||||||
input_report_abs(dev, ABS_RY,
|
input_report_abs(dev, ABS_RY,
|
||||||
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
~(__s16) le16_to_cpup((__le16 *)(data + 12)));
|
||||||
|
}
|
||||||
|
|
||||||
/* triggers left/right */
|
/* triggers left/right */
|
||||||
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
if (xpad->mapping & MAP_TRIGGERS_TO_BUTTONS) {
|
||||||
@ -814,6 +828,8 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
|||||||
xpad->mapping |= MAP_DPAD_TO_BUTTONS;
|
xpad->mapping |= MAP_DPAD_TO_BUTTONS;
|
||||||
if (triggers_to_buttons)
|
if (triggers_to_buttons)
|
||||||
xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS;
|
xpad->mapping |= MAP_TRIGGERS_TO_BUTTONS;
|
||||||
|
if (sticks_to_null)
|
||||||
|
xpad->mapping |= MAP_STICKS_TO_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
xpad->dev = input_dev;
|
xpad->dev = input_dev;
|
||||||
@ -830,16 +846,20 @@ static int xpad_probe(struct usb_interface *intf, const struct usb_device_id *id
|
|||||||
input_dev->open = xpad_open;
|
input_dev->open = xpad_open;
|
||||||
input_dev->close = xpad_close;
|
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++)
|
for (i = 0; xpad_common_btn[i] >= 0; i++)
|
||||||
__set_bit(xpad_common_btn[i], input_dev->keybit);
|
__set_bit(xpad_common_btn[i], input_dev->keybit);
|
||||||
|
|
||||||
for (i = 0; xpad_abs[i] >= 0; i++)
|
/* set up model-specific ones */
|
||||||
xpad_set_up_abs(input_dev, xpad_abs[i]);
|
|
||||||
|
|
||||||
/* Now set up model-specific ones */
|
|
||||||
if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) {
|
if (xpad->xtype == XTYPE_XBOX360 || xpad->xtype == XTYPE_XBOX360W) {
|
||||||
for (i = 0; xpad360_btn[i] >= 0; i++)
|
for (i = 0; xpad360_btn[i] >= 0; i++)
|
||||||
__set_bit(xpad360_btn[i], input_dev->keybit);
|
__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
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called max7359_keypad.
|
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
|
config KEYBOARD_IMX
|
||||||
tristate "IMX keypad support"
|
tristate "IMX keypad support"
|
||||||
depends on ARCH_MXC
|
depends on ARCH_MXC
|
||||||
@ -342,6 +354,15 @@ config KEYBOARD_PXA930_ROTARY
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called pxa930_rotary.
|
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
|
config KEYBOARD_STOWAWAY
|
||||||
tristate "Stowaway keyboard"
|
tristate "Stowaway keyboard"
|
||||||
select SERIO
|
select SERIO
|
||||||
|
@ -26,12 +26,14 @@ obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
|
|||||||
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
|
||||||
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_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_NEWTON) += newtonkbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
|
obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
|
||||||
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
|
||||||
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
obj-$(CONFIG_KEYBOARD_PXA930_ROTARY) += pxa930_rotary.o
|
||||||
obj-$(CONFIG_KEYBOARD_QT2160) += qt2160.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_SH_KEYSC) += sh_keysc.o
|
||||||
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
|
||||||
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
#include <linux/i2c/adp5588.h>
|
#include <linux/i2c/adp5588.h>
|
||||||
@ -54,6 +55,10 @@
|
|||||||
|
|
||||||
#define KEYP_MAX_EVENT 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,
|
* Early pre 4.0 Silicon required to delay readout by at least 25ms,
|
||||||
* since the Event Counter Register updated 25ms after the interrupt
|
* since the Event Counter Register updated 25ms after the interrupt
|
||||||
@ -67,6 +72,16 @@ struct adp5588_kpad {
|
|||||||
struct delayed_work work;
|
struct delayed_work work;
|
||||||
unsigned long delay;
|
unsigned long delay;
|
||||||
unsigned short keycode[ADP5588_KEYMAPSIZE];
|
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)
|
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);
|
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)
|
static void adp5588_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct adp5588_kpad *kpad = container_of(work,
|
struct adp5588_kpad *kpad = container_of(work,
|
||||||
struct adp5588_kpad, work.work);
|
struct adp5588_kpad, work.work);
|
||||||
struct i2c_client *client = kpad->client;
|
struct i2c_client *client = kpad->client;
|
||||||
int i, key, status, ev_cnt;
|
int status, ev_cnt;
|
||||||
|
|
||||||
status = adp5588_read(client, INT_STAT);
|
status = adp5588_read(client, INT_STAT);
|
||||||
|
|
||||||
@ -99,12 +324,7 @@ static void adp5588_work(struct work_struct *work)
|
|||||||
if (status & KE_INT) {
|
if (status & KE_INT) {
|
||||||
ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC;
|
ev_cnt = adp5588_read(client, KEY_LCK_EC_STAT) & KEC;
|
||||||
if (ev_cnt) {
|
if (ev_cnt) {
|
||||||
for (i = 0; i < ev_cnt; i++) {
|
adp5588_report_events(kpad, ev_cnt);
|
||||||
key = adp5588_read(client, Key_EVENTA + i);
|
|
||||||
input_report_key(kpad->input,
|
|
||||||
kpad->keycode[(key & KEY_EV_MASK) - 1],
|
|
||||||
key & KEY_EV_PRESSED);
|
|
||||||
}
|
|
||||||
input_sync(kpad->input);
|
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)
|
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;
|
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_GPIO1, KP_SEL(pdata->rows));
|
||||||
ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
|
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++)
|
for (i = 0; i < KEYP_MAX_EVENT; i++)
|
||||||
ret |= adp5588_read(client, Key_EVENTA);
|
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 |
|
ret |= adp5588_write(client, INT_STAT, CMP2_INT | CMP1_INT |
|
||||||
OVR_FLOW_INT | K_LCK_INT |
|
OVR_FLOW_INT | K_LCK_INT |
|
||||||
GPI_INT | KE_INT); /* Status is W1C */
|
GPI_INT | KE_INT); /* Status is W1C */
|
||||||
@ -158,11 +406,49 @@ static int __devinit adp5588_setup(struct i2c_client *client)
|
|||||||
return 0;
|
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,
|
static int __devinit adp5588_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct adp5588_kpad *kpad;
|
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;
|
struct input_dev *input;
|
||||||
unsigned int revid;
|
unsigned int revid;
|
||||||
int ret, i;
|
int ret, i;
|
||||||
@ -189,6 +475,37 @@ static int __devinit adp5588_probe(struct i2c_client *client,
|
|||||||
return -EINVAL;
|
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) {
|
if (!client->irq) {
|
||||||
dev_err(&client->dev, "no IRQ?\n");
|
dev_err(&client->dev, "no IRQ?\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -233,6 +550,9 @@ static int __devinit adp5588_probe(struct i2c_client *client,
|
|||||||
memcpy(kpad->keycode, pdata->keymap,
|
memcpy(kpad->keycode, pdata->keymap,
|
||||||
pdata->keymapsize * input->keycodesize);
|
pdata->keymapsize * input->keycodesize);
|
||||||
|
|
||||||
|
kpad->gpimap = pdata->gpimap;
|
||||||
|
kpad->gpimapsize = pdata->gpimapsize;
|
||||||
|
|
||||||
/* setup input device */
|
/* setup input device */
|
||||||
__set_bit(EV_KEY, input->evbit);
|
__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);
|
__set_bit(kpad->keycode[i] & KEY_MAX, input->keybit);
|
||||||
__clear_bit(KEY_RESERVED, 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);
|
error = input_register_device(input);
|
||||||
if (error) {
|
if (error) {
|
||||||
dev_err(&client->dev, "unable to register input device\n");
|
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)
|
if (error)
|
||||||
goto err_free_irq;
|
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);
|
device_init_wakeup(&client->dev, 1);
|
||||||
i2c_set_clientdata(client, kpad);
|
i2c_set_clientdata(client, kpad);
|
||||||
|
|
||||||
@ -287,6 +619,7 @@ static int __devexit adp5588_remove(struct i2c_client *client)
|
|||||||
free_irq(client->irq, kpad);
|
free_irq(client->irq, kpad);
|
||||||
cancel_delayed_work_sync(&kpad->work);
|
cancel_delayed_work_sync(&kpad->work);
|
||||||
input_unregister_device(kpad->input);
|
input_unregister_device(kpad->input);
|
||||||
|
adp5588_gpio_remove(kpad);
|
||||||
kfree(kpad);
|
kfree(kpad);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -31,6 +31,7 @@ struct gpio_button_data {
|
|||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
struct work_struct work;
|
struct work_struct work;
|
||||||
|
int timer_debounce; /* in msecs */
|
||||||
bool disabled;
|
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 and possible debouncing timer.
|
||||||
*/
|
*/
|
||||||
disable_irq(gpio_to_irq(bdata->button->gpio));
|
disable_irq(gpio_to_irq(bdata->button->gpio));
|
||||||
if (bdata->button->debounce_interval)
|
if (bdata->timer_debounce)
|
||||||
del_timer_sync(&bdata->timer);
|
del_timer_sync(&bdata->timer);
|
||||||
|
|
||||||
bdata->disabled = true;
|
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));
|
BUG_ON(irq != gpio_to_irq(button->gpio));
|
||||||
|
|
||||||
if (button->debounce_interval)
|
if (bdata->timer_debounce)
|
||||||
mod_timer(&bdata->timer,
|
mod_timer(&bdata->timer,
|
||||||
jiffies + msecs_to_jiffies(button->debounce_interval));
|
jiffies + msecs_to_jiffies(bdata->timer_debounce));
|
||||||
else
|
else
|
||||||
schedule_work(&bdata->work);
|
schedule_work(&bdata->work);
|
||||||
|
|
||||||
@ -383,6 +384,14 @@ static int __devinit gpio_keys_setup_key(struct platform_device *pdev,
|
|||||||
goto fail3;
|
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);
|
irq = gpio_to_irq(button->gpio);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
error = irq;
|
error = irq;
|
||||||
@ -498,7 +507,7 @@ static int __devinit gpio_keys_probe(struct platform_device *pdev)
|
|||||||
fail2:
|
fail2:
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
|
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);
|
del_timer_sync(&ddata->data[i].timer);
|
||||||
cancel_work_sync(&ddata->data[i].work);
|
cancel_work_sync(&ddata->data[i].work);
|
||||||
gpio_free(pdata->buttons[i].gpio);
|
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++) {
|
for (i = 0; i < pdata->nbuttons; i++) {
|
||||||
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
int irq = gpio_to_irq(pdata->buttons[i].gpio);
|
||||||
free_irq(irq, &ddata->data[i]);
|
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);
|
del_timer_sync(&ddata->data[i].timer);
|
||||||
cancel_work_sync(&ddata->data[i].work);
|
cancel_work_sync(&ddata->data[i].work);
|
||||||
gpio_free(pdata->buttons[i].gpio);
|
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 lm8323_platform_data *pdata = client->dev.platform_data;
|
||||||
struct input_dev *idev;
|
struct input_dev *idev;
|
||||||
struct lm8323_chip *lm;
|
struct lm8323_chip *lm;
|
||||||
|
int pwm;
|
||||||
int i, err;
|
int i, err;
|
||||||
unsigned long tmo;
|
unsigned long tmo;
|
||||||
u8 data[2];
|
u8 data[2];
|
||||||
@ -710,8 +711,9 @@ static int __devinit lm8323_probe(struct i2c_client *client,
|
|||||||
goto fail1;
|
goto fail1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < LM8323_NUM_PWMS; i++) {
|
for (pwm = 0; pwm < LM8323_NUM_PWMS; pwm++) {
|
||||||
err = init_pwm(lm, i + 1, &client->dev, pdata->pwm_names[i]);
|
err = init_pwm(lm, pwm + 1, &client->dev,
|
||||||
|
pdata->pwm_names[pwm]);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto fail2;
|
goto fail2;
|
||||||
}
|
}
|
||||||
@ -764,9 +766,9 @@ fail4:
|
|||||||
fail3:
|
fail3:
|
||||||
device_remove_file(&client->dev, &dev_attr_disable_kp);
|
device_remove_file(&client->dev, &dev_attr_disable_kp);
|
||||||
fail2:
|
fail2:
|
||||||
while (--i >= 0)
|
while (--pwm >= 0)
|
||||||
if (lm->pwm[i].enabled)
|
if (lm->pwm[pwm].enabled)
|
||||||
led_classdev_unregister(&lm->pwm[i].cdev);
|
led_classdev_unregister(&lm->pwm[pwm].cdev);
|
||||||
fail1:
|
fail1:
|
||||||
input_free_device(idev);
|
input_free_device(idev);
|
||||||
kfree(lm);
|
kfree(lm);
|
||||||
|
@ -37,6 +37,7 @@ struct matrix_keypad {
|
|||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
bool scan_pending;
|
bool scan_pending;
|
||||||
bool stopped;
|
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;
|
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
if (pdata->clustered_irq > 0)
|
||||||
enable_irq(gpio_to_irq(pdata->row_gpios[i]));
|
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)
|
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;
|
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++)
|
if (pdata->clustered_irq > 0)
|
||||||
disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
|
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
|
#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;
|
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
|
||||||
|
unsigned int gpio;
|
||||||
int i;
|
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++) {
|
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||||
if (!test_bit(i, keypad->disabled_gpios)) {
|
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)
|
if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
|
||||||
__set_bit(i, keypad->disabled_gpios);
|
__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;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -243,18 +285,9 @@ static int matrix_keypad_resume(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(dev);
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
|
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)) {
|
if (device_may_wakeup(&pdev->dev))
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
matrix_keypad_disable_wakeup(keypad);
|
||||||
if (test_and_clear_bit(i, keypad->disabled_gpios)) {
|
|
||||||
unsigned int gpio = pdata->row_gpios[i];
|
|
||||||
|
|
||||||
disable_irq_wake(gpio_to_irq(gpio));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matrix_keypad_start(keypad->input_dev);
|
matrix_keypad_start(keypad->input_dev);
|
||||||
|
|
||||||
@ -296,17 +329,31 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,
|
|||||||
gpio_direction_input(pdata->row_gpios[i]);
|
gpio_direction_input(pdata->row_gpios[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
if (pdata->clustered_irq > 0) {
|
||||||
err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
|
err = request_irq(pdata->clustered_irq,
|
||||||
matrix_keypad_interrupt,
|
matrix_keypad_interrupt,
|
||||||
IRQF_DISABLED |
|
pdata->clustered_irq_flags,
|
||||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
|
||||||
"matrix-keypad", keypad);
|
"matrix-keypad", keypad);
|
||||||
if (err) {
|
if (err) {
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"Unable to acquire interrupt for GPIO line %i\n",
|
"Unable to acquire clustered interrupt\n");
|
||||||
pdata->row_gpios[i]);
|
goto err_free_rows;
|
||||||
goto err_free_irqs;
|
}
|
||||||
|
} else {
|
||||||
|
for (i = 0; i < pdata->num_row_gpios; i++) {
|
||||||
|
err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
|
||||||
|
matrix_keypad_interrupt,
|
||||||
|
IRQF_DISABLED |
|
||||||
|
IRQF_TRIGGER_RISING |
|
||||||
|
IRQF_TRIGGER_FALLING,
|
||||||
|
"matrix-keypad", keypad);
|
||||||
|
if (err) {
|
||||||
|
dev_err(&pdev->dev,
|
||||||
|
"Unable to acquire interrupt "
|
||||||
|
"for GPIO line %i\n",
|
||||||
|
pdata->row_gpios[i]);
|
||||||
|
goto err_free_irqs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev)
|
|||||||
|
|
||||||
device_init_wakeup(&pdev->dev, 0);
|
device_init_wakeup(&pdev->dev, 0);
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_row_gpios; i++) {
|
if (pdata->clustered_irq > 0) {
|
||||||
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
|
free_irq(pdata->clustered_irq, keypad);
|
||||||
gpio_free(pdata->row_gpios[i]);
|
} else {
|
||||||
|
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||||
|
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < pdata->num_row_gpios; i++)
|
||||||
|
gpio_free(pdata->row_gpios[i]);
|
||||||
|
|
||||||
for (i = 0; i < pdata->num_col_gpios; i++)
|
for (i = 0; i < pdata->num_col_gpios; i++)
|
||||||
gpio_free(pdata->col_gpios[i]);
|
gpio_free(pdata->col_gpios[i]);
|
||||||
|
|
||||||
|
239
drivers/input/keyboard/mcs_touchkey.c
Normal file
239
drivers/input/keyboard/mcs_touchkey.c
Normal file
@ -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");
|
491
drivers/input/keyboard/samsung-keypad.c
Normal file
491
drivers/input/keyboard/samsung-keypad.c
Normal file
@ -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
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called pcf8574_keypad.
|
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
|
config INPUT_GPIO_ROTARY_ENCODER
|
||||||
tristate "Rotary encoders connected to GPIO pins"
|
tristate "Rotary encoders connected to GPIO pins"
|
||||||
depends on GPIOLIB && GENERIC_GPIO
|
depends on GPIOLIB && GENERIC_GPIO
|
||||||
@ -390,4 +401,41 @@ config INPUT_PCAP
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called pcap_keys.
|
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
|
endif
|
||||||
|
@ -8,6 +8,9 @@ obj-$(CONFIG_INPUT_88PM860X_ONKEY) += 88pm860x_onkey.o
|
|||||||
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
|
obj-$(CONFIG_INPUT_AD714X) += ad714x.o
|
||||||
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
|
obj-$(CONFIG_INPUT_AD714X_I2C) += ad714x-i2c.o
|
||||||
obj-$(CONFIG_INPUT_AD714X_SPI) += ad714x-spi.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_APANEL) += apanel.o
|
||||||
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
|
obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
|
||||||
obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.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_PCF8574) += pcf8574_keypad.o
|
||||||
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
|
obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o
|
||||||
obj-$(CONFIG_INPUT_POWERMATE) += powermate.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_RB532_BUTTON) += rb532_button.o
|
||||||
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
|
||||||
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
|
||||||
|
163
drivers/input/misc/adxl34x-i2c.c
Normal file
163
drivers/input/misc/adxl34x-i2c.c
Normal file
@ -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");
|
145
drivers/input/misc/adxl34x-spi.c
Normal file
145
drivers/input/misc/adxl34x-spi.c
Normal file
@ -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");
|
915
drivers/input/misc/adxl34x.c
Normal file
915
drivers/input/misc/adxl34x.c
Normal file
@ -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");
|
30
drivers/input/misc/adxl34x.h
Normal file
30
drivers/input/misc/adxl34x.h
Normal file
@ -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/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/init.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_report_key(input_dev, atlas_keymap[code], key_down);
|
||||||
input_sync(input_dev);
|
input_sync(input_dev);
|
||||||
|
|
||||||
status = 0;
|
status = AE_OK;
|
||||||
} else {
|
} else {
|
||||||
printk(KERN_WARNING "atlas: shrugged on unexpected function"
|
pr_warn("shrugged on unexpected function: function=%x,address=%lx,value=%x\n",
|
||||||
":function=%x,address=%lx,value=%x\n",
|
|
||||||
function, (unsigned long)address, (u32)*value);
|
function, (unsigned long)address, (u32)*value);
|
||||||
status = -EINVAL;
|
status = AE_BAD_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
@ -79,7 +80,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)
|
|||||||
|
|
||||||
input_dev = input_allocate_device();
|
input_dev = input_allocate_device();
|
||||||
if (!input_dev) {
|
if (!input_dev) {
|
||||||
printk(KERN_ERR "atlas: unable to allocate input device\n");
|
pr_err("unable to allocate input device\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ static int atlas_acpi_button_add(struct acpi_device *device)
|
|||||||
|
|
||||||
err = input_register_device(input_dev);
|
err = input_register_device(input_dev);
|
||||||
if (err) {
|
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);
|
input_free_device(input_dev);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
@ -112,12 +113,12 @@ static int atlas_acpi_button_add(struct acpi_device *device)
|
|||||||
0x81, &acpi_atlas_button_handler,
|
0x81, &acpi_atlas_button_handler,
|
||||||
&acpi_atlas_button_setup, device);
|
&acpi_atlas_button_setup, device);
|
||||||
if (ACPI_FAILURE(status)) {
|
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);
|
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)
|
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,
|
status = acpi_remove_address_space_handler(device->handle,
|
||||||
0x81, &acpi_atlas_button_handler);
|
0x81, &acpi_atlas_button_handler);
|
||||||
if (ACPI_FAILURE(status)) {
|
if (ACPI_FAILURE(status))
|
||||||
printk(KERN_ERR "Atlas: Error removing addr spc handler\n");
|
pr_err("error removing addr spc handler\n");
|
||||||
status = -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
input_unregister_device(input_dev);
|
input_unregister_device(input_dev);
|
||||||
|
|
||||||
return status;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct acpi_device_id atlas_device_ids[] = {
|
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 = {
|
static struct acpi_driver atlas_acpi_driver = {
|
||||||
.name = ACPI_ATLAS_NAME,
|
.name = ACPI_ATLAS_NAME,
|
||||||
.class = ACPI_ATLAS_CLASS,
|
.class = ACPI_ATLAS_CLASS,
|
||||||
|
.owner = THIS_MODULE,
|
||||||
.ids = atlas_device_ids,
|
.ids = atlas_device_ids,
|
||||||
.ops = {
|
.ops = {
|
||||||
.add = atlas_acpi_button_add,
|
.add = atlas_acpi_button_add,
|
||||||
@ -154,18 +154,10 @@ static struct acpi_driver atlas_acpi_driver = {
|
|||||||
|
|
||||||
static int __init atlas_acpi_init(void)
|
static int __init atlas_acpi_init(void)
|
||||||
{
|
{
|
||||||
int result;
|
|
||||||
|
|
||||||
if (acpi_disabled)
|
if (acpi_disabled)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
result = acpi_bus_register_driver(&atlas_acpi_driver);
|
return acpi_bus_register_driver(&atlas_acpi_driver);
|
||||||
if (result < 0) {
|
|
||||||
printk(KERN_ERR "Atlas ACPI: Unable to register driver\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __exit atlas_acpi_exit(void)
|
static void __exit atlas_acpi_exit(void)
|
||||||
|
199
drivers/input/misc/pwm-beeper.c
Normal file
199
drivers/input/misc/pwm-beeper.c
Normal file
@ -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;
|
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;
|
struct input_dev *pwr;
|
||||||
int irq = platform_get_irq(pdev, 0);
|
int irq = platform_get_irq(pdev, 0);
|
||||||
@ -95,7 +95,7 @@ free_input_dev:
|
|||||||
return err;
|
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);
|
struct input_dev *pwr = platform_get_drvdata(pdev);
|
||||||
int irq = platform_get_irq(pdev, 0);
|
int irq = platform_get_irq(pdev, 0);
|
||||||
@ -106,9 +106,8 @@ static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct platform_driver twl4030_pwrbutton_driver = {
|
static struct platform_driver twl4030_pwrbutton_driver = {
|
||||||
.probe = twl4030_pwrbutton_probe,
|
.remove = __exit_p(twl4030_pwrbutton_remove),
|
||||||
.remove = __devexit_p(twl4030_pwrbutton_remove),
|
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "twl4030_pwrbutton",
|
.name = "twl4030_pwrbutton",
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
@ -117,7 +116,8 @@ struct platform_driver twl4030_pwrbutton_driver = {
|
|||||||
|
|
||||||
static int __init twl4030_pwrbutton_init(void)
|
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);
|
module_init(twl4030_pwrbutton_init);
|
||||||
|
|
||||||
|
@ -1347,7 +1347,7 @@ static int __init wb_module_init(void)
|
|||||||
|
|
||||||
err = map_bios();
|
err = map_bios();
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
goto err_free_keymap;
|
||||||
|
|
||||||
err = platform_driver_register(&wistron_driver);
|
err = platform_driver_register(&wistron_driver);
|
||||||
if (err)
|
if (err)
|
||||||
@ -1371,6 +1371,8 @@ static int __init wb_module_init(void)
|
|||||||
platform_driver_unregister(&wistron_driver);
|
platform_driver_unregister(&wistron_driver);
|
||||||
err_unmap_bios:
|
err_unmap_bios:
|
||||||
unmap_bios();
|
unmap_bios();
|
||||||
|
err_free_keymap:
|
||||||
|
kfree(keymap);
|
||||||
|
|
||||||
return err;
|
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_TRIPLETAP, input_dev->keybit);
|
||||||
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
|
||||||
__set_bit(BTN_LEFT, 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 */
|
/* report button data as logical button state */
|
||||||
@ -580,23 +582,30 @@ exit:
|
|||||||
*/
|
*/
|
||||||
static int bcm5974_start_traffic(struct bcm5974 *dev)
|
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");
|
dprintk(1, "bcm5974: mode switch failed\n");
|
||||||
goto error;
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (usb_submit_urb(dev->bt_urb, GFP_KERNEL))
|
error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
|
||||||
goto error;
|
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;
|
goto err_kill_bt;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_kill_bt:
|
err_kill_bt:
|
||||||
usb_kill_urb(dev->bt_urb);
|
usb_kill_urb(dev->bt_urb);
|
||||||
error:
|
err_reset_mode:
|
||||||
return -EIO;
|
bcm5974_wellspring_mode(dev, false);
|
||||||
|
err_out:
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bcm5974_pause_traffic(struct bcm5974 *dev)
|
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);
|
input_report_abs(dev, ABS_PRESSURE, hw.z);
|
||||||
|
|
||||||
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
|
if (SYN_CAP_PALMDETECT(priv->capabilities))
|
||||||
|
input_report_abs(dev, ABS_TOOL_WIDTH, finger_width);
|
||||||
|
|
||||||
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
|
input_report_key(dev, BTN_TOOL_FINGER, num_fingers == 1);
|
||||||
input_report_key(dev, BTN_LEFT, hw.left);
|
input_report_key(dev, BTN_LEFT, hw.left);
|
||||||
input_report_key(dev, BTN_RIGHT, hw.right);
|
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,
|
input_set_abs_params(dev, ABS_Y,
|
||||||
YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
|
YMIN_NOMINAL, priv->y_max ?: YMAX_NOMINAL, 0, 0);
|
||||||
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 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(EV_KEY, dev->evbit);
|
||||||
__set_bit(BTN_TOUCH, dev->keybit);
|
__set_bit(BTN_TOUCH, dev->keybit);
|
||||||
|
@ -57,7 +57,6 @@ struct mousedev_hw_data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct mousedev {
|
struct mousedev {
|
||||||
int exist;
|
|
||||||
int open;
|
int open;
|
||||||
int minor;
|
int minor;
|
||||||
struct input_handle handle;
|
struct input_handle handle;
|
||||||
@ -66,6 +65,7 @@ struct mousedev {
|
|||||||
spinlock_t client_lock; /* protects client_list */
|
spinlock_t client_lock; /* protects client_list */
|
||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
bool exist;
|
||||||
|
|
||||||
struct list_head mixdev_node;
|
struct list_head mixdev_node;
|
||||||
int mixdev_open;
|
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_client *client = file->private_data;
|
||||||
struct mousedev *mousedev = client->mousedev;
|
struct mousedev *mousedev = client->mousedev;
|
||||||
|
unsigned int mask;
|
||||||
|
|
||||||
poll_wait(file, &mousedev->wait, wait);
|
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 = {
|
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)
|
static void mousedev_mark_dead(struct mousedev *mousedev)
|
||||||
{
|
{
|
||||||
mutex_lock(&mousedev->mutex);
|
mutex_lock(&mousedev->mutex);
|
||||||
mousedev->exist = 0;
|
mousedev->exist = false;
|
||||||
mutex_unlock(&mousedev->mutex);
|
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);
|
dev_set_name(&mousedev->dev, "mouse%d", minor);
|
||||||
|
|
||||||
mousedev->minor = minor;
|
mousedev->minor = minor;
|
||||||
mousedev->exist = 1;
|
mousedev->exist = true;
|
||||||
mousedev->handle.dev = input_get_device(dev);
|
mousedev->handle.dev = input_get_device(dev);
|
||||||
mousedev->handle.name = dev_name(&mousedev->dev);
|
mousedev->handle.name = dev_name(&mousedev->dev);
|
||||||
mousedev->handle.handler = handler;
|
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
|
#else
|
||||||
|
|
||||||
#include "i8042-io.h"
|
#include "i8042-io.h"
|
||||||
|
@ -861,9 +861,6 @@ static int i8042_controller_selftest(void)
|
|||||||
unsigned char param;
|
unsigned char param;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
if (!i8042_reset)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We try this 5 times; on some really fragile systems this does not
|
* We try this 5 times; on some really fragile systems this does not
|
||||||
* take the first time...
|
* take the first time...
|
||||||
@ -1020,7 +1017,8 @@ static void i8042_controller_reset(void)
|
|||||||
* Reset the controller if requested.
|
* Reset the controller if requested.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
i8042_controller_selftest();
|
if (i8042_reset)
|
||||||
|
i8042_controller_selftest();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore the original control register setting.
|
* Restore the original control register setting.
|
||||||
@ -1093,24 +1091,12 @@ static void i8042_dritek_enable(void)
|
|||||||
|
|
||||||
#ifdef CONFIG_PM
|
#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
|
* Here we try to reset everything back to a state we had
|
||||||
* before suspending.
|
* before suspending.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int i8042_pm_restore(struct device *dev)
|
static int i8042_controller_resume(bool force_reset)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
@ -1118,9 +1104,11 @@ static int i8042_pm_restore(struct device *dev)
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = i8042_controller_selftest();
|
if (i8042_reset || force_reset) {
|
||||||
if (error)
|
error = i8042_controller_selftest();
|
||||||
return error;
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Restore original CTR value and disable all ports
|
* Restore original CTR value and disable all ports
|
||||||
@ -1162,6 +1150,28 @@ static int i8042_pm_restore(struct device *dev)
|
|||||||
return 0;
|
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)
|
static int i8042_pm_thaw(struct device *dev)
|
||||||
{
|
{
|
||||||
i8042_interrupt(0, NULL);
|
i8042_interrupt(0, NULL);
|
||||||
@ -1169,9 +1179,14 @@ static int i8042_pm_thaw(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int i8042_pm_restore(struct device *dev)
|
||||||
|
{
|
||||||
|
return i8042_controller_resume(false);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct dev_pm_ops i8042_pm_ops = {
|
static const struct dev_pm_ops i8042_pm_ops = {
|
||||||
.suspend = i8042_pm_reset,
|
.suspend = i8042_pm_reset,
|
||||||
.resume = i8042_pm_restore,
|
.resume = i8042_pm_resume,
|
||||||
.thaw = i8042_pm_thaw,
|
.thaw = i8042_pm_thaw,
|
||||||
.poweroff = i8042_pm_reset,
|
.poweroff = i8042_pm_reset,
|
||||||
.restore = i8042_pm_restore,
|
.restore = i8042_pm_restore,
|
||||||
@ -1389,9 +1404,11 @@ static int __init i8042_probe(struct platform_device *dev)
|
|||||||
|
|
||||||
i8042_platform_device = dev;
|
i8042_platform_device = dev;
|
||||||
|
|
||||||
error = i8042_controller_selftest();
|
if (i8042_reset) {
|
||||||
if (error)
|
error = i8042_controller_selftest();
|
||||||
return error;
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
error = i8042_controller_init();
|
error = i8042_controller_init();
|
||||||
if (error)
|
if (error)
|
||||||
|
@ -158,6 +158,39 @@ static int wacom_ptu_irq(struct wacom_wac *wacom)
|
|||||||
return 1;
|
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)
|
static int wacom_graphire_irq(struct wacom_wac *wacom)
|
||||||
{
|
{
|
||||||
struct wacom_features *features = &wacom->features;
|
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);
|
sync = wacom_ptu_irq(wacom_wac);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DTU:
|
||||||
|
sync = wacom_dtu_irq(wacom_wac);
|
||||||
|
break;
|
||||||
|
|
||||||
case INTUOS:
|
case INTUOS:
|
||||||
case INTUOS3S:
|
case INTUOS3S:
|
||||||
case INTUOS3:
|
case INTUOS3:
|
||||||
@ -1030,6 +1067,7 @@ void wacom_setup_input_capabilities(struct input_dev *input_dev,
|
|||||||
|
|
||||||
case PL:
|
case PL:
|
||||||
case PTU:
|
case PTU:
|
||||||
|
case DTU:
|
||||||
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
__set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||||
__set_bit(BTN_STYLUS, input_dev->keybit);
|
__set_bit(BTN_STYLUS, input_dev->keybit);
|
||||||
__set_bit(BTN_STYLUS2, 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 };
|
{ "Wacom Cintiq 12WX", WACOM_PKGLEN_INTUOS, 53020, 33440, 1023, 63, WACOM_BEE };
|
||||||
static const struct wacom_features wacom_features_0xC7 =
|
static const struct wacom_features wacom_features_0xC7 =
|
||||||
{ "Wacom DTU1931", WACOM_PKGLEN_GRAPHIRE, 37832, 30305, 511, 0, PL };
|
{ "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 =
|
static const struct wacom_features wacom_features_0xCC =
|
||||||
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, 63, WACOM_21UX2 };
|
{ "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, 63, WACOM_21UX2 };
|
||||||
static const struct wacom_features wacom_features_0x90 =
|
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(0xC5) },
|
||||||
{ USB_DEVICE_WACOM(0xC6) },
|
{ USB_DEVICE_WACOM(0xC6) },
|
||||||
{ USB_DEVICE_WACOM(0xC7) },
|
{ USB_DEVICE_WACOM(0xC7) },
|
||||||
|
{ USB_DEVICE_WACOM(0xCE) },
|
||||||
|
{ USB_DEVICE_WACOM(0xF0) },
|
||||||
{ USB_DEVICE_WACOM(0xCC) },
|
{ USB_DEVICE_WACOM(0xCC) },
|
||||||
{ USB_DEVICE_WACOM(0x90) },
|
{ USB_DEVICE_WACOM(0x90) },
|
||||||
{ USB_DEVICE_WACOM(0x93) },
|
{ USB_DEVICE_WACOM(0x93) },
|
||||||
|
@ -43,6 +43,7 @@ enum {
|
|||||||
WACOM_G4,
|
WACOM_G4,
|
||||||
PTU,
|
PTU,
|
||||||
PL,
|
PL,
|
||||||
|
DTU,
|
||||||
INTUOS,
|
INTUOS,
|
||||||
INTUOS3S,
|
INTUOS3S,
|
||||||
INTUOS3,
|
INTUOS3,
|
||||||
|
@ -55,37 +55,36 @@ config TOUCHSCREEN_AD7877
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad7877.
|
module will be called ad7877.
|
||||||
|
|
||||||
config TOUCHSCREEN_AD7879_I2C
|
config TOUCHSCREEN_AD7879
|
||||||
tristate "AD7879 based touchscreens: AD7879-1 I2C Interface"
|
tristate "Analog Devices AD7879-1/AD7889-1 touchscreen interface"
|
||||||
depends on I2C
|
|
||||||
select TOUCHSCREEN_AD7879
|
|
||||||
help
|
help
|
||||||
Say Y here if you have a touchscreen interface using the
|
Say Y here if you want to support a touchscreen interface using
|
||||||
AD7879-1/AD7889-1 controller, and your board-specific
|
the AD7879-1/AD7889-1 controller.
|
||||||
initialization code includes that in its table of I2C devices.
|
|
||||||
|
|
||||||
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
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad7879.
|
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
|
config TOUCHSCREEN_AD7879_SPI
|
||||||
tristate "AD7879 based touchscreens: AD7879 SPI Interface"
|
tristate "support SPI bus connection"
|
||||||
depends on SPI_MASTER && TOUCHSCREEN_AD7879_I2C = n
|
depends on TOUCHSCREEN_AD7879 && SPI_MASTER
|
||||||
select TOUCHSCREEN_AD7879
|
|
||||||
help
|
help
|
||||||
Say Y here if you have a touchscreen interface using the
|
Say Y here if you have AD7879-1/AD7889-1 hooked to a SPI bus.
|
||||||
AD7879/AD7889 controller, and your board-specific initialization
|
|
||||||
code includes that in its table of SPI devices.
|
|
||||||
|
|
||||||
If unsure, say N (but it's safe to say "Y").
|
If unsure, say N (but it's safe to say "Y").
|
||||||
|
|
||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called ad7879.
|
module will be called ad7879-spi.
|
||||||
|
|
||||||
config TOUCHSCREEN_AD7879
|
|
||||||
tristate
|
|
||||||
default n
|
|
||||||
|
|
||||||
config TOUCHSCREEN_BITSY
|
config TOUCHSCREEN_BITSY
|
||||||
tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
|
tristate "Compaq iPAQ H3600 (Bitsy) touchscreen"
|
||||||
@ -99,6 +98,20 @@ config TOUCHSCREEN_BITSY
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called h3600_ts_input.
|
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
|
config TOUCHSCREEN_DA9034
|
||||||
tristate "Touchscreen support for Dialog Semiconductor DA9034"
|
tristate "Touchscreen support for Dialog Semiconductor DA9034"
|
||||||
depends on PMIC_DA903X
|
depends on PMIC_DA903X
|
||||||
@ -292,6 +305,18 @@ config TOUCHSCREEN_PENMOUNT
|
|||||||
To compile this driver as a module, choose M here: the
|
To compile this driver as a module, choose M here: the
|
||||||
module will be called penmount.
|
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
|
config TOUCHSCREEN_MIGOR
|
||||||
tristate "Renesas MIGO-R touchscreen"
|
tristate "Renesas MIGO-R touchscreen"
|
||||||
depends on SH_MIGOR && I2C
|
depends on SH_MIGOR && I2C
|
||||||
@ -540,9 +565,9 @@ config TOUCHSCREEN_USB_ZYTRONIC
|
|||||||
bool "Zytronic controller" if EMBEDDED
|
bool "Zytronic controller" if EMBEDDED
|
||||||
depends on TOUCHSCREEN_USB_COMPOSITE
|
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||||
|
|
||||||
config TOUCHSCREEN_USB_ETT_TC5UH
|
config TOUCHSCREEN_USB_ETT_TC45USB
|
||||||
default y
|
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
|
depends on TOUCHSCREEN_USB_COMPOSITE
|
||||||
|
|
||||||
config TOUCHSCREEN_USB_NEXIO
|
config TOUCHSCREEN_USB_NEXIO
|
||||||
|
@ -9,9 +9,13 @@ wm97xx-ts-y := wm97xx-core.o
|
|||||||
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
|
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.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_ADS7846) += ads7846.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
|
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.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_DYNAPRO) += dynapro.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
|
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_GUNZE) += gunze.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_USB_COMPOSITE) += usbtouchscreen.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
|
||||||
|
obj-$(CONFIG_TOUCHSCREEN_QT602240) += qt602240_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.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_UCB1400) += ucb1400_ts.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
|
obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o
|
||||||
obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.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_WM9705) += wm9705.o
|
||||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
|
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
|
||||||
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
|
wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
|
||||||
|
143
drivers/input/touchscreen/ad7879-i2c.c
Normal file
143
drivers/input/touchscreen/ad7879-i2c.c
Normal file
@ -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");
|
198
drivers/input/touchscreen/ad7879-spi.c
Normal file
198
drivers/input/touchscreen/ad7879-spi.c
Normal file
@ -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
|
* Copyright (C) 2008-2010 Michael Hennerich, Analog Devices Inc.
|
||||||
* (I2C/SPI Interface)
|
|
||||||
*
|
*
|
||||||
* Bugs: Enter bugs at http://blackfin.uclinux.org/
|
* Licensed under the GPL-2 or later.
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, see the file COPYING, or write
|
|
||||||
* to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*
|
*
|
||||||
* History:
|
* History:
|
||||||
* Copyright (c) 2005 David Brownell
|
* Copyright (c) 2005 David Brownell
|
||||||
@ -44,12 +28,12 @@
|
|||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/workqueue.h>
|
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
|
||||||
#include <linux/spi/ad7879.h>
|
#include <linux/spi/ad7879.h>
|
||||||
|
#include "ad7879.h"
|
||||||
|
|
||||||
#define AD7879_REG_ZEROS 0
|
#define AD7879_REG_ZEROS 0
|
||||||
#define AD7879_REG_CTRL1 1
|
#define AD7879_REG_CTRL1 1
|
||||||
@ -120,30 +104,19 @@ enum {
|
|||||||
#define MAX_12BIT ((1<<12)-1)
|
#define MAX_12BIT ((1<<12)-1)
|
||||||
#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
|
#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 {
|
struct ad7879 {
|
||||||
bus_device *bus;
|
const struct ad7879_bus_ops *bops;
|
||||||
|
|
||||||
|
struct device *dev;
|
||||||
struct input_dev *input;
|
struct input_dev *input;
|
||||||
struct work_struct work;
|
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
#ifdef CONFIG_GPIOLIB
|
#ifdef CONFIG_GPIOLIB
|
||||||
struct gpio_chip gc;
|
struct gpio_chip gc;
|
||||||
#endif
|
|
||||||
struct mutex mutex;
|
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
|
#endif
|
||||||
|
unsigned int irq;
|
||||||
|
bool disabled; /* P: input->mutex */
|
||||||
|
bool suspended; /* P: input->mutex */
|
||||||
u16 conversion_data[AD7879_NR_SENSE];
|
u16 conversion_data[AD7879_NR_SENSE];
|
||||||
char phys[32];
|
char phys[32];
|
||||||
u8 first_conversion_delay;
|
u8 first_conversion_delay;
|
||||||
@ -158,11 +131,22 @@ struct ad7879 {
|
|||||||
u16 cmd_crtl3;
|
u16 cmd_crtl3;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ad7879_read(bus_device *, u8);
|
static int ad7879_read(struct ad7879 *ts, u8 reg)
|
||||||
static int ad7879_write(bus_device *, u8, u16);
|
{
|
||||||
static void ad7879_collect(struct ad7879 *);
|
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;
|
struct input_dev *input_dev = ts->input;
|
||||||
unsigned Rt;
|
unsigned Rt;
|
||||||
@ -175,12 +159,14 @@ static void ad7879_report(struct ad7879 *ts)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* The samples processed here are already preprocessed by the AD7879.
|
* The samples processed here are already preprocessed by the AD7879.
|
||||||
* The preprocessing function consists of a median and an averaging filter.
|
* The preprocessing function consists of a median and an averaging
|
||||||
* The combination of these two techniques provides a robust solution,
|
* filter. The combination of these two techniques provides a robust
|
||||||
* discarding the spurious noise in the signal and keeping only the data of interest.
|
* solution, discarding the spurious noise in the signal and keeping
|
||||||
* The size of both filters is programmable. (dev.platform_data, see linux/spi/ad7879.h)
|
* only the data of interest. The size of both filters is
|
||||||
* Other user-programmable conversion controls include variable acquisition time,
|
* programmable. (dev.platform_data, see linux/spi/ad7879.h) Other
|
||||||
* and first conversion delay. Up to 16 averages can be taken per conversion.
|
* 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)) {
|
if (likely(x && z1)) {
|
||||||
@ -189,21 +175,17 @@ static void ad7879_report(struct ad7879 *ts)
|
|||||||
Rt /= z1;
|
Rt /= z1;
|
||||||
Rt = (Rt + 2047) >> 12;
|
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_X, x);
|
||||||
input_report_abs(input_dev, ABS_Y, y);
|
input_report_abs(input_dev, ABS_Y, y);
|
||||||
input_report_abs(input_dev, ABS_PRESSURE, Rt);
|
input_report_abs(input_dev, ABS_PRESSURE, Rt);
|
||||||
input_sync(input_dev);
|
input_sync(input_dev);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void ad7879_work(struct work_struct *work)
|
return -EINVAL;
|
||||||
{
|
|
||||||
struct ad7879 *ts = container_of(work, struct ad7879, work);
|
|
||||||
|
|
||||||
/* use keventd context to read the result registers */
|
|
||||||
ad7879_collect(ts);
|
|
||||||
ad7879_report(ts);
|
|
||||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ad7879_ts_event_release(struct ad7879 *ts)
|
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;
|
struct input_dev *input_dev = ts->input;
|
||||||
|
|
||||||
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
input_report_abs(input_dev, ABS_PRESSURE, 0);
|
||||||
|
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||||
input_sync(input_dev);
|
input_sync(input_dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,56 +208,98 @@ static irqreturn_t ad7879_irq(int irq, void *handle)
|
|||||||
{
|
{
|
||||||
struct ad7879 *ts = handle;
|
struct ad7879 *ts = handle;
|
||||||
|
|
||||||
/* The repeated conversion sequencer controlled by TMR kicked off too fast.
|
ad7879_multi_read(ts, AD7879_REG_XPLUS, AD7879_NR_SENSE, ts->conversion_data);
|
||||||
* We ignore the last and process the sample sequence currently in the queue.
|
|
||||||
* It can't be older than 9.4ms
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!work_pending(&ts->work))
|
if (!ad7879_report(ts))
|
||||||
schedule_work(&ts->work);
|
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
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, AD7879_REG_CTRL2, ts->cmd_crtl2);
|
||||||
ad7879_write(ts->bus, AD7879_REG_CTRL3, ts->cmd_crtl3);
|
ad7879_write(ts, AD7879_REG_CTRL3, ts->cmd_crtl3);
|
||||||
ad7879_write(ts->bus, AD7879_REG_CTRL1, ts->cmd_crtl1);
|
ad7879_write(ts, AD7879_REG_CTRL1, ts->cmd_crtl1);
|
||||||
|
|
||||||
|
enable_irq(ts->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ad7879_disable(struct ad7879 *ts)
|
static void __ad7879_disable(struct ad7879 *ts)
|
||||||
{
|
{
|
||||||
mutex_lock(&ts->mutex);
|
disable_irq(ts->irq);
|
||||||
|
|
||||||
if (!ts->disabled) {
|
if (del_timer_sync(&ts->timer))
|
||||||
|
ad7879_ts_event_release(ts);
|
||||||
|
|
||||||
ts->disabled = 1;
|
ad7879_write(ts, AD7879_REG_CTRL2, AD7879_PM(AD7879_PM_SHUTDOWN));
|
||||||
disable_irq(ts->bus->irq);
|
|
||||||
|
|
||||||
cancel_work_sync(&ts->work);
|
|
||||||
|
|
||||||
if (del_timer_sync(&ts->timer))
|
|
||||||
ad7879_ts_event_release(ts);
|
|
||||||
|
|
||||||
ad7879_write(ts->bus, AD7879_REG_CTRL2,
|
|
||||||
AD7879_PM(AD7879_PM_SHUTDOWN));
|
|
||||||
}
|
|
||||||
|
|
||||||
mutex_unlock(&ts->mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ad7879_enable(struct ad7879 *ts)
|
|
||||||
{
|
|
||||||
mutex_lock(&ts->mutex);
|
|
||||||
|
|
||||||
if (ts->disabled) {
|
static int ad7879_open(struct input_dev *input)
|
||||||
ad7879_setup(ts);
|
{
|
||||||
ts->disabled = 0;
|
struct ad7879 *ts = input_get_drvdata(input);
|
||||||
enable_irq(ts->bus->irq);
|
|
||||||
|
/* 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,
|
static ssize_t ad7879_disable_show(struct device *dev,
|
||||||
@ -297,10 +322,7 @@ static ssize_t ad7879_disable_store(struct device *dev,
|
|||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (val)
|
ad7879_toggle(ts, val);
|
||||||
ad7879_disable(ts);
|
|
||||||
else
|
|
||||||
ad7879_enable(ts);
|
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
@ -325,7 +347,7 @@ static int ad7879_gpio_direction_input(struct gpio_chip *chip,
|
|||||||
|
|
||||||
mutex_lock(&ts->mutex);
|
mutex_lock(&ts->mutex);
|
||||||
ts->cmd_crtl2 |= AD7879_GPIO_EN | AD7879_GPIODIR | AD7879_GPIOPOL;
|
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);
|
mutex_unlock(&ts->mutex);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -345,7 +367,7 @@ static int ad7879_gpio_direction_output(struct gpio_chip *chip,
|
|||||||
else
|
else
|
||||||
ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
|
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);
|
mutex_unlock(&ts->mutex);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
@ -357,7 +379,7 @@ static int ad7879_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
|
|||||||
u16 val;
|
u16 val;
|
||||||
|
|
||||||
mutex_lock(&ts->mutex);
|
mutex_lock(&ts->mutex);
|
||||||
val = ad7879_read(ts->bus, AD7879_REG_CTRL2);
|
val = ad7879_read(ts, AD7879_REG_CTRL2);
|
||||||
mutex_unlock(&ts->mutex);
|
mutex_unlock(&ts->mutex);
|
||||||
|
|
||||||
return !!(val & AD7879_GPIO_DATA);
|
return !!(val & AD7879_GPIO_DATA);
|
||||||
@ -374,16 +396,17 @@ static void ad7879_gpio_set_value(struct gpio_chip *chip,
|
|||||||
else
|
else
|
||||||
ts->cmd_crtl2 &= ~AD7879_GPIO_DATA;
|
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);
|
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;
|
int ret = 0;
|
||||||
|
|
||||||
|
mutex_init(&ts->mutex);
|
||||||
|
|
||||||
if (pdata->gpio_export) {
|
if (pdata->gpio_export) {
|
||||||
ts->gc.direction_input = ad7879_gpio_direction_input;
|
ts->gc.direction_input = ad7879_gpio_direction_input;
|
||||||
ts->gc.direction_output = ad7879_gpio_direction_output;
|
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.ngpio = 1;
|
||||||
ts->gc.label = "AD7879-GPIO";
|
ts->gc.label = "AD7879-GPIO";
|
||||||
ts->gc.owner = THIS_MODULE;
|
ts->gc.owner = THIS_MODULE;
|
||||||
ts->gc.dev = dev;
|
ts->gc.dev = ts->dev;
|
||||||
|
|
||||||
ret = gpiochip_add(&ts->gc);
|
ret = gpiochip_add(&ts->gc);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dev, "failed to register gpio %d\n",
|
dev_err(ts->dev, "failed to register gpio %d\n",
|
||||||
ts->gc.base);
|
ts->gc.base);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void ad7879_gpio_remove(struct ad7879 *ts)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
struct ad7879 *ts = dev_get_drvdata(dev);
|
const struct ad7879_platform_data *pdata = ts->dev->platform_data;
|
||||||
struct ad7879_platform_data *pdata = dev->platform_data;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (pdata->gpio_export) {
|
if (pdata->gpio_export) {
|
||||||
ret = gpiochip_remove(&ts->gc);
|
ret = gpiochip_remove(&ts->gc);
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_err(dev, "failed to remove gpio %d\n",
|
dev_err(ts->dev, "failed to remove gpio %d\n",
|
||||||
ts->gc.base);
|
ts->gc.base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ad7879_gpio_remove(struct device *dev)
|
static inline void ad7879_gpio_remove(struct ad7879 *ts)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif
|
#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 input_dev *input_dev;
|
||||||
struct ad7879_platform_data *pdata = bus->dev.platform_data;
|
|
||||||
int err;
|
int err;
|
||||||
u16 revid;
|
u16 revid;
|
||||||
|
|
||||||
if (!bus->irq) {
|
if (!irq) {
|
||||||
dev_err(&bus->dev, "no IRQ?\n");
|
dev_err(dev, "no IRQ?\n");
|
||||||
return -ENODEV;
|
err = -EINVAL;
|
||||||
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!pdata) {
|
if (!pdata) {
|
||||||
dev_err(&bus->dev, "no platform data?\n");
|
dev_err(dev, "no platform data?\n");
|
||||||
return -ENODEV;
|
err = -EINVAL;
|
||||||
|
goto err_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ts = kzalloc(sizeof(*ts), GFP_KERNEL);
|
||||||
input_dev = input_allocate_device();
|
input_dev = input_allocate_device();
|
||||||
if (!input_dev)
|
if (!ts || !input_dev) {
|
||||||
return -ENOMEM;
|
err = -ENOMEM;
|
||||||
|
goto err_free_mem;
|
||||||
|
}
|
||||||
|
|
||||||
|
ts->bops = bops;
|
||||||
|
ts->dev = dev;
|
||||||
ts->input = input_dev;
|
ts->input = input_dev;
|
||||||
|
ts->irq = irq;
|
||||||
|
|
||||||
setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
|
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->x_plate_ohms = pdata->x_plate_ohms ? : 400;
|
||||||
ts->pressure_max = pdata->pressure_max ? : ~0;
|
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->pen_down_acc_interval = pdata->pen_down_acc_interval;
|
||||||
ts->median = pdata->median;
|
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->name = "AD7879 Touchscreen";
|
||||||
input_dev->phys = ts->phys;
|
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(EV_ABS, input_dev->evbit);
|
||||||
__set_bit(ABS_X, input_dev->absbit);
|
__set_bit(ABS_X, input_dev->absbit);
|
||||||
__set_bit(ABS_Y, input_dev->absbit);
|
__set_bit(ABS_Y, input_dev->absbit);
|
||||||
__set_bit(ABS_PRESSURE, 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,
|
input_set_abs_params(input_dev, ABS_X,
|
||||||
pdata->x_min ? : 0,
|
pdata->x_min ? : 0,
|
||||||
pdata->x_max ? : MAX_12BIT,
|
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,
|
input_set_abs_params(input_dev, ABS_PRESSURE,
|
||||||
pdata->pressure_min, pdata->pressure_max, 0, 0);
|
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) {
|
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;
|
goto err_free_mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
revid = ad7879_read(bus, AD7879_REG_REVID);
|
revid = ad7879_read(ts, AD7879_REG_REVID);
|
||||||
|
input_dev->id.product = (revid & 0xff);
|
||||||
if ((revid & 0xFF) != AD7879_DEVID) {
|
input_dev->id.version = revid >> 8;
|
||||||
dev_err(&bus->dev, "Failed to probe %s\n", input_dev->name);
|
if (input_dev->id.product != devid) {
|
||||||
|
dev_err(dev, "Failed to probe %s (%x vs %x)\n",
|
||||||
|
input_dev->name, devid, revid);
|
||||||
err = -ENODEV;
|
err = -ENODEV;
|
||||||
goto err_free_mem;
|
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_ACQ(ts->acquisition_time) |
|
||||||
AD7879_TMR(ts->pen_down_acc_interval);
|
AD7879_TMR(ts->pen_down_acc_interval);
|
||||||
|
|
||||||
ad7879_setup(ts);
|
err = request_threaded_irq(ts->irq, NULL, ad7879_irq,
|
||||||
|
IRQF_TRIGGER_FALLING,
|
||||||
err = request_irq(bus->irq, ad7879_irq,
|
dev_name(dev), ts);
|
||||||
IRQF_TRIGGER_FALLING, bus->dev.driver->name, ts);
|
|
||||||
|
|
||||||
if (err) {
|
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;
|
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)
|
if (err)
|
||||||
goto err_free_irq;
|
goto err_free_irq;
|
||||||
|
|
||||||
err = ad7879_gpio_add(&bus->dev);
|
err = ad7879_gpio_add(ts, pdata);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_remove_attr;
|
goto err_remove_attr;
|
||||||
|
|
||||||
@ -546,321 +582,32 @@ static int __devinit ad7879_construct(bus_device *bus, struct ad7879 *ts)
|
|||||||
if (err)
|
if (err)
|
||||||
goto err_remove_gpio;
|
goto err_remove_gpio;
|
||||||
|
|
||||||
dev_info(&bus->dev, "Rev.%d touchscreen, irq %d\n",
|
return ts;
|
||||||
revid >> 8, bus->irq);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_remove_gpio:
|
err_remove_gpio:
|
||||||
ad7879_gpio_remove(&bus->dev);
|
ad7879_gpio_remove(ts);
|
||||||
err_remove_attr:
|
err_remove_attr:
|
||||||
sysfs_remove_group(&bus->dev.kobj, &ad7879_attr_group);
|
sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
|
||||||
err_free_irq:
|
err_free_irq:
|
||||||
free_irq(bus->irq, ts);
|
free_irq(ts->irq, ts);
|
||||||
err_free_mem:
|
err_free_mem:
|
||||||
input_free_device(input_dev);
|
input_free_device(input_dev);
|
||||||
|
kfree(ts);
|
||||||
return err;
|
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_gpio_remove(ts);
|
||||||
ad7879_disable(ts);
|
sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
|
||||||
sysfs_remove_group(&ts->bus->dev.kobj, &ad7879_attr_group);
|
free_irq(ts->irq, ts);
|
||||||
free_irq(ts->bus->irq, ts);
|
|
||||||
input_unregister_device(ts->input);
|
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);
|
kfree(ts);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(ad7879_remove);
|
||||||
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
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
||||||
MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
|
MODULE_DESCRIPTION("AD7879(-1) touchscreen Driver");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
MODULE_ALIAS("spi:ad7879");
|
|
||||||
|
30
drivers/input/touchscreen/ad7879.h
Normal file
30
drivers/input/touchscreen/ad7879.h
Normal file
@ -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 y;
|
||||||
u16 z1, z2;
|
u16 z1, z2;
|
||||||
int ignore;
|
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;
|
u8 read_x, read_y, read_z1, read_z2, pwrdown;
|
||||||
u16 dummy; /* for the pwrdown read */
|
u16 dummy; /* for the pwrdown read */
|
||||||
struct ts_event tc;
|
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 {
|
struct ads7846 {
|
||||||
@ -207,6 +211,14 @@ struct ser_req {
|
|||||||
struct spi_transfer xfer[6];
|
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_enable(struct ads7846 *ts);
|
||||||
static void ads7846_disable(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;
|
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)
|
#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE)
|
||||||
|
|
||||||
#define SHOW(name, var, adjust) static ssize_t \
|
#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
|
/* ads7846_rx_val() did in-place conversion (including byteswap) from
|
||||||
* on-the-wire format as part of debouncing to get stable readings.
|
* on-the-wire format as part of debouncing to get stable readings.
|
||||||
*/
|
*/
|
||||||
x = packet->tc.x;
|
if (ts->model == 7845) {
|
||||||
y = packet->tc.y;
|
x = *(u16 *)packet->tc.x_buf;
|
||||||
z1 = packet->tc.z1;
|
y = *(u16 *)packet->tc.y_buf;
|
||||||
z2 = packet->tc.z2;
|
z1 = 0;
|
||||||
|
z2 = 0;
|
||||||
|
} else {
|
||||||
|
x = packet->tc.x;
|
||||||
|
y = packet->tc.y;
|
||||||
|
z1 = packet->tc.z1;
|
||||||
|
z2 = packet->tc.z2;
|
||||||
|
}
|
||||||
|
|
||||||
/* range filtering */
|
/* range filtering */
|
||||||
if (x == MAX_12BIT)
|
if (x == MAX_12BIT)
|
||||||
@ -551,6 +605,12 @@ static void ads7846_rx(void *ads)
|
|||||||
|
|
||||||
if (ts->model == 7843) {
|
if (ts->model == 7843) {
|
||||||
Rt = ts->pressure_max / 2;
|
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)) {
|
} else if (likely(x && z1)) {
|
||||||
/* compute touch pressure resistance using equation #2 */
|
/* compute touch pressure resistance using equation #2 */
|
||||||
Rt = z2;
|
Rt = z2;
|
||||||
@ -671,10 +731,14 @@ static void ads7846_rx_val(void *ads)
|
|||||||
m = &ts->msg[ts->msg_idx];
|
m = &ts->msg[ts->msg_idx];
|
||||||
t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
|
t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list);
|
||||||
|
|
||||||
/* adjust: on-wire is a must-ignore bit, a BE12 value, then padding;
|
if (ts->model == 7845) {
|
||||||
* built from two 8 bit values written msb-first.
|
val = be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
|
||||||
*/
|
} else {
|
||||||
val = be16_to_cpup((__be16 *)t->rx_buf) >> 3;
|
/* 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);
|
action = ts->filter(ts->filter_data, ts->msg_idx, &val);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
@ -878,14 +942,15 @@ static int __devinit setup_pendown(struct spi_device *spi, struct ads7846 *ts)
|
|||||||
|
|
||||||
static int __devinit ads7846_probe(struct spi_device *spi)
|
static int __devinit ads7846_probe(struct spi_device *spi)
|
||||||
{
|
{
|
||||||
struct ads7846 *ts;
|
struct ads7846 *ts;
|
||||||
struct ads7846_packet *packet;
|
struct ads7846_packet *packet;
|
||||||
struct input_dev *input_dev;
|
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_message *m;
|
||||||
struct spi_transfer *x;
|
struct spi_transfer *x;
|
||||||
int vref;
|
unsigned long irq_flags;
|
||||||
int err;
|
int vref;
|
||||||
|
int err;
|
||||||
|
|
||||||
if (!spi->irq) {
|
if (!spi->irq) {
|
||||||
dev_dbg(&spi->dev, "no IRQ?\n");
|
dev_dbg(&spi->dev, "no IRQ?\n");
|
||||||
@ -1008,16 +1073,26 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||||||
|
|
||||||
spi_message_init(m);
|
spi_message_init(m);
|
||||||
|
|
||||||
/* y- still on; turn on only y+ (and ADC) */
|
if (ts->model == 7845) {
|
||||||
packet->read_y = READ_Y(vref);
|
packet->read_y_cmd[0] = READ_Y(vref);
|
||||||
x->tx_buf = &packet->read_y;
|
packet->read_y_cmd[1] = 0;
|
||||||
x->len = 1;
|
packet->read_y_cmd[2] = 0;
|
||||||
spi_message_add_tail(x, m);
|
x->tx_buf = &packet->read_y_cmd[0];
|
||||||
|
x->rx_buf = &packet->tc.y_buf[0];
|
||||||
|
x->len = 3;
|
||||||
|
spi_message_add_tail(x, m);
|
||||||
|
} else {
|
||||||
|
/* y- still on; turn on only y+ (and ADC) */
|
||||||
|
packet->read_y = READ_Y(vref);
|
||||||
|
x->tx_buf = &packet->read_y;
|
||||||
|
x->len = 1;
|
||||||
|
spi_message_add_tail(x, m);
|
||||||
|
|
||||||
x++;
|
x++;
|
||||||
x->rx_buf = &packet->tc.y;
|
x->rx_buf = &packet->tc.y;
|
||||||
x->len = 2;
|
x->len = 2;
|
||||||
spi_message_add_tail(x, m);
|
spi_message_add_tail(x, m);
|
||||||
|
}
|
||||||
|
|
||||||
/* the first sample after switching drivers can be low quality;
|
/* the first sample after switching drivers can be low quality;
|
||||||
* optionally discard it, using a second one after the signals
|
* optionally discard it, using a second one after the signals
|
||||||
@ -1043,17 +1118,28 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||||||
m++;
|
m++;
|
||||||
spi_message_init(m);
|
spi_message_init(m);
|
||||||
|
|
||||||
/* turn y- off, x+ on, then leave in lowpower */
|
if (ts->model == 7845) {
|
||||||
x++;
|
x++;
|
||||||
packet->read_x = READ_X(vref);
|
packet->read_x_cmd[0] = READ_X(vref);
|
||||||
x->tx_buf = &packet->read_x;
|
packet->read_x_cmd[1] = 0;
|
||||||
x->len = 1;
|
packet->read_x_cmd[2] = 0;
|
||||||
spi_message_add_tail(x, m);
|
x->tx_buf = &packet->read_x_cmd[0];
|
||||||
|
x->rx_buf = &packet->tc.x_buf[0];
|
||||||
|
x->len = 3;
|
||||||
|
spi_message_add_tail(x, m);
|
||||||
|
} else {
|
||||||
|
/* turn y- off, x+ on, then leave in lowpower */
|
||||||
|
x++;
|
||||||
|
packet->read_x = READ_X(vref);
|
||||||
|
x->tx_buf = &packet->read_x;
|
||||||
|
x->len = 1;
|
||||||
|
spi_message_add_tail(x, m);
|
||||||
|
|
||||||
x++;
|
x++;
|
||||||
x->rx_buf = &packet->tc.x;
|
x->rx_buf = &packet->tc.x;
|
||||||
x->len = 2;
|
x->len = 2;
|
||||||
spi_message_add_tail(x, m);
|
spi_message_add_tail(x, m);
|
||||||
|
}
|
||||||
|
|
||||||
/* ... maybe discard first sample ... */
|
/* ... maybe discard first sample ... */
|
||||||
if (pdata->settle_delay_usecs) {
|
if (pdata->settle_delay_usecs) {
|
||||||
@ -1144,15 +1230,25 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||||||
m++;
|
m++;
|
||||||
spi_message_init(m);
|
spi_message_init(m);
|
||||||
|
|
||||||
x++;
|
if (ts->model == 7845) {
|
||||||
packet->pwrdown = PWRDOWN;
|
x++;
|
||||||
x->tx_buf = &packet->pwrdown;
|
packet->pwrdown_cmd[0] = PWRDOWN;
|
||||||
x->len = 1;
|
packet->pwrdown_cmd[1] = 0;
|
||||||
spi_message_add_tail(x, m);
|
packet->pwrdown_cmd[2] = 0;
|
||||||
|
x->tx_buf = &packet->pwrdown_cmd[0];
|
||||||
|
x->len = 3;
|
||||||
|
} else {
|
||||||
|
x++;
|
||||||
|
packet->pwrdown = PWRDOWN;
|
||||||
|
x->tx_buf = &packet->pwrdown;
|
||||||
|
x->len = 1;
|
||||||
|
spi_message_add_tail(x, m);
|
||||||
|
|
||||||
|
x++;
|
||||||
|
x->rx_buf = &packet->dummy;
|
||||||
|
x->len = 2;
|
||||||
|
}
|
||||||
|
|
||||||
x++;
|
|
||||||
x->rx_buf = &packet->dummy;
|
|
||||||
x->len = 2;
|
|
||||||
CS_CHANGE(*x);
|
CS_CHANGE(*x);
|
||||||
spi_message_add_tail(x, m);
|
spi_message_add_tail(x, m);
|
||||||
|
|
||||||
@ -1174,17 +1270,22 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||||||
goto err_put_regulator;
|
goto err_put_regulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request_irq(spi->irq, ads7846_irq, IRQF_TRIGGER_FALLING,
|
irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
|
||||||
spi->dev.driver->name, ts)) {
|
|
||||||
|
err = request_irq(spi->irq, ads7846_irq, irq_flags,
|
||||||
|
spi->dev.driver->name, ts);
|
||||||
|
|
||||||
|
if (err && !pdata->irq_flags) {
|
||||||
dev_info(&spi->dev,
|
dev_info(&spi->dev,
|
||||||
"trying pin change workaround on irq %d\n", spi->irq);
|
"trying pin change workaround on irq %d\n", spi->irq);
|
||||||
err = request_irq(spi->irq, ads7846_irq,
|
err = request_irq(spi->irq, ads7846_irq,
|
||||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
|
||||||
spi->dev.driver->name, ts);
|
spi->dev.driver->name, ts);
|
||||||
if (err) {
|
}
|
||||||
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
|
|
||||||
goto err_disable_regulator;
|
if (err) {
|
||||||
}
|
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
|
||||||
|
goto err_disable_regulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ads784x_hwmon_register(spi, ts);
|
err = ads784x_hwmon_register(spi, ts);
|
||||||
@ -1196,8 +1297,11 @@ static int __devinit ads7846_probe(struct spi_device *spi)
|
|||||||
/* take a first sample, leaving nPENIRQ active and vREF off; avoid
|
/* take a first sample, leaving nPENIRQ active and vREF off; avoid
|
||||||
* the touchscreen, in case it's not connected.
|
* the touchscreen, in case it's not connected.
|
||||||
*/
|
*/
|
||||||
(void) ads7846_read12_ser(&spi->dev,
|
if (ts->model == 7845)
|
||||||
READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
|
ads7845_read12_ser(&spi->dev, PWRDOWN);
|
||||||
|
else
|
||||||
|
(void) ads7846_read12_ser(&spi->dev,
|
||||||
|
READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON);
|
||||||
|
|
||||||
err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
|
err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
|
||||||
if (err)
|
if (err)
|
||||||
|
363
drivers/input/touchscreen/cy8ctmg110_ts.c
Normal file
363
drivers/input/touchscreen/cy8ctmg110_ts.c
Normal file
@ -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/module.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c/mcs5000_ts.h>
|
#include <linux/i2c/mcs.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/input.h>
|
#include <linux/input.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
@ -105,7 +105,7 @@ enum mcs5000_ts_read_offset {
|
|||||||
struct mcs5000_ts_data {
|
struct mcs5000_ts_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct input_dev *input_dev;
|
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)
|
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)
|
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;
|
data->platform_data;
|
||||||
struct i2c_client *client = data->client;
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
|
1401
drivers/input/touchscreen/qt602240_ts.c
Normal file
1401
drivers/input/touchscreen/qt602240_ts.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -355,9 +355,6 @@ static int __devexit tps6507x_ts_remove(struct platform_device *pdev)
|
|||||||
struct tps6507x_ts *tsc = tps6507x_dev->ts;
|
struct tps6507x_ts *tsc = tps6507x_dev->ts;
|
||||||
struct input_dev *input_dev = tsc->input_dev;
|
struct input_dev *input_dev = tsc->input_dev;
|
||||||
|
|
||||||
if (!tsc)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
cancel_delayed_work_sync(&tsc->work);
|
cancel_delayed_work_sync(&tsc->work);
|
||||||
destroy_workqueue(tsc->wq);
|
destroy_workqueue(tsc->wq);
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ struct usbtouch_device_info {
|
|||||||
int (*get_pkt_len) (unsigned char *pkt, int len);
|
int (*get_pkt_len) (unsigned char *pkt, int len);
|
||||||
|
|
||||||
int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
|
int (*read_data) (struct usbtouch_usb *usbtouch, unsigned char *pkt);
|
||||||
|
int (*alloc) (struct usbtouch_usb *usbtouch);
|
||||||
int (*init) (struct usbtouch_usb *usbtouch);
|
int (*init) (struct usbtouch_usb *usbtouch);
|
||||||
void (*exit) (struct usbtouch_usb *usbtouch);
|
void (*exit) (struct usbtouch_usb *usbtouch);
|
||||||
};
|
};
|
||||||
@ -135,7 +136,7 @@ enum {
|
|||||||
DEVTYPE_JASTEC,
|
DEVTYPE_JASTEC,
|
||||||
DEVTYPE_E2I,
|
DEVTYPE_E2I,
|
||||||
DEVTYPE_ZYTRONIC,
|
DEVTYPE_ZYTRONIC,
|
||||||
DEVTYPE_TC5UH,
|
DEVTYPE_TC45USB,
|
||||||
DEVTYPE_NEXIO,
|
DEVTYPE_NEXIO,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -222,8 +223,11 @@ static const struct usb_device_id usbtouch_devices[] = {
|
|||||||
{USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
|
{USB_DEVICE(0x14c8, 0x0003), .driver_info = DEVTYPE_ZYTRONIC},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
|
#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
|
||||||
{USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC5UH},
|
/* TC5UH */
|
||||||
|
{USB_DEVICE(0x0664, 0x0309), .driver_info = DEVTYPE_TC45USB},
|
||||||
|
/* TC4UM */
|
||||||
|
{USB_DEVICE(0x0664, 0x0306), .driver_info = DEVTYPE_TC45USB},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
|
#ifdef CONFIG_TOUCHSCREEN_USB_NEXIO
|
||||||
@ -507,7 +511,7 @@ static int dmc_tsc10_init(struct usbtouch_usb *usbtouch)
|
|||||||
int ret = -ENOMEM;
|
int ret = -ENOMEM;
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
|
|
||||||
buf = kmalloc(2, GFP_KERNEL);
|
buf = kmalloc(2, GFP_NOIO);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
goto err_nobuf;
|
goto err_nobuf;
|
||||||
/* reset */
|
/* reset */
|
||||||
@ -574,10 +578,10 @@ static int irtouch_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
* ET&T TC5UH part
|
* ET&T TC5UH/TC4UM part
|
||||||
*/
|
*/
|
||||||
#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
|
#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
|
||||||
static int tc5uh_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
static int tc45usb_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
|
||||||
{
|
{
|
||||||
dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
|
dev->x = ((pkt[2] & 0x0F) << 8) | pkt[1];
|
||||||
dev->y = ((pkt[4] & 0x0F) << 8) | pkt[3];
|
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)
|
static int nexio_init(struct usbtouch_usb *usbtouch)
|
||||||
{
|
{
|
||||||
struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
|
struct usb_device *dev = interface_to_usbdev(usbtouch->interface);
|
||||||
struct usb_host_interface *interface = usbtouch->interface->cur_altsetting;
|
struct usb_host_interface *interface = usbtouch->interface->cur_altsetting;
|
||||||
struct nexio_priv *priv;
|
struct nexio_priv *priv = usbtouch->priv;
|
||||||
int ret = -ENOMEM;
|
int ret = -ENOMEM;
|
||||||
int actual_len, i;
|
int actual_len, i;
|
||||||
unsigned char *buf;
|
unsigned char *buf;
|
||||||
@ -755,7 +791,7 @@ static int nexio_init(struct usbtouch_usb *usbtouch)
|
|||||||
if (!input_ep || !output_ep)
|
if (!input_ep || !output_ep)
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
|
|
||||||
buf = kmalloc(NEXIO_BUFSIZE, GFP_KERNEL);
|
buf = kmalloc(NEXIO_BUFSIZE, GFP_NOIO);
|
||||||
if (!buf)
|
if (!buf)
|
||||||
goto out_buf;
|
goto out_buf;
|
||||||
|
|
||||||
@ -787,11 +823,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch)
|
|||||||
switch (buf[0]) {
|
switch (buf[0]) {
|
||||||
case 0x83: /* firmware version */
|
case 0x83: /* firmware version */
|
||||||
if (!firmware_ver)
|
if (!firmware_ver)
|
||||||
firmware_ver = kstrdup(&buf[2], GFP_KERNEL);
|
firmware_ver = kstrdup(&buf[2], GFP_NOIO);
|
||||||
break;
|
break;
|
||||||
case 0x84: /* device name */
|
case 0x84: /* device name */
|
||||||
if (!device_name)
|
if (!device_name)
|
||||||
device_name = kstrdup(&buf[2], GFP_KERNEL);
|
device_name = kstrdup(&buf[2], GFP_NOIO);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -802,36 +838,11 @@ static int nexio_init(struct usbtouch_usb *usbtouch)
|
|||||||
kfree(firmware_ver);
|
kfree(firmware_ver);
|
||||||
kfree(device_name);
|
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),
|
usb_fill_bulk_urb(priv->ack, dev, usb_sndbulkpipe(dev, output_ep),
|
||||||
priv->ack_buf, sizeof(nexio_ack_pkt),
|
priv->ack_buf, sizeof(nexio_ack_pkt),
|
||||||
nexio_ack_complete, usbtouch);
|
nexio_ack_complete, usbtouch);
|
||||||
ret = 0;
|
ret = 0;
|
||||||
goto out_buf;
|
|
||||||
|
|
||||||
err_ack_buf:
|
|
||||||
kfree(priv->ack_buf);
|
|
||||||
err_priv:
|
|
||||||
kfree(priv);
|
|
||||||
out_buf:
|
out_buf:
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return ret;
|
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)
|
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_touch_packet *packet = (void *) pkt;
|
||||||
struct nexio_priv *priv = usbtouch->priv;
|
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? */
|
/* got touch data? */
|
||||||
if ((pkt[0] & 0xe0) != 0xe0)
|
if ((pkt[0] & 0xe0) != 0xe0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (be16_to_cpu(packet->data_len) > 0xff)
|
if (data_len > 0xff)
|
||||||
packet->data_len = cpu_to_be16(be16_to_cpu(packet->data_len) - 0x100);
|
data_len -= 0x100;
|
||||||
if (be16_to_cpu(packet->x_len) > 0xff)
|
if (x_len > 0xff)
|
||||||
packet->x_len = cpu_to_be16(be16_to_cpu(packet->x_len) - 0x80);
|
x_len -= 0x80;
|
||||||
|
|
||||||
/* send ACK */
|
/* send ACK */
|
||||||
ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
|
ret = usb_submit_urb(priv->ack, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!usbtouch->type->max_xc) {
|
if (!usbtouch->type->max_xc) {
|
||||||
usbtouch->type->max_xc = 2 * be16_to_cpu(packet->x_len);
|
usbtouch->type->max_xc = 2 * x_len;
|
||||||
input_set_abs_params(usbtouch->input, ABS_X, 0,
|
input_set_abs_params(usbtouch->input, ABS_X,
|
||||||
2 * be16_to_cpu(packet->x_len), 0, 0);
|
0, usbtouch->type->max_xc, 0, 0);
|
||||||
usbtouch->type->max_yc = 2 * be16_to_cpu(packet->y_len);
|
usbtouch->type->max_yc = 2 * y_len;
|
||||||
input_set_abs_params(usbtouch->input, ABS_Y, 0,
|
input_set_abs_params(usbtouch->input, ABS_Y,
|
||||||
2 * be16_to_cpu(packet->y_len), 0, 0);
|
0, usbtouch->type->max_yc, 0, 0);
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* The device reports state of IR sensors on X and Y axes.
|
* 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.
|
* it's disabled (and untested) here as there's no X driver for that.
|
||||||
*/
|
*/
|
||||||
begin_x = end_x = begin_y = end_y = -1;
|
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) {
|
if (begin_x == -1 && packet->data[x] > NEXIO_THRESHOLD) {
|
||||||
begin_x = x;
|
begin_x = x;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) {
|
if (end_x == -1 && begin_x != -1 && packet->data[x] < NEXIO_THRESHOLD) {
|
||||||
end_x = x - 1;
|
end_x = x - 1;
|
||||||
for (y = be16_to_cpu(packet->x_len);
|
for (y = x_len; y < data_len; y++) {
|
||||||
y < be16_to_cpu(packet->data_len); y++) {
|
|
||||||
if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) {
|
if (begin_y == -1 && packet->data[y] > NEXIO_THRESHOLD) {
|
||||||
begin_y = y - be16_to_cpu(packet->x_len);
|
begin_y = y - x_len;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (end_y == -1 &&
|
if (end_y == -1 &&
|
||||||
begin_y != -1 && packet->data[y] < NEXIO_THRESHOLD) {
|
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;
|
w = end_x - begin_x;
|
||||||
h = end_y - begin_y;
|
h = end_y - begin_y;
|
||||||
#if 0
|
#if 0
|
||||||
@ -1104,14 +1117,14 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
|
|||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC5UH
|
#ifdef CONFIG_TOUCHSCREEN_USB_ETT_TC45USB
|
||||||
[DEVTYPE_TC5UH] = {
|
[DEVTYPE_TC45USB] = {
|
||||||
.min_xc = 0x0,
|
.min_xc = 0x0,
|
||||||
.max_xc = 0x0fff,
|
.max_xc = 0x0fff,
|
||||||
.min_yc = 0x0,
|
.min_yc = 0x0,
|
||||||
.max_yc = 0x0fff,
|
.max_yc = 0x0fff,
|
||||||
.rept_size = 5,
|
.rept_size = 5,
|
||||||
.read_data = tc5uh_read_data,
|
.read_data = tc45usb_read_data,
|
||||||
},
|
},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1120,6 +1133,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
|
|||||||
.rept_size = 1024,
|
.rept_size = 1024,
|
||||||
.irq_always = true,
|
.irq_always = true,
|
||||||
.read_data = nexio_read_data,
|
.read_data = nexio_read_data,
|
||||||
|
.alloc = nexio_alloc,
|
||||||
.init = nexio_init,
|
.init = nexio_init,
|
||||||
.exit = nexio_exit,
|
.exit = nexio_exit,
|
||||||
},
|
},
|
||||||
@ -1263,6 +1277,7 @@ static void usbtouch_irq(struct urb *urb)
|
|||||||
usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
|
usbtouch->type->process_pkt(usbtouch, usbtouch->data, urb->actual_length);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
usb_mark_last_busy(interface_to_usbdev(usbtouch->interface));
|
||||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||||
if (retval)
|
if (retval)
|
||||||
err("%s - usb_submit_urb failed with result: %d",
|
err("%s - usb_submit_urb failed with result: %d",
|
||||||
@ -1272,25 +1287,89 @@ exit:
|
|||||||
static int usbtouch_open(struct input_dev *input)
|
static int usbtouch_open(struct input_dev *input)
|
||||||
{
|
{
|
||||||
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
|
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
|
||||||
|
int r;
|
||||||
|
|
||||||
usbtouch->irq->dev = interface_to_usbdev(usbtouch->interface);
|
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 (!usbtouch->type->irq_always) {
|
||||||
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL))
|
if (usb_submit_urb(usbtouch->irq, GFP_KERNEL)) {
|
||||||
return -EIO;
|
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)
|
static void usbtouch_close(struct input_dev *input)
|
||||||
{
|
{
|
||||||
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
|
struct usbtouch_usb *usbtouch = input_get_drvdata(input);
|
||||||
|
int r;
|
||||||
|
|
||||||
if (!usbtouch->type->irq_always)
|
if (!usbtouch->type->irq_always)
|
||||||
usb_kill_urb(usbtouch->irq);
|
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,
|
static void usbtouch_free_buffers(struct usb_device *udev,
|
||||||
struct usbtouch_usb *usbtouch)
|
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_dma = usbtouch->data_dma;
|
||||||
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
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) {
|
if (type->init) {
|
||||||
err = type->init(usbtouch);
|
err = type->init(usbtouch);
|
||||||
if (err) {
|
if (err) {
|
||||||
dbg("%s - type->init() failed, err: %d", __func__, 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);
|
usb_set_intfdata(intf, usbtouch);
|
||||||
|
|
||||||
if (usbtouch->type->irq_always) {
|
if (usbtouch->type->irq_always) {
|
||||||
|
/* this can't fail */
|
||||||
|
usb_autopm_get_interface(intf);
|
||||||
err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);
|
err = usb_submit_urb(usbtouch->irq, GFP_KERNEL);
|
||||||
if (err) {
|
if (err) {
|
||||||
|
usb_autopm_put_interface(intf);
|
||||||
err("%s - usb_submit_urb failed with result: %d",
|
err("%s - usb_submit_urb failed with result: %d",
|
||||||
__func__, err);
|
__func__, err);
|
||||||
goto out_unregister_input;
|
goto out_unregister_input;
|
||||||
@ -1481,7 +1572,11 @@ static struct usb_driver usbtouch_driver = {
|
|||||||
.name = "usbtouchscreen",
|
.name = "usbtouchscreen",
|
||||||
.probe = usbtouch_probe,
|
.probe = usbtouch_probe,
|
||||||
.disconnect = usbtouch_disconnect,
|
.disconnect = usbtouch_disconnect,
|
||||||
|
.suspend = usbtouch_suspend,
|
||||||
|
.resume = usbtouch_resume,
|
||||||
|
.reset_resume = usbtouch_reset_resume,
|
||||||
.id_table = usbtouch_devices,
|
.id_table = usbtouch_devices,
|
||||||
|
.supports_autosuspend = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init usbtouch_init(void)
|
static int __init usbtouch_init(void)
|
||||||
|
@ -78,6 +78,40 @@
|
|||||||
|
|
||||||
#define ADP5588_KEYMAPSIZE 80
|
#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 {
|
struct adp5588_kpad_platform_data {
|
||||||
int rows; /* Number of rows */
|
int rows; /* Number of rows */
|
||||||
int cols; /* Number of columns */
|
int cols; /* Number of columns */
|
||||||
@ -87,6 +121,9 @@ struct adp5588_kpad_platform_data {
|
|||||||
unsigned en_keylock:1; /* Enable Key Lock feature */
|
unsigned en_keylock:1; /* Enable Key Lock feature */
|
||||||
unsigned short unlock_key1; /* Unlock Key 1 */
|
unsigned short unlock_key1; /* Unlock Key 1 */
|
||||||
unsigned short unlock_key2; /* Unlock Key 2 */
|
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 {
|
struct adp5588_gpio_platform_data {
|
||||||
|
34
include/linux/i2c/mcs.h
Normal file
34
include/linux/i2c/mcs.h
Normal file
@ -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 */
|
|
38
include/linux/i2c/qt602240_ts.h
Normal file
38
include/linux/i2c/qt602240_ts.h
Normal file
@ -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_X 0x1a
|
||||||
#define ABS_TILT_Y 0x1b
|
#define ABS_TILT_Y 0x1b
|
||||||
#define ABS_TOOL_WIDTH 0x1c
|
#define ABS_TOOL_WIDTH 0x1c
|
||||||
|
|
||||||
#define ABS_VOLUME 0x20
|
#define ABS_VOLUME 0x20
|
||||||
|
|
||||||
#define ABS_MISC 0x28
|
#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_MAJOR 0x30 /* Major axis of touching ellipse */
|
||||||
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
|
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
|
||||||
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
|
#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_TRACKING_ID 0x39 /* Unique ID of initiated contact */
|
||||||
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
|
#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_MAX 0x3f
|
||||||
#define ABS_CNT (ABS_MAX+1)
|
#define ABS_CNT (ABS_MAX+1)
|
||||||
|
|
||||||
@ -1047,6 +1056,14 @@ struct ff_effect {
|
|||||||
#include <linux/timer.h>
|
#include <linux/timer.h>
|
||||||
#include <linux/mod_devicetable.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
|
* struct input_dev - represents an input device
|
||||||
* @name: name of the device
|
* @name: name of the device
|
||||||
@ -1063,6 +1080,10 @@ struct ff_effect {
|
|||||||
* @sndbit: bitmap of sound effects supported by the device
|
* @sndbit: bitmap of sound effects supported by the device
|
||||||
* @ffbit: bitmap of force feedback effects supported by the device
|
* @ffbit: bitmap of force feedback effects supported by the device
|
||||||
* @swbit: bitmap of switches present on 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
|
* @keycodemax: size of keycode table
|
||||||
* @keycodesize: size of elements in keycode table
|
* @keycodesize: size of elements in keycode table
|
||||||
* @keycode: map of scancodes to keycodes for this device
|
* @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
|
* @repeat_key: stores key code of the last key pressed; used to implement
|
||||||
* software autorepeat
|
* software autorepeat
|
||||||
* @timer: timer for 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
|
* @abs: current values for reports from absolute axes
|
||||||
* @rep: current values for autorepeat parameters (delay, rate)
|
* @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
|
* @key: reflects current state of device's keys/buttons
|
||||||
* @led: reflects current state of device's LEDs
|
* @led: reflects current state of device's LEDs
|
||||||
* @snd: reflects current state of sound effects
|
* @snd: reflects current state of sound effects
|
||||||
@ -1119,6 +1143,7 @@ struct ff_effect {
|
|||||||
* last user closes the device
|
* last user closes the device
|
||||||
* @going_away: marks devices that are in a middle of unregistering and
|
* @going_away: marks devices that are in a middle of unregistering and
|
||||||
* causes input_open_device*() fail with -ENODEV.
|
* 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
|
* @dev: driver model's view of this device
|
||||||
* @h_list: list of input handles associated with the device. When
|
* @h_list: list of input handles associated with the device. When
|
||||||
* accessing the list dev->mutex must be held
|
* 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 ffbit[BITS_TO_LONGS(FF_CNT)];
|
||||||
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
|
unsigned long swbit[BITS_TO_LONGS(SW_CNT)];
|
||||||
|
|
||||||
|
unsigned int hint_events_per_packet;
|
||||||
|
|
||||||
unsigned int keycodemax;
|
unsigned int keycodemax;
|
||||||
unsigned int keycodesize;
|
unsigned int keycodesize;
|
||||||
void *keycode;
|
void *keycode;
|
||||||
@ -1153,11 +1180,13 @@ struct input_dev {
|
|||||||
unsigned int repeat_key;
|
unsigned int repeat_key;
|
||||||
struct timer_list timer;
|
struct timer_list timer;
|
||||||
|
|
||||||
int sync;
|
|
||||||
|
|
||||||
int abs[ABS_CNT];
|
int abs[ABS_CNT];
|
||||||
int rep[REP_MAX + 1];
|
int rep[REP_MAX + 1];
|
||||||
|
|
||||||
|
struct input_mt_slot *mt;
|
||||||
|
int mtsize;
|
||||||
|
int slot;
|
||||||
|
|
||||||
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
|
unsigned long key[BITS_TO_LONGS(KEY_CNT)];
|
||||||
unsigned long led[BITS_TO_LONGS(LED_CNT)];
|
unsigned long led[BITS_TO_LONGS(LED_CNT)];
|
||||||
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
|
unsigned long snd[BITS_TO_LONGS(SND_CNT)];
|
||||||
@ -1182,6 +1211,8 @@ struct input_dev {
|
|||||||
unsigned int users;
|
unsigned int users;
|
||||||
bool going_away;
|
bool going_away;
|
||||||
|
|
||||||
|
bool sync;
|
||||||
|
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
struct list_head h_list;
|
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);
|
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);
|
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)
|
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;
|
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 input_ff_create_memless(struct input_dev *dev, void *data,
|
||||||
int (*play_effect)(struct input_dev *, void *, struct ff_effect *));
|
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
|
||||||
#endif
|
#endif
|
||||||
|
349
include/linux/input/adxl34x.h
Normal file
349
include/linux/input/adxl34x.h
Normal file
@ -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
|
10
include/linux/input/cy8ctmg110_pdata.h
Normal file
10
include/linux/input/cy8ctmg110_pdata.h
Normal file
@ -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
|
* @col_scan_delay_us: delay, measured in microseconds, that is
|
||||||
* needed before we can keypad after activating column gpio
|
* needed before we can keypad after activating column gpio
|
||||||
* @debounce_ms: debounce interval in milliseconds
|
* @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
|
* @active_low: gpio polarity
|
||||||
* @wakeup: controls whether the device should be set up as wakeup
|
* @wakeup: controls whether the device should be set up as wakeup
|
||||||
* source
|
* source
|
||||||
@ -63,6 +66,9 @@ struct matrix_keypad_platform_data {
|
|||||||
/* key debounce interval in milli-second */
|
/* key debounce interval in milli-second */
|
||||||
unsigned int debounce_ms;
|
unsigned int debounce_ms;
|
||||||
|
|
||||||
|
unsigned int clustered_irq;
|
||||||
|
unsigned int clustered_irq_flags;
|
||||||
|
|
||||||
bool active_low;
|
bool active_low;
|
||||||
bool wakeup;
|
bool wakeup;
|
||||||
bool no_autorepeat;
|
bool no_autorepeat;
|
||||||
|
@ -48,11 +48,12 @@ struct ads7846_platform_data {
|
|||||||
* state if get_pendown_state == NULL
|
* state if get_pendown_state == NULL
|
||||||
*/
|
*/
|
||||||
int (*get_pendown_state)(void);
|
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);
|
void **filter_data);
|
||||||
int (*filter) (void *filter_data, int data_idx, int *val);
|
int (*filter) (void *filter_data, int data_idx, int *val);
|
||||||
void (*filter_cleanup)(void *filter_data);
|
void (*filter_cleanup)(void *filter_data);
|
||||||
void (*wait_for_sync)(void);
|
void (*wait_for_sync)(void);
|
||||||
bool wakeup;
|
bool wakeup;
|
||||||
|
unsigned long irq_flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user