Merge branch 'topic/asoc' into for-linus

This commit is contained in:
Takashi Iwai 2009-03-24 00:35:53 +01:00
commit b5c784894c
130 changed files with 8678 additions and 3645 deletions

View File

@ -116,6 +116,9 @@ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
ARRAY_SIZE(wm8731_output_mixer_controls)),
If you dont want the mixer elements prefixed with the name of the mixer widget,
you can use SND_SOC_DAPM_MIXER_NAMED_CTL instead. the parameters are the same
as for SND_SOC_DAPM_MIXER.
2.3 Platform/Machine domain Widgets
-----------------------------------

View File

@ -135,6 +135,11 @@ static unsigned long e740_pin_config[] __initdata = {
/* IrDA */
GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
/* Audio power control */
GPIO16_GPIO, /* AC97 codec AVDD2 supply (analogue power) */
GPIO40_GPIO, /* Mic amp power */
GPIO41_GPIO, /* Headphone amp power */
/* PC Card */
GPIO8_GPIO, /* CD0 */
GPIO44_GPIO, /* CD1 */

View File

@ -133,6 +133,11 @@ static unsigned long e750_pin_config[] __initdata = {
/* IrDA */
GPIO38_GPIO | MFP_LPM_DRIVE_HIGH,
/* Audio power control */
GPIO4_GPIO, /* Headphone amp power */
GPIO7_GPIO, /* Speaker amp power */
GPIO37_GPIO, /* Headphone detect */
/* PC Card */
GPIO8_GPIO, /* CD0 */
GPIO44_GPIO, /* CD1 */

View File

@ -153,6 +153,13 @@ static unsigned long h5000_pin_config[] __initdata = {
GPIO23_SSP1_SCLK,
GPIO25_SSP1_TXD,
GPIO26_SSP1_RXD,
/* I2S */
GPIO28_I2S_BITCLK_OUT,
GPIO29_I2S_SDATA_IN,
GPIO30_I2S_SDATA_OUT,
GPIO31_I2S_SYNC,
GPIO32_I2S_SYSCLK,
};
/*

View File

@ -45,6 +45,21 @@
/* e7xx IrDA power control */
#define GPIO_E7XX_IR_OFF 38
/* e740 audio control GPIOs */
#define GPIO_E740_WM9705_nAVDD2 16
#define GPIO_E740_MIC_ON 40
#define GPIO_E740_AMP_ON 41
/* e750 audio control GPIOs */
#define GPIO_E750_HP_AMP_OFF 4
#define GPIO_E750_SPK_AMP_OFF 7
#define GPIO_E750_HP_DETECT 37
/* e800 audio control GPIOs */
#define GPIO_E800_HP_DETECT 81
#define GPIO_E800_HP_AMP_OFF 82
#define GPIO_E800_SPK_AMP_ON 83
/* ASIC related GPIOs */
#define GPIO_ESERIES_TMIO_IRQ 5
#define GPIO_ESERIES_TMIO_PCLR 19

View File

@ -50,7 +50,7 @@
#define SSCR0_TUM (1 << 23) /* Transmit FIFO underrun interrupt mask */
#define SSCR0_FRDC (0x07000000) /* Frame rate divider control (mask) */
#define SSCR0_SlotsPerFrm(x) (((x) - 1) << 24) /* Time slots per frame [1..8] */
#define SSCR0_ADC (1 << 30) /* Audio clock select */
#define SSCR0_ACS (1 << 30) /* Audio clock select */
#define SSCR0_MOD (1 << 31) /* Mode (normal or network) */
#endif
@ -109,6 +109,11 @@
#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */
#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */
#if defined(CONFIG_PXA3xx)
#define SSPSP_EDMYSTOP(x) ((x) << 28) /* Extended Dummy Stop */
#define SSPSP_EDMYSTRT(x) ((x) << 26) /* Extended Dummy Start */
#endif
#define SSPSP_FSRT (1 << 25) /* Frame Sync Relative Timing */
#define SSPSP_DMYSTOP(x) ((x) << 23) /* Dummy Stop */
#define SSPSP_SFRMWDTH(x) ((x) << 16) /* Serial Frame Width */

View File

@ -105,6 +105,12 @@ static unsigned long spitz_pin_config[] __initdata = {
GPIO57_nIOIS16,
GPIO104_PSKTSEL,
/* I2S */
GPIO28_I2S_BITCLK_OUT,
GPIO29_I2S_SDATA_IN,
GPIO30_I2S_SDATA_OUT,
GPIO31_I2S_SYNC,
/* MMC */
GPIO32_MMC_CLK,
GPIO112_MMC_CMD,

View File

@ -28,7 +28,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-iis.h>
#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2410_dma_mappings[] = {

View File

@ -29,8 +29,8 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-s3c2412-iis.h>
#include <asm/plat-s3c24xx/regs-iis.h>
#include <plat/regs-s3c2412-iis.h>
#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
#define MAP(x) { (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID, (x)| DMA_CH_VALID }

View File

@ -28,7 +28,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-iis.h>
#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
static struct s3c24xx_dma_map __initdata s3c2440_dma_mappings[] = {

View File

@ -29,7 +29,7 @@
#include <mach/regs-mem.h>
#include <mach/regs-lcd.h>
#include <mach/regs-sdi.h>
#include <asm/plat-s3c24xx/regs-iis.h>
#include <plat/regs-iis.h>
#include <plat/regs-spi.h>
#define MAP(x) { \

View File

@ -33,6 +33,9 @@
#define S3C2412_IISCON_RXDMA_ACTIVE (1 << 1)
#define S3C2412_IISCON_IIS_ACTIVE (1 << 0)
#define S3C64XX_IISMOD_IMS_PCLK (0 << 10)
#define S3C64XX_IISMOD_IMS_SYSMUX (1 << 10)
#define S3C2412_IISMOD_MASTER_INTERNAL (0 << 10)
#define S3C2412_IISMOD_MASTER_EXTERNAL (1 << 10)
#define S3C2412_IISMOD_SLAVE (2 << 10)
@ -44,8 +47,8 @@
#define S3C2412_IISMOD_LR_LLOW (0 << 7)
#define S3C2412_IISMOD_LR_RLOW (1 << 7)
#define S3C2412_IISMOD_SDF_IIS (0 << 5)
#define S3C2412_IISMOD_SDF_MSB (0 << 5)
#define S3C2412_IISMOD_SDF_LSB (0 << 5)
#define S3C2412_IISMOD_SDF_MSB (1 << 5)
#define S3C2412_IISMOD_SDF_LSB (2 << 5)
#define S3C2412_IISMOD_SDF_MASK (3 << 5)
#define S3C2412_IISMOD_RCLK_256FS (0 << 3)
#define S3C2412_IISMOD_RCLK_512FS (1 << 3)

View File

@ -490,6 +490,7 @@
/*
* R231 (0xE7) - Jack Status
*/
#define WM8350_JACK_L_LVL 0x0800
#define WM8350_JACK_R_LVL 0x0400
/*

View File

@ -1181,6 +1181,7 @@
#define WM8400_FLL_OUTDIV_SHIFT 0 /* FLL_OUTDIV - [2:0] */
#define WM8400_FLL_OUTDIV_WIDTH 3 /* FLL_OUTDIV - [2:0] */
struct wm8400;
void wm8400_reset_codec_reg_cache(struct wm8400 *wm8400);
#endif

View File

@ -42,4 +42,19 @@ extern int pxa2xx_ac97_hw_resume(void);
extern int pxa2xx_ac97_hw_probe(struct platform_device *dev);
extern void pxa2xx_ac97_hw_remove(struct platform_device *dev);
/* AC97 platform_data */
/**
* struct pxa2xx_ac97_platform_data - pxa ac97 platform data
* @reset_gpio: AC97 reset gpio (normally gpio113 or gpio95)
* a -1 value means no gpio will be used for reset
*
* Platform data should only be specified for pxa27x CPUs where a silicon bug
* prevents correct operation of the reset line. If not specified, the default
* behaviour is to consider gpio 113 as the AC97 reset line, which is the
* default on most boards.
*/
struct pxa2xx_ac97_platform_data {
int reset_gpio;
};
#endif

View File

@ -203,7 +203,7 @@ struct snd_soc_dai {
int (*resume)(struct snd_soc_dai *dai);
/* ops */
struct snd_soc_dai_ops ops;
struct snd_soc_dai_ops *ops;
/* DAI capabilities */
struct snd_soc_pcm_stream capture;

View File

@ -76,6 +76,11 @@
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
wcontrols, wncontrols)\
{ .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
.shift = wshift, .invert = winvert, .kcontrols = wcontrols, \
.num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0}
@ -101,6 +106,11 @@
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, .num_kcontrols = wncontrols, \
.event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
wcontrols, wncontrols, wevent, wflags) \
{ .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = wcontrols, \
.num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_MICBIAS_E(wname, wreg, wshift, winvert, wevent, wflags) \
{ .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
.invert = winvert, .kcontrols = NULL, .num_kcontrols = 0, \
@ -182,6 +192,12 @@
.get = snd_soc_dapm_get_value_enum_double, \
.put = snd_soc_dapm_put_value_enum_double, \
.private_value = (unsigned long)&xenum }
#define SOC_DAPM_PIN_SWITCH(xname) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
.info = snd_soc_dapm_info_pin_switch, \
.get = snd_soc_dapm_get_pin_switch, \
.put = snd_soc_dapm_put_pin_switch, \
.private_value = (unsigned long)xname }
/* dapm stream operations */
#define SND_SOC_DAPM_STREAM_NOP 0x0
@ -228,6 +244,12 @@ int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uncontrol);
int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *uncontrol);
int snd_soc_dapm_new_control(struct snd_soc_codec *codec,
const struct snd_soc_dapm_widget *widget);
int snd_soc_dapm_new_controls(struct snd_soc_codec *codec,
@ -250,10 +272,10 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev,
int snd_soc_dapm_sys_add(struct device *dev);
/* dapm audio pin control and status */
int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin);
int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, const char *pin);
int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, const char *pin);
int snd_soc_dapm_nc_pin(struct snd_soc_codec *codec, const char *pin);
int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, const char *pin);
int snd_soc_dapm_sync(struct snd_soc_codec *codec);
/* dapm widget types */
@ -263,6 +285,7 @@ enum snd_soc_dapm_type {
snd_soc_dapm_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_value_mux, /* selects 1 analog signal from many inputs */
snd_soc_dapm_mixer, /* mixes several analog signals together */
snd_soc_dapm_mixer_named_ctl, /* mixer with named controls */
snd_soc_dapm_pga, /* programmable gain/attenuation (volume) */
snd_soc_dapm_adc, /* analog to digital converter */
snd_soc_dapm_dac, /* digital to analog converter */

View File

@ -16,6 +16,8 @@
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/control.h>
@ -154,6 +156,8 @@ enum snd_soc_bias_level {
SND_SOC_BIAS_OFF,
};
struct snd_jack;
struct snd_soc_card;
struct snd_soc_device;
struct snd_soc_pcm_stream;
struct snd_soc_ops;
@ -164,6 +168,11 @@ struct snd_soc_platform;
struct snd_soc_codec;
struct soc_enum;
struct snd_soc_ac97_ops;
struct snd_soc_jack;
struct snd_soc_jack_pin;
#ifdef CONFIG_GPIOLIB
struct snd_soc_jack_gpio;
#endif
typedef int (*hw_write_t)(void *,const char* ,int);
typedef int (*hw_read_t)(void *,char* ,int);
@ -184,6 +193,19 @@ int snd_soc_init_card(struct snd_soc_device *socdev);
int snd_soc_set_runtime_hwparams(struct snd_pcm_substream *substream,
const struct snd_pcm_hardware *hw);
/* Jack reporting */
int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type,
struct snd_soc_jack *jack);
void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask);
int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_pin *pins);
#ifdef CONFIG_GPIOLIB
int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios);
void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
struct snd_soc_jack_gpio *gpios);
#endif
/* codec IO */
#define snd_soc_read(codec, reg) codec->read(codec, reg)
#define snd_soc_write(codec, reg, value) codec->write(codec, reg, value)
@ -203,6 +225,8 @@ void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
*/
struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
void *data, char *long_name);
int snd_soc_add_controls(struct snd_soc_codec *codec,
const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
@ -237,6 +261,48 @@ int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
/**
* struct snd_soc_jack_pin - Describes a pin to update based on jack detection
*
* @pin: name of the pin to update
* @mask: bits to check for in reported jack status
* @invert: if non-zero then pin is enabled when status is not reported
*/
struct snd_soc_jack_pin {
struct list_head list;
const char *pin;
int mask;
bool invert;
};
/**
* struct snd_soc_jack_gpio - Describes a gpio pin for jack detection
*
* @gpio: gpio number
* @name: gpio name
* @report: value to report when jack detected
* @invert: report presence in low state
* @debouce_time: debouce time in ms
*/
#ifdef CONFIG_GPIOLIB
struct snd_soc_jack_gpio {
unsigned int gpio;
const char *name;
int report;
int invert;
int debounce_time;
struct snd_soc_jack *jack;
struct work_struct work;
};
#endif
struct snd_soc_jack {
struct snd_jack *jack;
struct snd_soc_card *card;
struct list_head pins;
int status;
};
/* SoC PCM stream information */
struct snd_soc_pcm_stream {
char *stream_name;
@ -384,6 +450,8 @@ struct snd_soc_card {
struct snd_soc_device *socdev;
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct delayed_work delayed_work;
struct work_struct deferred_resume_work;
@ -393,7 +461,6 @@ struct snd_soc_card {
struct snd_soc_device {
struct device *dev;
struct snd_soc_card *card;
struct snd_soc_codec *codec;
struct snd_soc_codec_device *codec_dev;
void *codec_data;
};

View File

@ -31,6 +31,7 @@ static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
static volatile long gsr_bits;
static struct clk *ac97_clk;
static struct clk *ac97conf_clk;
static int reset_gpio;
/*
* Beware PXA27x bugs:
@ -42,6 +43,45 @@ static struct clk *ac97conf_clk;
* 1 jiffy timeout if interrupt never comes).
*/
enum {
RESETGPIO_FORCE_HIGH,
RESETGPIO_FORCE_LOW,
RESETGPIO_NORMAL_ALTFUNC
};
/**
* set_resetgpio_mode - computes and sets the AC97_RESET gpio mode on PXA
* @mode: chosen action
*
* As the PXA27x CPUs suffer from a AC97 bug, a manual control of the reset line
* must be done to insure proper work of AC97 reset line. This function
* computes the correct gpio_mode for further use by reset functions, and
* applied the change through pxa_gpio_mode.
*/
static void set_resetgpio_mode(int resetgpio_action)
{
int mode = 0;
if (reset_gpio)
switch (resetgpio_action) {
case RESETGPIO_NORMAL_ALTFUNC:
if (reset_gpio == 113)
mode = 113 | GPIO_OUT | GPIO_DFLT_LOW;
if (reset_gpio == 95)
mode = 95 | GPIO_ALT_FN_1_OUT;
break;
case RESETGPIO_FORCE_LOW:
mode = reset_gpio | GPIO_OUT | GPIO_DFLT_LOW;
break;
case RESETGPIO_FORCE_HIGH:
mode = reset_gpio | GPIO_OUT | GPIO_DFLT_HIGH;
break;
};
if (mode)
pxa_gpio_mode(mode);
}
unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
unsigned short val = -1;
@ -137,10 +177,10 @@ static inline void pxa_ac97_warm_pxa27x(void)
/* warm reset broken on Bulverde,
so manually keep AC97 reset high */
pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
set_resetgpio_mode(RESETGPIO_FORCE_HIGH);
udelay(10);
GCR |= GCR_WARM_RST;
pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC);
udelay(500);
}
@ -308,8 +348,8 @@ int pxa2xx_ac97_hw_resume(void)
pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
}
if (cpu_is_pxa27x()) {
/* Use GPIO 113 as AC97 Reset on Bulverde */
pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
/* Use GPIO 113 or 95 as AC97 Reset on Bulverde */
set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC);
}
clk_enable(ac97_clk);
return 0;
@ -320,6 +360,27 @@ EXPORT_SYMBOL_GPL(pxa2xx_ac97_hw_resume);
int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
{
int ret;
struct pxa2xx_ac97_platform_data *pdata = dev->dev.platform_data;
if (pdata) {
switch (pdata->reset_gpio) {
case 95:
case 113:
reset_gpio = pdata->reset_gpio;
break;
case 0:
reset_gpio = 113;
break;
case -1:
break;
default:
dev_err(&dev->dev, "Invalid reset GPIO %d\n",
pdata->reset_gpio);
}
} else {
if (cpu_is_pxa27x())
reset_gpio = 113;
}
if (cpu_is_pxa25x() || cpu_is_pxa27x()) {
pxa_gpio_mode(GPIO31_SYNC_AC97_MD);
@ -330,7 +391,7 @@ int __devinit pxa2xx_ac97_hw_probe(struct platform_device *dev)
if (cpu_is_pxa27x()) {
/* Use GPIO 113 as AC97 Reset on Bulverde */
pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
set_resetgpio_mode(RESETGPIO_NORMAL_ALTFUNC);
ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
if (IS_ERR(ac97conf_clk)) {
ret = PTR_ERR(ac97conf_clk);

View File

@ -6,6 +6,7 @@ menuconfig SND_SOC
tristate "ALSA for SoC audio support"
select SND_PCM
select AC97_BUS if SND_SOC_AC97_BUS
select SND_JACK if INPUT=y || INPUT=SND
---help---
If you want ASoC support, you should say Y here and also to the

View File

@ -1,4 +1,4 @@
snd-soc-core-objs := soc-core.o soc-dapm.o
snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/

View File

@ -347,7 +347,7 @@ static int atmel_pcm_mmap(struct snd_pcm_substream *substream,
vma->vm_end - vma->vm_start, vma->vm_page_prot);
}
struct snd_pcm_ops atmel_pcm_ops = {
static struct snd_pcm_ops atmel_pcm_ops = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
.ioctl = snd_pcm_lib_ioctl,

View File

@ -697,6 +697,15 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops atmel_ssc_dai_ops = {
.startup = atmel_ssc_startup,
.shutdown = atmel_ssc_shutdown,
.prepare = atmel_ssc_prepare,
.hw_params = atmel_ssc_hw_params,
.set_fmt = atmel_ssc_set_dai_fmt,
.set_clkdiv = atmel_ssc_set_dai_clkdiv,
};
struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
{ .name = "atmel-ssc0",
.id = 0,
@ -712,13 +721,7 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = {
.startup = atmel_ssc_startup,
.shutdown = atmel_ssc_shutdown,
.prepare = atmel_ssc_prepare,
.hw_params = atmel_ssc_hw_params,
.set_fmt = atmel_ssc_set_dai_fmt,
.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
.ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[0],
},
#if NUM_SSC_DEVICES == 3
@ -736,13 +739,7 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = {
.startup = atmel_ssc_startup,
.shutdown = atmel_ssc_shutdown,
.prepare = atmel_ssc_prepare,
.hw_params = atmel_ssc_hw_params,
.set_fmt = atmel_ssc_set_dai_fmt,
.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
.ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[1],
},
{ .name = "atmel-ssc2",
@ -759,13 +756,7 @@ struct snd_soc_dai atmel_ssc_dai[NUM_SSC_DEVICES] = {
.channels_max = 2,
.rates = ATMEL_SSC_RATES,
.formats = ATMEL_SSC_FORMATS,},
.ops = {
.startup = atmel_ssc_startup,
.shutdown = atmel_ssc_shutdown,
.prepare = atmel_ssc_prepare,
.hw_params = atmel_ssc_hw_params,
.set_fmt = atmel_ssc_set_dai_fmt,
.set_clkdiv = atmel_ssc_set_dai_clkdiv,},
.ops = &atmel_ssc_dai_ops,
.private_data = &ssc_info[2],
},
#endif

View File

@ -164,41 +164,41 @@ static int playpaq_wm8510_hw_params(struct snd_pcm_substream *substream,
*/
switch (params_rate(params)) {
case 48000:
pll_out = 12288000;
mclk_div = WM8510_MCLKDIV_1;
bclk = WM8510_BCLKDIV_8;
break;
case 44100:
pll_out = 11289600;
mclk_div = WM8510_MCLKDIV_1;
bclk = WM8510_BCLKDIV_8;
break;
case 22050:
pll_out = 11289600;
pll_out = 24576000;
mclk_div = WM8510_MCLKDIV_2;
bclk = WM8510_BCLKDIV_8;
break;
case 16000:
pll_out = 12288000;
mclk_div = WM8510_MCLKDIV_3;
case 44100:
pll_out = 22579200;
mclk_div = WM8510_MCLKDIV_2;
bclk = WM8510_BCLKDIV_8;
break;
case 11025:
pll_out = 11289600;
case 22050:
pll_out = 22579200;
mclk_div = WM8510_MCLKDIV_4;
bclk = WM8510_BCLKDIV_8;
break;
case 8000:
pll_out = 12288000;
case 16000:
pll_out = 24576000;
mclk_div = WM8510_MCLKDIV_6;
bclk = WM8510_BCLKDIV_8;
break;
case 11025:
pll_out = 22579200;
mclk_div = WM8510_MCLKDIV_8;
bclk = WM8510_BCLKDIV_8;
break;
case 8000:
pll_out = 24576000;
mclk_div = WM8510_MCLKDIV_12;
bclk = WM8510_BCLKDIV_8;
break;
default:
pr_warning("playpaq_wm8510: Unsupported sample rate %d\n",
params_rate(params));

View File

@ -36,6 +36,7 @@
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/atmel-ssc.h>
@ -45,6 +46,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
#include <mach/gpio.h>
@ -52,6 +54,9 @@
#include "atmel-pcm.h"
#include "atmel_ssc_dai.h"
#define MCLK_RATE 12000000
static struct clk *mclk;
static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
{
@ -59,11 +64,12 @@ static int at91sam9g20ek_startup(struct snd_pcm_substream *substream)
struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
int ret;
/* codec system clock is supplied by PCK0, set to 12MHz */
ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK,
12000000, SND_SOC_CLOCK_IN);
if (ret < 0)
MCLK_RATE, SND_SOC_CLOCK_IN);
if (ret < 0) {
clk_disable(mclk);
return ret;
}
return 0;
}
@ -189,6 +195,31 @@ static struct snd_soc_ops at91sam9g20ek_ops = {
.shutdown = at91sam9g20ek_shutdown,
};
static int at91sam9g20ek_set_bias_level(struct snd_soc_card *card,
enum snd_soc_bias_level level)
{
static int mclk_on;
int ret = 0;
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
if (!mclk_on)
ret = clk_enable(mclk);
if (ret == 0)
mclk_on = 1;
break;
case SND_SOC_BIAS_OFF:
case SND_SOC_BIAS_STANDBY:
if (mclk_on)
clk_disable(mclk);
mclk_on = 0;
break;
}
return ret;
}
static const struct snd_soc_dapm_widget at91sam9g20ek_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Int Mic", NULL),
@ -243,21 +274,48 @@ static struct snd_soc_dai_link at91sam9g20ek_dai = {
};
static struct snd_soc_card snd_soc_at91sam9g20ek = {
.name = "WM8731",
.name = "AT91SAMG20-EK",
.platform = &atmel_soc_platform,
.dai_link = &at91sam9g20ek_dai,
.num_links = 1,
.set_bias_level = at91sam9g20ek_set_bias_level,
};
static struct wm8731_setup_data at91sam9g20ek_wm8731_setup = {
.i2c_bus = 0,
.i2c_address = 0x1b,
};
/*
* FIXME: This is a temporary bodge to avoid cross-tree merge issues.
* New drivers should register the wm8731 I2C device in the machine
* setup code (under arch/arm for ARM systems).
*/
static int wm8731_i2c_register(void)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = 0x1b;
strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
adapter = i2c_get_adapter(0);
if (!adapter) {
printk(KERN_ERR "can't get i2c adapter 0\n");
return -ENODEV;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
printk(KERN_ERR "can't add i2c device at 0x%x\n",
(unsigned int)info.addr);
return -ENODEV;
}
return 0;
}
static struct snd_soc_device at91sam9g20ek_snd_devdata = {
.card = &snd_soc_at91sam9g20ek,
.codec_dev = &soc_codec_dev_wm8731,
.codec_data = &at91sam9g20ek_wm8731_setup,
};
static struct platform_device *at91sam9g20ek_snd_device;
@ -266,23 +324,56 @@ static int __init at91sam9g20ek_init(void)
{
struct atmel_ssc_info *ssc_p = at91sam9g20ek_dai.cpu_dai->private_data;
struct ssc_device *ssc = NULL;
struct clk *pllb;
int ret;
if (!machine_is_at91sam9g20ek())
return -ENODEV;
/*
* Codec MCLK is supplied by PCK0 - set it up.
*/
mclk = clk_get(NULL, "pck0");
if (IS_ERR(mclk)) {
printk(KERN_ERR "ASoC: Failed to get MCLK\n");
ret = PTR_ERR(mclk);
goto err;
}
pllb = clk_get(NULL, "pllb");
if (IS_ERR(mclk)) {
printk(KERN_ERR "ASoC: Failed to get PLLB\n");
ret = PTR_ERR(mclk);
goto err_mclk;
}
ret = clk_set_parent(mclk, pllb);
clk_put(pllb);
if (ret != 0) {
printk(KERN_ERR "ASoC: Failed to set MCLK parent\n");
goto err_mclk;
}
clk_set_rate(mclk, MCLK_RATE);
/*
* Request SSC device
*/
ssc = ssc_request(0);
if (IS_ERR(ssc)) {
printk(KERN_ERR "ASoC: Failed to request SSC 0\n");
ret = PTR_ERR(ssc);
ssc = NULL;
goto err_ssc;
}
ssc_p->ssc = ssc;
ret = wm8731_i2c_register();
if (ret != 0)
goto err_ssc;
at91sam9g20ek_snd_device = platform_device_alloc("soc-audio", -1);
if (!at91sam9g20ek_snd_device) {
printk(KERN_DEBUG
"platform device allocation failed\n");
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
ret = -ENOMEM;
}
@ -292,14 +383,19 @@ static int __init at91sam9g20ek_init(void)
ret = platform_device_add(at91sam9g20ek_snd_device);
if (ret) {
printk(KERN_DEBUG
"platform device allocation failed\n");
printk(KERN_ERR "ASoC: Platform device allocation failed\n");
platform_device_put(at91sam9g20ek_snd_device);
}
return ret;
err_ssc:
ssc_free(ssc);
ssc_p->ssc = NULL;
err_mclk:
clk_put(mclk);
mclk = NULL;
err:
return ret;
}
@ -317,6 +413,8 @@ static void __exit at91sam9g20ek_exit(void)
platform_device_unregister(at91sam9g20ek_snd_device);
at91sam9g20ek_snd_device = NULL;
clk_put(mclk);
mclk = NULL;
}
module_init(at91sam9g20ek_init);

View File

@ -305,7 +305,7 @@ static int au1xpsc_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
struct snd_pcm_ops au1xpsc_pcm_ops = {
static struct snd_pcm_ops au1xpsc_pcm_ops = {
.open = au1xpsc_pcm_open,
.close = au1xpsc_pcm_close,
.ioctl = snd_pcm_lib_ioctl,

View File

@ -342,6 +342,11 @@ static int au1xpsc_ac97_resume(struct snd_soc_dai *dai)
return 0;
}
static struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
.trigger = au1xpsc_ac97_trigger,
.hw_params = au1xpsc_ac97_hw_params,
};
struct snd_soc_dai au1xpsc_ac97_dai = {
.name = "au1xpsc_ac97",
.ac97_control = 1,
@ -361,10 +366,7 @@ struct snd_soc_dai au1xpsc_ac97_dai = {
.channels_min = 2,
.channels_max = 2,
},
.ops = {
.trigger = au1xpsc_ac97_trigger,
.hw_params = au1xpsc_ac97_hw_params,
},
.ops = &au1xpsc_ac97_dai_ops,
};
EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai);

View File

@ -367,6 +367,12 @@ static int au1xpsc_i2s_resume(struct snd_soc_dai *cpu_dai)
return 0;
}
static struct snd_soc_dai_ops au1xpsc_i2s_dai_ops = {
.trigger = au1xpsc_i2s_trigger,
.hw_params = au1xpsc_i2s_hw_params,
.set_fmt = au1xpsc_i2s_set_fmt,
};
struct snd_soc_dai au1xpsc_i2s_dai = {
.name = "au1xpsc_i2s",
.probe = au1xpsc_i2s_probe,
@ -385,11 +391,7 @@ struct snd_soc_dai au1xpsc_i2s_dai = {
.channels_min = 2,
.channels_max = 8, /* 2 without external help */
},
.ops = {
.trigger = au1xpsc_i2s_trigger,
.hw_params = au1xpsc_i2s_hw_params,
.set_fmt = au1xpsc_i2s_set_fmt,
},
.ops = &au1xpsc_i2s_dai_ops,
};
EXPORT_SYMBOL(au1xpsc_i2s_dai);

View File

@ -297,7 +297,7 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
}
#endif
struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
static struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,

View File

