diff options
Diffstat (limited to 'sound/soc/codecs/wm8580.c')
| -rw-r--r-- | sound/soc/codecs/wm8580.c | 674 |
1 files changed, 327 insertions, 347 deletions
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 9f6be3d31ac..7665ff6aea6 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1,7 +1,7 @@ /* * wm8580.c -- WM8580 ALSA Soc Audio driver * - * Copyright 2008, 2009 Wolfson Microelectronics PLC. + * Copyright 2008-12 Wolfson Microelectronics PLC. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -23,12 +23,15 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/of_device.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/tlv.h> #include <sound/initval.h> #include <asm/div64.h> @@ -91,6 +94,8 @@ #define WM8580_MAX_REGISTER 0x35 +#define WM8580_DACOSR 0x40 + /* PLLB4 (register 7h) */ #define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 #define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 @@ -109,19 +114,7 @@ /* AIF control 1 (registers 9h-bh) */ #define WM8580_AIF_RATE_MASK 0x7 -#define WM8580_AIF_RATE_128 0x0 -#define WM8580_AIF_RATE_192 0x1 -#define WM8580_AIF_RATE_256 0x2 -#define WM8580_AIF_RATE_384 0x3 -#define WM8580_AIF_RATE_512 0x4 -#define WM8580_AIF_RATE_768 0x5 -#define WM8580_AIF_RATE_1152 0x6 - #define WM8580_AIF_BCLKSEL_MASK 0x18 -#define WM8580_AIF_BCLKSEL_64 0x00 -#define WM8580_AIF_BCLKSEL_128 0x08 -#define WM8580_AIF_BCLKSEL_256 0x10 -#define WM8580_AIF_BCLKSEL_SYSCLK 0x18 #define WM8580_AIF_MS 0x20 @@ -165,104 +158,93 @@ * We can't read the WM8580 register space when we * are using 2 wire for device control, so we cache them instead. */ -static const u16 wm8580_reg[] = { - 0x0121, 0x017e, 0x007d, 0x0014, /*R3*/ - 0x0121, 0x017e, 0x007d, 0x0194, /*R7*/ - 0x001c, 0x0002, 0x0002, 0x00c2, /*R11*/ - 0x0182, 0x0082, 0x000a, 0x0024, /*R15*/ - 0x0009, 0x0000, 0x00ff, 0x0000, /*R19*/ - 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R23*/ - 0x00ff, 0x00ff, 0x00ff, 0x00ff, /*R27*/ - 0x01f0, 0x0040, 0x0000, 0x0000, /*R31(0x1F)*/ - 0x0000, 0x0000, 0x0031, 0x000b, /*R35*/ - 0x0039, 0x0000, 0x0010, 0x0032, /*R39*/ - 0x0054, 0x0076, 0x0098, 0x0000, /*R43(0x2B)*/ - 0x0000, 0x0000, 0x0000, 0x0000, /*R47*/ - 0x0000, 0x0000, 0x005e, 0x003e, /*R51(0x33)*/ - 0x0000, 0x0000 /*R53*/ +static const struct reg_default wm8580_reg_defaults[] = { + { 0, 0x0121 }, + { 1, 0x017e }, + { 2, 0x007d }, + { 3, 0x0014 }, + { 4, 0x0121 }, + { 5, 0x017e }, + { 6, 0x007d }, + { 7, 0x0194 }, + { 8, 0x0010 }, + { 9, 0x0002 }, + { 10, 0x0002 }, + { 11, 0x00c2 }, + { 12, 0x0182 }, + { 13, 0x0082 }, + { 14, 0x000a }, + { 15, 0x0024 }, + { 16, 0x0009 }, + { 17, 0x0000 }, + { 18, 0x00ff }, + { 19, 0x0000 }, + { 20, 0x00ff }, + { 21, 0x00ff }, + { 22, 0x00ff }, + { 23, 0x00ff }, + { 24, 0x00ff }, + { 25, 0x00ff }, + { 26, 0x00ff }, + { 27, 0x00ff }, + { 28, 0x01f0 }, + { 29, 0x0040 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0031 }, + { 35, 0x000b }, + { 36, 0x0039 }, + { 37, 0x0000 }, + { 38, 0x0010 }, + { 39, 0x0032 }, + { 40, 0x0054 }, + { 41, 0x0076 }, + { 42, 0x0098 }, + { 43, 0x0000 }, + { 44, 0x0000 }, + { 45, 0x0000 }, + { 46, 0x0000 }, + { 47, 0x0000 }, + { 48, 0x0000 }, + { 49, 0x0000 }, + { 50, 0x005e }, + { 51, 0x003e }, + { 52, 0x0000 }, }; +static bool wm8580_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8580_RESET: + return true; + default: + return false; + } +} + struct pll_state { unsigned int in; unsigned int out; }; +#define WM8580_NUM_SUPPLIES 3 +static const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { + "AVDD", + "DVDD", + "PVDD", +}; + /* codec private data */ struct wm8580_priv { - struct snd_soc_codec codec; - u16 reg_cache[WM8580_MAX_REGISTER + 1]; + struct regmap *regmap; + struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; struct pll_state a; struct pll_state b; + int sysclk[2]; }; - -/* - * read wm8580 register cache - */ -static inline unsigned int wm8580_read_reg_cache(struct snd_soc_codec *codec, - unsigned int reg) -{ - u16 *cache = codec->reg_cache; - BUG_ON(reg >= ARRAY_SIZE(wm8580_reg)); - return cache[reg]; -} - -/* - * write wm8580 register cache - */ -static inline void wm8580_write_reg_cache(struct snd_soc_codec *codec, - unsigned int reg, unsigned int value) -{ - u16 *cache = codec->reg_cache; - - cache[reg] = value; -} - -/* - * write to the WM8580 register space - */ -static int wm8580_write(struct snd_soc_codec *codec, unsigned int reg, - unsigned int value) -{ - u8 data[2]; - - BUG_ON(reg >= ARRAY_SIZE(wm8580_reg)); - - /* Registers are 9 bits wide */ - value &= 0x1ff; - - switch (reg) { - case WM8580_RESET: - /* Uncached */ - break; - default: - if (value == wm8580_read_reg_cache(codec, reg)) - return 0; - } - - /* data is - * D15..D9 WM8580 register offset - * D8...D0 register data - */ - data[0] = (reg << 1) | ((value >> 8) & 0x0001); - data[1] = value & 0x00ff; - - wm8580_write_reg_cache(codec, reg, value); - if (codec->hw_write(codec->control_data, data, 2) == 2) - return 0; - else - return -EIO; -} - -static inline unsigned int wm8580_read(struct snd_soc_codec *codec, - unsigned int reg) -{ - switch (reg) { - default: - return wm8580_read_reg_cache(codec, reg); - } -} - static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static int wm8580_out_vu(struct snd_kcontrol *kcontrol, @@ -270,55 +252,42 @@ static int wm8580_out_vu(struct snd_kcontrol *kcontrol, { struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); unsigned int reg = mc->reg; unsigned int reg2 = mc->rreg; int ret; - u16 val; - /* Clear the register cache so we write without VU set */ - wm8580_write_reg_cache(codec, reg, 0); - wm8580_write_reg_cache(codec, reg2, 0); + /* Clear the register cache VU so we write without VU set */ + regcache_cache_only(wm8580->regmap, true); + regmap_update_bits(wm8580->regmap, reg, 0x100, 0x000); + regmap_update_bits(wm8580->regmap, reg2, 0x100, 0x000); + regcache_cache_only(wm8580->regmap, false); - ret = snd_soc_put_volsw_2r(kcontrol, ucontrol); + ret = snd_soc_put_volsw(kcontrol, ucontrol); if (ret < 0) return ret; /* Now write again with the volume update bit set */ - val = wm8580_read_reg_cache(codec, reg); - wm8580_write(codec, reg, val | 0x0100); - - val = wm8580_read_reg_cache(codec, reg2); - wm8580_write(codec, reg2, val | 0x0100); + snd_soc_update_bits(codec, reg, 0x100, 0x100); + snd_soc_update_bits(codec, reg2, 0x100, 0x100); return 0; } -#define SOC_WM8580_OUT_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, \ - .tlv.p = (tlv_array), \ - .info = snd_soc_info_volsw_2r, \ - .get = snd_soc_get_volsw_2r, .put = wm8580_out_vu, \ - .private_value = (unsigned long)&(struct soc_mixer_control) \ - {.reg = reg_left, .rreg = reg_right, .shift = xshift, \ - .max = xmax, .invert = xinvert} } - static const struct snd_kcontrol_new wm8580_snd_controls[] = { -SOC_WM8580_OUT_DOUBLE_R_TLV("DAC1 Playback Volume", - WM8580_DIGITAL_ATTENUATION_DACL1, - WM8580_DIGITAL_ATTENUATION_DACR1, - 0, 0xff, 0, dac_tlv), -SOC_WM8580_OUT_DOUBLE_R_TLV("DAC2 Playback Volume", - WM8580_DIGITAL_ATTENUATION_DACL2, - WM8580_DIGITAL_ATTENUATION_DACR2, - 0, 0xff, 0, dac_tlv), -SOC_WM8580_OUT_DOUBLE_R_TLV("DAC3 Playback Volume", - WM8580_DIGITAL_ATTENUATION_DACL3, - WM8580_DIGITAL_ATTENUATION_DACR3, - 0, 0xff, 0, dac_tlv), +SOC_DOUBLE_R_EXT_TLV("DAC1 Playback Volume", + WM8580_DIGITAL_ATTENUATION_DACL1, + WM8580_DIGITAL_ATTENUATION_DACR1, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), +SOC_DOUBLE_R_EXT_TLV("DAC2 Playback Volume", + WM8580_DIGITAL_ATTENUATION_DACL2, + WM8580_DIGITAL_ATTENUATION_DACR2, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), +SOC_DOUBLE_R_EXT_TLV("DAC3 Playback Volume", + WM8580_DIGITAL_ATTENUATION_DACL3, + WM8580_DIGITAL_ATTENUATION_DACR3, + 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), SOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0), SOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0), @@ -329,12 +298,12 @@ SOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0), SOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0), SOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0), -SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 0), -SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 0), -SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 0), +SOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 1), +SOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 1), +SOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 1), -SOC_DOUBLE("ADC Mute Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 0), -SOC_SINGLE("ADC High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), +SOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1), +SOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), }; static const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { @@ -355,7 +324,7 @@ SND_SOC_DAPM_INPUT("AINL"), SND_SOC_DAPM_INPUT("AINR"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8580_dapm_routes[] = { { "VOUT1L", NULL, "DAC1" }, { "VOUT1R", NULL, "DAC1" }, @@ -369,17 +338,6 @@ static const struct snd_soc_dapm_route audio_map[] = { { "ADC", NULL, "AINR" }, }; -static int wm8580_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_dapm_new_controls(codec, wm8580_dapm_widgets, - ARRAY_SIZE(wm8580_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - snd_soc_dapm_new_widgets(codec); - return 0; -} - /* PLL divisors */ struct _pll_div { u32 prescale:1; @@ -415,7 +373,7 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target, unsigned int K, Ndiv, Nmod; int i; - pr_debug("wm8580: PLL %dHz->%dHz\n", source, target); + pr_debug("wm8580: PLL %uHz->%uHz\n", source, target); /* Scale the output frequency up; the PLL should run in the * region of 90-100MHz. @@ -447,7 +405,7 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target, if ((Ndiv < 5) || (Ndiv > 13)) { printk(KERN_ERR - "WM8580 N=%d outside supported range\n", Ndiv); + "WM8580 N=%u outside supported range\n", Ndiv); return -EINVAL; } @@ -468,12 +426,12 @@ static int pll_factors(struct _pll_div *pll_div, unsigned int target, return 0; } -static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out) +static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, + int source, unsigned int freq_in, unsigned int freq_out) { int offset; struct snd_soc_codec *codec = codec_dai->codec; - struct wm8580_priv *wm8580 = codec->private_data; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); struct pll_state *state; struct _pll_div pll_div; unsigned int reg; @@ -512,31 +470,33 @@ static int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, /* Always disable the PLL - it is not safe to leave it running * while reprogramming it. */ - reg = wm8580_read(codec, WM8580_PWRDN2); - wm8580_write(codec, WM8580_PWRDN2, reg | pwr_mask); + snd_soc_update_bits(codec, WM8580_PWRDN2, pwr_mask, pwr_mask); if (!freq_in || !freq_out) return 0; - wm8580_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); - wm8580_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0xff); - wm8580_write(codec, WM8580_PLLA3 + offset, + snd_soc_write(codec, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); + snd_soc_write(codec, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff); + snd_soc_write(codec, WM8580_PLLA3 + offset, (pll_div.k >> 18 & 0xf) | (pll_div.n << 4)); - reg = wm8580_read(codec, WM8580_PLLA4 + offset); - reg &= ~0x3f; + reg = snd_soc_read(codec, WM8580_PLLA4 + offset); + reg &= ~0x1b; reg |= pll_div.prescale | pll_div.postscale << 1 | pll_div.freqmode << 3; - wm8580_write(codec, WM8580_PLLA4 + offset, reg); + snd_soc_write(codec, WM8580_PLLA4 + offset, reg); /* All done, turn it on */ - reg = wm8580_read(codec, WM8580_PWRDN2); - wm8580_write(codec, WM8580_PWRDN2, reg & ~pwr_mask); + snd_soc_update_bits(codec, WM8580_PWRDN2, pwr_mask, 0); return 0; } +static const int wm8580_sysclk_ratios[] = { + 128, 192, 256, 384, 512, 768, 1152, +}; + /* * Set PCM DAI bit size and sample rate. */ @@ -544,30 +504,68 @@ static int wm8580_paif_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 paifb = wm8580_read(codec, WM8580_PAIF3 + dai->id); + struct snd_soc_codec *codec = dai->codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + u16 paifa = 0; + u16 paifb = 0; + int i, ratio, osr; - paifb &= ~WM8580_AIF_LENGTH_MASK; /* bit size */ switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: + paifa |= 0x8; break; case SNDRV_PCM_FORMAT_S20_3LE: + paifa |= 0x0; paifb |= WM8580_AIF_LENGTH_20; break; case SNDRV_PCM_FORMAT_S24_LE: + paifa |= 0x0; paifb |= WM8580_AIF_LENGTH_24; break; case SNDRV_PCM_FORMAT_S32_LE: - paifb |= WM8580_AIF_LENGTH_24; + paifa |= 0x0; + paifb |= WM8580_AIF_LENGTH_32; break; default: return -EINVAL; } - wm8580_write(codec, WM8580_PAIF3 + dai->id, paifb); + /* Look up the SYSCLK ratio; accept only exact matches */ + ratio = wm8580->sysclk[dai->driver->id] / params_rate(params); + for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) + if (ratio == wm8580_sysclk_ratios[i]) + break; + if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { + dev_err(codec->dev, "Invalid clock ratio %d/%d\n", + wm8580->sysclk[dai->driver->id], params_rate(params)); + return -EINVAL; + } + paifa |= i; + dev_dbg(codec->dev, "Running at %dfs with %dHz clock\n", + wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + switch (ratio) { + case 128: + case 192: + osr = WM8580_DACOSR; + dev_dbg(codec->dev, "Selecting 64x OSR\n"); + break; + default: + osr = 0; + dev_dbg(codec->dev, "Selecting 128x OSR\n"); + break; + } + + snd_soc_update_bits(codec, WM8580_PAIF3, WM8580_DACOSR, osr); + } + + snd_soc_update_bits(codec, WM8580_PAIF1 + dai->driver->id, + WM8580_AIF_RATE_MASK | WM8580_AIF_BCLKSEL_MASK, + paifa); + snd_soc_update_bits(codec, WM8580_PAIF3 + dai->driver->id, + WM8580_AIF_LENGTH_MASK, paifb); return 0; } @@ -579,8 +577,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int aifb; int can_invert_lrclk; - aifa = wm8580_read(codec, WM8580_PAIF1 + codec_dai->id); - aifb = wm8580_read(codec, WM8580_PAIF3 + codec_dai->id); + aifa = snd_soc_read(codec, WM8580_PAIF1 + codec_dai->driver->id); + aifb = snd_soc_read(codec, WM8580_PAIF3 + codec_dai->driver->id); aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); @@ -646,8 +644,8 @@ static int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - wm8580_write(codec, WM8580_PAIF1 + codec_dai->id, aifa); - wm8580_write(codec, WM8580_PAIF3 + codec_dai->id, aifb); + snd_soc_write(codec, WM8580_PAIF1 + codec_dai->driver->id, aifa); + snd_soc_write(codec, WM8580_PAIF3 + codec_dai->driver->id, aifb); return 0; } @@ -660,7 +658,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, switch (div_id) { case WM8580_MCLK: - reg = wm8580_read(codec, WM8580_PLLB4); + reg = snd_soc_read(codec, WM8580_PLLB4); reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK; switch (div) { @@ -682,33 +680,11 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, default: return -EINVAL; } - wm8580_write(codec, WM8580_PLLB4, reg); - break; - - case WM8580_DAC_CLKSEL: - reg = wm8580_read(codec, WM8580_CLKSEL); - reg &= ~WM8580_CLKSEL_DAC_CLKSEL_MASK; - - switch (div) { - case WM8580_CLKSRC_MCLK: - break; - - case WM8580_CLKSRC_PLLA: - reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLA; - break; - - case WM8580_CLKSRC_PLLB: - reg |= WM8580_CLKSEL_DAC_CLKSEL_PLLB; - break; - - default: - return -EINVAL; - } - wm8580_write(codec, WM8580_CLKSEL, reg); + snd_soc_write(codec, WM8580_PLLB4, reg); break; case WM8580_CLKOUTSRC: - reg = wm8580_read(codec, WM8580_PLLB4); + reg = snd_soc_read(codec, WM8580_PLLB4); reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; switch (div) { @@ -730,7 +706,7 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, default: return -EINVAL; } - wm8580_write(codec, WM8580_PLLB4, reg); + snd_soc_write(codec, WM8580_PLLB4, reg); break; default: @@ -740,19 +716,72 @@ static int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, return 0; } +static int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_codec *codec = dai->codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + int ret, sel, sel_mask, sel_shift; + + switch (dai->driver->id) { + case WM8580_DAI_PAIFRX: + sel_mask = 0x3; + sel_shift = 0; + break; + + case WM8580_DAI_PAIFTX: + sel_mask = 0xc; + sel_shift = 2; + break; + + default: + WARN(1, "Unknown DAI driver ID\n"); + return -EINVAL; + } + + switch (clk_id) { + case WM8580_CLKSRC_ADCMCLK: + if (dai->driver->id != WM8580_DAI_PAIFTX) + return -EINVAL; + sel = 0 << sel_shift; + break; + case WM8580_CLKSRC_PLLA: + sel = 1 << sel_shift; + break; + case WM8580_CLKSRC_PLLB: + sel = 2 << sel_shift; + break; + case WM8580_CLKSRC_MCLK: + sel = 3 << sel_shift; + break; + default: + dev_err(codec->dev, "Unknown clock %d\n", clk_id); + return -EINVAL; + } + + /* We really should validate PLL settings but not yet */ + wm8580->sysclk[dai->driver->id] = freq; + + ret = snd_soc_update_bits(codec, WM8580_CLKSEL, sel_mask, sel); + if (ret < 0) + return ret; + + return 0; +} + static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) { struct snd_soc_codec *codec = codec_dai->codec; unsigned int reg; - reg = wm8580_read(codec, WM8580_DAC_CONTROL5); + reg = snd_soc_read(codec, WM8580_DAC_CONTROL5); if (mute) reg |= WM8580_DAC_CONTROL5_MUTEALL; else reg &= ~WM8580_DAC_CONTROL5_MUTEALL; - wm8580_write(codec, WM8580_DAC_CONTROL5, reg); + snd_soc_write(codec, WM8580_DAC_CONTROL5, reg); return 0; } @@ -760,39 +789,38 @@ static int wm8580_digital_mute(struct snd_soc_dai *codec_dai, int mute) static int wm8580_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { - u16 reg; switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: break; case SND_SOC_BIAS_STANDBY: - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { /* Power up and get individual control of the DACs */ - reg = wm8580_read(codec, WM8580_PWRDN1); - reg &= ~(WM8580_PWRDN1_PWDN | WM8580_PWRDN1_ALLDACPD); - wm8580_write(codec, WM8580_PWRDN1, reg); - - /* Make VMID high impedence */ - reg = wm8580_read(codec, WM8580_ADC_CONTROL1); - reg &= ~0x100; - wm8580_write(codec, WM8580_ADC_CONTROL1, reg); + snd_soc_update_bits(codec, WM8580_PWRDN1, + WM8580_PWRDN1_PWDN | + WM8580_PWRDN1_ALLDACPD, 0); + + /* Make VMID high impedance */ + snd_soc_update_bits(codec, WM8580_ADC_CONTROL1, + 0x100, 0); } break; case SND_SOC_BIAS_OFF: - reg = wm8580_read(codec, WM8580_PWRDN1); - wm8580_write(codec, WM8580_PWRDN1, reg | WM8580_PWRDN1_PWDN); + snd_soc_update_bits(codec, WM8580_PWRDN1, + WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN); break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } #define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) -static struct snd_soc_dai_ops wm8580_dai_ops_playback = { +static const struct snd_soc_dai_ops wm8580_dai_ops_playback = { + .set_sysclk = wm8580_set_sysclk, .hw_params = wm8580_paif_hw_params, .set_fmt = wm8580_set_paif_dai_fmt, .set_clkdiv = wm8580_set_dai_clkdiv, @@ -800,17 +828,18 @@ static struct snd_soc_dai_ops wm8580_dai_ops_playback = { .digital_mute = wm8580_digital_mute, }; -static struct snd_soc_dai_ops wm8580_dai_ops_capture = { +static const struct snd_soc_dai_ops wm8580_dai_ops_capture = { + .set_sysclk = wm8580_set_sysclk, .hw_params = wm8580_paif_hw_params, .set_fmt = wm8580_set_paif_dai_fmt, .set_clkdiv = wm8580_set_dai_clkdiv, .set_pll = wm8580_set_dai_pll, }; -struct snd_soc_dai wm8580_dai[] = { +static struct snd_soc_dai_driver wm8580_dai[] = { { - .name = "WM8580 PAIFRX", - .id = 0, + .name = "wm8580-hifi-playback", + .id = WM8580_DAI_PAIFRX, .playback = { .stream_name = "Playback", .channels_min = 1, @@ -821,8 +850,8 @@ struct snd_soc_dai wm8580_dai[] = { .ops = &wm8580_dai_ops_playback, }, { - .name = "WM8580 PAIFTX", - .id = 1, + .name = "wm8580-hifi-capture", + .id = WM8580_DAI_PAIFTX, .capture = { .stream_name = "Capture", .channels_min = 2, @@ -833,165 +862,115 @@ struct snd_soc_dai wm8580_dai[] = { .ops = &wm8580_dai_ops_capture, }, }; -EXPORT_SYMBOL_GPL(wm8580_dai); -static struct snd_soc_codec *wm8580_codec; - -static int wm8580_probe(struct platform_device *pdev) +static int wm8580_probe(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec; + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); int ret = 0; - if (wm8580_codec == NULL) { - dev_err(&pdev->dev, "Codec device not registered\n"); - return -ENODEV; + ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), + wm8580->supplies); + if (ret != 0) { + dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); + goto err_regulator_get; } - socdev->card->codec = wm8580_codec; - codec = wm8580_codec; - - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - dev_err(codec->dev, "failed to create pcms: %d\n", ret); - goto pcm_err; + /* Get the codec into a known state */ + ret = snd_soc_write(codec, WM8580_RESET, 0); + if (ret != 0) { + dev_err(codec->dev, "Failed to reset codec: %d\n", ret); + goto err_regulator_enable; } - snd_soc_add_controls(codec, wm8580_snd_controls, - ARRAY_SIZE(wm8580_snd_controls)); - wm8580_add_widgets(codec); - ret = snd_soc_init_card(socdev); - if (ret < 0) { - dev_err(codec->dev, "failed to register card: %d\n", ret); - goto card_err; - } + wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - return ret; + return 0; -card_err: - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -pcm_err: +err_regulator_enable: + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); +err_regulator_get: return ret; } /* power down chip */ -static int wm8580_remove(struct platform_device *pdev) +static int wm8580_remove(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec); + + wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF); - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); + regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); return 0; } -struct snd_soc_codec_device soc_codec_dev_wm8580 = { - .probe = wm8580_probe, - .remove = wm8580_remove, +static struct snd_soc_codec_driver soc_codec_dev_wm8580 = { + .probe = wm8580_probe, + .remove = wm8580_remove, + .set_bias_level = wm8580_set_bias_level, + + .controls = wm8580_snd_controls, + .num_controls = ARRAY_SIZE(wm8580_snd_controls), + .dapm_widgets = wm8580_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets), + .dapm_routes = wm8580_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes), }; -EXPORT_SYMBOL_GPL(soc_codec_dev_wm8580); -static int wm8580_register(struct wm8580_priv *wm8580) -{ - int ret, i; - struct snd_soc_codec *codec = &wm8580->codec; - - if (wm8580_codec) { - dev_err(codec->dev, "Another WM8580 is registered\n"); - ret = -EINVAL; - goto err; - } - - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); - - codec->private_data = wm8580; - codec->name = "WM8580"; - codec->owner = THIS_MODULE; - codec->read = wm8580_read_reg_cache; - codec->write = wm8580_write; - codec->bias_level = SND_SOC_BIAS_OFF; - codec->set_bias_level = wm8580_set_bias_level; - codec->dai = wm8580_dai; - codec->num_dai = ARRAY_SIZE(wm8580_dai); - codec->reg_cache_size = ARRAY_SIZE(wm8580->reg_cache); - codec->reg_cache = &wm8580->reg_cache; - - memcpy(codec->reg_cache, wm8580_reg, sizeof(wm8580_reg)); - - /* Get the codec into a known state */ - ret = wm8580_write(codec, WM8580_RESET, 0); - if (ret != 0) { - dev_err(codec->dev, "Failed to reset codec: %d\n", ret); - goto err; - } - - for (i = 0; i < ARRAY_SIZE(wm8580_dai); i++) - wm8580_dai[i].dev = codec->dev; - - wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - - wm8580_codec = codec; - - ret = snd_soc_register_codec(codec); - if (ret != 0) { - dev_err(codec->dev, "Failed to register codec: %d\n", ret); - goto err; - } - - ret = snd_soc_register_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); - if (ret != 0) { - dev_err(codec->dev, "Failed to register DAI: %d\n", ret); - goto err_codec; - } +static const struct of_device_id wm8580_of_match[] = { + { .compatible = "wlf,wm8580" }, + { }, +}; - return 0; +static const struct regmap_config wm8580_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8580_MAX_REGISTER, -err_codec: - snd_soc_unregister_codec(codec); -err: - kfree(wm8580); - return ret; -} + .reg_defaults = wm8580_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults), + .cache_type = REGCACHE_RBTREE, -static void wm8580_unregister(struct wm8580_priv *wm8580) -{ - wm8580_set_bias_level(&wm8580->codec, SND_SOC_BIAS_OFF); - snd_soc_unregister_dais(wm8580_dai, ARRAY_SIZE(wm8580_dai)); - snd_soc_unregister_codec(&wm8580->codec); - kfree(wm8580); - wm8580_codec = NULL; -} + .volatile_reg = wm8580_volatile, +}; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) static int wm8580_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8580_priv *wm8580; - struct snd_soc_codec *codec; + int ret, i; - wm8580 = kzalloc(sizeof(struct wm8580_priv), GFP_KERNEL); + wm8580 = devm_kzalloc(&i2c->dev, sizeof(struct wm8580_priv), + GFP_KERNEL); if (wm8580 == NULL) return -ENOMEM; - codec = &wm8580->codec; - codec->hw_write = (hw_write_t)i2c_master_send; + wm8580->regmap = devm_regmap_init_i2c(i2c, &wm8580_regmap); + if (IS_ERR(wm8580->regmap)) + return PTR_ERR(wm8580->regmap); + + for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++) + wm8580->supplies[i].supply = wm8580_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8580->supplies), + wm8580->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } i2c_set_clientdata(i2c, wm8580); - codec->control_data = i2c; - codec->dev = &i2c->dev; + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai)); - return wm8580_register(wm8580); + return ret; } static int wm8580_i2c_remove(struct i2c_client *client) { - struct wm8580_priv *wm8580 = i2c_get_clientdata(client); - wm8580_unregister(wm8580); + snd_soc_unregister_codec(&client->dev); return 0; } @@ -1005,6 +984,7 @@ static struct i2c_driver wm8580_i2c_driver = { .driver = { .name = "wm8580", .owner = THIS_MODULE, + .of_match_table = wm8580_of_match, }, .probe = wm8580_i2c_probe, .remove = wm8580_i2c_remove, @@ -1014,22 +994,22 @@ static struct i2c_driver wm8580_i2c_driver = { static int __init wm8580_modinit(void) { - int ret; + int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8580_i2c_driver); if (ret != 0) { pr_err("Failed to register WM8580 I2C driver: %d\n", ret); } #endif - return 0; + return ret; } module_init(wm8580_modinit); static void __exit wm8580_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8580_i2c_driver); #endif } |
