xref: /openbmc/linux/sound/soc/mxs/mxs-sgtl5000.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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