diff options
Diffstat (limited to 'sound/soc/codecs/wm9712.c')
| -rw-r--r-- | sound/soc/codecs/wm9712.c | 369 |
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"); |