@ -31,72 +31,46 @@
#include "bf5xx-sport.h"
#include "bf5xx-ac97.h"
#if defined(CONFIG_BF54x)
#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \
P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \
P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \
P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0}
#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \
P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0}
#else
#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
#endif
static int *cmd_count;
static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
#define SPORT_REQ(x) \
[x] = {P_SPORT##x##_TFS, P_SPORT##x##_DTPRI, P_SPORT##x##_TSCLK, \
P_SPORT##x##_RFS, P_SPORT##x##_DRPRI, P_SPORT##x##_RSCLK, 0}
static u16 sport_req[][7] = {
PIN_REQ_SPORT_0,
#ifdef PIN_REQ_SPORT_1
PIN_REQ_SPORT_1,
#ifdef SPORT0_TCR1
SPORT_REQ(0),
#endif
#ifdef PIN_REQ_SPORT_2
PIN_REQ_SPORT_2,
#ifdef SPORT1_TCR1
SPORT_REQ(1),
#endif
#ifdef PIN_REQ_SPORT_3
PIN_REQ_SPORT_3,
#ifdef SPORT2_TCR1
SPORT_REQ(2),
#endif
};
#ifdef SPORT3_TCR1
SPORT_REQ(3),
#endif
};
static struct sport_param sport_params[4] = {
{
.dma_rx_chan = CH_SPORT0_RX,
.dma_tx_chan = CH_SPORT0_TX,
.err_irq = IRQ_SPORT0_ERROR,
.regs = (struct sport_register *)SPORT0_TCR1,
},
#ifdef PIN_REQ_SPORT_1
{
.dma_rx_chan = CH_SPORT1_RX,
.dma_tx_chan = CH_SPORT1_TX,
.err_irq = IRQ_SPORT1_ERROR,
.regs = (struct sport_register *)SPORT1_TCR1,
},
#endif
#ifdef PIN_REQ_SPORT_2
{
.dma_rx_chan = CH_SPORT2_RX,
.dma_tx_chan = CH_SPORT2_TX,
.err_irq = IRQ_SPORT2_ERROR,
.regs = (struct sport_register *)SPORT2_TCR1,
},
#endif
#ifdef PIN_REQ_SPORT_3
{
.dma_rx_chan = CH_SPORT3_RX,
.dma_tx_chan = CH_SPORT3_TX,
.err_irq = IRQ_SPORT3_ERROR,
.regs = (struct sport_register *)SPORT3_TCR1,
#define SPORT_PARAMS(x) \
[x] = { \
.dma_rx_chan = CH_SPORT##x##_RX, \
.dma_tx_chan = CH_SPORT##x##_TX, \
.err_irq = IRQ_SPORT##x##_ERROR, \
.regs = (struct sport_register *)SPORT##x##_TCR1, \
}
static struct sport_param sport_params[4] = {
#ifdef SPORT0_TCR1
SPORT_PARAMS(0),
#endif
#ifdef SPORT1_TCR1
SPORT_PARAMS(1),
#endif
#ifdef SPORT2_TCR1
SPORT_PARAMS(2),
#endif
#ifdef SPORT3_TCR1
SPORT_PARAMS(3),
#endif
};
@ -332,11 +306,11 @@ static int bf5xx_ac97_probe(struct platform_device *pdev,
if (cmd_count == NULL)
return -ENOMEM;
if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
if (peripheral_request_list(sport_req[sport_num], "soc-audio")) {
pr_err("Requesting Peripherals failed\n");
ret = -EFAULT;
goto peripheral_err;
}
}
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
/* Request PB3 as reset pin */
@ -383,9 +357,9 @@ sport_config_err:
sport_err:
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif
gpio_err:
peripheral_free_list(&sport_req[sport_num][0]);
#endif
peripheral_free_list(sport_req[sport_num]);
peripheral_err:
free_page((unsigned long)cmd_count);
cmd_count = NULL;
@ -398,7 +372,7 @@ static void bf5xx_ac97_remove(struct platform_device *pdev,
{
free_page((unsigned long)cmd_count);
cmd_count = NULL;
peripheral_free_list(&sport_req[sport_num][0]);
peripheral_free_list(sport_req[sport_num]);
#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
#endif

View File

@ -114,7 +114,7 @@ static int snd_ad73311_configure(void)
SSYNC();
/* When TUVF is set, the data is already send out */
while (!(status & TUVF) && count++ < 10000) {
while (!(status & TUVF) && ++count < 10000) {
udelay(1);
status = bfin_read_SPORT_STAT();
SSYNC();
@ -123,7 +123,7 @@ static int snd_ad73311_configure(void)
SSYNC();
local_irq_enable();
if (count == 10000) {
if (count >= 10000) {
printk(KERN_ERR "ad73311: failed to configure codec\n");
return -1;
}

View File

@ -184,7 +184,7 @@ static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
return 0 ;
}
struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
static struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
.open = bf5xx_pcm_open,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = bf5xx_pcm_hw_params,

View File

@ -287,6 +287,13 @@ static int bf5xx_i2s_resume(struct platform_device *pdev,
#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops bf5xx_i2s_dai_ops = {
.startup = bf5xx_i2s_startup,
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
};
struct snd_soc_dai bf5xx_i2s_dai = {
.name = "bf5xx-i2s",
.id = 0,
@ -304,12 +311,7 @@ struct snd_soc_dai bf5xx_i2s_dai = {
.channels_max = 2,
.rates = BF5XX_I2S_RATES,
.formats = BF5XX_I2S_FORMATS,},
.ops = {
.startup = bf5xx_i2s_startup,
.shutdown = bf5xx_i2s_shutdown,
.hw_params = bf5xx_i2s_hw_params,
.set_fmt = bf5xx_i2s_set_dai_fmt,
},
.ops = &bf5xx_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);

View File

@ -133,7 +133,7 @@ static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
int i;
for (i = 0; i < fragcount; ++i) {
desc[i].next_desc_addr = (unsigned long)&(desc[i + 1]);
desc[i].next_desc_addr = &(desc[i + 1]);
desc[i].start_addr = (unsigned long)buf + i*fragsize;
desc[i].cfg = cfg;
desc[i].x_count = x_count;
@ -143,12 +143,12 @@ static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
}
/* make circular */
desc[fragcount-1].next_desc_addr = (unsigned long)desc;
desc[fragcount-1].next_desc_addr = desc;
pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p,"
"next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
&(desc[0]), desc[0].next_desc_addr,
&(desc[1]), desc[1].next_desc_addr,
pr_debug("setup desc: desc0=%p, next0=%p, desc1=%p,"
"next1=%p\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
desc, desc[0].next_desc_addr,
desc+1, desc[1].next_desc_addr,
desc[0].x_count, desc[0].y_count,
desc[0].start_addr, desc[0].cfg);
}
@ -184,22 +184,20 @@ static inline int sport_hook_rx_dummy(struct sport_device *sport)
BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc);
/* Maybe the dummy buffer descriptor ring is damaged */
sport->dummy_rx_desc->next_desc_addr = \
(unsigned long)(sport->dummy_rx_desc+1);
sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc + 1;
local_irq_save(flags);
desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan);
desc = get_dma_next_desc_ptr(sport->dma_rx_chan);
/* Copy the descriptor which will be damaged to backup */
temp_desc = *desc;
desc->x_count = 0xa;
desc->y_count = 0;
desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc);
desc->next_desc_addr = sport->dummy_rx_desc;
local_irq_restore(flags);
/* Waiting for dummy buffer descriptor is already hooked*/
while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
sizeof(struct dmasg)) !=
(unsigned long)sport->dummy_rx_desc)
;
sizeof(struct dmasg)) != sport->dummy_rx_desc)
continue;
sport->curr_rx_desc = sport->dummy_rx_desc;
/* Restore the damaged descriptor */
*desc = temp_desc;
@ -210,14 +208,12 @@ static inline int sport_hook_rx_dummy(struct sport_device *sport)
static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
{
if (dummy) {
sport->dummy_rx_desc->next_desc_addr = \
(unsigned long) sport->dummy_rx_desc;
sport->dummy_rx_desc->next_desc_addr = sport->dummy_rx_desc;
sport->curr_rx_desc = sport->dummy_rx_desc;
} else
sport->curr_rx_desc = sport->dma_rx_desc;
set_dma_next_desc_addr(sport->dma_rx_chan, \
(unsigned long)(sport->curr_rx_desc));
set_dma_next_desc_addr(sport->dma_rx_chan, sport->curr_rx_desc);
set_dma_x_count(sport->dma_rx_chan, 0);
set_dma_x_modify(sport->dma_rx_chan, 0);
set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \
@ -231,14 +227,12 @@ static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
static inline int sport_tx_dma_start(struct sport_device *sport, int dummy)
{
if (dummy) {
sport->dummy_tx_desc->next_desc_addr = \
(unsigned long) sport->dummy_tx_desc;
sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc;
sport->curr_tx_desc = sport->dummy_tx_desc;
} else
sport->curr_tx_desc = sport->dma_tx_desc;
set_dma_next_desc_addr(sport->dma_tx_chan, \
(unsigned long)(sport->curr_tx_desc));
set_dma_next_desc_addr(sport->dma_tx_chan, sport->curr_tx_desc);
set_dma_x_count(sport->dma_tx_chan, 0);
set_dma_x_modify(sport->dma_tx_chan, 0);
set_dma_config(sport->dma_tx_chan,
@ -261,11 +255,9 @@ int sport_rx_start(struct sport_device *sport)
BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc);
local_irq_save(flags);
while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
sizeof(struct dmasg)) !=
(unsigned long)sport->dummy_rx_desc)
;
sport->dummy_rx_desc->next_desc_addr =
(unsigned long)(sport->dma_rx_desc);
sizeof(struct dmasg)) != sport->dummy_rx_desc)
continue;
sport->dummy_rx_desc->next_desc_addr = sport->dma_rx_desc;
local_irq_restore(flags);
sport->curr_rx_desc = sport->dma_rx_desc;
} else {
@ -310,23 +302,21 @@ static inline int sport_hook_tx_dummy(struct sport_device *sport)
BUG_ON(sport->dummy_tx_desc == NULL);
BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc);
sport->dummy_tx_desc->next_desc_addr = \
(unsigned long)(sport->dummy_tx_desc+1);
sport->dummy_tx_desc->next_desc_addr = sport->dummy_tx_desc + 1;
/* Shorten the time on last normal descriptor */
local_irq_save(flags);
desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan);
desc = get_dma_next_desc_ptr(sport->dma_tx_chan);
/* Store the descriptor which will be damaged */
temp_desc = *desc;
desc->x_count = 0xa;
desc->y_count = 0;
desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc);
desc->next_desc_addr = sport->dummy_tx_desc;
local_irq_restore(flags);
/* Waiting for dummy buffer descriptor is already hooked*/
while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \
sizeof(struct dmasg)) != \
(unsigned long)sport->dummy_tx_desc)
;
sizeof(struct dmasg)) != sport->dummy_tx_desc)
continue;
sport->curr_tx_desc = sport->dummy_tx_desc;
/* Restore the damaged descriptor */
*desc = temp_desc;
@ -347,11 +337,9 @@ int sport_tx_start(struct sport_device *sport)
/* Hook the normal buffer descriptor */
local_irq_save(flags);
while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) -
sizeof(struct dmasg)) !=
(unsigned long)sport->dummy_tx_desc)
;
sport->dummy_tx_desc->next_desc_addr =
(unsigned long)(sport->dma_tx_desc);
sizeof(struct dmasg)) != sport->dummy_tx_desc)
continue;
sport->dummy_tx_desc->next_desc_addr = sport->dma_tx_desc;
local_irq_restore(flags);
sport->curr_tx_desc = sport->dma_tx_desc;
} else {
@ -536,19 +524,17 @@ static int sport_config_rx_dummy(struct sport_device *sport)
unsigned config;
pr_debug("%s entered\n", __func__);
#if L1_DATA_A_LENGTH != 0
desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
#else
{
if (L1_DATA_A_LENGTH)
desc = l1_data_sram_zalloc(2 * sizeof(*desc));
else {
dma_addr_t addr;
desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
memset(desc, 0, 2 * sizeof(*desc));
}
#endif
if (desc == NULL) {
pr_err("Failed to allocate memory for dummy rx desc\n");
return -ENOMEM;
}
memset(desc, 0, 2 * sizeof(*desc));
sport->dummy_rx_desc = desc;
desc->start_addr = (unsigned long)sport->dummy_buf;
config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize)
@ -559,8 +545,8 @@ static int sport_config_rx_dummy(struct sport_device *sport)
desc->y_count = 0;
desc->y_modify = 0;
memcpy(desc+1, desc, sizeof(*desc));
desc->next_desc_addr = (unsigned long)(desc+1);
desc[1].next_desc_addr = (unsigned long)desc;
desc->next_desc_addr = desc + 1;
desc[1].next_desc_addr = desc;
return 0;
}
@ -571,19 +557,17 @@ static int sport_config_tx_dummy(struct sport_device *sport)
pr_debug("%s entered\n", __func__);
#if L1_DATA_A_LENGTH != 0
desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
#else
{
if (L1_DATA_A_LENGTH)
desc = l1_data_sram_zalloc(2 * sizeof(*desc));
else {
dma_addr_t addr;
desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
memset(desc, 0, 2 * sizeof(*desc));
}
#endif
if (!desc) {
pr_err("Failed to allocate memory for dummy tx desc\n");
return -ENOMEM;
}
memset(desc, 0, 2 * sizeof(*desc));
sport->dummy_tx_desc = desc;
desc->start_addr = (unsigned long)sport->dummy_buf + \
sport->dummy_count;
@ -595,8 +579,8 @@ static int sport_config_tx_dummy(struct sport_device *sport)
desc->y_count = 0;
desc->y_modify = 0;
memcpy(desc+1, desc, sizeof(*desc));
desc->next_desc_addr = (unsigned long)(desc+1);
desc[1].next_desc_addr = (unsigned long)desc;
desc->next_desc_addr = desc + 1;
desc[1].next_desc_addr = desc;
return 0;
}
@ -872,17 +856,15 @@ struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
sport->wdsize = wdsize;
sport->dummy_count = dummy_count;
#if L1_DATA_A_LENGTH != 0
sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2);
#else
sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL);
#endif
if (L1_DATA_A_LENGTH)
sport->dummy_buf = l1_data_sram_zalloc(dummy_count * 2);
else
sport->dummy_buf = kzalloc(dummy_count * 2, GFP_KERNEL);
if (sport->dummy_buf == NULL) {
pr_err("Failed to allocate dummy buffer\n");
goto __error;
}
memset(sport->dummy_buf, 0, dummy_count * 2);
ret = sport_config_rx_dummy(sport);
if (ret) {
pr_err("Failed to config rx dummy ring\n");
@ -939,6 +921,7 @@ void sport_done(struct sport_device *sport)
sport = NULL;
}
EXPORT_SYMBOL(sport_done);
/*
* It is only used to send several bytes when dma is not enabled
* sport controller is configured but not enabled.
@ -1029,4 +1012,3 @@ EXPORT_SYMBOL(sport_send_and_recv);
MODULE_AUTHOR("Roy Huang");
MODULE_DESCRIPTION("SPORT driver for ADI Blackfin");
MODULE_LICENSE("GPL");

View File

@ -10,9 +10,11 @@ config SND_SOC_I2C_AND_SPI
config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
select SND_SOC_L3
select SND_SOC_AC97_CODEC if SND_SOC_AC97_BUS
select SND_SOC_AD1980 if SND_SOC_AC97_BUS
select SND_SOC_AD73311 if I2C
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
select SND_SOC_CS4270 if I2C
select SND_SOC_PCM3008
@ -24,6 +26,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_UDA134X
select SND_SOC_UDA1380 if I2C
select SND_SOC_WM8350 if MFD_WM8350
select SND_SOC_WM8400 if MFD_WM8400
select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI
select SND_SOC_WM8580 if I2C
select SND_SOC_WM8728 if SND_SOC_I2C_AND_SPI
@ -34,6 +37,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_WM8903 if I2C
select SND_SOC_WM8971 if I2C
select SND_SOC_WM8990 if I2C
select SND_SOC_WM9705 if SND_SOC_AC97_BUS
select SND_SOC_WM9712 if SND_SOC_AC97_BUS
select SND_SOC_WM9713 if SND_SOC_AC97_BUS
help
@ -58,6 +62,9 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
config SND_SOC_AK4104
tristate
config SND_SOC_AK4535
tristate
@ -65,12 +72,6 @@ config SND_SOC_AK4535
config SND_SOC_CS4270
tristate
# Cirrus Logic CS4270 Codec Hardware Mute Support
# Select if you have external muting circuitry attached to your CS4270.
config SND_SOC_CS4270_HWMUTE
bool
depends on SND_SOC_CS4270
# Cirrus Logic CS4270 Codec VD = 3.3V Errata
# Select if you are affected by the errata where the part will not function
# if MCLK divide-by-1.5 is selected and VD is set to 3.3V. The driver will
@ -90,7 +91,6 @@ config SND_SOC_SSM2602
config SND_SOC_TLV320AIC23
tristate
depends on I2C
config SND_SOC_TLV320AIC26
tristate "TI TLV320AIC26 Codec support" if SND_SOC_OF_SIMPLE
@ -98,15 +98,12 @@ config SND_SOC_TLV320AIC26
config SND_SOC_TLV320AIC3X
tristate
depends on I2C
config SND_SOC_TWL4030
tristate
depends on TWL4030_CORE
config SND_SOC_UDA134X
tristate
select SND_SOC_L3
config SND_SOC_UDA1380
tristate
@ -114,6 +111,9 @@ config SND_SOC_UDA1380
config SND_SOC_WM8350
tristate
config SND_SOC_WM8400
tristate
config SND_SOC_WM8510
tristate
@ -144,6 +144,9 @@ config SND_SOC_WM8971
config SND_SOC_WM8990
tristate
config SND_SOC_WM9705
tristate
config SND_SOC_WM9712
tristate

View File

@ -1,6 +1,7 @@
snd-soc-ac97-objs := ac97.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
snd-soc-ak4104-objs := ak4104.o
snd-soc-ak4535-objs := ak4535.o
snd-soc-cs4270-objs := cs4270.o
snd-soc-l3-objs := l3.o
@ -13,6 +14,7 @@ snd-soc-twl4030-objs := twl4030.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wm8350-objs := wm8350.o
snd-soc-wm8400-objs := wm8400.o
snd-soc-wm8510-objs := wm8510.o
snd-soc-wm8580-objs := wm8580.o
snd-soc-wm8728-objs := wm8728.o
@ -23,12 +25,14 @@ snd-soc-wm8900-objs := wm8900.o
snd-soc-wm8903-objs := wm8903.o
snd-soc-wm8971-objs := wm8971.o
snd-soc-wm8990-objs := wm8990.o
snd-soc-wm9705-objs := wm9705.o
snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
obj-$(CONFIG_SND_SOC_AC97_CODEC) += snd-soc-ac97.o
obj-$(CONFIG_SND_SOC_AD1980) += snd-soc-ad1980.o
obj-$(CONFIG_SND_SOC_AD73311) += snd-soc-ad73311.o
obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o
obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o
obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o
obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
@ -41,6 +45,7 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o
obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o
obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o
obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o
obj-$(CONFIG_SND_SOC_WM8728) += snd-soc-wm8728.o
@ -51,5 +56,7 @@ obj-$(CONFIG_SND_SOC_WM8900) += snd-soc-wm8900.o
obj-$(CONFIG_SND_SOC_WM8903) += snd-soc-wm8903.o
obj-$(CONFIG_SND_SOC_WM8971) += snd-soc-wm8971.o
obj-$(CONFIG_SND_SOC_WM8990) += snd-soc-wm8990.o
obj-$(CONFIG_SND_SOC_WM8991) += snd-soc-wm8991.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o

View File

@ -30,7 +30,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
@ -41,6 +41,10 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
static struct snd_soc_dai_ops ac97_dai_ops = {
.prepare = ac97_prepare,
};
struct snd_soc_dai ac97_dai = {
.name = "AC97 HiFi",
.ac97_control = 1,
@ -56,8 +60,7 @@ struct snd_soc_dai ac97_dai = {
.channels_max = 2,
.rates = STD_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.prepare = ac97_prepare,},
.ops = &ac97_dai_ops,
};
EXPORT_SYMBOL_GPL(ac97_dai);
@ -84,10 +87,10 @@ static int ac97_soc_probe(struct platform_device *pdev)
printk(KERN_INFO "AC97 SoC Audio Codec %s\n", AC97_VERSION);
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (!socdev->codec)
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (!socdev->card->codec)
return -ENOMEM;
codec = socdev->codec;
codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->name = "AC97";
@ -123,23 +126,21 @@ bus_err:
snd_soc_free_pcms(socdev);
err:
kfree(socdev->codec->reg_cache);
kfree(socdev->codec);
socdev->codec = NULL;
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
static int ac97_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (!codec)
return 0;
snd_soc_free_pcms(socdev);
kfree(socdev->codec->reg_cache);
kfree(socdev->codec);
kfree(socdev->card->codec);
return 0;
}
@ -149,7 +150,7 @@ static int ac97_soc_suspend(struct platform_device *pdev, pm_message_t msg)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_ac97_suspend(socdev->codec->ac97);
snd_ac97_suspend(socdev->card->codec->ac97);
return 0;
}
@ -158,7 +159,7 @@ static int ac97_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_ac97_resume(socdev->codec->ac97);
snd_ac97_resume(socdev->card->codec->ac97);
return 0;
}

View File

@ -93,20 +93,6 @@ SOC_ENUM("Capture Source", ad1980_cap_src),
SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
};
/* add non dapm controls */
static int ad1980_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(ad1980_snd_ac97_controls); i++) {
err = snd_ctl_add(codec->card, snd_soc_cnew(
&ad1980_snd_ac97_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@ -123,7 +109,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
default:
reg = reg >> 1;
if (reg >= (ARRAY_SIZE(ad1980_reg)))
if (reg >= ARRAY_SIZE(ad1980_reg))
return -EINVAL;
return cache[reg];
@ -137,7 +123,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(ad1980_reg)))
if (reg < ARRAY_SIZE(ad1980_reg))
cache[reg] = val;
return 0;
@ -200,10 +186,10 @@ static int ad1980_soc_probe(struct platform_device *pdev)
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (socdev->codec == NULL)
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (socdev->card->codec == NULL)
return -ENOMEM;
codec = socdev->codec;
codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache =
@ -269,7 +255,8 @@ static int ad1980_soc_probe(struct platform_device *pdev)
ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
ad1980_add_controls(codec);
snd_soc_add_controls(codec, ad1980_snd_ac97_controls,
ARRAY_SIZE(ad1980_snd_ac97_controls));
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register card\n");
@ -288,15 +275,15 @@ codec_err:
kfree(codec->reg_cache);
cache_err:
kfree(socdev->codec);
socdev->codec = NULL;
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
static int ad1980_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;

View File

@ -53,7 +53,7 @@ static int ad73311_soc_probe(struct platform_device *pdev)
codec->owner = THIS_MODULE;
codec->dai = &ad73311_dai;
codec->num_dai = 1;
socdev->codec = codec;
socdev->card->codec = codec;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -75,15 +75,15 @@ static int ad73311_soc_probe(struct platform_device *pdev)
register_err:
snd_soc_free_pcms(socdev);
pcm_err:
kfree(socdev->codec);
socdev->codec = NULL;
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
static int ad73311_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;

View File

@ -70,7 +70,7 @@
#define REGD_IGS(x) (x & 0x7)
#define REGD_RMOD (1 << 3)
#define REGD_OGS(x) ((x & 0x7) << 4)
#define REGD_MUTE (x << 7)
#define REGD_MUTE (1 << 7)
/* Control register E */
#define CTRL_REG_E (4 << 8)

365
sound/soc/codecs/ak4104.c Normal file
View File

@ -0,0 +1,365 @@
/*
* AK4104 ALSA SoC (ASoC) driver
*
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*
* 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.
*/
#include <linux/module.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/spi/spi.h>
#include <sound/asoundef.h>
#include "ak4104.h"
/* AK4104 registers addresses */
#define AK4104_REG_CONTROL1 0x00
#define AK4104_REG_RESERVED 0x01
#define AK4104_REG_CONTROL2 0x02
#define AK4104_REG_TX 0x03
#define AK4104_REG_CHN_STATUS(x) ((x) + 0x04)
#define AK4104_NUM_REGS 10
#define AK4104_REG_MASK 0x1f
#define AK4104_READ 0xc0
#define AK4104_WRITE 0xe0
#define AK4104_RESERVED_VAL 0x5b
/* Bit masks for AK4104 registers */
#define AK4104_CONTROL1_RSTN (1 << 0)
#define AK4104_CONTROL1_PW (1 << 1)
#define AK4104_CONTROL1_DIF0 (1 << 2)
#define AK4104_CONTROL1_DIF1 (1 << 3)
#define AK4104_CONTROL2_SEL0 (1 << 0)
#define AK4104_CONTROL2_SEL1 (1 << 1)
#define AK4104_CONTROL2_MODE (1 << 2)
#define AK4104_TX_TXE (1 << 0)
#define AK4104_TX_V (1 << 1)
#define DRV_NAME "ak4104"
struct ak4104_private {
struct snd_soc_codec codec;
u8 reg_cache[AK4104_NUM_REGS];
};
static int ak4104_fill_cache(struct snd_soc_codec *codec)
{
int i;
u8 *reg_cache = codec->reg_cache;
struct spi_device *spi = codec->control_data;
for (i = 0; i < codec->reg_cache_size; i++) {
int ret = spi_w8r8(spi, i | AK4104_READ);
if (ret < 0) {
dev_err(&spi->dev, "SPI write failure\n");
return ret;
}
reg_cache[i] = ret;
}
return 0;
}
static unsigned int ak4104_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u8 *reg_cache = codec->reg_cache;
if (reg >= codec->reg_cache_size)
return -EINVAL;
return reg_cache[reg];
}
static int ak4104_spi_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int value)
{
u8 *cache = codec->reg_cache;
struct spi_device *spi = codec->control_data;
if (reg >= codec->reg_cache_size)
return -EINVAL;
reg &= AK4104_REG_MASK;
reg |= AK4104_WRITE;
/* only write to the hardware if value has changed */
if (cache[reg] != value) {
u8 tmp[2] = { reg, value };
if (spi_write(spi, tmp, sizeof(tmp))) {
dev_err(&spi->dev, "SPI write failed\n");
return -EIO;
}
cache[reg] = value;
}
return 0;
}
static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int format)
{
struct snd_soc_codec *codec = codec_dai->codec;
int val = 0;
val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
if (val < 0)
return val;
val &= ~(AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1);
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
break;
case SND_SOC_DAIFMT_LEFT_J:
val |= AK4104_CONTROL1_DIF0;
break;
case SND_SOC_DAIFMT_I2S:
val |= AK4104_CONTROL1_DIF0 | AK4104_CONTROL1_DIF1;
break;
default:
dev_err(codec->dev, "invalid dai format\n");
return -EINVAL;
}
/* This device can only be slave */
if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
return ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
}
static int ak4104_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
int val = 0;
/* set the IEC958 bits: consumer mode, no copyright bit */
val |= IEC958_AES0_CON_NOT_COPYRIGHT;
ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(0), val);
val = 0;
switch (params_rate(params)) {
case 44100:
val |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
val |= IEC958_AES3_CON_FS_48000;
break;
case 32000:
val |= IEC958_AES3_CON_FS_32000;
break;
default:
dev_err(codec->dev, "unsupported sampling rate\n");
return -EINVAL;
}
return ak4104_spi_write(codec, AK4104_REG_CHN_STATUS(3), val);
}
static struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
.set_fmt = ak4104_set_dai_fmt,
};
struct snd_soc_dai ak4104_dai = {
.name = DRV_NAME,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_44100 |
SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_32000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S24_LE
},
.ops = &ak4101_dai_ops,
};
static struct snd_soc_codec *ak4104_codec;
static int ak4104_spi_probe(struct spi_device *spi)
{
struct snd_soc_codec *codec;
struct ak4104_private *ak4104;
int ret, val;
spi->bits_per_word = 8;
spi->mode = SPI_MODE_0;
ret = spi_setup(spi);
if (ret < 0)
return ret;
ak4104 = kzalloc(sizeof(struct ak4104_private), GFP_KERNEL);
if (!ak4104) {
dev_err(&spi->dev, "could not allocate codec\n");
return -ENOMEM;
}
codec = &ak4104->codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->dev = &spi->dev;
codec->name = DRV_NAME;
codec->owner = THIS_MODULE;
codec->dai = &ak4104_dai;
codec->num_dai = 1;
codec->private_data = ak4104;
codec->control_data = spi;
codec->reg_cache = ak4104->reg_cache;
codec->reg_cache_size = AK4104_NUM_REGS;
/* read all regs and fill the cache */
ret = ak4104_fill_cache(codec);
if (ret < 0) {
dev_err(&spi->dev, "failed to fill register cache\n");
return ret;
}
/* read the 'reserved' register - according to the datasheet, it
* should contain 0x5b. Not a good way to verify the presence of
* the device, but there is no hardware ID register. */
if (ak4104_read_reg_cache(codec, AK4104_REG_RESERVED) !=
AK4104_RESERVED_VAL) {
ret = -ENODEV;
goto error_free_codec;
}
/* set power-up and non-reset bits */
val = ak4104_read_reg_cache(codec, AK4104_REG_CONTROL1);
val |= AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN;
ret = ak4104_spi_write(codec, AK4104_REG_CONTROL1, val);
if (ret < 0)
goto error_free_codec;
/* enable transmitter */
val = ak4104_read_reg_cache(codec, AK4104_REG_TX);
val |= AK4104_TX_TXE;
ret = ak4104_spi_write(codec, AK4104_REG_TX, val);
if (ret < 0)
goto error_free_codec;
ak4104_codec = codec;
ret = snd_soc_register_dai(&ak4104_dai);
if (ret < 0) {
dev_err(&spi->dev, "failed to register DAI\n");
goto error_free_codec;
}
spi_set_drvdata(spi, ak4104);
dev_info(&spi->dev, "SPI device initialized\n");
return 0;
error_free_codec:
kfree(ak4104);
ak4104_dai.dev = NULL;
return ret;
}
static int __devexit ak4104_spi_remove(struct spi_device *spi)
{
int ret, val;
struct ak4104_private *ak4104 = spi_get_drvdata(spi);
val = ak4104_read_reg_cache(&ak4104->codec, AK4104_REG_CONTROL1);
if (val < 0)
return val;
/* clear power-up and non-reset bits */
val &= ~(AK4104_CONTROL1_PW | AK4104_CONTROL1_RSTN);
ret = ak4104_spi_write(&ak4104->codec, AK4104_REG_CONTROL1, val);
if (ret < 0)
return ret;
ak4104_codec = NULL;
kfree(ak4104);
return 0;
}
static int ak4104_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = ak4104_codec;
int ret;
/* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
socdev->card->codec = codec;
/* Register PCMs */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(codec->dev, "failed to create pcms\n");
return ret;
}
/* Register the socdev */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
dev_err(codec->dev, "failed to register card\n");
snd_soc_free_pcms(socdev);
return ret;
}
return 0;
}
static int ak4104_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
return 0;
};
struct snd_soc_codec_device soc_codec_device_ak4104 = {
.probe = ak4104_probe,
.remove = ak4104_remove
};
EXPORT_SYMBOL_GPL(soc_codec_device_ak4104);
static struct spi_driver ak4104_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = ak4104_spi_probe,
.remove = __devexit_p(ak4104_spi_remove),
};
static int __init ak4104_init(void)
{
pr_info("Asahi Kasei AK4104 ALSA SoC Codec Driver\n");
return spi_register_driver(&ak4104_spi_driver);
}
module_init(ak4104_init);
static void __exit ak4104_exit(void)
{
spi_unregister_driver(&ak4104_spi_driver);
}
module_exit(ak4104_exit);
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
MODULE_DESCRIPTION("Asahi Kasei AK4104 ALSA SoC driver");
MODULE_LICENSE("GPL");

View File

@ -0,0 +1,7 @@
#ifndef _AK4104_H
#define _AK4104_H
extern struct snd_soc_dai ak4104_dai;
extern struct snd_soc_codec_device soc_codec_device_ak4104;
#endif

View File

@ -155,21 +155,6 @@ static const struct snd_kcontrol_new ak4535_snd_controls[] = {
SOC_SINGLE("Mic Sidetone Volume", AK4535_VOL, 4, 7, 0),
};
/* add non dapm controls */
static int ak4535_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(ak4535_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&ak4535_snd_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Mono 1 Mixer */
static const struct snd_kcontrol_new ak4535_mono1_mixer_controls[] = {
SOC_DAPM_SINGLE("Mic Sidetone Switch", AK4535_SIG1, 4, 1, 0),
@ -344,7 +329,7 @@ static int ak4535_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct ak4535_priv *ak4535 = codec->private_data;
u8 mode2 = ak4535_read_reg_cache(codec, AK4535_MODE2) & ~(0x3 << 5);
int rate = params_rate(params), fs = 256;
@ -436,6 +421,13 @@ static int ak4535_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
static struct snd_soc_dai_ops ak4535_dai_ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
.digital_mute = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
};
struct snd_soc_dai ak4535_dai = {
.name = "AK4535",
.playback = {
@ -450,19 +442,14 @@ struct snd_soc_dai ak4535_dai = {
.channels_max = 2,
.rates = AK4535_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
.digital_mute = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
},
.ops = &ak4535_dai_ops,
};
EXPORT_SYMBOL_GPL(ak4535_dai);
static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -471,7 +458,7 @@ static int ak4535_suspend(struct platform_device *pdev, pm_message_t state)
static int ak4535_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
ak4535_sync(codec);
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ak4535_set_bias_level(codec, codec->suspend_bias_level);
@ -484,7 +471,7 @@ static int ak4535_resume(struct platform_device *pdev)
*/
static int ak4535_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "AK4535";
@ -510,7 +497,8 @@ static int ak4535_init(struct snd_soc_device *socdev)
/* power on device */
ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
ak4535_add_controls(codec);
snd_soc_add_controls(codec, ak4535_snd_controls,
ARRAY_SIZE(ak4535_snd_controls));
ak4535_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -537,7 +525,7 @@ static int ak4535_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = ak4535_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -636,7 +624,7 @@ static int ak4535_probe(struct platform_device *pdev)
}
codec->private_data = ak4535;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -663,7 +651,7 @@ static int ak4535_probe(struct platform_device *pdev)
static int ak4535_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -3,27 +3,22 @@
*
* Author: Timur Tabi <timur@freescale.com>
*
* Copyright 2007 Freescale Semiconductor, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
* Copyright 2007-2009 Freescale Semiconductor, Inc. This file is licensed
* under the terms of the GNU General Public License version 2. This
* program is licensed "as is" without any warranty of any kind, whether
* express or implied.
*
* This is an ASoC device driver for the Cirrus Logic CS4270 codec.
*
* Current features/limitations:
*
* 1) Software mode is supported. Stand-alone mode is automatically
* selected if I2C is disabled or if a CS4270 is not found on the I2C
* bus. However, stand-alone mode is only partially implemented because
* there is no mechanism yet for this driver and the machine driver to
* communicate the values of the M0, M1, MCLK1, and MCLK2 pins.
* 2) Only I2C is supported, not SPI
* 3) Only Master mode is supported, not Slave.
* 4) The machine driver's 'startup' function must call
* cs4270_set_dai_sysclk() with the value of MCLK.
* 5) Only I2S and left-justified modes are supported
* 6) Power management is not supported
* 7) The only supported control is volume and hardware mute (if enabled)
* - Software mode is supported. Stand-alone mode is not supported.
* - Only I2C is supported, not SPI
* - Support for master and slave mode
* - The machine driver's 'startup' function must call
* cs4270_set_dai_sysclk() with the value of MCLK.
* - Only I2S and left-justified modes are supported
* - Power management is not supported
*/
#include <linux/module.h>
@ -35,18 +30,6 @@
#include "cs4270.h"
/* If I2C is defined, then we support software mode. However, if we're
not compiled as module but I2C is, then we can't use I2C calls. */
#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE))
#define USE_I2C
#endif
/* Private data for the CS4270 */
struct cs4270_private {
unsigned int mclk; /* Input frequency of the MCLK pin */
unsigned int mode; /* The mode (I2S or left-justified) */
};
/*
* The codec isn't really big-endian or little-endian, since the I2S
* interface requires data to be sent serially with the MSbit first.
@ -60,8 +43,6 @@ struct cs4270_private {
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
#ifdef USE_I2C
/* CS4270 registers addresses */
#define CS4270_CHIPID 0x01 /* Chip ID */
#define CS4270_PWRCTL 0x02 /* Power Control */
@ -121,8 +102,22 @@ struct cs4270_private {
#define CS4270_MUTE_DAC_A 0x01
#define CS4270_MUTE_DAC_B 0x02
/*
* Clock Ratio Selection for Master Mode with I2C enabled
/* Private data for the CS4270 */
struct cs4270_private {
struct snd_soc_codec codec;
u8 reg_cache[CS4270_NUMREGS];
unsigned int mclk; /* Input frequency of the MCLK pin */
unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode;
};
/**
* struct cs4270_mode_ratios - clock ratio tables
* @ratio: the ratio of MCLK to the sample rate
* @speed_mode: the Speed Mode bits to set in the Mode Control register for
* this ratio
* @mclk: the Ratio Select bits to set in the Mode Control register for this
* ratio
*
* The data for this chart is taken from Table 5 of the CS4270 reference
* manual.
@ -131,31 +126,30 @@ struct cs4270_private {
* It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling
* rates the CS4270 currently supports.
*
* Each element in this array corresponds to the ratios in mclk_ratios[].
* These two arrays need to be in sync.
*
* 'speed_mode' is the corresponding bit pattern to be written to the
* @speed_mode is the corresponding bit pattern to be written to the
* MODE bits of the Mode Control Register
*
* 'mclk' is the corresponding bit pattern to be wirten to the MCLK bits of
* @mclk is the corresponding bit pattern to be wirten to the MCLK bits of
* the Mode Control Register.
*
* In situations where a single ratio is represented by multiple speed
* modes, we favor the slowest speed. E.g, for a ratio of 128, we pick
* double-speed instead of quad-speed. However, the CS4270 errata states
* that Divide-By-1.5 can cause failures, so we avoid that mode where
* that divide-By-1.5 can cause failures, so we avoid that mode where
* possible.
*
* ERRATA: There is an errata for the CS4270 where divide-by-1.5 does not
* work if VD = 3.3V. If this effects you, select the
* Errata: There is an errata for the CS4270 where divide-by-1.5 does not
* work if Vd is 3.3V. If this effects you, select the
* CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will
* never select any sample rates that require divide-by-1.5.
*/
static struct {
struct cs4270_mode_ratios {
unsigned int ratio;
u8 speed_mode;
u8 mclk;
} cs4270_mode_ratios[] = {
};
static struct cs4270_mode_ratios cs4270_mode_ratios[] = {
{64, CS4270_MODE_4X, CS4270_MODE_DIV1},
#ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA
{96, CS4270_MODE_4X, CS4270_MODE_DIV15},
@ -172,34 +166,27 @@ static struct {
/* The number of MCLK/LRCK ratios supported by the CS4270 */
#define NUM_MCLK_RATIOS ARRAY_SIZE(cs4270_mode_ratios)
/*
* Determine the CS4270 samples rates.
/**
* cs4270_set_dai_sysclk - determine the CS4270 samples rates.
* @codec_dai: the codec DAI
* @clk_id: the clock ID (ignored)
* @freq: the MCLK input frequency
* @dir: the clock direction (ignored)
*
* 'freq' is the input frequency to MCLK. The other parameters are ignored.
* This function is used to tell the codec driver what the input MCLK
* frequency is.
*
* The value of MCLK is used to determine which sample rates are supported
* by the CS4270. The ratio of MCLK / Fs must be equal to one of nine
* support values: 64, 96, 128, 192, 256, 384, 512, 768, and 1024.
* supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024.
*
* This function calculates the nine ratios and determines which ones match
* a standard sample rate. If there's a match, then it is added to the list
* of support sample rates.
* of supported sample rates.
*
* This function must be called by the machine driver's 'startup' function,
* otherwise the list of supported sample rates will not be available in
* time for ALSA.
*
* Note that in stand-alone mode, the sample rate is determined by input
* pins M0, M1, MDIV1, and MDIV2. Also in stand-alone mode, divide-by-3
* is not a programmable option. However, divide-by-3 is not an available
* option in stand-alone mode. This cases two problems: a ratio of 768 is
* not available (it requires divide-by-3) and B) ratios 192 and 384 can
* only be selected with divide-by-1.5, but there is an errate that make
* this selection difficult.
*
* In addition, there is no mechanism for communicating with the machine
* driver what the input settings can be. This would need to be implemented
* for stand-alone mode to work.
*/
static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
@ -225,7 +212,7 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
rates &= ~SNDRV_PCM_RATE_KNOT;
if (!rates) {
printk(KERN_ERR "cs4270: could not find a valid sample rate\n");
dev_err(codec->dev, "could not find a valid sample rate\n");
return -EINVAL;
}
@ -240,8 +227,10 @@ static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
/*
* Configure the codec for the selected audio format
/**
* cs4270_set_dai_fmt - configure the codec for the selected audio format
* @codec_dai: the codec DAI
* @format: a SND_SOC_DAIFMT_x value indicating the data format
*
* This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the
* codec accordingly.
@ -258,32 +247,43 @@ static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai,
struct cs4270_private *cs4270 = codec->private_data;
int ret = 0;
/* set DAI format */
switch (format & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
case SND_SOC_DAIFMT_LEFT_J:
cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK;
break;
default:
printk(KERN_ERR "cs4270: invalid DAI format\n");
dev_err(codec->dev, "invalid dai format\n");
ret = -EINVAL;
}
/* set master/slave audio interface */
switch (format & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBS_CFS:
cs4270->slave_mode = 1;
break;
case SND_SOC_DAIFMT_CBM_CFM:
cs4270->slave_mode = 0;
break;
default:
/* all other modes are unsupported by the hardware */
ret = -EINVAL;
}
return ret;
}
/*
* A list of addresses on which this CS4270 could use. I2C addresses are
* 7 bits. For the CS4270, the upper four bits are always 1001, and the
* lower three bits are determined via the AD2, AD1, and AD0 pins
* (respectively).
*/
static const unsigned short normal_i2c[] = {
0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, I2C_CLIENT_END
};
I2C_CLIENT_INSMOD;
/*
* Pre-fill the CS4270 register cache.
/**
* cs4270_fill_cache - pre-fill the CS4270 register cache.
* @codec: the codec for this CS4270
*
* This function fills in the CS4270 register cache by reading the register
* values from the hardware.
*
* This CS4270 registers are cached to avoid excessive I2C I/O operations.
* After the initial read to pre-fill the cache, the CS4270 never updates
* the register values, so we won't have a cache coherency problem.
*
* We use the auto-increment feature of the CS4270 to read all registers in
* one shot.
@ -298,7 +298,7 @@ static int cs4270_fill_cache(struct snd_soc_codec *codec)
CS4270_FIRSTREG | 0x80, CS4270_NUMREGS, cache);
if (length != CS4270_NUMREGS) {
printk(KERN_ERR "cs4270: I2C read failure, addr=0x%x\n",
dev_err(codec->dev, "i2c read failure, addr=0x%x\n",
i2c_client->addr);
return -EIO;
}
@ -306,12 +306,17 @@ static int cs4270_fill_cache(struct snd_soc_codec *codec)
return 0;
}
/*
* Read from the CS4270 register cache.
/**
* cs4270_read_reg_cache - read from the CS4270 register cache.
* @codec: the codec for this CS4270
* @reg: the register to read
*
* This function returns the value for a given register. It reads only from
* the register cache, not the hardware itself.
*
* This CS4270 registers are cached to avoid excessive I2C I/O operations.
* After the initial read to pre-fill the cache, the CS4270 never updates
* the register values, so we won't have a cache coherncy problem.
* the register values, so we won't have a cache coherency problem.
*/
static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
@ -324,8 +329,11 @@ static unsigned int cs4270_read_reg_cache(struct snd_soc_codec *codec,
return cache[reg - CS4270_FIRSTREG];
}
/*
* Write to a CS4270 register via the I2C bus.
/**
* cs4270_i2c_write - write to a CS4270 register via the I2C bus.
* @codec: the codec for this CS4270
* @reg: the register to write
* @value: the value to write to the register
*
* This function writes the given value to the given CS4270 register, and
* also updates the register cache.
@ -346,7 +354,7 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
if (cache[reg - CS4270_FIRSTREG] != value) {
struct i2c_client *client = codec->control_data;
if (i2c_smbus_write_byte_data(client, reg, value)) {
printk(KERN_ERR "cs4270: I2C write failed\n");
dev_err(codec->dev, "i2c write failed\n");
return -EIO;
}
@ -357,11 +365,17 @@ static int cs4270_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
return 0;
}
/*
* Program the CS4270 with the given hardware parameters.
/**
* cs4270_hw_params - program the CS4270 with the given hardware parameters.
* @substream: the audio stream
* @params: the hardware parameters to set
* @dai: the SOC DAI (ignored)
*
* The .ops functions are used to provide board-specific data, like
* input frequencies, to this driver. This function takes that information,
* This function programs the hardware with the values provided.
* Specifically, the sample rate and the data format.
*
* The .ops functions are used to provide board-specific data, like input
* frequencies, to this driver. This function takes that information,
* combines it with the hardware parameters provided, and programs the
* hardware accordingly.
*/
@ -371,7 +385,7 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct cs4270_private *cs4270 = codec->private_data;
int ret;
unsigned int i;
@ -391,33 +405,28 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
if (i == NUM_MCLK_RATIOS) {
/* We did not find a matching ratio */
printk(KERN_ERR "cs4270: could not find matching ratio\n");
dev_err(codec->dev, "could not find matching ratio\n");
return -EINVAL;
}
/* Freeze and power-down the codec */
ret = snd_soc_write(codec, CS4270_PWRCTL, CS4270_PWRCTL_FREEZE |
CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC |
CS4270_PWRCTL_PDN);
if (ret < 0) {
printk(KERN_ERR "cs4270: I2C write failed\n");
return ret;
}
/* Program the mode control register */
/* Set the sample rate */
reg = snd_soc_read(codec, CS4270_MODE);
reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK);
reg |= cs4270_mode_ratios[i].speed_mode | cs4270_mode_ratios[i].mclk;
reg |= cs4270_mode_ratios[i].mclk;
if (cs4270->slave_mode)
reg |= CS4270_MODE_SLAVE;
else
reg |= cs4270_mode_ratios[i].speed_mode;
ret = snd_soc_write(codec, CS4270_MODE, reg);
if (ret < 0) {
printk(KERN_ERR "cs4270: I2C write failed\n");
dev_err(codec->dev, "i2c write failed\n");
return ret;
}
/* Program the format register */
/* Set the DAI format */
reg = snd_soc_read(codec, CS4270_FORMAT);
reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK);
@ -430,55 +439,23 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ;
break;
default:
printk(KERN_ERR "cs4270: unknown format\n");
dev_err(codec->dev, "unknown dai format\n");
return -EINVAL;
}
ret = snd_soc_write(codec, CS4270_FORMAT, reg);
if (ret < 0) {
printk(KERN_ERR "cs4270: I2C write failed\n");
return ret;
}
/* Disable auto-mute. This feature appears to be buggy, because in
some situations, auto-mute will not deactivate when it should. */
reg = snd_soc_read(codec, CS4270_MUTE);
reg &= ~CS4270_MUTE_AUTO;
ret = snd_soc_write(codec, CS4270_MUTE, reg);
if (ret < 0) {
printk(KERN_ERR "cs4270: I2C write failed\n");
return ret;
}
/* Disable automatic volume control. It's enabled by default, and
* it causes volume change commands to be delayed, sometimes until
* after playback has started.
*/
reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
if (ret < 0) {
printk(KERN_ERR "I2C write failed\n");
return ret;
}
/* Thaw and power-up the codec */
ret = snd_soc_write(codec, CS4270_PWRCTL, 0);
if (ret < 0) {
printk(KERN_ERR "cs4270: I2C write failed\n");
dev_err(codec->dev, "i2c write failed\n");
return ret;
}
return ret;
}
#ifdef CONFIG_SND_SOC_CS4270_HWMUTE
/*
* Set the CS4270 external mute
/**
* cs4270_mute - enable/disable the CS4270 external mute
* @dai: the SOC DAI
* @mute: 0 = disable mute, 1 = enable mute
*
* This function toggles the mute bits in the MUTE register. The CS4270's
* mute capability is intended for external muting circuitry, so if the
@ -493,150 +470,47 @@ static int cs4270_mute(struct snd_soc_dai *dai, int mute)
reg6 = snd_soc_read(codec, CS4270_MUTE);
if (mute)
reg6 |= CS4270_MUTE_ADC_A | CS4270_MUTE_ADC_B |
CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B;
else
reg6 &= ~(CS4270_MUTE_ADC_A | CS4270_MUTE_ADC_B |
CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B);
return snd_soc_write(codec, CS4270_MUTE, reg6);
}
#endif
static int cs4270_i2c_probe(struct i2c_client *, const struct i2c_device_id *);
/* A list of non-DAPM controls that the CS4270 supports */
static const struct snd_kcontrol_new cs4270_snd_controls[] = {
SOC_DOUBLE_R("Master Playback Volume",
CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1)
};
static const struct i2c_device_id cs4270_id[] = {
{"cs4270", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
.name = "CS4270 I2C",
.owner = THIS_MODULE,
},
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1),
SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0),
SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0),
SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0),
SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1),
SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0),
SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 0)
};
/*
* Global variable to store socdev for i2c probe function.
* cs4270_codec - global variable to store codec for the ASoC probe function
*
* If struct i2c_driver had a private_data field, we wouldn't need to use
* cs4270_socdec. This is the only way to pass the socdev structure to
* cs4270_i2c_probe().
*
* The real solution to cs4270_socdev is to create a mechanism
* that maps I2C addresses to snd_soc_device structures. Perhaps the
* creation of the snd_soc_device object should be moved out of
* cs4270_probe() and into cs4270_i2c_probe(), but that would make this
* driver dependent on I2C. The CS4270 supports "stand-alone" mode, whereby
* the chip is *not* connected to the I2C bus, but is instead configured via
* input pins.
* cs4270_codec. This is the only way to pass the codec structure from
* cs4270_i2c_probe() to cs4270_probe(). Unfortunately, there is no good
* way to synchronize these two functions. cs4270_i2c_probe() can be called
* multiple times before cs4270_probe() is called even once. So for now, we
* also only allow cs4270_i2c_probe() to be run once. That means that we do
* not support more than one cs4270 device in the system, at least for now.
*/
static struct snd_soc_device *cs4270_socdev;
static struct snd_soc_codec *cs4270_codec;
/*
* Initialize the I2C interface of the CS4270
*
* This function is called for whenever the I2C subsystem finds a device
* at a particular address.
*
* Note: snd_soc_new_pcms() must be called before this function can be called,
* because of snd_ctl_add().
*/
static int cs4270_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = cs4270_socdev;
struct snd_soc_codec *codec = socdev->codec;
int i;
int ret = 0;
/* Probing all possible addresses has one drawback: if there are
multiple CS4270s on the bus, then you cannot specify which
socdev is matched with which CS4270. For now, we just reject
this I2C device if the socdev already has one attached. */
if (codec->control_data)
return -ENODEV;
/* Note: codec_dai->codec is NULL here */
codec->reg_cache = kzalloc(CS4270_NUMREGS, GFP_KERNEL);
if (!codec->reg_cache) {
printk(KERN_ERR "cs4270: could not allocate register cache\n");
ret = -ENOMEM;
goto error;
}
/* Verify that we have a CS4270 */
ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to read I2C\n");
goto error;
}
/* The top four bits of the chip ID should be 1100. */
if ((ret & 0xF0) != 0xC0) {
/* The device at this address is not a CS4270 codec */
ret = -ENODEV;
goto error;
}
printk(KERN_INFO "cs4270: found device at I2C address %X\n",
i2c_client->addr);
printk(KERN_INFO "cs4270: hardware revision %X\n", ret & 0xF);
codec->control_data = i2c_client;
codec->read = cs4270_read_reg_cache;
codec->write = cs4270_i2c_write;
codec->reg_cache_size = CS4270_NUMREGS;
/* The I2C interface is set up, so pre-fill our register cache */
ret = cs4270_fill_cache(codec);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to fill register cache\n");
goto error;
}
/* Add the non-DAPM controls */
for (i = 0; i < ARRAY_SIZE(cs4270_snd_controls); i++) {
struct snd_kcontrol *kctrl =
snd_soc_cnew(&cs4270_snd_controls[i], codec, NULL);
ret = snd_ctl_add(codec->card, kctrl);
if (ret < 0)
goto error;
}
i2c_set_clientdata(i2c_client, codec);
return 0;
error:
codec->control_data = NULL;
kfree(codec->reg_cache);
codec->reg_cache = NULL;
codec->reg_cache_size = 0;
return ret;
}
#endif /* USE_I2C*/
static struct snd_soc_dai_ops cs4270_dai_ops = {
.hw_params = cs4270_hw_params,
.set_sysclk = cs4270_set_dai_sysclk,
.set_fmt = cs4270_set_dai_fmt,
.digital_mute = cs4270_mute,
};
struct snd_soc_dai cs4270_dai = {
.name = "CS4270",
.name = "cs4270",
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@ -651,117 +525,250 @@ struct snd_soc_dai cs4270_dai = {
.rates = 0,
.formats = CS4270_FORMATS,
},
.ops = &cs4270_dai_ops,
};
EXPORT_SYMBOL_GPL(cs4270_dai);
/*
* ASoC probe function
/**
* cs4270_probe - ASoC probe function
* @pdev: platform device
*
* This function is called when the machine driver calls
* platform_device_add().
* This function is called when ASoC has all the pieces it needs to
* instantiate a sound driver.
*/
static int cs4270_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
struct snd_soc_codec *codec = cs4270_codec;
int ret;
printk(KERN_INFO "CS4270 ALSA SoC Codec\n");
/* Allocate enough space for the snd_soc_codec structure
and our private data together. */
codec = kzalloc(ALIGN(sizeof(struct snd_soc_codec), 4) +
sizeof(struct cs4270_private), GFP_KERNEL);
if (!codec) {
printk(KERN_ERR "cs4270: Could not allocate codec structure\n");
return -ENOMEM;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "CS4270";
codec->owner = THIS_MODULE;
codec->dai = &cs4270_dai;
codec->num_dai = 1;
codec->private_data = (void *) codec +
ALIGN(sizeof(struct snd_soc_codec), 4);
socdev->codec = codec;
/* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
socdev->card->codec = codec;
/* Register PCMs */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to create PCMs\n");
goto error_free_codec;
dev_err(codec->dev, "failed to create pcms\n");
return ret;
}
#ifdef USE_I2C
cs4270_socdev = socdev;
ret = i2c_add_driver(&cs4270_i2c_driver);
if (ret) {
printk(KERN_ERR "cs4270: failed to attach driver");
/* Add the non-DAPM controls */
ret = snd_soc_add_controls(codec, cs4270_snd_controls,
ARRAY_SIZE(cs4270_snd_controls));
if (ret < 0) {
dev_err(codec->dev, "failed to add controls\n");
goto error_free_pcms;
}
/* Did we find a CS4270 on the I2C bus? */
if (codec->control_data) {
/* Initialize codec ops */
cs4270_dai.ops.hw_params = cs4270_hw_params;
cs4270_dai.ops.set_sysclk = cs4270_set_dai_sysclk;
cs4270_dai.ops.set_fmt = cs4270_set_dai_fmt;
#ifdef CONFIG_SND_SOC_CS4270_HWMUTE
cs4270_dai.ops.digital_mute = cs4270_mute;
#endif
} else
printk(KERN_INFO "cs4270: no I2C device found, "
"using stand-alone mode\n");
#else
printk(KERN_INFO "cs4270: I2C disabled, using stand-alone mode\n");
#endif
/* And finally, register the socdev */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "cs4270: failed to register card\n");
goto error_del_driver;
dev_err(codec->dev, "failed to register card\n");
goto error_free_pcms;
}
return 0;
error_del_driver:
#ifdef USE_I2C
i2c_del_driver(&cs4270_i2c_driver);
error_free_pcms:
#endif
snd_soc_free_pcms(socdev);
error_free_codec:
kfree(socdev->codec);
socdev->codec = NULL;
return ret;
}
/**
* cs4270_remove - ASoC remove function
* @pdev: platform device
*
* This function is the counterpart to cs4270_probe().
*/
static int cs4270_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
#ifdef USE_I2C
i2c_del_driver(&cs4270_i2c_driver);
#endif
return 0;
};
kfree(socdev->codec);
socdev->codec = NULL;
/**
* cs4270_i2c_probe - initialize the I2C interface of the CS4270
* @i2c_client: the I2C client object
* @id: the I2C device ID (ignored)
*
* This function is called whenever the I2C subsystem finds a device that
* matches the device ID given via a prior call to i2c_add_driver().
*/
static int cs4270_i2c_probe(struct i2c_client *i2c_client,
const struct i2c_device_id *id)
{
struct snd_soc_codec *codec;
struct cs4270_private *cs4270;
unsigned int reg;
int ret;
/* For now, we only support one cs4270 device in the system. See the
* comment for cs4270_codec.
*/
if (cs4270_codec) {
dev_err(&i2c_client->dev, "ignoring CS4270 at addr %X\n",
i2c_client->addr);
dev_err(&i2c_client->dev, "only one per board allowed\n");
/* Should we return something other than ENODEV here? */
return -ENODEV;
}
/* Verify that we have a CS4270 */
ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
i2c_client->addr);
return ret;
}
/* The top four bits of the chip ID should be 1100. */
if ((ret & 0xF0) != 0xC0) {
dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n",
i2c_client->addr);
return -ENODEV;
}
dev_info(&i2c_client->dev, "found device at i2c address %X\n",
i2c_client->addr);
dev_info(&i2c_client->dev, "hardware revision %X\n", ret & 0xF);
/* Allocate enough space for the snd_soc_codec structure
and our private data together. */
cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL);
if (!cs4270) {
dev_err(&i2c_client->dev, "could not allocate codec\n");
return -ENOMEM;
}
codec = &cs4270->codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->dev = &i2c_client->dev;
codec->name = "CS4270";
codec->owner = THIS_MODULE;
codec->dai = &cs4270_dai;
codec->num_dai = 1;
codec->private_data = cs4270;
codec->control_data = i2c_client;
codec->read = cs4270_read_reg_cache;
codec->write = cs4270_i2c_write;
codec->reg_cache = cs4270->reg_cache;
codec->reg_cache_size = CS4270_NUMREGS;
/* The I2C interface is set up, so pre-fill our register cache */
ret = cs4270_fill_cache(codec);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to fill register cache\n");
goto error_free_codec;
}
/* Disable auto-mute. This feature appears to be buggy. In some
* situations, auto-mute will not deactivate when it should, so we want
* this feature disabled by default. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
reg = cs4270_read_reg_cache(codec, CS4270_MUTE);
reg &= ~CS4270_MUTE_AUTO;
ret = cs4270_i2c_write(codec, CS4270_MUTE, reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "i2c write failed\n");
return ret;
}
/* Disable automatic volume control. The hardware enables, and it
* causes volume change commands to be delayed, sometimes until after
* playback has started. An application (e.g. alsactl) can
* re-enabled it by using the controls.
*/
reg = cs4270_read_reg_cache(codec, CS4270_TRANS);
reg &= ~(CS4270_TRANS_SOFT | CS4270_TRANS_ZERO);
ret = cs4270_i2c_write(codec, CS4270_TRANS, reg);
if (ret < 0) {
dev_err(&i2c_client->dev, "i2c write failed\n");
return ret;
}
/* Initialize the DAI. Normally, we'd prefer to have a kmalloc'd DAI
* structure for each CS4270 device, but the machine driver needs to
* have a pointer to the DAI structure, so for now it must be a global
* variable.
*/
cs4270_dai.dev = &i2c_client->dev;
/* Register the DAI. If all the other ASoC driver have already
* registered, then this will call our probe function, so
* cs4270_codec needs to be ready.
*/
cs4270_codec = codec;
ret = snd_soc_register_dai(&cs4270_dai);
if (ret < 0) {
dev_err(&i2c_client->dev, "failed to register DAIe\n");
goto error_free_codec;
}
i2c_set_clientdata(i2c_client, cs4270);
return 0;
error_free_codec:
kfree(cs4270);
cs4270_codec = NULL;
cs4270_dai.dev = NULL;
return ret;
}
/**
* cs4270_i2c_remove - remove an I2C device
* @i2c_client: the I2C client object
*
* This function is the counterpart to cs4270_i2c_probe().
*/
static int cs4270_i2c_remove(struct i2c_client *i2c_client)
{
struct cs4270_private *cs4270 = i2c_get_clientdata(i2c_client);
kfree(cs4270);
cs4270_codec = NULL;
cs4270_dai.dev = NULL;
return 0;
}
/*
* cs4270_id - I2C device IDs supported by this driver
*/
static struct i2c_device_id cs4270_id[] = {
{"cs4270", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
/*
* cs4270_i2c_driver - I2C device identification
*
* This structure tells the I2C subsystem how to identify and support a
* given I2C device type.
*/
static struct i2c_driver cs4270_i2c_driver = {
.driver = {
.name = "cs4270",
.owner = THIS_MODULE,
},
.id_table = cs4270_id,
.probe = cs4270_i2c_probe,
.remove = cs4270_i2c_remove,
};
/*
* ASoC codec device structure
*
@ -776,13 +783,15 @@ EXPORT_SYMBOL_GPL(soc_codec_device_cs4270);
static int __init cs4270_init(void)
{
return snd_soc_register_dai(&cs4270_dai);
pr_info("Cirrus Logic CS4270 ALSA SoC Codec Driver\n");
return i2c_add_driver(&cs4270_i2c_driver);
}
module_init(cs4270_init);
static void __exit cs4270_exit(void)
{
snd_soc_unregister_dai(&cs4270_dai);
i2c_del_driver(&cs4270_i2c_driver);
}
module_exit(cs4270_exit);

View File

@ -67,11 +67,11 @@ static int pcm3008_soc_probe(struct platform_device *pdev)
printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (!socdev->codec)
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (!socdev->card->codec)
return -ENOMEM;
codec = socdev->codec;
codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->name = "PCM3008";
@ -139,7 +139,7 @@ gpio_err:
card_err:
snd_soc_free_pcms(socdev);
pcm_err:
kfree(socdev->codec);
kfree(socdev->card->codec);
return ret;
}
@ -147,7 +147,7 @@ pcm_err:
static int pcm3008_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct pcm3008_setup_data *setup = socdev->codec_data;
if (!codec)
@ -155,7 +155,7 @@ static int pcm3008_soc_remove(struct platform_device *pdev)
pcm3008_gpio_free(setup);
snd_soc_free_pcms(socdev);
kfree(socdev->codec);
kfree(socdev->card->codec);
return 0;
}

View File

@ -151,21 +151,6 @@ SOC_ENUM("Capture Source", ssm2602_enum[0]),
SOC_ENUM("Playback De-emphasis", ssm2602_enum[1]),
};
/* add non dapm controls */
static int ssm2602_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(ssm2602_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&ssm2602_snd_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Output Mixer */
static const struct snd_kcontrol_new ssm2602_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", SSM2602_APANA, 3, 1, 0),
@ -291,7 +276,7 @@ static int ssm2602_hw_params(struct snd_pcm_substream *substream,
u16 srate;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
u16 iface = ssm2602_read_reg_cache(codec, SSM2602_IFACE) & 0xfff3;
@ -336,7 +321,7 @@ static int ssm2602_startup(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
@ -373,7 +358,7 @@ static int ssm2602_pcm_prepare(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
ssm2602_write(codec, SSM2602_ACTIVE, ACTIVE_ACTIVATE_CODEC);
@ -385,7 +370,7 @@ static void ssm2602_shutdown(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct ssm2602_priv *ssm2602 = codec->private_data;
/* deactivate */
if (!codec->active)
@ -521,6 +506,16 @@ static int ssm2602_set_bias_level(struct snd_soc_codec *codec,
#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
};
struct snd_soc_dai ssm2602_dai = {
.name = "SSM2602",
.playback = {
@ -535,22 +530,14 @@ struct snd_soc_dai ssm2602_dai = {
.channels_max = 2,
.rates = SSM2602_RATES,
.formats = SSM2602_FORMATS,},
.ops = {
.startup = ssm2602_startup,
.prepare = ssm2602_pcm_prepare,
.hw_params = ssm2602_hw_params,
.shutdown = ssm2602_shutdown,
.digital_mute = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
}
.ops = &ssm2602_dai_ops,
};
EXPORT_SYMBOL_GPL(ssm2602_dai);
static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -559,7 +546,7 @@ static int ssm2602_suspend(struct platform_device *pdev, pm_message_t state)
static int ssm2602_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -581,7 +568,7 @@ static int ssm2602_resume(struct platform_device *pdev)
*/
static int ssm2602_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "SSM2602";
@ -622,7 +609,8 @@ static int ssm2602_init(struct snd_soc_device *socdev)
APANA_ENABLE_MIC_BOOST);
ssm2602_write(codec, SSM2602_PWR, 0);
ssm2602_add_controls(codec);
snd_soc_add_controls(codec, ssm2602_snd_controls,
ARRAY_SIZE(ssm2602_snd_controls));
ssm2602_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -653,7 +641,7 @@ static int ssm2602_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = ssm2602_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -747,7 +735,7 @@ static int ssm2602_probe(struct platform_device *pdev)
}
codec->private_data = ssm2602;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -768,7 +756,7 @@ static int ssm2602_probe(struct platform_device *pdev)
static int ssm2602_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
ssm2602_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -183,24 +183,6 @@ static const struct snd_kcontrol_new tlv320aic23_snd_controls[] = {
SOC_ENUM("Playback De-emphasis", tlv320aic23_deemph),
};
/* add non dapm controls */
static int tlv320aic23_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(tlv320aic23_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&tlv320aic23_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* PGA Mixer controls for Line and Mic switch */
static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
@ -423,7 +405,7 @@ static int tlv320aic23_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 iface_reg;
int ret;
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
@ -471,7 +453,7 @@ static int tlv320aic23_pcm_prepare(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0001);
@ -484,7 +466,7 @@ static void tlv320aic23_shutdown(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
/* deactivate */
@ -598,6 +580,15 @@ static int tlv320aic23_set_bias_level(struct snd_soc_codec *codec,
#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops tlv320aic23_dai_ops = {
.prepare = tlv320aic23_pcm_prepare,
.hw_params = tlv320aic23_hw_params,
.shutdown = tlv320aic23_shutdown,
.digital_mute = tlv320aic23_mute,
.set_fmt = tlv320aic23_set_dai_fmt,
.set_sysclk = tlv320aic23_set_dai_sysclk,
};
struct snd_soc_dai tlv320aic23_dai = {
.name = "tlv320aic23",
.playback = {
@ -612,14 +603,7 @@ struct snd_soc_dai tlv320aic23_dai = {
.channels_max = 2,
.rates = AIC23_RATES,
.formats = AIC23_FORMATS,},
.ops = {
.prepare = tlv320aic23_pcm_prepare,
.hw_params = tlv320aic23_hw_params,
.shutdown = tlv320aic23_shutdown,
.digital_mute = tlv320aic23_mute,
.set_fmt = tlv320aic23_set_dai_fmt,
.set_sysclk = tlv320aic23_set_dai_sysclk,
}
.ops = &tlv320aic23_dai_ops,
};
EXPORT_SYMBOL_GPL(tlv320aic23_dai);
@ -627,7 +611,7 @@ static int tlv320aic23_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x0);
tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -638,7 +622,7 @@ static int tlv320aic23_suspend(struct platform_device *pdev,
static int tlv320aic23_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u16 reg;
@ -660,7 +644,7 @@ static int tlv320aic23_resume(struct platform_device *pdev)
*/
static int tlv320aic23_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
u16 reg;
@ -718,7 +702,8 @@ static int tlv320aic23_init(struct snd_soc_device *socdev)
tlv320aic23_write(codec, TLV320AIC23_ACTIVE, 0x1);
tlv320aic23_add_controls(codec);
snd_soc_add_controls(codec, tlv320aic23_snd_controls,
ARRAY_SIZE(tlv320aic23_snd_controls));
tlv320aic23_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -746,7 +731,7 @@ static int tlv320aic23_codec_probe(struct i2c_client *i2c,
const struct i2c_device_id *i2c_id)
{
struct snd_soc_device *socdev = tlv320aic23_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
@ -804,7 +789,7 @@ static int tlv320aic23_probe(struct platform_device *pdev)
if (aic23 == NULL)
return -ENOMEM;
codec = &aic23->codec;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -823,7 +808,7 @@ static int tlv320aic23_probe(struct platform_device *pdev)
static int tlv320aic23_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct aic23 *aic23 = container_of(codec, struct aic23, codec);
if (codec->control_data)

View File

@ -130,7 +130,7 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct aic26 *aic26 = codec->private_data;
int fsref, divisor, wlen, pval, jval, dval, qval;
u16 reg;
@ -270,6 +270,13 @@ static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
static struct snd_soc_dai_ops aic26_dai_ops = {
.hw_params = aic26_hw_params,
.digital_mute = aic26_mute,
.set_sysclk = aic26_set_sysclk,
.set_fmt = aic26_set_fmt,
};
struct snd_soc_dai aic26_dai = {
.name = "tlv320aic26",
.playback = {
@ -286,12 +293,7 @@ struct snd_soc_dai aic26_dai = {
.rates = AIC26_RATES,
.formats = AIC26_FORMATS,
},
.ops = {
.hw_params = aic26_hw_params,
.digital_mute = aic26_mute,
.set_sysclk = aic26_set_sysclk,
.set_fmt = aic26_set_fmt,
},
.ops = &aic26_dai_ops,
};
EXPORT_SYMBOL_GPL(aic26_dai);
@ -322,9 +324,8 @@ static int aic26_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct snd_kcontrol *kcontrol;
struct aic26 *aic26;
int i, ret, err;
int ret, err;
dev_info(&pdev->dev, "Probing AIC26 SoC CODEC driver\n");
dev_dbg(&pdev->dev, "socdev=%p\n", socdev);
@ -338,7 +339,7 @@ static int aic26_probe(struct platform_device *pdev)
return -ENODEV;
}
codec = &aic26->codec;
socdev->codec = codec;
socdev->card->codec = codec;
dev_dbg(&pdev->dev, "Registering PCMs, dev=%p, socdev->dev=%p\n",
&pdev->dev, socdev->dev);
@ -351,11 +352,9 @@ static int aic26_probe(struct platform_device *pdev)
/* register controls */
dev_dbg(&pdev->dev, "Registering controls\n");
for (i = 0; i < ARRAY_SIZE(aic26_snd_controls); i++) {
kcontrol = snd_soc_cnew(&aic26_snd_controls[i], codec, NULL);
err = snd_ctl_add(codec->card, kcontrol);
WARN_ON(err < 0);
}
err = snd_soc_add_controls(codec, aic26_snd_controls,
ARRAY_SIZE(aic26_snd_controls));
WARN_ON(err < 0);
/* CODEC is setup, we can register the card now */
dev_dbg(&pdev->dev, "Registering card\n");

View File

@ -45,6 +45,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include "tlv320aic3x.h"
@ -250,56 +251,86 @@ static const struct soc_enum aic3x_enum[] = {
SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
};
/*
* DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
*/
static DECLARE_TLV_DB_SCALE(dac_tlv, -6350, 50, 0);
/* ADC PGA gain volumes. From 0 to 59.5 dB in 0.5 dB steps */
static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 50, 0);
/*
* Output stage volumes. From -78.3 to 0 dB. Muted below -78.3 dB.
* Step size is approximately 0.5 dB over most of the scale but increasing
* near the very low levels.
* Define dB scale so that it is mostly correct for range about -55 to 0 dB
* but having increasing dB difference below that (and where it doesn't count
* so much). This setting shows -50 dB (actual is -50.3 dB) for register
* value 100 and -58.5 dB (actual is -78.3 dB) for register value 117.
*/
static DECLARE_TLV_DB_SCALE(output_stage_tlv, -5900, 50, 1);
static const struct snd_kcontrol_new aic3x_snd_controls[] = {
/* Output */
SOC_DOUBLE_R("PCM Playback Volume", LDAC_VOL, RDAC_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("PCM Playback Volume",
LDAC_VOL, RDAC_VOL, 0, 0x7f, 1, dac_tlv),
SOC_DOUBLE_R("Line DAC Playback Volume", DACL1_2_LLOPM_VOL,
DACR1_2_RLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("Line DAC Playback Volume",
DACL1_2_LLOPM_VOL, DACR1_2_RLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_SINGLE("LineL Playback Switch", LLOPM_CTRL, 3, 0x01, 0),
SOC_SINGLE("LineR Playback Switch", RLOPM_CTRL, 3, 0x01, 0),
SOC_DOUBLE_R("LineL DAC Playback Volume", DACL1_2_LLOPM_VOL,
DACR1_2_LLOPM_VOL, 0, 0x7f, 1),
SOC_SINGLE("LineL Left PGA Bypass Playback Volume", PGAL_2_LLOPM_VOL,
0, 0x7f, 1),
SOC_SINGLE("LineR Right PGA Bypass Playback Volume", PGAR_2_RLOPM_VOL,
0, 0x7f, 1),
SOC_DOUBLE_R("LineL Line2 Bypass Playback Volume", LINE2L_2_LLOPM_VOL,
LINE2R_2_LLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R("LineR Line2 Bypass Playback Volume", LINE2L_2_RLOPM_VOL,
LINE2R_2_RLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("LineL DAC Playback Volume",
DACL1_2_LLOPM_VOL, DACR1_2_LLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_SINGLE_TLV("LineL Left PGA Bypass Playback Volume",
PGAL_2_LLOPM_VOL, 0, 118, 1, output_stage_tlv),
SOC_SINGLE_TLV("LineR Right PGA Bypass Playback Volume",
PGAR_2_RLOPM_VOL, 0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R_TLV("LineL Line2 Bypass Playback Volume",
LINE2L_2_LLOPM_VOL, LINE2R_2_LLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R_TLV("LineR Line2 Bypass Playback Volume",
LINE2L_2_RLOPM_VOL, LINE2R_2_RLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("Mono DAC Playback Volume", DACL1_2_MONOLOPM_VOL,
DACR1_2_MONOLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("Mono DAC Playback Volume",
DACL1_2_MONOLOPM_VOL, DACR1_2_MONOLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_SINGLE("Mono DAC Playback Switch", MONOLOPM_CTRL, 3, 0x01, 0),
SOC_DOUBLE_R("Mono PGA Bypass Playback Volume", PGAL_2_MONOLOPM_VOL,
PGAR_2_MONOLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R("Mono Line2 Bypass Playback Volume", LINE2L_2_MONOLOPM_VOL,
LINE2R_2_MONOLOPM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("Mono PGA Bypass Playback Volume",
PGAL_2_MONOLOPM_VOL, PGAR_2_MONOLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R_TLV("Mono Line2 Bypass Playback Volume",
LINE2L_2_MONOLOPM_VOL, LINE2R_2_MONOLOPM_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("HP DAC Playback Volume", DACL1_2_HPLOUT_VOL,
DACR1_2_HPROUT_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("HP DAC Playback Volume",
DACL1_2_HPLOUT_VOL, DACR1_2_HPROUT_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("HP DAC Playback Switch", HPLOUT_CTRL, HPROUT_CTRL, 3,
0x01, 0),
SOC_DOUBLE_R("HP Right PGA Bypass Playback Volume", PGAR_2_HPLOUT_VOL,
PGAR_2_HPROUT_VOL, 0, 0x7f, 1),
SOC_SINGLE("HPL PGA Bypass Playback Volume", PGAL_2_HPLOUT_VOL,
0, 0x7f, 1),
SOC_SINGLE("HPR PGA Bypass Playback Volume", PGAL_2_HPROUT_VOL,
0, 0x7f, 1),
SOC_DOUBLE_R("HP Line2 Bypass Playback Volume", LINE2L_2_HPLOUT_VOL,
LINE2R_2_HPROUT_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("HP Right PGA Bypass Playback Volume",
PGAR_2_HPLOUT_VOL, PGAR_2_HPROUT_VOL,
0, 118, 1, output_stage_tlv),
SOC_SINGLE_TLV("HPL PGA Bypass Playback Volume",
PGAL_2_HPLOUT_VOL, 0, 118, 1, output_stage_tlv),
SOC_SINGLE_TLV("HPR PGA Bypass Playback Volume",
PGAL_2_HPROUT_VOL, 0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R_TLV("HP Line2 Bypass Playback Volume",
LINE2L_2_HPLOUT_VOL, LINE2R_2_HPROUT_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("HPCOM DAC Playback Volume", DACL1_2_HPLCOM_VOL,
DACR1_2_HPRCOM_VOL, 0, 0x7f, 1),
SOC_DOUBLE_R_TLV("HPCOM DAC Playback Volume",
DACL1_2_HPLCOM_VOL, DACR1_2_HPRCOM_VOL,
0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R("HPCOM DAC Playback Switch", HPLCOM_CTRL, HPRCOM_CTRL, 3,
0x01, 0),
SOC_SINGLE("HPLCOM PGA Bypass Playback Volume", PGAL_2_HPLCOM_VOL,
0, 0x7f, 1),
SOC_SINGLE("HPRCOM PGA Bypass Playback Volume", PGAL_2_HPRCOM_VOL,
0, 0x7f, 1),
SOC_DOUBLE_R("HPCOM Line2 Bypass Playback Volume", LINE2L_2_HPLCOM_VOL,
LINE2R_2_HPRCOM_VOL, 0, 0x7f, 1),
SOC_SINGLE_TLV("HPLCOM PGA Bypass Playback Volume",
PGAL_2_HPLCOM_VOL, 0, 118, 1, output_stage_tlv),
SOC_SINGLE_TLV("HPRCOM PGA Bypass Playback Volume",
PGAL_2_HPRCOM_VOL, 0, 118, 1, output_stage_tlv),
SOC_DOUBLE_R_TLV("HPCOM Line2 Bypass Playback Volume",
LINE2L_2_HPLCOM_VOL, LINE2R_2_HPRCOM_VOL,
0, 118, 1, output_stage_tlv),
/*
* Note: enable Automatic input Gain Controller with care. It can
@ -308,28 +339,13 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
/* Input */
SOC_DOUBLE_R("PGA Capture Volume", LADC_VOL, RADC_VOL, 0, 0x7f, 0),
SOC_DOUBLE_R_TLV("PGA Capture Volume", LADC_VOL, RADC_VOL,
0, 119, 0, adc_tlv),
SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
};
/* add non dapm controls */
static int aic3x_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(aic3x_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&aic3x_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Left DAC Mux */
static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
@ -746,7 +762,7 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct aic3x_priv *aic3x = codec->private_data;
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
@ -1072,6 +1088,13 @@ EXPORT_SYMBOL_GPL(aic3x_button_pressed);
#define AIC3X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops aic3x_dai_ops = {
.hw_params = aic3x_hw_params,
.digital_mute = aic3x_mute,
.set_sysclk = aic3x_set_dai_sysclk,
.set_fmt = aic3x_set_dai_fmt,
};
struct snd_soc_dai aic3x_dai = {
.name = "tlv320aic3x",
.playback = {
@ -1086,19 +1109,14 @@ struct snd_soc_dai aic3x_dai = {
.channels_max = 2,
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
.ops = {
.hw_params = aic3x_hw_params,
.digital_mute = aic3x_mute,
.set_sysclk = aic3x_set_dai_sysclk,
.set_fmt = aic3x_set_dai_fmt,
}
.ops = &aic3x_dai_ops,
};
EXPORT_SYMBOL_GPL(aic3x_dai);
static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -1108,7 +1126,7 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state)
static int aic3x_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u8 *cache = codec->reg_cache;
@ -1131,7 +1149,7 @@ static int aic3x_resume(struct platform_device *pdev)
*/
static int aic3x_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct aic3x_setup_data *setup = socdev->codec_data;
int reg, ret = 0;
@ -1227,7 +1245,8 @@ static int aic3x_init(struct snd_soc_device *socdev)
aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4);
aic3x_write(codec, AIC3X_GPIO2_REG, (setup->gpio_func[1] & 0xf) << 4);
aic3x_add_controls(codec);
snd_soc_add_controls(codec, aic3x_snd_controls,
ARRAY_SIZE(aic3x_snd_controls));
aic3x_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -1261,7 +1280,7 @@ static int aic3x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = aic3x_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -1366,7 +1385,7 @@ static int aic3x_probe(struct platform_device *pdev)
}
codec->private_data = aic3x;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -1392,7 +1411,7 @@ static int aic3x_probe(struct platform_device *pdev)
static int aic3x_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* power down chip */
if (codec->control_data)

View File

@ -42,7 +42,7 @@
*/
static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* this register not used */
0x93, /* REG_CODEC_MODE (0x1) */
0x91, /* REG_CODEC_MODE (0x1) */
0xc3, /* REG_OPTION (0x2) */
0x00, /* REG_UNKNOWN (0x3) */
0x00, /* REG_MICBIAS_CTL (0x4) */
@ -117,6 +117,13 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
0x00, /* REG_MISC_SET_2 (0x49) */
};
/* codec private data */
struct twl4030_priv {
unsigned int bypass_state;
unsigned int codec_powered;
unsigned int codec_muted;
};
/*
* read twl4030 register cache
*/
@ -125,6 +132,9 @@ static inline unsigned int twl4030_read_reg_cache(struct snd_soc_codec *codec,
{
u8 *cache = codec->reg_cache;
if (reg >= TWL4030_CACHEREGNUM)
return -EIO;
return cache[reg];
}
@ -151,26 +161,22 @@ static int twl4030_write(struct snd_soc_codec *codec,
return twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE, value, reg);
}
static void twl4030_clear_codecpdz(struct snd_soc_codec *codec)
static void twl4030_codec_enable(struct snd_soc_codec *codec, int enable)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 mode;
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
twl4030_write(codec, TWL4030_REG_CODEC_MODE,
mode & ~TWL4030_CODECPDZ);
/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
udelay(10);
}
static void twl4030_set_codecpdz(struct snd_soc_codec *codec)
{
u8 mode;
if (enable == twl4030->codec_powered)
return;
mode = twl4030_read_reg_cache(codec, TWL4030_REG_CODEC_MODE);
twl4030_write(codec, TWL4030_REG_CODEC_MODE,
mode | TWL4030_CODECPDZ);
if (enable)
mode |= TWL4030_CODECPDZ;
else
mode &= ~TWL4030_CODECPDZ;
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030->codec_powered = enable;
/* REVISIT: this delay is present in TI sample drivers */
/* but there seems to be no TRM requirement for it */
@ -182,7 +188,7 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
int i;
/* clear CODECPDZ prior to setting register defaults */
twl4030_clear_codecpdz(codec);
twl4030_codec_enable(codec, 0);
/* set all audio section registers to reasonable defaults */
for (i = TWL4030_REG_OPTION; i <= TWL4030_REG_MISC_SET_2; i++)
@ -190,6 +196,122 @@ static void twl4030_init_chip(struct snd_soc_codec *codec)
}
static void twl4030_codec_mute(struct snd_soc_codec *codec, int mute)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 reg_val;
if (mute == twl4030->codec_muted)
return;
if (mute) {
/* Bypass the reg_cache and mute the volumes
* Headset mute is done in it's own event handler
* Things to mute: Earpiece, PreDrivL/R, CarkitL/R
*/
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_EAR_GAIN),
TWL4030_REG_EAR_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PREDL_GAIN),
TWL4030_REG_PREDL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PREDR_GAIN),
TWL4030_REG_PREDL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PRECKL_GAIN),
TWL4030_REG_PRECKL_CTL);
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL);
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
reg_val & (~TWL4030_PRECKL_GAIN),
TWL4030_REG_PRECKR_CTL);
/* Disable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val &= ~TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
} else {
/* Restore the volumes
* Headset mute is done in it's own event handler
* Things to restore: Earpiece, PreDrivL/R, CarkitL/R
*/
twl4030_write(codec, TWL4030_REG_EAR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_EAR_CTL));
twl4030_write(codec, TWL4030_REG_PREDL_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PREDL_CTL));
twl4030_write(codec, TWL4030_REG_PREDR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PREDR_CTL));
twl4030_write(codec, TWL4030_REG_PRECKL_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PRECKL_CTL));
twl4030_write(codec, TWL4030_REG_PRECKR_CTL,
twl4030_read_reg_cache(codec, TWL4030_REG_PRECKR_CTL));
/* Enable PLL */
reg_val = twl4030_read_reg_cache(codec, TWL4030_REG_APLL_CTL);
reg_val |= TWL4030_APLL_EN;
twl4030_write(codec, TWL4030_REG_APLL_CTL, reg_val);
}
twl4030->codec_muted = mute;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
{
struct twl4030_priv *twl4030 = codec->private_data;
u8 anamicl, regmisc1, byte;
int i = 0;
if (twl4030->codec_powered)
return;
/* set CODECPDZ to turn on codec */
twl4030_codec_enable(codec, 1);
/* initiate offset cancellation */
anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
twl4030_write(codec, TWL4030_REG_ANAMICL,
anamicl | TWL4030_CNCL_OFFSET_START);
/* wait for offset cancellation to complete */
do {
/* this takes a little while, so don't slam i2c */
udelay(2000);
twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
TWL4030_REG_ANAMICL);
} while ((i++ < 100) &&
((byte & TWL4030_CNCL_OFFSET_START) ==
TWL4030_CNCL_OFFSET_START));
/* Make sure that the reg_cache has the same value as the HW */
twl4030_write_reg_cache(codec, TWL4030_REG_ANAMICL, byte);
/* anti-pop when changing analog gain */
regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
twl4030_write(codec, TWL4030_REG_MISC_SET_1,
regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
/* toggle CODECPDZ as per TRM */
twl4030_codec_enable(codec, 0);
twl4030_codec_enable(codec, 1);
}
/*
* Unconditional power down
*/
static void twl4030_power_down(struct snd_soc_codec *codec)
{
/* power down */
twl4030_codec_enable(codec, 0);
}
/* Earpiece */
static const char *twl4030_earpiece_texts[] =
{"Off", "DACL1", "DACL2", "DACR1"};
@ -366,6 +488,41 @@ static const struct soc_enum twl4030_micpathtx2_enum =
static const struct snd_kcontrol_new twl4030_dapm_micpathtx2_control =
SOC_DAPM_ENUM("Route", twl4030_micpathtx2_enum);
/* Analog bypass for AudioR1 */
static const struct snd_kcontrol_new twl4030_dapm_abypassr1_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR1_APGA_CTL, 2, 1, 0);
/* Analog bypass for AudioL1 */
static const struct snd_kcontrol_new twl4030_dapm_abypassl1_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL1_APGA_CTL, 2, 1, 0);
/* Analog bypass for AudioR2 */
static const struct snd_kcontrol_new twl4030_dapm_abypassr2_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXR2_APGA_CTL, 2, 1, 0);
/* Analog bypass for AudioL2 */
static const struct snd_kcontrol_new twl4030_dapm_abypassl2_control =
SOC_DAPM_SINGLE("Switch", TWL4030_REG_ARXL2_APGA_CTL, 2, 1, 0);
/* Digital bypass gain, 0 mutes the bypass */
static const unsigned int twl4030_dapm_dbypass_tlv[] = {
TLV_DB_RANGE_HEAD(2),
0, 3, TLV_DB_SCALE_ITEM(-2400, 0, 1),
4, 7, TLV_DB_SCALE_ITEM(-1800, 600, 0),
};
/* Digital bypass left (TX1L -> RX2L) */
static const struct snd_kcontrol_new twl4030_dapm_dbypassl_control =
SOC_DAPM_SINGLE_TLV("Volume",
TWL4030_REG_ATX2ARXPGA, 3, 7, 0,
twl4030_dapm_dbypass_tlv);
/* Digital bypass right (TX1R -> RX2R) */
static const struct snd_kcontrol_new twl4030_dapm_dbypassr_control =
SOC_DAPM_SINGLE_TLV("Volume",
TWL4030_REG_ATX2ARXPGA, 0, 7, 0,
twl4030_dapm_dbypass_tlv);
static int micpath_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@ -420,6 +577,79 @@ static int handsfree_event(struct snd_soc_dapm_widget *w,
return 0;
}
static int headsetl_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
unsigned char hs_gain, hs_pop;
/* Save the current volume */
hs_gain = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_GAIN_SET);
hs_pop = twl4030_read_reg_cache(w->codec, TWL4030_REG_HS_POPN_SET);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
/* Do the anti-pop/bias ramp enable according to the TRM */
hs_pop |= TWL4030_VMID_EN;
twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Is this needed? Can we just use whatever gain here? */
twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET,
(hs_gain & (~0x0f)) | 0x0a);
hs_pop |= TWL4030_RAMP_EN;
twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Restore the original volume */
twl4030_write(w->codec, TWL4030_REG_HS_GAIN_SET, hs_gain);
break;
case SND_SOC_DAPM_POST_PMD:
/* Do the anti-pop/bias ramp disable according to the TRM */
hs_pop &= ~TWL4030_RAMP_EN;
twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
/* Bypass the reg_cache to mute the headset */
twl4030_i2c_write_u8(TWL4030_MODULE_AUDIO_VOICE,
hs_gain & (~0x0f),
TWL4030_REG_HS_GAIN_SET);
hs_pop &= ~TWL4030_VMID_EN;
twl4030_write(w->codec, TWL4030_REG_HS_POPN_SET, hs_pop);
break;
}
return 0;
}
static int bypass_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct soc_mixer_control *m =
(struct soc_mixer_control *)w->kcontrols->private_value;
struct twl4030_priv *twl4030 = w->codec->private_data;
unsigned char reg;
reg = twl4030_read_reg_cache(w->codec, m->reg);
if (m->reg <= TWL4030_REG_ARXR2_APGA_CTL) {
/* Analog bypass */
if (reg & (1 << m->shift))
twl4030->bypass_state |=
(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
else
twl4030->bypass_state &=
~(1 << (m->reg - TWL4030_REG_ARXL1_APGA_CTL));
} else {
/* Digital bypass */
if (reg & (0x7 << m->shift))
twl4030->bypass_state |= (1 << (m->shift ? 5 : 4));
else
twl4030->bypass_state &= ~(1 << (m->shift ? 5 : 4));
}
if (w->codec->bias_level == SND_SOC_BIAS_STANDBY) {
if (twl4030->bypass_state)
twl4030_codec_mute(w->codec, 0);
else
twl4030_codec_mute(w->codec, 1);
}
return 0;
}
/*
* Some of the gain controls in TWL (mostly those which are associated with
* the outputs) are implemented in an interesting way:
@ -614,6 +844,17 @@ static DECLARE_TLV_DB_SCALE(digital_capture_tlv, 0, 100, 0);
*/
static DECLARE_TLV_DB_SCALE(input_gain_tlv, 0, 600, 0);
static const char *twl4030_rampdelay_texts[] = {
"27/20/14 ms", "55/40/27 ms", "109/81/55 ms", "218/161/109 ms",
"437/323/218 ms", "874/645/437 ms", "1748/1291/874 ms",
"3495/2581/1748 ms"
};
static const struct soc_enum twl4030_rampdelay_enum =
SOC_ENUM_SINGLE(TWL4030_REG_HS_POPN_SET, 2,
ARRAY_SIZE(twl4030_rampdelay_texts),
twl4030_rampdelay_texts);
static const struct snd_kcontrol_new twl4030_snd_controls[] = {
/* Common playback gain controls */
SOC_DOUBLE_R_TLV("DAC1 Digital Fine Playback Volume",
@ -668,24 +909,10 @@ static const struct snd_kcontrol_new twl4030_snd_controls[] = {
SOC_DOUBLE_TLV("Analog Capture Volume", TWL4030_REG_ANAMIC_GAIN,
0, 3, 5, 0, input_gain_tlv),
SOC_ENUM("HS ramp delay", twl4030_rampdelay_enum),
};
/* add non dapm controls */
static int twl4030_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(twl4030_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&twl4030_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
/* Left channel inputs */
SND_SOC_DAPM_INPUT("MAINMIC"),
@ -714,13 +941,13 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
/* DACs */
SND_SOC_DAPM_DAC("DAC Right1", "Right Front Playback",
TWL4030_REG_AVDAC_CTL, 0, 0),
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Left1", "Left Front Playback",
TWL4030_REG_AVDAC_CTL, 1, 0),
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Right2", "Right Rear Playback",
TWL4030_REG_AVDAC_CTL, 2, 0),
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC Left2", "Left Rear Playback",
TWL4030_REG_AVDAC_CTL, 3, 0),
SND_SOC_NOPM, 0, 0),
/* Analog PGAs */
SND_SOC_DAPM_PGA("ARXR1_APGA", TWL4030_REG_ARXR1_APGA_CTL,
@ -732,6 +959,37 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_PGA("ARXL2_APGA", TWL4030_REG_ARXL2_APGA_CTL,
0, 0, NULL, 0),
/* Analog bypasses */
SND_SOC_DAPM_SWITCH_E("Right1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr1_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Left1 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl1_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Right2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassr2_control,
bypass_event, SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Left2 Analog Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_abypassl2_control,
bypass_event, SND_SOC_DAPM_POST_REG),
/* Digital bypasses */
SND_SOC_DAPM_SWITCH_E("Left Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassl_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_SWITCH_E("Right Digital Loopback", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_dbypassr_control, bypass_event,
SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_MIXER("Analog R1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
0, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Analog L1 Playback Mixer", TWL4030_REG_AVDAC_CTL,
1, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Analog R2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
2, 0, NULL, 0),
SND_SOC_DAPM_MIXER("Analog L2 Playback Mixer", TWL4030_REG_AVDAC_CTL,
3, 0, NULL, 0),
/* Output MUX controls */
/* Earpiece */
SND_SOC_DAPM_VALUE_MUX("Earpiece Mux", SND_SOC_NOPM, 0, 0,
@ -742,8 +1000,9 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_VALUE_MUX("PredriveR Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_predriver_control),
/* HeadsetL/R */
SND_SOC_DAPM_MUX("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_hsol_control),
SND_SOC_DAPM_MUX_E("HeadsetL Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_hsol_control, headsetl_event,
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("HeadsetR Mux", SND_SOC_NOPM, 0, 0,
&twl4030_dapm_hsor_control),
/* CarkitL/R */
@ -782,16 +1041,16 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_POST_PMU|SND_SOC_DAPM_POST_PMD|
SND_SOC_DAPM_POST_REG),
/* Analog input muxes with power switch for the physical ADCL/R */
/* Analog input muxes with switch for the capture amplifiers */
SND_SOC_DAPM_VALUE_MUX("Analog Left Capture Route",
TWL4030_REG_AVADC_CTL, 3, 0, &twl4030_dapm_analoglmic_control),
TWL4030_REG_ANAMICL, 4, 0, &twl4030_dapm_analoglmic_control),
SND_SOC_DAPM_VALUE_MUX("Analog Right Capture Route",
TWL4030_REG_AVADC_CTL, 1, 0, &twl4030_dapm_analogrmic_control),
TWL4030_REG_ANAMICR, 4, 0, &twl4030_dapm_analogrmic_control),
SND_SOC_DAPM_PGA("Analog Left Amplifier",
TWL4030_REG_ANAMICL, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("Analog Right Amplifier",
TWL4030_REG_ANAMICR, 4, 0, NULL, 0),
SND_SOC_DAPM_PGA("ADC Physical Left",
TWL4030_REG_AVADC_CTL, 3, 0, NULL, 0),
SND_SOC_DAPM_PGA("ADC Physical Right",
TWL4030_REG_AVADC_CTL, 1, 0, NULL, 0),
SND_SOC_DAPM_PGA("Digimic0 Enable",
TWL4030_REG_ADCMICSEL, 1, 0, NULL, 0),
@ -801,13 +1060,19 @@ static const struct snd_soc_dapm_widget twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MICBIAS("Mic Bias 1", TWL4030_REG_MICBIAS_CTL, 0, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias 2", TWL4030_REG_MICBIAS_CTL, 1, 0),
SND_SOC_DAPM_MICBIAS("Headset Mic Bias", TWL4030_REG_MICBIAS_CTL, 2, 0),
};
static const struct snd_soc_dapm_route intercon[] = {
{"ARXL1_APGA", NULL, "DAC Left1"},
{"ARXR1_APGA", NULL, "DAC Right1"},
{"ARXL2_APGA", NULL, "DAC Left2"},
{"ARXR2_APGA", NULL, "DAC Right2"},
{"Analog L1 Playback Mixer", NULL, "DAC Left1"},
{"Analog R1 Playback Mixer", NULL, "DAC Right1"},
{"Analog L2 Playback Mixer", NULL, "DAC Left2"},
{"Analog R2 Playback Mixer", NULL, "DAC Right2"},
{"ARXL1_APGA", NULL, "Analog L1 Playback Mixer"},
{"ARXR1_APGA", NULL, "Analog R1 Playback Mixer"},
{"ARXL2_APGA", NULL, "Analog L2 Playback Mixer"},
{"ARXR2_APGA", NULL, "Analog R2 Playback Mixer"},
/* Internal playback routings */
/* Earpiece */
@ -865,23 +1130,23 @@ static const struct snd_soc_dapm_route intercon[] = {
{"Analog Right Capture Route", "Sub mic", "SUBMIC"},
{"Analog Right Capture Route", "AUXR", "AUXR"},
{"Analog Left Amplifier", NULL, "Analog Left Capture Route"},
{"Analog Right Amplifier", NULL, "Analog Right Capture Route"},
{"ADC Physical Left", NULL, "Analog Left Capture Route"},
{"ADC Physical Right", NULL, "Analog Right Capture Route"},
{"Digimic0 Enable", NULL, "DIGIMIC0"},
{"Digimic1 Enable", NULL, "DIGIMIC1"},
/* TX1 Left capture path */
{"TX1 Capture Route", "Analog", "Analog Left Amplifier"},
{"TX1 Capture Route", "Analog", "ADC Physical Left"},
{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
/* TX1 Right capture path */
{"TX1 Capture Route", "Analog", "Analog Right Amplifier"},
{"TX1 Capture Route", "Analog", "ADC Physical Right"},
{"TX1 Capture Route", "Digimic0", "Digimic0 Enable"},
/* TX2 Left capture path */
{"TX2 Capture Route", "Analog", "Analog Left Amplifier"},
{"TX2 Capture Route", "Analog", "ADC Physical Left"},
{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
/* TX2 Right capture path */
{"TX2 Capture Route", "Analog", "Analog Right Amplifier"},
{"TX2 Capture Route", "Analog", "ADC Physical Right"},
{"TX2 Capture Route", "Digimic1", "Digimic1 Enable"},
{"ADC Virtual Left1", NULL, "TX1 Capture Route"},
@ -889,6 +1154,24 @@ static const struct snd_soc_dapm_route intercon[] = {
{"ADC Virtual Left2", NULL, "TX2 Capture Route"},
{"ADC Virtual Right2", NULL, "TX2 Capture Route"},
/* Analog bypass routes */
{"Right1 Analog Loopback", "Switch", "Analog Right Capture Route"},
{"Left1 Analog Loopback", "Switch", "Analog Left Capture Route"},
{"Right2 Analog Loopback", "Switch", "Analog Right Capture Route"},
{"Left2 Analog Loopback", "Switch", "Analog Left Capture Route"},
{"Analog R1 Playback Mixer", NULL, "Right1 Analog Loopback"},
{"Analog L1 Playback Mixer", NULL, "Left1 Analog Loopback"},
{"Analog R2 Playback Mixer", NULL, "Right2 Analog Loopback"},
{"Analog L2 Playback Mixer", NULL, "Left2 Analog Loopback"},
/* Digital bypass routes */
{"Right Digital Loopback", "Volume", "TX1 Capture Route"},
{"Left Digital Loopback", "Volume", "TX1 Capture Route"},
{"Analog R2 Playback Mixer", NULL, "Right Digital Loopback"},
{"Analog L2 Playback Mixer", NULL, "Left Digital Loopback"},
};
static int twl4030_add_widgets(struct snd_soc_codec *codec)
@ -902,82 +1185,28 @@ static int twl4030_add_widgets(struct snd_soc_codec *codec)
return 0;
}
static void twl4030_power_up(struct snd_soc_codec *codec)
{
u8 anamicl, regmisc1, byte, popn;
int i = 0;
/* set CODECPDZ to turn on codec */
twl4030_set_codecpdz(codec);
/* initiate offset cancellation */
anamicl = twl4030_read_reg_cache(codec, TWL4030_REG_ANAMICL);
twl4030_write(codec, TWL4030_REG_ANAMICL,
anamicl | TWL4030_CNCL_OFFSET_START);
/* wait for offset cancellation to complete */
do {
/* this takes a little while, so don't slam i2c */
udelay(2000);
twl4030_i2c_read_u8(TWL4030_MODULE_AUDIO_VOICE, &byte,
TWL4030_REG_ANAMICL);
} while ((i++ < 100) &&
((byte & TWL4030_CNCL_OFFSET_START) ==
TWL4030_CNCL_OFFSET_START));
/* anti-pop when changing analog gain */
regmisc1 = twl4030_read_reg_cache(codec, TWL4030_REG_MISC_SET_1);
twl4030_write(codec, TWL4030_REG_MISC_SET_1,
regmisc1 | TWL4030_SMOOTH_ANAVOL_EN);
/* toggle CODECPDZ as per TRM */
twl4030_clear_codecpdz(codec);
twl4030_set_codecpdz(codec);
/* program anti-pop with bias ramp delay */
popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
popn &= TWL4030_RAMP_DELAY;
popn |= TWL4030_RAMP_DELAY_645MS;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
popn |= TWL4030_VMID_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
/* enable anti-pop ramp */
popn |= TWL4030_RAMP_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
}
static void twl4030_power_down(struct snd_soc_codec *codec)
{
u8 popn;
/* disable anti-pop ramp */
popn = twl4030_read_reg_cache(codec, TWL4030_REG_HS_POPN_SET);
popn &= ~TWL4030_RAMP_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
/* disable bias out */
popn &= ~TWL4030_VMID_EN;
twl4030_write(codec, TWL4030_REG_HS_POPN_SET, popn);
/* power down */
twl4030_clear_codecpdz(codec);
}
static int twl4030_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
struct twl4030_priv *twl4030 = codec->private_data;
switch (level) {
case SND_SOC_BIAS_ON:
twl4030_power_up(codec);
twl4030_codec_mute(codec, 0);
break;
case SND_SOC_BIAS_PREPARE:
/* TODO: develop a twl4030_prepare function */
twl4030_power_up(codec);
if (twl4030->bypass_state)
twl4030_codec_mute(codec, 0);
else
twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_STANDBY:
/* TODO: develop a twl4030_standby function */
twl4030_power_down(codec);
twl4030_power_up(codec);
if (twl4030->bypass_state)
twl4030_codec_mute(codec, 0);
else
twl4030_codec_mute(codec, 1);
break;
case SND_SOC_BIAS_OFF:
twl4030_power_down(codec);
@ -994,10 +1223,9 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u8 mode, old_mode, format, old_format;
/* bit rate */
old_mode = twl4030_read_reg_cache(codec,
TWL4030_REG_CODEC_MODE) & ~TWL4030_CODECPDZ;
@ -1039,8 +1267,9 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
if (mode != old_mode) {
/* change rate and set CODECPDZ */
twl4030_codec_enable(codec, 0);
twl4030_write(codec, TWL4030_REG_CODEC_MODE, mode);
twl4030_set_codecpdz(codec);
twl4030_codec_enable(codec, 1);
}
/* sample size */
@ -1063,13 +1292,13 @@ static int twl4030_hw_params(struct snd_pcm_substream *substream,
if (format != old_format) {
/* clear CODECPDZ before changing format (codec requirement) */
twl4030_clear_codecpdz(codec);
twl4030_codec_enable(codec, 0);
/* change format */
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
/* set CODECPDZ afterwards */
twl4030_set_codecpdz(codec);
twl4030_codec_enable(codec, 1);
}
return 0;
}
@ -1139,13 +1368,13 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
if (format != old_format) {
/* clear CODECPDZ before changing format (codec requirement) */
twl4030_clear_codecpdz(codec);
twl4030_codec_enable(codec, 0);
/* change format */
twl4030_write(codec, TWL4030_REG_AUDIO_IF, format);
/* set CODECPDZ afterwards */
twl4030_set_codecpdz(codec);
twl4030_codec_enable(codec, 1);
}
return 0;
@ -1154,6 +1383,12 @@ static int twl4030_set_dai_fmt(struct snd_soc_dai *codec_dai,
#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_S24_LE)
static struct snd_soc_dai_ops twl4030_dai_ops = {
.hw_params = twl4030_hw_params,
.set_sysclk = twl4030_set_dai_sysclk,
.set_fmt = twl4030_set_dai_fmt,
};
struct snd_soc_dai twl4030_dai = {
.name = "twl4030",
.playback = {
@ -1168,18 +1403,14 @@ struct snd_soc_dai twl4030_dai = {
.channels_max = 2,
.rates = TWL4030_RATES,
.formats = TWL4030_FORMATS,},
.ops = {
.hw_params = twl4030_hw_params,
.set_sysclk = twl4030_set_dai_sysclk,
.set_fmt = twl4030_set_dai_fmt,
}
.ops = &twl4030_dai_ops,
};
EXPORT_SYMBOL_GPL(twl4030_dai);
static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -1189,7 +1420,7 @@ static int twl4030_suspend(struct platform_device *pdev, pm_message_t state)
static int twl4030_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
twl4030_set_bias_level(codec, codec->suspend_bias_level);
@ -1203,7 +1434,7 @@ static int twl4030_resume(struct platform_device *pdev)
static int twl4030_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
printk(KERN_INFO "TWL4030 Audio Codec init \n");
@ -1233,7 +1464,8 @@ static int twl4030_init(struct snd_soc_device *socdev)
/* power on device */
twl4030_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
twl4030_add_controls(codec);
snd_soc_add_controls(codec, twl4030_snd_controls,
ARRAY_SIZE(twl4030_snd_controls));
twl4030_add_widgets(codec);
ret = snd_soc_init_card(socdev);
@ -1258,12 +1490,20 @@ static int twl4030_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
struct twl4030_priv *twl4030;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
socdev->codec = codec;
twl4030 = kzalloc(sizeof(struct twl4030_priv), GFP_KERNEL);
if (twl4030 == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = twl4030;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -1277,11 +1517,13 @@ static int twl4030_probe(struct platform_device *pdev)
static int twl4030_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
printk(KERN_INFO "TWL4030 Audio Codec remove\n");
twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
kfree(codec->private_data);
kfree(codec);
return 0;

View File

@ -170,6 +170,9 @@
#define TWL4030_CLK256FS_EN 0x02
#define TWL4030_AIF_EN 0x01
/* EAR_CTL (0x21) */
#define TWL4030_EAR_GAIN 0x30
/* HS_GAIN_SET (0x23) Fields */
#define TWL4030_HSR_GAIN 0x0C
@ -198,6 +201,18 @@
#define TWL4030_RAMP_DELAY_2581MS 0x1C
#define TWL4030_RAMP_EN 0x02
/* PREDL_CTL (0x25) */
#define TWL4030_PREDL_GAIN 0x30
/* PREDR_CTL (0x26) */
#define TWL4030_PREDR_GAIN 0x30
/* PRECKL_CTL (0x27) */
#define TWL4030_PRECKL_GAIN 0x30
/* PRECKR_CTL (0x28) */
#define TWL4030_PRECKR_GAIN 0x30
/* HFL_CTL (0x29, 0x2A) Fields */
#define TWL4030_HF_CTL_HB_EN 0x04
#define TWL4030_HF_CTL_LOOP_EN 0x08

View File

@ -173,7 +173,7 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct uda134x_priv *uda134x = codec->private_data;
struct snd_pcm_runtime *master_runtime;
@ -206,7 +206,7 @@ static void uda134x_shutdown(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct uda134x_priv *uda134x = codec->private_data;
if (uda134x->master_substream == substream)
@ -221,7 +221,7 @@ static int uda134x_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct uda134x_priv *uda134x = codec->private_data;
u8 hw_params;
@ -431,38 +431,14 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
};
static int uda134x_add_controls(struct snd_soc_codec *codec)
{
int err, i, n;
const struct snd_kcontrol_new *ctrls;
struct uda134x_platform_data *pd = codec->control_data;
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
n = ARRAY_SIZE(uda1340_snd_controls);
ctrls = uda1340_snd_controls;
break;
case UDA134X_UDA1341:
n = ARRAY_SIZE(uda1341_snd_controls);
ctrls = uda1341_snd_controls;
break;
default:
printk(KERN_ERR "%s unkown codec type: %d",
__func__, pd->model);
return -EINVAL;
}
for (i = 0; i < n; i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&ctrls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static struct snd_soc_dai_ops uda134x_dai_ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
.hw_params = uda134x_hw_params,
.digital_mute = uda134x_mute,
.set_sysclk = uda134x_set_dai_sysclk,
.set_fmt = uda134x_set_dai_fmt,
};
struct snd_soc_dai uda134x_dai = {
.name = "UDA134X",
@ -483,14 +459,7 @@ struct snd_soc_dai uda134x_dai = {
.formats = UDA134X_FORMATS,
},
/* pcm operations */
.ops = {
.startup = uda134x_startup,
.shutdown = uda134x_shutdown,
.hw_params = uda134x_hw_params,
.digital_mute = uda134x_mute,
.set_sysclk = uda134x_set_dai_sysclk,
.set_fmt = uda134x_set_dai_fmt,
}
.ops = &uda134x_dai_ops,
};
EXPORT_SYMBOL(uda134x_dai);
@ -525,11 +494,11 @@ static int uda134x_soc_probe(struct platform_device *pdev)
return -EINVAL;
}
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (socdev->codec == NULL)
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (socdev->card->codec == NULL)
return ret;
codec = socdev->codec;
codec = socdev->card->codec;
uda134x = kzalloc(sizeof(struct uda134x_priv), GFP_KERNEL);
if (uda134x == NULL)
@ -572,7 +541,22 @@ static int uda134x_soc_probe(struct platform_device *pdev)
goto pcm_err;
}
ret = uda134x_add_controls(codec);
switch (pd->model) {
case UDA134X_UDA1340:
case UDA134X_UDA1344:
ret = snd_soc_add_controls(codec, uda1340_snd_controls,
ARRAY_SIZE(uda1340_snd_controls));
break;
case UDA134X_UDA1341:
ret = snd_soc_add_controls(codec, uda1341_snd_controls,
ARRAY_SIZE(uda1341_snd_controls));
break;
default:
printk(KERN_ERR "%s unkown codec type: %d",
__func__, pd->model);
return -EINVAL;
}
if (ret < 0) {
printk(KERN_ERR "UDA134X: failed to register controls\n");
goto pcm_err;
@ -602,7 +586,7 @@ priv_err:
static int uda134x_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -622,7 +606,7 @@ static int uda134x_soc_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -632,7 +616,7 @@ static int uda134x_soc_suspend(struct platform_device *pdev,
static int uda134x_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);

View File

@ -25,6 +25,7 @@
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
@ -35,7 +36,8 @@
#include "uda1380.h"
#define UDA1380_VERSION "0.6"
static struct work_struct uda1380_work;
static struct snd_soc_codec *uda1380_codec;
/*
* uda1380 register cache
@ -52,6 +54,8 @@ static const u16 uda1380_reg[UDA1380_CACHEREGNUM] = {
0x0000, 0x8000, 0x0002, 0x0000,
};
static unsigned long uda1380_cache_dirty;
/*
* read uda1380 register cache
*/
@ -73,8 +77,11 @@ static inline void uda1380_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
if (reg >= UDA1380_CACHEREGNUM)
return;
if ((reg >= 0x10) && (cache[reg] != value))
set_bit(reg - 0x10, &uda1380_cache_dirty);
cache[reg] = value;
}
@ -113,6 +120,8 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
(data[0]<<8) | data[1]);
return -EIO;
}
if (reg >= 0x10)
clear_bit(reg - 0x10, &uda1380_cache_dirty);
return 0;
} else
return -EIO;
@ -120,6 +129,20 @@ static int uda1380_write(struct snd_soc_codec *codec, unsigned int reg,
#define uda1380_reset(c) uda1380_write(c, UDA1380_RESET, 0)
static void uda1380_flush_work(struct work_struct *work)
{
int bit, reg;
for_each_bit(bit, &uda1380_cache_dirty, UDA1380_CACHEREGNUM - 0x10) {
reg = 0x10 + bit;
pr_debug("uda1380: flush reg %x val %x:\n", reg,
uda1380_read_reg_cache(uda1380_codec, reg));
uda1380_write(uda1380_codec, reg,
uda1380_read_reg_cache(uda1380_codec, reg));
clear_bit(bit, &uda1380_cache_dirty);
}
}
/* declarations of ALSA reg_elem_REAL controls */
static const char *uda1380_deemp[] = {
"None",
@ -254,7 +277,6 @@ static const struct snd_kcontrol_new uda1380_snd_controls[] = {
SOC_SINGLE("DAC Polarity inverting Switch", UDA1380_MIXER, 15, 1, 0), /* DA_POL_INV */
SOC_ENUM("Noise Shaper", uda1380_sel_ns_enum), /* SEL_NS */
SOC_ENUM("Digital Mixer Signal Control", uda1380_mix_enum), /* MIX_POS, MIX */
SOC_SINGLE("Silence Switch", UDA1380_MIXER, 7, 1, 0), /* SILENCE, force DAC output to silence */
SOC_SINGLE("Silence Detector Switch", UDA1380_MIXER, 6, 1, 0), /* SDET_ON */
SOC_ENUM("Silence Detector Setting", uda1380_sdet_enum), /* SD_VALUE */
SOC_ENUM("Oversampling Input", uda1380_os_enum), /* OS */
@ -271,21 +293,6 @@ static const struct snd_kcontrol_new uda1380_snd_controls[] = {
SOC_SINGLE("AGC Switch", UDA1380_AGC, 0, 1, 0),
};
/* add non dapm controls */
static int uda1380_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(uda1380_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&uda1380_snd_controls[i], codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Input mux */
static const struct snd_kcontrol_new uda1380_input_mux_control =
SOC_DAPM_ENUM("Route", uda1380_input_sel_enum);
@ -371,7 +378,7 @@ static int uda1380_add_widgets(struct snd_soc_codec *codec)
return 0;
}
static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int uda1380_set_dai_fmt_both(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
@ -381,16 +388,75 @@ static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface &= ~(R01_SFORI_MASK | R01_SIM | R01_SFORO_MASK);
/* FIXME: how to select I2S for DATAO and MSB for DATAI correctly? */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= R01_SFORI_I2S | R01_SFORO_I2S;
break;
case SND_SOC_DAIFMT_LSB:
iface |= R01_SFORI_LSB16 | R01_SFORO_I2S;
iface |= R01_SFORI_LSB16 | R01_SFORO_LSB16;
break;
case SND_SOC_DAIFMT_MSB:
iface |= R01_SFORI_MSB | R01_SFORO_I2S;
iface |= R01_SFORI_MSB | R01_SFORO_MSB;
}
/* DATAI is slave only, so in single-link mode, this has to be slave */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
uda1380_write(codec, UDA1380_IFACE, iface);
return 0;
}
static int uda1380_set_dai_fmt_playback(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface &= ~R01_SFORI_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= R01_SFORI_I2S;
break;
case SND_SOC_DAIFMT_LSB:
iface |= R01_SFORI_LSB16;
break;
case SND_SOC_DAIFMT_MSB:
iface |= R01_SFORI_MSB;
}
/* DATAI is slave only, so this has to be slave */
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS)
return -EINVAL;
uda1380_write(codec, UDA1380_IFACE, iface);
return 0;
}
static int uda1380_set_dai_fmt_capture(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
int iface;
/* set up DAI based upon fmt */
iface = uda1380_read_reg_cache(codec, UDA1380_IFACE);
iface &= ~(R01_SIM | R01_SFORO_MASK);
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
iface |= R01_SFORO_I2S;
break;
case SND_SOC_DAIFMT_LSB:
iface |= R01_SFORO_LSB16;
break;
case SND_SOC_DAIFMT_MSB:
iface |= R01_SFORO_MSB;
}
if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) == SND_SOC_DAIFMT_CBM_CFM)
@ -401,41 +467,28 @@ static int uda1380_set_dai_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
/*
* Flush reg cache
* We can only write the interpolator and decimator registers
* when the DAI is being clocked by the CPU DAI. It's up to the
* machine and cpu DAI driver to do this before we are called.
*/
static int uda1380_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
int reg, reg_start, reg_end, clk;
struct snd_soc_codec *codec = socdev->card->codec;
int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
reg_start = UDA1380_MVOL;
reg_end = UDA1380_MIXER;
} else {
reg_start = UDA1380_DEC;
reg_end = UDA1380_AGC;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
mixer & ~R14_SILENCE);
schedule_work(&uda1380_work);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
uda1380_write_reg_cache(codec, UDA1380_MIXER,
mixer | R14_SILENCE);
schedule_work(&uda1380_work);
break;
}
/* FIXME disable DAC_CLK */
clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
uda1380_write(codec, UDA1380_CLK, clk & ~R00_DAC_CLK);
for (reg = reg_start; reg <= reg_end; reg++) {
pr_debug("uda1380: flush reg %x val %x:", reg,
uda1380_read_reg_cache(codec, reg));
uda1380_write(codec, reg, uda1380_read_reg_cache(codec, reg));
}
/* FIXME enable DAC_CLK */
uda1380_write(codec, UDA1380_CLK, clk | R00_DAC_CLK);
return 0;
}
@ -445,7 +498,7 @@ static int uda1380_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* set WSPLL power and divider if running from this clock */
@ -484,7 +537,7 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
/* shut down WSPLL power if running from this clock */
@ -501,24 +554,6 @@ static void uda1380_pcm_shutdown(struct snd_pcm_substream *substream,
uda1380_write(codec, UDA1380_CLK, clk);
}
static int uda1380_mute(struct snd_soc_dai *codec_dai, int mute)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 mute_reg = uda1380_read_reg_cache(codec, UDA1380_DEEMP) & ~R13_MTM;
/* FIXME: mute(codec,0) is called when the magician clock is already
* set to WSPLL, but for some unknown reason writing to interpolator
* registers works only when clocked by SYSCLK */
u16 clk = uda1380_read_reg_cache(codec, UDA1380_CLK);
uda1380_write(codec, UDA1380_CLK, ~R00_DAC_CLK & clk);
if (mute)
uda1380_write(codec, UDA1380_DEEMP, mute_reg | R13_MTM);
else
uda1380_write(codec, UDA1380_DEEMP, mute_reg);
uda1380_write(codec, UDA1380_CLK, clk);
return 0;
}
static int uda1380_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@ -544,6 +579,27 @@ static int uda1380_set_bias_level(struct snd_soc_codec *codec,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
static struct snd_soc_dai_ops uda1380_dai_ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.trigger = uda1380_trigger,
.set_fmt = uda1380_set_dai_fmt_both,
};
static struct snd_soc_dai_ops uda1380_dai_ops_playback = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.trigger = uda1380_trigger,
.set_fmt = uda1380_set_dai_fmt_playback,
};
static struct snd_soc_dai_ops uda1380_dai_ops_capture = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.trigger = uda1380_trigger,
.set_fmt = uda1380_set_dai_fmt_capture,
};
struct snd_soc_dai uda1380_dai[] = {
{
.name = "UDA1380",
@ -559,13 +615,7 @@ struct snd_soc_dai uda1380_dai[] = {
.channels_max = 2,
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
.digital_mute = uda1380_mute,
.set_fmt = uda1380_set_dai_fmt,
},
.ops = &uda1380_dai_ops,
},
{ /* playback only - dual interface */
.name = "UDA1380",
@ -576,13 +626,7 @@ struct snd_soc_dai uda1380_dai[] = {
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
.digital_mute = uda1380_mute,
.set_fmt = uda1380_set_dai_fmt,
},
.ops = &uda1380_dai_ops_playback,
},
{ /* capture only - dual interface*/
.name = "UDA1380",
@ -593,12 +637,7 @@ struct snd_soc_dai uda1380_dai[] = {
.rates = UDA1380_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.prepare = uda1380_pcm_prepare,
.set_fmt = uda1380_set_dai_fmt,
},
.ops = &uda1380_dai_ops_capture,
},
};
EXPORT_SYMBOL_GPL(uda1380_dai);
@ -606,7 +645,7 @@ EXPORT_SYMBOL_GPL(uda1380_dai);
static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -615,7 +654,7 @@ static int uda1380_suspend(struct platform_device *pdev, pm_message_t state)
static int uda1380_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -637,7 +676,7 @@ static int uda1380_resume(struct platform_device *pdev)
*/
static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "UDA1380";
@ -655,6 +694,9 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
codec->reg_cache_step = 1;
uda1380_reset(codec);
uda1380_codec = codec;
INIT_WORK(&uda1380_work, uda1380_flush_work);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@ -675,7 +717,8 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk)
}
/* uda1380 init */
uda1380_add_controls(codec);
snd_soc_add_controls(codec, uda1380_snd_controls,
ARRAY_SIZE(uda1380_snd_controls));
uda1380_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -702,7 +745,7 @@ static int uda1380_i2c_probe(struct i2c_client *i2c,
{
struct snd_soc_device *socdev = uda1380_socdev;
struct uda1380_setup_data *setup = socdev->codec_data;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -786,14 +829,12 @@ static int uda1380_probe(struct platform_device *pdev)
struct snd_soc_codec *codec;
int ret;
pr_info("UDA1380 Audio Codec %s", UDA1380_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -817,7 +858,7 @@ static int uda1380_probe(struct platform_device *pdev)
static int uda1380_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -51,10 +51,17 @@ struct wm8350_output {
u16 mute;
};
struct wm8350_jack_data {
struct snd_soc_jack *jack;
int report;
};
struct wm8350_data {
struct snd_soc_codec codec;
struct wm8350_output out1;
struct wm8350_output out2;
struct wm8350_jack_data hpl;
struct wm8350_jack_data hpr;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
@ -775,21 +782,6 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Beep", NULL, "IN3R PGA"},
};
static int wm8350_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8350_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8350_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static int wm8350_add_widgets(struct snd_soc_codec *codec)
{
int ret;
@ -1309,7 +1301,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -1318,7 +1310,7 @@ static int wm8350_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8350_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@ -1328,6 +1320,95 @@ static int wm8350_resume(struct platform_device *pdev)
return 0;
}
static void wm8350_hp_jack_handler(struct wm8350 *wm8350, int irq, void *data)
{
struct wm8350_data *priv = data;
u16 reg;
int report;
int mask;
struct wm8350_jack_data *jack = NULL;
switch (irq) {
case WM8350_IRQ_CODEC_JCK_DET_L:
jack = &priv->hpl;
mask = WM8350_JACK_L_LVL;
break;
case WM8350_IRQ_CODEC_JCK_DET_R:
jack = &priv->hpr;
mask = WM8350_JACK_R_LVL;
break;
default:
BUG();
}
if (!jack->jack) {
dev_warn(wm8350->dev, "Jack interrupt called with no jack\n");
return;
}
/* Debounce */
msleep(200);
reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS);
if (reg & mask)
report = jack->report;
else
report = 0;
snd_soc_jack_report(jack->jack, report, jack->report);
}
/**
* wm8350_hp_jack_detect - Enable headphone jack detection.
*
* @codec: WM8350 codec
* @which: left or right jack detect signal
* @jack: jack to report detection events on
* @report: value to report
*
* Enables the headphone jack detection of the WM8350.
*/
int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
struct snd_soc_jack *jack, int report)
{
struct wm8350_data *priv = codec->private_data;
struct wm8350 *wm8350 = codec->control_data;
int irq;
int ena;
switch (which) {
case WM8350_JDL:
priv->hpl.jack = jack;
priv->hpl.report = report;
irq = WM8350_IRQ_CODEC_JCK_DET_L;
ena = WM8350_JDL_ENA;
break;
case WM8350_JDR:
priv->hpr.jack = jack;
priv->hpr.report = report;
irq = WM8350_IRQ_CODEC_JCK_DET_R;
ena = WM8350_JDR_ENA;
break;
default:
return -EINVAL;
}
wm8350_set_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
wm8350_set_bits(wm8350, WM8350_JACK_DETECT, ena);
/* Sync status */
wm8350_hp_jack_handler(wm8350, irq, priv);
wm8350_unmask_irq(wm8350, irq);
return 0;
}
EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect);
static struct snd_soc_codec *wm8350_codec;
static int wm8350_probe(struct platform_device *pdev)
@ -1342,8 +1423,8 @@ static int wm8350_probe(struct platform_device *pdev)
BUG_ON(!wm8350_codec);
socdev->codec = wm8350_codec;
codec = socdev->codec;
socdev->card->codec = wm8350_codec;
codec = socdev->card->codec;
wm8350 = codec->control_data;
priv = codec->private_data;
@ -1381,13 +1462,21 @@ static int wm8350_probe(struct platform_device *pdev)
wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME,
WM8350_OUT2_VU | WM8350_OUT2R_MUTE);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L,
wm8350_hp_jack_handler, priv);
wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R,
wm8350_hp_jack_handler, priv);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
dev_err(&pdev->dev, "failed to create pcms\n");
return ret;
}
wm8350_add_controls(codec);
snd_soc_add_controls(codec, wm8350_snd_controls,
ARRAY_SIZE(wm8350_snd_controls));
wm8350_add_widgets(codec);
wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@ -1409,10 +1498,23 @@ card_err:
static int wm8350_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8350 *wm8350 = codec->control_data;
struct wm8350_data *priv = codec->private_data;
int ret;
wm8350_clear_bits(wm8350, WM8350_JACK_DETECT,
WM8350_JDL_ENA | WM8350_JDR_ENA);
wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_4, WM8350_TOCLK_ENA);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_mask_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L);
wm8350_free_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R);
priv->hpl.jack = NULL;
priv->hpr.jack = NULL;
/* cancel any work waiting to be queued. */
ret = cancel_delayed_work(&codec->delayed_work);
@ -1436,6 +1538,16 @@ static int wm8350_remove(struct platform_device *pdev)
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8350_dai_ops = {
.hw_params = wm8350_pcm_hw_params,
.digital_mute = wm8350_mute,
.trigger = wm8350_pcm_trigger,
.set_fmt = wm8350_set_dai_fmt,
.set_sysclk = wm8350_set_dai_sysclk,
.set_pll = wm8350_set_fll,
.set_clkdiv = wm8350_set_clkdiv,
};
struct snd_soc_dai wm8350_dai = {
.name = "WM8350",
.playback = {
@ -1452,15 +1564,7 @@ struct snd_soc_dai wm8350_dai = {
.rates = WM8350_RATES,
.formats = WM8350_FORMATS,
},
.ops = {
.hw_params = wm8350_pcm_hw_params,
.digital_mute = wm8350_mute,
.trigger = wm8350_pcm_trigger,
.set_fmt = wm8350_set_dai_fmt,
.set_sysclk = wm8350_set_dai_sysclk,
.set_pll = wm8350_set_fll,
.set_clkdiv = wm8350_set_clkdiv,
},
.ops = &wm8350_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8350_dai);
@ -1472,7 +1576,7 @@ struct snd_soc_codec_device soc_codec_dev_wm8350 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8350);
static int wm8350_codec_probe(struct platform_device *pdev)
static __devinit int wm8350_codec_probe(struct platform_device *pdev)
{
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
struct wm8350_data *priv;

View File

@ -17,4 +17,12 @@
extern struct snd_soc_dai wm8350_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8350;
enum wm8350_jack {
WM8350_JDL = 1,
WM8350_JDR = 2,
};
int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which,
struct snd_soc_jack *jack, int report);
#endif

