Commit e9fb342a authored by Jarkko Nikula's avatar Jarkko Nikula Committed by Tony Lindgren

SPI: Add support for TSC2301 protocol driver

This adds a driver for TSC2301 including support for audio, keypad and
touchscreen. Patch is originally developed for Nokia N800 by Imre Deak,
Jarkko Nikula, Jarkko Oikarinen and Juha Yrjola.
Signed-off-by: default avatarJarkko Nikula <jarkko.nikula@nokia.com>
parent 92074255
...@@ -198,6 +198,28 @@ config SPI_TSC2102 ...@@ -198,6 +198,28 @@ config SPI_TSC2102
Say Y here if you want support for the TSC2102 chip. It Say Y here if you want support for the TSC2102 chip. It
will be needed for the touchscreen driver on some boards. will be needed for the touchscreen driver on some boards.
config SPI_TSC2301
tristate "TSC2301 driver"
depends on SPI_MASTER
help
Say Y here if you have a TSC2301 chip connected to an SPI
bus on your board.
The TSC2301 is a highly integrated PDA analog interface circuit.
It contains a complete 12-bit A/D resistive touch screen
converter (ADC) including drivers, touch pressure measurement
capability, keypad controller, and 8-bit D/A converter (DAC) output
for LCD contrast control.
To compile this driver as a module, choose M here: the
module will be called tsc2301.
config SPI_TSC2301_AUDIO
boolean "TSC2301 audio support"
depends on SPI_TSC2301 && SND
help
Say Y here for if you are using the audio features of TSC2301.
config SPI_SPIDEV config SPI_SPIDEV
tristate "User mode SPI device driver support" tristate "User mode SPI device driver support"
depends on SPI_MASTER && EXPERIMENTAL depends on SPI_MASTER && EXPERIMENTAL
......
...@@ -31,6 +31,9 @@ obj-$(CONFIG_SPI_AT25) += at25.o ...@@ -31,6 +31,9 @@ obj-$(CONFIG_SPI_AT25) += at25.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o obj-$(CONFIG_SPI_SPIDEV) += spidev.o
obj-$(CONFIG_SPI_TSC2101) += tsc2101.o obj-$(CONFIG_SPI_TSC2101) += tsc2101.o
obj-$(CONFIG_SPI_TSC2102) += tsc2102.o obj-$(CONFIG_SPI_TSC2102) += tsc2102.o
obj-$(CONFIG_SPI_TSC2301) += tsc2301.o
tsc2301-objs := tsc2301-core.o
tsc2301-$(CONFIG_SPI_TSC2301_AUDIO) += tsc2301-mixer.o
# ... add above this line ... # ... add above this line ...
# SPI slave controller drivers (upstream link) # SPI slave controller drivers (upstream link)
......
/*
* TSC2301 driver
*
* Copyright (C) 2005, 2006 Nokia Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/spi/tsc2301.h>
#ifdef CONFIG_ARCH_OMAP
#include <asm/arch/gpio.h>
#endif
u16 tsc2301_read_reg(struct tsc2301 *tsc, int reg)
{
struct spi_transfer t[2];
struct spi_message m;
u16 data = 0, cmd;
cmd = reg;
cmd |= 0x8000;
memset(t, 0, sizeof(t));
spi_message_init(&m);
m.spi = tsc->spi;
t[0].tx_buf = &cmd;
t[0].rx_buf = NULL;
t[0].len = 2;
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = NULL;
t[1].rx_buf = &data;
t[1].len = 2;
spi_message_add_tail(&t[1], &m);
spi_sync(m.spi, &m);
return data;
}
void tsc2301_write_reg(struct tsc2301 *tsc, int reg, u16 val)
{
struct spi_transfer t;
struct spi_message m;
u16 data[2];
/* Now we prepare the command for transferring */
data[0] = reg;
data[1] = val;
spi_message_init(&m);
m.spi = tsc->spi;
memset(&t, 0, sizeof(t));
t.tx_buf = data;
t.rx_buf = NULL;
t.len = 4;
spi_message_add_tail(&t, &m);
spi_sync(m.spi, &m);
}
void tsc2301_write_kbc(struct tsc2301 *tsc, int val)
{
u16 w;
w = tsc->config2_shadow;
w &= ~(0x03 << 14);
w |= (val & 0x03) << 14;
tsc2301_write_reg(tsc, TSC2301_REG_CONFIG2, w);
tsc->config2_shadow = w;
}
void tsc2301_write_pll(struct tsc2301 *tsc,
int pll_n, int pll_a, int pll_pdc, int pct_e, int pll_o)
{
u16 w;
w = tsc->config2_shadow;
w &= ~0x3fff;
w |= (pll_n & 0x0f) | ((pll_a & 0x0f) << 4) | ((pll_pdc & 0x0f) << 8);
w |= pct_e ? (1 << 12) : 0;
w |= pll_o ? (1 << 13) : 0;
tsc2301_write_reg(tsc, TSC2301_REG_CONFIG2, w);
tsc->config2_shadow = w;
}
void tsc2301_read_buf(struct tsc2301 *tsc, int reg, u16 *rx_buf, int len)
{
struct spi_transfer t[2];
struct spi_message m;
u16 cmd, i;
cmd = reg;
cmd |= 0x8000;
spi_message_init(&m);
m.spi = tsc->spi;
memset(t, 0, sizeof(t));
t[0].tx_buf = &cmd;
t[0].rx_buf = NULL;
t[0].len = 2;
spi_message_add_tail(&t[0], &m);
t[1].tx_buf = NULL;
t[1].rx_buf = rx_buf;
t[1].len = 2 * len;
spi_message_add_tail(&t[1], &m);
spi_sync(m.spi, &m);
for (i = 0; i < len; i++)
printk(KERN_DEBUG "rx_buf[%d]: %04x\n", i, rx_buf[i]);
}
static int __devinit tsc2301_probe(struct spi_device *spi)
{
struct tsc2301 *tsc;
struct tsc2301_platform_data *pdata = spi->dev.platform_data;
int r;
u16 w;
if (!pdata) {
dev_dbg(&spi->dev, "no platform data?\n");
return -ENODEV;
}
tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
if (tsc == NULL)
return -ENOMEM;
dev_set_drvdata(&spi->dev, tsc);
tsc->spi = spi;
spi->dev.power.power_state = PMSG_ON;
tsc->enable_clock = pdata->enable_clock;
tsc->disable_clock = pdata->disable_clock;
if (pdata->reset_gpio >= 0) {
tsc->reset_gpio = pdata->reset_gpio;
#ifdef CONFIG_ARCH_OMAP
r = omap_request_gpio(tsc->reset_gpio);
if (r < 0)
goto err1;
omap_set_gpio_dataout(tsc->reset_gpio, 1);
omap_set_gpio_direction(tsc->reset_gpio, 0);
mdelay(1);
omap_set_gpio_dataout(tsc->reset_gpio, 0);
#endif
} else
tsc->reset_gpio = -1;
spi->mode = SPI_MODE_0;
spi->bits_per_word = 16;
/* The max speed might've been defined by the board-specific
* struct */
if (!spi->max_speed_hz)
spi->max_speed_hz = TSC2301_HZ;
spi_setup(spi);
/* Soft reset */
tsc2301_write_reg(tsc, TSC2301_REG_RESET, 0xbb00);
msleep(1);
w = tsc2301_read_reg(tsc, TSC2301_REG_ADC);
if (!(w & (1 << 14))) {
dev_err(&spi->dev, "invalid ADC reg value: %04x\n", w);
r = -ENODEV;
goto err1;
}
w = tsc2301_read_reg(tsc, TSC2301_REG_DAC);
if (!(w & (1 << 15))) {
dev_err(&spi->dev, "invalid DAC reg value: %04x\n", w);
r = -ENODEV;
goto err1;
}
/* Stop keypad scanning */
tsc2301_write_reg(tsc, TSC2301_REG_KEY, 0x4000);
/* We have to cache this for read-modify-write, since we can't
* read back BIT15 */
w = tsc2301_read_reg(tsc, TSC2301_REG_CONFIG2);
/* By default BIT15 is set */
w |= 1 << 15;
tsc->config2_shadow = w;
r = tsc2301_kp_init(tsc, pdata);
if (r)
goto err1;
r = tsc2301_ts_init(tsc, pdata);
if (r)
goto err2;
r = tsc2301_mixer_init(tsc, pdata);
if (r)
goto err3;
return 0;
err3:
tsc2301_ts_exit(tsc);
err2:
tsc2301_kp_exit(tsc);
err1:
kfree(tsc);
return r;
}
static int __devexit tsc2301_remove(struct spi_device *spi)
{
struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
tsc2301_mixer_exit(tsc);
tsc2301_ts_exit(tsc);
tsc2301_kp_exit(tsc);
kfree(tsc);
return 0;
}
#ifdef CONFIG_PM
static int tsc2301_suspend(struct spi_device *spi, pm_message_t mesg)
{
struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
int r;
if ((r = tsc2301_mixer_suspend(tsc)) < 0)
return r;
if ((r = tsc2301_kp_suspend(tsc)) < 0)
goto err1;
if ((r = tsc2301_ts_suspend(tsc)) < 0)
goto err2;
return 0;
err2:
tsc2301_kp_resume(tsc);
err1:
tsc2301_mixer_resume(tsc);
return r;
}
static int tsc2301_resume(struct spi_device *spi)
{
struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
tsc2301_ts_resume(tsc);
tsc2301_kp_resume(tsc);
tsc2301_mixer_resume(tsc);
return 0;
}
#endif
static struct spi_driver tsc2301_driver = {
.driver = {
.name = "tsc2301",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
#ifdef CONFIG_PM
.suspend = tsc2301_suspend,
.resume = tsc2301_resume,
#endif
.probe = tsc2301_probe,
.remove = __devexit_p(tsc2301_remove),
};
static int __init tsc2301_init(void)
{
printk("TSC2301 driver initializing\n");
return spi_register_driver(&tsc2301_driver);
}
module_init(tsc2301_init);
static void __exit tsc2301_exit(void)
{
spi_unregister_driver(&tsc2301_driver);
}
module_exit(tsc2301_exit);
MODULE_AUTHOR("Juha Yrjl <juha.yrjola@nokia.com>");
MODULE_LICENSE("GPL");
This diff is collapsed.
#ifndef _LINUX_SPI_TSC2301_H
#define _LINUX_SPI_TSC2301_H
#include <linux/types.h>
#include <linux/timer.h>
struct tsc2301_platform_data {
/*
* Keypad
*/
s16 reset_gpio;
s16 keyb_int;
s16 keymap[16]; /* Set a key to a negative value if not used */
unsigned kp_rep:1; /* Enable keypad repeating */
/*
* Touchscreen
*/
s16 dav_gpio;
s16 pen_int_gpio;
u16 ts_x_plate_ohm;
u32 ts_stab_time; /* voltage settling time */
u8 ts_hw_avg; /* HW assiseted averaging. Can be
0, 4, 8, 16 samples per reading */
u32 ts_max_pressure;/* Samples with bigger pressure value will
be ignored, since the corresponding X, Y
values are unreliable */
u32 ts_touch_pressure; /* Pressure limit until we report a
touch event. After that we switch
to ts_max_pressure. */
unsigned ts_ignore_last : 1;
/*
* Audio
*/
unsigned pll_pdc:4;
unsigned pll_a:4;
unsigned pll_n:4;
unsigned pll_output:1; /* Output PLL on GPIO_0 */
unsigned mclk_ratio:2;
unsigned i2s_sample_rate:4;
unsigned i2s_format:2;
/* Mask for audio blocks to be powered down */
u16 power_down_blocks;
/* Called after codec has been initialized, can be NULL */
int (* codec_init)(struct device *tsc2301_dev);
/* Called when codec is being removed, can be NULL */
void (* codec_cleanup)(struct device *tsc2301_dev);
int (*enable_clock)(struct device *dev);
void (*disable_clock)(struct device *dev);
int (*get_keyb_irq_state)(struct device *dev);
const struct tsc2301_mixer_gpio {
const char *name;
unsigned gpio:4;
unsigned inverted:1;
unsigned def_enable:1; /* enable by default */
unsigned deactivate_on_pd:1; /* power-down flag */
} *mixer_gpios;
int n_mixer_gpios;
};
struct tsc2301_kp;
struct tsc2301_ts;
struct tsc2301_mixer;
struct tsc2301 {
struct spi_device *spi;
s16 reset_gpio;
u16 config2_shadow;
struct tsc2301_kp *kp;
struct tsc2301_ts *ts;
struct tsc2301_mixer *mixer;
int (*enable_clock)(struct device *dev);
void (*disable_clock)(struct device *dev);
};
#define TSC2301_HZ 33000000
#define TSC2301_REG(page, addr) (((page) << 11) | ((addr) << 5))
#define TSC2301_REG_TO_PAGE(reg) (((reg) >> 11) & 0x03)
#define TSC2301_REG_TO_ADDR(reg) (((reg) >> 5) & 0x1f)
#define TSC2301_REG_X TSC2301_REG(0, 0)
#define TSC2301_REG_Y TSC2301_REG(0, 1)
#define TSC2301_REG_Z1 TSC2301_REG(0, 2)
#define TSC2301_REG_Z2 TSC2301_REG(0, 3)
#define TSC2301_REG_KPDATA TSC2301_REG(0, 4)
#define TSC2301_REG_ADC TSC2301_REG(1, 0)
#define TSC2301_REG_KEY TSC2301_REG(1, 1)
#define TSC2301_REG_DAC TSC2301_REG(1, 2)
#define TSC2301_REG_REF TSC2301_REG(1, 3)
#define TSC2301_REG_RESET TSC2301_REG(1, 4)
#define TSC2301_REG_CONFIG TSC2301_REG(1, 5)
#define TSC2301_REG_CONFIG2 TSC2301_REG(1, 6)
#define TSC2301_REG_KPMASK TSC2301_REG(1, 16)
#define TSC2301_REG_AUDCNTL TSC2301_REG(2, 0)
#define TSC2301_REG_ADCVOL TSC2301_REG(2, 1)
#define TSC2301_REG_DACVOL TSC2301_REG(2, 2)
#define TSC2301_REG_BPVOL TSC2301_REG(2, 3)
#define TSC2301_REG_KEYCTL TSC2301_REG(2, 4)
#define TSC2301_REG_PD_MISC TSC2301_REG(2, 5)
#define TSC2301_REG_GPIO TSC2301_REG(2, 6)
#define TSC2301_REG_ADCLKCFG TSC2301_REG(2, 27)
#define TSC2301_REG_PD_MISC_APD (1 << 15)
#define TSC2301_REG_PD_MISC_AVPD (1 << 14)
#define TSC2301_REG_PD_MISC_ABPD (1 << 13)
#define TSC2301_REG_PD_MISC_HAPD (1 << 12)
#define TSC2301_REG_PD_MISC_MOPD (1 << 11)
#define TSC2301_REG_PD_MISC_DAPD (1 << 10)
#define TSC2301_REG_PD_MISC_ADPDL (1 << 9)
#define TSC2301_REG_PD_MISC_ADPDR (1 << 8)
#define TSC2301_REG_PD_MISC_PDSTS (1 << 7)
#define TSC2301_REG_PD_MISC_MIBPD (1 << 6)
#define TSC2301_REG_PD_MISC_OTSYN (1 << 2)
/* I2S sample rate */
#define TSC2301_I2S_SR_48000 0x00
#define TSC2301_I2S_SR_44100 0x01
#define TSC2301_I2S_SR_32000 0x02
#define TSC2301_I2S_SR_24000 0x03
#define TSC2301_I2S_SR_22050 0x04
#define TSC2301_I2S_SR_16000 0x05
#define TSC2301_I2S_SR_12000 0x06
#define TSC2301_I2S_SR_11050 0x07
#define TSC2301_I2S_SR_8000 0x08
/* 16-bit, MSB-first. DAC Right-Justified, ADC Left-Justified */
#define TSC2301_I2S_FORMAT0 0x00
/* 20-bit, MSB-first. DAC Right-Justified, ADC Left-Justified */
#define TSC2301_I2S_FORMAT1 0x01
/* 20-bit, MSB-first. DAC Left-Justified, ADC Left-Justified */
#define TSC2301_I2S_FORMAT2 0x02
/* 20-bit, MSB-first */
#define TSC2301_I2S_FORMAT3 0x03
/* Master Clock Ratio */
#define TSC2301_MCLK_256xFS 0x00 /* default */
#define TSC2301_MCLK_384xFS 0x01
#define TSC2301_MCLK_512xFS 0x02
extern u16 tsc2301_read_reg(struct tsc2301 *tsc, int reg);
extern void tsc2301_write_reg(struct tsc2301 *tsc, int reg, u16 val);
extern void tsc2301_write_kbc(struct tsc2301 *tsc, int val);
extern void tsc2301_write_pll(struct tsc2301 *tsc, int pll_n, int pll_a,
int pll_pdc, int pct_e, int pll_o);
extern void tsc2301_read_buf(struct tsc2301 *tsc, int reg, u16 *buf, int len);
#define TSC2301_DECL_MOD(module) \
extern int tsc2301_##module##_init(struct tsc2301 *tsc, \
struct tsc2301_platform_data *pdata); \
extern void tsc2301_##module##_exit(struct tsc2301 *tsc); \
extern int tsc2301_##module##_suspend(struct tsc2301 *tsc); \
extern void tsc2301_##module##_resume(struct tsc2301 *tsc);
#define TSC2301_DECL_EMPTY_MOD(module) \
static inline int tsc2301_##module##_init(struct tsc2301 *tsc, \
struct tsc2301_platform_data *pdata) \
{ \
return 0; \
} \
static inline void tsc2301_##module##_exit(struct tsc2301 *tsc) {} \
static inline int tsc2301_##module##_suspend(struct tsc2301 *tsc) \
{ \
return 0; \
} \
static inline void tsc2301_##module##_resume(struct tsc2301 *tsc) {}
#ifdef CONFIG_KEYBOARD_TSC2301
TSC2301_DECL_MOD(kp)
void tsc2301_kp_restart(struct tsc2301 *tsc);
#else
TSC2301_DECL_EMPTY_MOD(kp)
static inline void tsc2301_kp_restart(struct tsc2301 *tsc) {}
#endif
#ifdef CONFIG_TOUCHSCREEN_TSC2301
TSC2301_DECL_MOD(ts)
#else
TSC2301_DECL_EMPTY_MOD(ts)
#endif
#ifdef CONFIG_SPI_TSC2301_AUDIO
TSC2301_DECL_MOD(mixer)
extern void tsc2301_mixer_set_power(struct device *tsc_dev, int dac, int adc);
struct snd_card;
extern int tsc2301_mixer_register_controls(struct device *tsc_dev,
struct snd_card *card);
#else
TSC2301_DECL_EMPTY_MOD(mixer)
#endif
extern void tsc2301_mixer_enable_mclk(struct device *tsc_dev);
extern void tsc2301_mixer_disable_mclk(struct device *tsc_dev);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment