[Armadeus-commitlog] SF.net SVN: armadeus: [611] trunk/buildroot/target/device/armadeus
Brought to you by:
sszy
|
From: <ar...@us...> - 2007-07-14 15:29:43
|
Revision: 611
http://armadeus.svn.sourceforge.net/armadeus/?rev=611&view=rev
Author: artemys
Date: 2007-07-14 08:29:41 -0700 (Sat, 14 Jul 2007)
Log Message:
-----------
[LINUX] Add TSC2102 core and touchscreen drivers as found on the http://source.mvista.com/git repository
Additionnal Armadeus adaptation patches will be required to compile and use this driver... so don't use it yet :-)
This commit can be seen as the hardware independant part of the stuff
Modified Paths:
--------------
trunk/buildroot/target/device/armadeus/apf9328/linux-2.6.18.1.config
Added Paths:
-----------
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/073-linux-2.6.18-tsc2102.diff
trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/074-linux-2.6.18-tsc2102-touchscreen.diff
Modified: trunk/buildroot/target/device/armadeus/apf9328/linux-2.6.18.1.config
===================================================================
--- trunk/buildroot/target/device/armadeus/apf9328/linux-2.6.18.1.config 2007-07-02 16:53:36 UTC (rev 610)
+++ trunk/buildroot/target/device/armadeus/apf9328/linux-2.6.18.1.config 2007-07-14 15:29:41 UTC (rev 611)
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.18.1
-# Wed Jan 24 12:26:10 2007
+# Sat Jul 14 16:47:01 2007
#
CONFIG_ARM=y
CONFIG_MMU=y
@@ -649,6 +649,7 @@
#
# SPI Protocol Masters
#
+# CONFIG_SPI_TSC2102 is not set
#
# Dallas's 1-wire bus
Added: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/073-linux-2.6.18-tsc2102.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/073-linux-2.6.18-tsc2102.diff (rev 0)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/073-linux-2.6.18-tsc2102.diff 2007-07-14 15:29:41 UTC (rev 611)
@@ -0,0 +1,1419 @@
+From: Andrzej Zaborowski <ba...@za...>
+Date: Mon, 2 Apr 2007 17:15:00 +0000 (-0400)
+Subject: SPI: Add TSC2102 main SPI driver
+X-Git-Url: http://source.mvista.com/git/gitweb.cgi?p=linux-davinci-2.6.git;a=commitdiff;h=05f24ed7f2264675cd4c01bba62374d14db37fe9
+
+SPI: Add TSC2102 main SPI driver
+
+This adds a driver for the TSC2102 chip by Texas Instruments. This
+driver is responsible for all communication with the chip and doesn't
+export anything for user-space, but for other drivers controlling
+devices connected to the chip.
+
+Signed-off-by: Andrzej Zaborowski <ba...@za...>
+---
+
+--- a/drivers/spi/Kconfig
++++ b/drivers/spi/Kconfig
+@@ -131,6 +131,12 @@
+ #
+ # Add new SPI protocol masters in alphabetical order above this line
+ #
++config SPI_TSC2102
++ depends on SPI_MASTER
++ tristate "TSC2102 codec support"
++ ---help---
++ Say Y here if you want support for the TSC2102 chip. It
++ will be needed for the touchscreen driver on some boards.
+
+
+ # (slave support would go here)
+--- a/drivers/spi/Makefile
++++ a/drivers/spi/Makefile
+@@ -21,6 +21,7 @@
+ # ... add above this line ...
+
+ # SPI protocol drivers (device/link on bus)
++obj-$(CONFIG_SPI_TSC2102) += tsc2102.o
+ # ... add above this line ...
+
+ # SPI slave controller drivers (upstream link)
+--- /dev/null
++++ b/drivers/spi/tsc2102.c
+@@ -0,0 +1,1198 @@
++/*
++ * drivers/spi/tsc2102.c
++ *
++ * TSC2102 interface driver.
++ *
++ * Copyright (c) 2005 Andrzej Zaborowski <ba...@za...>
++ *
++ * This package 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 package 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 package; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/suspend.h>
++#include <linux/interrupt.h>
++#include <linux/clk.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/tsc2102.h>
++#include <linux/err.h>
++#include <linux/hwmon.h>
++#include <linux/hwmon-sysfs.h>
++
++#include <asm/system.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++
++#ifdef CONFIG_APM
++#include <asm/apm.h>
++#endif
++
++/* Bit field definitions for chip registers */
++#define TSC2102_ADC_TS_CONTROL 0x8bf4
++#define TSC2102_ADC_SCAN_CONTROL 0x2ff4
++#define TSC2102_ADC_T1_CONTROL 0x2bf4
++#define TSC2102_ADC_T2_CONTROL 0x33f4
++#define TSC2102_ADC_DAV 0x4000
++#define TSC2102_ADC_INT_REF 0x0016
++#define TSC2102_ADC_EXT_REF 0x0002
++#define TSC2102_CONFIG_TIMES 0x0008
++#define TSC2102_RESET 0xbb00
++#define TSC2102_ADC_PSTCM (1 << 15)
++#define TSC2102_ADC_ADST (1 << 14)
++#define TSC2102_TS_DAV 0x0780
++#define TSC2102_PS_DAV 0x0078
++#define TSC2102_T1_DAV 0x0004
++#define TSC2102_T2_DAV 0x0002
++#define TSC2102_DAC_ON 0x3ba0
++#define TSC2102_DAC_OFF 0xafa0
++#define TSC2102_FS44K (1 << 13)
++#define TSC2102_PLL1_OFF 0x0000
++#define TSC2102_PLL1_44K 0x811c
++#define TSC2102_PLL1_48K 0x8120
++#define TSC2102_PLL2_44K (5462 << 2)
++#define TSC2102_PLL2_48K (1920 << 2)
++#define TSC2102_SLVMS (1 << 11)
++#define TSC2102_DEEMPF (1 << 0)
++#define TSC2102_BASSBC (1 << 1)
++#define TSC2102_KEYCLICK_OFF 0x0000
++
++#define CS_CHANGE(val) 0
++
++struct tsc2102_spi_req {
++ struct spi_device *dev;
++ uint16_t command;
++ uint16_t data;
++ struct spi_transfer *transfer;
++ struct spi_message message;
++};
++
++struct tsc2102_dev {
++ struct tsc2102_config *pdata;
++ spinlock_t lock, lock_sync;
++ struct clk *bclk_ck;
++
++ int state; /* 0: TS, 1: Portscan, 2-3: Temps */
++ struct timer_list ts_timer; /* Busy-wait for PEN UP */
++ struct timer_list mode_timer; /* Change .state every some time */
++ int pendown;
++ int data_pending;
++ uint16_t status, adc_status, adc_data[4];
++ tsc2102_touch_t touch_cb;
++ tsc2102_coords_t coords_cb;
++ tsc2102_ports_t ports_cb;
++ tsc2102_temp_t temp1_cb;
++ tsc2102_temp_t temp2_cb;
++ unsigned int ts_msecs; /* Interval for .ts_timer */
++ unsigned int mode_msecs; /* Interval for .mode_timer */
++
++ struct spi_device *spi;
++ struct spi_transfer *transfers;
++ struct tsc2102_spi_req req_adc;
++ struct tsc2102_spi_req req_status;
++ struct tsc2102_spi_req req_pressure;
++ struct tsc2102_spi_req req_stopadc;
++ struct tsc2102_spi_req req_mode;
++
++ int bat[2], aux[1], temp[2];
++ struct class_device *hwmondev;
++};
++
++static struct tsc2102_dev tsc;
++
++module_param_named(touch_check_msecs, tsc.ts_msecs, uint, 0);
++MODULE_PARM_DESC(touch_check_msecs, "Pen-up polling interval in msecs");
++
++module_param_named(sensor_scan_msecs, tsc.mode_msecs, uint, 0);
++MODULE_PARM_DESC(sensor_scan_msecs, "Temperature & battery scan interval");
++
++void tsc2102_write_sync(int page, u8 address, u16 data)
++{
++ static struct tsc2102_spi_req req;
++ static struct spi_transfer transfer[2];
++ int ret;
++
++ spi_message_init(&req.message);
++ req.transfer = transfer;
++
++ /* Address */
++ req.command = (page << 11) | (address << 5);
++ req.transfer[0].tx_buf = &req.command;
++ req.transfer[0].rx_buf = 0;
++ req.transfer[0].len = 2;
++ spi_message_add_tail(&req.transfer[0], &req.message);
++
++ /* Data */
++ req.transfer[1].tx_buf = &data;
++ req.transfer[1].rx_buf = 0;
++ req.transfer[1].len = 2;
++ req.transfer[1].cs_change = CS_CHANGE(1);
++ spi_message_add_tail(&req.transfer[1], &req.message);
++
++ ret = spi_sync(tsc.spi, &req.message);
++ if (!ret && req.message.status)
++ ret = req.message.status;
++
++ if (ret)
++ printk(KERN_ERR "%s: error %i in SPI request\n",
++ __FUNCTION__, ret);
++}
++
++void tsc2102_reads_sync(int page, u8 startaddress, u16 *data, int numregs)
++{
++ static struct tsc2102_spi_req req;
++ static struct spi_transfer transfer[6];
++ int ret, i, j;
++
++ BUG_ON(numregs + 1 > ARRAY_SIZE(transfer));
++
++ spi_message_init(&req.message);
++ req.transfer = transfer;
++ i = 0;
++ j = 0;
++
++ /* Address */
++ req.command = 0x8000 | (page << 11) | (startaddress << 5);
++ req.transfer[i].tx_buf = &req.command;
++ req.transfer[i].rx_buf = 0;
++ req.transfer[i].len = 2;
++ spi_message_add_tail(&req.transfer[i ++], &req.message);
++
++ /* Data */
++ while (j < numregs) {
++ req.transfer[i].tx_buf = 0;
++ req.transfer[i].rx_buf = &data[j ++];
++ req.transfer[i].len = 2;
++ req.transfer[i].cs_change = CS_CHANGE(j == numregs);
++ spi_message_add_tail(&req.transfer[i ++], &req.message);
++ }
++
++ ret = spi_sync(tsc.spi, &req.message);
++ if (!ret && req.message.status)
++ ret = req.message.status;
++
++ if (ret)
++ printk(KERN_ERR "%s: error %i in SPI request\n",
++ __FUNCTION__, ret);
++}
++
++u16 tsc2102_read_sync(int page, u8 address)
++{
++ u16 ret;
++ tsc2102_reads_sync(page, address, &ret, 1);
++ return ret;
++}
++
++static void tsc2102_write_async(
++ struct tsc2102_spi_req *spi, int page, u8 address, u16 data,
++ void (*complete)(struct tsc2102_dev *context))
++{
++ int ret;
++
++ spi_message_init(&spi->message);
++ spi->message.complete = (void (*)(void *)) complete;
++ spi->message.context = &tsc;
++
++ /* Address */
++ spi->command = (page << 11) | (address << 5);
++ spi->transfer[0].tx_buf = &spi->command;
++ spi->transfer[0].rx_buf = 0;
++ spi->transfer[0].len = 2;
++ spi_message_add_tail(&spi->transfer[0], &spi->message);
++
++ /* Data */
++ spi->data = data;
++ spi->transfer[1].tx_buf = &spi->data;
++ spi->transfer[1].rx_buf = 0;
++ spi->transfer[1].len = 2;
++ spi->transfer[1].cs_change = CS_CHANGE(1);
++ spi_message_add_tail(&spi->transfer[1], &spi->message);
++
++ ret = spi_async(spi->dev, &spi->message);
++ if (ret)
++ printk(KERN_ERR "%s: error %i in SPI request\n",
++ __FUNCTION__, ret);
++}
++
++static void tsc2102_reads_async(struct tsc2102_spi_req *spi,
++ int page, u8 startaddress, u16 *data, int numregs,
++ void (*complete)(struct tsc2102_dev *context))
++{
++ int ret, i, j;
++
++ spi_message_init(&spi->message);
++ spi->message.complete = (void (*)(void *)) complete;
++ spi->message.context = &tsc;
++ i = 0;
++ j = 0;
++
++ /* Address */
++ spi->command = 0x8000 | (page << 11) | (startaddress << 5);
++ spi->transfer[i].tx_buf = &spi->command;
++ spi->transfer[i].rx_buf = 0;
++ spi->transfer[i].len = 2;
++ spi_message_add_tail(&spi->transfer[i ++], &spi->message);
++
++ /* Data */
++ while (j < numregs) {
++ spi->transfer[i].tx_buf = 0;
++ spi->transfer[i].rx_buf = &data[j ++];
++ spi->transfer[i].len = 2;
++ spi->transfer[i].cs_change = CS_CHANGE(j == numregs);
++ spi_message_add_tail(&spi->transfer[i ++], &spi->message);
++ }
++
++ ret = spi_async(spi->dev, &spi->message);
++ if (ret)
++ printk(KERN_ERR "%s: error %i in SPI request\n",
++ __FUNCTION__, ret);
++}
++
++static void tsc2102_read_async(struct tsc2102_spi_req *spi,
++ int page, u8 address, u16 *ret,
++ void (*complete)(struct tsc2102_dev *context))
++{
++ tsc2102_reads_async(spi, page, address, ret, 1, complete);
++}
++
++static void tsc2102_request_alloc(struct tsc2102_dev *dev,
++ struct tsc2102_spi_req *spi, int direction, int numregs,
++ struct spi_transfer **buffer)
++{
++ spi->dev = dev->spi;
++
++ if (direction == 1) /* Write */
++ numregs = 2;
++ else /* Read */
++ numregs += 1;
++
++ spi->transfer = *buffer;
++ *buffer += numregs;
++}
++
++#define tsc2102_cb_register_func(cb, cb_t) \
++int tsc2102_ ## cb(cb_t handler) \
++{ \
++ spin_lock(&tsc.lock); \
++ \
++ /* Lock the module */ \
++ if (handler && !tsc.cb) \
++ if (!try_module_get(THIS_MODULE)) { \
++ printk(KERN_INFO "Failed to get TSC module\n"); \
++ } \
++ if (!handler && tsc.cb) \
++ module_put(THIS_MODULE); \
++ \
++ tsc.cb = handler; \
++ \
++ spin_unlock(&tsc.lock); \
++ return 0; \
++}
++
++tsc2102_cb_register_func(touch_cb, tsc2102_touch_t)
++tsc2102_cb_register_func(coords_cb, tsc2102_coords_t)
++tsc2102_cb_register_func(ports_cb, tsc2102_ports_t)
++tsc2102_cb_register_func(temp1_cb, tsc2102_temp_t)
++tsc2102_cb_register_func(temp2_cb, tsc2102_temp_t)
++
++#ifdef DEBUG
++static void tsc2102_print_dav(void)
++{
++ u16 status = tsc2102_read_sync(TSC2102_TS_STATUS_CTRL);
++ if (status & 0x0fff)
++ printk("TSC2102: data in");
++ if (status & 0x0400)
++ printk(" X");
++ if (status & 0x0200)
++ printk(" Y");
++ if (status & 0x0100)
++ printk(" Z1");
++ if (status & 0x0080)
++ printk(" Z2");
++ if (status & 0x0040)
++ printk(" BAT1");
++ if (status & 0x0020)
++ printk(" BAT2");
++ if (status & 0x0010)
++ printk(" AUX1");
++ if (status & 0x0008)
++ printk(" AUX2");
++ if (status & 0x0004)
++ printk(" TEMP1");
++ if (status & 0x0002)
++ printk(" TEMP2");
++ if (status & 0x0001)
++ printk(" KP");
++ if (status & 0x0fff)
++ printk(".\n");
++}
++#endif
++
++static void tsc2102_complete_dummy(struct tsc2102_dev *dev)
++{
++}
++
++static inline void tsc2102_touchscreen_mode(struct tsc2102_dev *dev)
++{
++ /* Scan X, Y, Z1, Z2, chip controlled, 12-bit, 16 samples, 500 usec */
++ tsc2102_write_async(&dev->req_mode,
++ TSC2102_TS_ADC_CTRL, TSC2102_ADC_TS_CONTROL,
++ tsc2102_complete_dummy);
++}
++
++static inline void tsc2102_portscan_mode(struct tsc2102_dev *dev)
++{
++ /* Scan BAT1, BAT2, AUX, 12-bit, 16 samples, 500 usec */
++ tsc2102_write_async(&dev->req_mode,
++ TSC2102_TS_ADC_CTRL, TSC2102_ADC_SCAN_CONTROL,
++ tsc2102_complete_dummy);
++}
++
++static inline void tsc2102_temp1_mode(struct tsc2102_dev *dev)
++{
++ /* Scan TEMP1, 12-bit, 16 samples, 500 usec */
++ tsc2102_write_async(&dev->req_mode,
++ TSC2102_TS_ADC_CTRL, TSC2102_ADC_T1_CONTROL,
++ tsc2102_complete_dummy);
++}
++
++static inline void tsc2102_temp2_mode(struct tsc2102_dev *dev)
++{
++ /* Scan TEMP2, 12-bit, 16 samples, 500 usec */
++ tsc2102_write_async(&dev->req_mode,
++ TSC2102_TS_ADC_CTRL, TSC2102_ADC_T2_CONTROL,
++ tsc2102_complete_dummy);
++}
++
++static void tsc2102_mode(struct tsc2102_dev *dev)
++{
++ switch (dev->state) {
++ case 0:
++ tsc2102_touchscreen_mode(dev);
++ break;
++ case 1:
++ tsc2102_portscan_mode(dev);
++ break;
++ case 2:
++ tsc2102_temp1_mode(dev);
++ break;
++ case 3:
++ tsc2102_temp2_mode(dev);
++ break;
++ default:
++ dev->state = 0;
++ tsc2102_touchscreen_mode(dev);
++ break;
++ }
++}
++
++/* Lock is held when this is called. */
++static void tsc2102_new_mode(struct tsc2102_dev *dev)
++{
++ /* Abort current conversion if any */
++ tsc2102_write_async(&dev->req_stopadc,
++ TSC2102_TS_ADC_CTRL, TSC2102_ADC_ADST,
++ tsc2102_complete_dummy);
++
++ dev->state ++;
++ tsc2102_mode(dev);
++}
++
++static void tsc2102_check_status(struct tsc2102_dev *dev);
++
++/* TSC has new data for us availiable. */
++static irqreturn_t tsc2102_handler(int irq, void *dev_id)
++{
++ struct tsc2102_dev *dev = (struct tsc2102_dev *) dev_id;
++ spin_lock_irq(&dev->lock);
++
++ if (!dev->data_pending)
++ tsc2102_check_status(dev);
++
++ dev->data_pending ++;
++
++ spin_unlock_irq(&dev->lock);
++ return IRQ_HANDLED;
++}
++
++static void tsc2102_data_report(struct tsc2102_dev *dev)
++{
++ if (dev->status & TSC2102_TS_DAV) {
++ if (dev->coords_cb)
++ dev->coords_cb(
++ dev->adc_data[0], dev->adc_data[1],
++ dev->adc_data[2], dev->adc_data[3]);
++ }
++
++ if (dev->status & TSC2102_PS_DAV) {
++ if (dev->ports_cb)
++ dev->ports_cb(dev->adc_data[0],
++ dev->adc_data[1], dev->adc_data[2]);
++ dev->bat[0] = dev->adc_data[0];
++ dev->bat[1] = dev->adc_data[1];
++ dev->aux[0] = dev->adc_data[2];
++ }
++
++ if (dev->status & TSC2102_T1_DAV) {
++ if (dev->temp1_cb)
++ dev->temp1_cb(*dev->adc_data);
++ dev->temp[0] = *dev->adc_data;
++ }
++
++ if (dev->status & TSC2102_T2_DAV) {
++ if (dev->temp2_cb)
++ dev->temp2_cb(*dev->adc_data);
++ dev->temp[1] = *dev->adc_data;
++ }
++
++ spin_lock_irq(&dev->lock);
++
++ dev->data_pending --;
++
++ /*
++ * This may happen if the registers were successfully read and a
++ * new conversion was started and completed by the TSC before the
++ * completion for SPI read was called.
++ */
++ if (dev->data_pending)
++ tsc2102_check_status(dev);
++
++ if (dev->status & (TSC2102_PS_DAV | TSC2102_T1_DAV | TSC2102_T2_DAV))
++ tsc2102_new_mode(dev);
++
++ spin_unlock_irq(&dev->lock);
++}
++
++static void tsc2102_status_report(struct tsc2102_dev *dev)
++{
++ /*
++ * Read all converted data from corresponding registers
++ * so that the ADC can move on to a new conversion.
++ */
++ if (dev->status & TSC2102_TS_DAV) {
++ tsc2102_reads_async(&dev->req_adc, TSC2102_TS_X,
++ dev->adc_data, 4, tsc2102_data_report);
++ if (!dev->pendown) {
++ dev->pendown = 1;
++ if (dev->touch_cb)
++ dev->touch_cb(1);
++
++ mod_timer(&dev->ts_timer, jiffies +
++ msecs_to_jiffies(dev->ts_msecs));
++ }
++ }
++
++ if (dev->status & TSC2102_PS_DAV) {
++ tsc2102_reads_async(&dev->req_adc, TSC2102_TS_BAT1,
++ dev->adc_data, 3, tsc2102_data_report);
++ }
++
++ if (dev->status & TSC2102_T1_DAV) {
++ tsc2102_read_async(&dev->req_adc, TSC2102_TS_TEMP1,
++ dev->adc_data, tsc2102_data_report);
++ }
++
++ if (dev->status & TSC2102_T2_DAV) {
++ tsc2102_read_async(&dev->req_adc, TSC2102_TS_TEMP2,
++ dev->adc_data, tsc2102_data_report);
++ }
++
++ if (!(dev->status & (TSC2102_TS_DAV | TSC2102_PS_DAV |
++ TSC2102_T1_DAV | TSC2102_T2_DAV))) {
++ spin_lock_irq(&dev->lock);
++ dev->data_pending --;
++ spin_unlock_irq(&dev->lock);
++
++ WARN_ON(!dev->state);
++ }
++}
++
++static void tsc2102_check_status(struct tsc2102_dev *dev)
++{
++ tsc2102_read_async(&dev->req_status, TSC2102_TS_STATUS_CTRL,
++ &dev->status, tsc2102_status_report);
++}
++
++static void tsc2102_mode_timer(unsigned long data)
++{
++ struct tsc2102_dev *dev = (struct tsc2102_dev *) data;
++ spin_lock_irq(&dev->lock);
++
++ BUG_ON(dev->state);
++
++ tsc2102_new_mode(dev);
++
++ mod_timer(&dev->mode_timer, jiffies +
++ msecs_to_jiffies(dev->mode_msecs));
++ spin_unlock_irq(&dev->lock);
++}
++
++/*
++ * There are at least three ways to check for pen-up:
++ * - the PINT/DAV pin state,
++ * - reading PSTCM bit in ADC Control register (D15, offset 0x00),
++ * - reading ADST bit in ADC Control register (D14, offset 0x00),
++ * ADC idle would indicate no screen touch.
++ * Unfortunately none of them seems to be 100% accurate and you will
++ * find they are totally inconsistent, i.e. you get to see any arbitrary
++ * combination of values in these three bits. So we will busy-wait
++ * for a moment when all three indicate a pen-up, using a timer, before
++ * we report a pen-up.
++ */
++static void tsc2102_pressure_report(struct tsc2102_dev *dev)
++{
++ if (!dev->pendown)
++ return;
++
++ if (dev->state ||
++ (dev->adc_status & TSC2102_ADC_PSTCM) ||
++ !(dev->adc_status & TSC2102_ADC_ADST)) {
++ mod_timer(&dev->ts_timer, jiffies +
++ msecs_to_jiffies(dev->ts_msecs));
++ } else {
++ dev->pendown = 0;
++ if (dev->touch_cb)
++ dev->touch_cb(0);
++ }
++}
++
++static void tsc2102_pressure(unsigned long data)
++{
++ struct tsc2102_dev *dev = (struct tsc2102_dev *) data;
++
++ BUG_ON(!dev->pendown);
++
++ tsc2102_read_async(&dev->req_pressure, TSC2102_TS_ADC_CTRL,
++ &dev->adc_status, tsc2102_pressure_report);
++}
++
++#ifdef CONFIG_SOUND
++/*
++ * Volume level values should be in the range [0, 127].
++ * Higher values mean lower volume.
++ */
++void tsc2102_set_volume(uint8_t left_ch, uint8_t right_ch)
++{
++ u16 val;
++ if (left_ch == 0x00 || left_ch == 0x7f) /* All 0's or all 1's */
++ left_ch ^= 0x7f;
++ if (right_ch == 0x00 || right_ch == 0x7f)
++ right_ch ^= 0x7f;
++
++ spin_lock(&tsc.lock_sync);
++
++ val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL);
++
++ val &= 0x8080; /* Preserve mute-bits */
++ val |= (left_ch << 8) | right_ch;
++
++ tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, val);
++
++ spin_unlock(&tsc.lock_sync);
++}
++
++void tsc2102_set_mute(int left_ch, int right_ch)
++{
++ u16 val;
++ spin_lock(&tsc.lock_sync);
++
++ val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL);
++
++ val &= 0x7f7f; /* Preserve volume settings */
++ val |= (left_ch << 15) | (right_ch << 7);
++
++ tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, val);
++
++ spin_unlock(&tsc.lock_sync);
++}
++
++void tsc2102_get_mute(int *left_ch, int *right_ch)
++{
++ u16 val;
++ spin_lock(&tsc.lock_sync);
++
++ val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL);
++
++ spin_unlock(&tsc.lock_sync);
++
++ *left_ch = !!(val & (1 << 15));
++ *right_ch = !!(val & (1 << 7));
++}
++
++void tsc2102_set_deemphasis(int enable)
++{
++ u16 val;
++ spin_lock(&tsc.lock_sync);
++ val = tsc2102_read_sync(TSC2102_DAC_POWER_CTRL);
++
++ if (enable)
++ val &= ~TSC2102_DEEMPF;
++ else
++ val |= TSC2102_DEEMPF;
++
++ tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, val);
++ spin_unlock(&tsc.lock_sync);
++}
++
++void tsc2102_set_bassboost(int enable)
++{
++ u16 val;
++ spin_lock(&tsc.lock_sync);
++ val = tsc2102_read_sync(TSC2102_DAC_POWER_CTRL);
++
++ if (enable)
++ val &= ~TSC2102_BASSBC;
++ else
++ val |= TSC2102_BASSBC;
++
++ tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, val);
++ spin_unlock(&tsc.lock_sync);
++}
++
++/* {rate, dsor, fsref} */
++static const struct tsc2102_rate_info_s tsc2102_rates[] = {
++ /* Fsref / 6.0 */
++ {7350, 63, 1},
++ {8000, 63, 0},
++ /* Fsref / 6.0 */
++ {7350, 54, 1},
++ {8000, 54, 0},
++ /* Fsref / 5.0 */
++ {8820, 45, 1},
++ {9600, 45, 0},
++ /* Fsref / 4.0 */
++ {11025, 36, 1},
++ {12000, 36, 0},
++ /* Fsref / 3.0 */
++ {14700, 27, 1},
++ {16000, 27, 0},
++ /* Fsref / 2.0 */
++ {22050, 18, 1},
++ {24000, 18, 0},
++ /* Fsref / 1.5 */
++ {29400, 9, 1},
++ {32000, 9, 0},
++ /* Fsref */
++ {44100, 0, 1},
++ {48000, 0, 0},
++
++ {0, 0, 0},
++};
++
++int tsc2102_set_rate(int rate)
++{
++ int i;
++ uint16_t val;
++
++ for (i = 0; tsc2102_rates[i].sample_rate; i ++)
++ if (tsc2102_rates[i].sample_rate == rate)
++ break;
++ if (tsc2102_rates[i].sample_rate == 0) {
++ printk(KERN_ERR "Unknown sampling rate %i.0 Hz\n", rate);
++ return -EINVAL;
++ }
++
++ spin_lock(&tsc.lock_sync);
++
++ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, tsc2102_rates[i].divisor);
++
++ val = tsc2102_read_sync(TSC2102_AUDIO3_CTRL);
++
++ if (tsc2102_rates[i].fs_44k) {
++ tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val | TSC2102_FS44K);
++ /* Enable Phase-locked-loop, set up clock dividers */
++ tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_44K);
++ tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_44K);
++ } else {
++ tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val & ~TSC2102_FS44K);
++ /* Enable Phase-locked-loop, set up clock dividers */
++ tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_48K);
++ tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_48K);
++ }
++
++ spin_unlock(&tsc.lock_sync);
++ return 0;
++}
++
++/*
++ * Perform basic set-up with default values and power the DAC on.
++ */
++void tsc2102_dac_power(int state)
++{
++ spin_lock(&tsc.lock_sync);
++
++ if (state) {
++ /* 16-bit words, DSP mode, sample at Fsref */
++ tsc2102_write_sync(TSC2102_AUDIO1_CTRL, 0x0100);
++ /* Keyclicks off, soft-stepping at normal rate */
++ tsc2102_write_sync(TSC2102_AUDIO2_CTRL, TSC2102_KEYCLICK_OFF);
++ /* 44.1 kHz Fsref, continuous transfer mode, master DAC */
++ tsc2102_write_sync(TSC2102_AUDIO3_CTRL, 0x2800);
++ /* Soft-stepping enabled */
++ tsc2102_write_sync(TSC2102_AUDIO4_CTRL, 0x0000);
++
++ /* PLL generates 44.1 kHz */
++ tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_44K);
++ tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_44K);
++
++ /* Codec & DAC power up, virtual ground disabled */
++ tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, TSC2102_DAC_ON);
++ } else {
++ /* All off */
++ tsc2102_write_sync(TSC2102_AUDIO4_CTRL, TSC2102_KEYCLICK_OFF);
++ tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_OFF);
++ }
++
++ spin_unlock(&tsc.lock_sync);
++}
++
++void tsc2102_set_i2s_master(int state)
++{
++ uint16_t val;
++ spin_lock(&tsc.lock_sync);
++
++ val = tsc2102_read_sync(TSC2102_AUDIO3_CTRL);
++
++ if (state)
++ tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val | TSC2102_SLVMS);
++ else
++ tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val & ~TSC2102_SLVMS);
++
++ spin_unlock(&tsc.lock_sync);
++}
++#endif /* CONFIG_SOUND */
++
++static int tsc2102_configure(struct tsc2102_dev *dev)
++{
++ /* Reset the chip */
++ tsc2102_write_sync(TSC2102_TS_RESET_CTRL, TSC2102_RESET);
++
++ /* Reference mode, 100 usec delay, 1.25 V reference */
++ if (dev->pdata->use_internal)
++ tsc2102_write_sync(TSC2102_TS_REF_CTRL, TSC2102_ADC_INT_REF);
++ else
++ tsc2102_write_sync(TSC2102_TS_REF_CTRL, TSC2102_ADC_EXT_REF);
++
++ /* 84 usec precharge time, 32 usec sense time */
++ tsc2102_write_sync(TSC2102_TS_CONFIG_CTRL, TSC2102_CONFIG_TIMES);
++
++ /* PINT/DAV acts as DAV */
++ tsc2102_write_sync(TSC2102_TS_STATUS_CTRL, TSC2102_ADC_DAV);
++
++ tsc2102_mode(dev);
++ mod_timer(&dev->mode_timer, jiffies +
++ msecs_to_jiffies(dev->mode_msecs));
++ return 0;
++}
++
++/*
++ * Retrieves chip revision. Should be always 1.
++ */
++int tsc2102_get_revision(void)
++{
++ return tsc2102_read_sync(TSC2102_AUDIO3_CTRL) & 7;
++}
++
++/*
++ * Emit a short keyclick typically in order to give feedback to
++ * user on specific events.
++ *
++ * amplitude must be between 0 (lowest) and 2 (highest).
++ * freq must be between 0 (corresponds to 62.5 Hz) and 7 (8 kHz).
++ * length should be between 2 and 32 periods.
++ *
++ * This function sleeps but doesn't sleep until the sound has
++ * finished.
++ */
++void tsc2102_keyclick(int amplitude, int freq, int length)
++{
++ u16 val;
++ spin_lock(&tsc.lock_sync);
++ val = tsc2102_read_sync(TSC2102_AUDIO2_CTRL);
++ val &= 0x800f;
++
++ /* Set amplitude */
++ switch (amplitude) {
++ case 1:
++ val |= 4 << 12;
++ break;
++ case 2:
++ val |= 7 << 12;
++ break;
++ default:
++ break;
++ }
++
++ /* Frequency */
++ val |= (freq & 0x7) << 8;
++
++ /* Round to nearest supported length */
++ if (length > 20)
++ val |= 4 << 4;
++ else if (length > 6)
++ val |= 3 << 4;
++ else if (length > 4)
++ val |= 2 << 4;
++ else if (length > 2)
++ val |= 1 << 4;
++
++ /* Enable keyclick */
++ val |= 0x8000;
++
++ tsc2102_write_sync(TSC2102_AUDIO2_CTRL, val);
++ spin_unlock(&tsc.lock_sync);
++}
++
++#ifdef CONFIG_HWMON
++#define TSC2102_INPUT(devname, field) \
++static ssize_t show_ ## devname(struct device *dev, \
++ struct device_attribute *devattr, char *buf) \
++{ \
++ struct tsc2102_dev *devhwmon = dev_get_drvdata(dev); \
++ int value = devhwmon->field; \
++ return sprintf(buf, "%i\n", value); \
++} \
++static DEVICE_ATTR(devname ## _input, S_IRUGO, show_ ## devname, NULL);
++
++TSC2102_INPUT(in0, bat[0])
++TSC2102_INPUT(in1, bat[1])
++TSC2102_INPUT(in2, aux[0])
++TSC2102_INPUT(in3, temp[0])
++TSC2102_INPUT(in4, temp[1])
++
++static ssize_t show_temp1(struct device *dev,
++ struct device_attribute *devattr, char *buf)
++{
++ struct tsc2102_dev *devhwmon = dev_get_drvdata(dev);
++ int t1, t2;
++ int value, diff;
++
++ t1 = devhwmon->temp[0];
++ t2 = devhwmon->temp[1];
++
++ /*
++ * Use method #2 (differential) to calculate current temperature.
++ * The difference between TEMP2 and TEMP1 input values is
++ * multiplied by a constant to obtain current temperature.
++ * To find this constant we use the values measured at 25 C as
++ * thermometer calibration data.
++ *
++ * 298150 is 25 degrees Celcius represented in Kelvins and
++ * multiplied by 1000 for fixed point precision (273.15 + 25).
++ * 273150 is zero degrees Celcius.
++ */
++ diff = devhwmon->pdata->temp_at25c[1] - devhwmon->pdata->temp_at25c[0];
++ BUG_ON(diff == 0);
++ value = (t2 - t1) * 298150 / diff; /* This is in Kelvins now */
++
++ t1 = value - 273150; /* Celcius millidegree */
++ return sprintf(buf, "%i\n", t1);
++}
++static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1, NULL);
++#endif /* CONFIG_HWMON */
++
++#ifdef CONFIG_APM
++static void tsc2102_get_power_status(struct apm_power_info *info)
++{
++ tsc.pdata->apm_report(info, tsc.bat);
++}
++#endif
++
++#ifdef CONFIG_PM
++/*
++ * Suspend the chip.
++ */
++static int
++tsc2102_suspend(struct spi_device *spi, pm_message_t state)
++{
++ struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev);
++
++ if (!dev)
++ return 0;
++
++ spin_lock(&dev->lock_sync);
++
++ del_timer(&dev->mode_timer);
++ del_timer(&dev->ts_timer);
++
++ if (dev->pendown && dev->touch_cb)
++ dev->touch_cb(0);
++
++ /* Abort current conversion and power down the ADC */
++ tsc2102_write_sync(TSC2102_TS_ADC_CTRL, TSC2102_ADC_ADST);
++
++ dev->spi->dev.power.power_state = state;
++
++ spin_unlock(&dev->lock_sync);
++ return 0;
++}
++
++/*
++ * Resume chip operation.
++ */
++static int tsc2102_resume(struct spi_device *spi)
++{
++ struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev);
++ int err;
++
++ if (!dev)
++ return 0;
++
++ spin_lock(&dev->lock_sync);
++
++ dev->spi->dev.power.power_state = PMSG_ON;
++
++ dev->state = 0;
++ dev->pendown = 0;
++
++ err = tsc2102_configure(dev);
++
++ spin_unlock(&dev->lock_sync);
++ return err;
++}
++#else
++#define tsc2102_suspend NULL
++#define tsc2102_resume NULL
++#endif
++
++static struct platform_device tsc2102_ts_device = {
++ .name = "tsc2102-ts",
++ .id = -1,
++};
++
++static struct platform_device tsc2102_alsa_device = {
++ .name = "tsc2102-alsa",
++ .id = -1,
++};
++
++static int tsc2102_probe(struct spi_device *spi)
++{
++ struct tsc2102_config *pdata = spi->dev.platform_data;
++ struct spi_transfer *spi_buffer;
++ int err = 0;
++
++ if (!pdata) {
++ printk(KERN_ERR "TSC2102: Platform data not supplied\n");
++ return -ENOENT;
++ }
++
++ if (!spi->irq) {
++ printk(KERN_ERR "TSC2102: Invalid irq value\n");
++ return -ENOENT;
++ }
++
++ tsc.pdata = pdata;
++ tsc.state = 0;
++ tsc.pendown = 0;
++ tsc.data_pending = 0;
++ tsc.ts_msecs = 20;
++ tsc.mode_msecs = 1000;
++ tsc.spi = spi;
++
++ /* Allocate enough struct spi_transfer's for all requests */
++ spi_buffer = kzalloc(sizeof(struct spi_transfer) * 16, GFP_KERNEL);
++ if (!spi_buffer) {
++ printk(KERN_ERR "TSC2102: No memory for SPI buffers\n");
++ return -ENOMEM;
++ }
++
++ tsc.transfers = spi_buffer;
++ tsc2102_request_alloc(&tsc, &tsc.req_adc, 0, 4, &spi_buffer);
++ tsc2102_request_alloc(&tsc, &tsc.req_status, 0, 1, &spi_buffer);
++ tsc2102_request_alloc(&tsc, &tsc.req_pressure, 0, 1, &spi_buffer);
++ tsc2102_request_alloc(&tsc, &tsc.req_stopadc, 1, 1, &spi_buffer);
++ tsc2102_request_alloc(&tsc, &tsc.req_mode, 1, 1, &spi_buffer);
++
++ spin_lock_init(&tsc.lock);
++ spin_lock(&tsc.lock_sync);
++
++ /* Get the BCLK - assuming the rate is at 12000000 */
++ tsc.bclk_ck = clk_get(0, "bclk");
++ if (!tsc.bclk_ck) {
++ printk(KERN_ERR "Unable to get the clock BCLK\n");
++ err = -EPERM;
++ goto done;
++ }
++
++ clk_enable(tsc.bclk_ck);
++
++ if (request_irq(spi->irq, tsc2102_handler, IRQF_SAMPLE_RANDOM |
++ IRQF_TRIGGER_FALLING, "tsc2102", &tsc)) {
++ printk(KERN_ERR "Could not allocate touchscreen IRQ!\n");
++ err = -EINVAL;
++ goto err_clk;
++ }
++
++ setup_timer(&tsc.ts_timer,
++ tsc2102_pressure, (unsigned long) &tsc);
++ setup_timer(&tsc.mode_timer,
++ tsc2102_mode_timer, (unsigned long) &tsc);
++
++ /* Set up the communication bus */
++ dev_set_drvdata(&spi->dev, &tsc);
++ spi->dev.power.power_state = PMSG_ON;
++ spi->mode = SPI_MODE_1;
++ spi->bits_per_word = 16;
++ err = spi_setup(spi);
++ if (err)
++ goto err_timer;
++
++ /* Now try to detect the chip, make first contact */
++ if (tsc2102_get_revision() != 0x1) {
++ printk(KERN_ERR "No TI TSC2102 chip found!\n");
++ goto err_timer;
++ }
++
++ err = tsc2102_configure(&tsc);
++ if (err)
++ goto err_timer;
++
++ /* Register devices controlled by TSC 2102 */
++ tsc2102_ts_device.dev.platform_data = pdata;
++ tsc2102_ts_device.dev.parent = &spi->dev;
++ err = platform_device_register(&tsc2102_ts_device);
++ if (err)
++ goto err_timer;
++
++ tsc2102_alsa_device.dev.platform_data = pdata->alsa_config;
++ tsc2102_alsa_device.dev.parent = &spi->dev;
++ err = platform_device_register(&tsc2102_alsa_device);
++ if (err)
++ goto err_ts;
++
++#ifdef CONFIG_HWMON
++ tsc.hwmondev = hwmon_device_register(&spi->dev);
++ if (IS_ERR(tsc.hwmondev)) {
++ printk(KERN_ERR "tsc2102_hwmon: Device registration failed\n");
++ err = PTR_ERR(tsc.hwmondev);
++ goto err_alsa;
++ }
++
++ if (pdata->monitor & TSC_BAT1)
++ err |= device_create_file(&spi->dev, &dev_attr_in0_input);
++ if (pdata->monitor & TSC_BAT2)
++ err |= device_create_file(&spi->dev, &dev_attr_in1_input);
++ if (pdata->monitor & TSC_AUX)
++ err |= device_create_file(&spi->dev, &dev_attr_in2_input);
++ if (pdata->monitor & TSC_TEMP) {
++ err |= device_create_file(&spi->dev, &dev_attr_temp1_input);
++ err |= device_create_file(&spi->dev, &dev_attr_in3_input);
++ err |= device_create_file(&spi->dev, &dev_attr_in4_input);
++ }
++
++ if (err)
++ printk(KERN_ERR "tsc2102_hwmon: Creating one or more "
++ "attribute files failed\n");
++ err = 0; /* Not fatal */
++#endif
++
++#ifdef CONFIG_APM
++ if (pdata->apm_report)
++ apm_get_power_status = tsc2102_get_power_status;
++#endif
++
++ if (!err)
++ goto done;
++
++err_alsa:
++ platform_device_unregister(&tsc2102_alsa_device);
++err_ts:
++ platform_device_unregister(&tsc2102_ts_device);
++err_timer:
++ del_timer(&tsc.ts_timer);
++ del_timer(&tsc.mode_timer);
++ dev_set_drvdata(&spi->dev, NULL);
++err_clk:
++ clk_disable(tsc.bclk_ck);
++ clk_put(tsc.bclk_ck);
++done:
++ spin_unlock(&tsc.lock_sync);
++ return err;
++}
++
++static int tsc2102_remove(struct spi_device *spi)
++{
++ struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev);
++
++ spin_lock(&dev->lock_sync);
++
++ platform_device_unregister(&tsc2102_ts_device);
++ platform_device_unregister(&tsc2102_alsa_device);
++
++ dev_set_drvdata(&spi->dev, NULL);
++
++ /* Release the BCLK */
++ clk_disable(dev->bclk_ck);
++ clk_put(dev->bclk_ck);
++
++ del_timer(&tsc.mode_timer);
++ del_timer(&tsc.ts_timer);
++
++ kfree(tsc.transfers);
++
++#ifdef CONFIG_HWMON
++ hwmon_device_unregister(dev->hwmondev);
++#endif
++
++#ifdef CONFIG_APM
++ apm_get_power_status = 0;
++#endif
++
++ spin_unlock(&dev->lock_sync);
++
++ return 0;
++}
++
++static struct spi_driver tsc2102_driver = {
++ .probe = tsc2102_probe,
++ .remove = tsc2102_remove,
++ .suspend = tsc2102_suspend,
++ .resume = tsc2102_resume,
++ .driver = {
++ .name = "tsc2102",
++ .owner = THIS_MODULE,
++ .bus = &spi_bus_type,
++ },
++};
++
++static char __initdata banner[] = KERN_INFO "TI TSC2102 driver initializing\n";
++
++static int __init tsc2102_init(void)
++{
++ printk(banner);
++ return spi_register_driver(&tsc2102_driver);
++}
++
++static void __exit tsc2102_exit(void)
++{
++ spi_unregister_driver(&tsc2102_driver);
++}
++
++module_init(tsc2102_init);
++module_exit(tsc2102_exit);
++
++EXPORT_SYMBOL(tsc2102_read_sync);
++EXPORT_SYMBOL(tsc2102_reads_sync);
++EXPORT_SYMBOL(tsc2102_write_sync);
++EXPORT_SYMBOL(tsc2102_keyclick);
++
++MODULE_AUTHOR("Andrzej Zaborowski");
++MODULE_DESCRIPTION("Interface driver for TI TSC2102 chips.");
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/include/linux/spi/tsc2102.h
+@@ -0,0 +1,175 @@
++/*
++ * include/linux/spi/tsc2102.h
++ *
++ * TI TSC2102 Touchscreen, Audio and Battery control register definitions
++ *
++ * Copyright (c) 2005 Andrzej Zaborowski <ba...@za...>
++ *
++ * This package 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 package 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 package; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#ifndef __LINUX_SPI_TSC2102_H
++#define __LINUX_SPI_TSC2102_H
++
++struct apm_power_info;
++struct tsc2102_config {
++ int use_internal; /* Use internal reference voltage */
++ uint32_t monitor; /* What inputs are relevant */
++ int temp_at25c[2]; /* Thermometer calibration data */
++ void (*apm_report)(struct apm_power_info *info, int *battery);
++ /* Report status to APM based on battery[] */
++ void *alsa_config; /* .platform_data for the ALSA device */
++};
++
++#define TSC_BAT1 (1 << 0)
++#define TSC_BAT2 (1 << 1)
++#define TSC_AUX (1 << 2)
++#define TSC_TEMP (1 << 4)
++
++extern u16 tsc2102_read_sync(int page, u8 address);
++extern void tsc2102_reads_sync(int page, u8 startaddress, u16 *data,
++ int numregs);
++extern void tsc2102_write_sync(int page, u8 address, u16 data);
++
++typedef void (*tsc2102_touch_t)(int touching);
++typedef void (*tsc2102_coords_t)(int x, int y, int z1, int z2);
++typedef void (*tsc2102_ports_t)(int bat1, int bat2, int aux);
++typedef void (*tsc2102_temp_t)(int temp);
++extern int tsc2102_touch_cb(tsc2102_touch_t handler);
++extern int tsc2102_coords_cb(tsc2102_coords_t handler);
++extern int tsc2102_ports_cb(tsc2102_ports_t handler);
++extern int tsc2102_temp1_cb(tsc2102_temp_t handler);
++extern int tsc2102_temp2_cb(tsc2102_temp_t handler);
++
++#ifdef CONFIG_SOUND
++extern void tsc2102_set_volume(uint8_t left_ch, uint8_t right_ch);
++extern void tsc2102_set_mute(int left_ch, int right_ch);
++extern void tsc2102_get_mute(int *left_ch, int *right_ch);
++extern void tsc2102_dac_power(int state);
++extern int tsc2102_set_rate(int rate);
++extern void tsc2102_set_i2s_master(int state);
++extern void tsc2102_set_deemphasis(int enable);
++extern void tsc2102_set_bassboost(int enable);
++#endif
++
++extern void tsc2102_keyclick(int amplitude, int freq, int length);
++
++#define TSC2102_REG(pg, addr) pg, addr
++
++/* Page 0, Touch Screen & Keypad Data registers */
++#define TSC2102_TS_X TSC2102_REG(0, 0x00)
++#define TSC2102_TS_Y TSC2102_REG(0, 0x01)
++#define TSC2102_TS_Z1 TSC2102_REG(0, 0x02)
++#define TSC2102_TS_Z2 TSC2102_REG(0, 0x03)
++#define TSC2102_TS_BAT1 TSC2102_REG(0, 0x05)
++#define TSC2102_TS_BAT2 TSC2102_REG(0, 0x06)
++#define TSC2102_TS_AUX TSC2102_REG(0, 0x07)
++#define TSC2102_TS_TEMP1 TSC2102_REG(0, 0x09)
++#define TSC2102_TS_TEMP2 TSC2102_REG(0, 0x0a)
++
++/* Page 1, Touch Screen & Keypad Control registers */
++#define TSC2102_TS_ADC_CTRL TSC2102_REG(1, 0x00)
++#define TSC2102_TS_STATUS_CTRL TSC2102_REG(1, 0x01)
++#define TSC2102_TS_REF_CTRL TSC2102_REG(1, 0x03)
++#define TSC2102_TS_RESET_CTRL TSC2102_REG(1, 0x04)
++#define TSC2102_TS_CONFIG_CTRL TSC2102_REG(1, 0x05)
++
++/* Page 2, Audio Control registers */
++#define TSC2102_AUDIO1_CTRL TSC2102_REG(2, 0x00)
++#define TSC2102_DAC_GAIN_CTRL TSC2102_REG(2, 0x02)
++#define TSC2102_AUDIO2_CTRL TSC2102_REG(2, 0x04)
++#define TSC2102_DAC_POWER_CTRL TSC2102_REG(2, 0x05)
++#define TSC2102_AUDIO3_CTRL TSC2102_REG(2, 0x06)
++#define TSC2102_LCH_BASS_BOOST_N0 TSC2102_REG(2, 0x07)
++#define TSC2102_LCH_BASS_BOOST_N1 TSC2102_REG(2, 0x08)
++#define TSC2102_LCH_BASS_BOOST_N2 TSC2102_REG(2, 0x09)
++#define TSC2102_LCH_BASS_BOOST_N3 TSC2102_REG(2, 0x0a)
++#define TSC2102_LCH_BASS_BOOST_N4 TSC2102_REG(2, 0x0b)
++#define TSC2102_LCH_BASS_BOOST_N5 TSC2102_REG(2, 0x0c)
++#define TSC2102_LCH_BASS_BOOST_D1 TSC2102_REG(2, 0x0d)
++#define TSC2102_LCH_BASS_BOOST_D2 TSC2102_REG(2, 0x0e)
++#define TSC2102_LCH_BASS_BOOST_D4 TSC2102_REG(2, 0x0f)
++#define TSC2102_LCH_BASS_BOOST_D5 TSC2102_REG(2, 0x10)
++#define TSC2102_RCH_BASS_BOOST_N0 TSC2102_REG(2, 0x11)
++#define TSC2102_RCH_BASS_BOOST_N1 TSC2102_REG(2, 0x12)
++#define TSC2102_RCH_BASS_BOOST_N2 TSC2102_REG(2, 0x13)
++#define TSC2102_RCH_BASS_BOOST_N3 TSC2102_REG(2, 0x14)
++#define TSC2102_RCH_BASS_BOOST_N4 TSC2102_REG(2, 0x15)
++#define TSC2102_RCH_BASS_BOOST_N5 TSC2102_REG(2, 0x16)
++#define TSC2102_RCH_BASS_BOOST_D1 TSC2102_REG(2, 0x17)
++#define TSC2102_RCH_BASS_BOOST_D2 TSC2102_REG(2, 0x18)
++#define TSC2102_RCH_BASS_BOOST_D4 TSC2102_REG(2, 0x19)
++#define TSC2102_RCH_BASS_BOOST_D5 TSC2102_REG(2, 0x1a)
++#define TSC2102_PLL1_CTRL TSC2102_REG(2, 0x1b)
++#define TSC2102_PLL2_CTRL TSC2102_REG(2, 0x1c)
++#define TSC2102_AUDIO4_CTRL TSC2102_REG(2, 0x1d)
++
++/* Field masks for Audio Control 1 */
++#define AC1_WLEN(ARG) (((ARG) & 0x03) << 10)
++#define AC1_DATFM(ARG) (((ARG) & 0x03) << 8)
++#define AC1_DACFS(ARG) ((ARG) & 0x3f)
++
++/* Field masks for TSC2102_DAC_GAIN_CTRL */
++#define DGC_DALMU (1 << 15)
++#define DGC_DALVL(ARG) (((ARG) & 0x7f) << 8)
++#define DGC_DARMU (1 << 7)
++#define DGC_DARVL(ARG) (((ARG) & 0x7f))
++
++/* Field formats for TSC2102_AUDIO2_CTRL */
++#define AC2_KCLEN (1 << 15)
++#define AC2_KCLAC(ARG) (((ARG) & 0x07) << 12)
++#define AC2_KCLFRQ(ARG) (((ARG) & 0x07) << 8)
++#define AC2_KCLLN(ARG) (((ARG) & 0x0f) << 4)
++#define AC2_DLGAF (1 << 3)
++#define AC2_DRGAF (1 << 2)
++#define AC2_DASTC (1 << 1)
++
++/* Field masks for TSC2102_DAC_POWER_CTRL */
++#define CPC_PWDNC (1 << 15)
++#define CPC_DAODRC (1 << 12)
++#define CPC_DAPWDN (1 << 10)
++#define CPC_VGPWDN (1 << 8)
++#define CPC_DAPWDF (1 << 6)
++#define CPC_BASSBC (1 << 1)
++#define CPC_DEEMPF (0x01)
++
++/* Field masks for TSC2101_AUDIO_CTRL_3 */
++#define AC3_DMSVOL(ARG) (((ARG) & 0x03) << 14)
++#define AC3_REFFS (1 << 13)
++#define AC3_DAXFM (1 << 12)
++#define AC3_SLVMS (1 << 11)
++#define AC3_DALOVF (1 << 7)
++#define AC3_DAROVF (1 << 6)
++#define AC3_REVID(ARG) (((ARG) & 0x07))
++
++/* Field masks for TSC2102_PLL1_CTRL */
++#define PLL1_PLLEN (1 << 15)
++#define PLL1_Q_VAL(ARG) (((ARG) & 0x0f) << 11)
++#define PLL1_P_VAL(ARG) (((ARG) & 0x07) << 8)
++#define PLL1_I_VAL(ARG) (((ARG) & 0x3f) << 2)
++
++/* Field masks for TSC2102_PLL2_CTRL */
++#define PLL2_D_VAL(ARG) (((ARG) & 0x3fff) << 2)
++
++/* Field masks for TSC2101_AUDIO_CTRL_4 */
++#define AC4_DASTPD (1 << 14)
++
++struct tsc2102_rate_info_s {
++ u16 sample_rate;
++ u8 divisor;
++ u8 fs_44k; /* 44.1 kHz Fsref if 1, 48 kHz if 0 */
++};
++
++#endif /* __LINUX_SPI_TSC2102_H */
Added: trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/074-linux-2.6.18-tsc2102-touchscreen.diff
===================================================================
--- trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/074-linux-2.6.18-tsc2102-touchscreen.diff (rev 0)
+++ trunk/buildroot/target/device/armadeus/linux/kernel-patches/2.6.18.1/074-linux-2.6.18-tsc2102-touchscreen.diff 2007-07-14 15:29:41 UTC (rev 611)
@@ -0,0 +1,184 @@
+diff -pNaur -X b/Documentation/dontdiff a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
+--- a/drivers/input/touchscreen/Kconfig 2006-07-06 02:48:09.000000000 +0000
++++ b/drivers/input/touchscreen/Kconfig 2006-08-17 23:35:38.000000000 +0000
+@@ -111,6 +111,20 @@ config TOUCHSCREEN_HP600
+ To compile this driver as a module, choose M here: the
+ module will be called hp680_ts_input.
+
++config TOUCHSCREEN_TSC2102
++ tristate "TSC 2102 based touchscreens"
++ depends on SPI_MASTER
++ select SPI_TSC2102
++ help
++ Say Y here if you have a touchscreen interface using the
++ TI TSC 2102 controller, and your board-specific initialization
++ code includes that in its table of SPI devices.
++
++ If unsure, say N (but it's safe to say "Y").
++
++ To compile this driver as a module, choose M here: the
++ module will be called tsc2102_ts.
++
+ endif
+ config TOUCHSCREEN_OMAP
+ tristate "OMAP touchscreen input driver"
+diff -pNaur -X b/Documentation/dontdiff a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
+--- a/drivers/input/touchscreen/Makefile 2006-03-22 17:44:37.000000000 +0000
++++ b/drivers/input/touchscreen/Makefile 2006-08-17 23:35:38.000000000 +0000
+@@ -12,3 +12,4 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
+ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
+ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
+ obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
++obj-$(CONFIG_TOUCHSCREEN_TSC2102) += tsc2102_ts.o
+diff -pNaur -X b/Documentation/dontdiff a/drivers/input/touchscreen/tsc2102_ts.c b/drivers/input/touchscreen/tsc2102_ts.c
+--- a/drivers/input/touchscreen/tsc2102_ts.c 1970-01-01 00:00:00.000000000 +0000
++++ b/drivers/input/touchscreen/tsc2102_ts.c 2006-08-17 23:35:38.000000000 +0000
+@@ -0,0 +1,148 @@
++/*
++ * input/touchscreen/tsc2102_ts.c
++ *
++ * Touchscreen input device driver for boards using the TSC 2102 chip.
++ *
++ * Copyright (c) 2006 Andrzej Zaborowski <ba...@za...>
++ *
++ * This package 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 package 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 package; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include <linux/errno.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/input.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/kbd_kern.h>
++
++#include <linux/spi/tsc2102.h>
++
++struct input_dev *dev;
++
++static void tsc2102_touch(int touching)
++{
++ if (!touching) {
++ input_report_abs(dev, ABS_X, 0);
++ input_report_abs(dev, ABS_Y, 0);
++ input_report_abs(dev, ABS_PRESSURE, 0);
++ input_sync(dev);
++ }
++
++ input_report_key(dev, BTN_TOUCH, touching);
++
++ do_poke_blanked_console = 1;
++}
++
++static void tsc2102_coords(int x, int y, int z1, int z2)
++{
++ int p;
++
++ /* Calculate the touch resistance a la equation #1 */
++ if (z1 != 0)
++ p = x * (z2 - z1) / (z1 << 4);
++ else
++ p = 1;
++
++ input_report_abs(dev, ABS_X, x);
++ input_report_abs(dev, ABS_Y, y);
++ input_report_abs(dev, ABS_PRESSURE, p);
++ input_sync(dev);
++}
++
++static int tsc2102_ts_probe(struct platform_device *pdev)
++{
++ int status;
++
++ dev = input_allocate_device();
++ if (!dev)
++ return -ENOMEM;
++
++ status = tsc2102_touch_cb(tsc2102_touch);
++ if (status) {
++ input_free_device(dev);
++ return status;
++ }
++
++ status = tsc2102_coords_cb(tsc2102_coords);
++ if (status) {
++ input_free_device(dev);
++ return status;
++ }
++
++ dev->name = "TSC2102 Touchscreen";
++ dev->dev = &pdev->dev;
++ dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
++ dev->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH);
++ dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
++ input_register_device(dev);
++
++ printk("TSC2102 touchscreen driver initialized\n");
++ return 0;
++}
++
++static int tsc2102_ts_remove(struct platform_device *pdev)
++{
++ tsc2102_touch_cb(0);
++ tsc2102_coords_cb(0);
++ input_unregister_device(dev);
++ return 0;
++}
++
++static int
++tsc2102_ts_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ /* Nothing yet */
++ return 0;
++}
++
++static int tsc2102_ts_resume(struct platform_device *pdev)
++{
++ /* Nothing either */
++ return 0;
++}
++
++static struct platform_driver tsc2102_ts_driver = {
++ .probe = tsc2102_ts_probe,
++ .remove = tsc2102_ts_remove,
++ .suspend = tsc2102_ts_suspend,
++ .resume = tsc2102_ts_resume,
++ .driver = {
++ .name = "tsc2102-ts",
++ },
++};
++
++static int __init tsc2102_ts_init(void)
++{
++ int ret;
++
++ ret = platform_driver_register(&tsc2102_ts_driver);
++ if (ret)
++ return -ENODEV;
++
++ return 0;
++}
++
++static void __exit tsc2102_ts_exit(void)
++{
++ platform_driver_unregister(&tsc2102_ts_driver);
++}
++
++module_init(tsc2102_ts_init);
++module_exit(tsc2102_ts_exit);
++
++MODULE_AUTHOR("Andrzej Zaborowski");
++MODULE_DESCRIPTION("Touchscreen input driver for TI TSC2102.");
++MODULE_LICENSE("GPL");
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|