diff options
Diffstat (limited to 'sound/soc/codecs/wm8900.c')
| -rw-r--r-- | sound/soc/codecs/wm8900.c | 248 |
1 files changed, 137 insertions, 111 deletions
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index f18c554efc9..d09fdce57f5 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -23,6 +23,7 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/regmap.h> #include <linux/spi/spi.h> #include <linux/slab.h> #include <sound/core.h> @@ -137,7 +138,7 @@ #define WM8900_LRC_MASK 0x03ff struct wm8900_priv { - enum snd_soc_control_type control_type; + struct regmap *regmap; u32 fll_in; /* FLL input frequency */ u32 fll_out; /* FLL output frequency */ @@ -147,54 +148,77 @@ struct wm8900_priv { * wm8900 register cache. We can't read the entire register space and we * have slow control buses so we cache the registers. */ -static const u16 wm8900_reg_defaults[WM8900_MAXREG] = { - 0x8900, 0x0000, - 0xc000, 0x0000, - 0x4050, 0x4000, - 0x0008, 0x0000, - 0x0040, 0x0040, - 0x1004, 0x00c0, - 0x00c0, 0x0000, - 0x0100, 0x00c0, - 0x00c0, 0x0000, - 0xb001, 0x0000, - 0x0000, 0x0044, - 0x004c, 0x004c, - 0x0044, 0x0044, - 0x0000, 0x0044, - 0x0000, 0x0000, - 0x0002, 0x0000, - 0x0000, 0x0000, - 0x0000, 0x0000, - 0x0008, 0x0000, - 0x0000, 0x0008, - 0x0097, 0x0100, - 0x0000, 0x0000, - 0x0050, 0x0050, - 0x0055, 0x0055, - 0x0055, 0x0000, - 0x0000, 0x0079, - 0x0079, 0x0079, - 0x0079, 0x0000, - /* Remaining registers all zero */ +static const struct reg_default wm8900_reg_defaults[] = { + { 1, 0x0000 }, + { 2, 0xc000 }, + { 3, 0x0000 }, + { 4, 0x4050 }, + { 5, 0x4000 }, + { 6, 0x0008 }, + { 7, 0x0000 }, + { 8, 0x0040 }, + { 9, 0x0040 }, + { 10, 0x1004 }, + { 11, 0x00c0 }, + { 12, 0x00c0 }, + { 13, 0x0000 }, + { 14, 0x0100 }, + { 15, 0x00c0 }, + { 16, 0x00c0 }, + { 17, 0x0000 }, + { 18, 0xb001 }, + { 19, 0x0000 }, + { 20, 0x0000 }, + { 21, 0x0044 }, + { 22, 0x004c }, + { 23, 0x004c }, + { 24, 0x0044 }, + { 25, 0x0044 }, + { 26, 0x0000 }, + { 27, 0x0044 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0002 }, + { 31, 0x0000 }, + { 32, 0x0000 }, + { 33, 0x0000 }, + { 34, 0x0000 }, + { 35, 0x0000 }, + { 36, 0x0008 }, + { 37, 0x0000 }, + { 38, 0x0000 }, + { 39, 0x0008 }, + { 40, 0x0097 }, + { 41, 0x0100 }, + { 42, 0x0000 }, + { 43, 0x0000 }, + { 44, 0x0050 }, + { 45, 0x0050 }, + { 46, 0x0055 }, + { 47, 0x0055 }, + { 48, 0x0055 }, + { 49, 0x0000 }, + { 50, 0x0000 }, + { 51, 0x0079 }, + { 52, 0x0079 }, + { 53, 0x0079 }, + { 54, 0x0079 }, + { 55, 0x0000 }, }; -static int wm8900_volatile_register(struct snd_soc_codec *codec, unsigned int reg) +static bool wm8900_volatile_register(struct device *dev, unsigned int reg) { switch (reg) { case WM8900_REG_ID: - return 1; + return true; default: - return 0; + return false; } } static void wm8900_reset(struct snd_soc_codec *codec) { snd_soc_write(codec, WM8900_REG_RESET, 0); - - memcpy(codec->reg_cache, wm8900_reg_defaults, - sizeof(wm8900_reg_defaults)); } static int wm8900_hp_event(struct snd_soc_dapm_widget *w, @@ -255,7 +279,8 @@ static int wm8900_hp_event(struct snd_soc_dapm_widget *w, break; default: - BUG(); + WARN(1, "Invalid event %d\n", event); + break; } return 0; @@ -279,53 +304,53 @@ static const DECLARE_TLV_DB_SCALE(adc_tlv, -7200, 75, 1); static const char *mic_bias_level_txt[] = { "0.9*AVDD", "0.65*AVDD" }; -static const struct soc_enum mic_bias_level = -SOC_ENUM_SINGLE(WM8900_REG_INCTL, 8, 2, mic_bias_level_txt); +static SOC_ENUM_SINGLE_DECL(mic_bias_level, + WM8900_REG_INCTL, 8, mic_bias_level_txt); static const char *dac_mute_rate_txt[] = { "Fast", "Slow" }; -static const struct soc_enum dac_mute_rate = -SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 7, 2, dac_mute_rate_txt); +static SOC_ENUM_SINGLE_DECL(dac_mute_rate, + WM8900_REG_DACCTRL, 7, dac_mute_rate_txt); static const char *dac_deemphasis_txt[] = { "Disabled", "32kHz", "44.1kHz", "48kHz" }; -static const struct soc_enum dac_deemphasis = -SOC_ENUM_SINGLE(WM8900_REG_DACCTRL, 4, 4, dac_deemphasis_txt); +static SOC_ENUM_SINGLE_DECL(dac_deemphasis, + WM8900_REG_DACCTRL, 4, dac_deemphasis_txt); static const char *adc_hpf_cut_txt[] = { "Hi-fi mode", "Voice mode 1", "Voice mode 2", "Voice mode 3" }; -static const struct soc_enum adc_hpf_cut = -SOC_ENUM_SINGLE(WM8900_REG_ADCCTRL, 5, 4, adc_hpf_cut_txt); +static SOC_ENUM_SINGLE_DECL(adc_hpf_cut, + WM8900_REG_ADCCTRL, 5, adc_hpf_cut_txt); static const char *lr_txt[] = { "Left", "Right" }; -static const struct soc_enum aifl_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 15, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(aifl_src, + WM8900_REG_AUDIO1, 15, lr_txt); -static const struct soc_enum aifr_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO1, 14, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(aifr_src, + WM8900_REG_AUDIO1, 14, lr_txt); -static const struct soc_enum dacl_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 15, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(dacl_src, + WM8900_REG_AUDIO2, 15, lr_txt); -static const struct soc_enum dacr_src = -SOC_ENUM_SINGLE(WM8900_REG_AUDIO2, 14, 2, lr_txt); +static SOC_ENUM_SINGLE_DECL(dacr_src, + WM8900_REG_AUDIO2, 14, lr_txt); static const char *sidetone_txt[] = { "Disabled", "Left ADC", "Right ADC" }; -static const struct soc_enum dacl_sidetone = -SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 2, 3, sidetone_txt); +static SOC_ENUM_SINGLE_DECL(dacl_sidetone, + WM8900_REG_SIDETONE, 2, sidetone_txt); -static const struct soc_enum dacr_sidetone = -SOC_ENUM_SINGLE(WM8900_REG_SIDETONE, 0, 3, sidetone_txt); +static SOC_ENUM_SINGLE_DECL(dacr_sidetone, + WM8900_REG_SIDETONE, 0, sidetone_txt); static const struct snd_kcontrol_new wm8900_snd_controls[] = { SOC_ENUM("Mic Bias Level", mic_bias_level), @@ -469,10 +494,10 @@ SOC_DAPM_SINGLE("RINPUT2 Switch", WM8900_REG_INCTL, 1, 1, 0), SOC_DAPM_SINGLE("RINPUT3 Switch", WM8900_REG_INCTL, 0, 1, 0), }; -static const char *wm9700_lp_mux[] = { "Disabled", "Enabled" }; +static const char *wm8900_lp_mux[] = { "Disabled", "Enabled" }; -static const struct soc_enum wm8900_lineout2_lp_mux = -SOC_ENUM_SINGLE(WM8900_REG_LOUTMIXCTL1, 1, 2, wm9700_lp_mux); +static SOC_ENUM_SINGLE_DECL(wm8900_lineout2_lp_mux, + WM8900_REG_LOUTMIXCTL1, 1, wm8900_lp_mux); static const struct snd_kcontrol_new wm8900_lineout2_lp = SOC_DAPM_ENUM("Route", wm8900_lineout2_lp_mux); @@ -610,8 +635,7 @@ static int wm8900_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_codec *codec = rtd->codec; + struct snd_soc_codec *codec = dai->codec; u16 reg; reg = snd_soc_read(codec, WM8900_REG_AUDIO1) & ~0x60; @@ -668,7 +692,8 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, unsigned int K, Ndiv, Nmod, target; unsigned int div; - BUG_ON(!Fout); + if (WARN_ON(!Fout)) + return -EINVAL; /* The FLL must run at 90-100MHz which is then scaled down to * the output value by FLLCLK_DIV. */ @@ -719,8 +744,9 @@ static int fll_factors(struct _fll_div *fll_div, unsigned int Fref, /* Move down to proper range now rounding is done */ fll_div->k = K / 10; - BUG_ON(target != Fout * (fll_div->fllclk_div << 2)); - BUG_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n); + if (WARN_ON(target != Fout * (fll_div->fllclk_div << 2)) || + WARN_ON(!K && target != Fref * fll_div->fll_ratio * fll_div->n)) + return -EINVAL; return 0; } @@ -1120,13 +1146,16 @@ static int wm8900_suspend(struct snd_soc_codec *codec) static int wm8900_resume(struct snd_soc_codec *codec) { struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec); - u16 *cache; - int i, ret; - - cache = kmemdup(codec->reg_cache, sizeof(wm8900_reg_defaults), - GFP_KERNEL); + int ret; wm8900_reset(codec); + + ret = regcache_sync(wm8900->regmap); + if (ret != 0) { + dev_err(codec->dev, "Failed to restore cache: %d\n", ret); + return ret; + } + wm8900_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* Restart the FLL? */ @@ -1140,31 +1169,16 @@ static int wm8900_resume(struct snd_soc_codec *codec) ret = wm8900_set_fll(codec, 0, fll_in, fll_out); if (ret != 0) { dev_err(codec->dev, "Failed to restart FLL\n"); - kfree(cache); return ret; } } - if (cache) { - for (i = 0; i < WM8900_MAXREG; i++) - snd_soc_write(codec, i, cache[i]); - kfree(cache); - } else - dev_err(codec->dev, "Unable to allocate register cache\n"); - return 0; } static int wm8900_probe(struct snd_soc_codec *codec) { - struct wm8900_priv *wm8900 = snd_soc_codec_get_drvdata(codec); - int ret = 0, reg; - - ret = snd_soc_codec_set_cache_io(codec, 8, 16, wm8900->control_type); - if (ret != 0) { - dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret); - return ret; - } + int reg; reg = snd_soc_read(codec, WM8900_REG_ID); if (reg != 0x8900) { @@ -1208,10 +1222,6 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8900 = { .suspend = wm8900_suspend, .resume = wm8900_resume, .set_bias_level = wm8900_set_bias_level, - .volatile_register = wm8900_volatile_register, - .reg_cache_size = ARRAY_SIZE(wm8900_reg_defaults), - .reg_word_size = sizeof(u16), - .reg_cache_default = wm8900_reg_defaults, .controls = wm8900_snd_controls, .num_controls = ARRAY_SIZE(wm8900_snd_controls), @@ -1221,30 +1231,44 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8900 = { .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes), }; +static const struct regmap_config wm8900_regmap = { + .reg_bits = 8, + .val_bits = 16, + .max_register = WM8900_MAXREG, + + .reg_defaults = wm8900_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8900_reg_defaults), + .cache_type = REGCACHE_RBTREE, + + .volatile_reg = wm8900_volatile_register, +}; + #if defined(CONFIG_SPI_MASTER) -static int __devinit wm8900_spi_probe(struct spi_device *spi) +static int wm8900_spi_probe(struct spi_device *spi) { struct wm8900_priv *wm8900; int ret; - wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL); + wm8900 = devm_kzalloc(&spi->dev, sizeof(struct wm8900_priv), + GFP_KERNEL); if (wm8900 == NULL) return -ENOMEM; - wm8900->control_type = SND_SOC_SPI; + wm8900->regmap = devm_regmap_init_spi(spi, &wm8900_regmap); + if (IS_ERR(wm8900->regmap)) + return PTR_ERR(wm8900->regmap); + spi_set_drvdata(spi, wm8900); ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_wm8900, &wm8900_dai, 1); - if (ret < 0) - kfree(wm8900); + return ret; } -static int __devexit wm8900_spi_remove(struct spi_device *spi) +static int wm8900_spi_remove(struct spi_device *spi) { snd_soc_unregister_codec(&spi->dev); - kfree(spi_get_drvdata(spi)); return 0; } @@ -1254,35 +1278,37 @@ static struct spi_driver wm8900_spi_driver = { .owner = THIS_MODULE, }, .probe = wm8900_spi_probe, - .remove = __devexit_p(wm8900_spi_remove), + .remove = wm8900_spi_remove, }; #endif /* CONFIG_SPI_MASTER */ -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static __devinit int wm8900_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +#if IS_ENABLED(CONFIG_I2C) +static int wm8900_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct wm8900_priv *wm8900; int ret; - wm8900 = kzalloc(sizeof(struct wm8900_priv), GFP_KERNEL); + wm8900 = devm_kzalloc(&i2c->dev, sizeof(struct wm8900_priv), + GFP_KERNEL); if (wm8900 == NULL) return -ENOMEM; + wm8900->regmap = devm_regmap_init_i2c(i2c, &wm8900_regmap); + if (IS_ERR(wm8900->regmap)) + return PTR_ERR(wm8900->regmap); + i2c_set_clientdata(i2c, wm8900); - wm8900->control_type = SND_SOC_I2C; ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8900, &wm8900_dai, 1); - if (ret < 0) - kfree(wm8900); + return ret; } -static __devexit int wm8900_i2c_remove(struct i2c_client *client) +static int wm8900_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); - kfree(i2c_get_clientdata(client)); return 0; } @@ -1298,7 +1324,7 @@ static struct i2c_driver wm8900_i2c_driver = { .owner = THIS_MODULE, }, .probe = wm8900_i2c_probe, - .remove = __devexit_p(wm8900_i2c_remove), + .remove = wm8900_i2c_remove, .id_table = wm8900_i2c_id, }; #endif @@ -1306,7 +1332,7 @@ static struct i2c_driver wm8900_i2c_driver = { static int __init wm8900_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8900_i2c_driver); if (ret != 0) { printk(KERN_ERR "Failed to register wm8900 I2C driver: %d\n", @@ -1326,7 +1352,7 @@ module_init(wm8900_modinit); static void __exit wm8900_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8900_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) |
