blob: c4a42311167371446cfd5e24b2c23c418e796627 [file] [log] [blame]
Xiubo Li43550822013-12-17 11:24:38 +08001/*
2 * Freescale ALSA SoC Digital Audio Interface (SAI) driver.
3 *
4 * Copyright 2012-2013 Freescale Semiconductor, Inc.
5 *
6 * This program is free software, you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 2 of the License, or(at your
9 * option) any later version.
10 *
11 */
12
13#include <linux/clk.h>
14#include <linux/delay.h>
15#include <linux/dmaengine.h>
16#include <linux/module.h>
17#include <linux/of_address.h>
Xiubo Li78957fc2014-02-08 14:38:28 +080018#include <linux/regmap.h>
Xiubo Li43550822013-12-17 11:24:38 +080019#include <linux/slab.h>
20#include <sound/core.h>
21#include <sound/dmaengine_pcm.h>
22#include <sound/pcm_params.h>
23
24#include "fsl_sai.h"
25
Xiubo Li43550822013-12-17 11:24:38 +080026static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
27 int clk_id, unsigned int freq, int fsl_dir)
28{
Xiubo Li43550822013-12-17 11:24:38 +080029 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Nicolin Chen4e3a99f2013-12-20 16:41:05 +080030 u32 val_cr2, reg_cr2;
Xiubo Li43550822013-12-17 11:24:38 +080031
32 if (fsl_dir == FSL_FMT_TRANSMITTER)
33 reg_cr2 = FSL_SAI_TCR2;
34 else
35 reg_cr2 = FSL_SAI_RCR2;
36
Xiubo Li78957fc2014-02-08 14:38:28 +080037 regmap_read(sai->regmap, reg_cr2, &val_cr2);
38
Xiubo Li633ff8f2014-01-08 16:13:05 +080039 val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
40
Xiubo Li43550822013-12-17 11:24:38 +080041 switch (clk_id) {
42 case FSL_SAI_CLK_BUS:
Xiubo Li43550822013-12-17 11:24:38 +080043 val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
44 break;
45 case FSL_SAI_CLK_MAST1:
Xiubo Li43550822013-12-17 11:24:38 +080046 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
47 break;
48 case FSL_SAI_CLK_MAST2:
Xiubo Li43550822013-12-17 11:24:38 +080049 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
50 break;
51 case FSL_SAI_CLK_MAST3:
Xiubo Li43550822013-12-17 11:24:38 +080052 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
53 break;
54 default:
55 return -EINVAL;
56 }
Xiubo Li633ff8f2014-01-08 16:13:05 +080057
Xiubo Li78957fc2014-02-08 14:38:28 +080058 regmap_write(sai->regmap, reg_cr2, val_cr2);
Xiubo Li43550822013-12-17 11:24:38 +080059
60 return 0;
61}
62
63static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
64 int clk_id, unsigned int freq, int dir)
65{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +080066 int ret;
Xiubo Li43550822013-12-17 11:24:38 +080067
68 if (dir == SND_SOC_CLOCK_IN)
69 return 0;
70
Xiubo Li43550822013-12-17 11:24:38 +080071 ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
72 FSL_FMT_TRANSMITTER);
73 if (ret) {
Nicolin Chen190af122013-12-20 16:41:04 +080074 dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
Xiubo Li78957fc2014-02-08 14:38:28 +080075 return ret;
Xiubo Li43550822013-12-17 11:24:38 +080076 }
77
78 ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
79 FSL_FMT_RECEIVER);
Xiubo Li78957fc2014-02-08 14:38:28 +080080 if (ret)
Nicolin Chen190af122013-12-20 16:41:04 +080081 dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
Xiubo Li43550822013-12-17 11:24:38 +080082
Nicolin Chen1fb2d9d2013-12-20 16:41:00 +080083 return ret;
Xiubo Li43550822013-12-17 11:24:38 +080084}
85
86static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
87 unsigned int fmt, int fsl_dir)
88{
Xiubo Li43550822013-12-17 11:24:38 +080089 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Lie5d0fa92013-12-25 12:40:04 +080090 u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
Xiubo Li43550822013-12-17 11:24:38 +080091
92 if (fsl_dir == FSL_FMT_TRANSMITTER) {
93 reg_cr2 = FSL_SAI_TCR2;
Xiubo Li43550822013-12-17 11:24:38 +080094 reg_cr4 = FSL_SAI_TCR4;
95 } else {
96 reg_cr2 = FSL_SAI_RCR2;
Xiubo Li43550822013-12-17 11:24:38 +080097 reg_cr4 = FSL_SAI_RCR4;
98 }
99
Xiubo Li78957fc2014-02-08 14:38:28 +0800100 regmap_read(sai->regmap, reg_cr2, &val_cr2);
101 regmap_read(sai->regmap, reg_cr4, &val_cr4);
Xiubo Li43550822013-12-17 11:24:38 +0800102
103 if (sai->big_endian_data)
Xiubo Li43550822013-12-17 11:24:38 +0800104 val_cr4 &= ~FSL_SAI_CR4_MF;
Xiubo Li72aa62b2013-12-31 15:33:22 +0800105 else
106 val_cr4 |= FSL_SAI_CR4_MF;
Xiubo Li43550822013-12-17 11:24:38 +0800107
Xiubo Li13cde092014-02-25 17:54:51 +0800108 /* DAI mode */
Xiubo Li43550822013-12-17 11:24:38 +0800109 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
110 case SND_SOC_DAIFMT_I2S:
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800111 /*
112 * Frame low, 1clk before data, one word length for frame sync,
113 * frame sync starts one serial clock cycle earlier,
114 * that is, together with the last bit of the previous
115 * data word.
116 */
Xiubo Li13cde092014-02-25 17:54:51 +0800117 val_cr2 &= ~FSL_SAI_CR2_BCP;
118 val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP;
Xiubo Li43550822013-12-17 11:24:38 +0800119 break;
Xiubo Li13cde092014-02-25 17:54:51 +0800120 case SND_SOC_DAIFMT_LEFT_J:
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800121 /*
122 * Frame high, one word length for frame sync,
123 * frame sync asserts with the first bit of the frame.
124 */
Xiubo Li13cde092014-02-25 17:54:51 +0800125 val_cr2 &= ~FSL_SAI_CR2_BCP;
126 val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
127 break;
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800128 case SND_SOC_DAIFMT_DSP_A:
129 /*
130 * Frame high, 1clk before data, one bit for frame sync,
131 * frame sync starts one serial clock cycle earlier,
132 * that is, together with the last bit of the previous
133 * data word.
134 */
135 val_cr2 &= ~FSL_SAI_CR2_BCP;
136 val_cr4 &= ~FSL_SAI_CR4_FSP;
137 val_cr4 |= FSL_SAI_CR4_FSE;
138 sai->is_dsp_mode = true;
139 break;
140 case SND_SOC_DAIFMT_DSP_B:
141 /*
142 * Frame high, one bit for frame sync,
143 * frame sync asserts with the first bit of the frame.
144 */
145 val_cr2 &= ~FSL_SAI_CR2_BCP;
146 val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
147 sai->is_dsp_mode = true;
148 break;
Xiubo Li13cde092014-02-25 17:54:51 +0800149 case SND_SOC_DAIFMT_RIGHT_J:
150 /* To be done */
Xiubo Li43550822013-12-17 11:24:38 +0800151 default:
152 return -EINVAL;
153 }
154
Xiubo Li13cde092014-02-25 17:54:51 +0800155 /* DAI clock inversion */
Xiubo Li43550822013-12-17 11:24:38 +0800156 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
157 case SND_SOC_DAIFMT_IB_IF:
Xiubo Li13cde092014-02-25 17:54:51 +0800158 /* Invert both clocks */
159 val_cr2 ^= FSL_SAI_CR2_BCP;
160 val_cr4 ^= FSL_SAI_CR4_FSP;
Xiubo Li43550822013-12-17 11:24:38 +0800161 break;
162 case SND_SOC_DAIFMT_IB_NF:
Xiubo Li13cde092014-02-25 17:54:51 +0800163 /* Invert bit clock */
164 val_cr2 ^= FSL_SAI_CR2_BCP;
Xiubo Li43550822013-12-17 11:24:38 +0800165 break;
166 case SND_SOC_DAIFMT_NB_IF:
Xiubo Li13cde092014-02-25 17:54:51 +0800167 /* Invert frame clock */
168 val_cr4 ^= FSL_SAI_CR4_FSP;
Xiubo Li43550822013-12-17 11:24:38 +0800169 break;
170 case SND_SOC_DAIFMT_NB_NF:
Xiubo Li13cde092014-02-25 17:54:51 +0800171 /* Nothing to do for both normal cases */
Xiubo Li43550822013-12-17 11:24:38 +0800172 break;
173 default:
174 return -EINVAL;
175 }
176
Xiubo Li13cde092014-02-25 17:54:51 +0800177 /* DAI clock master masks */
Xiubo Li43550822013-12-17 11:24:38 +0800178 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
179 case SND_SOC_DAIFMT_CBS_CFS:
180 val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
181 val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
182 break;
183 case SND_SOC_DAIFMT_CBM_CFM:
184 val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
185 val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
186 break;
Xiubo Li13cde092014-02-25 17:54:51 +0800187 case SND_SOC_DAIFMT_CBS_CFM:
188 val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
189 val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
190 break;
191 case SND_SOC_DAIFMT_CBM_CFS:
192 val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
193 val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
194 break;
Xiubo Li43550822013-12-17 11:24:38 +0800195 default:
196 return -EINVAL;
197 }
198
Xiubo Li78957fc2014-02-08 14:38:28 +0800199 regmap_write(sai->regmap, reg_cr2, val_cr2);
200 regmap_write(sai->regmap, reg_cr4, val_cr4);
Xiubo Li43550822013-12-17 11:24:38 +0800201
202 return 0;
203}
204
205static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
206{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800207 int ret;
Xiubo Li43550822013-12-17 11:24:38 +0800208
Xiubo Li43550822013-12-17 11:24:38 +0800209 ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER);
210 if (ret) {
Nicolin Chen190af122013-12-20 16:41:04 +0800211 dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
Xiubo Li78957fc2014-02-08 14:38:28 +0800212 return ret;
Xiubo Li43550822013-12-17 11:24:38 +0800213 }
214
215 ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
Xiubo Li78957fc2014-02-08 14:38:28 +0800216 if (ret)
Nicolin Chen190af122013-12-20 16:41:04 +0800217 dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
Xiubo Li43550822013-12-17 11:24:38 +0800218
Nicolin Chen1fb2d9d2013-12-20 16:41:00 +0800219 return ret;
Xiubo Li43550822013-12-17 11:24:38 +0800220}
221
222static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
223 struct snd_pcm_hw_params *params,
224 struct snd_soc_dai *cpu_dai)
225{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800226 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Nicolin Chen1d700302013-12-20 16:41:01 +0800227 u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
Xiubo Li43550822013-12-17 11:24:38 +0800228 unsigned int channels = params_channels(params);
Nicolin Chen1d700302013-12-20 16:41:01 +0800229 u32 word_width = snd_pcm_format_width(params_format(params));
Xiubo Li43550822013-12-17 11:24:38 +0800230
231 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
232 reg_cr4 = FSL_SAI_TCR4;
233 reg_cr5 = FSL_SAI_TCR5;
234 reg_mr = FSL_SAI_TMR;
235 } else {
236 reg_cr4 = FSL_SAI_RCR4;
237 reg_cr5 = FSL_SAI_RCR5;
238 reg_mr = FSL_SAI_RMR;
239 }
240
Xiubo Li78957fc2014-02-08 14:38:28 +0800241 regmap_read(sai->regmap, reg_cr4, &val_cr4);
242 regmap_read(sai->regmap, reg_cr4, &val_cr5);
243
Xiubo Li43550822013-12-17 11:24:38 +0800244 val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
245 val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
246
Xiubo Li43550822013-12-17 11:24:38 +0800247 val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
248 val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
249 val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
250
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800251 if (!sai->is_dsp_mode)
252 val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
253
Xiubo Li43550822013-12-17 11:24:38 +0800254 val_cr5 |= FSL_SAI_CR5_WNW(word_width);
255 val_cr5 |= FSL_SAI_CR5_W0W(word_width);
256
Xiubo Li496a39d2013-12-31 15:33:21 +0800257 val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
Xiubo Li43550822013-12-17 11:24:38 +0800258 if (sai->big_endian_data)
Xiubo Li43550822013-12-17 11:24:38 +0800259 val_cr5 |= FSL_SAI_CR5_FBT(0);
Xiubo Li72aa62b2013-12-31 15:33:22 +0800260 else
261 val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
Xiubo Li43550822013-12-17 11:24:38 +0800262
263 val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
Nicolin Chend22e28c2013-12-20 16:41:02 +0800264 val_mr = ~0UL - ((1 << channels) - 1);
Xiubo Li43550822013-12-17 11:24:38 +0800265
Xiubo Li78957fc2014-02-08 14:38:28 +0800266 regmap_write(sai->regmap, reg_cr4, val_cr4);
267 regmap_write(sai->regmap, reg_cr5, val_cr5);
268 regmap_write(sai->regmap, reg_mr, val_mr);
Xiubo Li43550822013-12-17 11:24:38 +0800269
270 return 0;
271}
272
273static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
274 struct snd_soc_dai *cpu_dai)
275{
276 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Li78957fc2014-02-08 14:38:28 +0800277 u32 tcsr, rcsr;
Xiubo Li496a39d2013-12-31 15:33:21 +0800278
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800279 /*
280 * The transmitter bit clock and frame sync are to be
281 * used by both the transmitter and receiver.
282 */
Xiubo Li78957fc2014-02-08 14:38:28 +0800283 regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
284 ~FSL_SAI_CR2_SYNC);
285 regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
286 FSL_SAI_CR2_SYNC);
Xiubo Li496a39d2013-12-31 15:33:21 +0800287
Xiubo Li78957fc2014-02-08 14:38:28 +0800288 regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
289 regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
Xiubo Li43550822013-12-17 11:24:38 +0800290
291 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
292 tcsr |= FSL_SAI_CSR_FRDE;
293 rcsr &= ~FSL_SAI_CSR_FRDE;
294 } else {
295 rcsr |= FSL_SAI_CSR_FRDE;
296 tcsr &= ~FSL_SAI_CSR_FRDE;
297 }
298
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800299 /*
300 * It is recommended that the transmitter is the last enabled
301 * and the first disabled.
302 */
Xiubo Li43550822013-12-17 11:24:38 +0800303 switch (cmd) {
304 case SNDRV_PCM_TRIGGER_START:
305 case SNDRV_PCM_TRIGGER_RESUME:
306 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
307 tcsr |= FSL_SAI_CSR_TERE;
308 rcsr |= FSL_SAI_CSR_TERE;
Xiubo Lie5d0fa92013-12-25 12:40:04 +0800309
Xiubo Li78957fc2014-02-08 14:38:28 +0800310 regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
311 regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
Xiubo Li43550822013-12-17 11:24:38 +0800312 break;
Xiubo Li43550822013-12-17 11:24:38 +0800313 case SNDRV_PCM_TRIGGER_STOP:
314 case SNDRV_PCM_TRIGGER_SUSPEND:
315 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
316 if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
317 tcsr &= ~FSL_SAI_CSR_TERE;
318 rcsr &= ~FSL_SAI_CSR_TERE;
319 }
Xiubo Lie5d0fa92013-12-25 12:40:04 +0800320
Xiubo Li78957fc2014-02-08 14:38:28 +0800321 regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
322 regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
Xiubo Li43550822013-12-17 11:24:38 +0800323 break;
324 default:
325 return -EINVAL;
326 }
327
328 return 0;
329}
330
331static int fsl_sai_startup(struct snd_pcm_substream *substream,
332 struct snd_soc_dai *cpu_dai)
333{
Xiubo Li43550822013-12-17 11:24:38 +0800334 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Li78957fc2014-02-08 14:38:28 +0800335 u32 reg;
Xiubo Li43550822013-12-17 11:24:38 +0800336
Xiubo Li78957fc2014-02-08 14:38:28 +0800337 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
338 reg = FSL_SAI_TCR3;
339 else
340 reg = FSL_SAI_RCR3;
341
342 regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
343 FSL_SAI_CR3_TRCE);
344
345 return 0;
Xiubo Li43550822013-12-17 11:24:38 +0800346}
347
348static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
349 struct snd_soc_dai *cpu_dai)
350{
351 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Li78957fc2014-02-08 14:38:28 +0800352 u32 reg;
Xiubo Li43550822013-12-17 11:24:38 +0800353
Xiubo Li78957fc2014-02-08 14:38:28 +0800354 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
355 reg = FSL_SAI_TCR3;
356 else
357 reg = FSL_SAI_RCR3;
358
359 regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
360 ~FSL_SAI_CR3_TRCE);
Xiubo Li43550822013-12-17 11:24:38 +0800361}
362
363static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
364 .set_sysclk = fsl_sai_set_dai_sysclk,
365 .set_fmt = fsl_sai_set_dai_fmt,
366 .hw_params = fsl_sai_hw_params,
367 .trigger = fsl_sai_trigger,
368 .startup = fsl_sai_startup,
369 .shutdown = fsl_sai_shutdown,
370};
371
372static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
373{
374 struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
Xiubo Lie6dc12d2013-12-25 11:20:14 +0800375
Xiubo Li78957fc2014-02-08 14:38:28 +0800376 regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, 0x0);
377 regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, 0x0);
378 regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
379 FSL_SAI_MAXBURST_TX * 2);
380 regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
381 FSL_SAI_MAXBURST_RX - 1);
Xiubo Li43550822013-12-17 11:24:38 +0800382
Xiubo Lidd9f4062013-12-20 12:35:33 +0800383 snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
384 &sai->dma_params_rx);
Xiubo Li43550822013-12-17 11:24:38 +0800385
386 snd_soc_dai_set_drvdata(cpu_dai, sai);
387
388 return 0;
389}
390
Xiubo Li43550822013-12-17 11:24:38 +0800391static struct snd_soc_dai_driver fsl_sai_dai = {
392 .probe = fsl_sai_dai_probe,
Xiubo Li43550822013-12-17 11:24:38 +0800393 .playback = {
394 .channels_min = 1,
395 .channels_max = 2,
396 .rates = SNDRV_PCM_RATE_8000_96000,
397 .formats = FSL_SAI_FORMATS,
398 },
399 .capture = {
400 .channels_min = 1,
401 .channels_max = 2,
402 .rates = SNDRV_PCM_RATE_8000_96000,
403 .formats = FSL_SAI_FORMATS,
404 },
405 .ops = &fsl_sai_pcm_dai_ops,
406};
407
408static const struct snd_soc_component_driver fsl_component = {
409 .name = "fsl-sai",
410};
411
Xiubo Li78957fc2014-02-08 14:38:28 +0800412static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
413{
414 switch (reg) {
415 case FSL_SAI_TCSR:
416 case FSL_SAI_TCR1:
417 case FSL_SAI_TCR2:
418 case FSL_SAI_TCR3:
419 case FSL_SAI_TCR4:
420 case FSL_SAI_TCR5:
421 case FSL_SAI_TFR:
422 case FSL_SAI_TMR:
423 case FSL_SAI_RCSR:
424 case FSL_SAI_RCR1:
425 case FSL_SAI_RCR2:
426 case FSL_SAI_RCR3:
427 case FSL_SAI_RCR4:
428 case FSL_SAI_RCR5:
429 case FSL_SAI_RDR:
430 case FSL_SAI_RFR:
431 case FSL_SAI_RMR:
432 return true;
433 default:
434 return false;
435 }
436}
437
438static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
439{
440 switch (reg) {
441 case FSL_SAI_TFR:
442 case FSL_SAI_RFR:
443 case FSL_SAI_TDR:
444 case FSL_SAI_RDR:
445 return true;
446 default:
447 return false;
448 }
449
450}
451
452static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
453{
454 switch (reg) {
455 case FSL_SAI_TCSR:
456 case FSL_SAI_TCR1:
457 case FSL_SAI_TCR2:
458 case FSL_SAI_TCR3:
459 case FSL_SAI_TCR4:
460 case FSL_SAI_TCR5:
461 case FSL_SAI_TDR:
462 case FSL_SAI_TMR:
463 case FSL_SAI_RCSR:
464 case FSL_SAI_RCR1:
465 case FSL_SAI_RCR2:
466 case FSL_SAI_RCR3:
467 case FSL_SAI_RCR4:
468 case FSL_SAI_RCR5:
469 case FSL_SAI_RMR:
470 return true;
471 default:
472 return false;
473 }
474}
475
476static struct regmap_config fsl_sai_regmap_config = {
477 .reg_bits = 32,
478 .reg_stride = 4,
479 .val_bits = 32,
480
481 .max_register = FSL_SAI_RMR,
482 .readable_reg = fsl_sai_readable_reg,
483 .volatile_reg = fsl_sai_volatile_reg,
484 .writeable_reg = fsl_sai_writeable_reg,
485};
486
Xiubo Li43550822013-12-17 11:24:38 +0800487static int fsl_sai_probe(struct platform_device *pdev)
488{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800489 struct device_node *np = pdev->dev.of_node;
Xiubo Li43550822013-12-17 11:24:38 +0800490 struct fsl_sai *sai;
491 struct resource *res;
Xiubo Li78957fc2014-02-08 14:38:28 +0800492 void __iomem *base;
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800493 int ret;
Xiubo Li43550822013-12-17 11:24:38 +0800494
495 sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
496 if (!sai)
497 return -ENOMEM;
498
Xiubo Li78957fc2014-02-08 14:38:28 +0800499 sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
500 if (sai->big_endian_regs)
501 fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
Xiubo Li43550822013-12-17 11:24:38 +0800502
Xiubo Li78957fc2014-02-08 14:38:28 +0800503 sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
504
505 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
506 base = devm_ioremap_resource(&pdev->dev, res);
507 if (IS_ERR(base))
508 return PTR_ERR(base);
509
510 sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
511 "sai", base, &fsl_sai_regmap_config);
512 if (IS_ERR(sai->regmap)) {
513 dev_err(&pdev->dev, "regmap init failed\n");
514 return PTR_ERR(sai->regmap);
Xiubo Li43550822013-12-17 11:24:38 +0800515 }
516
517 sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
518 sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
519 sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
520 sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
521
Xiubo Li43550822013-12-17 11:24:38 +0800522 platform_set_drvdata(pdev, sai);
523
524 ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
525 &fsl_sai_dai, 1);
526 if (ret)
527 return ret;
528
Xiubo Lie5180df32013-12-20 12:30:26 +0800529 return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
Xiubo Li43550822013-12-17 11:24:38 +0800530 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
Xiubo Li43550822013-12-17 11:24:38 +0800531}
532
533static const struct of_device_id fsl_sai_ids[] = {
534 { .compatible = "fsl,vf610-sai", },
535 { /* sentinel */ }
536};
537
538static struct platform_driver fsl_sai_driver = {
539 .probe = fsl_sai_probe,
Xiubo Li43550822013-12-17 11:24:38 +0800540 .driver = {
541 .name = "fsl-sai",
542 .owner = THIS_MODULE,
543 .of_match_table = fsl_sai_ids,
544 },
545};
546module_platform_driver(fsl_sai_driver);
547
548MODULE_DESCRIPTION("Freescale Soc SAI Interface");
549MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>");
550MODULE_ALIAS("platform:fsl-sai");
551MODULE_LICENSE("GPL");