Commit c65c733e authored by Tony Lindgren's avatar Tony Lindgren

REMOVE OMAP LEGACY CODE: Delete old tsc2301-mixer.c

As pointed out by Jarkko Nikula. All future development
must be done on the alsa list with linux-omap list Cc'd.
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent f8990697
...@@ -31,297 +31,10 @@ ...@@ -31,297 +31,10 @@
#include <mach/dsp_common.h> #include <mach/dsp_common.h>
#if defined(CONFIG_SPI_TSC2301_AUDIO) && defined(CONFIG_SND_OMAP24XX_EAC)
#define AUDIO_ENABLED
static struct clk *sys_clkout2;
static struct clk *sys_clkout2_src;
static struct clk *func96m_clk;
static struct device *eac_device;
static struct device *tsc2301_device;
static int enable_audio;
static int audio_ok;
static spinlock_t audio_lock;
/*
* Leaving EAC and sys_clkout2 pins multiplexed to those subsystems results
* in about 2 mA extra current leak when audios are powered down. The
* workaround is to multiplex them to protected mode (with pull-ups enabled)
* whenever audio is not being used.
*/
static int eac_mux_disabled = 0;
static int clkout2_mux_disabled = 0;
static u32 saved_mux[2];
static void n800_enable_eac_mux(void)
{
if (!eac_mux_disabled)
return;
__raw_writel(saved_mux[1], OMAP2_IO_ADDRESS(0x48000124));
eac_mux_disabled = 0;
}
static void n800_disable_eac_mux(void)
{
if (eac_mux_disabled) {
WARN_ON(eac_mux_disabled);
return;
}
saved_mux[1] = __raw_readl(OMAP2_IO_ADDRESS(0x48000124));
__raw_writel(0x1f1f1f1f, OMAP2_IO_ADDRESS(0x48000124));
eac_mux_disabled = 1;
}
static void n800_enable_clkout2_mux(void)
{
if (!clkout2_mux_disabled)
return;
__raw_writel(saved_mux[0], OMAP2_IO_ADDRESS(0x480000e8));
clkout2_mux_disabled = 0;
}
static void n800_disable_clkout2_mux(void)
{
u32 l;
if (clkout2_mux_disabled) {
WARN_ON(clkout2_mux_disabled);
return;
}
saved_mux[0] = __raw_readl(OMAP2_IO_ADDRESS(0x480000e8));
l = saved_mux[0] & ~0xff;
l |= 0x1f;
__raw_writel(l, OMAP2_IO_ADDRESS(0x480000e8));
clkout2_mux_disabled = 1;
}
static int n800_eac_enable_ext_clocks(struct device *dev)
{
BUG_ON(tsc2301_device == NULL);
n800_enable_eac_mux();
tsc2301_mixer_enable_mclk(tsc2301_device);
return 0;
}
static void n800_eac_disable_ext_clocks(struct device *dev)
{
BUG_ON(tsc2301_device == NULL);
tsc2301_mixer_disable_mclk(tsc2301_device);
n800_disable_eac_mux();
}
static int n800_audio_set_power(void *pdata, int dac, int adc)
{
BUG_ON(pdata != tsc2301_device);
tsc2301_mixer_set_power(tsc2301_device, dac, adc);
return 0;
}
static int n800_audio_register_controls(void *pdata, struct snd_card *card)
{
BUG_ON(pdata != tsc2301_device);
return tsc2301_mixer_register_controls(tsc2301_device, card);
}
static struct eac_codec n800_eac_codec = {
.mclk_src = EAC_MCLK_EXT_2x12288000,
.codec_mode = EAC_CODEC_I2S_MASTER,
.codec_conf.i2s.polarity_changed_mode = 0,
.codec_conf.i2s.sync_delay_enable = 0,
.default_rate = 48000,
.set_power = n800_audio_set_power,
.register_controls = n800_audio_register_controls,
.short_name = "TSC2301",
};
static int n800_register_codec(void)
{
int r, do_enable = 0;
unsigned long flags;
n800_eac_codec.private_data = tsc2301_device;
r = eac_register_codec(eac_device, &n800_eac_codec);
if (r < 0)
return r;
spin_lock_irqsave(&audio_lock, flags);
audio_ok = 1;
if (enable_audio)
do_enable = 1;
spin_unlock_irqrestore(&audio_lock, flags);
if (do_enable)
eac_set_mode(eac_device, 1, 1);
return 0;
}
static void n800_unregister_codec(void)
{
audio_ok = 0;
eac_unregister_codec(eac_device);
eac_set_mode(eac_device, 0, 0);
}
static int n800_eac_init(struct device *dev)
{
int r;
BUG_ON(eac_device != NULL);
eac_device = dev;
if (tsc2301_device != NULL) {
r = n800_register_codec();
if (r < 0)
return r;
}
return 0;
}
static void n800_eac_cleanup(struct device *dev)
{
eac_device = NULL;
if (tsc2301_device != NULL)
n800_unregister_codec();
}
static int n800_codec_get_clocks(struct device *dev)
{
sys_clkout2_src = clk_get(dev, "sys_clkout2_src");
if (IS_ERR(sys_clkout2_src)) {
dev_err(dev, "Could not get sys_clkout2_src clock\n");
return -ENODEV;
}
sys_clkout2 = clk_get(dev, "sys_clkout2");
if (IS_ERR(sys_clkout2)) {
dev_err(dev, "Could not get sys_clkout2 clock\n");
clk_put(sys_clkout2_src);
return -ENODEV;
}
/* configure 12 MHz output on SYS_CLKOUT2. Therefore we must use
* 96 MHz as its parent in order to get 12 MHz */
func96m_clk = clk_get(dev, "func_96m_ck");
if (IS_ERR(func96m_clk)) {
dev_err(dev, "Could not get func 96M clock\n");
clk_put(sys_clkout2);
clk_put(sys_clkout2_src);
return -ENODEV;
}
clk_set_parent(sys_clkout2_src, func96m_clk);
clk_set_rate(sys_clkout2, 12000000);
return 0;
}
static void n800_codec_put_clocks(struct device *dev)
{
clk_put(func96m_clk);
clk_put(sys_clkout2);
clk_put(sys_clkout2_src);
}
static int n800_codec_enable_clock(struct device *dev)
{
n800_enable_clkout2_mux();
return clk_enable(sys_clkout2);
}
static void n800_codec_disable_clock(struct device *dev)
{
clk_disable(sys_clkout2);
n800_disable_clkout2_mux();
}
static int n800_codec_init(struct device *dev)
{
int r;
BUG_ON(tsc2301_device != NULL);
tsc2301_device = dev;
if ((r = n800_codec_get_clocks(dev)) < 0)
return r;
if (eac_device != NULL) {
r = n800_register_codec();
if (r < 0) {
n800_codec_put_clocks(dev);
return r;
}
}
return 0;
}
static void n800_codec_cleanup(struct device *dev)
{
tsc2301_device = NULL;
if (eac_device != NULL)
n800_unregister_codec();
n800_codec_put_clocks(dev);
}
static struct eac_platform_data n800_eac_data = {
.init = n800_eac_init,
.cleanup = n800_eac_cleanup,
.enable_ext_clocks = n800_eac_enable_ext_clocks,
.disable_ext_clocks = n800_eac_disable_ext_clocks,
};
static const struct tsc2301_mixer_gpio n800_mixer_gpios[] = {
{
.name = "Headset Amplifier",
.gpio = 1,
.deactivate_on_pd = 1,
}, {
.name = "Speaker Amplifier",
.gpio = 2,
.def_enable = 1,
.deactivate_on_pd = 1,
}, {
.name = "Headset Mic Select",
.gpio = 3,
}
};
static struct platform_device retu_headset_device = {
.name = "retu-headset",
.id = -1,
.dev = {
.release = NULL,
},
};
void __init n800_audio_init(struct tsc2301_platform_data *tc)
{
spin_lock_init(&audio_lock);
if (platform_device_register(&retu_headset_device) < 0)
return;
omap_init_eac(&n800_eac_data);
tc->pll_pdc = 7;
tc->pll_a = 7;
tc->pll_n = 9;
tc->pll_output = 1;
tc->mclk_ratio = TSC2301_MCLK_256xFS;
tc->i2s_sample_rate = TSC2301_I2S_SR_48000;
tc->i2s_format = TSC2301_I2S_FORMAT0;
tc->power_down_blocks = TSC2301_REG_PD_MISC_MOPD;
tc->mixer_gpios = n800_mixer_gpios;
tc->n_mixer_gpios = ARRAY_SIZE(n800_mixer_gpios);
tc->codec_init = n800_codec_init;
tc->codec_cleanup = n800_codec_cleanup;
tc->enable_clock = n800_codec_enable_clock;
tc->disable_clock = n800_codec_disable_clock;
}
#else
void __init n800_audio_init(struct tsc2301_platform_data *tc) void __init n800_audio_init(struct tsc2301_platform_data *tc)
{ {
} }
#endif
#ifdef CONFIG_OMAP_DSP #ifdef CONFIG_OMAP_DSP
int n800_audio_enable(struct dsp_kfunc_device *kdev, int stage) int n800_audio_enable(struct dsp_kfunc_device *kdev, int stage)
......
...@@ -40,7 +40,6 @@ obj-$(CONFIG_SPI_TSC2102) += tsc2102.o ...@@ -40,7 +40,6 @@ obj-$(CONFIG_SPI_TSC2102) += tsc2102.o
obj-$(CONFIG_SPI_TSC210X) += tsc210x.o obj-$(CONFIG_SPI_TSC210X) += tsc210x.o
obj-$(CONFIG_SPI_TSC2301) += tsc2301.o obj-$(CONFIG_SPI_TSC2301) += tsc2301.o
tsc2301-objs := tsc2301-core.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)
......
...@@ -205,12 +205,8 @@ static int __devinit tsc2301_probe(struct spi_device *spi) ...@@ -205,12 +205,8 @@ static int __devinit tsc2301_probe(struct spi_device *spi)
r = tsc2301_ts_init(tsc, pdata); r = tsc2301_ts_init(tsc, pdata);
if (r) if (r)
goto err2; goto err2;
r = tsc2301_mixer_init(tsc, pdata);
if (r)
goto err3;
return 0; return 0;
err3:
tsc2301_ts_exit(tsc); tsc2301_ts_exit(tsc);
err2: err2:
tsc2301_kp_exit(tsc); tsc2301_kp_exit(tsc);
...@@ -223,7 +219,6 @@ static int __devexit tsc2301_remove(struct spi_device *spi) ...@@ -223,7 +219,6 @@ static int __devexit tsc2301_remove(struct spi_device *spi)
{ {
struct tsc2301 *tsc = dev_get_drvdata(&spi->dev); struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
tsc2301_mixer_exit(tsc);
tsc2301_ts_exit(tsc); tsc2301_ts_exit(tsc);
tsc2301_kp_exit(tsc); tsc2301_kp_exit(tsc);
if (tsc->reset_gpio >= 0) if (tsc->reset_gpio >= 0)
...@@ -239,19 +234,14 @@ static int tsc2301_suspend(struct spi_device *spi, pm_message_t mesg) ...@@ -239,19 +234,14 @@ static int tsc2301_suspend(struct spi_device *spi, pm_message_t mesg)
struct tsc2301 *tsc = dev_get_drvdata(&spi->dev); struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
int r; int r;
if ((r = tsc2301_mixer_suspend(tsc)) < 0)
return r;
if ((r = tsc2301_kp_suspend(tsc)) < 0) if ((r = tsc2301_kp_suspend(tsc)) < 0)
goto err1; return r;
if ((r = tsc2301_ts_suspend(tsc)) < 0) if ((r = tsc2301_ts_suspend(tsc)) < 0) {
goto err2;
return 0;
err2:
tsc2301_kp_resume(tsc); tsc2301_kp_resume(tsc);
err1:
tsc2301_mixer_resume(tsc);
return r; return r;
}
return 0;
} }
static int tsc2301_resume(struct spi_device *spi) static int tsc2301_resume(struct spi_device *spi)
...@@ -260,7 +250,7 @@ static int tsc2301_resume(struct spi_device *spi) ...@@ -260,7 +250,7 @@ static int tsc2301_resume(struct spi_device *spi)
tsc2301_ts_resume(tsc); tsc2301_ts_resume(tsc);
tsc2301_kp_resume(tsc); tsc2301_kp_resume(tsc);
tsc2301_mixer_resume(tsc);
return 0; return 0;
} }
#endif #endif
......
/*
* 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");
...@@ -191,17 +191,6 @@ TSC2301_DECL_MOD(ts) ...@@ -191,17 +191,6 @@ TSC2301_DECL_MOD(ts)
TSC2301_DECL_EMPTY_MOD(ts) TSC2301_DECL_EMPTY_MOD(ts)
#endif #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_enable_mclk(struct device *tsc_dev);
extern void tsc2301_mixer_disable_mclk(struct device *tsc_dev); extern void tsc2301_mixer_disable_mclk(struct device *tsc_dev);
......
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