[alsa-devel] [PATCH] Add support for CLKOUT to wm8731 codec driver
Bill Gatliff
bgat at billgatliff.com
Thu Oct 29 16:13:22 CET 2009
Signed-off-by: Bill Gatliff <bgat at billgatliff.com>
---
sound/soc/codecs/wm8731.c | 165 +++++++++++++++++++++++++-------------------
sound/soc/codecs/wm8731.h | 20 +++++-
2 files changed, 113 insertions(+), 72 deletions(-)
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 801b626..0e20956 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -29,6 +29,8 @@
#include "wm8731.h"
+#define PREFIX "wm8731: "
+
static struct snd_soc_codec *wm8731_codec;
struct snd_soc_codec_device soc_codec_dev_wm8731;
@@ -50,16 +52,17 @@ static int wm8731_spi_write(struct spi_device *spi, const char *data, int len);
* There is no point in caching the reset register
*/
static const u16 wm8731_reg[WM8731_CACHEREGNUM] = {
- 0x0097, 0x0097, 0x0079, 0x0079,
- 0x000a, 0x0008, 0x009f, 0x000a,
- 0x0000, 0x0000
+ 0x0097, 0x0097, 0x0079, 0x0079,
+ 0x000a, 0x0008, 0x009f, 0x000a,
+ 0x0000, 0x0000
};
/*
* read wm8731 register cache
*/
-static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
- unsigned int reg)
+static inline unsigned int
+wm8731_read_reg_cache(struct snd_soc_codec *codec,
+ unsigned int reg)
{
u16 *cache = codec->reg_cache;
if (reg == WM8731_RESET)
@@ -72,8 +75,9 @@ static inline unsigned int wm8731_read_reg_cache(struct snd_soc_codec *codec,
/*
* write wm8731 register cache
*/
-static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
- u16 reg, unsigned int value)
+static inline void
+wm8731_write_reg_cache(struct snd_soc_codec *codec,
+ u16 reg, unsigned int value)
{
u16 *cache = codec->reg_cache;
if (reg >= WM8731_CACHEREGNUM)
@@ -84,11 +88,13 @@ static inline void wm8731_write_reg_cache(struct snd_soc_codec *codec,
/*
* write to the WM8731 register space
*/
-static int wm8731_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static inline int
+wm8731_write(struct snd_soc_codec *codec,
+ unsigned int reg, unsigned int value)
{
u8 data[2];
+
/* data is
* D15..D9 WM8731 register offset
* D8...D0 register data
@@ -115,52 +121,52 @@ static const struct soc_enum wm8731_enum[] = {
static const struct snd_kcontrol_new wm8731_snd_controls[] = {
-SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
- 0, 127, 0),
-SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
- 7, 1, 0),
+ SOC_DOUBLE_R("Master Playback Volume", WM8731_LOUT1V, WM8731_ROUT1V,
+ 0, 127, 0),
+ SOC_DOUBLE_R("Master Playback ZC Switch", WM8731_LOUT1V, WM8731_ROUT1V,
+ 7, 1, 0),
-SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
-SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
+ SOC_DOUBLE_R("Capture Volume", WM8731_LINVOL, WM8731_RINVOL, 0, 31, 0),
+ SOC_DOUBLE_R("Line Capture Switch", WM8731_LINVOL, WM8731_RINVOL, 7, 1, 1),
-SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
-SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
+ SOC_SINGLE("Mic Boost (+20dB)", WM8731_APANA, 0, 1, 0),
+ SOC_SINGLE("Capture Mic Switch", WM8731_APANA, 1, 1, 1),
-SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
+ SOC_SINGLE("Sidetone Playback Volume", WM8731_APANA, 6, 3, 1),
-SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
-SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
+ SOC_SINGLE("ADC High Pass Filter Switch", WM8731_APDIGI, 0, 1, 1),
+ SOC_SINGLE("Store DC Offset Switch", WM8731_APDIGI, 4, 1, 0),
-SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
+ SOC_ENUM("Playback De-emphasis", wm8731_enum[1]),
};
/* Output Mixer */
static const struct snd_kcontrol_new wm8731_output_mixer_controls[] = {
-SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
-SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
-SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
+ SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, WM8731_APANA_BYPASS, 1, 0),
+ SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, WM8731_APANA_SIDETONE, 1, 0),
+ SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, WM8731_APANA_DACSEL, 1, 0),
};
/* Input mux */
static const struct snd_kcontrol_new wm8731_input_mux_controls =
-SOC_DAPM_ENUM("Input Select", wm8731_enum[0]);
+ SOC_DAPM_ENUM("Input Select", wm8731_enum[0]);
static const struct snd_soc_dapm_widget wm8731_dapm_widgets[] = {
-SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
- &wm8731_output_mixer_controls[0],
- ARRAY_SIZE(wm8731_output_mixer_controls)),
-SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
-SND_SOC_DAPM_OUTPUT("LOUT"),
-SND_SOC_DAPM_OUTPUT("LHPOUT"),
-SND_SOC_DAPM_OUTPUT("ROUT"),
-SND_SOC_DAPM_OUTPUT("RHPOUT"),
-SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
-SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
-SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
-SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
-SND_SOC_DAPM_INPUT("MICIN"),
-SND_SOC_DAPM_INPUT("RLINEIN"),
-SND_SOC_DAPM_INPUT("LLINEIN"),
+ SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1,
+ &wm8731_output_mixer_controls[0],
+ ARRAY_SIZE(wm8731_output_mixer_controls)),
+ SND_SOC_DAPM_DAC("DAC", "HiFi Playback", WM8731_PWR, 3, 1),
+ SND_SOC_DAPM_OUTPUT("LOUT"),
+ SND_SOC_DAPM_OUTPUT("LHPOUT"),
+ SND_SOC_DAPM_OUTPUT("ROUT"),
+ SND_SOC_DAPM_OUTPUT("RHPOUT"),
+ SND_SOC_DAPM_ADC("ADC", "HiFi Capture", WM8731_PWR, 2, 1),
+ SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, &wm8731_input_mux_controls),
+ SND_SOC_DAPM_PGA("Line Input", WM8731_PWR, 0, 1, NULL, 0),
+ SND_SOC_DAPM_MICBIAS("Mic Bias", WM8731_PWR, 1, 1),
+ SND_SOC_DAPM_INPUT("MICIN"),
+ SND_SOC_DAPM_INPUT("RLINEIN"),
+ SND_SOC_DAPM_INPUT("LLINEIN"),
};
static const struct snd_soc_dapm_route intercon[] = {
@@ -265,8 +271,6 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
u16 srate = (coeff_div[i].sr << 2) |
(coeff_div[i].bosr << 1) | coeff_div[i].usb;
- wm8731_write(codec, WM8731_SRATE, srate);
-
/* bit size */
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -277,8 +281,13 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
case SNDRV_PCM_FORMAT_S24_LE:
iface |= 0x0008;
break;
+ default:
+ pr_err("%s: unrecognized format %x\n",
+ __func__, params_format(params));
+ return -EINVAL;
}
+ wm8731_write(codec, WM8731_SRATE, srate);
wm8731_write(codec, WM8731_IFACE, iface);
return 0;
}
@@ -323,10 +332,13 @@ static int wm8731_mute(struct snd_soc_dai *dai, int mute)
}
static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
+ int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = codec_dai->codec;
struct wm8731_priv *wm8731 = codec->private_data;
+ u16 reg;
+
+ pr_debug(PREFIX "%s\n", __func__);
switch (freq) {
case 11289600:
@@ -335,18 +347,35 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
case 16934400:
case 18432000:
wm8731->sysclk = freq;
- return 0;
+ break;
+ default:
+ return -EINVAL;
}
- return -EINVAL;
+
+ if (dir == SND_SOC_CLOCK_OUT) {
+ dev_dbg(codec->dev, PREFIX "%s turning on CLKOUT\n", __func__);
+ reg = wm8731_read_reg_cache(codec, WM8731_PWR);
+ reg &= ~(1 << WM8731_PWR_CLKOUTPD);
+ wm8731_write(codec, WM8731_PWR, reg);
+ }
+ else {
+ dev_dbg(codec->dev, PREFIX "%s turning off CLKOUT\n", __func__);
+ reg = wm8731_read_reg_cache(codec, WM8731_PWR);
+ reg |= (1 << WM8731_PWR_CLKOUTPD);
+ wm8731_write(codec, WM8731_PWR, reg);
+ }
+ return 0;
}
static int wm8731_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
+ unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
u16 iface = 0;
+ pr_debug(PREFIX "%s\n", __func__);
+
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
@@ -405,25 +434,20 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
{
u16 reg;
+ pr_debug(PREFIX "%s\n", __func__);
+
switch (level) {
case SND_SOC_BIAS_ON:
-#warning "TODO: figure out how to turn on/off CLKOUT properly"
- wm8731_write(codec, WM8731_PWR, 0);
- break;
case SND_SOC_BIAS_PREPARE:
-#if 1
-#warning "TODO: figure out how to turn on/off CLKOUT properly"
- /* probably has to do with SND_SOC_CLOCK_OUT
- and whether we're master or slave ... */
- wm8731_write(codec, WM8731_PWR, 0);
-#endif
- break;
case SND_SOC_BIAS_STANDBY:
-#if 1
- /* Clear PWROFF, gate CLKOUT, everything else as-is */
+ /* power up the part, but leave other power settings alone */
reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f;
- wm8731_write(codec, WM8731_PWR, reg | 0x0040);
-#endif
+
+ /* turn off CLKOUT in STANDBY */
+ if (level == SND_SOC_BIAS_STANDBY)
+ reg |= (1 << WM8731_PWR_CLKOUTPD);
+
+ wm8731_write(codec, WM8731_PWR, reg);
break;
case SND_SOC_BIAS_OFF:
wm8731_write(codec, WM8731_ACTIVE, 0x0);
@@ -434,14 +458,14 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-#define WM8731_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 | SNDRV_PCM_RATE_88200 |\
- SNDRV_PCM_RATE_96000)
+#define WM8731_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 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000)
-#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
- SNDRV_PCM_FMTBIT_S24_LE)
+#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,
@@ -608,8 +632,7 @@ static int wm8731_register(struct wm8731_priv *wm8731)
/* Disable bypass path by default */
reg = wm8731_read_reg_cache(codec, WM8731_APANA);
- wm8731_write(codec, WM8731_APANA, reg & ~0x4);
-
+ wm8731_write(codec, WM8731_APANA, reg & ~(1 << WM8731_APANA_BYPASS));
wm8731_codec = codec;
ret = snd_soc_register_codec(codec);
@@ -753,14 +776,14 @@ static int __init wm8731_modinit(void)
#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",
+ pr_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",
+ pr_err("Failed to register WM8731 SPI driver: %d\n",
ret);
}
#endif
diff --git a/sound/soc/codecs/wm8731.h b/sound/soc/codecs/wm8731.h
index cd7b806..2e77393 100644
--- a/sound/soc/codecs/wm8731.h
+++ b/sound/soc/codecs/wm8731.h
@@ -31,9 +31,27 @@
#define WM8731_CACHEREGNUM 10
-#define WM8731_SYSCLK 0
+#define WM8731_SYSCLK 0
#define WM8731_DAI 0
+#define WM8731_APANA_MICBOOST 0
+#define WM8731_APANA_MUTEMIC 1
+#define WM8731_APANA_INSEL 2
+#define WM8731_APANA_BYPASS 3
+#define WM8731_APANA_DACSEL 4
+#define WM8731_APANA_SIDETONE 5
+
+#define WM8731_PWR_LINEINPD 0
+#define WM8731_PWR_MICPD 1
+#define WM8731_PWR_ADCPD 2
+#define WM8731_PWR_DACPD 3
+#define WM8731_PWR_OUTPD 4
+#define WM8731_PWR_OSCPD 5
+#define WM8731_PWR_CLKOUTPD 6
+#define WM8731_PWR_POWEROFF 7
+
+
+
extern struct snd_soc_dai wm8731_dai;
extern struct snd_soc_codec_device soc_codec_dev_wm8731;
--
1.5.6.5
More information about the Alsa-devel
mailing list