diff options
Diffstat (limited to 'sound/soc/codecs/wm8350.c')
| -rw-r--r-- | sound/soc/codecs/wm8350.c | 440 |
1 files changed, 198 insertions, 242 deletions
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c index f4f1fba38eb..392285edb59 100644 --- a/sound/soc/codecs/wm8350.c +++ b/sound/soc/codecs/wm8350.c @@ -1,7 +1,7 @@ /* * wm8350.c -- WM8350 ALSA SoC audio driver * - * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * Copyright (C) 2007-12 Wolfson Microelectronics PLC. * * Author: Liam Girdwood <lrg@slimlogic.co.uk> * @@ -24,9 +24,9 @@ #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 <trace/events/asoc.h> #include "wm8350.h" @@ -54,12 +54,13 @@ struct wm8350_output { struct wm8350_jack_data { struct snd_soc_jack *jack; + struct delayed_work work; int report; int short_report; }; struct wm8350_data { - struct snd_soc_codec codec; + struct wm8350 *wm8350; struct wm8350_output out1; struct wm8350_output out2; struct wm8350_jack_data hpl; @@ -70,27 +71,6 @@ struct wm8350_data { int fll_freq_in; }; -static unsigned int wm8350_codec_cache_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm8350 *wm8350 = codec->control_data; - return wm8350->reg_cache[reg]; -} - -static unsigned int wm8350_codec_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - struct wm8350 *wm8350 = codec->control_data; - return wm8350_reg_read(wm8350, reg); -} - -static int wm8350_codec_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - struct wm8350 *wm8350 = codec->control_data; - return wm8350_reg_write(wm8350, reg, value); -} - /* * Ramp OUT1 PGA volume to minimise pops at stream startup and shutdown. */ @@ -98,7 +78,7 @@ static inline int wm8350_out1_ramp_step(struct snd_soc_codec *codec) { struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1; - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; u16 reg, val; @@ -164,7 +144,7 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) { struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out2 = &wm8350_data->out2; - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = wm8350_data->wm8350; int left_complete = 0, right_complete = 0; u16 reg, val; @@ -230,8 +210,9 @@ static inline int wm8350_out2_ramp_step(struct snd_soc_codec *codec) */ static void wm8350_pga_work(struct work_struct *work) { - struct snd_soc_codec *codec = - container_of(work, struct snd_soc_codec, delayed_work.work); + struct snd_soc_dapm_context *dapm = + container_of(work, struct snd_soc_dapm_context, delayed_work.work); + struct snd_soc_codec *codec = dapm->codec; struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_data->out1, *out2 = &wm8350_data->out2; @@ -293,7 +274,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid shift %d\n", w->shift); return -1; } @@ -302,18 +283,16 @@ static int pga_event(struct snd_soc_dapm_widget *w, out->ramp = WM8350_RAMP_UP; out->active = 1; - if (!delayed_work_pending(&codec->delayed_work)) - schedule_delayed_work(&codec->delayed_work, - msecs_to_jiffies(1)); + schedule_delayed_work(&codec->dapm.delayed_work, + msecs_to_jiffies(1)); break; case SND_SOC_DAPM_PRE_PMD: out->ramp = WM8350_RAMP_DOWN; out->active = 0; - if (!delayed_work_pending(&codec->delayed_work)) - schedule_delayed_work(&codec->delayed_work, - msecs_to_jiffies(1)); + schedule_delayed_work(&codec->dapm.delayed_work, + msecs_to_jiffies(1)); break; } @@ -323,7 +302,7 @@ static int pga_event(struct snd_soc_dapm_widget *w, static int wm8350_put_volsw_2r_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 wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out = NULL; struct soc_mixer_control *mc = @@ -353,20 +332,20 @@ static int wm8350_put_volsw_2r_vu(struct snd_kcontrol *kcontrol, return 1; } - ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + ret = snd_soc_put_volsw(kcontrol, ucontrol); if (ret < 0) return ret; /* now hit the volume update bits (always bit 8) */ - val = wm8350_codec_read(codec, reg); - wm8350_codec_write(codec, reg, val | WM8350_OUT1_VU); + val = snd_soc_read(codec, reg); + snd_soc_write(codec, reg, val | WM8350_OUT1_VU); return 1; } static int wm8350_get_volsw_2r(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 wm8350_data *wm8350_priv = snd_soc_codec_get_drvdata(codec); struct wm8350_output *out1 = &wm8350_priv->out1; struct wm8350_output *out2 = &wm8350_priv->out2; @@ -390,23 +369,9 @@ static int wm8350_get_volsw_2r(struct snd_kcontrol *kcontrol, break; } - return snd_soc_get_volsw_2r(kcontrol, ucontrol); + return snd_soc_get_volsw(kcontrol, ucontrol); } -/* double control with volume update */ -#define SOC_WM8350_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, \ - xinvert, tlv_array) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ - .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ - SNDRV_CTL_ELEM_ACCESS_READWRITE | \ - SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw_2r, \ - .get = wm8350_get_volsw_2r, .put = wm8350_put_volsw_2r_vu, \ - .private_value = (unsigned long)&(struct soc_mixer_control) \ - {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ - .rshift = xshift, .max = xmax, .invert = xinvert}, } - static const char *wm8350_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; static const char *wm8350_pol[] = { "Normal", "Inv R", "Inv L", "Inv L & R" }; static const char *wm8350_dacmutem[] = { "Normal", "Soft" }; @@ -441,26 +406,29 @@ static const unsigned int capture_sd_tlv[] = { static const struct snd_kcontrol_new wm8350_snd_controls[] = { SOC_ENUM("Playback Deemphasis", wm8350_enum[0]), SOC_ENUM("Playback DAC Inversion", wm8350_enum[1]), - SOC_WM8350_DOUBLE_R_TLV("Playback PCM Volume", + SOC_DOUBLE_R_EXT_TLV("Playback PCM Volume", WM8350_DAC_DIGITAL_VOLUME_L, WM8350_DAC_DIGITAL_VOLUME_R, - 0, 255, 0, dac_pcm_tlv), + 0, 255, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, dac_pcm_tlv), SOC_ENUM("Playback PCM Mute Function", wm8350_enum[2]), SOC_ENUM("Playback PCM Mute Speed", wm8350_enum[3]), SOC_ENUM("Capture PCM Filter", wm8350_enum[4]), SOC_ENUM("Capture PCM HP Filter", wm8350_enum[5]), SOC_ENUM("Capture ADC Inversion", wm8350_enum[6]), - SOC_WM8350_DOUBLE_R_TLV("Capture PCM Volume", + SOC_DOUBLE_R_EXT_TLV("Capture PCM Volume", WM8350_ADC_DIGITAL_VOLUME_L, WM8350_ADC_DIGITAL_VOLUME_R, - 0, 255, 0, adc_pcm_tlv), + 0, 255, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, adc_pcm_tlv), SOC_DOUBLE_TLV("Capture Sidetone Volume", WM8350_ADC_DIVIDER, 8, 4, 15, 1, capture_sd_tlv), - SOC_WM8350_DOUBLE_R_TLV("Capture Volume", + SOC_DOUBLE_R_EXT_TLV("Capture Volume", WM8350_LEFT_INPUT_VOLUME, WM8350_RIGHT_INPUT_VOLUME, - 2, 63, 0, pre_amp_tlv), + 2, 63, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, pre_amp_tlv), SOC_DOUBLE_R("Capture ZC Switch", WM8350_LEFT_INPUT_VOLUME, WM8350_RIGHT_INPUT_VOLUME, 13, 1, 0), @@ -488,17 +456,19 @@ static const struct snd_kcontrol_new wm8350_snd_controls[] = { SOC_SINGLE_TLV("Out4 Capture Volume", WM8350_INPUT_MIXER_VOLUME, 1, 7, 0, out_mix_tlv), - SOC_WM8350_DOUBLE_R_TLV("Out1 Playback Volume", + SOC_DOUBLE_R_EXT_TLV("Out1 Playback Volume", WM8350_LOUT1_VOLUME, WM8350_ROUT1_VOLUME, - 2, 63, 0, out_pga_tlv), + 2, 63, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, out_pga_tlv), SOC_DOUBLE_R("Out1 Playback ZC Switch", WM8350_LOUT1_VOLUME, WM8350_ROUT1_VOLUME, 13, 1, 0), - SOC_WM8350_DOUBLE_R_TLV("Out2 Playback Volume", + SOC_DOUBLE_R_EXT_TLV("Out2 Playback Volume", WM8350_LOUT2_VOLUME, WM8350_ROUT2_VOLUME, - 2, 63, 0, out_pga_tlv), + 2, 63, 0, wm8350_get_volsw_2r, + wm8350_put_volsw_2r_vu, out_pga_tlv), SOC_DOUBLE_R("Out2 Playback ZC Switch", WM8350_LOUT2_VOLUME, WM8350_ROUT2_VOLUME, 13, 1, 0), SOC_SINGLE("Out2 Right Invert Switch", WM8350_ROUT2_VOLUME, 10, 1, 0), @@ -703,7 +673,7 @@ static const struct snd_soc_dapm_widget wm8350_dapm_widgets[] = { SND_SOC_DAPM_INPUT("IN3L"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8350_dapm_routes[] = { /* left playback mixer */ {"Left Playback Mixer", "Playback Switch", "Left DAC"}, @@ -784,33 +754,12 @@ static const struct snd_soc_dapm_route audio_map[] = { {"Beep", NULL, "IN3R PGA"}, }; -static int wm8350_add_widgets(struct snd_soc_codec *codec) -{ - int ret; - - ret = snd_soc_dapm_new_controls(codec, - wm8350_dapm_widgets, - ARRAY_SIZE(wm8350_dapm_widgets)); - if (ret != 0) { - dev_err(codec->dev, "dapm control register failed\n"); - return ret; - } - - /* set up audio paths */ - ret = snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - if (ret != 0) { - dev_err(codec->dev, "DAPM route register failed\n"); - return ret; - } - - return 0; -} - static int wm8350_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 wm8350 *wm8350 = codec->control_data; + struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = wm8350_data->wm8350; u16 fll_4; switch (clk_id) { @@ -824,14 +773,14 @@ static int wm8350_set_dai_sysclk(struct snd_soc_dai *codec_dai, case WM8350_MCLK_SEL_PLL_32K: wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_1, WM8350_MCLK_SEL); - fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & + fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) & ~WM8350_FLL_CLK_SRC_MASK; - wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id); + snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | clk_id); break; } /* MCLK direction */ - if (dir == WM8350_MCLK_DIR_OUT) + if (dir == SND_SOC_CLOCK_OUT) wm8350_set_bits(wm8350, WM8350_CLOCK_CONTROL_2, WM8350_MCLK_DIR); else @@ -848,39 +797,39 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) switch (div_id) { case WM8350_ADC_CLKDIV: - val = wm8350_codec_read(codec, WM8350_ADC_DIVIDER) & + val = snd_soc_read(codec, WM8350_ADC_DIVIDER) & ~WM8350_ADC_CLKDIV_MASK; - wm8350_codec_write(codec, WM8350_ADC_DIVIDER, val | div); + snd_soc_write(codec, WM8350_ADC_DIVIDER, val | div); break; case WM8350_DAC_CLKDIV: - val = wm8350_codec_read(codec, WM8350_DAC_CLOCK_CONTROL) & + val = snd_soc_read(codec, WM8350_DAC_CLOCK_CONTROL) & ~WM8350_DAC_CLKDIV_MASK; - wm8350_codec_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div); + snd_soc_write(codec, WM8350_DAC_CLOCK_CONTROL, val | div); break; case WM8350_BCLK_CLKDIV: - val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & + val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_BCLK_DIV_MASK; - wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); + snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_OPCLK_CLKDIV: - val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & + val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_OPCLK_DIV_MASK; - wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); + snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_SYS_CLKDIV: - val = wm8350_codec_read(codec, WM8350_CLOCK_CONTROL_1) & + val = snd_soc_read(codec, WM8350_CLOCK_CONTROL_1) & ~WM8350_MCLK_DIV_MASK; - wm8350_codec_write(codec, WM8350_CLOCK_CONTROL_1, val | div); + snd_soc_write(codec, WM8350_CLOCK_CONTROL_1, val | div); break; case WM8350_DACLR_CLKDIV: - val = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) & + val = snd_soc_read(codec, WM8350_DAC_LR_RATE) & ~WM8350_DACLRC_RATE_MASK; - wm8350_codec_write(codec, WM8350_DAC_LR_RATE, val | div); + snd_soc_write(codec, WM8350_DAC_LR_RATE, val | div); break; case WM8350_ADCLR_CLKDIV: - val = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) & + val = snd_soc_read(codec, WM8350_ADC_LR_RATE) & ~WM8350_ADCLRC_RATE_MASK; - wm8350_codec_write(codec, WM8350_ADC_LR_RATE, val | div); + snd_soc_write(codec, WM8350_ADC_LR_RATE, val | div); break; default: return -EINVAL; @@ -892,13 +841,13 @@ static int wm8350_set_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; - u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & + u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) & ~(WM8350_AIF_BCLK_INV | WM8350_AIF_LRCLK_INV | WM8350_AIF_FMT_MASK); - u16 master = wm8350_codec_read(codec, WM8350_AI_DAC_CONTROL) & + u16 master = snd_soc_read(codec, WM8350_AI_DAC_CONTROL) & ~WM8350_BCLK_MSTR; - u16 dac_lrc = wm8350_codec_read(codec, WM8350_DAC_LR_RATE) & + u16 dac_lrc = snd_soc_read(codec, WM8350_DAC_LR_RATE) & ~WM8350_DACLRC_ENA; - u16 adc_lrc = wm8350_codec_read(codec, WM8350_ADC_LR_RATE) & + u16 adc_lrc = snd_soc_read(codec, WM8350_ADC_LR_RATE) & ~WM8350_ADCLRC_ENA; /* set master/slave audio interface */ @@ -951,42 +900,10 @@ static int wm8350_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); - wm8350_codec_write(codec, WM8350_AI_DAC_CONTROL, master); - wm8350_codec_write(codec, WM8350_DAC_LR_RATE, dac_lrc); - wm8350_codec_write(codec, WM8350_ADC_LR_RATE, adc_lrc); - return 0; -} - -static int wm8350_pcm_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *codec_dai) -{ - struct snd_soc_codec *codec = codec_dai->codec; - int master = wm8350_codec_cache_read(codec, WM8350_AI_DAC_CONTROL) & - WM8350_BCLK_MSTR; - int enabled = 0; - - /* Check that the DACs or ADCs are enabled since they are - * required for LRC in master mode. The DACs or ADCs need a - * valid audio path i.e. pin -> ADC or DAC -> pin before - * the LRC will be enabled in master mode. */ - if (!master || cmd != SNDRV_PCM_TRIGGER_START) - return 0; - - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) & - (WM8350_ADCR_ENA | WM8350_ADCL_ENA); - } else { - enabled = wm8350_codec_cache_read(codec, WM8350_POWER_MGMT_4) & - (WM8350_DACR_ENA | WM8350_DACL_ENA); - } - - if (!enabled) { - dev_err(codec->dev, - "%s: invalid audio path - no clocks available\n", - __func__); - return -EINVAL; - } + snd_soc_write(codec, WM8350_AI_FORMATING, iface); + snd_soc_write(codec, WM8350_AI_DAC_CONTROL, master); + snd_soc_write(codec, WM8350_DAC_LR_RATE, dac_lrc); + snd_soc_write(codec, WM8350_ADC_LR_RATE, adc_lrc); return 0; } @@ -995,8 +912,9 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *codec_dai) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8350 *wm8350 = codec->control_data; - u16 iface = wm8350_codec_read(codec, WM8350_AI_FORMATING) & + struct wm8350_data *wm8350_data = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = wm8350_data->wm8350; + u16 iface = snd_soc_read(codec, WM8350_AI_FORMATING) & ~WM8350_AIF_WL_MASK; /* bit size */ @@ -1014,7 +932,7 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, break; } - wm8350_codec_write(codec, WM8350_AI_FORMATING, iface); + snd_soc_write(codec, WM8350_AI_FORMATING, iface); /* The sloping stopband filter is recommended for use with * lower sample rates to improve performance. @@ -1034,12 +952,15 @@ static int wm8350_pcm_hw_params(struct snd_pcm_substream *substream, static int wm8350_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; - struct wm8350 *wm8350 = codec->control_data; + unsigned int val; if (mute) - wm8350_set_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); + val = WM8350_DAC_MUTE_ENA; else - wm8350_clear_bits(wm8350, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA); + val = 0; + + snd_soc_update_bits(codec, WM8350_DAC_MUTE, WM8350_DAC_MUTE_ENA, val); + return 0; } @@ -1108,8 +1029,8 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; - struct wm8350 *wm8350 = codec->control_data; struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = priv->wm8350; struct _fll_div fll_div; int ret = 0; u16 fll_1, fll_4; @@ -1133,17 +1054,17 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai, fll_div.ratio); /* set up N.K & dividers */ - fll_1 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_1) & + fll_1 = snd_soc_read(codec, WM8350_FLL_CONTROL_1) & ~(WM8350_FLL_OUTDIV_MASK | WM8350_FLL_RSP_RATE_MASK | 0xc000); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_1, + snd_soc_write(codec, WM8350_FLL_CONTROL_1, fll_1 | (fll_div.div << 8) | 0x50); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_2, + snd_soc_write(codec, WM8350_FLL_CONTROL_2, (fll_div.ratio << 11) | (fll_div. n & WM8350_FLL_N_MASK)); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_3, fll_div.k); - fll_4 = wm8350_codec_read(codec, WM8350_FLL_CONTROL_4) & + snd_soc_write(codec, WM8350_FLL_CONTROL_3, fll_div.k); + fll_4 = snd_soc_read(codec, WM8350_FLL_CONTROL_4) & ~(WM8350_FLL_FRAC | WM8350_FLL_SLOW_LOCK_REF); - wm8350_codec_write(codec, WM8350_FLL_CONTROL_4, + snd_soc_write(codec, WM8350_FLL_CONTROL_4, fll_4 | (fll_div.k ? WM8350_FLL_FRAC : 0) | (fll_div.ratio == 8 ? WM8350_FLL_SLOW_LOCK_REF : 0)); @@ -1160,8 +1081,8 @@ static int wm8350_set_fll(struct snd_soc_dai *codec_dai, static int wm8350_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - struct wm8350 *wm8350 = codec->control_data; struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); + struct wm8350 *wm8350 = priv->wm8350; struct wm8350_audio_platform_data *platform = wm8350->codec.platform_data; u16 pm1; @@ -1184,7 +1105,7 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, 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(priv->supplies), priv->supplies); if (ret != 0) @@ -1317,11 +1238,11 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec, priv->supplies); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } -static int wm8350_suspend(struct snd_soc_codec *codec, pm_message_t state) +static int wm8350_suspend(struct snd_soc_codec *codec) { wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; @@ -1334,37 +1255,13 @@ static int wm8350_resume(struct snd_soc_codec *codec) return 0; } -static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) +static void wm8350_hp_work(struct wm8350_data *priv, + struct wm8350_jack_data *jack, + u16 mask) { - struct wm8350_data *priv = data; - struct wm8350 *wm8350 = priv->codec.control_data; + struct wm8350 *wm8350 = priv->wm8350; u16 reg; int report; - int mask; - struct wm8350_jack_data *jack = NULL; - - switch (irq - wm8350->irq_base) { - 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 IRQ_NONE; - } - - /* Debounce */ - msleep(200); reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); if (reg & mask) @@ -1374,6 +1271,57 @@ static irqreturn_t wm8350_hp_jack_handler(int irq, void *data) snd_soc_jack_report(jack->jack, report, jack->report); +} + +static void wm8350_hpl_work(struct work_struct *work) +{ + struct wm8350_data *priv = + container_of(work, struct wm8350_data, hpl.work.work); + + wm8350_hp_work(priv, &priv->hpl, WM8350_JACK_L_LVL); +} + +static void wm8350_hpr_work(struct work_struct *work) +{ + struct wm8350_data *priv = + container_of(work, struct wm8350_data, hpr.work.work); + + wm8350_hp_work(priv, &priv->hpr, WM8350_JACK_R_LVL); +} + +static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->wm8350; + +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 HPL"); +#endif + + if (device_may_wakeup(wm8350->dev)) + pm_wakeup_event(wm8350->dev, 250); + + queue_delayed_work(system_power_efficient_wq, + &priv->hpl.work, msecs_to_jiffies(200)); + + return IRQ_HANDLED; +} + +static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data) +{ + struct wm8350_data *priv = data; + struct wm8350 *wm8350 = priv->wm8350; + +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 HPR"); +#endif + + if (device_may_wakeup(wm8350->dev)) + pm_wakeup_event(wm8350->dev, 250); + + queue_delayed_work(system_power_efficient_wq, + &priv->hpr.work, msecs_to_jiffies(200)); + return IRQ_HANDLED; } @@ -1392,7 +1340,7 @@ 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 = snd_soc_codec_get_drvdata(codec); - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = priv->wm8350; int irq; int ena; @@ -1423,7 +1371,14 @@ int wm8350_hp_jack_detect(struct snd_soc_codec *codec, enum wm8350_jack which, } /* Sync status */ - wm8350_hp_jack_handler(irq + wm8350->irq_base, priv); + switch (which) { + case WM8350_JDL: + wm8350_hpl_jack_handler(0, priv); + break; + case WM8350_JDR: + wm8350_hpr_jack_handler(0, priv); + break; + } return 0; } @@ -1432,10 +1387,14 @@ EXPORT_SYMBOL_GPL(wm8350_hp_jack_detect); static irqreturn_t wm8350_mic_handler(int irq, void *data) { struct wm8350_data *priv = data; - struct wm8350 *wm8350 = priv->codec.control_data; + struct wm8350 *wm8350 = priv->wm8350; u16 reg; int report = 0; +#ifndef CONFIG_SND_SOC_WM8350_MODULE + trace_snd_soc_jack_irq("WM8350 mic"); +#endif + reg = wm8350_reg_read(wm8350, WM8350_JACK_PIN_STATUS); if (reg & WM8350_JACK_MICSCD_LVL) report |= priv->mic.short_report; @@ -1464,7 +1423,7 @@ int wm8350_mic_jack_detect(struct snd_soc_codec *codec, int detect_report, int short_report) { struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); - struct wm8350 *wm8350 = codec->control_data; + struct wm8350 *wm8350 = priv->wm8350; priv->mic.jack = jack; priv->mic.report = detect_report; @@ -1489,10 +1448,9 @@ EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect); SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE) -static struct snd_soc_dai_ops wm8350_dai_ops = { +static const 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, @@ -1531,34 +1489,36 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) return -EINVAL; } - priv = kzalloc(sizeof(struct wm8350_data), GFP_KERNEL); + priv = devm_kzalloc(codec->dev, sizeof(struct wm8350_data), + GFP_KERNEL); if (priv == NULL) return -ENOMEM; snd_soc_codec_set_drvdata(codec, priv); + priv->wm8350 = wm8350; + for (i = 0; i < ARRAY_SIZE(supply_names); i++) priv->supplies[i].supply = supply_names[i]; - ret = regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies), + ret = devm_regulator_bulk_get(wm8350->dev, ARRAY_SIZE(priv->supplies), priv->supplies); if (ret != 0) - goto err_priv; - - wm8350->codec.codec = codec; - codec->control_data = wm8350; + return ret; /* Put the codec into reset if it wasn't already */ wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); - INIT_DELAYED_WORK(&codec->delayed_work, wm8350_pga_work); + INIT_DELAYED_WORK(&codec->dapm.delayed_work, wm8350_pga_work); + INIT_DELAYED_WORK(&priv->hpl.work, wm8350_hpl_work); + INIT_DELAYED_WORK(&priv->hpr.work, wm8350_hpr_work); /* Enable the codec */ wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); /* Enable robust clocking mode in ADC */ - wm8350_codec_write(codec, WM8350_SECURITY, 0xa7); - wm8350_codec_write(codec, 0xde, 0x13); - wm8350_codec_write(codec, WM8350_SECURITY, 0); + snd_soc_write(codec, WM8350_SECURITY, 0xa7); + snd_soc_write(codec, 0xde, 0x13); + snd_soc_write(codec, WM8350_SECURITY, 0); /* read OUT1 & OUT2 volumes */ out1 = &priv->out1; @@ -1586,15 +1546,22 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) wm8350_set_bits(wm8350, WM8350_ROUT2_VOLUME, WM8350_OUT2_VU | WM8350_OUT2R_MUTE); + /* Make sure AIF tristating is disabled by default */ + wm8350_clear_bits(wm8350, WM8350_AI_FORMATING, WM8350_AIF_TRI); + + /* Make sure we've got a sane companding setup too */ + wm8350_clear_bits(wm8350, WM8350_ADC_DAC_COMP, + WM8350_DAC_COMP | WM8350_LOOPBACK); + /* Make sure jack detect is disabled to start off with */ wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_L, - wm8350_hp_jack_handler, 0, "Left jack detect", + wm8350_hpl_jack_handler, 0, "Left jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_JCK_DET_R, - wm8350_hp_jack_handler, 0, "Right jack detect", + wm8350_hpr_jack_handler, 0, "Right jack detect", priv); wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICSCD, wm8350_mic_handler, 0, "Microphone short", priv); @@ -1602,24 +1569,15 @@ static int wm8350_codec_probe(struct snd_soc_codec *codec) wm8350_mic_handler, 0, "Microphone detect", priv); - 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); return 0; - -err_priv: - kfree(priv); - return ret; } static int wm8350_codec_remove(struct snd_soc_codec *codec) { struct wm8350_data *priv = snd_soc_codec_get_drvdata(codec); struct wm8350 *wm8350 = dev_get_platdata(codec->dev); - int ret; wm8350_clear_bits(wm8350, WM8350_JACK_DETECT, WM8350_JDL_ENA | WM8350_JDR_ENA); @@ -1634,42 +1592,50 @@ static int wm8350_codec_remove(struct snd_soc_codec *codec) priv->hpr.jack = NULL; priv->mic.jack = NULL; - /* cancel any work waiting to be queued. */ - ret = cancel_delayed_work(&codec->delayed_work); + cancel_delayed_work_sync(&priv->hpl.work); + cancel_delayed_work_sync(&priv->hpr.work); /* if there was any work waiting then we run it now and * wait for its completion */ - if (ret) { - schedule_delayed_work(&codec->delayed_work, 0); - flush_scheduled_work(); - } + flush_delayed_work(&codec->dapm.delayed_work); wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF); wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA); - regulator_bulk_free(ARRAY_SIZE(priv->supplies), priv->supplies); - kfree(priv); return 0; } +static struct regmap *wm8350_get_regmap(struct device *dev) +{ + struct wm8350 *wm8350 = dev_get_platdata(dev); + + return wm8350->regmap; +} + static struct snd_soc_codec_driver soc_codec_dev_wm8350 = { .probe = wm8350_codec_probe, .remove = wm8350_codec_remove, .suspend = wm8350_suspend, .resume = wm8350_resume, - .read = wm8350_codec_read, - .write = wm8350_codec_write, + .get_regmap = wm8350_get_regmap, .set_bias_level = wm8350_set_bias_level, + + .controls = wm8350_snd_controls, + .num_controls = ARRAY_SIZE(wm8350_snd_controls), + .dapm_widgets = wm8350_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets), + .dapm_routes = wm8350_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes), }; -static int __devinit wm8350_probe(struct platform_device *pdev) +static int wm8350_probe(struct platform_device *pdev) { return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8350, &wm8350_dai, 1); } -static int __devexit wm8350_remove(struct platform_device *pdev) +static int wm8350_remove(struct platform_device *pdev) { snd_soc_unregister_codec(&pdev->dev); return 0; @@ -1681,20 +1647,10 @@ static struct platform_driver wm8350_codec_driver = { .owner = THIS_MODULE, }, .probe = wm8350_probe, - .remove = __devexit_p(wm8350_remove), + .remove = wm8350_remove, }; -static __init int wm8350_init(void) -{ - return platform_driver_register(&wm8350_codec_driver); -} -module_init(wm8350_init); - -static __exit void wm8350_exit(void) -{ - platform_driver_unregister(&wm8350_codec_driver); -} -module_exit(wm8350_exit); +module_platform_driver(wm8350_codec_driver); MODULE_DESCRIPTION("ASoC WM8350 driver"); MODULE_AUTHOR("Liam Girdwood"); |
