diff options
Diffstat (limited to 'sound/soc/codecs/wm8804.c')
| -rw-r--r-- | sound/soc/codecs/wm8804.c | 225 |
1 files changed, 109 insertions, 116 deletions
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c index 6785688f880..d96e5963ee3 100644 --- a/sound/soc/codecs/wm8804.c +++ b/sound/soc/codecs/wm8804.c @@ -1,7 +1,7 @@ /* * wm8804.c -- WM8804 S/PDIF transceiver driver * - * Copyright 2010 Wolfson Microelectronics plc + * Copyright 2010-11 Wolfson Microelectronics plc * * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> * @@ -16,7 +16,9 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> +#include <linux/of_device.h> #include <linux/spi/spi.h> +#include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> @@ -34,45 +36,34 @@ static const char *wm8804_supply_names[WM8804_NUM_SUPPLIES] = { "DVDD" }; -static const u8 wm8804_reg_defs[] = { - 0x05, /* R0 - RST/DEVID1 */ - 0x88, /* R1 - DEVID2 */ - 0x04, /* R2 - DEVREV */ - 0x21, /* R3 - PLL1 */ - 0xFD, /* R4 - PLL2 */ - 0x36, /* R5 - PLL3 */ - 0x07, /* R6 - PLL4 */ - 0x16, /* R7 - PLL5 */ - 0x18, /* R8 - PLL6 */ - 0xFF, /* R9 - SPDMODE */ - 0x00, /* R10 - INTMASK */ - 0x00, /* R11 - INTSTAT */ - 0x00, /* R12 - SPDSTAT */ - 0x00, /* R13 - RXCHAN1 */ - 0x00, /* R14 - RXCHAN2 */ - 0x00, /* R15 - RXCHAN3 */ - 0x00, /* R16 - RXCHAN4 */ - 0x00, /* R17 - RXCHAN5 */ - 0x00, /* R18 - SPDTX1 */ - 0x00, /* R19 - SPDTX2 */ - 0x00, /* R20 - SPDTX3 */ - 0x71, /* R21 - SPDTX4 */ - 0x0B, /* R22 - SPDTX5 */ - 0x70, /* R23 - GPO0 */ - 0x57, /* R24 - GPO1 */ - 0x00, /* R25 */ - 0x42, /* R26 - GPO2 */ - 0x06, /* R27 - AIFTX */ - 0x06, /* R28 - AIFRX */ - 0x80, /* R29 - SPDRX1 */ - 0x07, /* R30 - PWRDN */ +static const struct reg_default wm8804_reg_defaults[] = { + { 3, 0x21 }, /* R3 - PLL1 */ + { 4, 0xFD }, /* R4 - PLL2 */ + { 5, 0x36 }, /* R5 - PLL3 */ + { 6, 0x07 }, /* R6 - PLL4 */ + { 7, 0x16 }, /* R7 - PLL5 */ + { 8, 0x18 }, /* R8 - PLL6 */ + { 9, 0xFF }, /* R9 - SPDMODE */ + { 10, 0x00 }, /* R10 - INTMASK */ + { 18, 0x00 }, /* R18 - SPDTX1 */ + { 19, 0x00 }, /* R19 - SPDTX2 */ + { 20, 0x00 }, /* R20 - SPDTX3 */ + { 21, 0x71 }, /* R21 - SPDTX4 */ + { 22, 0x0B }, /* R22 - SPDTX5 */ + { 23, 0x70 }, /* R23 - GPO0 */ + { 24, 0x57 }, /* R24 - GPO1 */ + { 26, 0x42 }, /* R26 - GPO2 */ + { 27, 0x06 }, /* R27 - AIFTX */ + { 28, 0x06 }, /* R28 - AIFRX */ + { 29, 0x80 }, /* R29 - SPDRX1 */ + { 30, 0x07 }, /* R30 - PWRDN */ }; struct wm8804_priv { - enum snd_soc_control_type control_type; + struct regmap *regmap; struct regulator_bulk_data supplies[WM8804_NUM_SUPPLIES]; struct notifier_block disable_nb[WM8804_NUM_SUPPLIES]; - struct snd_soc_codec *codec; + int mclk_div; }; static int txsrc_get(struct snd_kcontrol *kcontrol, @@ -93,7 +84,7 @@ static int wm8804_regulator_event_##n(struct notifier_block *nb, \ struct wm8804_priv *wm8804 = container_of(nb, struct wm8804_priv, \ disable_nb[n]); \ if (event & REGULATOR_EVENT_DISABLE) { \ - wm8804->codec->cache_sync = 1; \ + regcache_mark_dirty(wm8804->regmap); \ } \ return 0; \ } @@ -102,7 +93,7 @@ WM8804_REGULATOR_EVENT(0) WM8804_REGULATOR_EVENT(1) static const char *txsrc_text[] = { "S/PDIF RX", "AIF" }; -static const SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); +static SOC_ENUM_SINGLE_EXT_DECL(txsrc, txsrc_text); static const struct snd_kcontrol_new wm8804_snd_controls[] = { SOC_ENUM_EXT("Input Source", txsrc, txsrc_get, txsrc_put), @@ -116,7 +107,7 @@ static int txsrc_get(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec; unsigned int src; - codec = snd_kcontrol_chip(kcontrol); + codec = snd_soc_kcontrol_codec(kcontrol); src = snd_soc_read(codec, WM8804_SPDTX4); if (src & 0x40) ucontrol->value.integer.value[0] = 1; @@ -132,7 +123,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, struct snd_soc_codec *codec; unsigned int src, txpwr; - codec = snd_kcontrol_chip(kcontrol); + codec = snd_soc_kcontrol_codec(kcontrol); if (ucontrol->value.integer.value[0] != 0 && ucontrol->value.integer.value[0] != 1) @@ -175,7 +166,7 @@ static int txsrc_put(struct snd_kcontrol *kcontrol, return 0; } -static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg) +static bool wm8804_volatile(struct device *dev, unsigned int reg) { switch (reg) { case WM8804_RST_DEVID1: @@ -188,12 +179,10 @@ static int wm8804_volatile(struct snd_soc_codec *codec, unsigned int reg) case WM8804_RXCHAN3: case WM8804_RXCHAN4: case WM8804_RXCHAN5: - return 1; + return true; default: - break; + return false; } - - return 0; } static int wm8804_reset(struct snd_soc_codec *codec) @@ -330,7 +319,7 @@ static struct { #define FIXED_PLL_SIZE ((1ULL << 22) * 10) static int pll_factors(struct pll_div *pll_div, unsigned int target, - unsigned int source) + unsigned int source, unsigned int mclk_div) { u64 Kpart; unsigned long int K, Ndiv, Nmod, tmp; @@ -342,7 +331,8 @@ static int pll_factors(struct pll_div *pll_div, unsigned int target, */ for (i = 0; i < ARRAY_SIZE(post_table); i++) { tmp = target * post_table[i].div; - if (tmp >= 90000000 && tmp <= 100000000) { + if ((tmp >= 90000000 && tmp <= 100000000) && + (mclk_div == post_table[i].mclkdiv)) { pll_div->freqmode = post_table[i].freqmode; pll_div->mclkdiv = post_table[i].mclkdiv; target *= post_table[i].div; @@ -399,17 +389,18 @@ static int wm8804_set_pll(struct snd_soc_dai *dai, int pll_id, } else { int ret; struct pll_div pll_div; + struct wm8804_priv *wm8804; + + wm8804 = snd_soc_codec_get_drvdata(codec); - ret = pll_factors(&pll_div, freq_out, freq_in); + ret = pll_factors(&pll_div, freq_out, freq_in, + wm8804->mclk_div); if (ret) return ret; /* power down the PLL before reprogramming it */ snd_soc_update_bits(codec, WM8804_PWRDN, 0x1, 0x1); - if (!freq_in || !freq_out) - return 0; - /* set PLLN and PRESCALE */ snd_soc_update_bits(codec, WM8804_PLL4, 0xf | 0x10, pll_div.n | (pll_div.prescale << 4)); @@ -467,6 +458,7 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai, int div_id, int div) { struct snd_soc_codec *codec; + struct wm8804_priv *wm8804; codec = dai->codec; switch (div_id) { @@ -474,6 +466,10 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai, snd_soc_update_bits(codec, WM8804_PLL5, 0x30, (div & 0x3) << 4); break; + case WM8804_MCLK_DIV: + wm8804 = snd_soc_codec_get_drvdata(codec); + wm8804->mclk_div = div; + break; default: dev_err(dai->dev, "Unknown clock divider: %d\n", div_id); return -EINVAL; @@ -481,24 +477,6 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai, return 0; } -static void wm8804_sync_cache(struct snd_soc_codec *codec) -{ - short i; - u8 *cache; - - if (!codec->cache_sync) - return; - - codec->cache_only = 0; - cache = codec->reg_cache; - for (i = 0; i < codec->driver->reg_cache_size; i++) { - if (i == WM8804_RST_DEVID1 || cache[i] == wm8804_reg_defs[i]) - continue; - snd_soc_write(codec, i, cache[i]); - } - codec->cache_sync = 0; -} - static int wm8804_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { @@ -523,7 +501,7 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec, ret); return ret; } - wm8804_sync_cache(codec); + regcache_sync(wm8804->regmap); } /* power down the OSC and the PLL */ snd_soc_update_bits(codec, WM8804_PWRDN, 0x9, 0x9); @@ -541,7 +519,7 @@ static int wm8804_set_bias_level(struct snd_soc_codec *codec, } #ifdef CONFIG_PM -static int wm8804_suspend(struct snd_soc_codec *codec, pm_message_t state) +static int wm8804_suspend(struct snd_soc_codec *codec) { wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; @@ -568,7 +546,6 @@ static int wm8804_remove(struct snd_soc_codec *codec) for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i) regulator_unregister_notifier(wm8804->supplies[i].consumer, &wm8804->disable_nb[i]); - regulator_bulk_free(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); return 0; } @@ -578,20 +555,11 @@ static int wm8804_probe(struct snd_soc_codec *codec) int i, id1, id2, ret; wm8804 = snd_soc_codec_get_drvdata(codec); - wm8804->codec = codec; - - codec->dapm.idle_bias_off = 1; - - ret = snd_soc_codec_set_cache_io(codec, 8, 8, wm8804->control_type); - if (ret < 0) { - dev_err(codec->dev, "Failed to set cache i/o: %d\n", ret); - return ret; - } for (i = 0; i < ARRAY_SIZE(wm8804->supplies); i++) wm8804->supplies[i].supply = wm8804_supply_names[i]; - ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies), + ret = devm_regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8804->supplies), wm8804->supplies); if (ret) { dev_err(codec->dev, "Failed to request supplies: %d\n", ret); @@ -616,7 +584,7 @@ static int wm8804_probe(struct snd_soc_codec *codec) wm8804->supplies); if (ret) { dev_err(codec->dev, "Failed to enable supplies: %d\n", ret); - goto err_reg_get; + return ret; } id1 = snd_soc_read(codec, WM8804_RST_DEVID1); @@ -635,8 +603,7 @@ static int wm8804_probe(struct snd_soc_codec *codec) id2 = (id2 << 8) | id1; - if (id2 != ((wm8804_reg_defs[WM8804_DEVID2] << 8) - | wm8804_reg_defs[WM8804_RST_DEVID1])) { + if (id2 != 0x8805) { dev_err(codec->dev, "Invalid device ID: %#x\n", id2); ret = -EINVAL; goto err_reg_enable; @@ -658,18 +625,14 @@ static int wm8804_probe(struct snd_soc_codec *codec) wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_controls(codec, wm8804_snd_controls, - ARRAY_SIZE(wm8804_snd_controls)); return 0; err_reg_enable: regulator_bulk_disable(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); -err_reg_get: - regulator_bulk_free(ARRAY_SIZE(wm8804->supplies), wm8804->supplies); return ret; } -static struct snd_soc_dai_ops wm8804_dai_ops = { +static const struct snd_soc_dai_ops wm8804_dai_ops = { .hw_params = wm8804_hw_params, .set_fmt = wm8804_set_fmt, .set_sysclk = wm8804_set_sysclk, @@ -680,20 +643,25 @@ static struct snd_soc_dai_ops wm8804_dai_ops = { #define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE) +#define WM8804_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + static struct snd_soc_dai_driver wm8804_dai = { .name = "wm8804-spdif", .playback = { .stream_name = "Playback", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = WM8804_RATES, .formats = WM8804_FORMATS, }, .capture = { .stream_name = "Capture", .channels_min = 2, .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, + .rates = WM8804_RATES, .formats = WM8804_FORMATS, }, .ops = &wm8804_dai_ops, @@ -706,36 +674,57 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8804 = { .suspend = wm8804_suspend, .resume = wm8804_resume, .set_bias_level = wm8804_set_bias_level, - .reg_cache_size = ARRAY_SIZE(wm8804_reg_defs), - .reg_word_size = sizeof(u8), - .reg_cache_default = wm8804_reg_defs, - .volatile_register = wm8804_volatile + .idle_bias_off = true, + + .controls = wm8804_snd_controls, + .num_controls = ARRAY_SIZE(wm8804_snd_controls), +}; + +static const struct of_device_id wm8804_of_match[] = { + { .compatible = "wlf,wm8804", }, + { } +}; +MODULE_DEVICE_TABLE(of, wm8804_of_match); + +static struct regmap_config wm8804_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = WM8804_MAX_REGISTER, + .volatile_reg = wm8804_volatile, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = wm8804_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8804_reg_defaults), }; #if defined(CONFIG_SPI_MASTER) -static int __devinit wm8804_spi_probe(struct spi_device *spi) +static int wm8804_spi_probe(struct spi_device *spi) { struct wm8804_priv *wm8804; int ret; - wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL); + wm8804 = devm_kzalloc(&spi->dev, sizeof *wm8804, GFP_KERNEL); if (!wm8804) return -ENOMEM; - wm8804->control_type = SND_SOC_SPI; + wm8804->regmap = devm_regmap_init_spi(spi, &wm8804_regmap_config); + if (IS_ERR(wm8804->regmap)) { + ret = PTR_ERR(wm8804->regmap); + return ret; + } + spi_set_drvdata(spi, wm8804); ret = snd_soc_register_codec(&spi->dev, &soc_codec_dev_wm8804, &wm8804_dai, 1); - if (ret < 0) - kfree(wm8804); + return ret; } -static int __devexit wm8804_spi_remove(struct spi_device *spi) +static int wm8804_spi_remove(struct spi_device *spi) { snd_soc_unregister_codec(&spi->dev); - kfree(spi_get_drvdata(spi)); return 0; } @@ -743,37 +732,40 @@ static struct spi_driver wm8804_spi_driver = { .driver = { .name = "wm8804", .owner = THIS_MODULE, + .of_match_table = wm8804_of_match, }, .probe = wm8804_spi_probe, - .remove = __devexit_p(wm8804_spi_remove) + .remove = wm8804_spi_remove }; #endif -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) -static __devinit int wm8804_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +#if IS_ENABLED(CONFIG_I2C) +static int wm8804_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { struct wm8804_priv *wm8804; int ret; - wm8804 = kzalloc(sizeof *wm8804, GFP_KERNEL); + wm8804 = devm_kzalloc(&i2c->dev, sizeof *wm8804, GFP_KERNEL); if (!wm8804) return -ENOMEM; - wm8804->control_type = SND_SOC_I2C; + wm8804->regmap = devm_regmap_init_i2c(i2c, &wm8804_regmap_config); + if (IS_ERR(wm8804->regmap)) { + ret = PTR_ERR(wm8804->regmap); + return ret; + } + i2c_set_clientdata(i2c, wm8804); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8804, &wm8804_dai, 1); - if (ret < 0) - kfree(wm8804); return ret; } -static __devexit int wm8804_i2c_remove(struct i2c_client *client) +static int wm8804_i2c_remove(struct i2c_client *i2c) { - snd_soc_unregister_codec(&client->dev); - kfree(i2c_get_clientdata(client)); + snd_soc_unregister_codec(&i2c->dev); return 0; } @@ -787,9 +779,10 @@ static struct i2c_driver wm8804_i2c_driver = { .driver = { .name = "wm8804", .owner = THIS_MODULE, + .of_match_table = wm8804_of_match, }, .probe = wm8804_i2c_probe, - .remove = __devexit_p(wm8804_i2c_remove), + .remove = wm8804_i2c_remove, .id_table = wm8804_i2c_id }; #endif @@ -798,7 +791,7 @@ static int __init wm8804_modinit(void) { int ret = 0; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) ret = i2c_add_driver(&wm8804_i2c_driver); if (ret) { printk(KERN_ERR "Failed to register wm8804 I2C driver: %d\n", @@ -818,7 +811,7 @@ module_init(wm8804_modinit); static void __exit wm8804_exit(void) { -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +#if IS_ENABLED(CONFIG_I2C) i2c_del_driver(&wm8804_i2c_driver); #endif #if defined(CONFIG_SPI_MASTER) |
