diff options
Diffstat (limited to 'sound/soc/codecs/wm8510.c')
| -rw-r--r-- | sound/soc/codecs/wm8510.c | 437 |
1 files changed, 190 insertions, 247 deletions
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index af8cb6995a1..1c1e328feeb 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -17,49 +17,102 @@ #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> -#include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/slab.h> +#include <linux/of_device.h> +#include <linux/regmap.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/initval.h> #include "wm8510.h" -#define WM8510_VERSION "0.6" - -struct snd_soc_codec_device soc_codec_dev_wm8510; - /* * wm8510 register cache * We can't read the WM8510 register space when we are * using 2 wire for device control, so we cache them instead. */ -static const u16 wm8510_reg[WM8510_CACHEREGNUM] = { - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0050, 0x0000, 0x0140, 0x0000, - 0x0000, 0x0000, 0x0000, 0x00ff, - 0x0000, 0x0000, 0x0100, 0x00ff, - 0x0000, 0x0000, 0x012c, 0x002c, - 0x002c, 0x002c, 0x002c, 0x0000, - 0x0032, 0x0000, 0x0000, 0x0000, - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0038, 0x000b, 0x0032, 0x0000, - 0x0008, 0x000c, 0x0093, 0x00e9, - 0x0000, 0x0000, 0x0000, 0x0000, - 0x0003, 0x0010, 0x0000, 0x0000, - 0x0000, 0x0002, 0x0001, 0x0000, - 0x0000, 0x0000, 0x0039, 0x0000, - 0x0001, +static const struct reg_default wm8510_reg_defaults[] = { + { 1, 0x0000 }, + { 2, 0x0000 }, + { 3, 0x0000 }, + { 4, 0x0050 }, + { 5, 0x0000 }, + { 6, 0x0140 }, + { 7, 0x0000 }, + { 8, 0x0000 }, + { 9, 0x0000 }, + { 10, 0x0000 }, + { 11, 0x00ff }, + { 12, 0x0000 }, + { 13, 0x0000 }, + { 14, 0x0100 }, + { 15, 0x00ff }, + { 16, 0x0000 }, + { 17, 0x0000 }, + { 18, 0x012c }, + { 19, 0x002c }, + { 20, 0x002c }, + { 21, 0x002c }, + { 22, 0x002c }, + { 23, 0x0000 }, + { 24, 0x0032 }, + { 25, 0x0000 }, + { 26, 0x0000 }, + { 27, 0x0000 }, + { 28, 0x0000 }, + { 29, 0x0000 }, + { 30, 0x0000 }, + { 31, 0x0000 }, + { 32, 0x0038 }, + { 33, 0x000b }, + { 34, 0x0032 }, + { 35, 0x0000 }, + { 36, 0x0008 }, + { 37, 0x000c }, + { 38, 0x0093 }, + { 39, 0x00e9 }, + { 40, 0x0000 }, + { 41, 0x0000 }, + { 42, 0x0000 }, + { 43, 0x0000 }, + { 44, 0x0003 }, + { 45, 0x0010 }, + { 46, 0x0000 }, + { 47, 0x0000 }, + { 48, 0x0000 }, + { 49, 0x0002 }, + { 50, 0x0001 }, + { 51, 0x0000 }, + { 52, 0x0000 }, + { 53, 0x0000 }, + { 54, 0x0039 }, + { 55, 0x0000 }, + { 56, 0x0001 }, }; +static bool wm8510_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WM8510_RESET: + return true; + default: + return false; + } +} + #define WM8510_POWER1_BIASEN 0x08 #define WM8510_POWER1_BUFIOEN 0x10 #define wm8510_reset(c) snd_soc_write(c, WM8510_RESET, 0) +/* codec private data */ +struct wm8510_priv { + struct regmap *regmap; +}; + static const char *wm8510_companding[] = { "Off", "NC", "u-law", "A-law" }; static const char *wm8510_deemp[] = { "None", "32kHz", "44.1kHz", "48kHz" }; static const char *wm8510_alc[] = { "ALC", "Limiter" }; @@ -180,7 +233,7 @@ SND_SOC_DAPM_OUTPUT("SPKOUTP"), SND_SOC_DAPM_OUTPUT("SPKOUTN"), }; -static const struct snd_soc_dapm_route audio_map[] = { +static const struct snd_soc_dapm_route wm8510_dapm_routes[] = { /* Mono output mixer */ {"Mono Mixer", "PCM Playback Switch", "DAC"}, {"Mono Mixer", "Aux Playback Switch", "Aux Input"}, @@ -212,16 +265,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"ADC", NULL, "Boost Mixer"}, }; -static int wm8510_add_widgets(struct snd_soc_codec *codec) -{ - snd_soc_dapm_new_controls(codec, wm8510_dapm_widgets, - ARRAY_SIZE(wm8510_dapm_widgets)); - - snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - - return 0; -} - struct pll_ { unsigned int pre_div:4; /* prescale - 1 */ unsigned int n:4; @@ -401,9 +444,7 @@ static int wm8510_pcm_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; + struct snd_soc_codec *codec = dai->codec; u16 iface = snd_soc_read(codec, WM8510_IFACE) & 0x19f; u16 adn = snd_soc_read(codec, WM8510_ADD) & 0x1f1; @@ -465,6 +506,7 @@ static int wm8510_mute(struct snd_soc_dai *dai, int mute) static int wm8510_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { + struct wm8510_priv *wm8510 = snd_soc_codec_get_drvdata(codec); u16 power1 = snd_soc_read(codec, WM8510_POWER1) & ~0x3; switch (level) { @@ -477,7 +519,9 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, case SND_SOC_BIAS_STANDBY: power1 |= WM8510_POWER1_BIASEN | WM8510_POWER1_BUFIOEN; - if (codec->bias_level == SND_SOC_BIAS_OFF) { + if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { + regcache_sync(wm8510->regmap); + /* Initial cap charge at VMID 5k */ snd_soc_write(codec, WM8510_POWER1, power1 | 0x3); mdelay(100); @@ -494,7 +538,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, break; } - codec->bias_level = level; + codec->dapm.bias_level = level; return 0; } @@ -505,7 +549,7 @@ static int wm8510_set_bias_level(struct snd_soc_codec *codec, #define WM8510_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 wm8510_dai_ops = { +static const struct snd_soc_dai_ops wm8510_dai_ops = { .hw_params = wm8510_pcm_hw_params, .digital_mute = wm8510_mute, .set_fmt = wm8510_set_dai_fmt, @@ -513,8 +557,8 @@ static struct snd_soc_dai_ops wm8510_dai_ops = { .set_pll = wm8510_set_dai_pll, }; -struct snd_soc_dai wm8510_dai = { - .name = "WM8510 HiFi", +static struct snd_soc_dai_driver wm8510_dai = { + .name = "wm8510-hifi", .playback = { .stream_name = "Playback", .channels_min = 2, @@ -530,286 +574,185 @@ struct snd_soc_dai wm8510_dai = { .ops = &wm8510_dai_ops, .symmetric_rates = 1, }; -EXPORT_SYMBOL_GPL(wm8510_dai); -static int wm8510_suspend(struct platform_device *pdev, pm_message_t state) +static int wm8510_suspend(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } -static int wm8510_resume(struct platform_device *pdev) +static int wm8510_resume(struct snd_soc_codec *codec) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - int i; - u8 data[2]; - u16 *cache = codec->reg_cache; - - /* Sync reg_cache with the hardware */ - for (i = 0; i < ARRAY_SIZE(wm8510_reg); i++) { - data[0] = (i << 1) | ((cache[i] >> 8) & 0x0001); - data[1] = cache[i] & 0x00ff; - codec->hw_write(codec->control_data, data, 2); - } wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - wm8510_set_bias_level(codec, codec->suspend_bias_level); return 0; } -/* - * initialise the WM8510 driver - * register the mixer and dsp interfaces with the kernel - */ -static int wm8510_init(struct snd_soc_device *socdev, - enum snd_soc_control_type control) +static int wm8510_probe(struct snd_soc_codec *codec) { - struct snd_soc_codec *codec = socdev->card->codec; - int ret = 0; - - codec->name = "WM8510"; - codec->owner = THIS_MODULE; - codec->set_bias_level = wm8510_set_bias_level; - codec->dai = &wm8510_dai; - codec->num_dai = 1; - codec->reg_cache_size = ARRAY_SIZE(wm8510_reg); - codec->reg_cache = kmemdup(wm8510_reg, sizeof(wm8510_reg), GFP_KERNEL); - - if (codec->reg_cache == NULL) - return -ENOMEM; - - ret = snd_soc_codec_set_cache_io(codec, 7, 9, control); - if (ret < 0) { - printk(KERN_ERR "wm8510: failed to set cache I/O: %d\n", - ret); - goto err; - } - wm8510_reset(codec); - /* register pcms */ - ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); - if (ret < 0) { - printk(KERN_ERR "wm8510: failed to create pcms\n"); - goto err; - } - /* power on device */ - codec->bias_level = SND_SOC_BIAS_OFF; wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY); - snd_soc_add_controls(codec, wm8510_snd_controls, - ARRAY_SIZE(wm8510_snd_controls)); - wm8510_add_widgets(codec); - return ret; - -err: - kfree(codec->reg_cache); - return ret; -} - -static struct snd_soc_device *wm8510_socdev; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - -/* - * WM8510 2 wire address is 0x1a - */ - -static int wm8510_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct snd_soc_device *socdev = wm8510_socdev; - struct snd_soc_codec *codec = socdev->card->codec; - int ret; - - i2c_set_clientdata(i2c, codec); - codec->control_data = i2c; - - ret = wm8510_init(socdev, SND_SOC_I2C); - if (ret < 0) - pr_err("failed to initialise WM8510\n"); - - return ret; + return 0; } -static int wm8510_i2c_remove(struct i2c_client *client) +/* power down chip */ +static int wm8510_remove(struct snd_soc_codec *codec) { - struct snd_soc_codec *codec = i2c_get_clientdata(client); - kfree(codec->reg_cache); + wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } -static const struct i2c_device_id wm8510_i2c_id[] = { - { "wm8510", 0 }, - { } +static struct snd_soc_codec_driver soc_codec_dev_wm8510 = { + .probe = wm8510_probe, + .remove = wm8510_remove, + .suspend = wm8510_suspend, + .resume = wm8510_resume, + .set_bias_level = wm8510_set_bias_level, + + .controls = wm8510_snd_controls, + .num_controls = ARRAY_SIZE(wm8510_snd_controls), + .dapm_widgets = wm8510_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets), + .dapm_routes = wm8510_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes), }; -MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); -static struct i2c_driver wm8510_i2c_driver = { - .driver = { - .name = "WM8510 I2C Codec", - .owner = THIS_MODULE, - }, - .probe = wm8510_i2c_probe, - .remove = wm8510_i2c_remove, - .id_table = wm8510_i2c_id, +static const struct of_device_id wm8510_of_match[] = { + { .compatible = "wlf,wm8510" }, + { }, }; -static int wm8510_add_i2c_device(struct platform_device *pdev, - const struct wm8510_setup_data *setup) -{ - struct i2c_board_info info; - struct i2c_adapter *adapter; - struct i2c_client *client; - int ret; +static const struct regmap_config wm8510_regmap = { + .reg_bits = 7, + .val_bits = 9, + .max_register = WM8510_MONOMIX, - ret = i2c_add_driver(&wm8510_i2c_driver); - if (ret != 0) { - dev_err(&pdev->dev, "can't add i2c driver\n"); - return ret; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = setup->i2c_address; - strlcpy(info.type, "wm8510", I2C_NAME_SIZE); + .reg_defaults = wm8510_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(wm8510_reg_defaults), + .cache_type = REGCACHE_RBTREE, - adapter = i2c_get_adapter(setup->i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "can't get i2c adapter %d\n", - setup->i2c_bus); - goto err_driver; - } - - client = i2c_new_device(adapter, &info); - i2c_put_adapter(adapter); - if (!client) { - dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", - (unsigned int)info.addr); - goto err_driver; - } - - return 0; - -err_driver: - i2c_del_driver(&wm8510_i2c_driver); - return -ENODEV; -} -#endif + .volatile_reg = wm8510_volatile, +}; #if defined(CONFIG_SPI_MASTER) -static int __devinit wm8510_spi_probe(struct spi_device *spi) +static int wm8510_spi_probe(struct spi_device *spi) { - struct snd_soc_device *socdev = wm8510_socdev; - struct snd_soc_codec *codec = socdev->card->codec; + struct wm8510_priv *wm8510; int ret; - codec->control_data = spi; + wm8510 = devm_kzalloc(&spi->dev, sizeof(struct wm8510_priv), + GFP_KERNEL); + if (wm8510 == NULL) + return -ENOMEM; + + wm8510->regmap = devm_regmap_init_spi(spi, &wm8510_regmap); + if (IS_ERR(wm8510->regmap)) + return PTR_ERR(wm8510->regmap); + + spi_set_drvdata(spi, wm8510); - ret = wm8510_init(socdev, SND_SOC_SPI); - if (ret < 0) - dev_err(&spi->dev, "failed to initialise WM8510\n"); + ret = snd_soc_register_codec(&spi->dev, + &soc_codec_dev_wm8510, &wm8510_dai, 1); return ret; } -static int __devexit wm8510_spi_remove(struct spi_device *spi) +static int wm8510_spi_remove(struct spi_device *spi) { + snd_soc_unregister_codec(&spi->dev); return 0; } static struct spi_driver wm8510_spi_driver = { .driver = { .name = "wm8510", - .bus = &spi_bus_type, .owner = THIS_MODULE, + .of_match_table = wm8510_of_match, }, .probe = wm8510_spi_probe, - .remove = __devexit_p(wm8510_spi_remove), + .remove = wm8510_spi_remove, }; #endif /* CONFIG_SPI_MASTER */ -static int wm8510_probe(struct platform_device *pdev) +#if IS_ENABLED(CONFIG_I2C) +static int wm8510_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct wm8510_setup_data *setup; - struct snd_soc_codec *codec; - int ret = 0; - - pr_info("WM8510 Audio Codec %s", WM8510_VERSION); + struct wm8510_priv *wm8510; + int ret; - setup = socdev->codec_data; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) + wm8510 = devm_kzalloc(&i2c->dev, sizeof(struct wm8510_priv), + GFP_KERNEL); + if (wm8510 == NULL) return -ENOMEM; - socdev->card->codec = codec; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); + wm8510->regmap = devm_regmap_init_i2c(i2c, &wm8510_regmap); + if (IS_ERR(wm8510->regmap)) + return PTR_ERR(wm8510->regmap); - wm8510_socdev = socdev; -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - if (setup->i2c_address) { - ret = wm8510_add_i2c_device(pdev, setup); - } -#endif -#if defined(CONFIG_SPI_MASTER) - if (setup->spi) { - ret = spi_register_driver(&wm8510_spi_driver); - if (ret != 0) - printk(KERN_ERR "can't add spi driver"); - } -#endif + i2c_set_clientdata(i2c, wm8510); + + ret = snd_soc_register_codec(&i2c->dev, + &soc_codec_dev_wm8510, &wm8510_dai, 1); - if (ret != 0) - kfree(codec); return ret; } -/* power down chip */ -static int wm8510_remove(struct platform_device *pdev) +static int wm8510_i2c_remove(struct i2c_client *client) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - - if (codec->control_data) - wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF); - - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_unregister_device(codec->control_data); - i2c_del_driver(&wm8510_i2c_driver); -#endif -#if defined(CONFIG_SPI_MASTER) - spi_unregister_driver(&wm8510_spi_driver); -#endif - kfree(codec); - + snd_soc_unregister_codec(&client->dev); return 0; } -struct snd_soc_codec_device soc_codec_dev_wm8510 = { - .probe = wm8510_probe, - .remove = wm8510_remove, - .suspend = wm8510_suspend, - .resume = wm8510_resume, +static const struct i2c_device_id wm8510_i2c_id[] = { + { "wm8510", 0 }, + { } }; -EXPORT_SYMBOL_GPL(soc_codec_dev_wm8510); +MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); + +static struct i2c_driver wm8510_i2c_driver = { + .driver = { + .name = "wm8510", + .owner = THIS_MODULE, + .of_match_table = wm8510_of_match, + }, + .probe = wm8510_i2c_probe, + .remove = wm8510_i2c_remove, + .id_table = wm8510_i2c_id, +}; +#endif static int __init wm8510_modinit(void) { - return snd_soc_register_dai(&wm8510_dai); + int ret = 0; +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&wm8510_i2c_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8510 I2C driver: %d\n", + ret); + } +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&wm8510_spi_driver); + if (ret != 0) { + printk(KERN_ERR "Failed to register WM8510 SPI driver: %d\n", + ret); + } +#endif + return ret; } module_init(wm8510_modinit); static void __exit wm8510_exit(void) { - snd_soc_unregister_dai(&wm8510_dai); +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&wm8510_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&wm8510_spi_driver); +#endif } module_exit(wm8510_exit); |
