aboutsummaryrefslogtreecommitdiff
path: root/sound/soc/codecs/wm9712.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/wm9712.c')
-rw-r--r--sound/soc/codecs/wm9712.c369
1 files changed, 155 insertions, 214 deletions
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 524f7450804..c5eb746087b 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -1,22 +1,18 @@
/*
* wm9712.c -- ALSA Soc WM9712 codec support
*
- * Copyright 2006 Wolfson Microelectronics PLC.
- * Author: Liam Girdwood
- * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
+ * Copyright 2006-12 Wolfson Microelectronics PLC.
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
*
* 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.
- *
- * Revision history
- * 4th Feb 2006 Initial version.
*/
#include <linux/init.h>
+#include <linux/slab.h>
#include <linux/module.h>
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <sound/core.h>
@@ -24,9 +20,8 @@
#include <sound/ac97_codec.h>
#include <sound/initval.h>
#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-
-#define WM9712_VERSION "0.4"
+#include <sound/tlv.h>
+#include "wm9712.h"
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg);
@@ -37,23 +32,23 @@ static int ac97_write(struct snd_soc_codec *codec,
* WM9712 register cache
*/
static const u16 wm9712_reg[] = {
- 0x6174, 0x8000, 0x8000, 0x8000, // 6
- 0x0f0f, 0xaaa0, 0xc008, 0x6808, // e
- 0xe808, 0xaaa0, 0xad00, 0x8000, // 16
- 0xe808, 0x3000, 0x8000, 0x0000, // 1e
- 0x0000, 0x0000, 0x0000, 0x000f, // 26
- 0x0405, 0x0410, 0xbb80, 0xbb80, // 2e
- 0x0000, 0xbb80, 0x0000, 0x0000, // 36
- 0x0000, 0x2000, 0x0000, 0x0000, // 3e
- 0x0000, 0x0000, 0x0000, 0x0000, // 46
- 0x0000, 0x0000, 0xf83e, 0xffff, // 4e
- 0x0000, 0x0000, 0x0000, 0xf83e, // 56
- 0x0008, 0x0000, 0x0000, 0x0000, // 5e
- 0xb032, 0x3e00, 0x0000, 0x0000, // 66
- 0x0000, 0x0000, 0x0000, 0x0000, // 6e
- 0x0000, 0x0000, 0x0000, 0x0006, // 76
- 0x0001, 0x0000, 0x574d, 0x4c12, // 7e
- 0x0000, 0x0000 // virtual hp mixers
+ 0x6174, 0x8000, 0x8000, 0x8000, /* 6 */
+ 0x0f0f, 0xaaa0, 0xc008, 0x6808, /* e */
+ 0xe808, 0xaaa0, 0xad00, 0x8000, /* 16 */
+ 0xe808, 0x3000, 0x8000, 0x0000, /* 1e */
+ 0x0000, 0x0000, 0x0000, 0x000f, /* 26 */
+ 0x0405, 0x0410, 0xbb80, 0xbb80, /* 2e */
+ 0x0000, 0xbb80, 0x0000, 0x0000, /* 36 */
+ 0x0000, 0x2000, 0x0000, 0x0000, /* 3e */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 46 */
+ 0x0000, 0x0000, 0xf83e, 0xffff, /* 4e */
+ 0x0000, 0x0000, 0x0000, 0xf83e, /* 56 */
+ 0x0008, 0x0000, 0x0000, 0x0000, /* 5e */
+ 0xb032, 0x3e00, 0x0000, 0x0000, /* 66 */
+ 0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
+ 0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
+ 0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
+ 0x0000, 0x0000 /* virtual hp mixers */
};
/* virtual HP mixers regs */
@@ -75,6 +70,9 @@ static const char *wm9712_rec_sel[] = {"Mic", "NC", "NC", "Speaker Mixer",
static const char *wm9712_ng_type[] = {"Constant Gain", "Mute"};
static const char *wm9712_diff_sel[] = {"Mic", "Line"};
+static const DECLARE_TLV_DB_SCALE(main_tlv, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(boost_tlv, 0, 2000, 0);
+
static const struct soc_enum wm9712_enum[] = {
SOC_ENUM_SINGLE(AC97_PCI_SVID, 14, 4, wm9712_alc_select),
SOC_ENUM_SINGLE(AC97_VIDEO, 12, 4, wm9712_alc_mux),
@@ -94,7 +92,7 @@ static const struct snd_kcontrol_new wm9712_snd_ac97_controls[] = {
SOC_DOUBLE("Speaker Playback Volume", AC97_MASTER, 8, 0, 31, 1),
SOC_SINGLE("Speaker 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_SINGLE("Headphone Playback Switch", AC97_HEADPHONE, 15, 1, 1),
SOC_DOUBLE("PCM Playback Volume", AC97_PCM, 8, 0, 31, 1),
SOC_SINGLE("Speaker Playback ZC Switch", AC97_MASTER, 7, 1, 0),
@@ -134,8 +132,9 @@ SOC_SINGLE("Aux Playback Phone Volume", AC97_CD, 4, 7, 1),
SOC_SINGLE("Phone Volume", AC97_PHONE, 0, 15, 1),
SOC_DOUBLE("Line Capture Volume", AC97_LINE, 8, 0, 31, 1),
-SOC_SINGLE("Capture 20dB Boost Switch", AC97_REC_SEL, 14, 1, 0),
-SOC_SINGLE("Capture to Phone 20dB Boost Switch", AC97_REC_SEL, 11, 1, 1),
+SOC_SINGLE_TLV("Capture Boost Switch", AC97_REC_SEL, 14, 1, 0, boost_tlv),
+SOC_SINGLE_TLV("Capture to Phone Boost Switch", AC97_REC_SEL, 11, 1, 1,
+ boost_tlv),
SOC_SINGLE("3D Upper Cut-off Switch", AC97_3D_CONTROL, 5, 1, 1),
SOC_SINGLE("3D Lower Cut-off Switch", AC97_3D_CONTROL, 4, 1, 1),
@@ -148,30 +147,16 @@ SOC_SINGLE("Playback Attenuate (-6dB) Switch", AC97_MASTER_TONE, 6, 1, 0),
SOC_SINGLE("Bass Volume", AC97_MASTER_TONE, 8, 15, 1),
SOC_SINGLE("Treble Volume", AC97_MASTER_TONE, 0, 15, 1),
-SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
+SOC_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
-SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
+SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
-SOC_SINGLE("Mic 1 Volume", AC97_MIC, 8, 31, 1),
-SOC_SINGLE("Mic 2 Volume", AC97_MIC, 0, 31, 1),
-SOC_SINGLE("Mic 20dB Boost Switch", AC97_MIC, 7, 1, 0),
+SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
+SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
};
-/* 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.
@@ -288,7 +273,7 @@ SOC_DAPM_ENUM("Route", wm9712_enum[9]);
/* Mic select */
static const struct snd_kcontrol_new wm9712_mic_src_controls =
-SOC_DAPM_ENUM("Route", wm9712_enum[7]);
+SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]);
/* diff select */
static const struct snd_kcontrol_new wm9712_diff_sel_controls =
@@ -307,7 +292,9 @@ SND_SOC_DAPM_MUX("Left Capture Select", SND_SOC_NOPM, 0, 0,
&wm9712_capture_selectl_controls),
SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
&wm9712_capture_selectr_controls),
-SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0,
+SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_mic_src_controls),
+SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
&wm9712_mic_src_controls),
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
&wm9712_diff_sel_controls),
@@ -335,6 +322,7 @@ SND_SOC_DAPM_PGA("Out 3 PGA", AC97_INT_PAGING, 5, 1, NULL, 0),
SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
SND_SOC_DAPM_OUTPUT("MONOOUT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
@@ -350,7 +338,7 @@ SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
};
-static const char *audio_map[][3] = {
+static const struct snd_soc_dapm_route wm9712_audio_map[] = {
/* virtual mixer - mixes left & right channels for spk and mono */
{"AC97 Mixer", NULL, "Left DAC"},
{"AC97 Mixer", NULL, "Right DAC"},
@@ -363,7 +351,6 @@ static const char *audio_map[][3] = {
{"Left HP Mixer", "PCM Playback Switch", "Left DAC"},
{"Left HP Mixer", "Mic Sidetone Switch", "Mic PGA"},
{"Left HP Mixer", NULL, "ALC Sidetone Mux"},
- //{"Right HP Mixer", NULL, "HP Mixer"},
/* Right HP mixer */
{"Right HP Mixer", "PCBeep Bypass Switch", "PCBEEP"},
@@ -396,6 +383,18 @@ static const char *audio_map[][3] = {
{"Mic PGA", NULL, "MIC1"},
{"Mic PGA", NULL, "MIC2"},
+ /* microphones */
+ {"Differential Mic", NULL, "MIC1"},
+ {"Differential Mic", NULL, "MIC2"},
+ {"Left Mic Select Source", "Mic 1", "MIC1"},
+ {"Left Mic Select Source", "Mic 2", "MIC2"},
+ {"Left Mic Select Source", "Stereo", "MIC1"},
+ {"Left Mic Select Source", "Differential", "Differential Mic"},
+ {"Right Mic Select Source", "Mic 1", "MIC1"},
+ {"Right Mic Select Source", "Mic 2", "MIC2"},
+ {"Right Mic Select Source", "Stereo", "MIC2"},
+ {"Right Mic Select Source", "Differential", "Differential Mic"},
+
/* left capture selector */
{"Left Capture Select", "Mic", "MIC1"},
{"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
@@ -429,45 +428,25 @@ static const char *audio_map[][3] = {
{"HPOUTR", NULL, "Headphone PGA"},
{"Headphone PGA", NULL, "Right HP Mixer"},
- /* mono hp mixer */
- {"Mono HP Mixer", NULL, "Left HP Mixer"},
- {"Mono HP Mixer", NULL, "Right HP Mixer"},
+ /* mono mixer */
+ {"Mono Mixer", NULL, "Left HP Mixer"},
+ {"Mono Mixer", NULL, "Right HP Mixer"},
/* Out3 Mux */
{"Out3 Mux", "Left", "Left HP Mixer"},
{"Out3 Mux", "Mono", "Phone Mixer"},
- {"Out3 Mux", "Left + Right", "Mono HP Mixer"},
+ {"Out3 Mux", "Left + Right", "Mono Mixer"},
{"Out 3 PGA", NULL, "Out3 Mux"},
{"OUT3", NULL, "Out 3 PGA"},
/* speaker Mux */
{"Speaker Mux", "Speaker Mix", "Speaker Mixer"},
- {"Speaker Mux", "Headphone Mix", "Mono HP Mixer"},
+ {"Speaker Mux", "Headphone Mix", "Mono Mixer"},
{"Speaker PGA", NULL, "Speaker Mux"},
{"LOUT2", NULL, "Speaker PGA"},
{"ROUT2", NULL, "Speaker PGA"},
-
- {NULL, NULL, NULL},
};
-static int wm9712_add_widgets(struct snd_soc_codec *codec)
-{
- int i;
-
- for(i = 0; i < ARRAY_SIZE(wm9712_dapm_widgets); i++) {
- snd_soc_dapm_new_control(codec, &wm9712_dapm_widgets[i]);
- }
-
- /* set up audio path audio_mapnects */
- for(i = 0; audio_map[i][0] != NULL; i++) {
- snd_soc_dapm_connect_input(codec, audio_map[i][0],
- audio_map[i][1], audio_map[i][2]);
- }
-
- snd_soc_dapm_new_widgets(codec);
- return 0;
-}
-
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
@@ -476,11 +455,11 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_REC_GAIN)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
- if (reg > (ARRAY_SIZE(wm9712_reg)))
+ if (reg >= (ARRAY_SIZE(wm9712_reg)))
return -EIO;
return cache[reg];
@@ -492,22 +471,22 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ if (reg < 0x7c)
+ 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;
}
-static int ac97_prepare(struct snd_pcm_substream *substream)
+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->codec;
+ struct snd_soc_codec *codec = dai->codec;
int reg;
u16 vra;
+ struct snd_pcm_runtime *runtime = substream->runtime;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
@@ -520,13 +499,12 @@ static int ac97_prepare(struct snd_pcm_substream *substream)
return ac97_write(codec, reg, runtime->rate);
}
-static int ac97_aux_prepare(struct snd_pcm_substream *substream)
+static int ac97_aux_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->codec;
+ struct snd_soc_codec *codec = dai->codec;
u16 vra, xsle;
+ struct snd_pcm_runtime *runtime = substream->runtime;
vra = ac97_read(codec, AC97_EXTENDED_STATUS);
ac97_write(codec, AC97_EXTENDED_STATUS, vra | 0x1);
@@ -540,81 +518,79 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream)
}
#define WM9712_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
- SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 |\
+ SNDRV_PCM_RATE_48000)
+
+static const struct snd_soc_dai_ops wm9712_dai_ops_hifi = {
+ .prepare = ac97_prepare,
+};
+
+static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
+ .prepare = ac97_aux_prepare,
+};
-struct snd_soc_codec_dai wm9712_dai[] = {
+static struct snd_soc_dai_driver wm9712_dai[] = {
{
- .name = "AC97 HiFi",
- .type = SND_SOC_DAI_AC97_BUS,
+ .name = "wm9712-hifi",
+ .ac97_control = 1,
.playback = {
.stream_name = "HiFi Playback",
.channels_min = 1,
.channels_max = 2,
.rates = WM9712_AC97_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
+ .formats = SND_SOC_STD_AC97_FMTS,},
.capture = {
.stream_name = "HiFi Capture",
.channels_min = 1,
.channels_max = 2,
.rates = WM9712_AC97_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_prepare,},
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &wm9712_dai_ops_hifi,
},
{
- .name = "AC97 Aux",
+ .name = "wm9712-aux",
.playback = {
.stream_name = "Aux Playback",
.channels_min = 1,
.channels_max = 1,
.rates = WM9712_AC97_RATES,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,},
- .ops = {
- .prepare = ac97_aux_prepare,},
+ .formats = SND_SOC_STD_AC97_FMTS,},
+ .ops = &wm9712_dai_ops_aux,
}
};
-EXPORT_SYMBOL_GPL(wm9712_dai);
-static int wm9712_dapm_event(struct snd_soc_codec *codec, int event)
+static int wm9712_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
{
- u16 reg;
-
- switch (event) {
- case SNDRV_CTL_POWER_D0: /* full On */
- /* liam - maybe enable thermal shutdown */
- reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xdfff;
- ac97_write(codec, AC97_EXTENDED_MID, reg);
- break;
- case SNDRV_CTL_POWER_D1: /* partial On */
- case SNDRV_CTL_POWER_D2: /* partial On */
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
break;
- case SNDRV_CTL_POWER_D3hot: /* Off, with power */
- /* enable master bias and vmid */
- reg = ac97_read(codec, AC97_EXTENDED_MID) & 0xbbff;
- ac97_write(codec, AC97_EXTENDED_MID, reg);
+ case SND_SOC_BIAS_STANDBY:
ac97_write(codec, AC97_POWERDOWN, 0x0000);
break;
- case SNDRV_CTL_POWER_D3cold: /* Off, without power */
+ case SND_SOC_BIAS_OFF:
/* disable everything including AC link */
- ac97_write(codec, AC97_EXTENDED_MID, 0xffff);
ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff);
ac97_write(codec, AC97_POWERDOWN, 0xffff);
break;
}
- codec->dapm_state = event;
+ codec->dapm.bias_level = level;
return 0;
}
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
- if (!(ac97_read(codec, 0) & 0x8000))
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
+ if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (ac97_read(codec, 0) & 0x8000)
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
+ if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
@@ -623,150 +599,115 @@ err:
return -EIO;
}
-static int wm9712_soc_suspend(struct platform_device *pdev,
- pm_message_t state)
+static int wm9712_soc_suspend(struct snd_soc_codec *codec)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
-
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
-static int wm9712_soc_resume(struct platform_device *pdev)
+static int wm9712_soc_resume(struct snd_soc_codec *codec)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->codec;
int i, ret;
u16 *cache = codec->reg_cache;
ret = wm9712_reset(codec, 1);
- if (ret < 0){
+ if (ret < 0) {
printk(KERN_ERR "could not reset AC97 codec\n");
return ret;
}
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
if (ret == 0) {
/* Sync reg_cache with the hardware after cold reset */
- for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i+=2) {
+ for (i = 2; i < ARRAY_SIZE(wm9712_reg) << 1; i += 2) {
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
- (i > 0x58 && i != 0x5c))
+ (i > 0x58 && i != 0x5c))
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
- if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0)
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0);
-
return ret;
}
-static int wm9712_soc_probe(struct platform_device *pdev)
+static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec;
int ret = 0;
- 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)
- return -ENOMEM;
- codec = socdev->codec;
- mutex_init(&codec->mutex);
-
- codec->reg_cache = kmemdup(wm9712_reg, sizeof(wm9712_reg), GFP_KERNEL);
-
- if (codec->reg_cache == NULL) {
- ret = -ENOMEM;
- goto cache_err;
- }
- codec->reg_cache_size = sizeof(wm9712_reg);
- codec->reg_cache_step = 2;
-
- codec->name = "WM9712";
- codec->owner = THIS_MODULE;
- codec->dai = wm9712_dai;
- codec->num_dai = ARRAY_SIZE(wm9712_dai);
- codec->write = ac97_write;
- codec->read = ac97_read;
- codec->dapm_event = wm9712_dapm_event;
- INIT_LIST_HEAD(&codec->dapm_widgets);
- INIT_LIST_HEAD(&codec->dapm_paths);
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
- goto codec_err;
+ return ret;
}
- /* register pcms */
- ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1);
- if (ret < 0)
- goto pcm_err;
-
ret = wm9712_reset(codec, 0);
if (ret < 0) {
- printk(KERN_ERR "AC97 link error\n");
+ printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
goto reset_err;
}
/* set alc mux to none */
ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
- wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot);
- wm9712_add_controls(codec);
- wm9712_add_widgets(codec);
- ret = snd_soc_register_card(socdev);
- if (ret < 0) {
- printk(KERN_ERR "wm9712: failed to register card\n");
- goto reset_err;
- }
+ wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
+ ARRAY_SIZE(wm9712_snd_ac97_controls));
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->codec);
- socdev->codec = NULL;
return ret;
}
-static int wm9712_soc_remove(struct platform_device *pdev)
+static int wm9712_soc_remove(struct snd_soc_codec *codec)
{
- struct snd_soc_device *socdev = platform_get_drvdata(pdev);
- struct snd_soc_codec *codec = socdev->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_wm9712 = {
+static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.probe = wm9712_soc_probe,
.remove = wm9712_soc_remove,
.suspend = wm9712_soc_suspend,
.resume = wm9712_soc_resume,
+ .read = ac97_read,
+ .write = ac97_write,
+ .set_bias_level = wm9712_set_bias_level,
+ .reg_cache_size = ARRAY_SIZE(wm9712_reg),
+ .reg_word_size = sizeof(u16),
+ .reg_cache_step = 2,
+ .reg_cache_default = wm9712_reg,
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
+};
+
+static int wm9712_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
+}
+
+static int wm9712_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+static struct platform_driver wm9712_codec_driver = {
+ .driver = {
+ .name = "wm9712-codec",
+ .owner = THIS_MODULE,
+ },
+
+ .probe = wm9712_probe,
+ .remove = wm9712_remove,
};
-EXPORT_SYMBOL_GPL(soc_codec_dev_wm9712);
+module_platform_driver(wm9712_codec_driver);
MODULE_DESCRIPTION("ASoC WM9711/WM9712 driver");
MODULE_AUTHOR("Liam Girdwood");