* [PATCH 1/4] TSC2102 main SPI driver
@ 2006-08-18 10:01 andrzej zaborowski
2006-08-18 13:27 ` Komal Shah
2006-09-02 5:14 ` David Brownell
0 siblings, 2 replies; 6+ messages in thread
From: andrzej zaborowski @ 2006-08-18 10:01 UTC (permalink / raw)
To: Linux-OMAP
[-- Attachment #1: Type: text/plain, Size: 288 bytes --]
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 <balrog@zabor.org>
[-- Attachment #2: linux-tsc2102-core.patch --]
[-- Type: application/octet-stream, Size: 35234 bytes --]
diff -pNaur -X b/Documentation/dontdiff a/drivers/spi/Kconfig b/drivers/spi/Kconfig
--- a/drivers/spi/Kconfig 2006-07-06 02:48:18.000000000 +0000
+++ b/drivers/spi/Kconfig 2006-08-17 23:35:38.000000000 +0000
@@ -135,7 +135,12 @@ config SPI_S3C24XX
comment "SPI Protocol Masters"
depends on SPI_MASTER
-
+config TSC2102
+ depends on SPI_MASTER
+ tristate "TSC2102 codec support"
+ ---help---
+ Say Y here if you want support for the TSC2102 codec. It is
+ needed for the touchscreen driver on some boards.
#
# Add new SPI protocol masters in alphabetical order above this line
#
diff -pNaur -X b/Documentation/dontdiff a/drivers/spi/Makefile b/drivers/spi/Makefile
--- a/drivers/spi/Makefile 2006-07-06 02:48:18.000000000 +0000
+++ b/drivers/spi/Makefile 2006-08-17 23:35:38.000000000 +0000
@@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uw
# ... add above this line ...
# SPI protocol drivers (device/link on bus)
+obj-$(CONFIG_TSC2102) += tsc2102.o
# ... add above this line ...
# SPI slave controller drivers (upstream link)
diff -pNaur -X b/Documentation/dontdiff a/drivers/spi/tsc2102.c b/drivers/spi/tsc2102.c
--- a/drivers/spi/tsc2102.c 1970-01-01 00:00:00.000000000 +0000
+++ b/drivers/spi/tsc2102.c 2006-08-18 01:15:20.000000000 +0000
@@ -0,0 +1,1081 @@
+/*
+ * drivers/spi/tsc2102.c
+ *
+ * TSC2102 codec interface driver
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 <asm/system.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/hardware.h>
+
+/* 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;
+ 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 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;
+};
+
+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)
+{
+ spi->dev = dev->spi;
+
+ if (direction == 1) /* Write */
+ numregs = 2;
+ else /* Read */
+ numregs += 1;
+ spi->transfer = kzalloc(sizeof(struct spi_transfer) * numregs,
+ GFP_KERNEL);
+}
+
+#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 pt_regs *regs)
+{
+ 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]);
+ }
+
+ if (dev->status & TSC2102_T1_DAV) {
+ if (dev->temp1_cb)
+ dev->temp1_cb(*dev->adc_data);
+ }
+
+ if (dev->status & TSC2102_T2_DAV) {
+ if (dev->temp2_cb)
+ dev->temp2_cb(*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);
+}
+
+/*
+ * 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);
+
+ 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);
+}
+
+void tsc2102_set_mute(int left_ch, int right_ch)
+{
+ u16 val;
+ spin_lock(&tsc.lock);
+
+ 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);
+}
+
+void tsc2102_get_mute(int *left_ch, int *right_ch)
+{
+ u16 val;
+ spin_lock(&tsc.lock);
+
+ val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL);
+
+ spin_unlock(&tsc.lock);
+
+ *left_ch = !!(val & (1 << 15));
+ *right_ch = !!(val & (1 << 7));
+}
+
+void tsc2102_set_deemphasis(int enable)
+{
+ u16 val;
+ spin_lock(&tsc.lock);
+ 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);
+}
+
+void tsc2102_set_bassboost(int enable)
+{
+ u16 val;
+ spin_lock(&tsc.lock);
+ 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);
+}
+
+/* {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);
+
+ 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);
+ return 0;
+}
+
+/*
+ * Perform basic set-up with default values and power the DAC on.
+ */
+void tsc2102_dac_power(int state)
+{
+ spin_lock(&tsc.lock);
+
+ 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);
+}
+
+void tsc2102_set_i2s_master(int state)
+{
+ uint16_t val;
+ spin_lock(&tsc.lock);
+
+ 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);
+}
+
+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);
+ 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);
+}
+
+#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);
+
+ 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);
+ 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);
+
+ dev->spi->dev.power.power_state = PMSG_ON;
+
+ dev->state = 0;
+ dev->pendown = 0;
+
+ err = tsc2102_configure(dev);
+
+ spin_unlock(&dev->lock);
+ 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_hwmon_device = {
+ .name = "tsc2102-hwmon",
+ .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;
+ 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;
+
+ tsc2102_request_alloc(&tsc, &tsc.req_adc, 0, 4);
+ tsc2102_request_alloc(&tsc, &tsc.req_status, 0, 1);
+ tsc2102_request_alloc(&tsc, &tsc.req_pressure, 0, 1);
+ tsc2102_request_alloc(&tsc, &tsc.req_stopadc, 1, 1);
+ tsc2102_request_alloc(&tsc, &tsc.req_mode, 1, 1);
+
+ spin_lock_init(&tsc.lock);
+ spin_lock(&tsc.lock);
+
+ /* 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);
+
+ set_irq_type(spi->irq, IRQT_FALLING);
+ if (request_irq(spi->irq, tsc2102_handler,
+ SA_SAMPLE_RANDOM, "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;
+ err = platform_device_register(&tsc2102_ts_device);
+ if (err)
+ goto err_timer;
+
+ tsc2102_hwmon_device.dev.platform_data = pdata;
+ err = platform_device_register(&tsc2102_hwmon_device);
+ if (err)
+ goto err_ts;
+
+ tsc2102_alsa_device.dev.platform_data = pdata->alsa_config;
+ err = platform_device_register(&tsc2102_alsa_device);
+
+ if (!err)
+ goto done;
+
+ platform_device_unregister(&tsc2102_hwmon_device);
+err_ts:
+ platform_device_unregister(&tsc2102_ts_device);
+err_timer:
+ del_timer(&tsc.ts_timer);
+ del_timer(&tsc.mode_timer);
+err_clk:
+ clk_disable(tsc.bclk_ck);
+ clk_put(tsc.bclk_ck);
+done:
+ spin_unlock(&tsc.lock);
+ return err;
+}
+
+static int tsc2102_remove(struct spi_device *spi)
+{
+ struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev);
+
+ spin_lock(&dev->lock);
+
+ platform_device_unregister(&tsc2102_ts_device);
+ platform_device_unregister(&tsc2102_hwmon_device);
+ platform_device_unregister(&tsc2102_alsa_device);
+
+ /* 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.req_adc.transfer);
+ kfree(tsc.req_status.transfer);
+ kfree(tsc.req_pressure.transfer);
+ kfree(tsc.req_stopadc.transfer);
+ kfree(tsc.req_mode.transfer);
+
+ spin_unlock(&dev->lock);
+
+ return 0;
+}
+
+static struct spi_driver tsc2102_driver = {
+ .probe = tsc2102_probe,
+ .remove = tsc2102_remove,
+ .suspend = tsc2102_suspend,
+ .resume = tsc2102_resume,
+ .driver = {
+ .name = "tsc2102",
+ .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");
diff -pNaur -X b/Documentation/dontdiff a/include/linux/spi/tsc2102.h b/include/linux/spi/tsc2102.h
--- a/include/linux/spi/tsc2102.h 1970-01-01 00:00:00.000000000 +0000
+++ b/include/linux/spi/tsc2102.h 2006-08-18 10:34:32.000000000 +0000
@@ -0,0 +1,171 @@
+/*
+ * include/linux/spi/tsc2102.h
+ *
+ * TI TSC2102 Touchscreen, Audio and Battery control register definitions
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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);
+
+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);
+
+extern void tsc2102_keyclick(int amplitude, int freq, int length);
+
+/* Page 0, Touch Screen & Keypad Data registers */
+#define TSC2102_TS_X 0, 0x00
+#define TSC2102_TS_Y 0, 0x01
+#define TSC2102_TS_Z1 0, 0x02
+#define TSC2102_TS_Z2 0, 0x03
+#define TSC2102_TS_BAT1 0, 0x05
+#define TSC2102_TS_BAT2 0, 0x06
+#define TSC2102_TS_AUX 0, 0x07
+#define TSC2102_TS_TEMP1 0, 0x09
+#define TSC2102_TS_TEMP2 0, 0x0a
+
+/* Page 1, Touch Screen & Keypad Control registers */
+#define TSC2102_TS_ADC_CTRL 1, 0x00
+#define TSC2102_TS_STATUS_CTRL 1, 0x01
+#define TSC2102_TS_REF_CTRL 1, 0x03
+#define TSC2102_TS_RESET_CTRL 1, 0x04
+#define TSC2102_TS_CONFIG_CTRL 1, 0x05
+
+/* Page 2, Audio Control registers */
+#define TSC2102_AUDIO1_CTRL 2, 0x00
+#define TSC2102_DAC_GAIN_CTRL 2, 0x02
+#define TSC2102_AUDIO2_CTRL 2, 0x04
+#define TSC2102_DAC_POWER_CTRL 2, 0x05
+#define TSC2102_AUDIO3_CTRL 2, 0x06
+#define TSC2102_LCH_BASS_BOOST_N0 2, 0x07
+#define TSC2102_LCH_BASS_BOOST_N1 2, 0x08
+#define TSC2102_LCH_BASS_BOOST_N2 2, 0x09
+#define TSC2102_LCH_BASS_BOOST_N3 2, 0x0a
+#define TSC2102_LCH_BASS_BOOST_N4 2, 0x0b
+#define TSC2102_LCH_BASS_BOOST_N5 2, 0x0c
+#define TSC2102_LCH_BASS_BOOST_D1 2, 0x0d
+#define TSC2102_LCH_BASS_BOOST_D2 2, 0x0e
+#define TSC2102_LCH_BASS_BOOST_D4 2, 0x0f
+#define TSC2102_LCH_BASS_BOOST_D5 2, 0x10
+#define TSC2102_RCH_BASS_BOOST_N0 2, 0x11
+#define TSC2102_RCH_BASS_BOOST_N1 2, 0x12
+#define TSC2102_RCH_BASS_BOOST_N2 2, 0x13
+#define TSC2102_RCH_BASS_BOOST_N3 2, 0x14
+#define TSC2102_RCH_BASS_BOOST_N4 2, 0x15
+#define TSC2102_RCH_BASS_BOOST_N5 2, 0x16
+#define TSC2102_RCH_BASS_BOOST_D1 2, 0x17
+#define TSC2102_RCH_BASS_BOOST_D2 2, 0x18
+#define TSC2102_RCH_BASS_BOOST_D4 2, 0x19
+#define TSC2102_RCH_BASS_BOOST_D5 2, 0x1a
+#define TSC2102_PLL1_CTRL 2, 0x1b
+#define TSC2102_PLL2_CTRL 2, 0x1c
+#define TSC2102_AUDIO4_CTRL 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 */
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/4] TSC2102 main SPI driver
2006-08-18 10:01 [PATCH 1/4] TSC2102 main SPI driver andrzej zaborowski
@ 2006-08-18 13:27 ` Komal Shah
2006-08-18 23:59 ` andrzej zaborowski
2006-09-02 5:14 ` David Brownell
1 sibling, 1 reply; 6+ messages in thread
From: Komal Shah @ 2006-08-18 13:27 UTC (permalink / raw)
To: balrogg, Linux-OMAP
--- andrzej zaborowski <balrog@zabor.org> wrote:
> 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.
>
Quick review comments, will come later with more detailed review.
+ spi->transfer = kzalloc(sizeof(struct spi_transfer) * numregs,
+ GFP_KERNEL);
How about checking return value here?
+ set_irq_type(spi->irq, IRQT_FALLING);
+ if (request_irq(spi->irq, tsc2102_handler,
+ SA_SAMPLE_RANDOM, "tsc2102", &tsc)) {
+ printk(KERN_ERR "Could not allocate touchscreen IRQ!\n");
+ err = -EINVAL;
+ goto err_clk;
+ }
You should use new flags as per genirq patches, and I think
set_irq_type may not be required.
+#define TSC2102_TS_X 0, 0x00
+#define TSC2102_TS_Y 0, 0x01
I think better/clean way could be, having defines taking page and addr
as argument. e.g
#define tsc2102_reg(page, addr) /* your code here as per pg and addr
needs to be shifted and ORed as needed*/
#define tsc2102_ts_x tsc2102_reg(0, 0x0)
and may be conversion macros from reg_to_page and page_to_reg as
required.
---Komal Shah
http://komalshah.blogspot.com/
__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/4] TSC2102 main SPI driver
2006-08-18 13:27 ` Komal Shah
@ 2006-08-18 23:59 ` andrzej zaborowski
0 siblings, 0 replies; 6+ messages in thread
From: andrzej zaborowski @ 2006-08-18 23:59 UTC (permalink / raw)
To: Komal Shah; +Cc: Linux-OMAP
On 18/08/06, Komal Shah <komal_shah802003@yahoo.com> wrote:
> --- andrzej zaborowski <balrog@zabor.org> wrote:
>
> > 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.
> >
>
> Quick review comments, will come later with more detailed review.
Many thanks for looking at the code, I wouldn't notice all the issues.
I'm going to send a corrected code next week when I'm online.
>
> + spi->transfer = kzalloc(sizeof(struct spi_transfer) * numregs,
> + GFP_KERNEL);
>
> How about checking return value here?
Ok. I'm going to allocate only a single big buffer to avoid creating a
five error paths.
>
> + set_irq_type(spi->irq, IRQT_FALLING);
> + if (request_irq(spi->irq, tsc2102_handler,
> + SA_SAMPLE_RANDOM, "tsc2102", &tsc)) {
> + printk(KERN_ERR "Could not allocate touchscreen IRQ!\n");
> + err = -EINVAL;
> + goto err_clk;
> + }
>
> You should use new flags as per genirq patches, and I think
> set_irq_type may not be required.
Ah, right. I changed it to this, and dumped set_irq_type():
if (request_irq(spi->irq, tsc2102_handler, SA_SAMPLE_RANDOM |
SA_TRIGGER_FALLING, "tsc2102", &tsc)) {
>
> +#define TSC2102_TS_X 0, 0x00
> +#define TSC2102_TS_Y 0, 0x01
>
> I think better/clean way could be, having defines taking page and addr
> as argument. e.g
>
> #define tsc2102_reg(page, addr) /* your code here as per pg and addr
> needs to be shifted and ORed as needed*/
> #define tsc2102_ts_x tsc2102_reg(0, 0x0)
>
> and may be conversion macros from reg_to_page and page_to_reg as
> required.
Here I was trying to maintain api similar to that in omap-tsc2101.c
with page and offset as separate parameters but I see now that it
prevents an optimisation.
>
>
> ---Komal Shah
> http://komalshah.blogspot.com/
>
> __________________________________________________
> Do You Yahoo!?
> Tired of spam? Yahoo! Mail has the best spam protection around
> http://mail.yahoo.com
>
I will appreciate further comments.
Do you know where I can find out what functions are "must check"?
Regards,
Andrzej
--
balrog 2oo6
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/4] TSC2102 main SPI driver
2006-08-18 10:01 [PATCH 1/4] TSC2102 main SPI driver andrzej zaborowski
2006-08-18 13:27 ` Komal Shah
@ 2006-09-02 5:14 ` David Brownell
2006-09-13 19:01 ` andrzej zaborowski
1 sibling, 1 reply; 6+ messages in thread
From: David Brownell @ 2006-09-02 5:14 UTC (permalink / raw)
To: linux-omap-open-source, balrogg
On Friday 18 August 2006 3:01 am, andrzej zaborowski wrote:
> --- a/drivers/spi/Kconfig 2006-07-06 02:48:18.000000000 +0000
> +++ b/drivers/spi/Kconfig 2006-08-17 23:35:38.000000000 +0000
Hmm, seems to me this is the wrong directory for this driver.
I can see it primarily being a touchscreen driver (with some
hwmon sensors too); or primarily an audio driver. But maybe
you're right that the core should be here.
> > +config TSC2102
> + depends on SPI_MASTER
> + tristate "TSC2102 codec support"
> + ---help---
> + Say Y here if you want support for the TSC2102 codec. It is
> + needed for the touchscreen driver on some boards.
Don't describe it as a "codec". It's a multifunction chip, with audio
codec and amplifier, and sensors for touchscreen, battery voltage, and
temperature ... plus more, I don't have the spec sheet here.
How much does this driver need to differ from a TSC 2101 driver, by the
way? Enough to really need separate drivers?
> +module_param_named(sensor_scan_msecs, tsc.mode_msecs, uint, 0);
> +MODULE_PARM_DESC(sensor_scan_msecs, "Temperature & battery scan interval");
If you even need to have this be configurable, it should be part of
the hwmon support. AFAICT only the battery would need polling, and
that would only be for APM emulation...
> +void tsc2102_set_volume(uint8_t left_ch, uint8_t right_ch)
> ...
> +void tsc2102_set_mute(int left_ch, int right_ch)
> ...
> +void tsc2102_get_mute(int *left_ch, int *right_ch)
> ...
> +void tsc2102_set_deemphasis(int enable)
> ...
> +void tsc2102_set_bassboost(int enable)
> ... etc ...
All of that should surely be included only if the ALSA support is
included ...
> +static struct platform_device tsc2102_ts_device = {
> + .name = "tsc2102-ts",
> + .id = -1,
> +};
> +
> +static struct platform_device tsc2102_hwmon_device = {
> + .name = "tsc2102-hwmon",
> + .id = -1,
> +};
I don't much like spilitting those out like that; if HWMON is
configured, just include tsc2102 hwmon hooks here, directly;
even if you do split out the touchscreen and audio support
into separate drivers.
> +static struct platform_device tsc2102_alsa_device = {
> + .name = "tsc2102-alsa",
> + .id = -1,
> +};
> +
Surely you should be setting the parent of all those devices
to be the spi_device.dev ???
Also, the linkage between this "core" to "layered" drivers
leaves a bit to be desired. Why not just provide those
layered drivers a hook function they can call, maybe along
the lines of
tsc210x_write_sync(struct device *tsc210x,
unsigned register,
u16 value);
Or whatever ... the "device" being of course the sysfs parent
of the platform device.
- Dave
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/4] TSC2102 main SPI driver
2006-09-02 5:14 ` David Brownell
@ 2006-09-13 19:01 ` andrzej zaborowski
2006-10-19 13:42 ` Tony Lindgren
0 siblings, 1 reply; 6+ messages in thread
From: andrzej zaborowski @ 2006-09-13 19:01 UTC (permalink / raw)
To: linux-omap-open-source, David Brownell
[-- Attachment #1: Type: text/plain, Size: 4929 bytes --]
Thanks for the review.
On 02/09/06, David Brownell <david-b@pacbell.net> wrote:
> On Friday 18 August 2006 3:01 am, andrzej zaborowski wrote:
> > --- a/drivers/spi/Kconfig 2006-07-06 02:48:18.000000000 +0000
> > +++ b/drivers/spi/Kconfig 2006-08-17 23:35:38.000000000 +0000
>
> Hmm, seems to me this is the wrong directory for this driver.
> I can see it primarily being a touchscreen driver (with some
> hwmon sensors too); or primarily an audio driver. But maybe
> you're right that the core should be here.
I was also wondering where it should go. With functions as diverse as
audio, input and hwmon I can't tell what is the primary function.
>
>
> > > +config TSC2102
> > + depends on SPI_MASTER
> > + tristate "TSC2102 codec support"
> > + ---help---
> > + Say Y here if you want support for the TSC2102 codec. It is
> > + needed for the touchscreen driver on some boards.
>
> Don't describe it as a "codec". It's a multifunction chip, with audio
> codec and amplifier, and sensors for touchscreen, battery voltage, and
> temperature ... plus more, I don't have the spec sheet here.
Ok. Basically this, audio controller and the sensors.
>
> How much does this driver need to differ from a TSC 2101 driver, by the
> way? Enough to really need separate drivers?
Well, I can imagine a TSC 2XXX driver but there would be *a lot* of
conditional code because there are differences: some registers or
single bits in particular registers have changed places or are
"Reserved"; the capabilities differ: some chips include a keypad
controller, have different audio outputs and inputs and different
sensors. The main difference, however, is that on 2102 and later (e.g.
2301) a new conversion doesn't start until all converted data is read
out of the registers which means that you always have to read the data
or you won't receive another interrupt.
>
>
>
> > +module_param_named(sensor_scan_msecs, tsc.mode_msecs, uint, 0);
> > +MODULE_PARM_DESC(sensor_scan_msecs, "Temperature & battery scan interval");
>
> If you even need to have this be configurable, it should be part of
> the hwmon support. AFAICT only the battery would need polling, and
> that would only be for APM emulation...
This value also affects the touchscreen responsiveness because
touchscreen conversions have to be stopped for the scanning of other
sensors.
>
> > +void tsc2102_set_volume(uint8_t left_ch, uint8_t right_ch)
> > ...
> > +void tsc2102_set_mute(int left_ch, int right_ch)
> > ...
> > +void tsc2102_get_mute(int *left_ch, int *right_ch)
> > ...
> > +void tsc2102_set_deemphasis(int enable)
> > ...
> > +void tsc2102_set_bassboost(int enable)
> > ... etc ...
>
> All of that should surely be included only if the ALSA support is
> included ...
Ok. I now include it if CONFIG_SOUND is enabled because it's not ALSA specific.
>
>
> > +static struct platform_device tsc2102_ts_device = {
> > + .name = "tsc2102-ts",
> > + .id = -1,
> > +};
> > +
> > +static struct platform_device tsc2102_hwmon_device = {
> > + .name = "tsc2102-hwmon",
> > + .id = -1,
> > +};
>
> I don't much like spilitting those out like that; if HWMON is
> configured, just include tsc2102 hwmon hooks here, directly;
> even if you do split out the touchscreen and audio support
> into separate drivers.
The attached version is how it looks with the hwmon part included in
this same file. I liked more the approach with hwmon code separated in
drivers/hwmon/ because then the "platform_device" framework itself
takes care of the configuration. Also I think that a device
"tsc2102-hwmon" should exist even if hwmon is not configured.
>
>
> > +static struct platform_device tsc2102_alsa_device = {
> > + .name = "tsc2102-alsa",
> > + .id = -1,
> > +};
> > +
>
> Surely you should be setting the parent of all those devices
> to be the spi_device.dev ???
Ok.
>
>
> Also, the linkage between this "core" to "layered" drivers
> leaves a bit to be desired. Why not just provide those
> layered drivers a hook function they can call, maybe along
> the lines of
>
> tsc210x_write_sync(struct device *tsc210x,
> unsigned register,
> u16 value);
>
> Or whatever ... the "device" being of course the sysfs parent
> of the platform device.
I'm not sure I understand. There are hooks for writing and reading
registers provided, but I figured it is better to access registers
directly (using these hooks) only from one file because of the
dependency between the chip's functions, e.g touchscreen and the
sensors, they have to share the ADC time and shouldn't mess up the
order of some accesses. Also, when the audio DAC is powered up, the
proper clock must be enabled or the touchscreen will stop working (not
the audio).
Regards,
--
balrog 2oo6
[-- Attachment #2: linux-tsc2102-core.patch --]
[-- Type: application/octet-stream, Size: 39027 bytes --]
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -135,7 +135,12 @@ config SPI_S3C24XX
comment "SPI Protocol Masters"
depends on SPI_MASTER
-
+config 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.
#
# Add new SPI protocol masters in alphabetical order above this line
#
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uw
# ... add above this line ...
# SPI protocol drivers (device/link on bus)
+obj-$(CONFIG_TSC2102) += tsc2102.o
# ... add above this line ...
# SPI slave controller drivers (upstream link)
diff --git a/drivers/spi/tsc2102.c b/drivers/spi/tsc2102.c
new file mode 100644
--- /dev/null
+++ b/drivers/spi/tsc2102.c
@@ -0,0 +1,1199 @@
+/*
+ * drivers/spi/tsc2102.c
+ *
+ * TSC2102 interface driver.
+ *
+ * Copyright (c) 2005 Andrzej Zaborowski <balrog@zabor.org>
+ *
+ * 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 pt_regs *regs)
+{
+ 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, SA_SAMPLE_RANDOM |
+ SA_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");
diff --git a/include/linux/spi/tsc2102.h b/include/linux/spi/tsc2102.h
new file mode 100644
--- /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 <balrog@zabor.org>
+ *
+ * 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 */
[-- Attachment #3: Type: text/plain, Size: 0 bytes --]
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 1/4] TSC2102 main SPI driver
2006-09-13 19:01 ` andrzej zaborowski
@ 2006-10-19 13:42 ` Tony Lindgren
0 siblings, 0 replies; 6+ messages in thread
From: Tony Lindgren @ 2006-10-19 13:42 UTC (permalink / raw)
To: balrogg; +Cc: linux-omap-open-source
* andrzej zaborowski <balrog@zabor.org> [060913 22:08]:
> Thanks for the review.
>
> On 02/09/06, David Brownell <david-b@pacbell.net> wrote:
> >On Friday 18 August 2006 3:01 am, andrzej zaborowski wrote:
> >> --- a/drivers/spi/Kconfig 2006-07-06 02:48:18.000000000 +0000
> >> +++ b/drivers/spi/Kconfig 2006-08-17 23:35:38.000000000 +0000
> >
> >Hmm, seems to me this is the wrong directory for this driver.
> >I can see it primarily being a touchscreen driver (with some
> >hwmon sensors too); or primarily an audio driver. But maybe
> >you're right that the core should be here.
>
> I was also wondering where it should go. With functions as diverse as
> audio, input and hwmon I can't tell what is the primary function.
>
> >
> >
> >> > +config TSC2102
> >> + depends on SPI_MASTER
> >> + tristate "TSC2102 codec support"
> >> + ---help---
> >> + Say Y here if you want support for the TSC2102 codec. It is
> >> + needed for the touchscreen driver on some boards.
> >
> >Don't describe it as a "codec". It's a multifunction chip, with audio
> >codec and amplifier, and sensors for touchscreen, battery voltage, and
> >temperature ... plus more, I don't have the spec sheet here.
>
> Ok. Basically this, audio controller and the sensors.
>
> >
> >How much does this driver need to differ from a TSC 2101 driver, by the
> >way? Enough to really need separate drivers?
>
> Well, I can imagine a TSC 2XXX driver but there would be *a lot* of
> conditional code because there are differences: some registers or
> single bits in particular registers have changed places or are
> "Reserved"; the capabilities differ: some chips include a keypad
> controller, have different audio outputs and inputs and different
> sensors. The main difference, however, is that on 2102 and later (e.g.
> 2301) a new conversion doesn't start until all converted data is read
> out of the registers which means that you always have to read the data
> or you won't receive another interrupt.
>
> >
> >
> >
> >> +module_param_named(sensor_scan_msecs, tsc.mode_msecs, uint, 0);
> >> +MODULE_PARM_DESC(sensor_scan_msecs, "Temperature & battery scan
> >interval");
> >
> >If you even need to have this be configurable, it should be part of
> >the hwmon support. AFAICT only the battery would need polling, and
> >that would only be for APM emulation...
>
> This value also affects the touchscreen responsiveness because
> touchscreen conversions have to be stopped for the scanning of other
> sensors.
>
> >
> >> +void tsc2102_set_volume(uint8_t left_ch, uint8_t right_ch)
> >> ...
> >> +void tsc2102_set_mute(int left_ch, int right_ch)
> >> ...
> >> +void tsc2102_get_mute(int *left_ch, int *right_ch)
> >> ...
> >> +void tsc2102_set_deemphasis(int enable)
> >> ...
> >> +void tsc2102_set_bassboost(int enable)
> >> ... etc ...
> >
> >All of that should surely be included only if the ALSA support is
> >included ...
>
> Ok. I now include it if CONFIG_SOUND is enabled because it's not ALSA
> specific.
>
> >
> >
> >> +static struct platform_device tsc2102_ts_device = {
> >> + .name = "tsc2102-ts",
> >> + .id = -1,
> >> +};
> >> +
> >> +static struct platform_device tsc2102_hwmon_device = {
> >> + .name = "tsc2102-hwmon",
> >> + .id = -1,
> >> +};
> >
> >I don't much like spilitting those out like that; if HWMON is
> >configured, just include tsc2102 hwmon hooks here, directly;
> >even if you do split out the touchscreen and audio support
> >into separate drivers.
>
> The attached version is how it looks with the hwmon part included in
> this same file. I liked more the approach with hwmon code separated in
> drivers/hwmon/ because then the "platform_device" framework itself
> takes care of the configuration. Also I think that a device
> "tsc2102-hwmon" should exist even if hwmon is not configured.
>
> >
> >
> >> +static struct platform_device tsc2102_alsa_device = {
> >> + .name = "tsc2102-alsa",
> >> + .id = -1,
> >> +};
> >> +
> >
> >Surely you should be setting the parent of all those devices
> >to be the spi_device.dev ???
>
> Ok.
>
> >
> >
> >Also, the linkage between this "core" to "layered" drivers
> >leaves a bit to be desired. Why not just provide those
> >layered drivers a hook function they can call, maybe along
> >the lines of
> >
> > tsc210x_write_sync(struct device *tsc210x,
> > unsigned register,
> > u16 value);
> >
> >Or whatever ... the "device" being of course the sysfs parent
> >of the platform device.
>
> I'm not sure I understand. There are hooks for writing and reading
> registers provided, but I figured it is better to access registers
> directly (using these hooks) only from one file because of the
> dependency between the chip's functions, e.g touchscreen and the
> sensors, they have to share the ADC time and shouldn't mess up the
> order of some accesses. Also, when the audio DAC is powered up, the
> proper clock must be enabled or the touchscreen will stop working (not
> the audio).
Pushing today.
Tony
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2006-10-19 13:42 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2006-08-18 10:01 [PATCH 1/4] TSC2102 main SPI driver andrzej zaborowski
2006-08-18 13:27 ` Komal Shah
2006-08-18 23:59 ` andrzej zaborowski
2006-09-02 5:14 ` David Brownell
2006-09-13 19:01 ` andrzej zaborowski
2006-10-19 13:42 ` Tony Lindgren
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox