xref: /openbmc/linux/sound/soc/fsl/mpc5200_psc_i2s.c (revision 295dc79c)
1 // SPDX-License-Identifier: GPL-2.0-only
2 //
3 // Freescale MPC5200 PSC in I2S mode
4 // ALSA SoC Digital Audio Interface (DAI) driver
5 //
6 // Copyright (C) 2008 Secret Lab Technologies Ltd.
7 // Copyright (C) 2009 Jon Smirl, Digispeaker
8 
9 #include <linux/module.h>
10 #include <linux/of_device.h>
11 #include <linux/of_platform.h>
12 
13 #include <sound/pcm.h>
14 #include <sound/pcm_params.h>
15 #include <sound/soc.h>
16 
17 #include <asm/mpc52xx_psc.h>
18 
19 #include "mpc5200_dma.h"
20 
21 /**
22  * PSC_I2S_RATES: sample rates supported by the I2S
23  *
24  * This driver currently only supports the PSC running in I2S slave mode,
25  * which means the codec determines the sample rate.  Therefore, we tell
26  * ALSA that we support all rates and let the codec driver decide what rates
27  * are really supported.
28  */
29 #define PSC_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
30 
31 /**
32  * PSC_I2S_FORMATS: audio formats supported by the PSC I2S mode
33  */
34 #define PSC_I2S_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
35 			 SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
36 
psc_i2s_hw_params(struct snd_pcm_substream * substream,struct snd_pcm_hw_params * params,struct snd_soc_dai * dai)37 static int psc_i2s_hw_params(struct snd_pcm_substream *substream,
38 				 struct snd_pcm_hw_params *params,
39 				 struct snd_soc_dai *dai)
40 {
41 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
42 	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(rtd, 0));
43 	u32 mode;
44 
45 	dev_dbg(psc_dma->dev, "%s(substream=%p) p_size=%i p_bytes=%i"
46 		" periods=%i buffer_size=%i  buffer_bytes=%i\n",
47 		__func__, substream, params_period_size(params),
48 		params_period_bytes(params), params_periods(params),
49 		params_buffer_size(params), params_buffer_bytes(params));
50 
51 	switch (params_format(params)) {
52 	case SNDRV_PCM_FORMAT_S8:
53 		mode = MPC52xx_PSC_SICR_SIM_CODEC_8;
54 		break;
55 	case SNDRV_PCM_FORMAT_S16_BE:
56 		mode = MPC52xx_PSC_SICR_SIM_CODEC_16;
57 		break;
58 	case SNDRV_PCM_FORMAT_S24_BE:
59 		mode = MPC52xx_PSC_SICR_SIM_CODEC_24;
60 		break;
61 	case SNDRV_PCM_FORMAT_S32_BE:
62 		mode = MPC52xx_PSC_SICR_SIM_CODEC_32;
63 		break;
64 	default:
65 		dev_dbg(psc_dma->dev, "invalid format\n");
66 		return -EINVAL;
67 	}
68 	out_be32(&psc_dma->psc_regs->sicr, psc_dma->sicr | mode);
69 
70 	return 0;
71 }
72 
73 /**
74  * psc_i2s_set_sysclk: set the clock frequency and direction
75  *
76  * This function is called by the machine driver to tell us what the clock
77  * frequency and direction are.
78  *
79  * Currently, we only support operating as a clock slave (SND_SOC_CLOCK_IN),
80  * and we don't care about the frequency.  Return an error if the direction
81  * is not SND_SOC_CLOCK_IN.
82  *
83  * @clk_id: reserved, should be zero
84  * @freq: the frequency of the given clock ID, currently ignored
85  * @dir: SND_SOC_CLOCK_IN (clock slave) or SND_SOC_CLOCK_OUT (clock master)
86  */
psc_i2s_set_sysclk(struct snd_soc_dai * cpu_dai,int clk_id,unsigned int freq,int dir)87 static int psc_i2s_set_sysclk(struct snd_soc_dai *cpu_dai,
88 			      int clk_id, unsigned int freq, int dir)
89 {
90 	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai);
91 	dev_dbg(psc_dma->dev, "psc_i2s_set_sysclk(cpu_dai=%p, dir=%i)\n",
92 				cpu_dai, dir);
93 	return (dir == SND_SOC_CLOCK_IN) ? 0 : -EINVAL;
94 }
95 
96 /**
97  * psc_i2s_set_fmt: set the serial format.
98  *
99  * This function is called by the machine driver to tell us what serial
100  * format to use.
101  *
102  * This driver only supports I2S mode.  Return an error if the format is
103  * not SND_SOC_DAIFMT_I2S.
104  *
105  * @format: one of SND_SOC_DAIFMT_xxx
106  */
psc_i2s_set_fmt(struct snd_soc_dai * cpu_dai,unsigned int format)107 static int psc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int format)
108 {
109 	struct psc_dma *psc_dma = snd_soc_dai_get_drvdata(cpu_dai);
110 	dev_dbg(psc_dma->dev, "psc_i2s_set_fmt(cpu_dai=%p, format=%i)\n",
111 				cpu_dai, format);
112 	return (format == SND_SOC_DAIFMT_I2S) ? 0 : -EINVAL;
113 }
114 
115 /* ---------------------------------------------------------------------
116  * ALSA SoC Bindings
117  *
118  * - Digital Audio Interface (DAI) template
119  * - create/destroy dai hooks
120  */
121 
122 /**
123  * psc_i2s_dai_template: template CPU Digital Audio Interface
124  */
125 static const struct snd_soc_dai_ops psc_i2s_dai_ops = {
126 	.hw_params	= psc_i2s_hw_params,
127 	.set_sysclk	= psc_i2s_set_sysclk,
128 	.set_fmt	= psc_i2s_set_fmt,
129 };
130 
131 static struct snd_soc_dai_driver psc_i2s_dai[] = {{
132 	.name = "mpc5200-psc-i2s.0",
133 	.playback = {
134 		.stream_name = "I2S Playback",
135 		.channels_min = 2,
136 		.channels_max = 2,
137 		.rates = PSC_I2S_RATES,
138 		.formats = PSC_I2S_FORMATS,
139 	},
140 	.capture = {
141 		.stream_name = "I2S Capture",
142 		.channels_min = 2,
143 		.channels_max = 2,
144 		.rates = PSC_I2S_RATES,
145 		.formats = PSC_I2S_FORMATS,
146 	},
147 	.ops = &psc_i2s_dai_ops,
148 } };
149 
150 static const struct snd_soc_component_driver psc_i2s_component = {
151 	.name			= "mpc5200-i2s",
152 	.legacy_dai_naming	= 1,
153 };
154 
155 /* ---------------------------------------------------------------------
156  * OF platform bus binding code:
157  * - Probe/remove operations
158  * - OF device match table
159  */
psc_i2s_of_probe(struct platform_device * op)160 static int psc_i2s_of_probe(struct platform_device *op)
161 {
162 	int rc;
163 	struct psc_dma *psc_dma;
164 	struct mpc52xx_psc __iomem *regs;
165 
166 	rc = mpc5200_audio_dma_create(op);
167 	if (rc != 0)
168 		return rc;
169 
170 	rc = snd_soc_register_component(&op->dev, &psc_i2s_component,
171 					psc_i2s_dai, ARRAY_SIZE(psc_i2s_dai));
172 	if (rc != 0) {
173 		pr_err("Failed to register DAI\n");
174 		return rc;
175 	}
176 
177 	psc_dma = dev_get_drvdata(&op->dev);
178 	regs = psc_dma->psc_regs;
179 
180 	/* Configure the serial interface mode; defaulting to CODEC8 mode */
181 	psc_dma->sicr = MPC52xx_PSC_SICR_DTS1 | MPC52xx_PSC_SICR_I2S |
182 			MPC52xx_PSC_SICR_CLKPOL;
183 	out_be32(&psc_dma->psc_regs->sicr,
184 		 psc_dma->sicr | MPC52xx_PSC_SICR_SIM_CODEC_8);
185 
186 	/* Check for the codec handle.  If it is not present then we
187 	 * are done */
188 	if (!of_get_property(op->dev.of_node, "codec-handle", NULL))
189 		return 0;
190 
191 	/* Due to errata in the dma mode; need to line up enabling
192 	 * the transmitter with a transition on the frame sync
193 	 * line */
194 
195 	/* first make sure it is low */
196 	while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) != 0)
197 		;
198 	/* then wait for the transition to high */
199 	while ((in_8(&regs->ipcr_acr.ipcr) & 0x80) == 0)
200 		;
201 	/* Finally, enable the PSC.
202 	 * Receiver must always be enabled; even when we only want
203 	 * transmit.  (see 15.3.2.3 of MPC5200B User's Guide) */
204 
205 	/* Go */
206 	out_8(&psc_dma->psc_regs->command,
207 			MPC52xx_PSC_TX_ENABLE | MPC52xx_PSC_RX_ENABLE);
208 
209 	return 0;
210 
211 }
212 
psc_i2s_of_remove(struct platform_device * op)213 static void psc_i2s_of_remove(struct platform_device *op)
214 {
215 	mpc5200_audio_dma_destroy(op);
216 	snd_soc_unregister_component(&op->dev);
217 }
218 
219 /* Match table for of_platform binding */
220 static const struct of_device_id psc_i2s_match[] = {
221 	{ .compatible = "fsl,mpc5200-psc-i2s", },
222 	{ .compatible = "fsl,mpc5200b-psc-i2s", },
223 	{}
224 };
225 MODULE_DEVICE_TABLE(of, psc_i2s_match);
226 
227 static struct platform_driver psc_i2s_driver = {
228 	.probe = psc_i2s_of_probe,
229 	.remove_new = psc_i2s_of_remove,
230 	.driver = {
231 		.name = "mpc5200-psc-i2s",
232 		.of_match_table = psc_i2s_match,
233 	},
234 };
235 
236 module_platform_driver(psc_i2s_driver);
237 
238 MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");
239 MODULE_DESCRIPTION("Freescale MPC5200 PSC in I2S mode ASoC Driver");
240 MODULE_LICENSE("GPL");
241 
242