2 * wm8524.c -- WM8524 ALSA SoC Audio driver
4 * Copyright 2009 Wolfson Microelectronics plc
7 * Based on WM8523 ALSA SoC Audio driver written by Mark Brown
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.
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>
27 #define WM8524_NUM_RATES 7
29 /* codec private data */
31 struct gpio_desc
*mute
;
33 unsigned int rate_constraint_list
[WM8524_NUM_RATES
];
34 struct snd_pcm_hw_constraint_list rate_constraint
;
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"),
44 static const struct snd_soc_dapm_route wm8524_dapm_routes
[] = {
45 { "LINEVOUTL", NULL
, "DAC" },
46 { "LINEVOUTR", NULL
, "DAC" },
52 } lrclk_ratios
[WM8524_NUM_RATES
] = {
62 static int wm8524_startup(struct snd_pcm_substream
*substream
,
63 struct snd_soc_dai
*dai
)
65 struct snd_soc_codec
*codec
= dai
->codec
;
66 struct wm8524_priv
*wm8524
= snd_soc_codec_get_drvdata(codec
);
68 /* The set of sample rates that can be supported depends on the
69 * MCLK supplied to the CODEC - enforce this.
71 if (!wm8524
->sysclk
) {
73 "No MCLK configured, call set_sysclk() on init\n");
77 snd_pcm_hw_constraint_list(substream
->runtime
, 0,
78 SNDRV_PCM_HW_PARAM_RATE
,
79 &wm8524
->rate_constraint
);
81 gpiod_set_value_cansleep(wm8524
->mute
, 1);
86 static void wm8524_shutdown(struct snd_pcm_substream
*substream
,
87 struct snd_soc_dai
*dai
)
89 struct snd_soc_codec
*codec
= dai
->codec
;
90 struct wm8524_priv
*wm8524
= snd_soc_codec_get_drvdata(codec
);
92 gpiod_set_value_cansleep(wm8524
->mute
, 0);
95 static int wm8524_set_dai_sysclk(struct snd_soc_dai
*codec_dai
,
96 int clk_id
, unsigned int freq
, int dir
)
98 struct snd_soc_codec
*codec
= codec_dai
->codec
;
99 struct wm8524_priv
*wm8524
= snd_soc_codec_get_drvdata(codec
);
103 wm8524
->sysclk
= freq
;
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.
121 dev_dbg(codec
->dev
, "Supported sample rate: %dHz\n",
123 wm8524
->rate_constraint_list
[j
++] = val
;
124 wm8524
->rate_constraint
.count
++;
127 dev_dbg(codec
->dev
, "Skipping sample rate: %dHz\n",
132 /* Need at least one supported rate... */
133 if (wm8524
->rate_constraint
.count
== 0)
139 static int wm8524_set_fmt(struct snd_soc_dai
*codec_dai
, unsigned int fmt
)
141 fmt
&= (SND_SOC_DAIFMT_FORMAT_MASK
| SND_SOC_DAIFMT_INV_MASK
|
142 SND_SOC_DAIFMT_MASTER_MASK
);
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");
153 static int wm8524_mute_stream(struct snd_soc_dai
*dai
, int mute
, int stream
)
155 struct wm8524_priv
*wm8524
= snd_soc_codec_get_drvdata(dai
->codec
);
158 gpiod_set_value_cansleep(wm8524
->mute
, mute
);
163 #define WM8524_RATES SNDRV_PCM_RATE_8000_192000
165 #define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
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
,
175 static struct snd_soc_dai_driver wm8524_dai
= {
176 .name
= "wm8524-hifi",
178 .stream_name
= "Playback",
181 .rates
= WM8524_RATES
,
182 .formats
= WM8524_FORMATS
,
184 .ops
= &wm8524_dai_ops
,
187 static int wm8524_probe(struct snd_soc_codec
*codec
)
189 struct wm8524_priv
*wm8524
= snd_soc_codec_get_drvdata(codec
);
191 wm8524
->rate_constraint
.list
= &wm8524
->rate_constraint_list
[0];
192 wm8524
->rate_constraint
.count
=
193 ARRAY_SIZE(wm8524
->rate_constraint_list
);
198 static const struct snd_soc_codec_driver soc_codec_dev_wm8524
= {
199 .probe
= wm8524_probe
,
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
),
209 static const struct of_device_id wm8524_of_match
[] = {
210 { .compatible
= "wlf,wm8524" },
213 MODULE_DEVICE_TABLE(of
, wm8524_of_match
);
215 static int wm8524_codec_probe(struct platform_device
*pdev
)
217 struct wm8524_priv
*wm8524
;
220 wm8524
= devm_kzalloc(&pdev
->dev
, sizeof(struct wm8524_priv
),
225 platform_set_drvdata(pdev
, wm8524
);
227 wm8524
->mute
= devm_gpiod_get(&pdev
->dev
, "wlf,mute", GPIOD_OUT_LOW
);
228 if (IS_ERR(wm8524
->mute
)) {
229 ret
= PTR_ERR(wm8524
->mute
);
230 dev_err(&pdev
->dev
, "Failed to get mute line: %d\n", ret
);
234 ret
= snd_soc_register_codec(&pdev
->dev
,
235 &soc_codec_dev_wm8524
, &wm8524_dai
, 1);
237 dev_err(&pdev
->dev
, "Failed to register codec: %d\n", ret
);
242 static int wm8524_codec_remove(struct platform_device
*pdev
)
244 snd_soc_unregister_codec(&pdev
->dev
);
248 static struct platform_driver wm8524_codec_driver
= {
249 .probe
= wm8524_codec_probe
,
250 .remove
= wm8524_codec_remove
,
252 .name
= "wm8524-codec",
253 .of_match_table
= wm8524_of_match
,
256 module_platform_driver(wm8524_codec_driver
);
258 MODULE_DESCRIPTION("ASoC WM8524 driver");
259 MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
260 MODULE_ALIAS("platform:wm8524-codec");
261 MODULE_LICENSE("GPL");