From 92074255bda4707a6401096609feccf32875d4de Mon Sep 17 00:00:00 2001 From: Andrzej Zaborowski Date: Mon, 2 Apr 2007 13:15:00 -0400 Subject: [PATCH] SPI: Add TSC2102 main SPI driver This adds a driver for the TSC2102 chip by Texas Instruments. This driver is responsible for all communication with the chip and doesn't export anything for user-space, but for other drivers controlling devices connected to the chip. Signed-off-by: Andrzej Zaborowski --- drivers/spi/Kconfig | 7 + drivers/spi/Makefile | 1 + drivers/spi/tsc2102.c | 1198 +++++++++++++++++++++++++++++++++++ include/linux/spi/tsc2102.h | 175 +++++ 4 files changed, 1381 insertions(+) create mode 100644 drivers/spi/tsc2102.c create mode 100644 include/linux/spi/tsc2102.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 8139f0c9575..9daa4bb76d3 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -191,6 +191,13 @@ config SPI_TSC2101 At the moment it provides basic register read / write interface as well as a way to enable the MCLK clock. +config SPI_TSC2102 + depends on SPI_MASTER + tristate "TSC2102 codec support" + ---help--- + Say Y here if you want support for the TSC2102 chip. It + will be needed for the touchscreen driver on some boards. + config SPI_SPIDEV tristate "User mode SPI device driver support" depends on SPI_MASTER && EXPERIMENTAL diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index d87d100103c..d44d6516d29 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_AT25) += at25.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_TSC2101) += tsc2101.o +obj-$(CONFIG_SPI_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 index 00000000000..130b1c22dab --- /dev/null +++ b/drivers/spi/tsc2102.c @@ -0,0 +1,1198 @@ +/* + * drivers/spi/tsc2102.c + * + * TSC2102 interface driver. + * + * Copyright (c) 2005 Andrzej Zaborowski + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_APM +#include +#endif + +/* Bit field definitions for chip registers */ +#define TSC2102_ADC_TS_CONTROL 0x8bf4 +#define TSC2102_ADC_SCAN_CONTROL 0x2ff4 +#define TSC2102_ADC_T1_CONTROL 0x2bf4 +#define TSC2102_ADC_T2_CONTROL 0x33f4 +#define TSC2102_ADC_DAV 0x4000 +#define TSC2102_ADC_INT_REF 0x0016 +#define TSC2102_ADC_EXT_REF 0x0002 +#define TSC2102_CONFIG_TIMES 0x0008 +#define TSC2102_RESET 0xbb00 +#define TSC2102_ADC_PSTCM (1 << 15) +#define TSC2102_ADC_ADST (1 << 14) +#define TSC2102_TS_DAV 0x0780 +#define TSC2102_PS_DAV 0x0078 +#define TSC2102_T1_DAV 0x0004 +#define TSC2102_T2_DAV 0x0002 +#define TSC2102_DAC_ON 0x3ba0 +#define TSC2102_DAC_OFF 0xafa0 +#define TSC2102_FS44K (1 << 13) +#define TSC2102_PLL1_OFF 0x0000 +#define TSC2102_PLL1_44K 0x811c +#define TSC2102_PLL1_48K 0x8120 +#define TSC2102_PLL2_44K (5462 << 2) +#define TSC2102_PLL2_48K (1920 << 2) +#define TSC2102_SLVMS (1 << 11) +#define TSC2102_DEEMPF (1 << 0) +#define TSC2102_BASSBC (1 << 1) +#define TSC2102_KEYCLICK_OFF 0x0000 + +#define CS_CHANGE(val) 0 + +struct tsc2102_spi_req { + struct spi_device *dev; + uint16_t command; + uint16_t data; + struct spi_transfer *transfer; + struct spi_message message; +}; + +struct tsc2102_dev { + struct tsc2102_config *pdata; + spinlock_t lock, lock_sync; + struct clk *bclk_ck; + + int state; /* 0: TS, 1: Portscan, 2-3: Temps */ + struct timer_list ts_timer; /* Busy-wait for PEN UP */ + struct timer_list mode_timer; /* Change .state every some time */ + int pendown; + int data_pending; + uint16_t status, adc_status, adc_data[4]; + tsc2102_touch_t touch_cb; + tsc2102_coords_t coords_cb; + tsc2102_ports_t ports_cb; + tsc2102_temp_t temp1_cb; + tsc2102_temp_t temp2_cb; + unsigned int ts_msecs; /* Interval for .ts_timer */ + unsigned int mode_msecs; /* Interval for .mode_timer */ + + struct spi_device *spi; + struct spi_transfer *transfers; + struct tsc2102_spi_req req_adc; + struct tsc2102_spi_req req_status; + struct tsc2102_spi_req req_pressure; + struct tsc2102_spi_req req_stopadc; + struct tsc2102_spi_req req_mode; + + int bat[2], aux[1], temp[2]; + struct class_device *hwmondev; +}; + +static struct tsc2102_dev tsc; + +module_param_named(touch_check_msecs, tsc.ts_msecs, uint, 0); +MODULE_PARM_DESC(touch_check_msecs, "Pen-up polling interval in msecs"); + +module_param_named(sensor_scan_msecs, tsc.mode_msecs, uint, 0); +MODULE_PARM_DESC(sensor_scan_msecs, "Temperature & battery scan interval"); + +void tsc2102_write_sync(int page, u8 address, u16 data) +{ + static struct tsc2102_spi_req req; + static struct spi_transfer transfer[2]; + int ret; + + spi_message_init(&req.message); + req.transfer = transfer; + + /* Address */ + req.command = (page << 11) | (address << 5); + req.transfer[0].tx_buf = &req.command; + req.transfer[0].rx_buf = 0; + req.transfer[0].len = 2; + spi_message_add_tail(&req.transfer[0], &req.message); + + /* Data */ + req.transfer[1].tx_buf = &data; + req.transfer[1].rx_buf = 0; + req.transfer[1].len = 2; + req.transfer[1].cs_change = CS_CHANGE(1); + spi_message_add_tail(&req.transfer[1], &req.message); + + ret = spi_sync(tsc.spi, &req.message); + if (!ret && req.message.status) + ret = req.message.status; + + if (ret) + printk(KERN_ERR "%s: error %i in SPI request\n", + __FUNCTION__, ret); +} + +void tsc2102_reads_sync(int page, u8 startaddress, u16 *data, int numregs) +{ + static struct tsc2102_spi_req req; + static struct spi_transfer transfer[6]; + int ret, i, j; + + BUG_ON(numregs + 1 > ARRAY_SIZE(transfer)); + + spi_message_init(&req.message); + req.transfer = transfer; + i = 0; + j = 0; + + /* Address */ + req.command = 0x8000 | (page << 11) | (startaddress << 5); + req.transfer[i].tx_buf = &req.command; + req.transfer[i].rx_buf = 0; + req.transfer[i].len = 2; + spi_message_add_tail(&req.transfer[i ++], &req.message); + + /* Data */ + while (j < numregs) { + req.transfer[i].tx_buf = 0; + req.transfer[i].rx_buf = &data[j ++]; + req.transfer[i].len = 2; + req.transfer[i].cs_change = CS_CHANGE(j == numregs); + spi_message_add_tail(&req.transfer[i ++], &req.message); + } + + ret = spi_sync(tsc.spi, &req.message); + if (!ret && req.message.status) + ret = req.message.status; + + if (ret) + printk(KERN_ERR "%s: error %i in SPI request\n", + __FUNCTION__, ret); +} + +u16 tsc2102_read_sync(int page, u8 address) +{ + u16 ret; + tsc2102_reads_sync(page, address, &ret, 1); + return ret; +} + +static void tsc2102_write_async( + struct tsc2102_spi_req *spi, int page, u8 address, u16 data, + void (*complete)(struct tsc2102_dev *context)) +{ + int ret; + + spi_message_init(&spi->message); + spi->message.complete = (void (*)(void *)) complete; + spi->message.context = &tsc; + + /* Address */ + spi->command = (page << 11) | (address << 5); + spi->transfer[0].tx_buf = &spi->command; + spi->transfer[0].rx_buf = 0; + spi->transfer[0].len = 2; + spi_message_add_tail(&spi->transfer[0], &spi->message); + + /* Data */ + spi->data = data; + spi->transfer[1].tx_buf = &spi->data; + spi->transfer[1].rx_buf = 0; + spi->transfer[1].len = 2; + spi->transfer[1].cs_change = CS_CHANGE(1); + spi_message_add_tail(&spi->transfer[1], &spi->message); + + ret = spi_async(spi->dev, &spi->message); + if (ret) + printk(KERN_ERR "%s: error %i in SPI request\n", + __FUNCTION__, ret); +} + +static void tsc2102_reads_async(struct tsc2102_spi_req *spi, + int page, u8 startaddress, u16 *data, int numregs, + void (*complete)(struct tsc2102_dev *context)) +{ + int ret, i, j; + + spi_message_init(&spi->message); + spi->message.complete = (void (*)(void *)) complete; + spi->message.context = &tsc; + i = 0; + j = 0; + + /* Address */ + spi->command = 0x8000 | (page << 11) | (startaddress << 5); + spi->transfer[i].tx_buf = &spi->command; + spi->transfer[i].rx_buf = 0; + spi->transfer[i].len = 2; + spi_message_add_tail(&spi->transfer[i ++], &spi->message); + + /* Data */ + while (j < numregs) { + spi->transfer[i].tx_buf = 0; + spi->transfer[i].rx_buf = &data[j ++]; + spi->transfer[i].len = 2; + spi->transfer[i].cs_change = CS_CHANGE(j == numregs); + spi_message_add_tail(&spi->transfer[i ++], &spi->message); + } + + ret = spi_async(spi->dev, &spi->message); + if (ret) + printk(KERN_ERR "%s: error %i in SPI request\n", + __FUNCTION__, ret); +} + +static void tsc2102_read_async(struct tsc2102_spi_req *spi, + int page, u8 address, u16 *ret, + void (*complete)(struct tsc2102_dev *context)) +{ + tsc2102_reads_async(spi, page, address, ret, 1, complete); +} + +static void tsc2102_request_alloc(struct tsc2102_dev *dev, + struct tsc2102_spi_req *spi, int direction, int numregs, + struct spi_transfer **buffer) +{ + spi->dev = dev->spi; + + if (direction == 1) /* Write */ + numregs = 2; + else /* Read */ + numregs += 1; + + spi->transfer = *buffer; + *buffer += numregs; +} + +#define tsc2102_cb_register_func(cb, cb_t) \ +int tsc2102_ ## cb(cb_t handler) \ +{ \ + spin_lock(&tsc.lock); \ + \ + /* Lock the module */ \ + if (handler && !tsc.cb) \ + if (!try_module_get(THIS_MODULE)) { \ + printk(KERN_INFO "Failed to get TSC module\n"); \ + } \ + if (!handler && tsc.cb) \ + module_put(THIS_MODULE); \ + \ + tsc.cb = handler; \ + \ + spin_unlock(&tsc.lock); \ + return 0; \ +} + +tsc2102_cb_register_func(touch_cb, tsc2102_touch_t) +tsc2102_cb_register_func(coords_cb, tsc2102_coords_t) +tsc2102_cb_register_func(ports_cb, tsc2102_ports_t) +tsc2102_cb_register_func(temp1_cb, tsc2102_temp_t) +tsc2102_cb_register_func(temp2_cb, tsc2102_temp_t) + +#ifdef DEBUG +static void tsc2102_print_dav(void) +{ + u16 status = tsc2102_read_sync(TSC2102_TS_STATUS_CTRL); + if (status & 0x0fff) + printk("TSC2102: data in"); + if (status & 0x0400) + printk(" X"); + if (status & 0x0200) + printk(" Y"); + if (status & 0x0100) + printk(" Z1"); + if (status & 0x0080) + printk(" Z2"); + if (status & 0x0040) + printk(" BAT1"); + if (status & 0x0020) + printk(" BAT2"); + if (status & 0x0010) + printk(" AUX1"); + if (status & 0x0008) + printk(" AUX2"); + if (status & 0x0004) + printk(" TEMP1"); + if (status & 0x0002) + printk(" TEMP2"); + if (status & 0x0001) + printk(" KP"); + if (status & 0x0fff) + printk(".\n"); +} +#endif + +static void tsc2102_complete_dummy(struct tsc2102_dev *dev) +{ +} + +static inline void tsc2102_touchscreen_mode(struct tsc2102_dev *dev) +{ + /* Scan X, Y, Z1, Z2, chip controlled, 12-bit, 16 samples, 500 usec */ + tsc2102_write_async(&dev->req_mode, + TSC2102_TS_ADC_CTRL, TSC2102_ADC_TS_CONTROL, + tsc2102_complete_dummy); +} + +static inline void tsc2102_portscan_mode(struct tsc2102_dev *dev) +{ + /* Scan BAT1, BAT2, AUX, 12-bit, 16 samples, 500 usec */ + tsc2102_write_async(&dev->req_mode, + TSC2102_TS_ADC_CTRL, TSC2102_ADC_SCAN_CONTROL, + tsc2102_complete_dummy); +} + +static inline void tsc2102_temp1_mode(struct tsc2102_dev *dev) +{ + /* Scan TEMP1, 12-bit, 16 samples, 500 usec */ + tsc2102_write_async(&dev->req_mode, + TSC2102_TS_ADC_CTRL, TSC2102_ADC_T1_CONTROL, + tsc2102_complete_dummy); +} + +static inline void tsc2102_temp2_mode(struct tsc2102_dev *dev) +{ + /* Scan TEMP2, 12-bit, 16 samples, 500 usec */ + tsc2102_write_async(&dev->req_mode, + TSC2102_TS_ADC_CTRL, TSC2102_ADC_T2_CONTROL, + tsc2102_complete_dummy); +} + +static void tsc2102_mode(struct tsc2102_dev *dev) +{ + switch (dev->state) { + case 0: + tsc2102_touchscreen_mode(dev); + break; + case 1: + tsc2102_portscan_mode(dev); + break; + case 2: + tsc2102_temp1_mode(dev); + break; + case 3: + tsc2102_temp2_mode(dev); + break; + default: + dev->state = 0; + tsc2102_touchscreen_mode(dev); + break; + } +} + +/* Lock is held when this is called. */ +static void tsc2102_new_mode(struct tsc2102_dev *dev) +{ + /* Abort current conversion if any */ + tsc2102_write_async(&dev->req_stopadc, + TSC2102_TS_ADC_CTRL, TSC2102_ADC_ADST, + tsc2102_complete_dummy); + + dev->state ++; + tsc2102_mode(dev); +} + +static void tsc2102_check_status(struct tsc2102_dev *dev); + +/* TSC has new data for us availiable. */ +static irqreturn_t tsc2102_handler(int irq, void *dev_id) +{ + struct tsc2102_dev *dev = (struct tsc2102_dev *) dev_id; + spin_lock_irq(&dev->lock); + + if (!dev->data_pending) + tsc2102_check_status(dev); + + dev->data_pending ++; + + spin_unlock_irq(&dev->lock); + return IRQ_HANDLED; +} + +static void tsc2102_data_report(struct tsc2102_dev *dev) +{ + if (dev->status & TSC2102_TS_DAV) { + if (dev->coords_cb) + dev->coords_cb( + dev->adc_data[0], dev->adc_data[1], + dev->adc_data[2], dev->adc_data[3]); + } + + if (dev->status & TSC2102_PS_DAV) { + if (dev->ports_cb) + dev->ports_cb(dev->adc_data[0], + dev->adc_data[1], dev->adc_data[2]); + dev->bat[0] = dev->adc_data[0]; + dev->bat[1] = dev->adc_data[1]; + dev->aux[0] = dev->adc_data[2]; + } + + if (dev->status & TSC2102_T1_DAV) { + if (dev->temp1_cb) + dev->temp1_cb(*dev->adc_data); + dev->temp[0] = *dev->adc_data; + } + + if (dev->status & TSC2102_T2_DAV) { + if (dev->temp2_cb) + dev->temp2_cb(*dev->adc_data); + dev->temp[1] = *dev->adc_data; + } + + spin_lock_irq(&dev->lock); + + dev->data_pending --; + + /* + * This may happen if the registers were successfully read and a + * new conversion was started and completed by the TSC before the + * completion for SPI read was called. + */ + if (dev->data_pending) + tsc2102_check_status(dev); + + if (dev->status & (TSC2102_PS_DAV | TSC2102_T1_DAV | TSC2102_T2_DAV)) + tsc2102_new_mode(dev); + + spin_unlock_irq(&dev->lock); +} + +static void tsc2102_status_report(struct tsc2102_dev *dev) +{ + /* + * Read all converted data from corresponding registers + * so that the ADC can move on to a new conversion. + */ + if (dev->status & TSC2102_TS_DAV) { + tsc2102_reads_async(&dev->req_adc, TSC2102_TS_X, + dev->adc_data, 4, tsc2102_data_report); + if (!dev->pendown) { + dev->pendown = 1; + if (dev->touch_cb) + dev->touch_cb(1); + + mod_timer(&dev->ts_timer, jiffies + + msecs_to_jiffies(dev->ts_msecs)); + } + } + + if (dev->status & TSC2102_PS_DAV) { + tsc2102_reads_async(&dev->req_adc, TSC2102_TS_BAT1, + dev->adc_data, 3, tsc2102_data_report); + } + + if (dev->status & TSC2102_T1_DAV) { + tsc2102_read_async(&dev->req_adc, TSC2102_TS_TEMP1, + dev->adc_data, tsc2102_data_report); + } + + if (dev->status & TSC2102_T2_DAV) { + tsc2102_read_async(&dev->req_adc, TSC2102_TS_TEMP2, + dev->adc_data, tsc2102_data_report); + } + + if (!(dev->status & (TSC2102_TS_DAV | TSC2102_PS_DAV | + TSC2102_T1_DAV | TSC2102_T2_DAV))) { + spin_lock_irq(&dev->lock); + dev->data_pending --; + spin_unlock_irq(&dev->lock); + + WARN_ON(!dev->state); + } +} + +static void tsc2102_check_status(struct tsc2102_dev *dev) +{ + tsc2102_read_async(&dev->req_status, TSC2102_TS_STATUS_CTRL, + &dev->status, tsc2102_status_report); +} + +static void tsc2102_mode_timer(unsigned long data) +{ + struct tsc2102_dev *dev = (struct tsc2102_dev *) data; + spin_lock_irq(&dev->lock); + + BUG_ON(dev->state); + + tsc2102_new_mode(dev); + + mod_timer(&dev->mode_timer, jiffies + + msecs_to_jiffies(dev->mode_msecs)); + spin_unlock_irq(&dev->lock); +} + +/* + * There are at least three ways to check for pen-up: + * - the PINT/DAV pin state, + * - reading PSTCM bit in ADC Control register (D15, offset 0x00), + * - reading ADST bit in ADC Control register (D14, offset 0x00), + * ADC idle would indicate no screen touch. + * Unfortunately none of them seems to be 100% accurate and you will + * find they are totally inconsistent, i.e. you get to see any arbitrary + * combination of values in these three bits. So we will busy-wait + * for a moment when all three indicate a pen-up, using a timer, before + * we report a pen-up. + */ +static void tsc2102_pressure_report(struct tsc2102_dev *dev) +{ + if (!dev->pendown) + return; + + if (dev->state || + (dev->adc_status & TSC2102_ADC_PSTCM) || + !(dev->adc_status & TSC2102_ADC_ADST)) { + mod_timer(&dev->ts_timer, jiffies + + msecs_to_jiffies(dev->ts_msecs)); + } else { + dev->pendown = 0; + if (dev->touch_cb) + dev->touch_cb(0); + } +} + +static void tsc2102_pressure(unsigned long data) +{ + struct tsc2102_dev *dev = (struct tsc2102_dev *) data; + + BUG_ON(!dev->pendown); + + tsc2102_read_async(&dev->req_pressure, TSC2102_TS_ADC_CTRL, + &dev->adc_status, tsc2102_pressure_report); +} + +#ifdef CONFIG_SOUND +/* + * Volume level values should be in the range [0, 127]. + * Higher values mean lower volume. + */ +void tsc2102_set_volume(uint8_t left_ch, uint8_t right_ch) +{ + u16 val; + if (left_ch == 0x00 || left_ch == 0x7f) /* All 0's or all 1's */ + left_ch ^= 0x7f; + if (right_ch == 0x00 || right_ch == 0x7f) + right_ch ^= 0x7f; + + spin_lock(&tsc.lock_sync); + + val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL); + + val &= 0x8080; /* Preserve mute-bits */ + val |= (left_ch << 8) | right_ch; + + tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, val); + + spin_unlock(&tsc.lock_sync); +} + +void tsc2102_set_mute(int left_ch, int right_ch) +{ + u16 val; + spin_lock(&tsc.lock_sync); + + val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL); + + val &= 0x7f7f; /* Preserve volume settings */ + val |= (left_ch << 15) | (right_ch << 7); + + tsc2102_write_sync(TSC2102_DAC_GAIN_CTRL, val); + + spin_unlock(&tsc.lock_sync); +} + +void tsc2102_get_mute(int *left_ch, int *right_ch) +{ + u16 val; + spin_lock(&tsc.lock_sync); + + val = tsc2102_read_sync(TSC2102_DAC_GAIN_CTRL); + + spin_unlock(&tsc.lock_sync); + + *left_ch = !!(val & (1 << 15)); + *right_ch = !!(val & (1 << 7)); +} + +void tsc2102_set_deemphasis(int enable) +{ + u16 val; + spin_lock(&tsc.lock_sync); + val = tsc2102_read_sync(TSC2102_DAC_POWER_CTRL); + + if (enable) + val &= ~TSC2102_DEEMPF; + else + val |= TSC2102_DEEMPF; + + tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, val); + spin_unlock(&tsc.lock_sync); +} + +void tsc2102_set_bassboost(int enable) +{ + u16 val; + spin_lock(&tsc.lock_sync); + val = tsc2102_read_sync(TSC2102_DAC_POWER_CTRL); + + if (enable) + val &= ~TSC2102_BASSBC; + else + val |= TSC2102_BASSBC; + + tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, val); + spin_unlock(&tsc.lock_sync); +} + +/* {rate, dsor, fsref} */ +static const struct tsc2102_rate_info_s tsc2102_rates[] = { + /* Fsref / 6.0 */ + {7350, 63, 1}, + {8000, 63, 0}, + /* Fsref / 6.0 */ + {7350, 54, 1}, + {8000, 54, 0}, + /* Fsref / 5.0 */ + {8820, 45, 1}, + {9600, 45, 0}, + /* Fsref / 4.0 */ + {11025, 36, 1}, + {12000, 36, 0}, + /* Fsref / 3.0 */ + {14700, 27, 1}, + {16000, 27, 0}, + /* Fsref / 2.0 */ + {22050, 18, 1}, + {24000, 18, 0}, + /* Fsref / 1.5 */ + {29400, 9, 1}, + {32000, 9, 0}, + /* Fsref */ + {44100, 0, 1}, + {48000, 0, 0}, + + {0, 0, 0}, +}; + +int tsc2102_set_rate(int rate) +{ + int i; + uint16_t val; + + for (i = 0; tsc2102_rates[i].sample_rate; i ++) + if (tsc2102_rates[i].sample_rate == rate) + break; + if (tsc2102_rates[i].sample_rate == 0) { + printk(KERN_ERR "Unknown sampling rate %i.0 Hz\n", rate); + return -EINVAL; + } + + spin_lock(&tsc.lock_sync); + + tsc2102_write_sync(TSC2102_AUDIO1_CTRL, tsc2102_rates[i].divisor); + + val = tsc2102_read_sync(TSC2102_AUDIO3_CTRL); + + if (tsc2102_rates[i].fs_44k) { + tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val | TSC2102_FS44K); + /* Enable Phase-locked-loop, set up clock dividers */ + tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_44K); + tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_44K); + } else { + tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val & ~TSC2102_FS44K); + /* Enable Phase-locked-loop, set up clock dividers */ + tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_48K); + tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_48K); + } + + spin_unlock(&tsc.lock_sync); + return 0; +} + +/* + * Perform basic set-up with default values and power the DAC on. + */ +void tsc2102_dac_power(int state) +{ + spin_lock(&tsc.lock_sync); + + if (state) { + /* 16-bit words, DSP mode, sample at Fsref */ + tsc2102_write_sync(TSC2102_AUDIO1_CTRL, 0x0100); + /* Keyclicks off, soft-stepping at normal rate */ + tsc2102_write_sync(TSC2102_AUDIO2_CTRL, TSC2102_KEYCLICK_OFF); + /* 44.1 kHz Fsref, continuous transfer mode, master DAC */ + tsc2102_write_sync(TSC2102_AUDIO3_CTRL, 0x2800); + /* Soft-stepping enabled */ + tsc2102_write_sync(TSC2102_AUDIO4_CTRL, 0x0000); + + /* PLL generates 44.1 kHz */ + tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_44K); + tsc2102_write_sync(TSC2102_PLL2_CTRL, TSC2102_PLL2_44K); + + /* Codec & DAC power up, virtual ground disabled */ + tsc2102_write_sync(TSC2102_DAC_POWER_CTRL, TSC2102_DAC_ON); + } else { + /* All off */ + tsc2102_write_sync(TSC2102_AUDIO4_CTRL, TSC2102_KEYCLICK_OFF); + tsc2102_write_sync(TSC2102_PLL1_CTRL, TSC2102_PLL1_OFF); + } + + spin_unlock(&tsc.lock_sync); +} + +void tsc2102_set_i2s_master(int state) +{ + uint16_t val; + spin_lock(&tsc.lock_sync); + + val = tsc2102_read_sync(TSC2102_AUDIO3_CTRL); + + if (state) + tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val | TSC2102_SLVMS); + else + tsc2102_write_sync(TSC2102_AUDIO3_CTRL, val & ~TSC2102_SLVMS); + + spin_unlock(&tsc.lock_sync); +} +#endif /* CONFIG_SOUND */ + +static int tsc2102_configure(struct tsc2102_dev *dev) +{ + /* Reset the chip */ + tsc2102_write_sync(TSC2102_TS_RESET_CTRL, TSC2102_RESET); + + /* Reference mode, 100 usec delay, 1.25 V reference */ + if (dev->pdata->use_internal) + tsc2102_write_sync(TSC2102_TS_REF_CTRL, TSC2102_ADC_INT_REF); + else + tsc2102_write_sync(TSC2102_TS_REF_CTRL, TSC2102_ADC_EXT_REF); + + /* 84 usec precharge time, 32 usec sense time */ + tsc2102_write_sync(TSC2102_TS_CONFIG_CTRL, TSC2102_CONFIG_TIMES); + + /* PINT/DAV acts as DAV */ + tsc2102_write_sync(TSC2102_TS_STATUS_CTRL, TSC2102_ADC_DAV); + + tsc2102_mode(dev); + mod_timer(&dev->mode_timer, jiffies + + msecs_to_jiffies(dev->mode_msecs)); + return 0; +} + +/* + * Retrieves chip revision. Should be always 1. + */ +int tsc2102_get_revision(void) +{ + return tsc2102_read_sync(TSC2102_AUDIO3_CTRL) & 7; +} + +/* + * Emit a short keyclick typically in order to give feedback to + * user on specific events. + * + * amplitude must be between 0 (lowest) and 2 (highest). + * freq must be between 0 (corresponds to 62.5 Hz) and 7 (8 kHz). + * length should be between 2 and 32 periods. + * + * This function sleeps but doesn't sleep until the sound has + * finished. + */ +void tsc2102_keyclick(int amplitude, int freq, int length) +{ + u16 val; + spin_lock(&tsc.lock_sync); + val = tsc2102_read_sync(TSC2102_AUDIO2_CTRL); + val &= 0x800f; + + /* Set amplitude */ + switch (amplitude) { + case 1: + val |= 4 << 12; + break; + case 2: + val |= 7 << 12; + break; + default: + break; + } + + /* Frequency */ + val |= (freq & 0x7) << 8; + + /* Round to nearest supported length */ + if (length > 20) + val |= 4 << 4; + else if (length > 6) + val |= 3 << 4; + else if (length > 4) + val |= 2 << 4; + else if (length > 2) + val |= 1 << 4; + + /* Enable keyclick */ + val |= 0x8000; + + tsc2102_write_sync(TSC2102_AUDIO2_CTRL, val); + spin_unlock(&tsc.lock_sync); +} + +#ifdef CONFIG_HWMON +#define TSC2102_INPUT(devname, field) \ +static ssize_t show_ ## devname(struct device *dev, \ + struct device_attribute *devattr, char *buf) \ +{ \ + struct tsc2102_dev *devhwmon = dev_get_drvdata(dev); \ + int value = devhwmon->field; \ + return sprintf(buf, "%i\n", value); \ +} \ +static DEVICE_ATTR(devname ## _input, S_IRUGO, show_ ## devname, NULL); + +TSC2102_INPUT(in0, bat[0]) +TSC2102_INPUT(in1, bat[1]) +TSC2102_INPUT(in2, aux[0]) +TSC2102_INPUT(in3, temp[0]) +TSC2102_INPUT(in4, temp[1]) + +static ssize_t show_temp1(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct tsc2102_dev *devhwmon = dev_get_drvdata(dev); + int t1, t2; + int value, diff; + + t1 = devhwmon->temp[0]; + t2 = devhwmon->temp[1]; + + /* + * Use method #2 (differential) to calculate current temperature. + * The difference between TEMP2 and TEMP1 input values is + * multiplied by a constant to obtain current temperature. + * To find this constant we use the values measured at 25 C as + * thermometer calibration data. + * + * 298150 is 25 degrees Celcius represented in Kelvins and + * multiplied by 1000 for fixed point precision (273.15 + 25). + * 273150 is zero degrees Celcius. + */ + diff = devhwmon->pdata->temp_at25c[1] - devhwmon->pdata->temp_at25c[0]; + BUG_ON(diff == 0); + value = (t2 - t1) * 298150 / diff; /* This is in Kelvins now */ + + t1 = value - 273150; /* Celcius millidegree */ + return sprintf(buf, "%i\n", t1); +} +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1, NULL); +#endif /* CONFIG_HWMON */ + +#ifdef CONFIG_APM +static void tsc2102_get_power_status(struct apm_power_info *info) +{ + tsc.pdata->apm_report(info, tsc.bat); +} +#endif + +#ifdef CONFIG_PM +/* + * Suspend the chip. + */ +static int +tsc2102_suspend(struct spi_device *spi, pm_message_t state) +{ + struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev); + + if (!dev) + return 0; + + spin_lock(&dev->lock_sync); + + del_timer(&dev->mode_timer); + del_timer(&dev->ts_timer); + + if (dev->pendown && dev->touch_cb) + dev->touch_cb(0); + + /* Abort current conversion and power down the ADC */ + tsc2102_write_sync(TSC2102_TS_ADC_CTRL, TSC2102_ADC_ADST); + + dev->spi->dev.power.power_state = state; + + spin_unlock(&dev->lock_sync); + return 0; +} + +/* + * Resume chip operation. + */ +static int tsc2102_resume(struct spi_device *spi) +{ + struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev); + int err; + + if (!dev) + return 0; + + spin_lock(&dev->lock_sync); + + dev->spi->dev.power.power_state = PMSG_ON; + + dev->state = 0; + dev->pendown = 0; + + err = tsc2102_configure(dev); + + spin_unlock(&dev->lock_sync); + return err; +} +#else +#define tsc2102_suspend NULL +#define tsc2102_resume NULL +#endif + +static struct platform_device tsc2102_ts_device = { + .name = "tsc2102-ts", + .id = -1, +}; + +static struct platform_device tsc2102_alsa_device = { + .name = "tsc2102-alsa", + .id = -1, +}; + +static int tsc2102_probe(struct spi_device *spi) +{ + struct tsc2102_config *pdata = spi->dev.platform_data; + struct spi_transfer *spi_buffer; + int err = 0; + + if (!pdata) { + printk(KERN_ERR "TSC2102: Platform data not supplied\n"); + return -ENOENT; + } + + if (!spi->irq) { + printk(KERN_ERR "TSC2102: Invalid irq value\n"); + return -ENOENT; + } + + tsc.pdata = pdata; + tsc.state = 0; + tsc.pendown = 0; + tsc.data_pending = 0; + tsc.ts_msecs = 20; + tsc.mode_msecs = 1000; + tsc.spi = spi; + + /* Allocate enough struct spi_transfer's for all requests */ + spi_buffer = kzalloc(sizeof(struct spi_transfer) * 16, GFP_KERNEL); + if (!spi_buffer) { + printk(KERN_ERR "TSC2102: No memory for SPI buffers\n"); + return -ENOMEM; + } + + tsc.transfers = spi_buffer; + tsc2102_request_alloc(&tsc, &tsc.req_adc, 0, 4, &spi_buffer); + tsc2102_request_alloc(&tsc, &tsc.req_status, 0, 1, &spi_buffer); + tsc2102_request_alloc(&tsc, &tsc.req_pressure, 0, 1, &spi_buffer); + tsc2102_request_alloc(&tsc, &tsc.req_stopadc, 1, 1, &spi_buffer); + tsc2102_request_alloc(&tsc, &tsc.req_mode, 1, 1, &spi_buffer); + + spin_lock_init(&tsc.lock); + spin_lock(&tsc.lock_sync); + + /* Get the BCLK - assuming the rate is at 12000000 */ + tsc.bclk_ck = clk_get(0, "bclk"); + if (!tsc.bclk_ck) { + printk(KERN_ERR "Unable to get the clock BCLK\n"); + err = -EPERM; + goto done; + } + + clk_enable(tsc.bclk_ck); + + if (request_irq(spi->irq, tsc2102_handler, IRQF_SAMPLE_RANDOM | + IRQF_TRIGGER_FALLING, "tsc2102", &tsc)) { + printk(KERN_ERR "Could not allocate touchscreen IRQ!\n"); + err = -EINVAL; + goto err_clk; + } + + setup_timer(&tsc.ts_timer, + tsc2102_pressure, (unsigned long) &tsc); + setup_timer(&tsc.mode_timer, + tsc2102_mode_timer, (unsigned long) &tsc); + + /* Set up the communication bus */ + dev_set_drvdata(&spi->dev, &tsc); + spi->dev.power.power_state = PMSG_ON; + spi->mode = SPI_MODE_1; + spi->bits_per_word = 16; + err = spi_setup(spi); + if (err) + goto err_timer; + + /* Now try to detect the chip, make first contact */ + if (tsc2102_get_revision() != 0x1) { + printk(KERN_ERR "No TI TSC2102 chip found!\n"); + goto err_timer; + } + + err = tsc2102_configure(&tsc); + if (err) + goto err_timer; + + /* Register devices controlled by TSC 2102 */ + tsc2102_ts_device.dev.platform_data = pdata; + tsc2102_ts_device.dev.parent = &spi->dev; + err = platform_device_register(&tsc2102_ts_device); + if (err) + goto err_timer; + + tsc2102_alsa_device.dev.platform_data = pdata->alsa_config; + tsc2102_alsa_device.dev.parent = &spi->dev; + err = platform_device_register(&tsc2102_alsa_device); + if (err) + goto err_ts; + +#ifdef CONFIG_HWMON + tsc.hwmondev = hwmon_device_register(&spi->dev); + if (IS_ERR(tsc.hwmondev)) { + printk(KERN_ERR "tsc2102_hwmon: Device registration failed\n"); + err = PTR_ERR(tsc.hwmondev); + goto err_alsa; + } + + if (pdata->monitor & TSC_BAT1) + err |= device_create_file(&spi->dev, &dev_attr_in0_input); + if (pdata->monitor & TSC_BAT2) + err |= device_create_file(&spi->dev, &dev_attr_in1_input); + if (pdata->monitor & TSC_AUX) + err |= device_create_file(&spi->dev, &dev_attr_in2_input); + if (pdata->monitor & TSC_TEMP) { + err |= device_create_file(&spi->dev, &dev_attr_temp1_input); + err |= device_create_file(&spi->dev, &dev_attr_in3_input); + err |= device_create_file(&spi->dev, &dev_attr_in4_input); + } + + if (err) + printk(KERN_ERR "tsc2102_hwmon: Creating one or more " + "attribute files failed\n"); + err = 0; /* Not fatal */ +#endif + +#ifdef CONFIG_APM + if (pdata->apm_report) + apm_get_power_status = tsc2102_get_power_status; +#endif + + if (!err) + goto done; + +err_alsa: + platform_device_unregister(&tsc2102_alsa_device); +err_ts: + platform_device_unregister(&tsc2102_ts_device); +err_timer: + del_timer(&tsc.ts_timer); + del_timer(&tsc.mode_timer); + dev_set_drvdata(&spi->dev, NULL); +err_clk: + clk_disable(tsc.bclk_ck); + clk_put(tsc.bclk_ck); +done: + spin_unlock(&tsc.lock_sync); + return err; +} + +static int tsc2102_remove(struct spi_device *spi) +{ + struct tsc2102_dev *dev = dev_get_drvdata(&spi->dev); + + spin_lock(&dev->lock_sync); + + platform_device_unregister(&tsc2102_ts_device); + platform_device_unregister(&tsc2102_alsa_device); + + dev_set_drvdata(&spi->dev, NULL); + + /* Release the BCLK */ + clk_disable(dev->bclk_ck); + clk_put(dev->bclk_ck); + + del_timer(&tsc.mode_timer); + del_timer(&tsc.ts_timer); + + kfree(tsc.transfers); + +#ifdef CONFIG_HWMON + hwmon_device_unregister(dev->hwmondev); +#endif + +#ifdef CONFIG_APM + apm_get_power_status = 0; +#endif + + spin_unlock(&dev->lock_sync); + + return 0; +} + +static struct spi_driver tsc2102_driver = { + .probe = tsc2102_probe, + .remove = tsc2102_remove, + .suspend = tsc2102_suspend, + .resume = tsc2102_resume, + .driver = { + .name = "tsc2102", + .owner = THIS_MODULE, + .bus = &spi_bus_type, + }, +}; + +static char __initdata banner[] = KERN_INFO "TI TSC2102 driver initializing\n"; + +static int __init tsc2102_init(void) +{ + printk(banner); + return spi_register_driver(&tsc2102_driver); +} + +static void __exit tsc2102_exit(void) +{ + spi_unregister_driver(&tsc2102_driver); +} + +module_init(tsc2102_init); +module_exit(tsc2102_exit); + +EXPORT_SYMBOL(tsc2102_read_sync); +EXPORT_SYMBOL(tsc2102_reads_sync); +EXPORT_SYMBOL(tsc2102_write_sync); +EXPORT_SYMBOL(tsc2102_keyclick); + +MODULE_AUTHOR("Andrzej Zaborowski"); +MODULE_DESCRIPTION("Interface driver for TI TSC2102 chips."); +MODULE_LICENSE("GPL"); diff --git a/include/linux/spi/tsc2102.h b/include/linux/spi/tsc2102.h new file mode 100644 index 00000000000..b779fb1176e --- /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 + * + * 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 */ -- 2.41.1