1582
sound/soc/codecs/wm8400.c Normal file

File diff suppressed because it is too large Load Diff

62
sound/soc/codecs/wm8400.h Normal file
View File

@ -0,0 +1,62 @@
/*
* wm8400.h -- audio driver for WM8400
*
* Copyright 2008 Wolfson Microelectronics PLC.
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*
* 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.
*
*/
#ifndef _WM8400_CODEC_H
#define _WM8400_CODEC_H
#define WM8400_MCLK_DIV 0
#define WM8400_DACCLK_DIV 1
#define WM8400_ADCCLK_DIV 2
#define WM8400_BCLK_DIV 3
#define WM8400_MCLK_DIV_1 0x400
#define WM8400_MCLK_DIV_2 0x800
#define WM8400_DAC_CLKDIV_1 0x00
#define WM8400_DAC_CLKDIV_1_5 0x04
#define WM8400_DAC_CLKDIV_2 0x08
#define WM8400_DAC_CLKDIV_3 0x0c
#define WM8400_DAC_CLKDIV_4 0x10
#define WM8400_DAC_CLKDIV_5_5 0x14
#define WM8400_DAC_CLKDIV_6 0x18
#define WM8400_ADC_CLKDIV_1 0x00
#define WM8400_ADC_CLKDIV_1_5 0x20
#define WM8400_ADC_CLKDIV_2 0x40
#define WM8400_ADC_CLKDIV_3 0x60
#define WM8400_ADC_CLKDIV_4 0x80
#define WM8400_ADC_CLKDIV_5_5 0xa0
#define WM8400_ADC_CLKDIV_6 0xc0
#define WM8400_BCLK_DIV_1 (0x0 << 1)
#define WM8400_BCLK_DIV_1_5 (0x1 << 1)
#define WM8400_BCLK_DIV_2 (0x2 << 1)
#define WM8400_BCLK_DIV_3 (0x3 << 1)
#define WM8400_BCLK_DIV_4 (0x4 << 1)
#define WM8400_BCLK_DIV_5_5 (0x5 << 1)
#define WM8400_BCLK_DIV_6 (0x6 << 1)
#define WM8400_BCLK_DIV_8 (0x7 << 1)
#define WM8400_BCLK_DIV_11 (0x8 << 1)
#define WM8400_BCLK_DIV_12 (0x9 << 1)
#define WM8400_BCLK_DIV_16 (0xA << 1)
#define WM8400_BCLK_DIV_22 (0xB << 1)
#define WM8400_BCLK_DIV_24 (0xC << 1)
#define WM8400_BCLK_DIV_32 (0xD << 1)
#define WM8400_BCLK_DIV_44 (0xE << 1)
#define WM8400_BCLK_DIV_48 (0xF << 1)
extern struct snd_soc_dai wm8400_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8400;
#endif

