1dca6408dSSylwester Nawrocki // SPDX-License-Identifier: GPL-2.0+
2dca6408dSSylwester Nawrocki //
3dca6408dSSylwester Nawrocki // Copyright (c) 2014, Insignal Co., Ltd.
4dca6408dSSylwester Nawrocki //
5dca6408dSSylwester Nawrocki // Author: Claude <claude@insginal.co.kr>
6dca6408dSSylwester Nawrocki
7dca6408dSSylwester Nawrocki #include <linux/module.h>
8340d79a1SRob Herring #include <linux/of.h>
9dca6408dSSylwester Nawrocki #include <linux/platform_device.h>
10dca6408dSSylwester Nawrocki #include <linux/clk.h>
11dca6408dSSylwester Nawrocki
12dca6408dSSylwester Nawrocki #include <sound/soc.h>
13dca6408dSSylwester Nawrocki #include <sound/soc-dapm.h>
14dca6408dSSylwester Nawrocki #include <sound/pcm.h>
15dca6408dSSylwester Nawrocki #include <sound/pcm_params.h>
16dca6408dSSylwester Nawrocki
17dca6408dSSylwester Nawrocki #include "../codecs/wm8994.h"
18dca6408dSSylwester Nawrocki #include "i2s.h"
19dca6408dSSylwester Nawrocki
arndale_rt5631_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)20dca6408dSSylwester Nawrocki static int arndale_rt5631_hw_params(struct snd_pcm_substream *substream,
21dca6408dSSylwester Nawrocki struct snd_pcm_hw_params *params)
22dca6408dSSylwester Nawrocki {
2321b6cd54SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
2421b6cd54SKuninori Morimoto struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
2521b6cd54SKuninori Morimoto struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
26dca6408dSSylwester Nawrocki int rfs, ret;
27dca6408dSSylwester Nawrocki unsigned long rclk;
28dca6408dSSylwester Nawrocki
29dca6408dSSylwester Nawrocki rfs = 256;
30dca6408dSSylwester Nawrocki
31dca6408dSSylwester Nawrocki rclk = params_rate(params) * rfs;
32dca6408dSSylwester Nawrocki
33dca6408dSSylwester Nawrocki ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
34dca6408dSSylwester Nawrocki 0, SND_SOC_CLOCK_OUT);
35dca6408dSSylwester Nawrocki if (ret < 0)
36dca6408dSSylwester Nawrocki return ret;
37dca6408dSSylwester Nawrocki
38dca6408dSSylwester Nawrocki ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
39dca6408dSSylwester Nawrocki 0, SND_SOC_CLOCK_OUT);
40dca6408dSSylwester Nawrocki
41dca6408dSSylwester Nawrocki if (ret < 0)
42dca6408dSSylwester Nawrocki return ret;
43dca6408dSSylwester Nawrocki
44dca6408dSSylwester Nawrocki ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
45dca6408dSSylwester Nawrocki if (ret < 0)
46dca6408dSSylwester Nawrocki return ret;
47dca6408dSSylwester Nawrocki
48dca6408dSSylwester Nawrocki return 0;
49dca6408dSSylwester Nawrocki }
50dca6408dSSylwester Nawrocki
512080acf3SRikard Falkeborn static const struct snd_soc_ops arndale_rt5631_ops = {
52dca6408dSSylwester Nawrocki .hw_params = arndale_rt5631_hw_params,
53dca6408dSSylwester Nawrocki };
54dca6408dSSylwester Nawrocki
arndale_wm1811_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)55dca6408dSSylwester Nawrocki static int arndale_wm1811_hw_params(struct snd_pcm_substream *substream,
56dca6408dSSylwester Nawrocki struct snd_pcm_hw_params *params)
57dca6408dSSylwester Nawrocki {
5821b6cd54SKuninori Morimoto struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
5921b6cd54SKuninori Morimoto struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
60dca6408dSSylwester Nawrocki unsigned int rfs, rclk;
61dca6408dSSylwester Nawrocki
62dca6408dSSylwester Nawrocki /* Ensure AIF1CLK is >= 3 MHz for optimal performance */
63dca6408dSSylwester Nawrocki if (params_width(params) == 24)
64dca6408dSSylwester Nawrocki rfs = 384;
65dca6408dSSylwester Nawrocki else if (params_rate(params) == 8000 || params_rate(params) == 11025)
66dca6408dSSylwester Nawrocki rfs = 512;
67dca6408dSSylwester Nawrocki else
68dca6408dSSylwester Nawrocki rfs = 256;
69dca6408dSSylwester Nawrocki
70dca6408dSSylwester Nawrocki rclk = params_rate(params) * rfs;
71dca6408dSSylwester Nawrocki
72dca6408dSSylwester Nawrocki /*
73dca6408dSSylwester Nawrocki * We add 1 to the frequency value to ensure proper EPLL setting
74dca6408dSSylwester Nawrocki * for each audio sampling rate (see epll_24mhz_tbl in drivers/clk/
75dca6408dSSylwester Nawrocki * samsung/clk-exynos5250.c for list of available EPLL rates).
76dca6408dSSylwester Nawrocki * The CODEC uses clk API and the value will be rounded hence the MCLK1
77dca6408dSSylwester Nawrocki * clock's frequency will still be exact multiple of the sample rate.
78dca6408dSSylwester Nawrocki */
79dca6408dSSylwester Nawrocki return snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_MCLK1,
80dca6408dSSylwester Nawrocki rclk + 1, SND_SOC_CLOCK_IN);
81dca6408dSSylwester Nawrocki }
82dca6408dSSylwester Nawrocki
832080acf3SRikard Falkeborn static const struct snd_soc_ops arndale_wm1811_ops = {
84dca6408dSSylwester Nawrocki .hw_params = arndale_wm1811_hw_params,
85dca6408dSSylwester Nawrocki };
86dca6408dSSylwester Nawrocki
87dca6408dSSylwester Nawrocki SND_SOC_DAILINK_DEFS(rt5631_hifi,
88dca6408dSSylwester Nawrocki DAILINK_COMP_ARRAY(COMP_EMPTY()),
89dca6408dSSylwester Nawrocki DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5631-aif1")),
90dca6408dSSylwester Nawrocki DAILINK_COMP_ARRAY(COMP_EMPTY()));
91dca6408dSSylwester Nawrocki
92dca6408dSSylwester Nawrocki static struct snd_soc_dai_link arndale_rt5631_dai[] = {
93dca6408dSSylwester Nawrocki {
94dca6408dSSylwester Nawrocki .name = "RT5631 HiFi",
95dca6408dSSylwester Nawrocki .stream_name = "Primary",
96dca6408dSSylwester Nawrocki .dai_fmt = SND_SOC_DAIFMT_I2S
97dca6408dSSylwester Nawrocki | SND_SOC_DAIFMT_NB_NF
98dca6408dSSylwester Nawrocki | SND_SOC_DAIFMT_CBS_CFS,
99dca6408dSSylwester Nawrocki .ops = &arndale_rt5631_ops,
100dca6408dSSylwester Nawrocki SND_SOC_DAILINK_REG(rt5631_hifi),
101dca6408dSSylwester Nawrocki },
102dca6408dSSylwester Nawrocki };
103dca6408dSSylwester Nawrocki
104dca6408dSSylwester Nawrocki SND_SOC_DAILINK_DEFS(wm1811_hifi,
105dca6408dSSylwester Nawrocki DAILINK_COMP_ARRAY(COMP_EMPTY()),
106dca6408dSSylwester Nawrocki DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8994-aif1")),
107dca6408dSSylwester Nawrocki DAILINK_COMP_ARRAY(COMP_EMPTY()));
108dca6408dSSylwester Nawrocki
109dca6408dSSylwester Nawrocki static struct snd_soc_dai_link arndale_wm1811_dai[] = {
110dca6408dSSylwester Nawrocki {
111dca6408dSSylwester Nawrocki .name = "WM1811 HiFi",
112dca6408dSSylwester Nawrocki .stream_name = "Primary",
113dca6408dSSylwester Nawrocki .dai_fmt = SND_SOC_DAIFMT_I2S
114dca6408dSSylwester Nawrocki | SND_SOC_DAIFMT_NB_NF
115dca6408dSSylwester Nawrocki | SND_SOC_DAIFMT_CBM_CFM,
116dca6408dSSylwester Nawrocki .ops = &arndale_wm1811_ops,
117dca6408dSSylwester Nawrocki SND_SOC_DAILINK_REG(wm1811_hifi),
118dca6408dSSylwester Nawrocki },
119dca6408dSSylwester Nawrocki };
120dca6408dSSylwester Nawrocki
121dca6408dSSylwester Nawrocki static struct snd_soc_card arndale_rt5631 = {
122dca6408dSSylwester Nawrocki .name = "Arndale RT5631",
123dca6408dSSylwester Nawrocki .owner = THIS_MODULE,
124dca6408dSSylwester Nawrocki .dai_link = arndale_rt5631_dai,
125dca6408dSSylwester Nawrocki .num_links = ARRAY_SIZE(arndale_rt5631_dai),
126dca6408dSSylwester Nawrocki };
127dca6408dSSylwester Nawrocki
128dca6408dSSylwester Nawrocki static struct snd_soc_card arndale_wm1811 = {
129dca6408dSSylwester Nawrocki .name = "Arndale WM1811",
130dca6408dSSylwester Nawrocki .owner = THIS_MODULE,
131dca6408dSSylwester Nawrocki .dai_link = arndale_wm1811_dai,
132dca6408dSSylwester Nawrocki .num_links = ARRAY_SIZE(arndale_wm1811_dai),
133dca6408dSSylwester Nawrocki };
134dca6408dSSylwester Nawrocki
arndale_put_of_nodes(struct snd_soc_card * card)135dca6408dSSylwester Nawrocki static void arndale_put_of_nodes(struct snd_soc_card *card)
136dca6408dSSylwester Nawrocki {
137dca6408dSSylwester Nawrocki struct snd_soc_dai_link *dai_link;
138dca6408dSSylwester Nawrocki int i;
139dca6408dSSylwester Nawrocki
140dca6408dSSylwester Nawrocki for_each_card_prelinks(card, i, dai_link) {
141dca6408dSSylwester Nawrocki of_node_put(dai_link->cpus->of_node);
142dca6408dSSylwester Nawrocki of_node_put(dai_link->codecs->of_node);
143dca6408dSSylwester Nawrocki }
144dca6408dSSylwester Nawrocki }
145dca6408dSSylwester Nawrocki
arndale_audio_probe(struct platform_device * pdev)146dca6408dSSylwester Nawrocki static int arndale_audio_probe(struct platform_device *pdev)
147dca6408dSSylwester Nawrocki {
148dca6408dSSylwester Nawrocki struct device_node *np = pdev->dev.of_node;
149dca6408dSSylwester Nawrocki struct snd_soc_card *card;
150dca6408dSSylwester Nawrocki struct snd_soc_dai_link *dai_link;
151dca6408dSSylwester Nawrocki int ret;
152dca6408dSSylwester Nawrocki
153dca6408dSSylwester Nawrocki card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev);
154dca6408dSSylwester Nawrocki card->dev = &pdev->dev;
155dca6408dSSylwester Nawrocki dai_link = card->dai_link;
156dca6408dSSylwester Nawrocki
157dca6408dSSylwester Nawrocki dai_link->cpus->of_node = of_parse_phandle(np, "samsung,audio-cpu", 0);
158dca6408dSSylwester Nawrocki if (!dai_link->cpus->of_node) {
159dca6408dSSylwester Nawrocki dev_err(&pdev->dev,
160dca6408dSSylwester Nawrocki "Property 'samsung,audio-cpu' missing or invalid\n");
161dca6408dSSylwester Nawrocki return -EINVAL;
162dca6408dSSylwester Nawrocki }
163dca6408dSSylwester Nawrocki
164dca6408dSSylwester Nawrocki if (!dai_link->platforms->name)
165dca6408dSSylwester Nawrocki dai_link->platforms->of_node = dai_link->cpus->of_node;
166dca6408dSSylwester Nawrocki
167dca6408dSSylwester Nawrocki dai_link->codecs->of_node = of_parse_phandle(np, "samsung,audio-codec", 0);
168dca6408dSSylwester Nawrocki if (!dai_link->codecs->of_node) {
169dca6408dSSylwester Nawrocki dev_err(&pdev->dev,
170dca6408dSSylwester Nawrocki "Property 'samsung,audio-codec' missing or invalid\n");
171dca6408dSSylwester Nawrocki ret = -EINVAL;
172dca6408dSSylwester Nawrocki goto err_put_of_nodes;
173dca6408dSSylwester Nawrocki }
174dca6408dSSylwester Nawrocki
175dca6408dSSylwester Nawrocki ret = devm_snd_soc_register_card(card->dev, card);
176dca6408dSSylwester Nawrocki if (ret) {
17727c6eaebSKuninori Morimoto dev_err_probe(&pdev->dev, ret,
17827c6eaebSKuninori Morimoto "snd_soc_register_card() failed\n");
179dca6408dSSylwester Nawrocki goto err_put_of_nodes;
180dca6408dSSylwester Nawrocki }
181dca6408dSSylwester Nawrocki return 0;
182dca6408dSSylwester Nawrocki
183dca6408dSSylwester Nawrocki err_put_of_nodes:
184dca6408dSSylwester Nawrocki arndale_put_of_nodes(card);
185dca6408dSSylwester Nawrocki return ret;
186dca6408dSSylwester Nawrocki }
187dca6408dSSylwester Nawrocki
arndale_audio_remove(struct platform_device * pdev)1882149a1afSUwe Kleine-König static void arndale_audio_remove(struct platform_device *pdev)
189dca6408dSSylwester Nawrocki {
190dca6408dSSylwester Nawrocki struct snd_soc_card *card = platform_get_drvdata(pdev);
191dca6408dSSylwester Nawrocki
192dca6408dSSylwester Nawrocki arndale_put_of_nodes(card);
193dca6408dSSylwester Nawrocki }
194dca6408dSSylwester Nawrocki
195dca6408dSSylwester Nawrocki static const struct of_device_id arndale_audio_of_match[] = {
196dca6408dSSylwester Nawrocki { .compatible = "samsung,arndale-rt5631", .data = &arndale_rt5631 },
197dca6408dSSylwester Nawrocki { .compatible = "samsung,arndale-alc5631", .data = &arndale_rt5631 },
198dca6408dSSylwester Nawrocki { .compatible = "samsung,arndale-wm1811", .data = &arndale_wm1811 },
199dca6408dSSylwester Nawrocki {},
200dca6408dSSylwester Nawrocki };
201dca6408dSSylwester Nawrocki MODULE_DEVICE_TABLE(of, arndale_audio_of_match);
202dca6408dSSylwester Nawrocki
203dca6408dSSylwester Nawrocki static struct platform_driver arndale_audio_driver = {
204dca6408dSSylwester Nawrocki .driver = {
205dca6408dSSylwester Nawrocki .name = "arndale-audio",
206dca6408dSSylwester Nawrocki .pm = &snd_soc_pm_ops,
207dca6408dSSylwester Nawrocki .of_match_table = arndale_audio_of_match,
208dca6408dSSylwester Nawrocki },
209dca6408dSSylwester Nawrocki .probe = arndale_audio_probe,
210*130af75bSUwe Kleine-König .remove = arndale_audio_remove,
211dca6408dSSylwester Nawrocki };
212dca6408dSSylwester Nawrocki
213dca6408dSSylwester Nawrocki module_platform_driver(arndale_audio_driver);
214dca6408dSSylwester Nawrocki
215dca6408dSSylwester Nawrocki MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
216dca6408dSSylwester Nawrocki MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
217dca6408dSSylwester Nawrocki MODULE_LICENSE("GPL");
218