diff options
| -rw-r--r-- | Documentation/devicetree/bindings/sound/wm8524.txt | 16 | ||||
| -rw-r--r-- | sound/soc/codecs/Kconfig | 5 | ||||
| -rw-r--r-- | sound/soc/codecs/Makefile | 2 | ||||
| -rw-r--r-- | sound/soc/codecs/wm8524.c | 265 |
4 files changed, 288 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/sound/wm8524.txt b/Documentation/devicetree/bindings/sound/wm8524.txt new file mode 100644 index 000000000000..20c62002cbcd --- /dev/null +++ b/Documentation/devicetree/bindings/sound/wm8524.txt | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | WM8524 audio CODEC | ||
| 2 | |||
| 3 | This device does not use I2C or SPI but a simple Hardware Control Interface. | ||
| 4 | |||
| 5 | Required properties: | ||
| 6 | |||
| 7 | - compatible : "wlf,wm8524" | ||
| 8 | |||
| 9 | - wlf,mute-gpios: a GPIO spec for the MUTE pin. | ||
| 10 | |||
| 11 | Example: | ||
| 12 | |||
| 13 | codec: wm8524@0 { | ||
| 14 | compatible = "wlf,wm8524"; | ||
| 15 | wlf,mute-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>; | ||
| 16 | }; | ||
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 6c78b0b49b81..c3d8aef98608 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig | |||
| @@ -173,6 +173,7 @@ config SND_SOC_ALL_CODECS | |||
| 173 | select SND_SOC_WM8400 if MFD_WM8400 | 173 | select SND_SOC_WM8400 if MFD_WM8400 |
| 174 | select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI | 174 | select SND_SOC_WM8510 if SND_SOC_I2C_AND_SPI |
| 175 | select SND_SOC_WM8523 if I2C | 175 | select SND_SOC_WM8523 if I2C |
| 176 | select SND_SOC_WM8524 if GPIOLIB | ||
| 176 | select SND_SOC_WM8580 if I2C | 177 | select SND_SOC_WM8580 if I2C |
| 177 | select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI | 178 | select SND_SOC_WM8711 if SND_SOC_I2C_AND_SPI |
| 178 | select SND_SOC_WM8727 | 179 | select SND_SOC_WM8727 |
| @@ -967,6 +968,10 @@ config SND_SOC_WM8523 | |||
| 967 | tristate "Wolfson Microelectronics WM8523 DAC" | 968 | tristate "Wolfson Microelectronics WM8523 DAC" |
| 968 | depends on I2C | 969 | depends on I2C |
| 969 | 970 | ||
| 971 | config SND_SOC_WM8524 | ||
| 972 | tristate "Wolfson Microelectronics WM8524 DAC" | ||
| 973 | depends on GPIOLIB | ||
| 974 | |||
| 970 | config SND_SOC_WM8580 | 975 | config SND_SOC_WM8580 |
| 971 | tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs" | 976 | tristate "Wolfson Microelectronics WM8580 and WM8581 CODECs" |
| 972 | depends on I2C | 977 | depends on I2C |
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 1755a54e3dc9..66a8567d76d1 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile | |||
| @@ -182,6 +182,7 @@ snd-soc-wm8350-objs := wm8350.o | |||
| 182 | snd-soc-wm8400-objs := wm8400.o | 182 | snd-soc-wm8400-objs := wm8400.o |
| 183 | snd-soc-wm8510-objs := wm8510.o | 183 | snd-soc-wm8510-objs := wm8510.o |
| 184 | snd-soc-wm8523-objs := wm8523.o | 184 | snd-soc-wm8523-objs := wm8523.o |
| 185 | snd-soc-wm8524-objs := wm8524.o | ||
| 185 | snd-soc-wm8580-objs := wm8580.o | 186 | snd-soc-wm8580-objs := wm8580.o |
| 186 | snd-soc-wm8711-objs := wm8711.o | 187 | snd-soc-wm8711-objs := wm8711.o |
| 187 | snd-soc-wm8727-objs := wm8727.o | 188 | snd-soc-wm8727-objs := wm8727.o |
| @@ -414,6 +415,7 @@ obj-$(CONFIG_SND_SOC_WM8350) += snd-soc-wm8350.o | |||
| 414 | obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o | 415 | obj-$(CONFIG_SND_SOC_WM8400) += snd-soc-wm8400.o |
| 415 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o | 416 | obj-$(CONFIG_SND_SOC_WM8510) += snd-soc-wm8510.o |
| 416 | obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o | 417 | obj-$(CONFIG_SND_SOC_WM8523) += snd-soc-wm8523.o |
| 418 | obj-$(CONFIG_SND_SOC_WM8524) += snd-soc-wm8524.o | ||
| 417 | obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o | 419 | obj-$(CONFIG_SND_SOC_WM8580) += snd-soc-wm8580.o |
| 418 | obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o | 420 | obj-$(CONFIG_SND_SOC_WM8711) += snd-soc-wm8711.o |
| 419 | obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o | 421 | obj-$(CONFIG_SND_SOC_WM8727) += snd-soc-wm8727.o |
diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c new file mode 100644 index 000000000000..f42ec664877c --- /dev/null +++ b/sound/soc/codecs/wm8524.c | |||
| @@ -0,0 +1,265 @@ | |||
| 1 | /* | ||
| 2 | * wm8524.c -- WM8524 ALSA SoC Audio driver | ||
| 3 | * | ||
| 4 | * Copyright 2009 Wolfson Microelectronics plc | ||
| 5 | * Copyright 2017 NXP | ||
| 6 | * | ||
| 7 | * Based on WM8523 ALSA SoC Audio driver written by Mark Brown | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | */ | ||
| 13 | |||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/moduleparam.h> | ||
| 16 | #include <linux/init.h> | ||
| 17 | #include <linux/delay.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/gpio/consumer.h> | ||
| 20 | #include <linux/of_device.h> | ||
| 21 | #include <sound/core.h> | ||
| 22 | #include <sound/pcm.h> | ||
| 23 | #include <sound/pcm_params.h> | ||
| 24 | #include <sound/soc.h> | ||
| 25 | #include <sound/initval.h> | ||
| 26 | |||
| 27 | #define WM8524_NUM_RATES 7 | ||
| 28 | |||
| 29 | /* codec private data */ | ||
| 30 | struct wm8524_priv { | ||
| 31 | struct gpio_desc *mute; | ||
| 32 | unsigned int sysclk; | ||
| 33 | unsigned int rate_constraint_list[WM8524_NUM_RATES]; | ||
| 34 | struct snd_pcm_hw_constraint_list rate_constraint; | ||
| 35 | }; | ||
| 36 | |||
| 37 | |||
| 38 | static const struct snd_soc_dapm_widget wm8524_dapm_widgets[] = { | ||
| 39 | SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0), | ||
| 40 | SND_SOC_DAPM_OUTPUT("LINEVOUTL"), | ||
| 41 | SND_SOC_DAPM_OUTPUT("LINEVOUTR"), | ||
| 42 | }; | ||
| 43 | |||
| 44 | static const struct snd_soc_dapm_route wm8524_dapm_routes[] = { | ||
| 45 | { "LINEVOUTL", NULL, "DAC" }, | ||
| 46 | { "LINEVOUTR", NULL, "DAC" }, | ||
| 47 | }; | ||
| 48 | |||
| 49 | static const struct { | ||
| 50 | int value; | ||
| 51 | int ratio; | ||
| 52 | } lrclk_ratios[WM8524_NUM_RATES] = { | ||
| 53 | { 1, 128 }, | ||
| 54 | { 2, 192 }, | ||
| 55 | { 3, 256 }, | ||
| 56 | { 4, 384 }, | ||
| 57 | { 5, 512 }, | ||
| 58 | { 6, 768 }, | ||
| 59 | { 7, 1152 }, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static int wm8524_startup(struct snd_pcm_substream *substream, | ||
| 63 | struct snd_soc_dai *dai) | ||
| 64 | { | ||
| 65 | struct snd_soc_codec *codec = dai->codec; | ||
| 66 | struct wm8524_priv *wm8524 = snd_soc_codec_get_drvdata(codec); | ||
| 67 | |||
| 68 | /* The set of sample rates that can be supported depends on the | ||
| 69 | * MCLK supplied to the CODEC - enforce this. | ||
| 70 | */ | ||
| 71 | if (!wm8524->sysclk) { | ||
| 72 | dev_err(codec->dev, | ||
| 73 | "No MCLK configured, call set_sysclk() on init\n"); | ||
| 74 | return -EINVAL; | ||
| 75 | } | ||
| 76 | |||
| 77 | snd_pcm_hw_constraint_list(substream->runtime, 0, | ||
| 78 | SNDRV_PCM_HW_PARAM_RATE, | ||
| 79 | &wm8524->rate_constraint); | ||
| 80 | |||
| 81 | gpiod_set_value_cansleep(wm8524->mute, 1); | ||
| 82 | |||
| 83 | return 0; | ||
| 84 | } | ||
| 85 | |||
| 86 | static void wm8524_shutdown(struct snd_pcm_substream *substream, | ||
| 87 | struct snd_soc_dai *dai) | ||
| 88 | { | ||
| 89 | struct snd_soc_codec *codec = dai->codec; | ||
| 90 | struct wm8524_priv *wm8524 = snd_soc_codec_get_drvdata(codec); | ||
| 91 | |||
| 92 | gpiod_set_value_cansleep(wm8524->mute, 0); | ||
| 93 | } | ||
| 94 | |||
| 95 | static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai, | ||
| 96 | int clk_id, unsigned int freq, int dir) | ||
| 97 | { | ||
| 98 | struct snd_soc_codec *codec = codec_dai->codec; | ||
| 99 | struct wm8524_priv *wm8524 = snd_soc_codec_get_drvdata(codec); | ||
| 100 | unsigned int val; | ||
| 101 | int i, j = 0; | ||
| 102 | |||
| 103 | wm8524->sysclk = freq; | ||
| 104 | |||
| 105 | wm8524->rate_constraint.count = 0; | ||
| 106 | for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) { | ||
| 107 | val = freq / lrclk_ratios[i].ratio; | ||
| 108 | /* Check that it's a standard rate since core can't | ||
| 109 | * cope with others and having the odd rates confuses | ||
| 110 | * constraint matching. | ||
| 111 | */ | ||
| 112 | switch (val) { | ||
| 113 | case 8000: | ||
| 114 | case 32000: | ||
| 115 | case 44100: | ||
| 116 | case 48000: | ||
| 117 | case 88200: | ||
| 118 | case 96000: | ||
| 119 | case 176400: | ||
| 120 | case 192000: | ||
| 121 | dev_err(codec->dev, "Supported sample rate: %dHz\n", | ||
| 122 | val); | ||
| 123 | wm8524->rate_constraint_list[j++] = val; | ||
| 124 | wm8524->rate_constraint.count++; | ||
| 125 | break; | ||
| 126 | default: | ||
| 127 | dev_dbg(codec->dev, "Skipping sample rate: %dHz\n", | ||
| 128 | val); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | /* Need at least one supported rate... */ | ||
| 133 | if (wm8524->rate_constraint.count == 0) | ||
| 134 | return -EINVAL; | ||
| 135 | |||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int wm8524_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) | ||
| 140 | { | ||
| 141 | fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK | | ||
| 142 | SND_SOC_DAIFMT_MASTER_MASK); | ||
| 143 | |||
| 144 | if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | ||
| 145 | SND_SOC_DAIFMT_CBS_CFS)) { | ||
| 146 | dev_err(codec_dai->dev, "Invalid DAI format\n"); | ||
| 147 | return -EINVAL; | ||
| 148 | } | ||
| 149 | |||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream) | ||
| 154 | { | ||
| 155 | struct wm8524_priv *wm8524 = snd_soc_codec_get_drvdata(dai->codec); | ||
| 156 | |||
| 157 | if (wm8524->mute) | ||
| 158 | gpiod_set_value_cansleep(wm8524->mute, mute); | ||
| 159 | |||
| 160 | return 0; | ||
| 161 | } | ||
| 162 | |||
| 163 | #define WM8524_RATES SNDRV_PCM_RATE_8000_192000 | ||
| 164 | |||
| 165 | #define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) | ||
| 166 | |||
| 167 | static const struct snd_soc_dai_ops wm8524_dai_ops = { | ||
| 168 | .startup = wm8524_startup, | ||
| 169 | .shutdown = wm8524_shutdown, | ||
| 170 | .set_sysclk = wm8524_set_dai_sysclk, | ||
| 171 | .set_fmt = wm8524_set_fmt, | ||
| 172 | .mute_stream = wm8524_mute_stream, | ||
| 173 | }; | ||
| 174 | |||
| 175 | static struct snd_soc_dai_driver wm8524_dai = { | ||
| 176 | .name = "wm8524-hifi", | ||
| 177 | .playback = { | ||
| 178 | .stream_name = "Playback", | ||
| 179 | .channels_min = 2, | ||
| 180 | .channels_max = 2, | ||
| 181 | .rates = WM8524_RATES, | ||
| 182 | .formats = WM8524_FORMATS, | ||
| 183 | }, | ||
| 184 | .ops = &wm8524_dai_ops, | ||
| 185 | }; | ||
| 186 | |||
| 187 | static int wm8524_probe(struct snd_soc_codec *codec) | ||
| 188 | { | ||
| 189 | struct wm8524_priv *wm8524 = snd_soc_codec_get_drvdata(codec); | ||
| 190 | |||
| 191 | wm8524->rate_constraint.list = &wm8524->rate_constraint_list[0]; | ||
| 192 | wm8524->rate_constraint.count = | ||
| 193 | ARRAY_SIZE(wm8524->rate_constraint_list); | ||
| 194 | |||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | static const struct snd_soc_codec_driver soc_codec_dev_wm8524 = { | ||
| 199 | .probe = wm8524_probe, | ||
| 200 | |||
| 201 | .component_driver = { | ||
| 202 | .dapm_widgets = wm8524_dapm_widgets, | ||
| 203 | .num_dapm_widgets = ARRAY_SIZE(wm8524_dapm_widgets), | ||
| 204 | .dapm_routes = wm8524_dapm_routes, | ||
| 205 | .num_dapm_routes = ARRAY_SIZE(wm8524_dapm_routes), | ||
| 206 | }, | ||
| 207 | }; | ||
| 208 | |||
| 209 | #ifdef CONFIG_OF | ||
| 210 | static const struct of_device_id wm8524_of_match[] = { | ||
| 211 | { .compatible = "wlf,wm8524" }, | ||
| 212 | { /* sentinel*/ } | ||
| 213 | }; | ||
| 214 | MODULE_DEVICE_TABLE(of, wm8524_of_match); | ||
| 215 | #endif | ||
| 216 | |||
| 217 | static int wm8524_codec_probe(struct platform_device *pdev) | ||
| 218 | { | ||
| 219 | struct wm8524_priv *wm8524; | ||
| 220 | int ret; | ||
| 221 | |||
| 222 | wm8524 = devm_kzalloc(&pdev->dev, sizeof(struct wm8524_priv), | ||
| 223 | GFP_KERNEL); | ||
| 224 | if (wm8524 == NULL) | ||
| 225 | return -ENOMEM; | ||
| 226 | |||
| 227 | platform_set_drvdata(pdev, wm8524); | ||
| 228 | |||
| 229 | wm8524->mute = devm_gpiod_get(&pdev->dev, "wlf,mute", GPIOD_OUT_LOW); | ||
| 230 | if (IS_ERR(wm8524->mute)) { | ||
| 231 | ret = PTR_ERR(wm8524->mute); | ||
| 232 | dev_err(&pdev->dev, "Failed to get mute line: %d\n", ret); | ||
| 233 | return ret; | ||
| 234 | } | ||
| 235 | |||
| 236 | ret = snd_soc_register_codec(&pdev->dev, | ||
| 237 | &soc_codec_dev_wm8524, &wm8524_dai, 1); | ||
| 238 | if (ret < 0) { | ||
| 239 | dev_err(&pdev->dev, "Failed to register codec: %d\n", ret); | ||
| 240 | snd_soc_unregister_platform(&pdev->dev); | ||
| 241 | } | ||
| 242 | |||
| 243 | return ret; | ||
| 244 | } | ||
| 245 | |||
| 246 | static int wm8524_codec_remove(struct platform_device *pdev) | ||
| 247 | { | ||
| 248 | snd_soc_unregister_codec(&pdev->dev); | ||
| 249 | return 0; | ||
| 250 | } | ||
| 251 | |||
| 252 | static struct platform_driver wm8524_codec_driver = { | ||
| 253 | .probe = wm8524_codec_probe, | ||
| 254 | .remove = wm8524_codec_remove, | ||
| 255 | .driver = { | ||
| 256 | .name = "wm8524-codec", | ||
| 257 | .of_match_table = wm8524_of_match, | ||
| 258 | }, | ||
| 259 | }; | ||
| 260 | module_platform_driver(wm8524_codec_driver); | ||
| 261 | |||
| 262 | MODULE_DESCRIPTION("ASoC WM8524 driver"); | ||
| 263 | MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>"); | ||
| 264 | MODULE_ALIAS("platform:wm8524-codec"); | ||
| 265 | MODULE_LICENSE("GPL"); | ||
