Commit da2bb7fa 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 a627fa91
......@@ -173,6 +173,28 @@ config SPI_TSC2102
Say Y here if you want support for the TSC2102 chip. It
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.
#
# Add new SPI protocol masters in alphabetical order above this line
#
......
......@@ -27,6 +27,9 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
obj-$(CONFIG_SPI_AT25) += at25.o
obj-$(CONFIG_SPI_TSC2101) += tsc2101.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 ...
# 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