1 // SPDX-License-Identifier: GPL-2.0-only
3 * wm8524.c -- WM8524 ALSA SoC Audio driver
5 * Copyright 2009 Wolfson Microelectronics plc
8 * Based on WM8523 ALSA SoC Audio driver written by Mark Brown
11 #include <linux/module.h>
12 #include <linux/moduleparam.h>
13 #include <linux/init.h>
14 #include <linux/delay.h>
15 #include <linux/slab.h>
16 #include <linux/gpio/consumer.h>
17 #include <linux/of_device.h>
18 #include <sound/core.h>
19 #include <sound/pcm.h>
20 #include <sound/pcm_params.h>
21 #include <sound/soc.h>
22 #include <sound/initval.h>
24 #define WM8524_NUM_RATES 7
26 /* codec private data */
28 struct gpio_desc
*mute
;
30 unsigned int rate_constraint_list
[WM8524_NUM_RATES
];
31 struct snd_pcm_hw_constraint_list rate_constraint
;
35 static const struct snd_soc_dapm_widget wm8524_dapm_widgets
[] = {
36 SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM
, 0, 0),
37 SND_SOC_DAPM_OUTPUT("LINEVOUTL"),
38 SND_SOC_DAPM_OUTPUT("LINEVOUTR"),
41 static const struct snd_soc_dapm_route wm8524_dapm_routes
[] = {
42 { "LINEVOUTL", NULL
, "DAC" },
43 { "LINEVOUTR", NULL
, "DAC" },
49 } lrclk_ratios
[WM8524_NUM_RATES
] = {
59 static int wm8524_startup(struct snd_pcm_substream
*substream
,
60 struct snd_soc_dai
*dai
)
62 struct snd_soc_component
*component
= dai
->component
;
63 struct wm8524_priv
*wm8524
= snd_soc_component_get_drvdata(component
);
65 /* The set of sample rates that can be supported depends on the
66 * MCLK supplied to the CODEC - enforce this.
68 if (!wm8524
->sysclk
) {
69 dev_err(component
->dev
,
70 "No MCLK configured, call set_sysclk() on init\n");
74 snd_pcm_hw_constraint_list(substream
->runtime
, 0,
75 SNDRV_PCM_HW_PARAM_RATE
,
76 &wm8524
->rate_constraint
);
78 gpiod_set_value_cansleep(wm8524
->mute
, 1);
83 static void wm8524_shutdown(struct snd_pcm_substream
*substream
,
84 struct snd_soc_dai
*dai
)
86 struct snd_soc_component
*component
= dai
->component
;
87 struct wm8524_priv
*wm8524
= snd_soc_component_get_drvdata(component
);
89 gpiod_set_value_cansleep(wm8524
->mute
, 0);
92 static int wm8524_set_dai_sysclk(struct snd_soc_dai
*codec_dai
,
93 int clk_id
, unsigned int freq
, int dir
)
95 struct snd_soc_component
*component
= codec_dai
->component
;
96 struct wm8524_priv
*wm8524
= snd_soc_component_get_drvdata(component
);
100 wm8524
->sysclk
= freq
;
102 wm8524
->rate_constraint
.count
= 0;
103 for (i
= 0; i
< ARRAY_SIZE(lrclk_ratios
); i
++) {
104 val
= freq
/ lrclk_ratios
[i
].ratio
;
105 /* Check that it's a standard rate since core can't
106 * cope with others and having the odd rates confuses
107 * constraint matching.
118 dev_dbg(component
->dev
, "Supported sample rate: %dHz\n",
120 wm8524
->rate_constraint_list
[j
++] = val
;
121 wm8524
->rate_constraint
.count
++;
124 dev_dbg(component
->dev
, "Skipping sample rate: %dHz\n",
129 /* Need at least one supported rate... */
130 if (wm8524
->rate_constraint
.count
== 0)
136 static int wm8524_set_fmt(struct snd_soc_dai
*codec_dai
, unsigned int fmt
)
138 fmt
&= (SND_SOC_DAIFMT_FORMAT_MASK
| SND_SOC_DAIFMT_INV_MASK
|
139 SND_SOC_DAIFMT_MASTER_MASK
);
141 if (fmt
!= (SND_SOC_DAIFMT_I2S
| SND_SOC_DAIFMT_NB_NF
|
142 SND_SOC_DAIFMT_CBS_CFS
)) {
143 dev_err(codec_dai
->dev
, "Invalid DAI format\n");
150 static int wm8524_mute_stream(struct snd_soc_dai
*dai
, int mute
, int stream
)
152 struct wm8524_priv
*wm8524
= snd_soc_component_get_drvdata(dai
->component
);
155 gpiod_set_value_cansleep(wm8524
->mute
, mute
);
160 #define WM8524_RATES SNDRV_PCM_RATE_8000_192000
162 #define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
163 SNDRV_PCM_FMTBIT_S24_LE |\
164 SNDRV_PCM_FMTBIT_S32_LE)
166 static const struct snd_soc_dai_ops wm8524_dai_ops
= {
167 .startup
= wm8524_startup
,
168 .shutdown
= wm8524_shutdown
,
169 .set_sysclk
= wm8524_set_dai_sysclk
,
170 .set_fmt
= wm8524_set_fmt
,
171 .mute_stream
= wm8524_mute_stream
,
174 static struct snd_soc_dai_driver wm8524_dai
= {
175 .name
= "wm8524-hifi",
177 .stream_name
= "Playback",
180 .rates
= WM8524_RATES
,
181 .formats
= WM8524_FORMATS
,
183 .ops
= &wm8524_dai_ops
,
186 static int wm8524_probe(struct snd_soc_component
*component
)
188 struct wm8524_priv
*wm8524
= snd_soc_component_get_drvdata(component
);
190 wm8524
->rate_constraint
.list
= &wm8524
->rate_constraint_list
[0];
191 wm8524
->rate_constraint
.count
=
192 ARRAY_SIZE(wm8524
->rate_constraint_list
);
197 static const struct snd_soc_component_driver soc_component_dev_wm8524
= {
198 .probe
= wm8524_probe
,
199 .dapm_widgets
= wm8524_dapm_widgets
,
200 .num_dapm_widgets
= ARRAY_SIZE(wm8524_dapm_widgets
),
201 .dapm_routes
= wm8524_dapm_routes
,
202 .num_dapm_routes
= ARRAY_SIZE(wm8524_dapm_routes
),
204 .use_pmdown_time
= 1,
206 .non_legacy_dai_naming
= 1,
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
= devm_snd_soc_register_component(&pdev
->dev
,
235 &soc_component_dev_wm8524
, &wm8524_dai
, 1);
237 dev_err(&pdev
->dev
, "Failed to register component: %d\n", ret
);
242 static struct platform_driver wm8524_codec_driver
= {
243 .probe
= wm8524_codec_probe
,
245 .name
= "wm8524-codec",
246 .of_match_table
= wm8524_of_match
,
249 module_platform_driver(wm8524_codec_driver
);
251 MODULE_DESCRIPTION("ASoC WM8524 driver");
252 MODULE_AUTHOR("Mihai Serban <mihai.serban@nxp.com>");
253 MODULE_ALIAS("platform:wm8524-codec");
254 MODULE_LICENSE("GPL");