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");
/*
* ALSA Mixer implementation for TSC2301
*
* Copyright (C) 2006 Nokia Corporation.
*
* Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
* Juha Yrjola
*
* Some notes about TSC2301:
* - PLL will stop when DAC and ADC's are powered down.
* - Touchscreen will stop working when audio part is powered up and if audio
* MCLK is stopped. Problem is avoided if audio is powered down before
* stopping MCLK.
* - Audio DAC or audio outputs will activate only after 100 msec from the
* chip power-up. Reason seems to be VCM since there is no this delay if the
* chip and VCM (bit AVPD on PD/MISC) were not powered down. The chip will
* consume about 1 mA if all other audio blocks are powered down except the
* chip itself and VCM. Full power down consumes only about few uA.
* - Power-down transition could happen earliest about 100 msec after the chip
* power-up. Otherwise power-down will fail if there is no that 100 msec
* on time before it. It's not obvious why is that since chip reports
* power-up to be completed and also PLL output on GPIO_0 is active in few
* milliseconds.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* 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., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/tsc2301.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/control.h>
/* shadow register indexes */
enum {
/* audio control and volume registers */
AUDCNTL_INDEX,
ADCVOL_INDEX,
DACVOL_INDEX,
BPVOL_INDEX,
/* keyclick control register (not needed here) */
/* audio power control register */
PD_MISC_INDEX,
/* TSC2301 GPIO control register */
GPIO_INDEX,
SHADOW_REG_COUNT,
};
/* structure for driver private data */
struct tsc2301_mixer {
struct tsc2301 *tsc;
struct mutex mutex;
/* shadow registers holding TSC2301 audio registers. Used to hold
* their states during the sleep and also to reduce communication with
* the chip since get callback functions could get register values
* directly from these shadow registers without needing to read them
* from the chip */
u16 shadow_regs[SHADOW_REG_COUNT];
/* audio controller driver usage of the ADC and DAC */
unsigned adc_enabled:1, dac_enabled:1;
unsigned pll_output:1;
unsigned mclk_enabled;
/* latest audio power-up timestamp */
unsigned long pu_jiffies;
/* these are used when upper layer(s) are going to power-down TSC2301
* before 100 msec is passed from power-up */
struct delayed_work delayed_power_down;
unsigned delayed_pd_active:1;
int (* platform_init)(struct device *);
void (* platform_cleanup)(struct device *);
struct tsc2301_mixer_gpio *mixer_gpios;
int n_mixer_gpios;
};
#define TSC2301_DAC_DELAY msecs_to_jiffies(100)
#define TSC2301_MIN_PU_PERIOD msecs_to_jiffies(100)
#define TSC2301_REG_TO_PVAL(reg) \
(TSC2301_REG_TO_PAGE(reg) << 6 | TSC2301_REG_TO_ADDR(reg))
#define TSC2301_PVAL_TO_REG(v) \
(TSC2301_REG((((v) >> 6) & 3),((v) & 0x1f)))
#define TSC2301_VOLUME_MASK 0x7f
#define TSC2301_MIN_ADCVOL 6
#define TSC2301_MIN_DACVOL 0
#define TSC2301_MIN_BPVOL 31
#define TSC2301_MUTE_LEFT_SHIFT 15
#define TSC2301_VOL_LEFT_SHIFT 8
#define TSC2301_MUTE_RIGHT_SHIFT 7
#define TSC2301_VOL_RIGHT_SHIFT 0
#define TSC2301_INM_MASK 3
#define TSC2301_INML_SHIFT 12
#define TSC2301_INMR_SHIFT 10
#define TSC2301_MICG_MASK 3
#define TSC2301_MICG_MIN 1 /* values 0 & 1 both mean 0 dB */
#define TSC2301_MICG_SHIFT 8
#define TSC2301_REG_AUDCNTL_MCLK(v) (((v) & 3) << 6)
#define TSC2301_REG_AUDCNTL_I2SFS(v) (((v) & 0xf) << 2)
#define TSC2301_REG_AUDCNTL_I2SFM(v) (((v) & 3) << 0)
#define TSC2301_SINGLE(xname, xindex, reg, shadow_index, shift, mask, min) \
{\
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_tsc2301_info_single, \
.get = snd_tsc2301_get_single, \
.put = snd_tsc2301_put_single, \
.private_value = TSC2301_REG_TO_PVAL(reg) | \
(shadow_index << 8) | (shift << 16) | (mask << 24) | \
(min << 28) \
}
#define TSC2301_SINGLE_MINVAL(v) (((v) >> 28) & 15)
#define TSC2301_SINGLE_SHIFT(v) (((v) >> 16) & 15)
#define TSC2301_SINGLE_MASK(v) (((v) >> 24) & 15)
#define TSC2301_DOUBLE(xname, xindex, reg, shadow_index, ls, rs, mask, min) \
{\
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_tsc2301_info_double, \
.get = snd_tsc2301_get_double, \
.put = snd_tsc2301_put_double, \
.private_value = TSC2301_REG_TO_PVAL(reg) | \
(shadow_index << 8) | (min << 11) | \
(ls << 16) | (rs << 20) | (mask << 24) \
}
#define TSC2301_DOUBLE_MINVAL(v) (((v) >> 11) & 0x1f)
#define TSC2301_DOUBLE_LEFT_SHIFT(v) (((v) >> 16) & 15)
#define TSC2301_DOUBLE_RIGHT_SHIFT(v) (((v) >> 20) & 15)
#define TSC2301_DOUBLE_MASK(v) (((v) >> 24) & TSC2301_VOLUME_MASK)
#define TSC2301_MUX(xname, xindex, reg, shadow_index, ls, rs, mask) \
{\
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_tsc2301_info_mux, \
.get = snd_tsc2301_get_mux, \
.put = snd_tsc2301_put_mux, \
.private_value = TSC2301_REG_TO_PVAL(reg) | \
(shadow_index << 8) | (ls << 16) | (rs << 20) | (mask << 24) \
}
#define TSC2301_MUX_LEFT_SHIFT(v) (((v) >> 16) & 15)
#define TSC2301_MUX_RIGHT_SHIFT(v) (((v) >> 20) & 15)
#define TSC2301_MUX_MASK(v) (((v) >> 24) & TSC2301_VOLUME_MASK)
#define TSC2301_BOOL(xname, xindex, reg, shadow_index, shift, invert, state) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.name = xname, \
.index = xindex, \
.info = snd_tsc2301_info_bool, \
.get = snd_tsc2301_get_bool, \
.put = snd_tsc2301_put_bool, \
.private_value = TSC2301_REG_TO_PVAL(reg) | \
(shadow_index << 8) | (shift << 16) | \
(invert << 24) | (state << 25) \
}
#define TSC2301_BOOL_SHIFT(v) (((v) >> 16) & 7)
#define TSC2301_BOOL_INVERT(v) (((v) >> 24) & 1)
#define TSC2301_BOOL_STATE(v) (((v) >> 25) & 1)
#define TSC2301_SHADOW_INDEX(v) (((v) >> 8) & 7)
/*
* Power-down handler for additional GPIO mixer controls. GPIO state of GPIO
* controls whose power-down flag is enabled are set to their false/deactivate
* state
*
* Must be called tsc->mixer->mutex locked
*/
static void tsc2301_gpio_power_down(struct tsc2301 *tsc)
{
struct tsc2301_mixer *mix = tsc->mixer;
u16 temp;
int i;
temp = mix->shadow_regs[GPIO_INDEX];
for (i = 0; i < mix->n_mixer_gpios; i++) {
const struct tsc2301_mixer_gpio *mg;
mg = mix->mixer_gpios + i;
if (mg->deactivate_on_pd) {
int gpio = mg->gpio;
temp &= ~(1 << gpio);
temp |= mg->inverted << gpio;
}
}
tsc2301_write_reg(tsc, TSC2301_REG_GPIO, temp);
}
/*
* Powers down/up audio blocks which are muted or become unused.
* shadow_index >= 0, changes power state of single audio block
* shadow_index < 0, changes power state of all blocks
*
* Must be called tsc->mixer->mutex locked
*/
#define TSC2301_MUTE_MASK \
((1 << TSC2301_MUTE_LEFT_SHIFT) | (1 << TSC2301_MUTE_RIGHT_SHIFT))
static void tsc2301_power_ctrl(struct tsc2301 *tsc, int shadow_index,
int poll_pdsts)
{
struct tsc2301_mixer *mix = tsc->mixer;
u16 pd_ctrl, pd_ctrl_old, w;
unsigned long timeout;
int power_up = 0;
if (mix->delayed_pd_active) {
mix->delayed_pd_active = 0;
mix->mclk_enabled--;
cancel_delayed_work(&mix->delayed_power_down);
}
pd_ctrl = pd_ctrl_old = mix->shadow_regs[PD_MISC_INDEX];
/* power control helper based on used space mixer selections. See
* actual power control decisions below */
if (shadow_index < 0 || shadow_index == ADCVOL_INDEX) {
/* ADC left and right power down control */
if (mix->shadow_regs[ADCVOL_INDEX] &
(1 << TSC2301_MUTE_LEFT_SHIFT))
/* left ADC muted. Power down the left ADC */
pd_ctrl |= TSC2301_REG_PD_MISC_ADPDL;
else
pd_ctrl &= ~TSC2301_REG_PD_MISC_ADPDL;
if (mix->shadow_regs[ADCVOL_INDEX] &
(1 << TSC2301_MUTE_LEFT_SHIFT))
/* right ADC muted. Power down the right ADC */
pd_ctrl |= TSC2301_REG_PD_MISC_ADPDR;
else
pd_ctrl &= ~TSC2301_REG_PD_MISC_ADPDR;
}
if (shadow_index < 0 || shadow_index == DACVOL_INDEX) {
/* DAC power down control */
if ((mix->shadow_regs[DACVOL_INDEX] &
TSC2301_MUTE_MASK) == TSC2301_MUTE_MASK)
/* both DACs muted. Power down the DAC */
pd_ctrl |= TSC2301_REG_PD_MISC_DAPD;
else
pd_ctrl &= ~TSC2301_REG_PD_MISC_DAPD;
}
if (shadow_index < 0 || shadow_index == BPVOL_INDEX) {
/* line bypass power down control */
if ((mix->shadow_regs[BPVOL_INDEX] &
TSC2301_MUTE_MASK) == TSC2301_MUTE_MASK)
/* both line bypasses muted. Power down the bypass
* path */
pd_ctrl |= TSC2301_REG_PD_MISC_ABPD;
else
pd_ctrl &= ~TSC2301_REG_PD_MISC_ABPD;
}
if (shadow_index < 0 || shadow_index == AUDCNTL_INDEX) {
/* mic bias power down control */
if ((mix->shadow_regs[AUDCNTL_INDEX] &
(3 << TSC2301_INML_SHIFT)) &&
(mix->shadow_regs[AUDCNTL_INDEX] &
(3 << TSC2301_INMR_SHIFT)))
/* both ADC channels use other than mic input. Power
* down the mic bias output */
pd_ctrl |= TSC2301_REG_PD_MISC_MIBPD;
else
pd_ctrl &= ~TSC2301_REG_PD_MISC_MIBPD;
}
/* power control decisions based on codec usage and user space mixer
* selections detected above */
pd_ctrl &= ~TSC2301_REG_PD_MISC_APD; /* audio not powered down */
if (mix->mclk_enabled) {
if (!mix->adc_enabled) {
/* ADC not used, power down both ADC's and mic bias
* output independently of user space mixer
* selections */
pd_ctrl |= TSC2301_REG_PD_MISC_ADPDL;
pd_ctrl |= TSC2301_REG_PD_MISC_ADPDR;
pd_ctrl |= TSC2301_REG_PD_MISC_MIBPD;
}
if (!mix->dac_enabled) {
/* DAC not used, power down DAC independently of user
* space mixer selections */
pd_ctrl |= TSC2301_REG_PD_MISC_DAPD;
}
if (mix->pll_output) {
/* GPIO_0 is configured as PLL output so audio
* controller is expecting clock from TSC2301. Either
* ADC or DAC must be active in order to keep PLL on */
if ((pd_ctrl & TSC2301_REG_PD_MISC_ADPDL) &&
(pd_ctrl & TSC2301_REG_PD_MISC_ADPDR) &&
(pd_ctrl & TSC2301_REG_PD_MISC_DAPD)) {
/* neither ADC or DAC used. Force ADC on in
* order to keep PLL active */
pd_ctrl &= ~(TSC2301_REG_PD_MISC_ADPDL |
TSC2301_REG_PD_MISC_ADPDR);
}
}
} else {
/* audio input clock is not enabled so power down DAC and ADC
* in order to shutdown PLL and to keep touchscreen and keypad
* parts working. Touchscreen and keypad use audio clock when
* PLL is on and internal clock otherwise */
pd_ctrl |= TSC2301_REG_PD_MISC_DAPD |
TSC2301_REG_PD_MISC_ADPDL |
TSC2301_REG_PD_MISC_ADPDR;
}
if ((pd_ctrl & TSC2301_REG_PD_MISC_ADPDL) &&
(pd_ctrl & TSC2301_REG_PD_MISC_ADPDR) &&
(pd_ctrl & TSC2301_REG_PD_MISC_DAPD) &&
(pd_ctrl & TSC2301_REG_PD_MISC_ABPD)) {
/* all ADC, DAC and line bypass path unused. Power down the
* whole audio part of the TSC2301 */
pd_ctrl |= TSC2301_REG_PD_MISC_APD;
}
if (pd_ctrl == pd_ctrl_old)
return;
/* power down control changed. Update into TSC2301 */
if ((pd_ctrl ^ pd_ctrl_old) & TSC2301_REG_PD_MISC_APD) {
/* whole audio power state changed. Update GPIO states */
if (pd_ctrl & TSC2301_REG_PD_MISC_APD) {
/* power down GPIO controls before powering down
* the codec */
tsc2301_gpio_power_down(tsc);
/* we must really ensure that codec has been on no less
* than 100 msec before doing power-down */
timeout = mix->pu_jiffies + TSC2301_MIN_PU_PERIOD -
jiffies;
if (timeout <= TSC2301_MIN_PU_PERIOD) {
mix->delayed_pd_active = 1;
mix->mclk_enabled++;
schedule_delayed_work(&mix->delayed_power_down,
timeout + 1);
return;
}
} else
/* restore GPIOs after codec is powered up */
power_up = 1;
}
mix->shadow_regs[PD_MISC_INDEX] = pd_ctrl;
tsc2301_write_reg(tsc, TSC2301_REG_PD_MISC, pd_ctrl);
if (power_up)
mix->pu_jiffies = jiffies;
if (!poll_pdsts) {
if (power_up)
tsc2301_write_reg(tsc, TSC2301_REG_GPIO,
mix->shadow_regs[GPIO_INDEX]);
return;
}
/* wait until power-up/-down is completed */
timeout = jiffies + msecs_to_jiffies(100);
w = 0;
do {
if (time_after(jiffies, timeout)) {
/* Print a warning only if the I2S clock is not
* present / out of sync. This can happen during
* init time, when that clock will be turned on
* by another driver like in the OMAP EAC with
* external clock case.
*/
if (w & TSC2301_REG_PD_MISC_OTSYN) {
dev_warn(&tsc->spi->dev,
"I2S clock not in sync or off.\n");
} else {
dev_err(&tsc->spi->dev,
"power-up/-down timed out "
"(0x%04x, 0x%04x -> 0x%04x)\n",
w, pd_ctrl_old, pd_ctrl);
}
goto out;
}
w = tsc2301_read_reg(tsc, TSC2301_REG_PD_MISC);
} while (!(w & TSC2301_REG_PD_MISC_PDSTS));
out:
if (((pd_ctrl ^ pd_ctrl_old) & TSC2301_REG_PD_MISC_DAPD) &&
!(pd_ctrl & TSC2301_REG_PD_MISC_DAPD)) {
/* DAC powered up now. Ensure that DAC and audio outputs are
* activated. They are up 100 msec after the chip power-up
* command */
timeout = mix->pu_jiffies + TSC2301_DAC_DELAY - jiffies;
if (timeout <= TSC2301_DAC_DELAY)
schedule_timeout_interruptible(timeout);
/* FIXME: This is lazy. We restore GPIOs only after activating
* the DAC. It would be better to do some kind of delayed GPIO
* restore. That ensures that we restore them also if only ADC
* path is activated. But this is required only if there is
* some input amplifier, bias control, etc. and their power
* state is under TSC GPIO control */
tsc2301_write_reg(tsc, TSC2301_REG_GPIO,
mix->shadow_regs[GPIO_INDEX]);
}
}
static int snd_tsc2301_info_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
int mask = TSC2301_SINGLE_MASK(kcontrol->private_value);
int minval = TSC2301_SINGLE_MINVAL(kcontrol->private_value);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 1;
uinfo->value.integer.min = minval;
uinfo->value.integer.max = mask;
return 0;
}
static int snd_tsc2301_get_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
int mask = TSC2301_SINGLE_MASK(priv);
int shift = TSC2301_SINGLE_SHIFT(priv);
int shadow_index = TSC2301_SHADOW_INDEX(priv);
u16 shadow_reg;
shadow_reg = tsc->mixer->shadow_regs[shadow_index];
ucontrol->value.integer.value[0] = (shadow_reg >> shift) & mask;
return 0;
}
static int snd_tsc2301_put_single(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
int mask = TSC2301_SINGLE_MASK(priv);
int shadow_index = TSC2301_SHADOW_INDEX(priv);
u16 shadow_reg, shadow_reg_old;
int shift = TSC2301_SINGLE_SHIFT(priv);
int reg = TSC2301_PVAL_TO_REG(priv);
int changed;
mutex_lock(&tsc->mixer->mutex);
shadow_reg = shadow_reg_old = tsc->mixer->shadow_regs[shadow_index];
/* zero bits to be modified */
shadow_reg &= ~(mask << shift);
/* modify with new value */
shadow_reg |= ((ucontrol->value.integer.value[0] & mask) << shift);
changed = (shadow_reg != shadow_reg_old);
tsc->mixer->shadow_regs[shadow_index] = shadow_reg;
/* update into TSC2301 if necessary */
if (changed)
tsc2301_write_reg(tsc, reg, shadow_reg);
mutex_unlock(&tsc->mixer->mutex);
return changed;
}
static int snd_tsc2301_info_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
/* mask == 1 : Switch
* mask > 1 : Max volume */
int mask = TSC2301_DOUBLE_MASK(kcontrol->private_value);
int minval = TSC2301_DOUBLE_MINVAL(kcontrol->private_value);
uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN :
SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = minval;
uinfo->value.integer.max = mask;
return 0;
}
static int snd_tsc2301_get_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
/* mask == 1 : Switch
* mask > 1 : Volume */
int mask = TSC2301_DOUBLE_MASK(priv);
int ls = TSC2301_DOUBLE_LEFT_SHIFT(priv);
int rs = TSC2301_DOUBLE_RIGHT_SHIFT(priv);
int shadow_index = TSC2301_SHADOW_INDEX(priv);
u16 shadow_reg;
shadow_reg = tsc->mixer->shadow_regs[shadow_index];
/* invert mute bits for the switches */
if (mask == 1)
shadow_reg = ~shadow_reg;
ucontrol->value.integer.value[0] = (shadow_reg >> ls) & mask;
ucontrol->value.integer.value[1] = (shadow_reg >> rs) & mask;
return 0;
}
static int snd_tsc2301_put_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
/* mask == 1 : Switch
* mask > 1 : Volume */
int mask = TSC2301_DOUBLE_MASK(priv);
int shadow_index = TSC2301_SHADOW_INDEX(priv);
u16 shadow_reg, shadow_reg_old;
int ls = TSC2301_DOUBLE_LEFT_SHIFT(priv);
int rs = TSC2301_DOUBLE_RIGHT_SHIFT(priv);
int reg = TSC2301_PVAL_TO_REG(priv);
int changed;
mutex_lock(&tsc->mixer->mutex);
shadow_reg = shadow_reg_old = tsc->mixer->shadow_regs[shadow_index];
/* zero bits to be modified */
shadow_reg &= ~((mask << ls) | (mask << rs));
/* modify with new value */
if (mask == 1) {
/* switch. Invert switch values for the mute bits */
shadow_reg |=
((~ucontrol->value.integer.value[0] & mask) << ls) |
((~ucontrol->value.integer.value[1] & mask) << rs);
} else {
/* volume */
shadow_reg |=
(ucontrol->value.integer.value[0] << ls) |
(ucontrol->value.integer.value[1] << rs);
}
changed = (shadow_reg != shadow_reg_old);
tsc->mixer->shadow_regs[shadow_index] = shadow_reg;
/* update into TSC2301 if necessary */
if (changed)
tsc2301_write_reg(tsc, reg, shadow_reg);
if (mask == 1)
/* check is need to power down/up audio blocks in case of
* muted state change */
tsc2301_power_ctrl(tsc, shadow_index, 0);
mutex_unlock(&tsc->mixer->mutex);
return changed;
}
static int snd_tsc2301_info_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
static char *texts[4] = {"Mic", "Line", "Line swapped", "Line mono"};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 2;
uinfo->value.enumerated.items = 4;
if (uinfo->value.enumerated.item > 3)
uinfo->value.enumerated.item = 3;
strcpy(uinfo->value.enumerated.name,
texts[uinfo->value.enumerated.item]);
return 0;
}
static int snd_tsc2301_get_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
int mask = TSC2301_MUX_MASK(priv);
int ls = TSC2301_MUX_LEFT_SHIFT(priv);
int rs = TSC2301_MUX_RIGHT_SHIFT(priv);
int shadow_index = TSC2301_SHADOW_INDEX(priv);
u16 shadow_reg;
shadow_reg = tsc->mixer->shadow_regs[shadow_index];
ucontrol->value.enumerated.item[0] = (shadow_reg >> ls) & mask;
ucontrol->value.enumerated.item[1] = (shadow_reg >> rs) & mask;
return 0;
}
static int snd_tsc2301_put_mux(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
int mask = TSC2301_MUX_MASK(priv);
int shadow_index = TSC2301_SHADOW_INDEX(priv);
u16 shadow_reg, shadow_reg_old;
int ls = TSC2301_MUX_LEFT_SHIFT(priv);
int rs = TSC2301_MUX_RIGHT_SHIFT(priv);
int reg = TSC2301_PVAL_TO_REG(priv);
int changed;
mutex_lock(&tsc->mixer->mutex);
shadow_reg = shadow_reg_old = tsc->mixer->shadow_regs[shadow_index];
/* zero bits to be modified */
shadow_reg &= ~((mask << ls) | (mask << rs));
/* modify with new value */
shadow_reg |= (ucontrol->value.enumerated.item[0] << ls);
shadow_reg |= (ucontrol->value.enumerated.item[1] << rs);
changed = (shadow_reg != shadow_reg_old);
/* update into TSC2301 if necessary */
if (changed) {
tsc->mixer->shadow_regs[shadow_index] = shadow_reg;
tsc2301_write_reg(tsc, reg, shadow_reg);
}
/* check is need to power up/down audio blocks in case of ADC input
* change */
tsc2301_power_ctrl(tsc, shadow_index, 0);
mutex_unlock(&tsc->mixer->mutex);
return changed;
}
static int snd_tsc2301_info_bool(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int snd_tsc2301_get_bool(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
int shadow_index = TSC2301_SHADOW_INDEX(priv);
int shift = TSC2301_BOOL_SHIFT(priv);
int invert = TSC2301_BOOL_INVERT(priv);
u16 shadow_reg;
shadow_reg = tsc->mixer->shadow_regs[shadow_index];
ucontrol->value.integer.value[0] =
invert ^ ((shadow_reg >> shift) & 1);
return 0;
}
static int snd_tsc2301_put_bool(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct tsc2301 *tsc = kcontrol->private_data;
unsigned long priv = kcontrol->private_value;
int shadow_index = TSC2301_SHADOW_INDEX(priv);
int shift = TSC2301_BOOL_SHIFT(priv);
int invert = TSC2301_BOOL_INVERT(priv);
int reg = TSC2301_PVAL_TO_REG(priv);
u16 shadow_reg, shadow_reg_old;
int changed;
mutex_lock(&tsc->mixer->mutex);
shadow_reg = shadow_reg_old = tsc->mixer->shadow_regs[shadow_index];
/* zero bit to be modified */
shadow_reg &= ~(1 << shift);
/* modify with new value */
shadow_reg |=
(invert ^ (ucontrol->value.integer.value[0] & 1)) << shift;
changed = (shadow_reg != shadow_reg_old);
/* update into TSC2301 if necessary */
if (changed) {
tsc->mixer->shadow_regs[shadow_index] = shadow_reg;
if ((shadow_index == GPIO_INDEX) &&
(tsc->mixer->shadow_regs[PD_MISC_INDEX] &
TSC2301_REG_PD_MISC_APD)) {
/* changing GPIO control and audio is powered down.
* Update GPIO states according to their power-down
* flag */
tsc2301_gpio_power_down(tsc);
} else
tsc2301_write_reg(tsc, reg, shadow_reg);
}
mutex_unlock(&tsc->mixer->mutex);
return changed;
}
/* TSC2301 internal mixer controls */
static struct snd_kcontrol_new snd_tsc2301_controls[] = {
/* stereo ADC input switches and volumes */
TSC2301_DOUBLE("Capture Switch", 0,
TSC2301_REG_ADCVOL, ADCVOL_INDEX,
TSC2301_MUTE_LEFT_SHIFT, TSC2301_MUTE_RIGHT_SHIFT,
1, 0),
TSC2301_DOUBLE("Capture Volume", 0,
TSC2301_REG_ADCVOL, ADCVOL_INDEX,
TSC2301_VOL_LEFT_SHIFT, TSC2301_VOL_RIGHT_SHIFT,
TSC2301_VOLUME_MASK, TSC2301_MIN_ADCVOL),
/* stereo DAC output switches and volumes */
TSC2301_DOUBLE("PCM Playback Switch", 0,
TSC2301_REG_DACVOL, DACVOL_INDEX,
TSC2301_MUTE_LEFT_SHIFT, TSC2301_MUTE_RIGHT_SHIFT,
1, 0),
TSC2301_DOUBLE("PCM Playback Volume", 0,
TSC2301_REG_DACVOL, DACVOL_INDEX,
TSC2301_VOL_LEFT_SHIFT, TSC2301_VOL_RIGHT_SHIFT,
TSC2301_VOLUME_MASK, TSC2301_MIN_DACVOL),
/* stereo line input bypass switches and volumes */
TSC2301_DOUBLE("Line Playback Switch", 0,
TSC2301_REG_BPVOL, BPVOL_INDEX,
TSC2301_MUTE_LEFT_SHIFT, TSC2301_MUTE_RIGHT_SHIFT,
1, 0),
TSC2301_DOUBLE("Line Playback Volume", 0,
TSC2301_REG_BPVOL, BPVOL_INDEX,
TSC2301_VOL_LEFT_SHIFT, TSC2301_VOL_RIGHT_SHIFT,
TSC2301_VOLUME_MASK, TSC2301_MIN_BPVOL),
/* mono microphone input gain */
TSC2301_SINGLE("Mic Boost", 0,
TSC2301_REG_AUDCNTL, AUDCNTL_INDEX,
TSC2301_MICG_SHIFT,
TSC2301_MICG_MASK, TSC2301_MICG_MIN),
/* ADC input sources. Both channels could be selected separately */
TSC2301_MUX("Capture Source", 0,
TSC2301_REG_AUDCNTL, AUDCNTL_INDEX,
TSC2301_INML_SHIFT, TSC2301_INMR_SHIFT,
TSC2301_INM_MASK),
};
/* must be called tsc->mixer->mutex locked */
static void tsc2301_flush_shadow_regs(struct tsc2301 *tsc)
{
int i, page, addr;
u16 temp;
page = TSC2301_REG_TO_PAGE(TSC2301_REG_AUDCNTL);
addr = TSC2301_REG_TO_ADDR(TSC2301_REG_AUDCNTL);
for (i = 0; i < 4; i++) {
temp = tsc->mixer->shadow_regs[i];
tsc2301_write_reg(tsc, TSC2301_REG(page, addr + i), temp);
}
temp = tsc->mixer->shadow_regs[GPIO_INDEX];
tsc2301_write_reg(tsc, TSC2301_REG_GPIO, temp);
/* Update power state of all audio blocks depending are they
* muted or unused. */
tsc2301_power_ctrl(tsc, -1, 0);
}
#ifdef CONFIG_PM
int tsc2301_mixer_suspend(struct tsc2301 *tsc)
{
/* power down entire audio section inside TSC2301 in case the
* chip is still powered during the system sleep. However this driver
* doesn't require that chip is powered because registers are restored
* in function tsc2301_mixer_resume */
mutex_lock(&tsc->mixer->mutex);
tsc2301_gpio_power_down(tsc);
tsc->mixer->shadow_regs[PD_MISC_INDEX] |= TSC2301_REG_PD_MISC_APD;
tsc2301_write_reg(tsc, TSC2301_REG_PD_MISC,
tsc->mixer->shadow_regs[PD_MISC_INDEX]);
mutex_unlock(&tsc->mixer->mutex);
return 0;
}
void tsc2301_mixer_resume(struct tsc2301 *tsc)
{
/* power up the TSC2301 audio section and restore registers */
mutex_lock(&tsc->mixer->mutex);
tsc->mixer->shadow_regs[PD_MISC_INDEX] &= ~TSC2301_REG_PD_MISC_APD;
tsc2301_flush_shadow_regs(tsc);
mutex_unlock(&tsc->mixer->mutex);
}
#endif
void tsc2301_mixer_enable_mclk(struct device *dev)
{
struct tsc2301 *tsc = dev_get_drvdata(dev);
struct tsc2301_mixer *mix = tsc->mixer;
mutex_lock(&mix->mutex);
if (!mix->mclk_enabled++ && tsc->enable_clock != NULL) {
tsc->enable_clock(dev);
}
tsc2301_power_ctrl(tsc, -1, 1);
mutex_unlock(&mix->mutex);
}
void tsc2301_mixer_disable_mclk(struct device *dev)
{
struct tsc2301 *tsc = dev_get_drvdata(dev);
struct tsc2301_mixer *mix = tsc->mixer;
mutex_lock(&mix->mutex);
mix->mclk_enabled--;
tsc2301_power_ctrl(tsc, -1, 1);
if (!mix->mclk_enabled && tsc->disable_clock != NULL) {
tsc->disable_clock(dev);
}
mutex_unlock(&mix->mutex);
}
static void tsc2301_mixer_delayed_power_down(struct work_struct *work)
{
struct tsc2301_mixer *mix = container_of(work, struct tsc2301_mixer,
delayed_power_down.work);
struct tsc2301 *tsc = mix->tsc;
mutex_lock(&mix->mutex);
if (!mix->delayed_pd_active) {
mutex_unlock(&mix->mutex);
return;
}
mix->delayed_pd_active = 0;
mutex_unlock(&mix->mutex);
tsc2301_mixer_disable_mclk(&tsc->spi->dev);
}
/*
* Allows audio controller driver to notify its usage of ADC and DAC
*/
void tsc2301_mixer_set_power(struct device *dev, int dac, int adc)
{
struct tsc2301 *tsc = dev_get_drvdata(dev);
mutex_lock(&tsc->mixer->mutex);
tsc->mixer->adc_enabled = adc;
tsc->mixer->dac_enabled = dac;
/* update power state of all audio blocks */
tsc2301_power_ctrl(tsc, -1, 1);
mutex_unlock(&tsc->mixer->mutex);
}
/*
* Registers TSC2301 ALSA Mixer controls for the given sound card
*/
int tsc2301_mixer_register_controls(struct device *dev, struct snd_card *card)
{
struct tsc2301 *tsc = dev_get_drvdata(dev);
struct tsc2301_mixer *mix = tsc->mixer;
int i, err;
/* Register ALSA mixer controls */
for (i = 0; i < ARRAY_SIZE(snd_tsc2301_controls); i++) {
err = snd_ctl_add(card,
snd_ctl_new1(&snd_tsc2301_controls[i], tsc));
if (err < 0)
return err;
}
if (!mix->n_mixer_gpios)
return 0;
/* Register additional GPIO controls if defined */
for (i = 0; i < mix->n_mixer_gpios; i++) {
const struct tsc2301_mixer_gpio *mg = mix->mixer_gpios + i;
struct snd_kcontrol *ctrlp;
struct snd_kcontrol_new ctrl =
TSC2301_BOOL((char *)mg->name, 0,
TSC2301_REG_GPIO, GPIO_INDEX,
mg->gpio, mg->inverted, mg->def_enable);
ctrlp = snd_ctl_new1(&ctrl, tsc);
err = snd_ctl_add(card, ctrlp);
if (err < 0)
return err;
}
return 0;
}
int tsc2301_mixer_init(struct tsc2301 *tsc,
struct tsc2301_platform_data *pdata)
{
struct tsc2301_mixer *mix;
int err = 0;
u16 w;
mix = kzalloc(sizeof(*mix), GFP_KERNEL);
if (mix == NULL)
return -ENOMEM;
tsc->mixer = mix;
mix->tsc = tsc;
mutex_init(&mix->mutex);
mix->platform_init = pdata->codec_init;
mix->platform_cleanup = pdata->codec_cleanup;
mix->pll_output = pdata->pll_output;
INIT_DELAYED_WORK(&mix->delayed_power_down,
tsc2301_mixer_delayed_power_down);
/* initialize shadow register default values */
w = 0xc000;
w |= (pdata->mclk_ratio << 6) | (pdata->i2s_sample_rate << 2);
w |= pdata->i2s_format;
mix->shadow_regs[AUDCNTL_INDEX] = w;
mix->shadow_regs[ADCVOL_INDEX] = 0xd7d7;
mix->shadow_regs[DACVOL_INDEX] = 0xffff;
mix->shadow_regs[BPVOL_INDEX] = 0xe7e7;
mix->shadow_regs[PD_MISC_INDEX] = pdata->power_down_blocks;
/* if extra mixer controls configured, then configure associated
* GPIOs as output and drive their default state */
if (pdata->n_mixer_gpios) {
int i;
w = 0;
for (i = 0; i < pdata->n_mixer_gpios; i++) {
const struct tsc2301_mixer_gpio *mg;
int gpio;
mg = pdata->mixer_gpios + i;
gpio = mg->gpio;
w |= (1 << gpio) << 8;
w |= (mg->inverted ^ mg->def_enable) << gpio;
}
mix->shadow_regs[GPIO_INDEX] = w;
mix->mixer_gpios = kmalloc(sizeof(*pdata->mixer_gpios) *
pdata->n_mixer_gpios,
GFP_KERNEL);
if (mix->mixer_gpios == NULL) {
err = -ENOMEM;
goto err1;
}
memcpy(mix->mixer_gpios, pdata->mixer_gpios,
sizeof(*pdata->mixer_gpios) * pdata->n_mixer_gpios);
mix->n_mixer_gpios = pdata->n_mixer_gpios;
}
/* PLL control */
tsc2301_write_pll(tsc, pdata->pll_n, pdata->pll_a, pdata->pll_pdc,
0, mix->pll_output ? 0 : 1);
tsc2301_flush_shadow_regs(tsc);
if (mix->platform_init != NULL) {
err = mix->platform_init(&tsc->spi->dev);
if (err < 0)
goto err2;
}
return 0;
err2:
if (mix->mixer_gpios != NULL)
kfree(mix->mixer_gpios);
err1:
kfree(mix);
return err;
}
void tsc2301_mixer_exit(struct tsc2301 *tsc)
{
struct tsc2301_mixer *mixer = tsc->mixer;
if (mixer->platform_cleanup != NULL)
mixer->platform_cleanup(&tsc->spi->dev);
if (mixer->mixer_gpios != NULL)
kfree(mixer->mixer_gpios);
}
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@nokia.com>");
MODULE_LICENSE("GPL");
#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