View File

@ -171,22 +171,6 @@ SOC_SINGLE("Capture Boost(+20dB)", WM8510_ADCBOOST, 8, 1, 0),
SOC_SINGLE("Mono Playback Switch", WM8510_MONOMIX, 6, 1, 1),
};
/* add non dapm controls */
static int wm8510_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8510_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8510_snd_controls[i], codec,
NULL));
if (err < 0)
return err;
}
return 0;
}
/* Speaker Output Mixer */
static const struct snd_kcontrol_new wm8510_speaker_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8510_SPKMIX, 1, 1, 0),
@ -352,7 +336,7 @@ static int wm8510_set_dai_pll(struct snd_soc_dai *codec_dai,
return 0;
}
pll_factors(freq_out*8, freq_in);
pll_factors(freq_out*4, freq_in);
wm8510_write(codec, WM8510_PLLN, (pll_div.pre_div << 4) | pll_div.n);
wm8510_write(codec, WM8510_PLLK1, pll_div.k >> 18);
@ -383,7 +367,7 @@ static int wm8510_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
wm8510_write(codec, WM8510_GPIO, reg | div);
break;
case WM8510_MCLKDIV:
reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x1f;
reg = wm8510_read_reg_cache(codec, WM8510_CLOCK) & 0x11f;
wm8510_write(codec, WM8510_CLOCK, reg | div);
break;
case WM8510_ADCCLK:
@ -468,7 +452,7 @@ static int wm8510_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 iface = wm8510_read_reg_cache(codec, WM8510_IFACE) & 0x19f;
u16 adn = wm8510_read_reg_cache(codec, WM8510_ADD) & 0x1f1;
@ -570,6 +554,14 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec,
#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops wm8510_dai_ops = {
.hw_params = wm8510_pcm_hw_params,
.digital_mute = wm8510_mute,
.set_fmt = wm8510_set_dai_fmt,
.set_clkdiv = wm8510_set_dai_clkdiv,
.set_pll = wm8510_set_dai_pll,
};
struct snd_soc_dai wm8510_dai = {
.name = "WM8510 HiFi",
.playback = {
@ -584,20 +576,14 @@ struct snd_soc_dai wm8510_dai = {
.channels_max = 2,
.rates = WM8510_RATES,
.formats = WM8510_FORMATS,},
.ops = {
.hw_params = wm8510_pcm_hw_params,
.digital_mute = wm8510_mute,
.set_fmt = wm8510_set_dai_fmt,
.set_clkdiv = wm8510_set_dai_clkdiv,
.set_pll = wm8510_set_dai_pll,
},
.ops = &wm8510_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8510_dai);
static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -606,7 +592,7 @@ static int wm8510_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8510_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -628,7 +614,7 @@ static int wm8510_resume(struct platform_device *pdev)
*/
static int wm8510_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "WM8510";
@ -656,7 +642,8 @@ static int wm8510_init(struct snd_soc_device *socdev)
/* power on device */
codec->bias_level = SND_SOC_BIAS_OFF;
wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8510_add_controls(codec);
snd_soc_add_controls(codec, wm8510_snd_controls,
ARRAY_SIZE(wm8510_snd_controls));
wm8510_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -685,7 +672,7 @@ static int wm8510_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8510_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -766,7 +753,7 @@ err_driver:
static int __devinit wm8510_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8510_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
codec->control_data = spi;
@ -832,7 +819,7 @@ static int wm8510_probe(struct platform_device *pdev)
if (codec == NULL)
return -ENOMEM;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -862,7 +849,7 @@ static int wm8510_probe(struct platform_device *pdev)
static int wm8510_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -1,7 +1,7 @@
/*
* wm8580.c -- WM8580 ALSA Soc Audio driver
*
* Copyright 2008 Wolfson Microelectronics PLC.
* Copyright 2008, 2009 Wolfson Microelectronics PLC.
*
* 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
@ -35,19 +35,6 @@
#include "wm8580.h"
#define WM8580_VERSION "0.1"
struct pll_state {
unsigned int in;
unsigned int out;
};
/* codec private data */
struct wm8580_priv {
struct pll_state a;
struct pll_state b;
};
/* WM8580 register space */
#define WM8580_PLLA1 0x00
#define WM8580_PLLA2 0x01
@ -102,6 +89,8 @@ struct wm8580_priv {
#define WM8580_READBACK 0x34
#define WM8580_RESET 0x35
#define WM8580_MAX_REGISTER 0x35
/* PLLB4 (register 7h) */
#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60
#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20
@ -193,6 +182,20 @@ static const u16 wm8580_reg[] = {
0x0000, 0x0000 /*R53*/
};
struct pll_state {
unsigned int in;
unsigned int out;
};
/* codec private data */
struct wm8580_priv {
struct snd_soc_codec codec;
u16 reg_cache[WM8580_MAX_REGISTER + 1];
struct pll_state a;
struct pll_state b;
};
/*
* read wm8580 register cache
*/
@ -200,7 +203,7 @@ static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
return cache[reg];
}
@ -223,7 +226,7 @@ static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg,
{
u8 data[2];
BUG_ON(reg > ARRAY_SIZE(wm8580_reg));
BUG_ON(reg >= ARRAY_SIZE(wm8580_reg));
/* Registers are 9 bits wide */
value &= 0x1ff;
@ -330,20 +333,6 @@ SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0),
SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0),
};
/* Add non-DAPM controls */
static int wm8580_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8580_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8580_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = {
SND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1),
SND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1),
@ -553,7 +542,7 @@ static int wm8580_paif_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id);
paifb &= ~WM8580_AIF_LENGTH_MASK;
@ -771,8 +760,22 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
case SND_SOC_BIAS_STANDBY:
break;
case SND_SOC_BIAS_STANDBY:
if (codec->bias_level == SND_SOC_BIAS_OFF) {
/* Power up and get individual control of the DACs */
reg = wm8580_read(codec, WM8580_PWRDN1);
reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD);
wm8580_write(codec, WM8580_PWRDN1, reg);
/* Make VMID high impedence */
reg = wm8580_read(codec, WM8580_ADC_CONTROL1);
reg &= ~0x100;
wm8580_write(codec, WM8580_ADC_CONTROL1, reg);
}
break;
case SND_SOC_BIAS_OFF:
reg = wm8580_read(codec, WM8580_PWRDN1);
wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN);
@ -785,6 +788,21 @@ static int wm8580_set_bias_level(struct snd_soc_codec *codec,
#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_ops wm8580_dai_ops_playback = {
.hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
.digital_mute = wm8580_digital_mute,
};
static struct snd_soc_dai_ops wm8580_dai_ops_capture = {
.hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
};
struct snd_soc_dai wm8580_dai[] = {
{
.name = "WM8580 PAIFRX",
@ -796,13 +814,7 @@ struct snd_soc_dai wm8580_dai[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = WM8580_FORMATS,
},
.ops = {
.hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
.digital_mute = wm8580_digital_mute,
},
.ops = &wm8580_dai_ops_playback,
},
{
.name = "WM8580 PAIFTX",
@ -814,109 +826,168 @@ struct snd_soc_dai wm8580_dai[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = WM8580_FORMATS,
},
.ops = {
.hw_params = wm8580_paif_hw_params,
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
},
.ops = &wm8580_dai_ops_capture,
},
};
EXPORT_SYMBOL_GPL(wm8580_dai);
/*
* initialise the WM8580 driver
* register the mixer and dsp interfaces with the kernel
*/
static int wm8580_init(struct snd_soc_device *socdev)
static struct snd_soc_codec *wm8580_codec;
static int wm8580_probe(struct platform_device *pdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
codec->name = "WM8580";
codec->owner = THIS_MODULE;
codec->read = wm8580_read_reg_cache;
codec->write = wm8580_write;
codec->set_bias_level = wm8580_set_bias_level;
codec->dai = wm8580_dai;
codec->num_dai = ARRAY_SIZE(wm8580_dai);
codec->reg_cache_size = ARRAY_SIZE(wm8580_reg);
codec->reg_cache = kmemdup(wm8580_reg, sizeof(wm8580_reg),
GFP_KERNEL);
if (wm8580_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
if (codec->reg_cache == NULL)
return -ENOMEM;
/* Get the codec into a known state */
wm8580_write(codec, WM8580_RESET, 0);
/* Power up and get individual control of the DACs */
wm8580_write(codec, WM8580_PWRDN1, wm8580_read(codec, WM8580_PWRDN1) &
~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD));
/* Make VMID high impedence */
wm8580_write(codec, WM8580_ADC_CONTROL1,
wm8580_read(codec, WM8580_ADC_CONTROL1) & ~0x100);
socdev->card->codec = wm8580_codec;
codec = wm8580_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1,
SNDRV_DEFAULT_STR1);
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8580: failed to create pcms\n");
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
wm8580_add_controls(codec);
snd_soc_add_controls(codec, wm8580_snd_controls,
ARRAY_SIZE(wm8580_snd_controls));
wm8580_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8580: failed to register card\n");
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
return ret;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
/* If the i2c layer weren't so broken, we could pass this kind of data
around */
static struct snd_soc_device *wm8580_socdev;
/* power down chip */
static int wm8580_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_wm8580 = {
.probe = wm8580_probe,
.remove = wm8580_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
static int wm8580_register(struct wm8580_priv *wm8580)
{
int ret, i;
struct snd_soc_codec *codec = &wm8580->codec;
if (wm8580_codec) {
dev_err(codec->dev, "Another WM8580 is registered\n");
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = wm8580;
codec->name = "WM8580";
codec->owner = THIS_MODULE;
codec->read = wm8580_read_reg_cache;
codec->write = wm8580_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8580_set_bias_level;
codec->dai = wm8580_dai;
codec->num_dai = ARRAY_SIZE(wm8580_dai);
codec->reg_cache_size = ARRAY_SIZE(wm8580->reg_cache);
codec->reg_cache = &wm8580->reg_cache;
memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg));
/* Get the codec into a known state */
ret = wm8580_write(codec, WM8580_RESET, 0);
if (ret != 0) {
dev_err(codec->dev, "Failed to reset codec: %d\n", ret);
goto err;
}
for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++)
wm8580_dai[i].dev = codec->dev;
wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8580_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err;
}
ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
goto err_codec;
}
return 0;
err_codec:
snd_soc_unregister_codec(codec);
err:
kfree(wm8580);
return ret;
}
static void wm8580_unregister(struct wm8580_priv *wm8580)
{
wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
snd_soc_unregister_codec(&wm8580->codec);
kfree(wm8580);
wm8580_codec = NULL;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* WM8580 2 wire address is determined by GPIO5
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
static int wm8580_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8580_socdev;
struct snd_soc_codec *codec = socdev->codec;
int ret;
struct wm8580_priv *wm8580;
struct snd_soc_codec *codec;
i2c_set_clientdata(i2c, codec);
wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
if (wm8580 == NULL)
return -ENOMEM;
codec = &wm8580->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8580);
codec->control_data = i2c;
ret = wm8580_init(socdev);
if (ret < 0)
dev_err(&i2c->dev, "failed to initialise WM8580\n");
return ret;
codec->dev = &i2c->dev;
return wm8580_register(wm8580);
}
static int wm8580_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
kfree(codec->reg_cache);
struct wm8580_priv *wm8580 = i2c_get_clientdata(client);
wm8580_unregister(wm8580);
return 0;
}
@ -928,129 +999,35 @@ MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
static struct i2c_driver wm8580_i2c_driver = {
.driver = {
.name = "WM8580 I2C Codec",
.name = "wm8580",
.owner = THIS_MODULE,
},
.probe = wm8580_i2c_probe,
.remove = wm8580_i2c_remove,
.id_table = wm8580_i2c_id,
};
static int wm8580_add_i2c_device(struct platform_device *pdev,
const struct wm8580_setup_data *setup)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
int ret;
ret = i2c_add_driver(&wm8580_i2c_driver);
if (ret != 0) {
dev_err(&pdev->dev, "can't add i2c driver\n");
return ret;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = setup->i2c_address;
strlcpy(info.type, "wm8580", I2C_NAME_SIZE);
adapter = i2c_get_adapter(setup->i2c_bus);
if (!adapter) {
dev_err(&pdev->dev, "can't get i2c adapter %d\n",
setup->i2c_bus);
goto err_driver;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
(unsigned int)info.addr);
goto err_driver;
}
return 0;
err_driver:
i2c_del_driver(&wm8580_i2c_driver);
return -ENODEV;
}
#endif
static int wm8580_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct wm8580_setup_data *setup;
struct snd_soc_codec *codec;
struct wm8580_priv *wm8580;
int ret = 0;
pr_info("WM8580 Audio Codec %s\n", WM8580_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL);
if (wm8580 == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = wm8580;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
wm8580_socdev = socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8580_add_i2c_device(pdev, setup);
}
#else
/* Add other interfaces here */
#endif
return ret;
}
/* power down chip */
static int wm8580_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8580_i2c_driver);
#endif
kfree(codec->private_data);
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_wm8580 = {
.probe = wm8580_probe,
.remove = wm8580_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580);
static int __init wm8580_modinit(void)
{
return snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8580_i2c_driver);
if (ret != 0) {
pr_err("Failed to register WM8580 I2C driver: %d\n", ret);
}
#endif
return 0;
}
module_init(wm8580_modinit);
static void __exit wm8580_exit(void)
{
snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai));
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8580_i2c_driver);
#endif
}
module_exit(wm8580_exit);

View File

@ -28,11 +28,6 @@
#define WM8580_CLKSRC_OSC 4
#define WM8580_CLKSRC_NONE 5
struct wm8580_setup_data {
int i2c_bus;
unsigned short i2c_address;
};
#define WM8580_DAI_PAIFRX 0
#define WM8580_DAI_PAIFTX 1

View File

@ -47,7 +47,7 @@ static inline unsigned int wm8728_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
return cache[reg];
}
@ -55,7 +55,7 @@ static inline void wm8728_write_reg_cache(struct snd_soc_codec *codec,
u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
BUG_ON(reg > ARRAY_SIZE(wm8728_reg_defaults));
BUG_ON(reg >= ARRAY_SIZE(wm8728_reg_defaults));
cache[reg] = value;
}
@ -92,21 +92,6 @@ SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8728_DACLVOL, WM8728_DACRVOL,
SOC_SINGLE("Deemphasis", WM8728_DACCTL, 1, 1, 0),
};
static int wm8728_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8728_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8728_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/*
* DAPM controls.
*/
@ -152,7 +137,7 @@ static int wm8728_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 dac = wm8728_read_reg_cache(codec, WM8728_DACCTL);
dac &= ~0x18;
@ -259,6 +244,12 @@ static int wm8728_set_bias_level(struct snd_soc_codec *codec,
#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8728_dai_ops = {
.hw_params = wm8728_hw_params,
.digital_mute = wm8728_mute,
.set_fmt = wm8728_set_dai_fmt,
};
struct snd_soc_dai wm8728_dai = {
.name = "WM8728",
.playback = {
@ -268,18 +259,14 @@ struct snd_soc_dai wm8728_dai = {
.rates = WM8728_RATES,
.formats = WM8728_FORMATS,
},
.ops = {
.hw_params = wm8728_hw_params,
.digital_mute = wm8728_mute,
.set_fmt = wm8728_set_dai_fmt,
}
.ops = &wm8728_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8728_dai);
static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -289,7 +276,7 @@ static int wm8728_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8728_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8728_set_bias_level(codec, codec->suspend_bias_level);
@ -302,7 +289,7 @@ static int wm8728_resume(struct platform_device *pdev)
*/
static int wm8728_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret = 0;
codec->name = "WM8728";
@ -330,7 +317,8 @@ static int wm8728_init(struct snd_soc_device *socdev)
/* power on device */
wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm8728_add_controls(codec);
snd_soc_add_controls(codec, wm8728_snd_controls,
ARRAY_SIZE(wm8728_snd_controls));
wm8728_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -363,7 +351,7 @@ static int wm8728_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8728_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -444,7 +432,7 @@ err_driver:
static int __devinit wm8728_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8728_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
codec->control_data = spi;
@ -508,7 +496,7 @@ static int wm8728_probe(struct platform_device *pdev)
if (codec == NULL)
return -ENOMEM;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -541,7 +529,7 @@ static int wm8728_probe(struct platform_device *pdev)
static int wm8728_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -29,15 +29,20 @@
#include "wm8731.h"
#define WM8731_VERSION "0.13"
static struct snd_soc_codec *wm8731_codec;
struct snd_soc_codec_device soc_codec_dev_wm8731;
/* codec private data */
struct wm8731_priv {
struct snd_soc_codec codec;
u16 reg_cache[WM8731_CACHEREGNUM];
unsigned int sysclk;
};
#ifdef CONFIG_SPI_MASTER
static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
#endif
/*
* wm8731 register cache
* We can't read the WM8731 register space when we are
@ -129,22 +134,6 @@ SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
};
/* add non dapm controls */
static int wm8731_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8731_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8731_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* Output Mixer */
static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
@ -269,7 +258,7 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8731_priv *wm8731 = codec->private_data;
u16 iface = wm8731_read_reg_cache(codec, WM8731_IFACE) & 0xfff3;
int i = get_coeff(wm8731->sysclk, params_rate(params));
@ -299,7 +288,7 @@ static int wm8731_pcm_prepare(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* set active */
wm8731_write(codec, WM8731_ACTIVE, 0x0001);
@ -312,7 +301,7 @@ static void wm8731_shutdown(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* deactivate */
if (!codec->active) {
@ -414,21 +403,19 @@ static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
static int wm8731_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
u16 reg;
switch (level) {
case SND_SOC_BIAS_ON:
/* vref/mid, osc on, dac unmute */
wm8731_write(codec, WM8731_PWR, reg);
break;
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
/* everything off except vref/vmid, */
/* Clear PWROFF, gate CLKOUT, everything else as-is */
reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
wm8731_write(codec, WM8731_PWR, reg | 0x0040);
break;
case SND_SOC_BIAS_OFF:
/* everything off, dac mute, inactive */
wm8731_write(codec, WM8731_ACTIVE, 0x0);
wm8731_write(codec, WM8731_PWR, 0xffff);
break;
@ -446,6 +433,15 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8731_dai_ops = {
.prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params,
.shutdown = wm8731_shutdown,
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
};
struct snd_soc_dai wm8731_dai = {
.name = "WM8731",
.playback = {
@ -460,21 +456,14 @@ struct snd_soc_dai wm8731_dai = {
.channels_max = 2,
.rates = WM8731_RATES,
.formats = WM8731_FORMATS,},
.ops = {
.prepare = wm8731_pcm_prepare,
.hw_params = wm8731_hw_params,
.shutdown = wm8731_shutdown,
.digital_mute = wm8731_mute,
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
}
.ops = &wm8731_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8731_dai);
static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8731_write(codec, WM8731_ACTIVE, 0x0);
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -484,7 +473,7 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8731_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -500,54 +489,33 @@ static int wm8731_resume(struct platform_device *pdev)
return 0;
}
/*
* initialise the WM8731 driver
* register the mixer and dsp interfaces with the kernel
*/
static int wm8731_init(struct snd_soc_device *socdev)
static int wm8731_probe(struct platform_device *pdev)
{
struct snd_soc_codec *codec = socdev->codec;
int reg, ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
codec->name = "WM8731";
codec->owner = THIS_MODULE;
codec->read = wm8731_read_reg_cache;
codec->write = wm8731_write;
codec->set_bias_level = wm8731_set_bias_level;
codec->dai = &wm8731_dai;
codec->num_dai = 1;
codec->reg_cache_size = ARRAY_SIZE(wm8731_reg);
codec->reg_cache = kmemdup(wm8731_reg, sizeof(wm8731_reg), GFP_KERNEL);
if (codec->reg_cache == NULL)
return -ENOMEM;
if (wm8731_codec == NULL) {
dev_err(&pdev->dev, "Codec device not registered\n");
return -ENODEV;
}
wm8731_reset(codec);
socdev->card->codec = wm8731_codec;
codec = wm8731_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
printk(KERN_ERR "wm8731: failed to create pcms\n");
dev_err(codec->dev, "failed to create pcms: %d\n", ret);
goto pcm_err;
}
/* power on device */
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* set the update bits */
reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
wm8731_add_controls(codec);
snd_soc_add_controls(codec, wm8731_snd_controls,
ARRAY_SIZE(wm8731_snd_controls));
wm8731_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm8731: failed to register card\n");
dev_err(codec->dev, "failed to register card: %d\n", ret);
goto card_err;
}
@ -557,133 +525,109 @@ card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
static struct snd_soc_device *wm8731_socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* WM8731 2 wire address is determined by GPIO5
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
static int wm8731_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
/* power down chip */
static int wm8731_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = wm8731_socdev;
struct snd_soc_codec *codec = socdev->codec;
int ret;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
ret = wm8731_init(socdev);
if (ret < 0)
pr_err("failed to initialise WM8731\n");
return ret;
}
static int wm8731_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
kfree(codec->reg_cache);
return 0;
}
static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 },
{ }
struct snd_soc_codec_device soc_codec_dev_wm8731 = {
.probe = wm8731_probe,
.remove = wm8731_remove,
.suspend = wm8731_suspend,
.resume = wm8731_resume,
};
MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
.name = "WM8731 I2C Codec",
.owner = THIS_MODULE,
},
.probe = wm8731_i2c_probe,
.remove = wm8731_i2c_remove,
.id_table = wm8731_i2c_id,
};
static int wm8731_add_i2c_device(struct platform_device *pdev,
const struct wm8731_setup_data *setup)
static int wm8731_register(struct wm8731_priv *wm8731)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
int ret;
struct snd_soc_codec *codec = &wm8731->codec;
u16 reg;
ret = i2c_add_driver(&wm8731_i2c_driver);
if (ret != 0) {
dev_err(&pdev->dev, "can't add i2c driver\n");
if (wm8731_codec) {
dev_err(codec->dev, "Another WM8731 is registered\n");
return -EINVAL;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->private_data = wm8731;
codec->name = "WM8731";
codec->owner = THIS_MODULE;
codec->read = wm8731_read_reg_cache;
codec->write = wm8731_write;
codec->bias_level = SND_SOC_BIAS_OFF;
codec->set_bias_level = wm8731_set_bias_level;
codec->dai = &wm8731_dai;
codec->num_dai = 1;
codec->reg_cache_size = WM8731_CACHEREGNUM;
codec->reg_cache = &wm8731->reg_cache;
memcpy(codec->reg_cache, wm8731_reg, sizeof(wm8731_reg));
ret = wm8731_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
return ret;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = setup->i2c_address;
strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
wm8731_dai.dev = codec->dev;
adapter = i2c_get_adapter(setup->i2c_bus);
if (!adapter) {
dev_err(&pdev->dev, "can't get i2c adapter %d\n",
setup->i2c_bus);
goto err_driver;
wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* Latch the update bits */
reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V);
wm8731_write(codec, WM8731_LOUT1V, reg & ~0x0100);
reg = wm8731_read_reg_cache(codec, WM8731_ROUT1V);
wm8731_write(codec, WM8731_ROUT1V, reg & ~0x0100);
reg = wm8731_read_reg_cache(codec, WM8731_LINVOL);
wm8731_write(codec, WM8731_LINVOL, reg & ~0x0100);
reg = wm8731_read_reg_cache(codec, WM8731_RINVOL);
wm8731_write(codec, WM8731_RINVOL, reg & ~0x0100);
/* Disable bypass path by default */
reg = wm8731_read_reg_cache(codec, WM8731_APANA);
wm8731_write(codec, WM8731_APANA, reg & ~0x4);
wm8731_codec = codec;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
return ret;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
(unsigned int)info.addr);
goto err_driver;
ret = snd_soc_register_dai(&wm8731_dai);
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAI: %d\n", ret);
snd_soc_unregister_codec(codec);
return ret;
}
return 0;
err_driver:
i2c_del_driver(&wm8731_i2c_driver);
return -ENODEV;
}
#endif
static void wm8731_unregister(struct wm8731_priv *wm8731)
{
wm8731_set_bias_level(&wm8731->codec, SND_SOC_BIAS_OFF);
snd_soc_unregister_dai(&wm8731_dai);
snd_soc_unregister_codec(&wm8731->codec);
kfree(wm8731);
wm8731_codec = NULL;
}
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8731_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8731_socdev;
struct snd_soc_codec *codec = socdev->codec;
int ret;
codec->control_data = spi;
ret = wm8731_init(socdev);
if (ret < 0)
dev_err(&spi->dev, "failed to initialise WM8731\n");
return ret;
}
static int __devexit wm8731_spi_remove(struct spi_device *spi)
{
return 0;
}
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
.remove = __devexit_p(wm8731_spi_remove),
};
static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
{
struct spi_transfer t;
@ -707,101 +651,121 @@ static int wm8731_spi_write(struct spi_device *spi, const char *data, int len)
return len;
}
#endif /* CONFIG_SPI_MASTER */
static int wm8731_probe(struct platform_device *pdev)
static int __devinit wm8731_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct wm8731_setup_data *setup;
struct snd_soc_codec *codec;
struct wm8731_priv *wm8731;
int ret = 0;
pr_info("WM8731 Audio Codec %s", WM8731_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
if (wm8731 == NULL) {
kfree(codec);
if (wm8731 == NULL)
return -ENOMEM;
}
codec->private_data = wm8731;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec = &wm8731->codec;
codec->control_data = spi;
codec->hw_write = (hw_write_t)wm8731_spi_write;
codec->dev = &spi->dev;
wm8731_socdev = socdev;
ret = -ENODEV;
spi->dev.driver_data = wm8731;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8731_add_i2c_device(pdev, setup);
}
#endif
#if defined(CONFIG_SPI_MASTER)
if (setup->spi) {
codec->hw_write = (hw_write_t)wm8731_spi_write;
ret = spi_register_driver(&wm8731_spi_driver);
if (ret != 0)
printk(KERN_ERR "can't add spi driver");
}
#endif
if (ret != 0) {
kfree(codec->private_data);
kfree(codec);
}
return ret;
return wm8731_register(wm8731);
}
/* power down chip */
static int wm8731_remove(struct platform_device *pdev)
static int __devexit wm8731_spi_remove(struct spi_device *spi)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct wm8731_priv *wm8731 = spi->dev.driver_data;
if (codec->control_data)
wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8731_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8731_spi_driver);
#endif
kfree(codec->private_data);
kfree(codec);
wm8731_unregister(wm8731);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_wm8731 = {
.probe = wm8731_probe,
.remove = wm8731_remove,
.suspend = wm8731_suspend,
.resume = wm8731_resume,
static struct spi_driver wm8731_spi_driver = {
.driver = {
.name = "wm8731",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm8731_spi_probe,
.remove = __devexit_p(wm8731_spi_remove),
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8731);
#endif /* CONFIG_SPI_MASTER */
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static __devinit int wm8731_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8731_priv *wm8731;
struct snd_soc_codec *codec;
wm8731 = kzalloc(sizeof(struct wm8731_priv), GFP_KERNEL);
if (wm8731 == NULL)
return -ENOMEM;
codec = &wm8731->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
i2c_set_clientdata(i2c, wm8731);
codec->control_data = i2c;
codec->dev = &i2c->dev;
return wm8731_register(wm8731);
}
static __devexit int wm8731_i2c_remove(struct i2c_client *client)
{
struct wm8731_priv *wm8731 = i2c_get_clientdata(client);
wm8731_unregister(wm8731);
return 0;
}
static const struct i2c_device_id wm8731_i2c_id[] = {
{ "wm8731", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
static struct i2c_driver wm8731_i2c_driver = {
.driver = {
.name = "WM8731 I2C Codec",
.owner = THIS_MODULE,
},
.probe = wm8731_i2c_probe,
.remove = __devexit_p(wm8731_i2c_remove),
.id_table = wm8731_i2c_id,
};
#endif
static int __init wm8731_modinit(void)
{
return snd_soc_register_dai(&wm8731_dai);
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8731_i2c_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8731 I2C driver: %d\n",
ret);
}
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&wm8731_spi_driver);
if (ret != 0) {
printk(KERN_ERR "Failed to register WM8731 SPI driver: %d\n",
ret);
}
#endif
return 0;
}
module_init(wm8731_modinit);
static void __exit wm8731_exit(void)
{
snd_soc_unregister_dai(&wm8731_dai);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8731_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8731_spi_driver);
#endif
}
module_exit(wm8731_exit);

