1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * sam9x5_wm8731 -- SoC audio for AT91SAM9X5-based boards
4 * that are using WM8731 as codec.
6 * Copyright (C) 2011 Atmel,
7 * Nicolas Ferre <nicolas.ferre@atmel.com>
9 * Copyright (C) 2013 Paratronic,
10 * Richard Genoud <richard.genoud@gmail.com>
12 * Based on sam9g20_wm8731.c by:
13 * Sedji Gaouaou <sedji.gaouaou@atmel.com>
16 #include <linux/export.h>
17 #include <linux/module.h>
18 #include <linux/mod_devicetable.h>
19 #include <linux/platform_device.h>
20 #include <linux/device.h>
22 #include <sound/soc.h>
23 #include <sound/soc-dai.h>
24 #include <sound/soc-dapm.h>
26 #include "../codecs/wm8731.h"
27 #include "atmel_ssc_dai.h"
30 #define MCLK_RATE 12288000
32 #define DRV_NAME "sam9x5-snd-wm8731"
34 struct sam9x5_drvdata
{
39 * Logic for a wm8731 as connected on a at91sam9x5ek based board.
41 static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime
*rtd
)
43 struct snd_soc_dai
*codec_dai
= snd_soc_rtd_to_codec(rtd
, 0);
44 struct device
*dev
= rtd
->dev
;
47 dev_dbg(dev
, "%s called\n", __func__
);
49 /* set the codec system clock for DAC and ADC */
50 ret
= snd_soc_dai_set_sysclk(codec_dai
, WM8731_SYSCLK_XTAL
,
51 MCLK_RATE
, SND_SOC_CLOCK_IN
);
53 dev_err(dev
, "Failed to set WM8731 SYSCLK: %d\n", ret
);
61 * Audio paths on at91sam9x5ek board:
63 * |A| ------------> | | ---R----> Headphone Jack
64 * |T| <----\ | WM | ---L--/
65 * |9| ---> CLK <--> | 8731 | <--R----- Line In Jack
66 * |1| <------------ | | <--L--/
68 static const struct snd_soc_dapm_widget sam9x5_dapm_widgets
[] = {
69 SND_SOC_DAPM_HP("Headphone Jack", NULL
),
70 SND_SOC_DAPM_LINE("Line In Jack", NULL
),
73 static int sam9x5_wm8731_driver_probe(struct platform_device
*pdev
)
75 struct device_node
*np
= pdev
->dev
.of_node
;
76 struct device_node
*codec_np
, *cpu_np
;
77 struct snd_soc_card
*card
;
78 struct snd_soc_dai_link
*dai
;
79 struct sam9x5_drvdata
*priv
;
80 struct snd_soc_dai_link_component
*comp
;
84 dev_err(&pdev
->dev
, "No device node supplied\n");
88 card
= devm_kzalloc(&pdev
->dev
, sizeof(*card
), GFP_KERNEL
);
89 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
90 dai
= devm_kzalloc(&pdev
->dev
, sizeof(*dai
), GFP_KERNEL
);
91 comp
= devm_kzalloc(&pdev
->dev
, 3 * sizeof(*comp
), GFP_KERNEL
);
92 if (!dai
|| !card
|| !priv
|| !comp
) {
97 snd_soc_card_set_drvdata(card
, priv
);
99 card
->dev
= &pdev
->dev
;
100 card
->owner
= THIS_MODULE
;
101 card
->dai_link
= dai
;
103 card
->dapm_widgets
= sam9x5_dapm_widgets
;
104 card
->num_dapm_widgets
= ARRAY_SIZE(sam9x5_dapm_widgets
);
106 dai
->cpus
= &comp
[0];
108 dai
->codecs
= &comp
[1];
110 dai
->platforms
= &comp
[2];
111 dai
->num_platforms
= 1;
113 dai
->name
= "WM8731";
114 dai
->stream_name
= "WM8731 PCM";
115 dai
->codecs
->dai_name
= "wm8731-hifi";
116 dai
->init
= sam9x5_wm8731_init
;
117 dai
->dai_fmt
= SND_SOC_DAIFMT_DSP_A
| SND_SOC_DAIFMT_NB_NF
118 | SND_SOC_DAIFMT_CBP_CFP
;
120 ret
= snd_soc_of_parse_card_name(card
, "atmel,model");
122 dev_err(&pdev
->dev
, "atmel,model node missing\n");
126 ret
= snd_soc_of_parse_audio_routing(card
, "atmel,audio-routing");
128 dev_err(&pdev
->dev
, "atmel,audio-routing node missing\n");
132 codec_np
= of_parse_phandle(np
, "atmel,audio-codec", 0);
134 dev_err(&pdev
->dev
, "atmel,audio-codec node missing\n");
139 dai
->codecs
->of_node
= codec_np
;
141 cpu_np
= of_parse_phandle(np
, "atmel,ssc-controller", 0);
143 dev_err(&pdev
->dev
, "atmel,ssc-controller node missing\n");
145 goto out_put_codec_np
;
147 dai
->cpus
->of_node
= cpu_np
;
148 dai
->platforms
->of_node
= cpu_np
;
150 priv
->ssc_id
= of_alias_get_id(cpu_np
, "ssc");
152 ret
= atmel_ssc_set_audio(priv
->ssc_id
);
154 dev_err(&pdev
->dev
, "Failed to set SSC %d for audio: %d\n",
159 ret
= devm_snd_soc_register_card(&pdev
->dev
, card
);
161 dev_err(&pdev
->dev
, "Platform device allocation failed\n");
165 dev_dbg(&pdev
->dev
, "%s ok\n", __func__
);
170 atmel_ssc_put_audio(priv
->ssc_id
);
174 of_node_put(codec_np
);
179 static void sam9x5_wm8731_driver_remove(struct platform_device
*pdev
)
181 struct snd_soc_card
*card
= platform_get_drvdata(pdev
);
182 struct sam9x5_drvdata
*priv
= card
->drvdata
;
184 atmel_ssc_put_audio(priv
->ssc_id
);
187 static const struct of_device_id sam9x5_wm8731_of_match
[] = {
188 { .compatible
= "atmel,sam9x5-wm8731-audio", },
191 MODULE_DEVICE_TABLE(of
, sam9x5_wm8731_of_match
);
193 static struct platform_driver sam9x5_wm8731_driver
= {
196 .of_match_table
= of_match_ptr(sam9x5_wm8731_of_match
),
198 .probe
= sam9x5_wm8731_driver_probe
,
199 .remove
= sam9x5_wm8731_driver_remove
,
201 module_platform_driver(sam9x5_wm8731_driver
);
203 /* Module information */
204 MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
205 MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
206 MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
207 MODULE_LICENSE("GPL");
208 MODULE_ALIAS("platform:" DRV_NAME
);