diff options
Diffstat (limited to 'sound/soc/codecs/wm8400.c')
| -rw-r--r-- | sound/soc/codecs/wm8400.c | 553 |
1 files changed, 191 insertions, 362 deletions
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c index 510efa60400..06e913d3fea 100644 --- a/sound/soc/codecs/wm8400.c +++ b/sound/soc/codecs/wm8400.c @@ -1,7 +1,7 @@ /* * wm8400.c -- WM8400 ALSA Soc Audio driver * - * Copyright 2008, 2009 Wolfson Microelectronics PLC. + * Copyright 2008-11 Wolfson Microelectronics PLC. * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> * * This program is free software; you can redistribute it and/or modify it @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> @@ -21,23 +22,16 @@ #include <linux/regulator/consumer.h> #include <linux/mfd/wm8400-audio.h> #include <linux/mfd/wm8400-private.h> +#include <linux/mfd/core.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> -#include <sound/soc-dapm.h> #include <sound/initval.h> #include <sound/tlv.h> #include "wm8400.h" -/* Fake register for internal state */ -#define WM8400_INTDRIVBITS (WM8400_REGISTER_COUNT + 1) -#define WM8400_INMIXL_PWR 0 -#define WM8400_AINLMUX_PWR 1 -#define WM8400_INMIXR_PWR 2 -#define WM8400_AINRMUX_PWR 3 - static struct regulator_bulk_data power[] = { { .supply = "I2S1VDD", @@ -64,7 +58,7 @@ static struct regulator_bulk_data power[] = { /* codec private data */ struct wm8400_priv { - struct snd_soc_codec codec; + struct snd_soc_codec *codec; struct wm8400 *wm8400; u16 fake_register; unsigned int sysclk; @@ -73,59 +67,33 @@ struct wm8400_priv { int fll_in, fll_out; }; -static inline unsigned int wm8400_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm8400_priv *wm8400 = codec->private_data; - - if (reg == WM8400_INTDRIVBITS) - return wm8400->fake_register; - else - return wm8400_reg_read(wm8400->wm8400, reg); -} - -/* - * write to the wm8400 register space - */ -static int wm8400_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct wm8400_priv *wm8400 = codec->private_data; - - if (reg == WM8400_INTDRIVBITS) { - wm8400->fake_register = value; - return 0; - } else - return wm8400_set_bits(wm8400->wm8400, reg, 0xffff, value); -} - static void wm8400_codec_reset(struct snd_soc_codec *codec) { - struct wm8400_priv *wm8400 = codec->private_data; + struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); wm8400_reset_codec_reg_cache(wm8400->wm8400); } -static const DECLARE_TLV_DB_LINEAR(rec_mix_tlv, -1500, 600); +static const DECLARE_TLV_DB_SCALE(rec_mix_tlv, -1500, 600, 0); -static const DECLARE_TLV_DB_LINEAR(in_pga_tlv, -1650, 3000); +static const DECLARE_TLV_DB_SCALE(in_pga_tlv, -1650, 3000, 0); -static const DECLARE_TLV_DB_LINEAR(out_mix_tlv, -2100, 0); +static const DECLARE_TLV_DB_SCALE(out_mix_tlv, -2100, 0, 0); -static const DECLARE_TLV_DB_LINEAR(out_pga_tlv, -7300, 600); +static const DECLARE_TLV_DB_SCALE(out_pga_tlv, -7300, 600, 0); -static const DECLARE_TLV_DB_LINEAR(out_omix_tlv, -600, 0); +static const DECLARE_TLV_DB_SCALE(out_omix_tlv, -600, 0, 0); -static const DECLARE_TLV_DB_LINEAR(out_dac_tlv, -7163, 0); +static const DECLARE_TLV_DB_SCALE(out_dac_tlv, -7163, 0, 0); -static const DECLARE_TLV_DB_LINEAR(in_adc_tlv, -7163, 1763); +static const DECLARE_TLV_DB_SCALE(in_adc_tlv, -7163, 1763, 0); -static const DECLARE_TLV_DB_LINEAR(out_sidetone_tlv, -3600, 0); +static const DECLARE_TLV_DB_SCALE(out_sidetone_tlv, -3600, 0, 0); static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; int reg = mc->reg; @@ -137,36 +105,35 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol, return ret; /* now hit the volume update bits (always bit 8) */ - val = wm8400_read(codec, reg); - return wm8400_write(codec, reg, val | 0x0100); + val = snd_soc_read(codec, reg); + return snd_soc_write(codec, reg, val | 0x0100); } #define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ - SNDRV_CTL_ELEM_ACCESS_READWRITE,\ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw, \ - .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \ - .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) } + SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \ + snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array) static const char *wm8400_digital_sidetone[] = {"None", "Left ADC", "Right ADC", "Reserved"}; -static const struct soc_enum wm8400_left_digital_sidetone_enum = -SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE, - WM8400_ADC_TO_DACL_SHIFT, 2, wm8400_digital_sidetone); +static SOC_ENUM_SINGLE_DECL(wm8400_left_digital_sidetone_enum, + WM8400_DIGITAL_SIDE_TONE, + WM8400_ADC_TO_DACL_SHIFT, + wm8400_digital_sidetone); -static const struct soc_enum wm8400_right_digital_sidetone_enum = -SOC_ENUM_SINGLE(WM8400_DIGITAL_SIDE_TONE, - WM8400_ADC_TO_DACR_SHIFT, 2, wm8400_digital_sidetone); +static SOC_ENUM_SINGLE_DECL(wm8400_right_digital_sidetone_enum, + WM8400_DIGITAL_SIDE_TONE, + WM8400_ADC_TO_DACR_SHIFT, + wm8400_digital_sidetone); static const char *wm8400_adcmode[] = {"Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3"}; -static const struct soc_enum wm8400_right_adcmode_enum = -SOC_ENUM_SINGLE(WM8400_ADC_CTRL, WM8400_ADC_HPF_CUT_SHIFT, 3, wm8400_adcmode); +static SOC_ENUM_SINGLE_DECL(wm8400_right_adcmode_enum, + WM8400_ADC_CTRL, + WM8400_ADC_HPF_CUT_SHIFT, + wm8400_adcmode); static const struct snd_kcontrol_new wm8400_snd_controls[] = { /* INMIXL */ @@ -352,43 +319,10 @@ SOC_SINGLE("RIN34 Mute Switch", WM8400_RIGHT_LINE_INPUT_3_4_VOLUME, }; -/* add non dapm controls */ -static int wm8400_add_controls(struct snd_soc_codec *codec) -{ - return snd_soc_add_controls(codec, wm8400_snd_controls, - ARRAY_SIZE(wm8400_snd_controls)); -} - /* * _DAPM_ Controls */ -static int inmixer_event (struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event) -{ - u16 reg, fakepower; - - reg = wm8400_read(w->codec, WM8400_POWER_MANAGEMENT_2); - fakepower = wm8400_read(w->codec, WM8400_INTDRIVBITS); - - if (fakepower & ((1 << WM8400_INMIXL_PWR) | - (1 << WM8400_AINLMUX_PWR))) { - reg |= WM8400_AINL_ENA; - } else { - reg &= ~WM8400_AINL_ENA; - } - - if (fakepower & ((1 << WM8400_INMIXR_PWR) | - (1 << WM8400_AINRMUX_PWR))) { - reg |= WM8400_AINR_ENA; - } else { - reg &= ~WM8400_AINL_ENA; - } - wm8400_write(w->codec, WM8400_POWER_MANAGEMENT_2, reg); - - return 0; -} - static int outmixer_event (struct snd_soc_dapm_widget *w, struct snd_kcontrol * kcontrol, int event) { @@ -400,7 +334,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, switch (reg_shift) { case WM8400_SPEAKER_MIXER | (WM8400_LDSPK << 8) : - reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER1); + reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER1); if (reg & WM8400_LDLO) { printk(KERN_WARNING "Cannot set as Output Mixer 1 LDLO Set\n"); @@ -408,7 +342,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } break; case WM8400_SPEAKER_MIXER | (WM8400_RDSPK << 8): - reg = wm8400_read(w->codec, WM8400_OUTPUT_MIXER2); + reg = snd_soc_read(w->codec, WM8400_OUTPUT_MIXER2); if (reg & WM8400_RDRO) { printk(KERN_WARNING "Cannot set as Output Mixer 2 RDRO Set\n"); @@ -416,7 +350,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } break; case WM8400_OUTPUT_MIXER1 | (WM8400_LDLO << 8): - reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER); + reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER); if (reg & WM8400_LDSPK) { printk(KERN_WARNING "Cannot set as Speaker Mixer LDSPK Set\n"); @@ -424,7 +358,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, } break; case WM8400_OUTPUT_MIXER2 | (WM8400_RDRO << 8): - reg = wm8400_read(w->codec, WM8400_SPEAKER_MIXER); + reg = snd_soc_read(w->codec, WM8400_SPEAKER_MIXER); if (reg & WM8400_RDSPK) { printk(KERN_WARNING "Cannot set as Speaker Mixer RDSPK Set\n"); @@ -439,7 +373,7 @@ static int outmixer_event (struct snd_soc_dapm_widget *w, /* INMIX dB values */ static const unsigned int in_mix_tlv[] = { TLV_DB_RANGE_HEAD(1), - 0,7, TLV_DB_LINEAR_ITEM(-1200, 600), + 0,7, TLV_DB_SCALE_ITEM(-1200, 600, 0), }; /* Left In PGA Connections */ @@ -492,9 +426,10 @@ SOC_DAPM_SINGLE("RINPGA34 Switch", WM8400_INPUT_MIXER3, WM8400_L34MNB_SHIFT, static const char *wm8400_ainlmux[] = {"INMIXL Mix", "RXVOICE Mix", "DIFFINL Mix"}; -static const struct soc_enum wm8400_ainlmux_enum = -SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINLMODE_SHIFT, - ARRAY_SIZE(wm8400_ainlmux), wm8400_ainlmux); +static SOC_ENUM_SINGLE_DECL(wm8400_ainlmux_enum, + WM8400_INPUT_MIXER1, + WM8400_AINLMODE_SHIFT, + wm8400_ainlmux); static const struct snd_kcontrol_new wm8400_dapm_ainlmux_controls = SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum); @@ -505,9 +440,10 @@ SOC_DAPM_ENUM("Route", wm8400_ainlmux_enum); static const char *wm8400_ainrmux[] = {"INMIXR Mix", "RXVOICE Mix", "DIFFINR Mix"}; -static const struct soc_enum wm8400_ainrmux_enum = -SOC_ENUM_SINGLE( WM8400_INPUT_MIXER1, WM8400_AINRMODE_SHIFT, - ARRAY_SIZE(wm8400_ainrmux), wm8400_ainrmux); +static SOC_ENUM_SINGLE_DECL(wm8400_ainrmux_enum, + WM8400_INPUT_MIXER1, + WM8400_AINRMODE_SHIFT, + wm8400_ainrmux); static const struct snd_kcontrol_new wm8400_dapm_ainrmux_controls = SOC_DAPM_ENUM("Route", wm8400_ainrmux_enum); @@ -669,27 +605,26 @@ SND_SOC_DAPM_MIXER("RIN34 PGA", WM8400_POWER_MANAGEMENT_2, 0, &wm8400_dapm_rin34_pga_controls[0], ARRAY_SIZE(wm8400_dapm_rin34_pga_controls)), +SND_SOC_DAPM_SUPPLY("INL", WM8400_POWER_MANAGEMENT_2, WM8400_AINL_ENA_SHIFT, + 0, NULL, 0), +SND_SOC_DAPM_SUPPLY("INR", WM8400_POWER_MANAGEMENT_2, WM8400_AINR_ENA_SHIFT, + 0, NULL, 0), + /* INMIXL */ -SND_SOC_DAPM_MIXER_E("INMIXL", WM8400_INTDRIVBITS, WM8400_INMIXL_PWR, 0, +SND_SOC_DAPM_MIXER("INMIXL", SND_SOC_NOPM, 0, 0, &wm8400_dapm_inmixl_controls[0], - ARRAY_SIZE(wm8400_dapm_inmixl_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8400_dapm_inmixl_controls)), /* AINLMUX */ -SND_SOC_DAPM_MUX_E("AILNMUX", WM8400_INTDRIVBITS, WM8400_AINLMUX_PWR, 0, - &wm8400_dapm_ainlmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("AILNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainlmux_controls), /* INMIXR */ -SND_SOC_DAPM_MIXER_E("INMIXR", WM8400_INTDRIVBITS, WM8400_INMIXR_PWR, 0, +SND_SOC_DAPM_MIXER("INMIXR", SND_SOC_NOPM, 0, 0, &wm8400_dapm_inmixr_controls[0], - ARRAY_SIZE(wm8400_dapm_inmixr_controls), - inmixer_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + ARRAY_SIZE(wm8400_dapm_inmixr_controls)), /* AINRMUX */ -SND_SOC_DAPM_MUX_E("AIRNMUX", WM8400_INTDRIVBITS, WM8400_AINRMUX_PWR, 0, - &wm8400_dapm_ainrmux_controls, inmixer_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +SND_SOC_DAPM_MUX("AIRNMUX", SND_SOC_NOPM, 0, 0, &wm8400_dapm_ainrmux_controls), /* Output Side */ /* DACs */ @@ -765,8 +700,8 @@ SND_SOC_DAPM_PGA("ROPGA", WM8400_POWER_MANAGEMENT_3, WM8400_ROPGA_ENA_SHIFT, 0, NULL, 0), /* MICBIAS */ -SND_SOC_DAPM_MICBIAS("MICBIAS", WM8400_POWER_MANAGEMENT_1, - WM8400_MIC1BIAS_ENA_SHIFT, 0), +SND_SOC_DAPM_SUPPLY("MICBIAS", WM8400_POWER_MANAGEMENT_1, + WM8400_MIC1BIAS_ENA_SHIFT, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("LON"), SND_SOC_DAPM_OUTPUT("LOP"), @@ -782,7 +717,7 @@ SND_SOC_DAPM_OUTPUT("RON"), SND_SOC_DAPM_OUTPUT("Internal DAC Sink"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8400_dapm_routes[] = { /* Make DACs turn on when playing even if not mixed into any outputs */ {"Internal DAC Sink", NULL, "Left DAC"}, {"Internal DAC Sink", NULL, "Right DAC"}, @@ -800,11 +735,13 @@ static const struct snd_soc_dapm_route audio_map[] = { {"LIN34 PGA", "LIN3 Switch", "LIN3"}, {"LIN34 PGA", "LIN4 Switch", "LIN4/RXN"}, /* INMIXL */ + {"INMIXL", NULL, "INL"}, {"INMIXL", "Record Left Volume", "LOMIX"}, {"INMIXL", "LIN2 Volume", "LIN2"}, {"INMIXL", "LINPGA12 Switch", "LIN12 PGA"}, {"INMIXL", "LINPGA34 Switch", "LIN34 PGA"}, /* AILNMUX */ + {"AILNMUX", NULL, "INL"}, {"AILNMUX", "INMIXL Mix", "INMIXL"}, {"AILNMUX", "DIFFINL Mix", "LIN12 PGA"}, {"AILNMUX", "DIFFINL Mix", "LIN34 PGA"}, @@ -819,12 +756,14 @@ static const struct snd_soc_dapm_route audio_map[] = { /* RIN34 PGA */ {"RIN34 PGA", "RIN3 Switch", "RIN3"}, {"RIN34 PGA", "RIN4 Switch", "RIN4/RXP"}, - /* INMIXL */ + /* INMIXR */ + {"INMIXR", NULL, "INR"}, {"INMIXR", "Record Right Volume", "ROMIX"}, {"INMIXR", "RIN2 Volume", "RIN2"}, {"INMIXR", "RINPGA12 Switch", "RIN12 PGA"}, {"INMIXR", "RINPGA34 Switch", "RIN34 PGA"}, /* AIRNMUX */ + {"AIRNMUX", NULL, "INR"}, {"AIRNMUX", "INMIXR Mix", "INMIXR"}, {"AIRNMUX", "DIFFINR Mix", "RIN12 PGA"}, {"AIRNMUX", "DIFFINR Mix", "RIN34 PGA"}, @@ -908,17 +847,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"RON", NULL, "RONMIX"}, }; -static int wm8400_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_dapm_new_controls(codec, wm8400_dapm_widgets, - ARRAY_SIZE(wm8400_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - snd_soc_dapm_new_widgets(codec); - return 0; -} - /* * Clock after FLL and dividers */ @@ -926,7 +854,7 @@ static int wm8400_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8400_priv *wm8400 = codec->private_data; + struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); wm8400->sysclk = freq; return 0; @@ -954,7 +882,7 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors, factors->outdiv *= 2; if (factors->outdiv > 32) { dev_err(wm8400->wm8400->dev, - "Unsupported FLL output frequency %dHz\n", + "Unsupported FLL output frequency %uHz\n", Fout); return -EINVAL; } @@ -1003,7 +931,7 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors, factors->k = K / 10; dev_dbg(wm8400->wm8400->dev, - "FLL: Fref=%d Fout=%d N=%x K=%x, FRATIO=%x OUTDIV=%x\n", + "FLL: Fref=%u Fout=%u N=%x K=%x, FRATIO=%x OUTDIV=%x\n", Fref, Fout, factors->n, factors->k, factors->fratio, factors->outdiv); @@ -1011,10 +939,11 @@ static int fll_factors(struct wm8400_priv *wm8400, struct fll_factors *factors, } static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, - unsigned int freq_in, unsigned int freq_out) + int source, unsigned int freq_in, + unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8400_priv *wm8400 = codec->private_data; + struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); struct fll_factors factors; int ret; u16 reg; @@ -1022,39 +951,44 @@ static int wm8400_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, if (freq_in == wm8400->fll_in && freq_out == wm8400->fll_out) return 0; - if (freq_out != 0) { + if (freq_out) { ret = fll_factors(wm8400, &factors, freq_in, freq_out); if (ret != 0) return ret; + } else { + /* Bodge GCC 4.4.0 uninitialised variable warning - it + * doesn't seem capable of working out that we exit if + * freq_out is 0 before any of the uses. */ + memset(&factors, 0, sizeof(factors)); } wm8400->fll_out = freq_out; wm8400->fll_in = freq_in; /* We *must* disable the FLL before any changes */ - reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_2); + reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_2); reg &= ~WM8400_FLL_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_2, reg); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_2, reg); - reg = wm8400_read(codec, WM8400_FLL_CONTROL_1); + reg = snd_soc_read(codec, WM8400_FLL_CONTROL_1); reg &= ~WM8400_FLL_OSC_ENA; - wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); + snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg); - if (freq_out == 0) + if (!freq_out) return 0; reg &= ~(WM8400_FLL_REF_FREQ | WM8400_FLL_FRATIO_MASK); reg |= WM8400_FLL_FRAC | factors.fratio; reg |= factors.freq_ref << WM8400_FLL_REF_FREQ_SHIFT; - wm8400_write(codec, WM8400_FLL_CONTROL_1, reg); + snd_soc_write(codec, WM8400_FLL_CONTROL_1, reg); - wm8400_write(codec, WM8400_FLL_CONTROL_2, factors.k); - wm8400_write(codec, WM8400_FLL_CONTROL_3, factors.n); + snd_soc_write(codec, WM8400_FLL_CONTROL_2, factors.k); + snd_soc_write(codec, WM8400_FLL_CONTROL_3, factors.n); - reg = wm8400_read(codec, WM8400_FLL_CONTROL_4); - reg &= WM8400_FLL_OUTDIV_MASK; + reg = snd_soc_read(codec, WM8400_FLL_CONTROL_4); + reg &= ~WM8400_FLL_OUTDIV_MASK; reg |= factors.outdiv; - wm8400_write(codec, WM8400_FLL_CONTROL_4, reg); + snd_soc_write(codec, WM8400_FLL_CONTROL_4, reg); return 0; } @@ -1068,8 +1002,8 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_codec *codec = codec_dai->codec; u16 audio1, audio3; - audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); - audio3 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_3); + audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1); + audio3 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_3); /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -1110,8 +1044,8 @@ static int wm8400_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); - wm8400_write(codec, WM8400_AUDIO_INTERFACE_3, audio3); + snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); + snd_soc_write(codec, WM8400_AUDIO_INTERFACE_3, audio3); return 0; } @@ -1123,24 +1057,24 @@ static int wm8400_set_dai_clkdiv(struct snd_soc_dai *codec_dai, switch (div_id) { case WM8400_MCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_2) & + reg = snd_soc_read(codec, WM8400_CLOCKING_2) & ~WM8400_MCLK_DIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_2, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_2, reg | div); break; case WM8400_DACCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_2) & + reg = snd_soc_read(codec, WM8400_CLOCKING_2) & ~WM8400_DAC_CLKDIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_2, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_2, reg | div); break; case WM8400_ADCCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_2) & + reg = snd_soc_read(codec, WM8400_CLOCKING_2) & ~WM8400_ADC_CLKDIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_2, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_2, reg | div); break; case WM8400_BCLK_DIV: - reg = wm8400_read(codec, WM8400_CLOCKING_1) & + reg = snd_soc_read(codec, WM8400_CLOCKING_1) & ~WM8400_BCLK_DIV_MASK; - wm8400_write(codec, WM8400_CLOCKING_1, reg | div); + snd_soc_write(codec, WM8400_CLOCKING_1, reg | div); break; default: return -EINVAL; @@ -1156,10 +1090,8 @@ static int wm8400_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; - u16 audio1 = wm8400_read(codec, WM8400_AUDIO_INTERFACE_1); + struct snd_soc_codec *codec = dai->codec; + u16 audio1 = snd_soc_read(codec, WM8400_AUDIO_INTERFACE_1); audio1 &= ~WM8400_AIF_WL_MASK; /* bit size */ @@ -1177,19 +1109,19 @@ static int wm8400_hw_params(struct snd_pcm_substream *substream, break; } - wm8400_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); + snd_soc_write(codec, WM8400_AUDIO_INTERFACE_1, audio1); return 0; } static int wm8400_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - u16 val = wm8400_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE; + u16 val = snd_soc_read(codec, WM8400_DAC_CTRL) & ~WM8400_DAC_MUTE; if (mute) - wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); + snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); else - wm8400_write(codec, WM8400_DAC_CTRL, val); + snd_soc_write(codec, WM8400_DAC_CTRL, val); return 0; } @@ -1198,7 +1130,7 @@ static int wm8400_mute(struct snd_soc_dai *dai, int mute) static int wm8400_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct wm8400_priv *wm8400 = codec->private_data; + struct wm8400_priv *wm8400 = snd_soc_codec_get_drvdata(codec); u16 val; int ret; @@ -1208,13 +1140,13 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_PREPARE: /* VMID=2*50k */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) & + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) & ~WM8400_VMID_MODE_MASK; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x2); break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { ret = regulator_bulk_enable(ARRAY_SIZE(power), &power[0]); if (ret != 0) { @@ -1224,74 +1156,74 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, return ret; } - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, WM8400_CODEC_ENA | WM8400_SYSCLK_ENA); /* Enable POBCTRL, SOFT_ST, VMIDTOG and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_BUFDCOPEN | WM8400_POBCTRL); msleep(50); /* Enable VREF & VMID at 2x50k */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); val |= 0x2 | WM8400_VREF_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); /* Enable BUFIOEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_BUFDCOPEN | WM8400_POBCTRL | WM8400_BUFIOEN); /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN); + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_BUFIOEN); } /* VMID=2*300k */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1) & + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1) & ~WM8400_VMID_MODE_MASK; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val | 0x4); break; case SND_SOC_BIAS_OFF: /* Enable POBCTRL and SOFT_ST */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_POBCTRL | WM8400_BUFIOEN); /* Enable POBCTRL, SOFT_ST and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | + snd_soc_write(codec, WM8400_ANTIPOP2, WM8400_SOFTST | WM8400_BUFDCOPEN | WM8400_POBCTRL | WM8400_BUFIOEN); /* mute DAC */ - val = wm8400_read(codec, WM8400_DAC_CTRL); - wm8400_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); + val = snd_soc_read(codec, WM8400_DAC_CTRL); + snd_soc_write(codec, WM8400_DAC_CTRL, val | WM8400_DAC_MUTE); /* Enable any disabled outputs */ - val = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); + val = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); val |= WM8400_SPK_ENA | WM8400_OUT3_ENA | WM8400_OUT4_ENA | WM8400_LOUT_ENA | WM8400_ROUT_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); /* Disable VMID */ val &= ~WM8400_VMID_MODE_MASK; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); msleep(300); /* Enable all output discharge bits */ - wm8400_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE | + snd_soc_write(codec, WM8400_ANTIPOP1, WM8400_DIS_LLINE | WM8400_DIS_RLINE | WM8400_DIS_OUT3 | WM8400_DIS_OUT4 | WM8400_DIS_LOUT | WM8400_DIS_ROUT); /* Disable VREF */ val &= ~WM8400_VREF_ENA; - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, val); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, val); /* disable POBCTRL, SOFT_ST and BUFDCOPEN */ - wm8400_write(codec, WM8400_ANTIPOP2, 0x0); + snd_soc_write(codec, WM8400_ANTIPOP2, 0x0); ret = regulator_bulk_disable(ARRAY_SIZE(power), &power[0]); @@ -1301,7 +1233,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -1310,7 +1242,7 @@ static int wm8400_set_bias_level(struct snd_soc_codec *codec, #define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) -static struct snd_soc_dai_ops wm8400_dai_ops = { +static const struct snd_soc_dai_ops wm8400_dai_ops = { .hw_params = wm8400_hw_params, .digital_mute = wm8400_mute, .set_fmt = wm8400_set_dai_fmt, @@ -1326,10 +1258,9 @@ static struct snd_soc_dai_ops wm8400_dai_ops = { * 1. ADC/DAC on Primary Interface * 2. ADC on Primary Interface/DAC on secondary */ -struct snd_soc_dai wm8400_dai = { +static struct snd_soc_dai_driver wm8400_dai = { /* ADC/DAC on primary */ - .name = "WM8400 ADC/DAC Primary", - .id = 1, + .name = "wm8400-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, @@ -1346,235 +1277,133 @@ struct snd_soc_dai wm8400_dai = { }, .ops = &wm8400_dai_ops, }; -EXPORT_SYMBOL_GPL(wm8400_dai); -static int wm8400_suspend(struct platform_device *pdev, pm_message_t state) +static int wm8400_suspend(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } -static int wm8400_resume(struct platform_device *pdev) +static int wm8400_resume(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); return 0; } -static struct snd_soc_codec *wm8400_codec; - -static int wm8400_probe(struct platform_device *pdev) -{ - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec; - int ret; - - if (!wm8400_codec) { - dev_err(&pdev->dev, "wm8400 not yet discovered\n"); - return -ENODEV; - } - codec = wm8400_codec; - - socdev->card->codec = codec; - - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - dev_err(&pdev->dev, "failed to create pcms\n"); - goto pcm_err; - } - - wm8400_add_controls(codec); - wm8400_add_widgets(codec); - - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to register card\n"); - goto card_err; - } - - return ret; - -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -pcm_err: - return ret; -} - -/* power down chip */ -static int wm8400_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_wm8400 = { - .probe = wm8400_probe, - .remove = wm8400_remove, - .suspend = wm8400_suspend, - .resume = wm8400_resume, -}; - static void wm8400_probe_deferred(struct work_struct *work) { struct wm8400_priv *priv = container_of(work, struct wm8400_priv, work); - struct snd_soc_codec *codec = &priv->codec; - int ret; + struct snd_soc_codec *codec = priv->codec; /* charge output caps */ wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - /* We're done, tell the subsystem. */ - ret = snd_soc_register_codec(codec); - if (ret != 0) { - dev_err(priv->wm8400->dev, - "Failed to register codec: %d\n", ret); - goto err; - } - - ret = snd_soc_register_dai(&wm8400_dai); - if (ret != 0) { - dev_err(priv->wm8400->dev, - "Failed to register DAI: %d\n", ret); - goto err_codec; - } - - return; - -err_codec: - snd_soc_unregister_codec(codec); -err: - wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF); } -static int wm8400_codec_probe(struct platform_device *dev) +static int wm8400_codec_probe(struct snd_soc_codec *codec) { + struct wm8400 *wm8400 = dev_get_platdata(codec->dev); struct wm8400_priv *priv; int ret; u16 reg; - struct snd_soc_codec *codec; - priv = kzalloc(sizeof(struct wm8400_priv), GFP_KERNEL); + priv = devm_kzalloc(codec->dev, sizeof(struct wm8400_priv), + GFP_KERNEL); if (priv == NULL) return -ENOMEM; - codec = &priv->codec; - codec->private_data = priv; - codec->control_data = dev->dev.driver_data; - priv->wm8400 = dev->dev.driver_data; + snd_soc_codec_set_drvdata(codec, priv); + priv->wm8400 = wm8400; + priv->codec = codec; - ret = regulator_bulk_get(priv->wm8400->dev, + ret = devm_regulator_bulk_get(wm8400->dev, ARRAY_SIZE(power), &power[0]); if (ret != 0) { - dev_err(&dev->dev, "Failed to get regulators: %d\n", ret); - goto err; + dev_err(codec->dev, "Failed to get regulators: %d\n", ret); + return ret; } - codec->dev = &dev->dev; - wm8400_dai.dev = &dev->dev; - - codec->name = "WM8400"; - codec->owner = THIS_MODULE; - codec->read = wm8400_read; - codec->write = wm8400_write; - codec->bias_level = SND_SOC_BIAS_OFF; - codec->set_bias_level = wm8400_set_bias_level; - codec->dai = &wm8400_dai; - codec->num_dai = 1; - codec->reg_cache_size = WM8400_REGISTER_COUNT; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); INIT_WORK(&priv->work, wm8400_probe_deferred); wm8400_codec_reset(codec); - reg = wm8400_read(codec, WM8400_POWER_MANAGEMENT_1); - wm8400_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA); + reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg | WM8400_CODEC_ENA); /* Latch volume update bits */ - reg = wm8400_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME); - wm8400_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME, + reg = snd_soc_read(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME); + snd_soc_write(codec, WM8400_LEFT_LINE_INPUT_1_2_VOLUME, reg & WM8400_IPVU); - reg = wm8400_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME); - wm8400_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, + reg = snd_soc_read(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME); + snd_soc_write(codec, WM8400_RIGHT_LINE_INPUT_1_2_VOLUME, reg & WM8400_IPVU); - wm8400_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); - wm8400_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); - - wm8400_codec = codec; - - if (!schedule_work(&priv->work)) { - ret = -EINVAL; - goto err_regulator; - } + snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8)); + snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8)); + if (!schedule_work(&priv->work)) + return -EINVAL; return 0; - -err_regulator: - wm8400_codec = NULL; - regulator_bulk_free(ARRAY_SIZE(power), power); -err: - kfree(priv); - return ret; } -static int __exit wm8400_codec_remove(struct platform_device *dev) +static int wm8400_codec_remove(struct snd_soc_codec *codec) { - struct wm8400_priv *priv = wm8400_codec->private_data; u16 reg; - snd_soc_unregister_dai(&wm8400_dai); - snd_soc_unregister_codec(wm8400_codec); - - reg = wm8400_read(wm8400_codec, WM8400_POWER_MANAGEMENT_1); - wm8400_write(wm8400_codec, WM8400_POWER_MANAGEMENT_1, + reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1); + snd_soc_write(codec, WM8400_POWER_MANAGEMENT_1, reg & (~WM8400_CODEC_ENA)); - regulator_bulk_free(ARRAY_SIZE(power), power); - kfree(priv); + return 0; +} - wm8400_codec = NULL; +static struct regmap *wm8400_get_regmap(struct device *dev) +{ + struct wm8400 *wm8400 = dev_get_platdata(dev); - return 0; + return wm8400->regmap; } -static struct platform_driver wm8400_codec_driver = { - .driver = { - .name = "wm8400-codec", - .owner = THIS_MODULE, - }, - .probe = wm8400_codec_probe, - .remove = __exit_p(wm8400_codec_remove), +static struct snd_soc_codec_driver soc_codec_dev_wm8400 = { + .probe = wm8400_codec_probe, + .remove = wm8400_codec_remove, + .suspend = wm8400_suspend, + .resume = wm8400_resume, + .get_regmap = wm8400_get_regmap, + .set_bias_level = wm8400_set_bias_level, + + .controls = wm8400_snd_controls, + .num_controls = ARRAY_SIZE(wm8400_snd_controls), + .dapm_widgets = wm8400_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets), + .dapm_routes = wm8400_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes), }; -static int __init wm8400_codec_init(void) +static int wm8400_probe(struct platform_device *pdev) { - return platform_driver_register(&wm8400_codec_driver); + return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8400, + &wm8400_dai, 1); } -module_init(wm8400_codec_init); -static void __exit wm8400_codec_exit(void) +static int wm8400_remove(struct platform_device *pdev) { - platform_driver_unregister(&wm8400_codec_driver); + snd_soc_unregister_codec(&pdev->dev); + return 0; } -module_exit(wm8400_codec_exit); -EXPORT_SYMBOL_GPL(soc_codec_dev_wm8400); +static struct platform_driver wm8400_codec_driver = { + .driver = { + .name = "wm8400-codec", + .owner = THIS_MODULE, + }, + .probe = wm8400_probe, + .remove = wm8400_remove, +}; + +module_platform_driver(wm8400_codec_driver); MODULE_DESCRIPTION("ASoC WM8400 driver"); MODULE_AUTHOR("Mark Brown"); |
