diff options
Diffstat (limited to 'sound/soc/codecs/wm8960.c')
| -rw-r--r-- | sound/soc/codecs/wm8960.c | 425 |
1 files changed, 264 insertions, 161 deletions
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 9ed455700954..384e8e703446 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * wm8960.c -- WM8960 ALSA SoC Audio driver * * Copyright 2007-11 Wolfson Microelectronics, plc * * Author: Liam Girdwood - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/module.h> @@ -17,6 +14,7 @@ #include <linux/pm.h> #include <linux/clk.h> #include <linux/i2c.h> +#include <linux/acpi.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -48,8 +46,10 @@ #define WM8960_DISOP 0x40 #define WM8960_DRES_MASK 0x30 +#define WM8960_DSCH_TOUT 600 /* discharge timeout, ms */ + static bool is_pll_freq_available(unsigned int source, unsigned int target); -static int wm8960_set_pll(struct snd_soc_codec *codec, +static int wm8960_set_pll(struct snd_soc_component *component, unsigned int freq_in, unsigned int freq_out); /* * wm8960 register cache @@ -120,10 +120,19 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg) } } +#define WM8960_NUM_SUPPLIES 5 +static const char *wm8960_supply_names[WM8960_NUM_SUPPLIES] = { + "DCVDD", + "DBVDD", + "AVDD", + "SPKVDD1", + "SPKVDD2", +}; + struct wm8960_priv { struct clk *mclk; struct regmap *regmap; - int (*set_bias_level)(struct snd_soc_codec *, + int (*set_bias_level)(struct snd_soc_component *, enum snd_soc_bias_level level); struct snd_soc_dapm_widget *lout1; struct snd_soc_dapm_widget *rout1; @@ -136,6 +145,8 @@ struct wm8960_priv { int freq_in; bool is_stream_in_use[2]; struct wm8960_data pdata; + ktime_t dsch_start; + struct regulator_bulk_data supplies[WM8960_NUM_SUPPLIES]; }; #define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) @@ -154,6 +165,7 @@ static const char *wm8960_adc_data_output_sel[] = { "Left Data = Right ADC; Right Data = Left ADC", }; static const char *wm8960_dmonomix[] = {"Stereo", "Mono"}; +static const char *wm8960_dacslope[] = {"Normal", "Sloping"}; static const struct soc_enum wm8960_enum[] = { SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), @@ -164,13 +176,14 @@ static const struct soc_enum wm8960_enum[] = { SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel), SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix), + SOC_ENUM_SINGLE(WM8960_DACCTL2, 1, 2, wm8960_dacslope), }; static const int deemph_settings[] = { 0, 32000, 44100, 48000 }; -static int wm8960_set_deemph(struct snd_soc_codec *codec) +static int wm8960_set_deemph(struct snd_soc_component *component) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); int val, i, best; /* If we're using deemphasis select the nearest available sample @@ -189,17 +202,17 @@ static int wm8960_set_deemph(struct snd_soc_codec *codec) val = 0; } - dev_dbg(codec->dev, "Set deemphasis %d\n", val); + dev_dbg(component->dev, "Set deemphasis %d\n", val); - return snd_soc_update_bits(codec, WM8960_DACCTL1, + return snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x6, val); } static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); ucontrol->value.integer.value[0] = wm8960->deemph; return 0; @@ -208,8 +221,8 @@ static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) @@ -217,7 +230,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, wm8960->deemph = deemph; - return wm8960_set_deemph(codec); + return wm8960_set_deemph(component); } static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); @@ -306,6 +319,7 @@ SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", SOC_ENUM("ADC Data Output Select", wm8960_enum[6]), SOC_ENUM("DAC Mono Mix", wm8960_enum[7]), +SOC_ENUM("DAC Filter Characteristics", wm8960_enum[8]), }; static const struct snd_kcontrol_new wm8960_lin_boost[] = { @@ -470,11 +484,11 @@ static const struct snd_soc_dapm_route audio_paths_capless[] = { { "OUT3 VMID", NULL, "Right Output Mixer" }, }; -static int wm8960_add_widgets(struct snd_soc_codec *codec) +static int wm8960_add_widgets(struct snd_soc_component *component) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); struct wm8960_data *pdata = &wm8960->pdata; - struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct snd_soc_dapm_widget *w; snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, @@ -504,7 +518,7 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) * list each time to find the desired power state do so now * and save the result. */ - list_for_each_entry(w, &codec->component.card->widgets, list) { + list_for_each_entry(w, &component->card->widgets, list) { if (w->dapm != dapm) continue; if (strcmp(w->name, "LOUT1 PGA") == 0) @@ -521,15 +535,15 @@ static int wm8960_add_widgets(struct snd_soc_codec *codec) static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_component *component = codec_dai->component; u16 iface = 0; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBP_CFP: iface |= 0x0040; break; - case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBC_CFC: break; default: return -EINVAL; @@ -573,7 +587,7 @@ static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, } /* set iface */ - snd_soc_write(codec, WM8960_IFACE1, iface); + snd_soc_component_write(component, WM8960_IFACE1, iface); return 0; } @@ -611,11 +625,7 @@ static const int bclk_divs[] = { * - lrclk = sysclk / dac_divs * - 10 * bclk = sysclk / bclk_divs * - * If we cannot find an exact match for (sysclk, lrclk, bclk) - * triplet, we relax the bclk such that bclk is chosen as the - * closest available frequency greater than expected bclk. - * - * @wm8960_priv: wm8960 codec private data + * @wm8960: codec private data * @mclk: MCLK used to derive sysclk * @sysclk_idx: sysclk_divs index for found sysclk * @dac_idx: dac_divs index for found lrclk @@ -632,7 +642,7 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk, { int sysclk, bclk, lrclk; int i, j, k; - int diff, closest = mclk; + int diff; /* marker for no match */ *bclk_idx = -1; @@ -656,12 +666,6 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk, *bclk_idx = k; break; } - if (diff > 0 && closest > diff) { - *sysclk_idx = i; - *dac_idx = j; - *bclk_idx = k; - closest = diff; - } } if (k != ARRAY_SIZE(bclk_divs)) break; @@ -683,7 +687,7 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk, * triplet, we relax the bclk such that bclk is chosen as the * closest available frequency greater than expected bclk. * - * @codec: codec structure + * @component: component structure * @freq_in: input frequency used to derive freq out via PLL * @sysclk_idx: sysclk_divs index for found sysclk * @dac_idx: dac_divs index for found lrclk @@ -695,10 +699,10 @@ int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk, * (@sysclk_idx, @dac_idx, @bclk_idx) dividers */ static -int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, +int wm8960_configure_pll(struct snd_soc_component *component, int freq_in, int *sysclk_idx, int *dac_idx, int *bclk_idx) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); int sysclk, bclk, lrclk, freq_out; int diff, closest, best_freq_out; int i, j, k; @@ -710,7 +714,13 @@ int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, best_freq_out = -EINVAL; *sysclk_idx = *dac_idx = *bclk_idx = -1; - for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { + /* + * From Datasheet, the PLL performs best when f2 is between + * 90MHz and 100MHz, the desired sysclk output is 11.2896MHz + * or 12.288MHz, then sysclkdiv = 2 is the best choice. + * So search sysclk_divs from 2 to 1 other than from 1 to 2. + */ + for (i = ARRAY_SIZE(sysclk_divs) - 1; i >= 0; --i) { if (sysclk_divs[i] == -1) continue; for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { @@ -741,22 +751,29 @@ int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in, return best_freq_out; } -static int wm8960_configure_clocking(struct snd_soc_codec *codec) +static int wm8960_configure_clocking(struct snd_soc_component *component) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); int freq_out, freq_in; - u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); + u16 iface1 = snd_soc_component_read(component, WM8960_IFACE1); int i, j, k; int ret; - if (!(iface1 & (1<<6))) { - dev_dbg(codec->dev, - "Codec is slave mode, no need to configure clock\n"); + /* + * For Slave mode clocking should still be configured, + * so this if statement should be removed, but some platform + * may not work if the sysclk is not configured, to avoid such + * compatible issue, just add '!wm8960->sysclk' condition in + * this if statement. + */ + if (!(iface1 & (1 << 6)) && !wm8960->sysclk) { + dev_warn(component->dev, + "slave mode, but proceeding with no clock configuration\n"); return 0; } if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { - dev_err(codec->dev, "No MCLK configured\n"); + dev_err(component->dev, "No MCLK configured\n"); return -EINVAL; } @@ -769,12 +786,12 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { /* disable the PLL and using MCLK to provide sysclk */ - wm8960_set_pll(codec, 0, 0); + wm8960_set_pll(component, 0, 0); freq_out = freq_in; } else if (wm8960->sysclk) { freq_out = wm8960->sysclk; } else { - dev_err(codec->dev, "No SYSCLK configured\n"); + dev_err(component->dev, "No SYSCLK configured\n"); return -EINVAL; } @@ -783,28 +800,28 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec) if (ret >= 0) { goto configure_clock; } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { - dev_err(codec->dev, "failed to configure clock\n"); + dev_err(component->dev, "failed to configure clock\n"); return -EINVAL; } } - freq_out = wm8960_configure_pll(codec, freq_in, &i, &j, &k); + freq_out = wm8960_configure_pll(component, freq_in, &i, &j, &k); if (freq_out < 0) { - dev_err(codec->dev, "failed to configure clock via PLL\n"); + dev_err(component->dev, "failed to configure clock via PLL\n"); return freq_out; } - wm8960_set_pll(codec, freq_in, freq_out); + wm8960_set_pll(component, freq_in, freq_out); configure_clock: /* configure sysclk clock */ - snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 3 << 1, i << 1); /* configure frame clock */ - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 3, j << 3); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ - snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); + snd_soc_component_update_bits(component, WM8960_CLOCK2, 0xf, k); return 0; } @@ -813,9 +830,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct snd_soc_codec *codec = dai->codec; - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; + struct snd_soc_component *component = dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + u16 iface = snd_soc_component_read(component, WM8960_IFACE1) & 0xfff3; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; @@ -839,8 +856,9 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, iface |= 0x000c; break; } + fallthrough; default: - dev_err(codec->dev, "unsupported width %d\n", + dev_err(component->dev, "unsupported width %d\n", params_width(params)); return -EINVAL; } @@ -848,23 +866,22 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ if (tx) { - wm8960_set_deemph(codec); + wm8960_set_deemph(component); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) if (alc_rates[i].rate == params_rate(params)) - snd_soc_update_bits(codec, + snd_soc_component_update_bits(component, WM8960_ADDCTL3, 0x7, alc_rates[i].val); } /* set iface */ - snd_soc_write(codec, WM8960_IFACE1, iface); + snd_soc_component_write(component, WM8960_IFACE1, iface); wm8960->is_stream_in_use[tx] = true; - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON && - !wm8960->is_stream_in_use[!tx]) - return wm8960_configure_clocking(codec); + if (!wm8960->is_stream_in_use[!tx]) + return wm8960_configure_clocking(component); return 0; } @@ -872,8 +889,8 @@ static int wm8960_hw_params(struct snd_pcm_substream *substream, static int wm8960_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { - struct snd_soc_codec *codec = dai->codec; - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; wm8960->is_stream_in_use[tx] = false; @@ -881,47 +898,49 @@ static int wm8960_hw_free(struct snd_pcm_substream *substream, return 0; } -static int wm8960_mute(struct snd_soc_dai *dai, int mute) +static int wm8960_mute(struct snd_soc_dai *dai, int mute, int direction) { - struct snd_soc_codec *codec = dai->codec; + struct snd_soc_component *component = dai->component; if (mute) - snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); + snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0x8); else - snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); + snd_soc_component_update_bits(component, WM8960_DACCTL1, 0x8, 0); return 0; } -static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, +static int wm8960_set_bias_level_out3(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - u16 pm2 = snd_soc_read(codec, WM8960_POWER2); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + u16 pm2 = snd_soc_component_read(component, WM8960_POWER2); int ret; + ktime_t tout; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: - switch (snd_soc_codec_get_bias_level(codec)) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_STANDBY: if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to enable MCLK: %d\n", ret); return ret; } } - ret = wm8960_configure_clocking(codec); + ret = wm8960_configure_clocking(component); if (ret) return ret; /* Set VMID to 2x50k */ - snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); + snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: @@ -930,7 +949,7 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) - wm8960_set_pll(codec, 0, 0); + wm8960_set_pll(component, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); @@ -943,50 +962,56 @@ static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { + /* ensure discharge is complete */ + tout = WM8960_DSCH_TOUT - ktime_ms_delta(ktime_get(), wm8960->dsch_start); + if (tout > 0) + msleep(tout); + regcache_sync(wm8960->regmap); /* Enable anti-pop features */ - snd_soc_write(codec, WM8960_APOP1, + snd_soc_component_write(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); /* Enable & ramp VMID at 2x50k */ - snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); + snd_soc_component_update_bits(component, WM8960_POWER1, 0x80, 0x80); msleep(100); /* Enable VREF */ - snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF, WM8960_VREF); /* Disable anti-pop features */ - snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); + snd_soc_component_write(component, WM8960_APOP1, WM8960_BUFIOEN); } /* Set VMID to 2x250k */ - snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); + snd_soc_component_update_bits(component, WM8960_POWER1, 0x180, 0x100); break; case SND_SOC_BIAS_OFF: /* Enable anti-pop features */ - snd_soc_write(codec, WM8960_APOP1, + snd_soc_component_write(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); - /* Disable VMID and VREF, let them discharge */ - snd_soc_write(codec, WM8960_POWER1, 0); - msleep(600); + /* Disable VMID and VREF, mark discharge */ + snd_soc_component_write(component, WM8960_POWER1, 0); + wm8960->dsch_start = ktime_get(); break; } return 0; } -static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, +static int wm8960_set_bias_level_capless(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); - u16 pm2 = snd_soc_read(codec, WM8960_POWER2); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + u16 pm2 = snd_soc_component_read(component, WM8960_POWER2); int reg, ret; switch (level) { @@ -994,10 +1019,10 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_PREPARE: - switch (snd_soc_codec_get_bias_level(codec)) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_STANDBY: /* Enable anti pop mode */ - snd_soc_update_bits(codec, WM8960_APOP1, + snd_soc_component_update_bits(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | @@ -1011,20 +1036,20 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, reg |= WM8960_PWR2_ROUT1; if (wm8960->out3 && wm8960->out3->power) reg |= WM8960_PWR2_OUT3; - snd_soc_update_bits(codec, WM8960_POWER2, + snd_soc_component_update_bits(component, WM8960_POWER2, WM8960_PWR2_LOUT1 | WM8960_PWR2_ROUT1 | WM8960_PWR2_OUT3, reg); /* Enable VMID at 2*50k */ - snd_soc_update_bits(codec, WM8960_POWER1, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VMID_MASK, 0x80); /* Ramp */ msleep(100); /* Enable VREF */ - snd_soc_update_bits(codec, WM8960_POWER1, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF, WM8960_VREF); msleep(100); @@ -1032,14 +1057,14 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { - dev_err(codec->dev, + dev_err(component->dev, "Failed to enable MCLK: %d\n", ret); return ret; } } - ret = wm8960_configure_clocking(codec); + ret = wm8960_configure_clocking(component); if (ret) return ret; @@ -1051,20 +1076,20 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) - wm8960_set_pll(codec, 0, 0); + wm8960_set_pll(component, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); /* Enable anti-pop mode */ - snd_soc_update_bits(codec, WM8960_APOP1, + snd_soc_component_update_bits(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN); /* Disable VMID and VREF */ - snd_soc_update_bits(codec, WM8960_POWER1, + snd_soc_component_update_bits(component, WM8960_POWER1, WM8960_VREF | WM8960_VMID_MASK, 0); break; @@ -1077,15 +1102,15 @@ static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, break; case SND_SOC_BIAS_STANDBY: - switch (snd_soc_codec_get_bias_level(codec)) { + switch (snd_soc_dapm_get_bias_level(dapm)) { case SND_SOC_BIAS_PREPARE: /* Disable HP discharge */ - snd_soc_update_bits(codec, WM8960_APOP2, + snd_soc_component_update_bits(component, WM8960_APOP2, WM8960_DISOP | WM8960_DRES_MASK, 0); /* Disable anti-pop features */ - snd_soc_update_bits(codec, WM8960_APOP1, + snd_soc_component_update_bits(component, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | @@ -1184,7 +1209,7 @@ static int pll_factors(unsigned int source, unsigned int target, return 0; } -static int wm8960_set_pll(struct snd_soc_codec *codec, +static int wm8960_set_pll(struct snd_soc_component *component, unsigned int freq_in, unsigned int freq_out) { u16 reg; @@ -1199,29 +1224,29 @@ static int wm8960_set_pll(struct snd_soc_codec *codec, /* Disable the PLL: even if we are changing the frequency the * PLL needs to be disabled while we do so. */ - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); - snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0); + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0); if (!freq_in || !freq_out) return 0; - reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; + reg = snd_soc_component_read(component, WM8960_PLL1) & ~0x3f; reg |= pll_div.pre_div << 4; reg |= pll_div.n; if (pll_div.k) { reg |= 0x20; - snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); - snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); - snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); + snd_soc_component_write(component, WM8960_PLL2, (pll_div.k >> 16) & 0xff); + snd_soc_component_write(component, WM8960_PLL3, (pll_div.k >> 8) & 0xff); + snd_soc_component_write(component, WM8960_PLL4, pll_div.k & 0xff); } - snd_soc_write(codec, WM8960_PLL1, reg); + snd_soc_component_write(component, WM8960_PLL1, reg); /* Turn it on */ - snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); + snd_soc_component_update_bits(component, WM8960_POWER2, 0x1, 0x1); msleep(250); - snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, 0x1); return 0; } @@ -1229,43 +1254,43 @@ static int wm8960_set_pll(struct snd_soc_codec *codec, static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { - struct snd_soc_codec *codec = codec_dai->codec; - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = codec_dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); wm8960->freq_in = freq_in; if (pll_id == WM8960_SYSCLK_AUTO) return 0; - return wm8960_set_pll(codec, freq_in, freq_out); + return wm8960_set_pll(component, freq_in, freq_out); } static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { - struct snd_soc_codec *codec = codec_dai->codec; + struct snd_soc_component *component = codec_dai->component; u16 reg; switch (div_id) { case WM8960_SYSCLKDIV: - reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; - snd_soc_write(codec, WM8960_CLOCK1, reg | div); + reg = snd_soc_component_read(component, WM8960_CLOCK1) & 0x1f9; + snd_soc_component_write(component, WM8960_CLOCK1, reg | div); break; case WM8960_DACDIV: - reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; - snd_soc_write(codec, WM8960_CLOCK1, reg | div); + reg = snd_soc_component_read(component, WM8960_CLOCK1) & 0x1c7; + snd_soc_component_write(component, WM8960_CLOCK1, reg | div); break; case WM8960_OPCLKDIV: - reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; - snd_soc_write(codec, WM8960_PLL1, reg | div); + reg = snd_soc_component_read(component, WM8960_PLL1) & 0x03f; + snd_soc_component_write(component, WM8960_PLL1, reg | div); break; case WM8960_DCLKDIV: - reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; - snd_soc_write(codec, WM8960_CLOCK2, reg | div); + reg = snd_soc_component_read(component, WM8960_CLOCK2) & 0x03f; + snd_soc_component_write(component, WM8960_CLOCK2, reg | div); break; case WM8960_TOCLKSEL: - reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; - snd_soc_write(codec, WM8960_ADDCTL1, reg | div); + reg = snd_soc_component_read(component, WM8960_ADDCTL1) & 0x1fd; + snd_soc_component_write(component, WM8960_ADDCTL1, reg | div); break; default: return -EINVAL; @@ -1274,27 +1299,27 @@ static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } -static int wm8960_set_bias_level(struct snd_soc_codec *codec, +static int wm8960_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); - return wm8960->set_bias_level(codec, level); + return wm8960->set_bias_level(component, level); } static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { - struct snd_soc_codec *codec = dai->codec; - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct snd_soc_component *component = dai->component; + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); switch (clk_id) { case WM8960_SYSCLK_MCLK: - snd_soc_update_bits(codec, WM8960_CLOCK1, + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_MCLK); break; case WM8960_SYSCLK_PLL: - snd_soc_update_bits(codec, WM8960_CLOCK1, + snd_soc_component_update_bits(component, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; case WM8960_SYSCLK_AUTO: @@ -1318,11 +1343,12 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, .hw_free = wm8960_hw_free, - .digital_mute = wm8960_mute, + .mute_stream = wm8960_mute, .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, .set_pll = wm8960_set_dai_pll, .set_sysclk = wm8960_set_dai_sysclk, + .no_capture_mute = 1, }; static struct snd_soc_dai_driver wm8960_dai = { @@ -1340,12 +1366,12 @@ static struct snd_soc_dai_driver wm8960_dai = { .rates = WM8960_RATES, .formats = WM8960_FORMATS,}, .ops = &wm8960_dai_ops, - .symmetric_rates = 1, + .symmetric_rate = 1, }; -static int wm8960_probe(struct snd_soc_codec *codec) +static int wm8960_probe(struct snd_soc_component *component) { - struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); + struct wm8960_priv *wm8960 = snd_soc_component_get_drvdata(component); struct wm8960_data *pdata = &wm8960->pdata; if (pdata->capless) @@ -1353,17 +1379,20 @@ static int wm8960_probe(struct snd_soc_codec *codec) else wm8960->set_bias_level = wm8960_set_bias_level_out3; - snd_soc_add_codec_controls(codec, wm8960_snd_controls, + snd_soc_add_component_controls(component, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); - wm8960_add_widgets(codec); + wm8960_add_widgets(component); return 0; } -static const struct snd_soc_codec_driver soc_codec_dev_wm8960 = { - .probe = wm8960_probe, - .set_bias_level = wm8960_set_bias_level, - .suspend_bias_off = true, +static const struct snd_soc_component_driver soc_component_dev_wm8960 = { + .probe = wm8960_probe, + .set_bias_level = wm8960_set_bias_level, + .suspend_bias_off = 1, + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, }; static const struct regmap_config wm8960_regmap = { @@ -1373,7 +1402,7 @@ static const struct regmap_config wm8960_regmap = { .reg_defaults = wm8960_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .volatile_reg = wm8960_volatile, }; @@ -1388,14 +1417,21 @@ static void wm8960_set_pdata_from_of(struct i2c_client *i2c, if (of_property_read_bool(np, "wlf,shared-lrclk")) pdata->shared_lrclk = true; + + of_property_read_u32_array(np, "wlf,gpio-cfg", pdata->gpio_cfg, + ARRAY_SIZE(pdata->gpio_cfg)); + + of_property_read_u32_array(np, "wlf,hp-cfg", pdata->hp_cfg, + ARRAY_SIZE(pdata->hp_cfg)); } -static int wm8960_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +static int wm8960_i2c_probe(struct i2c_client *i2c) { struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); struct wm8960_priv *wm8960; + unsigned int i; int ret; + u8 val; wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), GFP_KERNEL); @@ -1406,21 +1442,55 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, if (IS_ERR(wm8960->mclk)) { if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER; + } else { + ret = clk_get_rate(wm8960->mclk); + if (ret >= 0) { + wm8960->freq_in = ret; + } else { + dev_err(&i2c->dev, "Failed to read MCLK rate: %d\n", + ret); + } + } + + for (i = 0; i < ARRAY_SIZE(wm8960->supplies); i++) + wm8960->supplies[i].supply = wm8960_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(ARRAY_SIZE(wm8960->supplies), + wm8960->supplies); + if (ret < 0) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; } wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); - if (IS_ERR(wm8960->regmap)) - return PTR_ERR(wm8960->regmap); + if (IS_ERR(wm8960->regmap)) { + ret = PTR_ERR(wm8960->regmap); + goto bulk_disable; + } if (pdata) memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data)); else if (i2c->dev.of_node) wm8960_set_pdata_from_of(i2c, &wm8960->pdata); + ret = i2c_master_recv(i2c, &val, sizeof(val)); + if (ret >= 0) { + dev_err(&i2c->dev, "Not wm8960, wm8960 reg can not read by i2c\n"); + ret = -EINVAL; + goto bulk_disable; + } + ret = wm8960_reset(wm8960->regmap); if (ret != 0) { dev_err(&i2c->dev, "Failed to issue reset\n"); - return ret; + goto bulk_disable; } if (wm8960->pdata.shared_lrclk) { @@ -1429,7 +1499,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, if (ret != 0) { dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", ret); - return ret; + goto bulk_disable; } } @@ -1445,36 +1515,69 @@ static int wm8960_i2c_probe(struct i2c_client *i2c, regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); + /* ADCLRC pin configured as GPIO. */ + regmap_update_bits(wm8960->regmap, WM8960_IFACE2, 1 << 6, + wm8960->pdata.gpio_cfg[0] << 6); + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 0xF << 4, + wm8960->pdata.gpio_cfg[1] << 4); + + /* Enable headphone jack detect */ + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL4, 3 << 2, + wm8960->pdata.hp_cfg[0] << 2); + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 3 << 5, + wm8960->pdata.hp_cfg[1] << 5); + regmap_update_bits(wm8960->regmap, WM8960_ADDCTL1, 3, + wm8960->pdata.hp_cfg[2]); + i2c_set_clientdata(i2c, wm8960); - ret = snd_soc_register_codec(&i2c->dev, - &soc_codec_dev_wm8960, &wm8960_dai, 1); + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_component_dev_wm8960, &wm8960_dai, 1); + if (ret) + goto bulk_disable; + + return 0; +bulk_disable: + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); return ret; } -static int wm8960_i2c_remove(struct i2c_client *client) +static void wm8960_i2c_remove(struct i2c_client *client) { - snd_soc_unregister_codec(&client->dev); - return 0; + struct wm8960_priv *wm8960 = i2c_get_clientdata(client); + + regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies); } static const struct i2c_device_id wm8960_i2c_id[] = { - { "wm8960", 0 }, + { "wm8960" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); +#if defined(CONFIG_OF) static const struct of_device_id wm8960_of_match[] = { { .compatible = "wlf,wm8960", }, { } }; MODULE_DEVICE_TABLE(of, wm8960_of_match); +#endif + +#if defined(CONFIG_ACPI) +static const struct acpi_device_id wm8960_acpi_match[] = { + { "1AEC8960", 0 }, /* Wolfson PCI ID + part ID */ + { "10138960", 0 }, /* Cirrus Logic PCI ID + part ID */ + { }, +}; +MODULE_DEVICE_TABLE(acpi, wm8960_acpi_match); +#endif static struct i2c_driver wm8960_i2c_driver = { .driver = { .name = "wm8960", - .of_match_table = wm8960_of_match, + .of_match_table = of_match_ptr(wm8960_of_match), + .acpi_match_table = ACPI_PTR(wm8960_acpi_match), }, .probe = wm8960_i2c_probe, .remove = wm8960_i2c_remove, |
