xref: /OK3568_Linux_fs/kernel/sound/soc/intel/boards/bytcht_nocodec.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  *  bytcht_nocodec.c - ASoc Machine driver for MinnowBoard Max and Up
4*4882a593Smuzhiyun  *  to make I2S signals observable on the Low-Speed connector. Audio codec
5*4882a593Smuzhiyun  *  is not managed by ASoC/DAPM
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  *  Copyright (C) 2015-2017 Intel Corp
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10*4882a593Smuzhiyun  *
11*4882a593Smuzhiyun  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12*4882a593Smuzhiyun  */
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <sound/pcm.h>
16*4882a593Smuzhiyun #include <sound/pcm_params.h>
17*4882a593Smuzhiyun #include <sound/soc.h>
18*4882a593Smuzhiyun #include "../atom/sst-atom-controls.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun static const struct snd_soc_dapm_widget widgets[] = {
21*4882a593Smuzhiyun 	SND_SOC_DAPM_MIC("Mic", NULL),
22*4882a593Smuzhiyun 	SND_SOC_DAPM_SPK("Speaker", NULL),
23*4882a593Smuzhiyun };
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun static const struct snd_kcontrol_new controls[] = {
26*4882a593Smuzhiyun 	SOC_DAPM_PIN_SWITCH("Mic"),
27*4882a593Smuzhiyun 	SOC_DAPM_PIN_SWITCH("Speaker"),
28*4882a593Smuzhiyun };
29*4882a593Smuzhiyun 
30*4882a593Smuzhiyun static const struct snd_soc_dapm_route audio_map[] = {
31*4882a593Smuzhiyun 	{"ssp2 Tx", NULL, "codec_out0"},
32*4882a593Smuzhiyun 	{"ssp2 Tx", NULL, "codec_out1"},
33*4882a593Smuzhiyun 	{"codec_in0", NULL, "ssp2 Rx"},
34*4882a593Smuzhiyun 	{"codec_in1", NULL, "ssp2 Rx"},
35*4882a593Smuzhiyun 
36*4882a593Smuzhiyun 	{"ssp2 Rx", NULL, "Mic"},
37*4882a593Smuzhiyun 	{"Speaker", NULL, "ssp2 Tx"},
38*4882a593Smuzhiyun };
39*4882a593Smuzhiyun 
codec_fixup(struct snd_soc_pcm_runtime * rtd,struct snd_pcm_hw_params * params)40*4882a593Smuzhiyun static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
41*4882a593Smuzhiyun 			    struct snd_pcm_hw_params *params)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun 	struct snd_interval *rate = hw_param_interval(params,
44*4882a593Smuzhiyun 			SNDRV_PCM_HW_PARAM_RATE);
45*4882a593Smuzhiyun 	struct snd_interval *channels = hw_param_interval(params,
46*4882a593Smuzhiyun 						SNDRV_PCM_HW_PARAM_CHANNELS);
47*4882a593Smuzhiyun 	int ret;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	/* The DSP will convert the FE rate to 48k, stereo, 24bits */
50*4882a593Smuzhiyun 	rate->min = rate->max = 48000;
51*4882a593Smuzhiyun 	channels->min = channels->max = 2;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	/* set SSP2 to 24-bit */
54*4882a593Smuzhiyun 	params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	/*
57*4882a593Smuzhiyun 	 * Default mode for SSP configuration is TDM 4 slot, override config
58*4882a593Smuzhiyun 	 * with explicit setting to I2S 2ch 24-bit. The word length is set with
59*4882a593Smuzhiyun 	 * dai_set_tdm_slot() since there is no other API exposed
60*4882a593Smuzhiyun 	 */
61*4882a593Smuzhiyun 	ret = snd_soc_dai_set_fmt(asoc_rtd_to_cpu(rtd, 0),
62*4882a593Smuzhiyun 				  SND_SOC_DAIFMT_I2S     |
63*4882a593Smuzhiyun 				  SND_SOC_DAIFMT_NB_NF   |
64*4882a593Smuzhiyun 				  SND_SOC_DAIFMT_CBS_CFS);
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	if (ret < 0) {
67*4882a593Smuzhiyun 		dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
68*4882a593Smuzhiyun 		return ret;
69*4882a593Smuzhiyun 	}
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	ret = snd_soc_dai_set_tdm_slot(asoc_rtd_to_cpu(rtd, 0), 0x3, 0x3, 2, 24);
72*4882a593Smuzhiyun 	if (ret < 0) {
73*4882a593Smuzhiyun 		dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
74*4882a593Smuzhiyun 		return ret;
75*4882a593Smuzhiyun 	}
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	return 0;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun static const unsigned int rates_48000[] = {
81*4882a593Smuzhiyun 	48000,
82*4882a593Smuzhiyun };
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun static const struct snd_pcm_hw_constraint_list constraints_48000 = {
85*4882a593Smuzhiyun 	.count = ARRAY_SIZE(rates_48000),
86*4882a593Smuzhiyun 	.list  = rates_48000,
87*4882a593Smuzhiyun };
88*4882a593Smuzhiyun 
aif1_startup(struct snd_pcm_substream * substream)89*4882a593Smuzhiyun static int aif1_startup(struct snd_pcm_substream *substream)
90*4882a593Smuzhiyun {
91*4882a593Smuzhiyun 	return snd_pcm_hw_constraint_list(substream->runtime, 0,
92*4882a593Smuzhiyun 			SNDRV_PCM_HW_PARAM_RATE,
93*4882a593Smuzhiyun 			&constraints_48000);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun static struct snd_soc_ops aif1_ops = {
97*4882a593Smuzhiyun 	.startup = aif1_startup,
98*4882a593Smuzhiyun };
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(dummy,
101*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_DUMMY()));
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(media,
104*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CPU("media-cpu-dai")));
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(deepbuffer,
107*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CPU("deepbuffer-cpu-dai")));
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(ssp2_port,
110*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_CPU("ssp2-port")));
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun SND_SOC_DAILINK_DEF(platform,
113*4882a593Smuzhiyun 	DAILINK_COMP_ARRAY(COMP_PLATFORM("sst-mfld-platform")));
114*4882a593Smuzhiyun 
115*4882a593Smuzhiyun static struct snd_soc_dai_link dais[] = {
116*4882a593Smuzhiyun 	[MERR_DPCM_AUDIO] = {
117*4882a593Smuzhiyun 		.name = "Audio Port",
118*4882a593Smuzhiyun 		.stream_name = "Audio",
119*4882a593Smuzhiyun 		.ignore_suspend = 1,
120*4882a593Smuzhiyun 		.nonatomic = true,
121*4882a593Smuzhiyun 		.dynamic = 1,
122*4882a593Smuzhiyun 		.dpcm_playback = 1,
123*4882a593Smuzhiyun 		.dpcm_capture = 1,
124*4882a593Smuzhiyun 		.ops = &aif1_ops,
125*4882a593Smuzhiyun 		SND_SOC_DAILINK_REG(media, dummy, platform),
126*4882a593Smuzhiyun 	},
127*4882a593Smuzhiyun 	[MERR_DPCM_DEEP_BUFFER] = {
128*4882a593Smuzhiyun 		.name = "Deep-Buffer Audio Port",
129*4882a593Smuzhiyun 		.stream_name = "Deep-Buffer Audio",
130*4882a593Smuzhiyun 		.ignore_suspend = 1,
131*4882a593Smuzhiyun 		.nonatomic = true,
132*4882a593Smuzhiyun 		.dynamic = 1,
133*4882a593Smuzhiyun 		.dpcm_playback = 1,
134*4882a593Smuzhiyun 		.ops = &aif1_ops,
135*4882a593Smuzhiyun 		SND_SOC_DAILINK_REG(deepbuffer, dummy, platform),
136*4882a593Smuzhiyun 	},
137*4882a593Smuzhiyun 	/* CODEC<->CODEC link */
138*4882a593Smuzhiyun 	/* back ends */
139*4882a593Smuzhiyun 	{
140*4882a593Smuzhiyun 		.name = "SSP2-LowSpeed Connector",
141*4882a593Smuzhiyun 		.id = 0,
142*4882a593Smuzhiyun 		.no_pcm = 1,
143*4882a593Smuzhiyun 		.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
144*4882a593Smuzhiyun 						| SND_SOC_DAIFMT_CBS_CFS,
145*4882a593Smuzhiyun 		.be_hw_params_fixup = codec_fixup,
146*4882a593Smuzhiyun 		.ignore_suspend = 1,
147*4882a593Smuzhiyun 		.nonatomic = true,
148*4882a593Smuzhiyun 		.dpcm_playback = 1,
149*4882a593Smuzhiyun 		.dpcm_capture = 1,
150*4882a593Smuzhiyun 		SND_SOC_DAILINK_REG(ssp2_port, dummy, platform),
151*4882a593Smuzhiyun 	},
152*4882a593Smuzhiyun };
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun /* SoC card */
155*4882a593Smuzhiyun static struct snd_soc_card bytcht_nocodec_card = {
156*4882a593Smuzhiyun 	.name = "bytcht-nocodec",
157*4882a593Smuzhiyun 	.owner = THIS_MODULE,
158*4882a593Smuzhiyun 	.dai_link = dais,
159*4882a593Smuzhiyun 	.num_links = ARRAY_SIZE(dais),
160*4882a593Smuzhiyun 	.dapm_widgets = widgets,
161*4882a593Smuzhiyun 	.num_dapm_widgets = ARRAY_SIZE(widgets),
162*4882a593Smuzhiyun 	.dapm_routes = audio_map,
163*4882a593Smuzhiyun 	.num_dapm_routes = ARRAY_SIZE(audio_map),
164*4882a593Smuzhiyun 	.controls = controls,
165*4882a593Smuzhiyun 	.num_controls = ARRAY_SIZE(controls),
166*4882a593Smuzhiyun 	.fully_routed = true,
167*4882a593Smuzhiyun };
168*4882a593Smuzhiyun 
snd_bytcht_nocodec_mc_probe(struct platform_device * pdev)169*4882a593Smuzhiyun static int snd_bytcht_nocodec_mc_probe(struct platform_device *pdev)
170*4882a593Smuzhiyun {
171*4882a593Smuzhiyun 	int ret_val = 0;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	/* register the soc card */
174*4882a593Smuzhiyun 	bytcht_nocodec_card.dev = &pdev->dev;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	ret_val = devm_snd_soc_register_card(&pdev->dev, &bytcht_nocodec_card);
177*4882a593Smuzhiyun 
178*4882a593Smuzhiyun 	if (ret_val) {
179*4882a593Smuzhiyun 		dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
180*4882a593Smuzhiyun 			ret_val);
181*4882a593Smuzhiyun 		return ret_val;
182*4882a593Smuzhiyun 	}
183*4882a593Smuzhiyun 	platform_set_drvdata(pdev, &bytcht_nocodec_card);
184*4882a593Smuzhiyun 	return ret_val;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun static struct platform_driver snd_bytcht_nocodec_mc_driver = {
188*4882a593Smuzhiyun 	.driver = {
189*4882a593Smuzhiyun 		.name = "bytcht_nocodec",
190*4882a593Smuzhiyun 	},
191*4882a593Smuzhiyun 	.probe = snd_bytcht_nocodec_mc_probe,
192*4882a593Smuzhiyun };
193*4882a593Smuzhiyun module_platform_driver(snd_bytcht_nocodec_mc_driver);
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Nocodec Machine driver");
196*4882a593Smuzhiyun MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>");
197*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
198*4882a593Smuzhiyun MODULE_ALIAS("platform:bytcht_nocodec");
199