View File

@ -34,12 +34,6 @@
#define WM8731_SYSCLK 0
#define WM8731_DAI 0
struct wm8731_setup_data {
int spi;
int i2c_bus;
unsigned short i2c_address;
};
extern struct snd_soc_dai wm8731_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8731;

View File

@ -231,21 +231,6 @@ SOC_SINGLE("Mono Playback Volume", WM8750_MOUTV, 0, 127, 0),
};
/* add non dapm controls */
static int wm8750_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8750_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8750_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/*
* DAPM Controls
*/
@ -619,7 +604,7 @@ static int wm8750_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8750_priv *wm8750 = codec->private_data;
u16 iface = wm8750_read_reg_cache(codec, WM8750_IFACE) & 0x1f3;
u16 srate = wm8750_read_reg_cache(codec, WM8750_SRATE) & 0x1c0;
@ -694,6 +679,13 @@ static int wm8750_set_bias_level(struct snd_soc_codec *codec,
#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8750_dai_ops = {
.hw_params = wm8750_pcm_hw_params,
.digital_mute = wm8750_mute,
.set_fmt = wm8750_set_dai_fmt,
.set_sysclk = wm8750_set_dai_sysclk,
};
struct snd_soc_dai wm8750_dai = {
.name = "WM8750",
.playback = {
@ -708,12 +700,7 @@ struct snd_soc_dai wm8750_dai = {
.channels_max = 2,
.rates = WM8750_RATES,
.formats = WM8750_FORMATS,},
.ops = {
.hw_params = wm8750_pcm_hw_params,
.digital_mute = wm8750_mute,
.set_fmt = wm8750_set_dai_fmt,
.set_sysclk = wm8750_set_dai_sysclk,
},
.ops = &wm8750_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8750_dai);
@ -727,7 +714,7 @@ static void wm8750_work(struct work_struct *work)
static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -736,7 +723,7 @@ static int wm8750_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8750_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -769,7 +756,7 @@ static int wm8750_resume(struct platform_device *pdev)
*/
static int wm8750_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "WM8750";
@ -816,7 +803,8 @@ static int wm8750_init(struct snd_soc_device *socdev)
reg = wm8750_read_reg_cache(codec, WM8750_RINVOL);
wm8750_write(codec, WM8750_RINVOL, reg | 0x0100);
wm8750_add_controls(codec);
snd_soc_add_controls(codec, wm8750_snd_controls,
ARRAY_SIZE(wm8750_snd_controls));
wm8750_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -850,7 +838,7 @@ static int wm8750_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8750_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -931,7 +919,7 @@ err_driver:
static int __devinit wm8750_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8750_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
codec->control_data = spi;
@ -1003,7 +991,7 @@ static int wm8750_probe(struct platform_device *pdev)
}
codec->private_data = wm8750;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -1057,7 +1045,7 @@ static int run_delayed_work(struct delayed_work *dwork)
static int wm8750_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -51,8 +51,6 @@
#include "wm8753.h"
#define WM8753_VERSION "0.16"
static int caps_charge = 2000;
module_param(caps_charge, int, 0);
MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
@ -60,12 +58,6 @@ MODULE_PARM_DESC(caps_charge, "WM8753 cap charge time (msecs)");
static void wm8753_set_dai_mode(struct snd_soc_codec *codec,
unsigned int mode);
/* codec private data */
struct wm8753_priv {
unsigned int sysclk;
unsigned int pcmclk;
};
/*
* wm8753 register cache
* We can't read the WM8753 register space when we
@ -90,6 +82,14 @@ static const u16 wm8753_reg[] = {
0x0000, 0x0000
};
/* codec private data */
struct wm8753_priv {
unsigned int sysclk;
unsigned int pcmclk;
struct snd_soc_codec codec;
u16 reg_cache[ARRAY_SIZE(wm8753_reg)];
};
/*
* read wm8753 register cache
*/
@ -97,7 +97,7 @@ static inline unsigned int wm8753_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg < 1 || reg > (ARRAY_SIZE(wm8753_reg) + 1))
if (reg < 1 || reg >= (ARRAY_SIZE(wm8753_reg) + 1))
return -1;
return cache[reg - 1];
}
@ -109,7 +109,7 @@ static inline void wm8753_write_reg_cache(struct snd_soc_codec *codec,
unsigned int reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
if (reg < 1 || reg > 0x3f)
if (reg < 1 || reg >= (ARRAY_SIZE(wm8753_reg) + 1))
return;
cache[reg - 1] = value;
}
@ -339,21 +339,6 @@ SOC_ENUM("ADC Data Select", wm8753_enum[27]),
SOC_ENUM("ROUT2 Phase", wm8753_enum[28]),
};
/* add non dapm controls */
static int wm8753_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8753_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8753_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/*
* _DAPM_ Controls
*/
@ -927,7 +912,7 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8753_priv *wm8753 = codec->private_data;
u16 voice = wm8753_read_reg_cache(codec, WM8753_PCM) & 0x01f3;
u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x017f;
@ -1161,7 +1146,7 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8753_priv *wm8753 = codec->private_data;
u16 srate = wm8753_read_reg_cache(codec, WM8753_SRATE1) & 0x01c0;
u16 hifi = wm8753_read_reg_cache(codec, WM8753_HIFI) & 0x01f3;
@ -1316,6 +1301,51 @@ static int wm8753_set_bias_level(struct snd_soc_codec *codec,
* 3. Voice disabled - HIFI over HIFI
* 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
*/
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode1 = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1h_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode1 = {
.hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1v_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_voice_mode2 = {
.hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode2_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode3 = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode4 = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
};
static const struct snd_soc_dai wm8753_all_dai[] = {
/* DAI HiFi mode 1 */
{ .name = "WM8753 HiFi",
@ -1332,14 +1362,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS},
.ops = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1h_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
},
.ops = &wm8753_dai_ops_hifi_mode1,
},
/* DAI Voice mode 1 */
{ .name = "WM8753 Voice",
@ -1356,14 +1379,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
.hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode1v_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
},
.ops = &wm8753_dai_ops_voice_mode1,
},
/* DAI HiFi mode 2 - dummy */
{ .name = "WM8753 HiFi",
@ -1384,14 +1400,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
.hw_params = wm8753_pcm_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode2_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
},
.ops = &wm8753_dai_ops_voice_mode2,
},
/* DAI HiFi mode 3 */
{ .name = "WM8753 HiFi",
@ -1408,14 +1417,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
},
.ops = &wm8753_dai_ops_hifi_mode3,
},
/* DAI Voice mode 3 - dummy */
{ .name = "WM8753 Voice",
@ -1436,14 +1438,7 @@ static const struct snd_soc_dai wm8753_all_dai[] = {
.channels_max = 2,
.rates = WM8753_RATES,
.formats = WM8753_FORMATS,},
.ops = {
.hw_params = wm8753_i2s_hw_params,
.digital_mute = wm8753_mute,
.set_fmt = wm8753_mode3_4_set_dai_fmt,
.set_clkdiv = wm8753_set_dai_clkdiv,
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
},
.ops = &wm8753_dai_ops_hifi_mode4,
},
/* DAI Voice mode 4 - dummy */
{ .name = "WM8753 Voice",
@ -1466,30 +1461,35 @@ static void wm8753_set_dai_mode(struct snd_soc_codec *codec, unsigned int mode)
if (mode < 4) {
int playback_active, capture_active, codec_active, pop_wait;
void *private_data;
struct list_head list;
playback_active = wm8753_dai[0].playback.active;
capture_active = wm8753_dai[0].capture.active;
codec_active = wm8753_dai[0].active;
private_data = wm8753_dai[0].private_data;
pop_wait = wm8753_dai[0].pop_wait;
list = wm8753_dai[0].list;
wm8753_dai[0] = wm8753_all_dai[mode << 1];
wm8753_dai[0].playback.active = playback_active;
wm8753_dai[0].capture.active = capture_active;
wm8753_dai[0].active = codec_active;
wm8753_dai[0].private_data = private_data;
wm8753_dai[0].pop_wait = pop_wait;
wm8753_dai[0].list = list;
playback_active = wm8753_dai[1].playback.active;
capture_active = wm8753_dai[1].capture.active;
codec_active = wm8753_dai[1].active;
private_data = wm8753_dai[1].private_data;
pop_wait = wm8753_dai[1].pop_wait;
list = wm8753_dai[1].list;
wm8753_dai[1] = wm8753_all_dai[(mode << 1) + 1];
wm8753_dai[1].playback.active = playback_active;
wm8753_dai[1].capture.active = capture_active;
wm8753_dai[1].active = codec_active;
wm8753_dai[1].private_data = private_data;
wm8753_dai[1].pop_wait = pop_wait;
wm8753_dai[1].list = list;
}
wm8753_dai[0].codec = codec;
wm8753_dai[1].codec = codec;
@ -1505,7 +1505,7 @@ static void wm8753_work(struct work_struct *work)
static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* we only need to suspend if we are a valid card */
if (!codec->card)
@ -1518,7 +1518,7 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8753_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -1531,6 +1531,11 @@ static int wm8753_resume(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(wm8753_reg); i++) {
if (i + 1 == WM8753_RESET)
continue;
/* No point in writing hardware default values back */
if (cache[i] == wm8753_reg[i])
continue;
data[0] = ((i + 1) << 1) | ((cache[i] >> 8) & 0x0001);
data[1] = cache[i] & 0x00ff;
codec->hw_write(codec->control_data, data, 2);
@ -1549,32 +1554,24 @@ static int wm8753_resume(struct platform_device *pdev)
return 0;
}
/*
* initialise the WM8753 driver
* register the mixer and dsp interfaces with the kernel
*/
static int wm8753_init(struct snd_soc_device *socdev)
static struct snd_soc_codec *wm8753_codec;
static int wm8753_probe(struct platform_device *pdev)
{
struct snd_soc_codec *codec = socdev->codec;
int reg, ret = 0;
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
codec->name = "WM8753";
codec->owner = THIS_MODULE;
codec->read = wm8753_read_reg_cache;
codec->write = wm8753_write;
codec->set_bias_level = wm8753_set_bias_level;
codec->dai = wm8753_dai;
codec->num_dai = 2;
codec->reg_cache_size = ARRAY_SIZE(wm8753_reg);
codec->reg_cache = kmemdup(wm8753_reg, sizeof(wm8753_reg), GFP_KERNEL);
if (!wm8753_codec) {
dev_err(&pdev->dev, "WM8753 codec not yet registered\n");
return -EINVAL;
}
if (codec->reg_cache == NULL)
return -ENOMEM;
socdev->card->codec = wm8753_codec;
codec = wm8753_codec;
wm8753_set_dai_mode(codec, 0);
wm8753_reset(codec);
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0) {
@ -1582,35 +1579,8 @@ static int wm8753_init(struct snd_soc_device *socdev)
goto pcm_err;
}
/* charge output caps */
wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
codec->bias_level = SND_SOC_BIAS_STANDBY;
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(caps_charge));
/* set the update bits */
reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LADC);
wm8753_write(codec, WM8753_LADC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_RADC);
wm8753_write(codec, WM8753_RADC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
wm8753_add_controls(codec);
snd_soc_add_controls(codec, wm8753_snd_controls,
ARRAY_SIZE(wm8753_snd_controls));
wm8753_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -1618,215 +1588,13 @@ static int wm8753_init(struct snd_soc_device *socdev)
goto card_err;
}
return ret;
return 0;
card_err:
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
pcm_err:
kfree(codec->reg_cache);
return ret;
}
/* If the i2c layer weren't so broken, we could pass this kind of data
around */
static struct snd_soc_device *wm8753_socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
/*
* WM8753 2 wire address is determined by GPIO5
* state during powerup.
* low = 0x1a
* high = 0x1b
*/
static int wm8753_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8753_socdev;
struct snd_soc_codec *codec = socdev->codec;
int ret;
i2c_set_clientdata(i2c, codec);
codec->control_data = i2c;
ret = wm8753_init(socdev);
if (ret < 0)
pr_err("failed to initialise WM8753\n");
return ret;
}
static int wm8753_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
kfree(codec->reg_cache);
return 0;
}
static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
static struct i2c_driver wm8753_i2c_driver = {
.driver = {
.name = "WM8753 I2C Codec",
.owner = THIS_MODULE,
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
.id_table = wm8753_i2c_id,
};
static int wm8753_add_i2c_device(struct platform_device *pdev,
const struct wm8753_setup_data *setup)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
int ret;
ret = i2c_add_driver(&wm8753_i2c_driver);
if (ret != 0) {
dev_err(&pdev->dev, "can't add i2c driver\n");
return ret;
}
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = setup->i2c_address;
strlcpy(info.type, "wm8753", I2C_NAME_SIZE);
adapter = i2c_get_adapter(setup->i2c_bus);
if (!adapter) {
dev_err(&pdev->dev, "can't get i2c adapter %d\n",
setup->i2c_bus);
goto err_driver;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
dev_err(&pdev->dev, "can't add i2c device at 0x%x\n",
(unsigned int)info.addr);
goto err_driver;
}
return 0;
err_driver:
i2c_del_driver(&wm8753_i2c_driver);
return -ENODEV;
}
#endif
#if defined(CONFIG_SPI_MASTER)
static int __devinit wm8753_spi_probe(struct spi_device *spi)
{
struct snd_soc_device *socdev = wm8753_socdev;
struct snd_soc_codec *codec = socdev->codec;
int ret;
codec->control_data = spi;
ret = wm8753_init(socdev);
if (ret < 0)
dev_err(&spi->dev, "failed to initialise WM8753\n");
return ret;
}
static int __devexit wm8753_spi_remove(struct spi_device *spi)
{
return 0;
}
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove),
};
static int wm8753_spi_write(struct spi_device *spi, const char *data, int len)
{
struct spi_transfer t;
struct spi_message m;
u8 msg[2];
if (len <= 0)
return 0;
msg[0] = data[0];
msg[1] = data[1];
spi_message_init(&m);
memset(&t, 0, (sizeof t));
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
#endif
static int wm8753_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct wm8753_setup_data *setup;
struct snd_soc_codec *codec;
struct wm8753_priv *wm8753;
int ret = 0;
pr_info("WM8753 Audio Codec %s", WM8753_VERSION);
setup = socdev->codec_data;
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (codec == NULL)
return -ENOMEM;
wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
if (wm8753 == NULL) {
kfree(codec);
return -ENOMEM;
}
codec->private_data = wm8753;
socdev->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
wm8753_socdev = socdev;
INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
if (setup->i2c_address) {
codec->hw_write = (hw_write_t)i2c_master_send;
ret = wm8753_add_i2c_device(pdev, setup);
}
#endif
#if defined(CONFIG_SPI_MASTER)
if (setup->spi) {
codec->hw_write = (hw_write_t)wm8753_spi_write;
ret = spi_register_driver(&wm8753_spi_driver);
if (ret != 0)
printk(KERN_ERR "can't add spi driver");
}
#endif
if (ret != 0) {
kfree(codec->private_data);
kfree(codec);
}
return ret;
}
@ -1853,22 +1621,9 @@ static int run_delayed_work(struct delayed_work *dwork)
static int wm8753_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
if (codec->control_data)
wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF);
run_delayed_work(&codec->delayed_work);
snd_soc_free_pcms(socdev);
snd_soc_dapm_free(socdev);
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_unregister_device(codec->control_data);
i2c_del_driver(&wm8753_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8753_spi_driver);
#endif
kfree(codec->private_data);
kfree(codec);
return 0;
}
@ -1881,15 +1636,240 @@ struct snd_soc_codec_device soc_codec_dev_wm8753 = {
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm8753);
static int wm8753_register(struct wm8753_priv *wm8753)
{
int ret, i;
struct snd_soc_codec *codec = &wm8753->codec;
u16 reg;
if (wm8753_codec) {
dev_err(codec->dev, "Multiple WM8753 devices not supported\n");
ret = -EINVAL;
goto err;
}
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
codec->name = "WM8753";
codec->owner = THIS_MODULE;
codec->read = wm8753_read_reg_cache;
codec->write = wm8753_write;
codec->bias_level = SND_SOC_BIAS_STANDBY;
codec->set_bias_level = wm8753_set_bias_level;
codec->dai = wm8753_dai;
codec->num_dai = 2;
codec->reg_cache_size = ARRAY_SIZE(wm8753->reg_cache);
codec->reg_cache = &wm8753->reg_cache;
codec->private_data = wm8753;
memcpy(codec->reg_cache, wm8753_reg, sizeof(codec->reg_cache));
INIT_DELAYED_WORK(&codec->delayed_work, wm8753_work);
ret = wm8753_reset(codec);
if (ret < 0) {
dev_err(codec->dev, "Failed to issue reset\n");
goto err;
}
/* charge output caps */
wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
schedule_delayed_work(&codec->delayed_work,
msecs_to_jiffies(caps_charge));
/* set the update bits */
reg = wm8753_read_reg_cache(codec, WM8753_LDAC);
wm8753_write(codec, WM8753_LDAC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_RDAC);
wm8753_write(codec, WM8753_RDAC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LADC);
wm8753_write(codec, WM8753_LADC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_RADC);
wm8753_write(codec, WM8753_RADC, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LOUT1V);
wm8753_write(codec, WM8753_LOUT1V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_ROUT1V);
wm8753_write(codec, WM8753_ROUT1V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LOUT2V);
wm8753_write(codec, WM8753_LOUT2V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_ROUT2V);
wm8753_write(codec, WM8753_ROUT2V, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_LINVOL);
wm8753_write(codec, WM8753_LINVOL, reg | 0x0100);
reg = wm8753_read_reg_cache(codec, WM8753_RINVOL);
wm8753_write(codec, WM8753_RINVOL, reg | 0x0100);
wm8753_codec = codec;
for (i = 0; i < ARRAY_SIZE(wm8753_dai); i++)
wm8753_dai[i].dev = codec->dev;
ret = snd_soc_register_codec(codec);
if (ret != 0) {
dev_err(codec->dev, "Failed to register codec: %d\n", ret);
goto err;
}
ret = snd_soc_register_dais(&wm8753_dai[0], ARRAY_SIZE(wm8753_dai));
if (ret != 0) {
dev_err(codec->dev, "Failed to register DAIs: %d\n", ret);
goto err_codec;
}
return 0;
err_codec:
run_delayed_work(&codec->delayed_work);
snd_soc_unregister_codec(codec);
err:
kfree(wm8753);
return ret;
}
static void wm8753_unregister(struct wm8753_priv *wm8753)
{
wm8753_set_bias_level(&wm8753->codec, SND_SOC_BIAS_OFF);
run_delayed_work(&wm8753->codec.delayed_work);
snd_soc_unregister_dais(&wm8753_dai[0], ARRAY_SIZE(wm8753_dai));
snd_soc_unregister_codec(&wm8753->codec);
kfree(wm8753);
wm8753_codec = NULL;
}
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
static int wm8753_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_codec *codec;
struct wm8753_priv *wm8753;
wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
if (wm8753 == NULL)
return -ENOMEM;
codec = &wm8753->codec;
codec->hw_write = (hw_write_t)i2c_master_send;
codec->control_data = i2c;
i2c_set_clientdata(i2c, wm8753);
codec->dev = &i2c->dev;
return wm8753_register(wm8753);
}
static int wm8753_i2c_remove(struct i2c_client *client)
{
struct wm8753_priv *wm8753 = i2c_get_clientdata(client);
wm8753_unregister(wm8753);
return 0;
}
static const struct i2c_device_id wm8753_i2c_id[] = {
{ "wm8753", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
static struct i2c_driver wm8753_i2c_driver = {
.driver = {
.name = "wm8753",
.owner = THIS_MODULE,
},
.probe = wm8753_i2c_probe,
.remove = wm8753_i2c_remove,
.id_table = wm8753_i2c_id,
};
#endif
#if defined(CONFIG_SPI_MASTER)
static int wm8753_spi_write(struct spi_device *spi, const char *data, int len)
{
struct spi_transfer t;
struct spi_message m;
u8 msg[2];
if (len <= 0)
return 0;
msg[0] = data[0];
msg[1] = data[1];
spi_message_init(&m);
memset(&t, 0, (sizeof t));
t.tx_buf = &msg[0];
t.len = len;
spi_message_add_tail(&t, &m);
spi_sync(spi, &m);
return len;
}
static int __devinit wm8753_spi_probe(struct spi_device *spi)
{
struct snd_soc_codec *codec;
struct wm8753_priv *wm8753;
wm8753 = kzalloc(sizeof(struct wm8753_priv), GFP_KERNEL);
if (wm8753 == NULL)
return -ENOMEM;
codec = &wm8753->codec;
codec->control_data = spi;
codec->hw_write = (hw_write_t)wm8753_spi_write;
codec->dev = &spi->dev;
spi->dev.driver_data = wm8753;
return wm8753_register(wm8753);
}
static int __devexit wm8753_spi_remove(struct spi_device *spi)
{
struct wm8753_priv *wm8753 = spi->dev.driver_data;
wm8753_unregister(wm8753);
return 0;
}
static struct spi_driver wm8753_spi_driver = {
.driver = {
.name = "wm8753",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = wm8753_spi_probe,
.remove = __devexit_p(wm8753_spi_remove),
};
#endif
static int __init wm8753_modinit(void)
{
return snd_soc_register_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
int ret;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
ret = i2c_add_driver(&wm8753_i2c_driver);
if (ret != 0)
pr_err("Failed to register WM8753 I2C driver: %d\n", ret);
#endif
#if defined(CONFIG_SPI_MASTER)
ret = spi_register_driver(&wm8753_spi_driver);
if (ret != 0)
pr_err("Failed to register WM8753 SPI driver: %d\n", ret);
#endif
return 0;
}
module_init(wm8753_modinit);
static void __exit wm8753_exit(void)
{
snd_soc_unregister_dais(wm8753_dai, ARRAY_SIZE(wm8753_dai));
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
i2c_del_driver(&wm8753_i2c_driver);
#endif
#if defined(CONFIG_SPI_MASTER)
spi_unregister_driver(&wm8753_spi_driver);
#endif
}
module_exit(wm8753_exit);

View File

@ -77,12 +77,6 @@
#define WM8753_BIASCTL 0x3d
#define WM8753_ADCTL2 0x3f
struct wm8753_setup_data {
int spi;
int i2c_bus;
unsigned short i2c_address;
};
#define WM8753_PLL1 0
#define WM8753_PLL2 1

View File

@ -517,22 +517,6 @@ SOC_SINGLE("LINEOUT2 LP -12dB", WM8900_REG_LOUTMIXCTL1,
};
/* add non dapm controls */
static int wm8900_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8900_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8900_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static const struct snd_kcontrol_new wm8900_dapm_loutput2_control =
SOC_DAPM_SINGLE("LINEOUT2L Switch", WM8900_REG_POWER3, 6, 1, 0);
@ -736,7 +720,7 @@ static int wm8900_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
reg = wm8900_read(codec, WM8900_REG_AUDIO1) & ~0x60;
@ -1104,6 +1088,14 @@ static int wm8900_digital_mute(struct snd_soc_dai *codec_dai, int mute)
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24_LE)
static struct snd_soc_dai_ops wm8900_dai_ops = {
.hw_params = wm8900_hw_params,
.set_clkdiv = wm8900_set_dai_clkdiv,
.set_pll = wm8900_set_dai_pll,
.set_fmt = wm8900_set_dai_fmt,
.digital_mute = wm8900_digital_mute,
};
struct snd_soc_dai wm8900_dai = {
.name = "WM8900 HiFi",
.playback = {
@ -1120,13 +1112,7 @@ struct snd_soc_dai wm8900_dai = {
.rates = WM8900_RATES,
.formats = WM8900_PCM_FORMATS,
},
.ops = {
.hw_params = wm8900_hw_params,
.set_clkdiv = wm8900_set_dai_clkdiv,
.set_pll = wm8900_set_dai_pll,
.set_fmt = wm8900_set_dai_fmt,
.digital_mute = wm8900_digital_mute,
},
.ops = &wm8900_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8900_dai);
@ -1226,7 +1212,7 @@ static int wm8900_set_bias_level(struct snd_soc_codec *codec,
static int wm8900_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8900_priv *wm8900 = codec->private_data;
int fll_out = wm8900->fll_out;
int fll_in = wm8900->fll_in;
@ -1250,7 +1236,7 @@ static int wm8900_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8900_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8900_priv *wm8900 = codec->private_data;
u16 *cache;
int i, ret;
@ -1288,8 +1274,8 @@ static int wm8900_resume(struct platform_device *pdev)
static struct snd_soc_codec *wm8900_codec;
static int wm8900_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
static __devinit int wm8900_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8900_priv *wm8900;
struct snd_soc_codec *codec;
@ -1388,7 +1374,7 @@ err:
return ret;
}
static int wm8900_i2c_remove(struct i2c_client *client)
static __devexit int wm8900_i2c_remove(struct i2c_client *client)
{
snd_soc_unregister_dai(&wm8900_dai);
snd_soc_unregister_codec(wm8900_codec);
@ -1414,7 +1400,7 @@ static struct i2c_driver wm8900_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = wm8900_i2c_probe,
.remove = wm8900_i2c_remove,
.remove = __devexit_p(wm8900_i2c_remove),
.id_table = wm8900_i2c_id,
};
@ -1430,7 +1416,7 @@ static int wm8900_probe(struct platform_device *pdev)
}
codec = wm8900_codec;
socdev->codec = codec;
socdev->card->codec = codec;
/* Register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@ -1439,7 +1425,8 @@ static int wm8900_probe(struct platform_device *pdev)
goto pcm_err;
}
wm8900_add_controls(codec);
snd_soc_add_controls(codec, wm8900_snd_controls,
ARRAY_SIZE(wm8900_snd_controls));
wm8900_add_widgets(codec);
ret = snd_soc_init_card(socdev);

View File

@ -744,21 +744,6 @@ SOC_DOUBLE_R_TLV("Speaker Volume",
0, 63, 0, out_tlv),
};
static int wm8903_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8903_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8903_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
static const struct snd_kcontrol_new linput_mode_mux =
SOC_DAPM_ENUM("Left Input Mode Mux", linput_mode_enum);
@ -1276,7 +1261,7 @@ static int wm8903_startup(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8903_priv *wm8903 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
struct snd_pcm_runtime *master_runtime;
@ -1318,7 +1303,7 @@ static void wm8903_shutdown(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8903_priv *wm8903 = codec->private_data;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@ -1338,7 +1323,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8903_priv *wm8903 = codec->private_data;
struct i2c_client *i2c = codec->control_data;
int fs = params_rate(params);
@ -1512,6 +1497,15 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8903_dai_ops = {
.startup = wm8903_startup,
.shutdown = wm8903_shutdown,
.hw_params = wm8903_hw_params,
.digital_mute = wm8903_digital_mute,
.set_fmt = wm8903_set_dai_fmt,
.set_sysclk = wm8903_set_dai_sysclk,
};
struct snd_soc_dai wm8903_dai = {
.name = "WM8903",
.playback = {
@ -1528,21 +1522,14 @@ struct snd_soc_dai wm8903_dai = {
.rates = WM8903_CAPTURE_RATES,
.formats = WM8903_FORMATS,
},
.ops = {
.startup = wm8903_startup,
.shutdown = wm8903_shutdown,
.hw_params = wm8903_hw_params,
.digital_mute = wm8903_digital_mute,
.set_fmt = wm8903_set_dai_fmt,
.set_sysclk = wm8903_set_dai_sysclk
}
.ops = &wm8903_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8903_dai);
static int wm8903_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
@ -1552,7 +1539,7 @@ static int wm8903_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8903_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct i2c_client *i2c = codec->control_data;
int i;
u16 *reg_cache = codec->reg_cache;
@ -1577,8 +1564,8 @@ static int wm8903_resume(struct platform_device *pdev)
static struct snd_soc_codec *wm8903_codec;
static int wm8903_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
static __devinit int wm8903_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm8903_priv *wm8903;
struct snd_soc_codec *codec;
@ -1684,7 +1671,7 @@ err:
return ret;
}
static int wm8903_i2c_remove(struct i2c_client *client)
static __devexit int wm8903_i2c_remove(struct i2c_client *client)
{
struct snd_soc_codec *codec = i2c_get_clientdata(client);
@ -1714,7 +1701,7 @@ static struct i2c_driver wm8903_i2c_driver = {
.owner = THIS_MODULE,
},
.probe = wm8903_i2c_probe,
.remove = wm8903_i2c_remove,
.remove = __devexit_p(wm8903_i2c_remove),
.id_table = wm8903_i2c_id,
};
@ -1728,7 +1715,7 @@ static int wm8903_probe(struct platform_device *pdev)
goto err;
}
socdev->codec = wm8903_codec;
socdev->card->codec = wm8903_codec;
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
@ -1737,8 +1724,9 @@ static int wm8903_probe(struct platform_device *pdev)
goto err;
}
wm8903_add_controls(socdev->codec);
wm8903_add_widgets(socdev->codec);
snd_soc_add_controls(socdev->card->codec, wm8903_snd_controls,
ARRAY_SIZE(wm8903_snd_controls));
wm8903_add_widgets(socdev->card->codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -1759,7 +1747,7 @@ err:
static int wm8903_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -195,21 +195,6 @@ static const struct snd_kcontrol_new wm8971_snd_controls[] = {
SOC_DOUBLE_R("Mic Boost", WM8971_LADCIN, WM8971_RADCIN, 4, 3, 0),
};
/* add non-DAPM controls */
static int wm8971_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8971_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8971_snd_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/*
* DAPM Controls
*/
@ -546,7 +531,7 @@ static int wm8971_pcm_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm8971_priv *wm8971 = codec->private_data;
u16 iface = wm8971_read_reg_cache(codec, WM8971_IFACE) & 0x1f3;
u16 srate = wm8971_read_reg_cache(codec, WM8971_SRATE) & 0x1c0;
@ -619,6 +604,13 @@ static int wm8971_set_bias_level(struct snd_soc_codec *codec,
#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_ops wm8971_dai_ops = {
.hw_params = wm8971_pcm_hw_params,
.digital_mute = wm8971_mute,
.set_fmt = wm8971_set_dai_fmt,
.set_sysclk = wm8971_set_dai_sysclk,
};
struct snd_soc_dai wm8971_dai = {
.name = "WM8971",
.playback = {
@ -633,12 +625,7 @@ struct snd_soc_dai wm8971_dai = {
.channels_max = 2,
.rates = WM8971_RATES,
.formats = WM8971_FORMATS,},
.ops = {
.hw_params = wm8971_pcm_hw_params,
.digital_mute = wm8971_mute,
.set_fmt = wm8971_set_dai_fmt,
.set_sysclk = wm8971_set_dai_sysclk,
},
.ops = &wm8971_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8971_dai);
@ -652,7 +639,7 @@ static void wm8971_work(struct work_struct *work)
static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -661,7 +648,7 @@ static int wm8971_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8971_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -692,7 +679,7 @@ static int wm8971_resume(struct platform_device *pdev)
static int wm8971_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int reg, ret = 0;
codec->name = "WM8971";
@ -745,7 +732,8 @@ static int wm8971_init(struct snd_soc_device *socdev)
reg = wm8971_read_reg_cache(codec, WM8971_RINVOL);
wm8971_write(codec, WM8971_RINVOL, reg | 0x0100);
wm8971_add_controls(codec);
snd_soc_add_controls(codec, wm8971_snd_controls,
ARRAY_SIZE(wm8971_snd_controls));
wm8971_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -772,7 +760,7 @@ static int wm8971_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8971_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -873,7 +861,7 @@ static int wm8971_probe(struct platform_device *pdev)
}
codec->private_data = wm8971;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -908,7 +896,7 @@ static int wm8971_probe(struct platform_device *pdev)
static int wm8971_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8971_set_bias_level(codec, SND_SOC_BIAS_OFF);

View File

@ -115,7 +115,7 @@ static inline unsigned int wm8990_read_reg_cache(struct snd_soc_codec *codec,
unsigned int reg)
{
u16 *cache = codec->reg_cache;
BUG_ON(reg > (ARRAY_SIZE(wm8990_reg)) - 1);
BUG_ON(reg >= ARRAY_SIZE(wm8990_reg));
return cache[reg];
}
@ -128,7 +128,7 @@ static inline void wm8990_write_reg_cache(struct snd_soc_codec *codec,
u16 *cache = codec->reg_cache;
/* Reset register and reserved registers are uncached */
if (reg == 0 || reg > ARRAY_SIZE(wm8990_reg) - 1)
if (reg == 0 || reg >= ARRAY_SIZE(wm8990_reg))
return;
cache[reg] = value;
@ -418,21 +418,6 @@ SOC_SINGLE("RIN34 Mute Switch", WM8990_RIGHT_LINE_INPUT_3_4_VOLUME,
};
/* add non dapm controls */
static int wm8990_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm8990_snd_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8990_snd_controls[i], codec,
NULL));
if (err < 0)
return err;
}
return 0;
}
/*
* _DAPM_ Controls
*/
@ -1178,7 +1163,7 @@ static int wm8990_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 audio1 = wm8990_read_reg_cache(codec, WM8990_AUDIO_INTERFACE_1);
audio1 &= ~WM8990_AIF_WL_MASK;
@ -1347,6 +1332,15 @@ static int wm8990_set_bias_level(struct snd_soc_codec *codec,
* 1. ADC/DAC on Primary Interface
* 2. ADC on Primary Interface/DAC on secondary
*/
static struct snd_soc_dai_ops wm8990_dai_ops = {
.hw_params = wm8990_hw_params,
.digital_mute = wm8990_mute,
.set_fmt = wm8990_set_dai_fmt,
.set_clkdiv = wm8990_set_dai_clkdiv,
.set_pll = wm8990_set_dai_pll,
.set_sysclk = wm8990_set_dai_sysclk,
};
struct snd_soc_dai wm8990_dai = {
/* ADC/DAC on primary */
.name = "WM8990 ADC/DAC Primary",
@ -1363,21 +1357,14 @@ struct snd_soc_dai wm8990_dai = {
.channels_max = 2,
.rates = WM8990_RATES,
.formats = WM8990_FORMATS,},
.ops = {
.hw_params = wm8990_hw_params,
.digital_mute = wm8990_mute,
.set_fmt = wm8990_set_dai_fmt,
.set_clkdiv = wm8990_set_dai_clkdiv,
.set_pll = wm8990_set_dai_pll,
.set_sysclk = wm8990_set_dai_sysclk,
},
.ops = &wm8990_dai_ops,
};
EXPORT_SYMBOL_GPL(wm8990_dai);
static int wm8990_suspend(struct platform_device *pdev, pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
/* we only need to suspend if we are a valid card */
if (!codec->card)
@ -1390,7 +1377,7 @@ static int wm8990_suspend(struct platform_device *pdev, pm_message_t state)
static int wm8990_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i;
u8 data[2];
u16 *cache = codec->reg_cache;
@ -1418,7 +1405,7 @@ static int wm8990_resume(struct platform_device *pdev)
*/
static int wm8990_init(struct snd_soc_device *socdev)
{
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
int ret = 0;
@ -1461,7 +1448,8 @@ static int wm8990_init(struct snd_soc_device *socdev)
wm8990_write(codec, WM8990_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
wm8990_write(codec, WM8990_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
wm8990_add_controls(codec);
snd_soc_add_controls(codec, wm8990_snd_controls,
ARRAY_SIZE(wm8990_snd_controls));
wm8990_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -1495,7 +1483,7 @@ static int wm8990_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct snd_soc_device *socdev = wm8990_socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int ret;
i2c_set_clientdata(i2c, codec);
@ -1594,7 +1582,7 @@ static int wm8990_probe(struct platform_device *pdev)
}
codec->private_data = wm8990;
socdev->codec = codec;
socdev->card->codec = codec;
mutex_init(&codec->mutex);
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
@ -1620,7 +1608,7 @@ static int wm8990_probe(struct platform_device *pdev)
static int wm8990_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec->control_data)
wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);

415
sound/soc/codecs/wm9705.c Normal file
View File

@ -0,0 +1,415 @@
/*
* wm9705.c -- ALSA Soc WM9705 codec support
*
* Copyright 2008 Ian Molton <spyro@f2s.com>
*
* 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; Version 2 of the License only.
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "wm9705.h"
/*
* WM9705 register cache
*/
static const u16 wm9705_reg[] = {
0x6150, 0x8000, 0x8000, 0x8000, /* 0x0 */
0x0000, 0x8000, 0x8008, 0x8008, /* 0x8 */
0x8808, 0x8808, 0x8808, 0x8808, /* 0x10 */
0x8808, 0x0000, 0x8000, 0x0000, /* 0x18 */
0x0000, 0x0000, 0x0000, 0x000f, /* 0x20 */
0x0605, 0x0000, 0xbb80, 0x0000, /* 0x28 */
0x0000, 0xbb80, 0x0000, 0x0000, /* 0x30 */
0x0000, 0x2000, 0x0000, 0x0000, /* 0x38 */
0x0000, 0x0000, 0x0000, 0x0000, /* 0x40 */
0x0000, 0x0000, 0x0000, 0x0000, /* 0x48 */
0x0000, 0x0000, 0x0000, 0x0000, /* 0x50 */
0x0000, 0x0000, 0x0000, 0x0000, /* 0x58 */
0x0000, 0x0000, 0x0000, 0x0000, /* 0x60 */
0x0000, 0x0000, 0x0000, 0x0000, /* 0x68 */
0x0000, 0x0808, 0x0000, 0x0006, /* 0x70 */
0x0000, 0x0000, 0x574d, 0x4c05, /* 0x78 */
};
static const struct snd_kcontrol_new wm9705_snd_ac97_controls[] = {
SOC_DOUBLE("Master Playback Volume", AC97_MASTER, 8, 0, 31, 1),
SOC_SINGLE("Master Playback Switch", AC97_MASTER, 15, 1, 1),
SOC_DOUBLE("Headphone Playback Volume", AC97_HEADPHONE, 8, 0, 31, 1),
SOC_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
SOC_SINGLE("PCM Playback Switch", AC97_PCM, 15, 1, 1),
SOC_SINGLE("Mono Playback Volume", AC97_MASTER_MONO, 0, 31, 1),
SOC_SINGLE("Mono Playback Switch", AC97_MASTER_MONO, 15, 1, 1),
SOC_SINGLE("PCBeep Playback Volume", AC97_PC_BEEP, 1, 15, 1),
SOC_SINGLE("Phone Playback Volume", AC97_PHONE, 0, 31, 1),
SOC_DOUBLE("Line Playback Volume", AC97_LINE, 8, 0, 31, 1),
SOC_DOUBLE("CD Playback Volume", AC97_CD, 8, 0, 31, 1),
SOC_SINGLE("Mic Playback Volume", AC97_MIC, 0, 31, 1),
SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 6, 1, 0),
SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0),
SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
};
static const char *wm9705_mic[] = {"Mic 1", "Mic 2"};
static const char *wm9705_rec_sel[] = {"Mic", "CD", "NC", "NC",
"Line", "Stereo Mix", "Mono Mix", "Phone"};
static const struct soc_enum wm9705_enum_mic =
SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 8, 2, wm9705_mic);
static const struct soc_enum wm9705_enum_rec_l =
SOC_ENUM_SINGLE(AC97_REC_SEL, 8, 8, wm9705_rec_sel);
static const struct soc_enum wm9705_enum_rec_r =
SOC_ENUM_SINGLE(AC97_REC_SEL, 0, 8, wm9705_rec_sel);
/* Headphone Mixer */
static const struct snd_kcontrol_new wm9705_hp_mixer_controls[] = {
SOC_DAPM_SINGLE("PCBeep Playback Switch", AC97_PC_BEEP, 15, 1, 1),
SOC_DAPM_SINGLE("CD Playback Switch", AC97_CD, 15, 1, 1),
SOC_DAPM_SINGLE("Mic Playback Switch", AC97_MIC, 15, 1, 1),
SOC_DAPM_SINGLE("Phone Playback Switch", AC97_PHONE, 15, 1, 1),
SOC_DAPM_SINGLE("Line Playback Switch", AC97_LINE, 15, 1, 1),
};
/* Mic source */
static const struct snd_kcontrol_new wm9705_mic_src_controls =
SOC_DAPM_ENUM("Route", wm9705_enum_mic);
/* Capture source */
static const struct snd_kcontrol_new wm9705_capture_selectl_controls =
SOC_DAPM_ENUM("Route", wm9705_enum_rec_l);
static const struct snd_kcontrol_new wm9705_capture_selectr_controls =
SOC_DAPM_ENUM("Route", wm9705_enum_rec_r);
/* DAPM widgets */
static const struct snd_soc_dapm_widget wm9705_dapm_widgets[] = {
SND_SOC_DAPM_MUX("Mic Source", SND_SOC_NOPM, 0, 0,
&wm9705_mic_src_controls),
SND_SOC_DAPM_MUX("Left Capture Source", SND_SOC_NOPM, 0, 0,
&wm9705_capture_selectl_controls),
SND_SOC_DAPM_MUX("Right Capture Source", SND_SOC_NOPM, 0, 0,
&wm9705_capture_selectr_controls),
SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback",
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback",
SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MIXER_NAMED_CTL("HP Mixer", SND_SOC_NOPM, 0, 0,
&wm9705_hp_mixer_controls[0],
ARRAY_SIZE(wm9705_hp_mixer_controls)),
SND_SOC_DAPM_MIXER("Mono Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PGA("Headphone PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Speaker PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Line PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Line out PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mono PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Phone PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("Mic PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("PCBEEP PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("CD PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("ADC PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
SND_SOC_DAPM_OUTPUT("HPOUTR"),
SND_SOC_DAPM_OUTPUT("LOUT"),
SND_SOC_DAPM_OUTPUT("ROUT"),
SND_SOC_DAPM_OUTPUT("MONOOUT"),
SND_SOC_DAPM_INPUT("PHONE"),
SND_SOC_DAPM_INPUT("LINEINL"),
SND_SOC_DAPM_INPUT("LINEINR"),
SND_SOC_DAPM_INPUT("CDINL"),
SND_SOC_DAPM_INPUT("CDINR"),
SND_SOC_DAPM_INPUT("PCBEEP"),
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
/* Audio map
* WM9705 has no switches to disable the route from the inputs to the HP mixer
* so in order to prevent active inputs from forcing the audio outputs to be
* constantly enabled, we use the mutes on those inputs to simulate such
* controls.
*/
static const struct snd_soc_dapm_route audio_map[] = {
/* HP mixer */
{"HP Mixer", "PCBeep Playback Switch", "PCBEEP PGA"},
{"HP Mixer", "CD Playback Switch", "CD PGA"},
{"HP Mixer", "Mic Playback Switch", "Mic PGA"},
{"HP Mixer", "Phone Playback Switch", "Phone PGA"},
{"HP Mixer", "Line Playback Switch", "Line PGA"},
{"HP Mixer", NULL, "Left DAC"},
{"HP Mixer", NULL, "Right DAC"},
/* mono mixer */
{"Mono Mixer", NULL, "HP Mixer"},
/* outputs */
{"Headphone PGA", NULL, "HP Mixer"},
{"HPOUTL", NULL, "Headphone PGA"},
{"HPOUTR", NULL, "Headphone PGA"},
{"Line out PGA", NULL, "HP Mixer"},
{"LOUT", NULL, "Line out PGA"},
{"ROUT", NULL, "Line out PGA"},
{"Mono PGA", NULL, "Mono Mixer"},
{"MONOOUT", NULL, "Mono PGA"},
/* inputs */
{"CD PGA", NULL, "CDINL"},
{"CD PGA", NULL, "CDINR"},
{"Line PGA", NULL, "LINEINL"},
{"Line PGA", NULL, "LINEINR"},
{"Phone PGA", NULL, "PHONE"},
{"Mic Source", "Mic 1", "MIC1"},
{"Mic Source", "Mic 2", "MIC2"},
{"Mic PGA", NULL, "Mic Source"},
{"PCBEEP PGA", NULL, "PCBEEP"},
/* Left capture selector */
{"Left Capture Source", "Mic", "Mic Source"},
{"Left Capture Source", "CD", "CDINL"},
{"Left Capture Source", "Line", "LINEINL"},
{"Left Capture Source", "Stereo Mix", "HP Mixer"},
{"Left Capture Source", "Mono Mix", "HP Mixer"},
{"Left Capture Source", "Phone", "PHONE"},
/* Right capture source */
{"Right Capture Source", "Mic", "Mic Source"},
{"Right Capture Source", "CD", "CDINR"},
{"Right Capture Source", "Line", "LINEINR"},
{"Right Capture Source", "Stereo Mix", "HP Mixer"},
{"Right Capture Source", "Mono Mix", "HP Mixer"},
{"Right Capture Source", "Phone", "PHONE"},
{"ADC PGA", NULL, "Left Capture Source"},
{"ADC PGA", NULL, "Right Capture Source"},
/* ADC's */
{"Left ADC", NULL, "ADC PGA"},
{"Right ADC", NULL, "ADC PGA"},
};
static int wm9705_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(codec, wm9705_dapm_widgets,
ARRAY_SIZE(wm9705_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_new_widgets(codec);
return 0;
}
/* We use a register cache to enhance read performance. */
static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
{
u16 *cache = codec->reg_cache;
switch (reg) {
case AC97_RESET:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
return soc_ac97_ops.read(codec->ac97, reg);
default:
reg = reg >> 1;
if (reg >= (ARRAY_SIZE(wm9705_reg)))
return -EIO;
return cache[reg];
}
}
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
u16 *cache = codec->reg_cache;
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9705_reg)))
cache[reg] = val;
return 0;
}
static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->card->codec;
int reg;
u16 vra;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = AC97_PCM_FRONT_DAC_RATE;
else
reg = AC97_PCM_LR_ADC_RATE;
return ac97_write(codec, reg, runtime->rate);
}
#define WM9705_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000)
static struct snd_soc_dai_ops wm9705_dai_ops = {
.prepare = ac97_prepare,
};
struct snd_soc_dai wm9705_dai[] = {
{
.name = "AC97 HiFi",
.ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM9705_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM9705_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.ops = &wm9705_dai_ops,
},
{
.name = "AC97 Aux",
.playback = {
.stream_name = "Aux Playback",
.channels_min = 1,
.channels_max = 1,
.rates = WM9705_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
}
};
EXPORT_SYMBOL_GPL(wm9705_dai);
static int wm9705_reset(struct snd_soc_codec *codec)
{
if (soc_ac97_ops.reset) {
soc_ac97_ops.reset(codec->ac97);
if (ac97_read(codec, 0) == wm9705_reg[0])
return 0; /* Success */
}
return -EIO;
}
static int wm9705_soc_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec;
int ret = 0;
printk(KERN_INFO "WM9705 SoC Audio Codec\n");
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
GFP_KERNEL);
if (socdev->card->codec == NULL)
return -ENOMEM;
codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache = kmemdup(wm9705_reg, sizeof(wm9705_reg), GFP_KERNEL);
if (codec->reg_cache == NULL) {
ret = -ENOMEM;
goto cache_err;
}
codec->reg_cache_size = sizeof(wm9705_reg);
codec->reg_cache_step = 2;
codec->name = "WM9705";
codec->owner = THIS_MODULE;
codec->dai = wm9705_dai;
codec->num_dai = ARRAY_SIZE(wm9705_dai);
codec->write = ac97_write;
codec->read = ac97_read;
INIT_LIST_HEAD(&codec->dapm_widgets);
INIT_LIST_HEAD(&codec->dapm_paths);
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
goto codec_err;
}
/* register pcms */
ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
if (ret < 0)
goto pcm_err;
ret = wm9705_reset(codec);
if (ret)
goto reset_err;
snd_soc_add_controls(codec, wm9705_snd_ac97_controls,
ARRAY_SIZE(wm9705_snd_ac97_controls));
wm9705_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register card\n");
goto pcm_err;
}
return 0;
reset_err:
snd_soc_free_pcms(socdev);
pcm_err:
snd_soc_free_ac97_codec(codec);
codec_err:
kfree(codec->reg_cache);
cache_err:
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
static int wm9705_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;
snd_soc_dapm_free(socdev);
snd_soc_free_pcms(socdev);
snd_soc_free_ac97_codec(codec);
kfree(codec->reg_cache);
kfree(codec);
return 0;
}
struct snd_soc_codec_device soc_codec_dev_wm9705 = {
.probe = wm9705_soc_probe,
.remove = wm9705_soc_remove,
};
EXPORT_SYMBOL_GPL(soc_codec_dev_wm9705);
MODULE_DESCRIPTION("ASoC WM9705 driver");
MODULE_AUTHOR("Ian Molton");
MODULE_LICENSE("GPL v2");

