Merge branch 'topic/asoc' into for-linus
This commit is contained in:
commit
b5c784894c
|
@ -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
|
||||
-----------------------------------
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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) { \
|
||||
|
|
|
@ -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)
|
|
@ -490,6 +490,7 @@
|
|||
/*
|
||||
* R231 (0xE7) - Jack Status
|
||||
*/
|
||||
#define WM8350_JACK_L_LVL 0x0800
|
||||
#define WM8350_JACK_R_LVL 0x0400
|
||||
|
||||
/*
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
365
sound/soc/codecs/ak4104.c
Normal 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");
|
||||
|
7
sound/soc/codecs/ak4104.h
Normal file
7
sound/soc/codecs/ak4104.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
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
62
sound/soc/codecs/wm8400.h
Normal 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
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
415
sound/soc/codecs/wm9705.c
Normal 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
14
sound/soc/codecs/wm9705.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, \
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
211
sound/soc/pxa/e740_wm9705.c
Normal 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
Loading…
Reference in a new issue