116216333SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2fcb5e47eSDong Aisheng /*
3fcb5e47eSDong Aisheng * Copyright 2011 Freescale Semiconductor, Inc.
4fcb5e47eSDong Aisheng */
5fcb5e47eSDong Aisheng
6fcb5e47eSDong Aisheng #include <linux/module.h>
7fcb5e47eSDong Aisheng #include <linux/device.h>
8e968194bSShawn Guo #include <linux/of.h>
9e968194bSShawn Guo #include <linux/of_device.h>
10fcb5e47eSDong Aisheng #include <sound/core.h>
11fcb5e47eSDong Aisheng #include <sound/pcm.h>
12fcb5e47eSDong Aisheng #include <sound/soc.h>
13fcb5e47eSDong Aisheng #include <sound/jack.h>
14fcb5e47eSDong Aisheng #include <sound/soc-dapm.h>
15fcb5e47eSDong Aisheng
16fcb5e47eSDong Aisheng #include "../codecs/sgtl5000.h"
17fcb5e47eSDong Aisheng #include "mxs-saif.h"
18fcb5e47eSDong Aisheng
mxs_sgtl5000_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params)19fcb5e47eSDong Aisheng static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
20fcb5e47eSDong Aisheng struct snd_pcm_hw_params *params)
21fcb5e47eSDong Aisheng {
223359128dSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2384a41e06SKuninori Morimoto struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
2484a41e06SKuninori Morimoto struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
25fcb5e47eSDong Aisheng unsigned int rate = params_rate(params);
26bc6a5d64SLars-Peter Clausen u32 mclk;
27fcb5e47eSDong Aisheng int ret;
28fcb5e47eSDong Aisheng
29fcb5e47eSDong Aisheng /* sgtl5000 does not support 512*rate when in 96000 fs */
30fcb5e47eSDong Aisheng switch (rate) {
31fcb5e47eSDong Aisheng case 96000:
32fcb5e47eSDong Aisheng mclk = 256 * rate;
33fcb5e47eSDong Aisheng break;
34fcb5e47eSDong Aisheng default:
35fcb5e47eSDong Aisheng mclk = 512 * rate;
36fcb5e47eSDong Aisheng break;
37fcb5e47eSDong Aisheng }
38fcb5e47eSDong Aisheng
39fcb5e47eSDong Aisheng /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
40fcb5e47eSDong Aisheng ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
41d66a5b9cSLothar Waßmann if (ret) {
42d66a5b9cSLothar Waßmann dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
43d66a5b9cSLothar Waßmann mclk / 1000000, mclk / 1000 % 1000);
44fcb5e47eSDong Aisheng return ret;
45d66a5b9cSLothar Waßmann }
46fcb5e47eSDong Aisheng
47fcb5e47eSDong Aisheng /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
48fcb5e47eSDong Aisheng ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
49d66a5b9cSLothar Waßmann if (ret) {
50d66a5b9cSLothar Waßmann dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
51d66a5b9cSLothar Waßmann mclk / 1000000, mclk / 1000 % 1000);
52fcb5e47eSDong Aisheng return ret;
53d66a5b9cSLothar Waßmann }
54fcb5e47eSDong Aisheng
55fcb5e47eSDong Aisheng return 0;
56fcb5e47eSDong Aisheng }
57fcb5e47eSDong Aisheng
589b6fdef6SJulia Lawall static const struct snd_soc_ops mxs_sgtl5000_hifi_ops = {
59fcb5e47eSDong Aisheng .hw_params = mxs_sgtl5000_hw_params,
60fcb5e47eSDong Aisheng };
61fcb5e47eSDong Aisheng
62bc6a5d64SLars-Peter Clausen #define MXS_SGTL5000_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \
63bc6a5d64SLars-Peter Clausen SND_SOC_DAIFMT_CBS_CFS)
64bc6a5d64SLars-Peter Clausen
6549cb4488SKuninori Morimoto
6649cb4488SKuninori Morimoto SND_SOC_DAILINK_DEFS(hifi_tx,
6749cb4488SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()),
68802e8ed9SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
69802e8ed9SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()));
7049cb4488SKuninori Morimoto
7149cb4488SKuninori Morimoto SND_SOC_DAILINK_DEFS(hifi_rx,
7249cb4488SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()),
73802e8ed9SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "sgtl5000")),
74802e8ed9SKuninori Morimoto DAILINK_COMP_ARRAY(COMP_EMPTY()));
7549cb4488SKuninori Morimoto
76fcb5e47eSDong Aisheng static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
77fcb5e47eSDong Aisheng {
7878a262c8SDong Aisheng .name = "HiFi Tx",
79fcb5e47eSDong Aisheng .stream_name = "HiFi Playback",
80bc6a5d64SLars-Peter Clausen .dai_fmt = MXS_SGTL5000_DAI_FMT,
81fcb5e47eSDong Aisheng .ops = &mxs_sgtl5000_hifi_ops,
82a90e6053SFabio Estevam .playback_only = true,
8349cb4488SKuninori Morimoto SND_SOC_DAILINK_REG(hifi_tx),
8478a262c8SDong Aisheng }, {
8578a262c8SDong Aisheng .name = "HiFi Rx",
8678a262c8SDong Aisheng .stream_name = "HiFi Capture",
87bc6a5d64SLars-Peter Clausen .dai_fmt = MXS_SGTL5000_DAI_FMT,
8878a262c8SDong Aisheng .ops = &mxs_sgtl5000_hifi_ops,
89a90e6053SFabio Estevam .capture_only = true,
9049cb4488SKuninori Morimoto SND_SOC_DAILINK_REG(hifi_rx),
91fcb5e47eSDong Aisheng },
92fcb5e47eSDong Aisheng };
93fcb5e47eSDong Aisheng
94949293d4SChristian Fischer static const struct snd_soc_dapm_widget mxs_sgtl5000_dapm_widgets[] = {
95949293d4SChristian Fischer SND_SOC_DAPM_MIC("Mic Jack", NULL),
96949293d4SChristian Fischer SND_SOC_DAPM_LINE("Line In Jack", NULL),
97949293d4SChristian Fischer SND_SOC_DAPM_HP("Headphone Jack", NULL),
98949293d4SChristian Fischer SND_SOC_DAPM_SPK("Line Out Jack", NULL),
99949293d4SChristian Fischer SND_SOC_DAPM_SPK("Ext Spk", NULL),
100949293d4SChristian Fischer };
101949293d4SChristian Fischer
102fcb5e47eSDong Aisheng static struct snd_soc_card mxs_sgtl5000 = {
103fcb5e47eSDong Aisheng .name = "mxs_sgtl5000",
104a5b68348SLothar Waßmann .owner = THIS_MODULE,
105fcb5e47eSDong Aisheng .dai_link = mxs_sgtl5000_dai,
106fcb5e47eSDong Aisheng .num_links = ARRAY_SIZE(mxs_sgtl5000_dai),
107fcb5e47eSDong Aisheng };
108fcb5e47eSDong Aisheng
mxs_sgtl5000_probe(struct platform_device * pdev)10938f2b8cbSFabio Estevam static int mxs_sgtl5000_probe(struct platform_device *pdev)
110e968194bSShawn Guo {
11138f2b8cbSFabio Estevam struct snd_soc_card *card = &mxs_sgtl5000;
11238f2b8cbSFabio Estevam int ret, i;
113e968194bSShawn Guo struct device_node *np = pdev->dev.of_node;
114e968194bSShawn Guo struct device_node *saif_np[2], *codec_np;
115e968194bSShawn Guo
116e968194bSShawn Guo saif_np[0] = of_parse_phandle(np, "saif-controllers", 0);
117e968194bSShawn Guo saif_np[1] = of_parse_phandle(np, "saif-controllers", 1);
118e968194bSShawn Guo codec_np = of_parse_phandle(np, "audio-codec", 0);
119e968194bSShawn Guo if (!saif_np[0] || !saif_np[1] || !codec_np) {
120e968194bSShawn Guo dev_err(&pdev->dev, "phandle missing or invalid\n");
1216ae0a4d8SMiaoqian Lin of_node_put(codec_np);
1226ae0a4d8SMiaoqian Lin of_node_put(saif_np[0]);
1236ae0a4d8SMiaoqian Lin of_node_put(saif_np[1]);
124e968194bSShawn Guo return -EINVAL;
125e968194bSShawn Guo }
126e968194bSShawn Guo
127e968194bSShawn Guo for (i = 0; i < 2; i++) {
12849cb4488SKuninori Morimoto mxs_sgtl5000_dai[i].codecs->name = NULL;
12949cb4488SKuninori Morimoto mxs_sgtl5000_dai[i].codecs->of_node = codec_np;
13049cb4488SKuninori Morimoto mxs_sgtl5000_dai[i].cpus->dai_name = NULL;
13149cb4488SKuninori Morimoto mxs_sgtl5000_dai[i].cpus->of_node = saif_np[i];
132802e8ed9SKuninori Morimoto mxs_sgtl5000_dai[i].platforms->name = NULL;
133802e8ed9SKuninori Morimoto mxs_sgtl5000_dai[i].platforms->of_node = saif_np[i];
134e968194bSShawn Guo }
135e968194bSShawn Guo
136e968194bSShawn Guo of_node_put(codec_np);
137e968194bSShawn Guo of_node_put(saif_np[0]);
138e968194bSShawn Guo of_node_put(saif_np[1]);
139e968194bSShawn Guo
140fcb5e47eSDong Aisheng /*
141fcb5e47eSDong Aisheng * Set an init clock(11.28Mhz) for sgtl5000 initialization(i2c r/w).
142fcb5e47eSDong Aisheng * The Sgtl5000 sysclk is derived from saif0 mclk and it's range
143fcb5e47eSDong Aisheng * should be >= 8MHz and <= 27M.
144fcb5e47eSDong Aisheng */
145fcb5e47eSDong Aisheng ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
146d66a5b9cSLothar Waßmann if (ret) {
147d66a5b9cSLothar Waßmann dev_err(&pdev->dev, "failed to get mclk\n");
148fcb5e47eSDong Aisheng return ret;
149d66a5b9cSLothar Waßmann }
150fcb5e47eSDong Aisheng
151fcb5e47eSDong Aisheng card->dev = &pdev->dev;
152fcb5e47eSDong Aisheng
1531e108e60SRob Herring if (of_property_present(np, "audio-routing")) {
154949293d4SChristian Fischer card->dapm_widgets = mxs_sgtl5000_dapm_widgets;
155949293d4SChristian Fischer card->num_dapm_widgets = ARRAY_SIZE(mxs_sgtl5000_dapm_widgets);
156949293d4SChristian Fischer
157949293d4SChristian Fischer ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
158949293d4SChristian Fischer if (ret) {
159949293d4SChristian Fischer dev_err(&pdev->dev, "failed to parse audio-routing (%d)\n",
160949293d4SChristian Fischer ret);
161949293d4SChristian Fischer return ret;
162949293d4SChristian Fischer }
163949293d4SChristian Fischer }
164949293d4SChristian Fischer
1655b73de19SAxel Lin ret = devm_snd_soc_register_card(&pdev->dev, card);
1667a17f6a9SKuninori Morimoto if (ret)
1677a17f6a9SKuninori Morimoto return dev_err_probe(&pdev->dev, ret, "snd_soc_register_card failed\n");
168fcb5e47eSDong Aisheng
169fcb5e47eSDong Aisheng return 0;
170fcb5e47eSDong Aisheng }
171fcb5e47eSDong Aisheng
mxs_sgtl5000_remove(struct platform_device * pdev)172*75c1ed91SUwe Kleine-König static void mxs_sgtl5000_remove(struct platform_device *pdev)
173fcb5e47eSDong Aisheng {
174fcb5e47eSDong Aisheng mxs_saif_put_mclk(0);
175fcb5e47eSDong Aisheng }
176fcb5e47eSDong Aisheng
177e968194bSShawn Guo static const struct of_device_id mxs_sgtl5000_dt_ids[] = {
178e968194bSShawn Guo { .compatible = "fsl,mxs-audio-sgtl5000", },
179e968194bSShawn Guo { /* sentinel */ }
180e968194bSShawn Guo };
181e968194bSShawn Guo MODULE_DEVICE_TABLE(of, mxs_sgtl5000_dt_ids);
182e968194bSShawn Guo
183fcb5e47eSDong Aisheng static struct platform_driver mxs_sgtl5000_audio_driver = {
184fcb5e47eSDong Aisheng .driver = {
185fcb5e47eSDong Aisheng .name = "mxs-sgtl5000",
186e968194bSShawn Guo .of_match_table = mxs_sgtl5000_dt_ids,
187fcb5e47eSDong Aisheng },
188fcb5e47eSDong Aisheng .probe = mxs_sgtl5000_probe,
189*75c1ed91SUwe Kleine-König .remove_new = mxs_sgtl5000_remove,
190fcb5e47eSDong Aisheng };
191fcb5e47eSDong Aisheng
19285aa0960SAxel Lin module_platform_driver(mxs_sgtl5000_audio_driver);
193fcb5e47eSDong Aisheng
194fcb5e47eSDong Aisheng MODULE_AUTHOR("Freescale Semiconductor, Inc.");
195fcb5e47eSDong Aisheng MODULE_DESCRIPTION("MXS ALSA SoC Machine driver");
196fcb5e47eSDong Aisheng MODULE_LICENSE("GPL");
1979fd369b1SLothar Waßmann MODULE_ALIAS("platform:mxs-sgtl5000");
198