14
sound/soc/codecs/wm9705.h Normal file
View File

@ -0,0 +1,14 @@
/*
* wm9705.h -- WM9705 Soc Audio driver
*/
#ifndef _WM9705_H
#define _WM9705_H
#define WM9705_DAI_AC97_HIFI 0
#define WM9705_DAI_AC97_AUX 1
extern struct snd_soc_dai wm9705_dai[2];
extern struct snd_soc_codec_device soc_codec_dev_wm9705;
#endif

View File

@ -154,21 +154,6 @@ SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
};
/* add non dapm controls */
static int wm9712_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm9712_snd_ac97_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm9712_snd_ac97_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
* This makes it impossible to determine the audio path.
@ -467,7 +452,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
else {
reg = reg >> 1;
if (reg > (ARRAY_SIZE(wm9712_reg)))
if (reg >= (ARRAY_SIZE(wm9712_reg)))
return -EIO;
return cache[reg];
@ -481,7 +466,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg <= (ARRAY_SIZE(wm9712_reg)))
if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
return 0;
@ -493,7 +478,7 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int reg;
u16 vra;
@ -514,7 +499,7 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_device *socdev = rtd->socdev;
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 vra, xsle;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
@ -532,6 +517,14 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000)
static struct snd_soc_dai_ops wm9712_dai_ops_hifi = {
.prepare = ac97_prepare,
};
static struct snd_soc_dai_ops wm9712_dai_ops_aux = {
.prepare = ac97_aux_prepare,
};
struct snd_soc_dai wm9712_dai[] = {
{
.name = "AC97 HiFi",
@ -548,8 +541,7 @@ struct snd_soc_dai wm9712_dai[] = {
.channels_max = 2,
.rates = WM9712_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.prepare = ac97_prepare,},
.ops = &wm9712_dai_ops_hifi,
},
{
.name = "AC97 Aux",
@ -559,8 +551,7 @@ struct snd_soc_dai wm9712_dai[] = {
.channels_max = 1,
.rates = WM9712_AC97_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.prepare = ac97_aux_prepare,},
.ops = &wm9712_dai_ops_aux,
}
};
EXPORT_SYMBOL_GPL(wm9712_dai);
@ -607,7 +598,7 @@ static int wm9712_soc_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
@ -616,7 +607,7 @@ static int wm9712_soc_suspend(struct platform_device *pdev,
static int wm9712_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
int i, ret;
u16 *cache = codec->reg_cache;
@ -652,10 +643,11 @@ static int wm9712_soc_probe(struct platform_device *pdev)
printk(KERN_INFO "WM9711/WM9712 SoC Audio Codec %s\n", WM9712_VERSION);
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (socdev->codec == NULL)
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
GFP_KERNEL);
if (socdev->card->codec == NULL)
return -ENOMEM;
codec = socdev->codec;
codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache = kmemdup(wm9712_reg, sizeof(wm9712_reg), GFP_KERNEL);
@ -698,7 +690,8 @@ static int wm9712_soc_probe(struct platform_device *pdev)
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
wm9712_add_controls(codec);
snd_soc_add_controls(codec, wm9712_snd_ac97_controls,
ARRAY_SIZE(wm9712_snd_ac97_controls));
wm9712_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@ -718,15 +711,15 @@ codec_err:
kfree(codec->reg_cache);
cache_err:
kfree(socdev->codec);
socdev->codec = NULL;
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
static int wm9712_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;

View File

@ -32,7 +32,6 @@
struct wm9713_priv {
u32 pll_in; /* PLL input frequency */
u32 pll_out; /* PLL output frequency */
};
static unsigned int ac97_read(struct snd_soc_codec *codec,
@ -190,21 +189,6 @@ SOC_SINGLE("3D Lower Cut-off Switch", AC97_REC_GAIN_MIC, 4, 1, 0),
SOC_SINGLE("3D Depth", AC97_REC_GAIN_MIC, 0, 15, 1),
};
/* add non dapm controls */
static int wm9713_add_controls(struct snd_soc_codec *codec)
{
int err, i;
for (i = 0; i < ARRAY_SIZE(wm9713_snd_ac97_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm9713_snd_ac97_controls[i],
codec, NULL));
if (err < 0)
return err;
}
return 0;
}
/* We have to create a fake left and right HP mixers because
* the codec only has a single control that is shared by both channels.
* This makes it impossible to determine the audio path using the current
@ -636,7 +620,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
else {
reg = reg >> 1;
if (reg > (ARRAY_SIZE(wm9713_reg)))
if (reg >= (ARRAY_SIZE(wm9713_reg)))
return -EIO;
return cache[reg];
@ -650,7 +634,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
if (reg < 0x7c)
soc_ac97_ops.write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg <= (ARRAY_SIZE(wm9713_reg)))
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
return 0;
@ -738,13 +722,13 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
struct _pll_div pll_div;
/* turn PLL off ? */
if (freq_in == 0 || freq_out == 0) {
if (freq_in == 0) {
/* disable PLL power and select ext source */
reg = ac97_read(codec, AC97_HANDSET_RATE);
ac97_write(codec, AC97_HANDSET_RATE, reg | 0x0080);
reg = ac97_read(codec, AC97_EXTENDED_MID);
ac97_write(codec, AC97_EXTENDED_MID, reg | 0x0200);
wm9713->pll_out = 0;
wm9713->pll_in = 0;
return 0;
}
@ -788,7 +772,6 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
ac97_write(codec, AC97_EXTENDED_MID, reg & 0xfdff);
reg = ac97_read(codec, AC97_HANDSET_RATE);
ac97_write(codec, AC97_HANDSET_RATE, reg & 0xff7f);
wm9713->pll_out = freq_out;
wm9713->pll_in = freq_in;
/* wait 10ms AC97 link frames for the link to stabilise */
@ -957,13 +940,14 @@ static void wm9713_voiceshutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
u16 status;
u16 status, rate;
/* Gracefully shut down the voice interface. */
status = ac97_read(codec, AC97_EXTENDED_STATUS) | 0x1000;
ac97_write(codec, AC97_HANDSET_RATE, 0x0280);
rate = ac97_read(codec, AC97_HANDSET_RATE) & 0xF0FF;
ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0200);
schedule_timeout_interruptible(msecs_to_jiffies(1));
ac97_write(codec, AC97_HANDSET_RATE, 0x0F80);
ac97_write(codec, AC97_HANDSET_RATE, rate | 0x0F00);
ac97_write(codec, AC97_EXTENDED_MID, status);
}
@ -1021,6 +1005,27 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
(SNDRV_PCM_FORMAT_S16_LE | SNDRV_PCM_FORMAT_S20_3LE | \
SNDRV_PCM_FORMAT_S24_LE)
static struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
.prepare = ac97_hifi_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,
};
static struct snd_soc_dai_ops wm9713_dai_ops_aux = {
.prepare = ac97_aux_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,
};
static struct snd_soc_dai_ops wm9713_dai_ops_voice = {
.hw_params = wm9713_pcm_hw_params,
.shutdown = wm9713_voiceshutdown,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,
.set_fmt = wm9713_set_dai_fmt,
.set_tristate = wm9713_set_dai_tristate,
};
struct snd_soc_dai wm9713_dai[] = {
{
.name = "AC97 HiFi",
@ -1037,10 +1042,7 @@ struct snd_soc_dai wm9713_dai[] = {
.channels_max = 2,
.rates = WM9713_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.prepare = ac97_hifi_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,},
.ops = &wm9713_dai_ops_hifi,
},
{
.name = "AC97 Aux",
@ -1050,10 +1052,7 @@ struct snd_soc_dai wm9713_dai[] = {
.channels_max = 1,
.rates = WM9713_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.prepare = ac97_aux_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,},
.ops = &wm9713_dai_ops_aux,
},
{
.name = "WM9713 Voice",
@ -1069,14 +1068,7 @@ struct snd_soc_dai wm9713_dai[] = {
.channels_max = 2,
.rates = WM9713_PCM_RATES,
.formats = WM9713_PCM_FORMATS,},
.ops = {
.hw_params = wm9713_pcm_hw_params,
.shutdown = wm9713_voiceshutdown,
.set_clkdiv = wm9713_set_dai_clkdiv,
.set_pll = wm9713_set_dai_pll,
.set_fmt = wm9713_set_dai_fmt,
.set_tristate = wm9713_set_dai_tristate,
},
.ops = &wm9713_dai_ops_voice,
},
};
EXPORT_SYMBOL_GPL(wm9713_dai);
@ -1132,7 +1124,7 @@ static int wm9713_soc_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
u16 reg;
/* Disable everything except touchpanel - that will be handled
@ -1150,7 +1142,7 @@ static int wm9713_soc_suspend(struct platform_device *pdev,
static int wm9713_soc_resume(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
struct wm9713_priv *wm9713 = codec->private_data;
int i, ret;
u16 *cache = codec->reg_cache;
@ -1164,8 +1156,8 @@ static int wm9713_soc_resume(struct platform_device *pdev)
wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
/* do we need to re-start the PLL ? */
if (wm9713->pll_out)
wm9713_set_pll(codec, 0, wm9713->pll_in, wm9713->pll_out);
if (wm9713->pll_in)
wm9713_set_pll(codec, 0, wm9713->pll_in, 0);
/* only synchronise the codec if warm reset failed */
if (ret == 0) {
@ -1191,10 +1183,11 @@ static int wm9713_soc_probe(struct platform_device *pdev)
printk(KERN_INFO "WM9713/WM9714 SoC Audio Codec %s\n", WM9713_VERSION);
socdev->codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
if (socdev->codec == NULL)
socdev->card->codec = kzalloc(sizeof(struct snd_soc_codec),
GFP_KERNEL);
if (socdev->card->codec == NULL)
return -ENOMEM;
codec = socdev->codec;
codec = socdev->card->codec;
mutex_init(&codec->mutex);
codec->reg_cache = kmemdup(wm9713_reg, sizeof(wm9713_reg), GFP_KERNEL);
@ -1245,7 +1238,8 @@ static int wm9713_soc_probe(struct platform_device *pdev)
reg = ac97_read(codec, AC97_CD) & 0x7fff;
ac97_write(codec, AC97_CD, reg);
wm9713_add_controls(codec);
snd_soc_add_controls(codec, wm9713_snd_ac97_controls,
ARRAY_SIZE(wm9713_snd_ac97_controls));
wm9713_add_widgets(codec);
ret = snd_soc_init_card(socdev);
if (ret < 0)
@ -1265,15 +1259,15 @@ priv_err:
kfree(codec->reg_cache);
cache_err:
kfree(socdev->codec);
socdev->codec = NULL;
kfree(socdev->card->codec);
socdev->card->codec = NULL;
return ret;
}
static int wm9713_soc_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = socdev->codec;
struct snd_soc_codec *codec = socdev->card->codec;
if (codec == NULL)
return 0;

View File

@ -20,7 +20,7 @@ config SND_DAVINCI_SOC_EVM
config SND_DAVINCI_SOC_SFFSDR
tristate "SoC Audio support for SFFSDR"
depends on SND_DAVINCI_SOC && MACH_DAVINCI_SFFSDR
depends on SND_DAVINCI_SOC && MACH_SFFSDR
select SND_DAVINCI_SOC_I2S
select SND_SOC_PCM3008
select SFFSDR_FPGA

View File

@ -186,7 +186,8 @@ static int __init evm_init(void)
platform_set_drvdata(evm_snd_device, &evm_snd_devdata);
evm_snd_devdata.dev = &evm_snd_device->dev;
evm_snd_device->dev.platform_data = &evm_snd_data;
platform_device_add_data(evm_snd_device, &evm_snd_data,
sizeof(evm_snd_data));
ret = platform_device_add_resources(evm_snd_device, evm_snd_resources,
ARRAY_SIZE(evm_snd_resources));

View File

@ -499,6 +499,13 @@ static void davinci_i2s_remove(struct platform_device *pdev,
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
static struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.startup = davinci_i2s_startup,
.trigger = davinci_i2s_trigger,
.hw_params = davinci_i2s_hw_params,
.set_fmt = davinci_i2s_set_dai_fmt,
};
struct snd_soc_dai davinci_i2s_dai = {
.name = "davinci-i2s",
.id = 0,
@ -514,12 +521,7 @@ struct snd_soc_dai davinci_i2s_dai = {
.channels_max = 2,
.rates = DAVINCI_I2S_RATES,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.ops = {
.startup = davinci_i2s_startup,
.trigger = davinci_i2s_trigger,
.hw_params = davinci_i2s_hw_params,
.set_fmt = davinci_i2s_set_dai_fmt,
},
.ops = &davinci_i2s_dai_ops,
};
EXPORT_SYMBOL_GPL(davinci_i2s_dai);

View File

@ -286,7 +286,7 @@ static int davinci_pcm_mmap(struct snd_pcm_substream *substream,
runtime->dma_bytes);
}
struct snd_pcm_ops davinci_pcm_ops = {
static struct snd_pcm_ops davinci_pcm_ops = {
.open = davinci_pcm_open,
.close = davinci_pcm_close,
.ioctl = snd_pcm_lib_ioctl,

View File

@ -25,7 +25,9 @@
#include <asm/dma.h>
#include <asm/mach-types.h>
#ifdef CONFIG_SFFSDR_FPGA
#include <asm/plat-sffsdr/sffsdr-fpga.h>
#endif
#include <mach/mcbsp.h>
#include <mach/edma.h>
@ -34,31 +36,45 @@
#include "davinci-pcm.h"
#include "davinci-i2s.h"
/*
* CLKX and CLKR are the inputs for the Sample Rate Generator.
* FSX and FSR are outputs, driven by the sample Rate Generator.
*/
#define AUDIO_FORMAT (SND_SOC_DAIFMT_DSP_B | \
SND_SOC_DAIFMT_CBM_CFS | \
SND_SOC_DAIFMT_IB_NF)
static int sffsdr_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
int fs;
int ret = 0;
/* Set cpu DAI configuration:
* CLKX and CLKR are the inputs for the Sample Rate Generator.
* FSX and FSR are outputs, driven by the sample Rate Generator. */
ret = snd_soc_dai_set_fmt(cpu_dai,
SND_SOC_DAIFMT_RIGHT_J |
SND_SOC_DAIFMT_CBM_CFS |
SND_SOC_DAIFMT_IB_NF);
if (ret < 0)
return ret;
/* Fsref can be 32000, 44100 or 48000. */
fs = params_rate(params);
#ifndef CONFIG_SFFSDR_FPGA
/* Without the FPGA module, the Fs is fixed at 44100 Hz */
if (fs != 44100) {
pr_debug("warning: only 44.1 kHz is supported without SFFSDR FPGA module\n");
return -EINVAL;
}
#endif
/* set cpu DAI configuration */
ret = snd_soc_dai_set_fmt(cpu_dai, AUDIO_FORMAT);
if (ret < 0)
return ret;
pr_debug("sffsdr_hw_params: rate = %d Hz\n", fs);
#ifndef CONFIG_SFFSDR_FPGA
return 0;
#else
return sffsdr_fpga_set_codec_fs(fs);
#endif
}
static struct snd_soc_ops sffsdr_ops = {
@ -127,7 +143,8 @@ static int __init sffsdr_init(void)
platform_set_drvdata(sffsdr_snd_device, &sffsdr_snd_devdata);
sffsdr_snd_devdata.dev = &sffsdr_snd_device->dev;
sffsdr_snd_device->dev.platform_data = &sffsdr_snd_data;
platform_device_add_data(sffsdr_snd_device, &sffsdr_snd_data,
sizeof(sffsdr_snd_data));
ret = platform_device_add_resources(sffsdr_snd_device,
sffsdr_snd_resources,

View File

@ -1,17 +1,18 @@
config SND_SOC_OF_SIMPLE
tristate
# ASoC platform support for the Freescale MPC8610 SOC. This compiles drivers
# for the SSI and the Elo DMA controller. You will still need to select
# a platform driver and a codec driver.
config SND_SOC_MPC8610
bool "ALSA SoC support for the MPC8610 SOC"
depends on MPC8610_HPCD
default y if MPC8610
help
Say Y if you want to add support for codecs attached to the SSI
device on an MPC8610.
tristate
depends on MPC8610
config SND_SOC_MPC8610_HPCD
bool "ALSA SoC support for the Freescale MPC8610 HPCD board"
depends on SND_SOC_MPC8610
tristate "ALSA SoC support for the Freescale MPC8610 HPCD board"
# I2C is necessary for the CS4270 driver
depends on MPC8610_HPCD && I2C
select SND_SOC_MPC8610
select SND_SOC_CS4270
select SND_SOC_CS4270_VD33_ERRATA
default y if MPC8610_HPCD

View File

@ -2,10 +2,13 @@
obj-$(CONFIG_SND_SOC_OF_SIMPLE) += soc-of-simple.o
# MPC8610 HPCD Machine Support
obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += mpc8610_hpcd.o
snd-soc-mpc8610-hpcd-objs := mpc8610_hpcd.o
obj-$(CONFIG_SND_SOC_MPC8610_HPCD) += snd-soc-mpc8610-hpcd.o
# MPC8610 Platform Support
obj-$(CONFIG_SND_SOC_MPC8610) += fsl_ssi.o fsl_dma.o
snd-soc-fsl-ssi-objs := fsl_ssi.o
snd-soc-fsl-dma-objs := fsl_dma.o
obj-$(CONFIG_SND_SOC_MPC8610) += snd-soc-fsl-ssi.o snd-soc-fsl-dma.o
obj-$(CONFIG_SND_SOC_MPC5200_I2S) += mpc5200_psc_i2s.o

View File

@ -142,7 +142,8 @@ static const struct snd_pcm_hardware fsl_dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_JOINT_DUPLEX,
SNDRV_PCM_INFO_JOINT_DUPLEX |
SNDRV_PCM_INFO_PAUSE,
.formats = FSLDMA_PCM_FORMATS,
.rates = FSLDMA_PCM_RATES,
.rate_min = 5512,
@ -464,11 +465,7 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
sizeof(struct fsl_dma_link_descriptor);
for (i = 0; i < NUM_DMA_LINKS; i++) {
struct fsl_dma_link_descriptor *link = &dma_private->link[i];
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
link->next = cpu_to_be64(temp_link);
dma_private->link[i].next = cpu_to_be64(temp_link);
temp_link += sizeof(struct fsl_dma_link_descriptor);
}
@ -525,79 +522,9 @@ static int fsl_dma_open(struct snd_pcm_substream *substream)
* This function obtains hardware parameters about the opened stream and
* programs the DMA controller accordingly.
*
* Note that due to a quirk of the SSI's STX register, the target address
* for the DMA operations depends on the sample size. So we don't program
* the dest_addr (for playback -- source_addr for capture) fields in the
* link descriptors here. We do that in fsl_dma_prepare()
*/
static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
dma_addr_t temp_addr; /* Pointer to next period */
unsigned int i;
/* Get all the parameters we need */
size_t buffer_size = params_buffer_bytes(hw_params);
size_t period_size = params_period_bytes(hw_params);
/* Initialize our DMA tracking variables */
dma_private->period_size = period_size;
dma_private->num_periods = params_periods(hw_params);
dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size;
dma_private->dma_buf_next = dma_private->dma_buf_phys +
(NUM_DMA_LINKS * period_size);
if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
dma_private->dma_buf_next = dma_private->dma_buf_phys;
/*
* The actual address in STX0 (destination for playback, source for
* capture) is based on the sample size, but we don't know the sample
* size in this function, so we'll have to adjust that later. See
* comments in fsl_dma_prepare().
*
* The DMA controller does not have a cache, so the CPU does not
* need to tell it to flush its cache. However, the DMA
* controller does need to tell the CPU to flush its cache.
* That's what the SNOOP bit does.
*
* Also, even though the DMA controller supports 36-bit addressing, for
* simplicity we currently support only 32-bit addresses for the audio
* buffer itself.
*/
temp_addr = substream->dma_buffer.addr;
for (i = 0; i < NUM_DMA_LINKS; i++) {
struct fsl_dma_link_descriptor *link = &dma_private->link[i];
link->count = cpu_to_be32(period_size);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
link->source_addr = cpu_to_be32(temp_addr);
else
link->dest_addr = cpu_to_be32(temp_addr);
temp_addr += period_size;
}
return 0;
}
/**
* fsl_dma_prepare - prepare the DMA registers for playback.
*
* This function is called after the specifics of the audio data are known,
* i.e. snd_pcm_runtime is initialized.
*
* In this function, we finish programming the registers of the DMA
* controller that are dependent on the sample size.
*
* One of the drawbacks with big-endian is that when copying integers of
* different sizes to a fixed-sized register, the address to which the
* integer must be copied is dependent on the size of the integer.
* One drawback of big-endian is that when copying integers of different
* sizes to a fixed-sized register, the address to which the integer must be
* copied is dependent on the size of the integer.
*
* For example, if P is the address of a 32-bit register, and X is a 32-bit
* integer, then X should be copied to address P. However, if X is a 16-bit
@ -613,22 +540,58 @@ static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
* and 8 bytes at a time). So we do not support packed 24-bit samples.
* 24-bit data must be padded to 32 bits.
*/
static int fsl_dma_prepare(struct snd_pcm_substream *substream)
static int fsl_dma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_dma_private *dma_private = runtime->private_data;
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
u32 mr;
unsigned int i;
dma_addr_t ssi_sxx_phys; /* Bus address of SSI STX register */
unsigned int frame_size; /* Number of bytes per frame */
ssi_sxx_phys = dma_private->ssi_sxx_phys;
/* Number of bits per sample */
unsigned int sample_size =
snd_pcm_format_physical_width(params_format(hw_params));
/* Number of bytes per frame */
unsigned int frame_size = 2 * (sample_size / 8);
/* Bus address of SSI STX register */
dma_addr_t ssi_sxx_phys = dma_private->ssi_sxx_phys;
/* Size of the DMA buffer, in bytes */
size_t buffer_size = params_buffer_bytes(hw_params);
/* Number of bytes per period */
size_t period_size = params_period_bytes(hw_params);
/* Pointer to next period */
dma_addr_t temp_addr = substream->dma_buffer.addr;
/* Pointer to DMA controller */
struct ccsr_dma_channel __iomem *dma_channel = dma_private->dma_channel;
u32 mr; /* DMA Mode Register */
unsigned int i;
/* Initialize our DMA tracking variables */
dma_private->period_size = period_size;
dma_private->num_periods = params_periods(hw_params);
dma_private->dma_buf_end = dma_private->dma_buf_phys + buffer_size;
dma_private->dma_buf_next = dma_private->dma_buf_phys +
(NUM_DMA_LINKS * period_size);
if (dma_private->dma_buf_next >= dma_private->dma_buf_end)
/* This happens if the number of periods == NUM_DMA_LINKS */
dma_private->dma_buf_next = dma_private->dma_buf_phys;
mr = in_be32(&dma_channel->mr) & ~(CCSR_DMA_MR_BWC_MASK |
CCSR_DMA_MR_SAHTS_MASK | CCSR_DMA_MR_DAHTS_MASK);
switch (runtime->sample_bits) {
/* Due to a quirk of the SSI's STX register, the target address
* for the DMA operations depends on the sample size. So we calculate
* that offset here. While we're at it, also tell the DMA controller
* how much data to transfer per sample.
*/
switch (sample_size) {
case 8:
mr |= CCSR_DMA_MR_DAHTS_1 | CCSR_DMA_MR_SAHTS_1;
ssi_sxx_phys += 3;
@ -641,12 +604,12 @@ static int fsl_dma_prepare(struct snd_pcm_substream *substream)
mr |= CCSR_DMA_MR_DAHTS_4 | CCSR_DMA_MR_SAHTS_4;
break;
default:
/* We should never get here */
dev_err(substream->pcm->card->dev,
"unsupported sample size %u\n", runtime->sample_bits);
"unsupported sample size %u\n", sample_size);
return -EINVAL;
}
frame_size = runtime->frame_bits / 8;
/*
* BWC should always be a multiple of the frame size. BWC determines
* how many bytes are sent/received before the DMA controller checks the
@ -655,7 +618,6 @@ static int fsl_dma_prepare(struct snd_pcm_substream *substream)
* capture, the receive FIFO is triggered when it contains one frame, so
* we want to receive one frame at a time.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
mr |= CCSR_DMA_MR_BWC(2 * frame_size);
else
@ -663,16 +625,48 @@ static int fsl_dma_prepare(struct snd_pcm_substream *substream)
out_be32(&dma_channel->mr, mr);
/*
* Program the address of the DMA transfer to/from the SSI.
*/
for (i = 0; i < NUM_DMA_LINKS; i++) {
struct fsl_dma_link_descriptor *link = &dma_private->link[i];
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
link->count = cpu_to_be32(period_size);
/* Even though the DMA controller supports 36-bit addressing,
* for simplicity we allow only 32-bit addresses for the audio
* buffer itself. This was enforced in fsl_dma_new() with the
* DMA mask.
*
* The snoop bit tells the DMA controller whether it should tell
* the ECM to snoop during a read or write to an address. For
* audio, we use DMA to transfer data between memory and an I/O
* device (the SSI's STX0 or SRX0 register). Snooping is only
* needed if there is a cache, so we need to snoop memory
* addresses only. For playback, that means we snoop the source
* but not the destination. For capture, we snoop the
* destination but not the source.
*
* Note that failing to snoop properly is unlikely to cause
* cache incoherency if the period size is larger than the
* size of L1 cache. This is because filling in one period will
* flush out the data for the previous period. So if you
* increased period_bytes_min to a large enough size, you might
* get more performance by not snooping, and you'll still be
* okay.
*/
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
link->source_addr = cpu_to_be32(temp_addr);
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
link->dest_addr = cpu_to_be32(ssi_sxx_phys);
else
link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP);
} else {
link->source_addr = cpu_to_be32(ssi_sxx_phys);
link->source_attr = cpu_to_be32(CCSR_DMA_ATR_NOSNOOP);
link->dest_addr = cpu_to_be32(temp_addr);
link->dest_attr = cpu_to_be32(CCSR_DMA_ATR_SNOOP);
}
temp_addr += period_size;
}
return 0;
@ -808,7 +802,6 @@ static struct snd_pcm_ops fsl_dma_ops = {
.ioctl = snd_pcm_lib_ioctl,
.hw_params = fsl_dma_hw_params,
.hw_free = fsl_dma_hw_free,
.prepare = fsl_dma_prepare,
.pointer = fsl_dma_pointer,
};

View File

@ -72,6 +72,7 @@
* @dev: struct device pointer
* @playback: the number of playback streams opened
* @capture: the number of capture streams opened
* @asynchronous: 0=synchronous mode, 1=asynchronous mode
* @cpu_dai: the CPU DAI for this device
* @dev_attr: the sysfs device attribute structure
* @stats: SSI statistics
@ -86,6 +87,7 @@ struct fsl_ssi_private {
struct device *dev;
unsigned int playback;
unsigned int capture;
int asynchronous;
struct snd_soc_dai cpu_dai;
struct device_attribute dev_attr;
@ -301,9 +303,10 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
*
* FIXME: Little-endian samples require a different shift dir
*/
clrsetbits_be32(&ssi->scr, CCSR_SSI_SCR_I2S_MODE_MASK,
CCSR_SSI_SCR_TFR_CLK_DIS |
CCSR_SSI_SCR_I2S_MODE_SLAVE | CCSR_SSI_SCR_SYN);
clrsetbits_be32(&ssi->scr,
CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
| (ssi_private->asynchronous ? 0 : CCSR_SSI_SCR_SYN));
out_be32(&ssi->stcr,
CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
@ -382,10 +385,15 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_RATE,
first_runtime->rate, first_runtime->rate);
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
first_runtime->sample_bits,
first_runtime->sample_bits);
/* If we're in synchronous mode, then we need to constrain
* the sample size as well. We don't support independent sample
* rates in asynchronous mode.
*/
if (!ssi_private->asynchronous)
snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
first_runtime->sample_bits,
first_runtime->sample_bits);
ssi_private->second_stream = substream;
}
@ -400,7 +408,7 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
}
/**
* fsl_ssi_prepare: prepare the SSI.
* fsl_ssi_hw_params - program the sample size
*
* Most of the SSI registers have been programmed in the startup function,
* but the word length must be programmed here. Unfortunately, programming
@ -412,23 +420,27 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
* Note: The SxCCR.DC and SxCCR.PM bits are only used if the SSI is the
* clock master.
*/
static int fsl_ssi_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
static int fsl_ssi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params, struct snd_soc_dai *cpu_dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_ssi_private *ssi_private = rtd->dai->cpu_dai->private_data;
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
struct fsl_ssi_private *ssi_private = cpu_dai->private_data;
if (substream == ssi_private->first_stream) {
u32 wl;
struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
unsigned int sample_size =
snd_pcm_format_width(params_format(hw_params));
u32 wl = CCSR_SSI_SxCCR_WL(sample_size);
/* The SSI should always be disabled at this points (SSIEN=0) */
wl = CCSR_SSI_SxCCR_WL(snd_pcm_format_width(runtime->format));
/* In synchronous mode, the SSI uses STCCR for capture */
clrsetbits_be32(&ssi->stccr, CCSR_SSI_SxCCR_WL_MASK, wl);
if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ||
!ssi_private->asynchronous)
clrsetbits_be32(&ssi->stccr,
CCSR_SSI_SxCCR_WL_MASK, wl);
else
clrsetbits_be32(&ssi->srccr,
CCSR_SSI_SxCCR_WL_MASK, wl);
}
return 0;
@ -452,28 +464,33 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
setbits32(&ssi->scr,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_TE);
} else {
clrbits32(&ssi->scr, CCSR_SSI_SCR_SSIEN);
long timeout = jiffies + 10;
setbits32(&ssi->scr,
CCSR_SSI_SCR_SSIEN | CCSR_SSI_SCR_RE);
/*
* I think we need this delay to allow time for the SSI
* to put data into its FIFO. Without it, ALSA starts
* to complain about overruns.
/* Wait until the SSI has filled its FIFO. Without this
* delay, ALSA complains about overruns. When the FIFO
* is full, the DMA controller initiates its first
* transfer. Until then, however, the DMA's DAR
* register is zero, which translates to an
* out-of-bounds pointer. This makes ALSA think an
* overrun has occurred.
*/
mdelay(1);
while (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0) &&
(jiffies < timeout));
if (!(in_be32(&ssi->sisr) & CCSR_SSI_SISR_RFF0))
return -EIO;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
clrbits32(&ssi->scr, CCSR_SSI_SCR_TE);
@ -563,6 +580,15 @@ static int fsl_ssi_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
/**
* fsl_ssi_dai_template: template CPU DAI for the SSI
*/
static struct snd_soc_dai_ops fsl_ssi_dai_ops = {
.startup = fsl_ssi_startup,
.hw_params = fsl_ssi_hw_params,
.shutdown = fsl_ssi_shutdown,
.trigger = fsl_ssi_trigger,
.set_sysclk = fsl_ssi_set_sysclk,
.set_fmt = fsl_ssi_set_fmt,
};
static struct snd_soc_dai fsl_ssi_dai_template = {
.playback = {
/* The SSI does not support monaural audio. */
@ -577,14 +603,7 @@ static struct snd_soc_dai fsl_ssi_dai_template = {
.rates = FSLSSI_I2S_RATES,
.formats = FSLSSI_I2S_FORMATS,
},
.ops = {
.startup = fsl_ssi_startup,
.prepare = fsl_ssi_prepare,
.shutdown = fsl_ssi_shutdown,
.trigger = fsl_ssi_trigger,
.set_sysclk = fsl_ssi_set_sysclk,
.set_fmt = fsl_ssi_set_fmt,
},
.ops = &fsl_ssi_dai_ops,
};
/**
@ -654,6 +673,7 @@ struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info)
ssi_private->ssi_phys = ssi_info->ssi_phys;
ssi_private->irq = ssi_info->irq;
ssi_private->dev = ssi_info->dev;
ssi_private->asynchronous = ssi_info->asynchronous;
ssi_private->dev->driver_data = fsl_ssi_dai;
@ -704,6 +724,14 @@ void fsl_ssi_destroy_dai(struct snd_soc_dai *fsl_ssi_dai)
}
EXPORT_SYMBOL_GPL(fsl_ssi_destroy_dai);
static int __init fsl_ssi_init(void)
{
printk(KERN_INFO "Freescale Synchronous Serial Interface (SSI) ASoC Driver\n");
return 0;
}
module_init(fsl_ssi_init);
MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
MODULE_LICENSE("GPL");

View File

@ -208,6 +208,7 @@ struct ccsr_ssi {
* ssi_phys: physical address of the SSI registers
* irq: IRQ of this SSI
* dev: struct device, used to create the sysfs statistics file
* asynchronous: 0=synchronous mode, 1=asynchronous mode
*/
struct fsl_ssi_info {
unsigned int id;
@ -215,6 +216,7 @@ struct fsl_ssi_info {
dma_addr_t ssi_phys;
unsigned int irq;
struct device *dev;
int asynchronous;
};
struct snd_soc_dai *fsl_ssi_create_dai(struct fsl_ssi_info *ssi_info);

View File

@ -468,6 +468,16 @@ static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
/**
* psc_i2s_dai_template: template CPU Digital Audio Interface
*/
static struct snd_soc_dai_ops psc_i2s_dai_ops = {
.startup = psc_i2s_startup,
.hw_params = psc_i2s_hw_params,
.hw_free = psc_i2s_hw_free,
.shutdown = psc_i2s_shutdown,
.trigger = psc_i2s_trigger,
.set_sysclk = psc_i2s_set_sysclk,
.set_fmt = psc_i2s_set_fmt,
};
static struct snd_soc_dai psc_i2s_dai_template = {
.playback = {
.channels_min = 2,
@ -481,15 +491,7 @@ static struct snd_soc_dai psc_i2s_dai_template = {
.rates = PSC_I2S_RATES,
.formats = PSC_I2S_FORMATS,
},
.ops = {
.startup = psc_i2s_startup,
.hw_params = psc_i2s_hw_params,
.hw_free = psc_i2s_hw_free,
.shutdown = psc_i2s_shutdown,
.trigger = psc_i2s_trigger,
.set_sysclk = psc_i2s_set_sysclk,
.set_fmt = psc_i2s_set_fmt,
},
.ops = &psc_i2s_dai_ops,
};
/* ---------------------------------------------------------------------

View File

@ -353,6 +353,11 @@ static int mpc8610_hpcd_probe(struct of_device *ofdev,
}
ssi_info.irq = machine_data->ssi_irq;
/* Do we want to use asynchronous mode? */
ssi_info.asynchronous =
of_find_property(np, "fsl,ssi-asynchronous", NULL) ? 1 : 0;
if (ssi_info.asynchronous)
dev_info(&ofdev->dev, "using asynchronous mode\n");
/* Map the global utilities registers. */
guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");

View File

@ -8,7 +8,7 @@ config SND_OMAP_SOC_MCBSP
config SND_OMAP_SOC_N810
tristate "SoC Audio support for Nokia N810"
depends on SND_OMAP_SOC && MACH_NOKIA_N810
depends on SND_OMAP_SOC && MACH_NOKIA_N810 && I2C
select SND_OMAP_SOC_MCBSP
select OMAP_MUX
select SND_SOC_TLV320AIC3X
@ -17,7 +17,7 @@ config SND_OMAP_SOC_N810
config SND_OMAP_SOC_OSK5912
tristate "SoC Audio support for omap osk5912"
depends on SND_OMAP_SOC && MACH_OMAP_OSK
depends on SND_OMAP_SOC && MACH_OMAP_OSK && I2C
select SND_OMAP_SOC_MCBSP
select SND_SOC_TLV320AIC23
help
@ -55,3 +55,13 @@ config SND_OMAP_SOC_OMAP3_PANDORA
select SND_SOC_TWL4030
help
Say Y if you want to add support for SoC audio on the OMAP3 Pandora.
config SND_OMAP_SOC_OMAP3_BEAGLE
tristate "SoC Audio support for OMAP3 Beagle"
depends on TWL4030_CORE && SND_OMAP_SOC && MACH_OMAP3_BEAGLE
select SND_OMAP_SOC_MCBSP
select SND_SOC_TWL4030
help
Say Y if you want to add support for SoC audio on the Beagleboard.

View File

@ -12,6 +12,7 @@ snd-soc-overo-objs := overo.o
snd-soc-omap2evm-objs := omap2evm.o
snd-soc-sdp3430-objs := sdp3430.o
snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o
@ -19,3 +20,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OVERO) += snd-soc-overo.o
obj-$(CONFIG_MACH_OMAP2EVM) += snd-soc-omap2evm.o
obj-$(CONFIG_SND_OMAP_SOC_SDP3430) += snd-soc-sdp3430.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o

View File

@ -40,6 +40,13 @@
#define N810_HEADSET_AMP_GPIO 10
#define N810_SPEAKER_AMP_GPIO 101
enum {
N810_JACK_DISABLED,
N810_JACK_HP,
N810_JACK_HS,
N810_JACK_MIC,
};
static struct clk *sys_clkout2;
static struct clk *sys_clkout2_src;
static struct clk *func96m_clk;
@ -50,15 +57,32 @@ static int n810_dmic_func;
static void n810_ext_control(struct snd_soc_codec *codec)
{
int hp = 0, line1l = 0;
switch (n810_jack_func) {
case N810_JACK_HS:
line1l = 1;
case N810_JACK_HP:
hp = 1;
break;
case N810_JACK_MIC:
line1l = 1;
break;
}
if (n810_spk_func)
snd_soc_dapm_enable_pin(codec, "Ext Spk");
else
snd_soc_dapm_disable_pin(codec, "Ext Spk");
if (n810_jack_func)
if (hp)
snd_soc_dapm_enable_pin(codec, "Headphone Jack");
else
snd_soc_dapm_disable_pin(codec, "Headphone Jack");
if (line1l)
snd_soc_dapm_enable_pin(codec, "LINE1L");
else
snd_soc_dapm_disable_pin(codec, "LINE1L");
if (n810_dmic_func)
snd_soc_dapm_enable_pin(codec, "DMic");
@ -72,7 +96,7 @@ static int n810_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
struct snd_soc_codec *codec = rtd->socdev->card->codec;
snd_pcm_hw_constraint_minmax(runtime,
SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
@ -229,7 +253,7 @@ static const struct snd_soc_dapm_route audio_map[] = {
};
static const char *spk_function[] = {"Off", "On"};
static const char *jack_function[] = {"Off", "Headphone"};
static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"};
static const char *input_function[] = {"ADC", "Digital Mic"};
static const struct soc_enum n810_enum[] = {
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function),
@ -248,20 +272,23 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = {
static int n810_aic33_init(struct snd_soc_codec *codec)
{
int i, err;
int err;
/* Not connected */
snd_soc_dapm_nc_pin(codec, "MONO_LOUT");
snd_soc_dapm_nc_pin(codec, "HPLCOM");
snd_soc_dapm_nc_pin(codec, "HPRCOM");
snd_soc_dapm_nc_pin(codec, "MIC3L");
snd_soc_dapm_nc_pin(codec, "MIC3R");
snd_soc_dapm_nc_pin(codec, "LINE1R");
snd_soc_dapm_nc_pin(codec, "LINE2L");
snd_soc_dapm_nc_pin(codec, "LINE2R");
/* Add N810 specific controls */
for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&aic33_n810_controls[i], codec, NULL));
if (err < 0)
return err;
}
err = snd_soc_add_controls(codec, aic33_n810_controls,
ARRAY_SIZE(aic33_n810_controls));
if (err < 0)
return err;
/* Add N810 specific widgets */
snd_soc_dapm_new_controls(codec, aic33_dapm_widgets,

View File

@ -461,6 +461,16 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
return err;
}
static struct snd_soc_dai_ops omap_mcbsp_dai_ops = {
.startup = omap_mcbsp_dai_startup,
.shutdown = omap_mcbsp_dai_shutdown,
.trigger = omap_mcbsp_dai_trigger,
.hw_params = omap_mcbsp_dai_hw_params,
.set_fmt = omap_mcbsp_dai_set_dai_fmt,
.set_clkdiv = omap_mcbsp_dai_set_clkdiv,
.set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
};
#define OMAP_MCBSP_DAI_BUILDER(link_id) \
{ \
.name = "omap-mcbsp-dai-"#link_id, \
@ -477,15 +487,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
.rates = OMAP_MCBSP_RATES, \
.formats = SNDRV_PCM_FMTBIT_S16_LE, \
}, \
.ops = { \
.startup = omap_mcbsp_dai_startup, \
.shutdown = omap_mcbsp_dai_shutdown, \
.trigger = omap_mcbsp_dai_trigger, \
.hw_params = omap_mcbsp_dai_hw_params, \
.set_fmt = omap_mcbsp_dai_set_dai_fmt, \
.set_clkdiv = omap_mcbsp_dai_set_clkdiv, \
.set_sysclk = omap_mcbsp_dai_set_dai_sysclk, \
}, \
.ops = &omap_mcbsp_dai_ops, \
.private_data = &mcbsp_data[(link_id)].bus_id, \
}

View File

@ -265,7 +265,7 @@ static int omap_pcm_mmap(struct snd_pcm_substream *substream,
runtime->dma_bytes);
}
struct snd_pcm_ops omap_pcm_ops = {
static struct snd_pcm_ops omap_pcm_ops = {
.open = omap_pcm_open,
.close = omap_pcm_close,
.ioctl = snd_pcm_lib_ioctl,

View File

@ -143,7 +143,7 @@ static const struct snd_soc_dapm_widget omap3pandora_out_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
SND_SOC_DAPM_MIC("Mic (internal)", NULL),
SND_SOC_DAPM_MIC("Mic (external)", NULL),
SND_SOC_DAPM_LINE("Line In", NULL),
};
@ -155,16 +155,33 @@ static const struct snd_soc_dapm_route omap3pandora_out_map[] = {
};
static const struct snd_soc_dapm_route omap3pandora_in_map[] = {
{"INL", NULL, "Line In"},
{"INR", NULL, "Line In"},
{"INL", NULL, "Mic (Internal)"},
{"INR", NULL, "Mic (external)"},
{"AUXL", NULL, "Line In"},
{"AUXR", NULL, "Line In"},
{"MAINMIC", NULL, "Mic Bias 1"},
{"Mic Bias 1", NULL, "Mic (internal)"},
{"SUBMIC", NULL, "Mic Bias 2"},
{"Mic Bias 2", NULL, "Mic (external)"},
};
static int omap3pandora_out_init(struct snd_soc_codec *codec)
{
int ret;
/* All TWL4030 output pins are floating */
snd_soc_dapm_nc_pin(codec, "OUTL");
snd_soc_dapm_nc_pin(codec, "OUTR");
snd_soc_dapm_nc_pin(codec, "EARPIECE");
snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
snd_soc_dapm_nc_pin(codec, "PREDRIVER");
snd_soc_dapm_nc_pin(codec, "HSOL");
snd_soc_dapm_nc_pin(codec, "HSOR");
snd_soc_dapm_nc_pin(codec, "CARKITL");
snd_soc_dapm_nc_pin(codec, "CARKITR");
snd_soc_dapm_nc_pin(codec, "HFL");
snd_soc_dapm_nc_pin(codec, "HFR");
ret = snd_soc_dapm_new_controls(codec, omap3pandora_out_dapm_widgets,
ARRAY_SIZE(omap3pandora_out_dapm_widgets));
if (ret < 0)
@ -180,18 +197,11 @@ static int omap3pandora_in_init(struct snd_soc_codec *codec)
{
int ret;
/* All TWL4030 output pins are floating */
snd_soc_dapm_nc_pin(codec, "OUTL"),
snd_soc_dapm_nc_pin(codec, "OUTR"),
snd_soc_dapm_nc_pin(codec, "EARPIECE"),
snd_soc_dapm_nc_pin(codec, "PREDRIVEL"),
snd_soc_dapm_nc_pin(codec, "PREDRIVER"),
snd_soc_dapm_nc_pin(codec, "HSOL"),
snd_soc_dapm_nc_pin(codec, "HSOR"),
snd_soc_dapm_nc_pin(codec, "CARKITL"),
snd_soc_dapm_nc_pin(codec, "CARKITR"),
snd_soc_dapm_nc_pin(codec, "HFL"),
snd_soc_dapm_nc_pin(codec, "HFR"),
/* Not comnnected */
snd_soc_dapm_nc_pin(codec, "HSMIC");
snd_soc_dapm_nc_pin(codec, "CARKITMIC");
snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
ret = snd_soc_dapm_new_controls(codec, omap3pandora_in_dapm_widgets,
ARRAY_SIZE(omap3pandora_in_dapm_widgets));
@ -251,10 +261,9 @@ static int __init omap3pandora_soc_init(void)
{
int ret;
if (!machine_is_omap3_pandora()) {
pr_debug(PREFIX "Not OMAP3 Pandora\n");
if (!machine_is_omap3_pandora())
return -ENODEV;
}
pr_info("OMAP3 Pandora SoC init\n");
ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power");

View File

@ -186,13 +186,6 @@ static int __init osk_soc_init(void)
return -ENODEV;
}
if (clk_get_usecount(tlv320aic23_mclk) > 0) {
/* MCLK is already in use */
printk(KERN_WARNING
"MCLK in use at %d Hz. We change it to %d Hz\n",
(uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
}
/*
* Configure 12 MHz output on MCLK.
*/
@ -205,9 +198,8 @@ static int __init osk_soc_init(void)
}
}
printk(KERN_INFO "MCLK = %d [%d], usecount = %d\n",
(uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK,
clk_get_usecount(tlv320aic23_mclk));
printk(KERN_INFO "MCLK = %d [%d]\n",
(uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK);
return 0;
err1:

View File

@ -28,6 +28,7 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
@ -38,6 +39,8 @@
#include "omap-pcm.h"
#include "../codecs/twl4030.h"
static struct snd_soc_card snd_soc_sdp3430;
static int sdp3430_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@ -81,12 +84,121 @@ static struct snd_soc_ops sdp3430_ops = {
.hw_params = sdp3430_hw_params,
};
/* Headset jack */
static struct snd_soc_jack hs_jack;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin hs_jack_pins[] = {
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Headset Stereophone",
.mask = SND_JACK_HEADPHONE,
},
};
/* Headset jack detection gpios */
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
{
.gpio = (OMAP_MAX_GPIO_LINES + 2),
.name = "hsdet-gpio",
.report = SND_JACK_HEADSET,
.debounce_time = 200,
},
};
/* SDP3430 machine DAPM */
static const struct snd_soc_dapm_widget sdp3430_twl4030_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Ext Mic", NULL),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_HP("Headset Stereophone", NULL),
};
static const struct snd_soc_dapm_route audio_map[] = {
/* External Mics: MAINMIC, SUBMIC with bias*/
{"MAINMIC", NULL, "Mic Bias 1"},
{"SUBMIC", NULL, "Mic Bias 2"},
{"Mic Bias 1", NULL, "Ext Mic"},
{"Mic Bias 2", NULL, "Ext Mic"},
/* External Speakers: HFL, HFR */
{"Ext Spk", NULL, "HFL"},
{"Ext Spk", NULL, "HFR"},
/* Headset Mic: HSMIC with bias */
{"HSMIC", NULL, "Headset Mic Bias"},
{"Headset Mic Bias", NULL, "Headset Mic"},
/* Headset Stereophone (Headphone): HSOL, HSOR */
{"Headset Stereophone", NULL, "HSOL"},
{"Headset Stereophone", NULL, "HSOR"},
};
static int sdp3430_twl4030_init(struct snd_soc_codec *codec)
{
int ret;
/* Add SDP3430 specific widgets */
ret = snd_soc_dapm_new_controls(codec, sdp3430_twl4030_dapm_widgets,
ARRAY_SIZE(sdp3430_twl4030_dapm_widgets));
if (ret)
return ret;
/* Set up SDP3430 specific audio path audio_map */
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
/* SDP3430 connected pins */
snd_soc_dapm_enable_pin(codec, "Ext Mic");
snd_soc_dapm_enable_pin(codec, "Ext Spk");
snd_soc_dapm_disable_pin(codec, "Headset Mic");
snd_soc_dapm_disable_pin(codec, "Headset Stereophone");
/* TWL4030 not connected pins */
snd_soc_dapm_nc_pin(codec, "AUXL");
snd_soc_dapm_nc_pin(codec, "AUXR");
snd_soc_dapm_nc_pin(codec, "CARKITMIC");
snd_soc_dapm_nc_pin(codec, "DIGIMIC0");
snd_soc_dapm_nc_pin(codec, "DIGIMIC1");
snd_soc_dapm_nc_pin(codec, "OUTL");
snd_soc_dapm_nc_pin(codec, "OUTR");
snd_soc_dapm_nc_pin(codec, "EARPIECE");
snd_soc_dapm_nc_pin(codec, "PREDRIVEL");
snd_soc_dapm_nc_pin(codec, "PREDRIVER");
snd_soc_dapm_nc_pin(codec, "CARKITL");
snd_soc_dapm_nc_pin(codec, "CARKITR");
ret = snd_soc_dapm_sync(codec);
if (ret)
return ret;
/* Headset jack detection */
ret = snd_soc_jack_new(&snd_soc_sdp3430, "Headset Jack",
SND_JACK_HEADSET, &hs_jack);
if (ret)
return ret;
ret = snd_soc_jack_add_pins(&hs_jack, ARRAY_SIZE(hs_jack_pins),
hs_jack_pins);
if (ret)
return ret;
ret = snd_soc_jack_add_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
return ret;
}
/* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link sdp3430_dai = {
.name = "TWL4030",
.stream_name = "TWL4030",
.cpu_dai = &omap_mcbsp_dai[0],
.codec_dai = &twl4030_dai,
.init = sdp3430_twl4030_init,
.ops = &sdp3430_ops,
};
@ -142,6 +254,9 @@ module_init(sdp3430_soc_init);
static void __exit sdp3430_soc_exit(void)
{
snd_soc_jack_free_gpios(&hs_jack, ARRAY_SIZE(hs_jack_gpios),
hs_jack_gpios);
platform_device_unregister(sdp3430_snd_device);
}
module_exit(sdp3430_soc_exit);

View File

@ -61,6 +61,24 @@ config SND_PXA2XX_SOC_TOSA
Say Y if you want to add support for SoC audio on Sharp
Zaurus SL-C6000x models (Tosa).
config SND_PXA2XX_SOC_E740
tristate "SoC AC97 Audio support for e740"
depends on SND_PXA2XX_SOC && MACH_E740
select SND_SOC_WM9705
select SND_PXA2XX_SOC_AC97
help
Say Y if you want to add support for SoC audio on the
toshiba e740 PDA
config SND_PXA2XX_SOC_E750
tristate "SoC AC97 Audio support for e750"
depends on SND_PXA2XX_SOC && MACH_E750
select SND_SOC_WM9705
select SND_PXA2XX_SOC_AC97
help
Say Y if you want to add support for SoC audio on the
toshiba e750 PDA
config SND_PXA2XX_SOC_E800
tristate "SoC AC97 Audio support for e800"
depends on SND_PXA2XX_SOC && MACH_E800
@ -97,3 +115,12 @@ config SND_SOC_ZYLONITE
help
Say Y if you want to add support for SoC audio on the
Marvell Zylonite reference platform.
config SND_PXA2XX_SOC_MIOA701
tristate "SoC Audio support for MIO A701"
depends on SND_PXA2XX_SOC && MACH_MIOA701
select SND_PXA2XX_SOC_AC97
select SND_SOC_WM9713
help
Say Y if you want to add support for SoC audio on the
MIO A701.

View File

@ -13,17 +13,23 @@ obj-$(CONFIG_SND_PXA_SOC_SSP) += snd-soc-pxa-ssp.o
snd-soc-corgi-objs := corgi.o
snd-soc-poodle-objs := poodle.o
snd-soc-tosa-objs := tosa.o
snd-soc-e740-objs := e740_wm9705.o
snd-soc-e750-objs := e750_wm9705.o
snd-soc-e800-objs := e800_wm9712.o
snd-soc-spitz-objs := spitz.o
snd-soc-em-x270-objs := em-x270.o
snd-soc-palm27x-objs := palm27x.o
snd-soc-zylonite-objs := zylonite.o
snd-soc-mioa701-objs := mioa701_wm9713.o
obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o
obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o
obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o
obj-$(CONFIG_SND_PXA2XX_SOC_E740) += snd-soc-e740.o
obj-$(CONFIG_SND_PXA2XX_SOC_E750) += snd-soc-e750.o
obj-$(CONFIG_SND_PXA2XX_SOC_E800) += snd-soc-e800.o
obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o
obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o
obj-$(CONFIG_SND_PXA2XX_SOC_PALM27X) += snd-soc-palm27x.o
obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o
obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o

View File

@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
@ -100,7 +101,7 @@ static void corgi_ext_control(struct snd_soc_codec *codec)
static int corgi_startup(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_codec *codec = rtd->socdev->codec;
struct snd_soc_codec *codec = rtd->socdev->card->codec;
/* check the jack status at stream startup */
corgi_ext_control(codec);
@ -275,18 +276,16 @@ static const struct snd_kcontrol_new wm8731_corgi_controls[] = {
*/
static int corgi_wm8731_init(struct snd_soc_codec *codec)
{
int i, err;
int err;
snd_soc_dapm_nc_pin(codec, "LLINEIN");
snd_soc_dapm_nc_pin(codec, "RLINEIN");
/* Add corgi specific controls */
for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) {
err = snd_ctl_add(codec->card,
snd_soc_cnew(&wm8731_corgi_controls[i], codec, NULL));
if (err < 0)
return err;
}
err = snd_soc_add_controls(codec, wm8731_corgi_controls,
ARRAY_SIZE(wm8731_corgi_controls));
if (err < 0)
return err;
/* Add corgi specific widgets */
snd_soc_dapm_new_controls(codec, wm8731_dapm_widgets,
@ -317,19 +316,44 @@ static struct snd_soc_card snd_soc_corgi = {
.num_links = 1,
};
/* corgi audio private data */
static struct wm8731_setup_data corgi_wm8731_setup = {
.i2c_bus = 0,
.i2c_address = 0x1b,
};
/* corgi audio subsystem */
static struct snd_soc_device corgi_snd_devdata = {
.card = &snd_soc_corgi,
.codec_dev = &soc_codec_dev_wm8731,
.codec_data = &corgi_wm8731_setup,
};
/*
* FIXME: This is a temporary bodge to avoid cross-tree merge issues.
* New drivers should register the wm8731 I2C device in the machine
* setup code (under arch/arm for ARM systems).
*/
static int wm8731_i2c_register(void)
{
struct i2c_board_info info;
struct i2c_adapter *adapter;
struct i2c_client *client;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = 0x1b;
strlcpy(info.type, "wm8731", I2C_NAME_SIZE);
adapter = i2c_get_adapter(0);
if (!adapter) {
printk(KERN_ERR "can't get i2c adapter 0\n");
return -ENODEV;
}
client = i2c_new_device(adapter, &info);
i2c_put_adapter(adapter);
if (!client) {
printk(KERN_ERR "can't add i2c device at 0x%x\n",
(unsigned int)info.addr);
return -ENODEV;
}
return 0;
}
static struct platform_device *corgi_snd_device;
static int __init corgi_init(void)
@ -340,6 +364,10 @@ static int __init corgi_init(void)
machine_is_husky()))
return -ENODEV;
ret = wm8731_i2c_register();
if (ret != 0)
return ret;
corgi_snd_device = platform_device_alloc("soc-audio", -1);
if (!corgi_snd_device)
return -ENOMEM;

211
sound/soc/pxa/e740_wm9705.c Normal file
View File

@ -0,0 +1,211 @@
/*
* e740-wm9705.c -- SoC audio for e740
*
* Copyright 2007 (c) Ian Molton <spyro@f2s.com>
*
* 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; version 2 ONLY.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <mach/audio.h>
#include <mach/eseries-gpio.h>
#include <asm/mach-types.h>
#include "../codecs/wm9705.h"
#include "pxa2xx-pcm.h"
#include "pxa2xx-ac97.h"
#define E740_AUDIO_OUT 1
#define E740_AUDIO_IN 2
static int e740_audio_power;
static void e740_sync_audio_power(int status)
{
gpio_set_value(GPIO_E740_WM9705_nAVDD2, !status);
gpio_set_value(GPIO_E740_AMP_ON, (status & E740_AUDIO_OUT) ? 1 : 0);
gpio_set_value(GPIO_E740_MIC_ON, (status & E740_AUDIO_IN) ? 1 : 0);
}
static int e740_mic_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (event & SND_SOC_DAPM_PRE_PMU)
e740_audio_power |= E740_AUDIO_IN;
else if (event & SND_SOC_DAPM_POST_PMD)
e740_audio_power &= ~E740_AUDIO_IN;
e740_sync_audio_power(e740_audio_power);
return 0;
}
static int e740_output_amp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
if (event & SND_SOC_DAPM_PRE_PMU)
e740_audio_power |= E740_AUDIO_OUT;
else if (event & SND_SOC_DAPM_POST_PMD)
e740_audio_power &= ~E740_AUDIO_OUT;
e740_sync_audio_power(e740_audio_power);
return 0;
}
static const struct snd_soc_dapm_widget e740_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Mic (Internal)", NULL),
SND_SOC_DAPM_PGA_E("Output Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
e740_output_amp_event, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_E("Mic Amp", SND_SOC_NOPM, 0, 0, NULL, 0,
e740_mic_amp_event, SND_SOC_DAPM_PRE_PMU |
SND_SOC_DAPM_POST_PMD),
};
static const struct snd_soc_dapm_route audio_map[] = {
{"Output Amp", NULL, "LOUT"},
{"Output Amp", NULL, "ROUT"},
{"Output Amp", NULL, "MONOOUT"},
{"Speaker", NULL, "Output Amp"},
{"Headphone Jack", NULL, "Output Amp"},
{"MIC1", NULL, "Mic Amp"},
{"Mic Amp", NULL, "Mic (Internal)"},
};
static int e740_ac97_init(struct snd_soc_codec *codec)
{
snd_soc_dapm_nc_pin(codec, "HPOUTL");
snd_soc_dapm_nc_pin(codec, "HPOUTR");
snd_soc_dapm_nc_pin(codec, "PHONE");
snd_soc_dapm_nc_pin(codec, "LINEINL");
snd_soc_dapm_nc_pin(codec, "LINEINR");
snd_soc_dapm_nc_pin(codec, "CDINL");
snd_soc_dapm_nc_pin(codec, "CDINR");
snd_soc_dapm_nc_pin(codec, "PCBEEP");
snd_soc_dapm_nc_pin(codec, "MIC2");
snd_soc_dapm_new_controls(codec, e740_dapm_widgets,
ARRAY_SIZE(e740_dapm_widgets));
snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map));
snd_soc_dapm_sync(codec);
return 0;
}
static struct snd_soc_dai_link e740_dai[] = {
{
.name = "AC97",
.stream_name = "AC97 HiFi",
.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI],
.codec_dai = &wm9705_dai[WM9705_DAI_AC97_HIFI],
.init = e740_ac97_init,
},
{
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX],
.codec_dai = &wm9705_dai[WM9705_DAI_AC97_AUX],
},
};
static struct snd_soc_card e740 = {
.name = "Toshiba e740",
.platform = &pxa2xx_soc_platform,
.dai_link = e740_dai,
.num_links = ARRAY_SIZE(e740_dai),
};
static struct snd_soc_device e740_snd_devdata = {
.card = &e740,
.codec_dev = &soc_codec_dev_wm9705,
};
static struct platform_device *e740_snd_device;
static int __init e740_init(void)
{
int ret;
if (!machine_is_e740())
return -ENODEV;
ret = gpio_request(GPIO_E740_MIC_ON, "Mic amp");
if (ret)
return ret;
ret = gpio_request(GPIO_E740_AMP_ON, "Output amp");
if (ret)
goto free_mic_amp_gpio;
ret = gpio_request(GPIO_E740_WM9705_nAVDD2, "Audio power");
if (ret)
goto free_op_amp_gpio;
/* Disable audio */
ret = gpio_direction_output(GPIO_E740_MIC_ON, 0);
if (ret)
goto free_apwr_gpio;
ret = gpio_direction_output(GPIO_E740_AMP_ON, 0);
if (ret)
goto free_apwr_gpio;
ret = gpio_direction_output(GPIO_E740_WM9705_nAVDD2, 1);
if (ret)
goto free_apwr_gpio;
e740_snd_device = platform_device_alloc("soc-audio", -1);
if (!e740_snd_device) {
ret = -ENOMEM;
goto free_apwr_gpio;
}
platform_set_drvdata(e740_snd_device, &e740_snd_devdata);
e740_snd_devdata.dev = &e740_snd_device->dev;
ret = platform_device_add(e740_snd_device);
if (!ret)
return 0;
/* Fail gracefully */
platform_device_put(e740_snd_device);
free_apwr_gpio:
gpio_free(GPIO_E740_WM9705_nAVDD2);
free_op_amp_gpio:
gpio_free(GPIO_E740_AMP_ON);
free_mic_amp_gpio:
gpio_free(GPIO_E740_MIC_ON);
return ret;
}
static void __exit e740_exit(void)
{
platform_device_unregister(e740_snd_device);
}
module_init(e740_init);
module_exit(e740_exit);
/* Module information */
MODULE_AUTHOR("Ian Molton <spyro@f2s.com>");
MODULE_DESCRIPTION("ALSA SoC driver for e740");
MODULE_LICENSE("GPL v2");

Some files were not shown because too many files have changed in this diff Show More