blob: 56da8c8c5960bd0ef99b405560081580a2bd0571 [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
Nicolin Chene2681a12014-03-27 19:06:59 +080026#define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
27 FSL_SAI_CSR_FEIE)
28
29static irqreturn_t fsl_sai_isr(int irq, void *devid)
30{
31 struct fsl_sai *sai = (struct fsl_sai *)devid;
32 struct device *dev = &sai->pdev->dev;
33 u32 xcsr, mask;
34
35 /* Only handle those what we enabled */
36 mask = (FSL_SAI_FLAGS >> FSL_SAI_CSR_xIE_SHIFT) << FSL_SAI_CSR_xF_SHIFT;
37
38 /* Tx IRQ */
39 regmap_read(sai->regmap, FSL_SAI_TCSR, &xcsr);
40 xcsr &= mask;
41
42 if (xcsr & FSL_SAI_CSR_WSF)
43 dev_dbg(dev, "isr: Start of Tx word detected\n");
44
45 if (xcsr & FSL_SAI_CSR_SEF)
46 dev_warn(dev, "isr: Tx Frame sync error detected\n");
47
48 if (xcsr & FSL_SAI_CSR_FEF) {
49 dev_warn(dev, "isr: Transmit underrun detected\n");
50 /* FIFO reset for safety */
51 xcsr |= FSL_SAI_CSR_FR;
52 }
53
54 if (xcsr & FSL_SAI_CSR_FWF)
55 dev_dbg(dev, "isr: Enabled transmit FIFO is empty\n");
56
57 if (xcsr & FSL_SAI_CSR_FRF)
58 dev_dbg(dev, "isr: Transmit FIFO watermark has been reached\n");
59
60 regmap_update_bits(sai->regmap, FSL_SAI_TCSR,
61 FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
62
63 /* Rx IRQ */
64 regmap_read(sai->regmap, FSL_SAI_RCSR, &xcsr);
65 xcsr &= mask;
66
67 if (xcsr & FSL_SAI_CSR_WSF)
68 dev_dbg(dev, "isr: Start of Rx word detected\n");
69
70 if (xcsr & FSL_SAI_CSR_SEF)
71 dev_warn(dev, "isr: Rx Frame sync error detected\n");
72
73 if (xcsr & FSL_SAI_CSR_FEF) {
74 dev_warn(dev, "isr: Receive overflow detected\n");
75 /* FIFO reset for safety */
76 xcsr |= FSL_SAI_CSR_FR;
77 }
78
79 if (xcsr & FSL_SAI_CSR_FWF)
80 dev_dbg(dev, "isr: Enabled receive FIFO is full\n");
81
82 if (xcsr & FSL_SAI_CSR_FRF)
83 dev_dbg(dev, "isr: Receive FIFO watermark has been reached\n");
84
85 regmap_update_bits(sai->regmap, FSL_SAI_RCSR,
86 FSL_SAI_CSR_xF_W_MASK | FSL_SAI_CSR_FR, xcsr);
87
88 return IRQ_HANDLED;
89}
90
Xiubo Li43550822013-12-17 11:24:38 +080091static int fsl_sai_set_dai_sysclk_tr(struct snd_soc_dai *cpu_dai,
92 int clk_id, unsigned int freq, int fsl_dir)
93{
Xiubo Li43550822013-12-17 11:24:38 +080094 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Nicolin Chen4e3a99f2013-12-20 16:41:05 +080095 u32 val_cr2, reg_cr2;
Xiubo Li43550822013-12-17 11:24:38 +080096
97 if (fsl_dir == FSL_FMT_TRANSMITTER)
98 reg_cr2 = FSL_SAI_TCR2;
99 else
100 reg_cr2 = FSL_SAI_RCR2;
101
Xiubo Li78957fc2014-02-08 14:38:28 +0800102 regmap_read(sai->regmap, reg_cr2, &val_cr2);
103
Xiubo Li633ff8f2014-01-08 16:13:05 +0800104 val_cr2 &= ~FSL_SAI_CR2_MSEL_MASK;
105
Xiubo Li43550822013-12-17 11:24:38 +0800106 switch (clk_id) {
107 case FSL_SAI_CLK_BUS:
Xiubo Li43550822013-12-17 11:24:38 +0800108 val_cr2 |= FSL_SAI_CR2_MSEL_BUS;
109 break;
110 case FSL_SAI_CLK_MAST1:
Xiubo Li43550822013-12-17 11:24:38 +0800111 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK1;
112 break;
113 case FSL_SAI_CLK_MAST2:
Xiubo Li43550822013-12-17 11:24:38 +0800114 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK2;
115 break;
116 case FSL_SAI_CLK_MAST3:
Xiubo Li43550822013-12-17 11:24:38 +0800117 val_cr2 |= FSL_SAI_CR2_MSEL_MCLK3;
118 break;
119 default:
120 return -EINVAL;
121 }
Xiubo Li633ff8f2014-01-08 16:13:05 +0800122
Xiubo Li78957fc2014-02-08 14:38:28 +0800123 regmap_write(sai->regmap, reg_cr2, val_cr2);
Xiubo Li43550822013-12-17 11:24:38 +0800124
125 return 0;
126}
127
128static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
129 int clk_id, unsigned int freq, int dir)
130{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800131 int ret;
Xiubo Li43550822013-12-17 11:24:38 +0800132
133 if (dir == SND_SOC_CLOCK_IN)
134 return 0;
135
Xiubo Li43550822013-12-17 11:24:38 +0800136 ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
137 FSL_FMT_TRANSMITTER);
138 if (ret) {
Nicolin Chen190af122013-12-20 16:41:04 +0800139 dev_err(cpu_dai->dev, "Cannot set tx sysclk: %d\n", ret);
Xiubo Li78957fc2014-02-08 14:38:28 +0800140 return ret;
Xiubo Li43550822013-12-17 11:24:38 +0800141 }
142
143 ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq,
144 FSL_FMT_RECEIVER);
Xiubo Li78957fc2014-02-08 14:38:28 +0800145 if (ret)
Nicolin Chen190af122013-12-20 16:41:04 +0800146 dev_err(cpu_dai->dev, "Cannot set rx sysclk: %d\n", ret);
Xiubo Li43550822013-12-17 11:24:38 +0800147
Nicolin Chen1fb2d9d2013-12-20 16:41:00 +0800148 return ret;
Xiubo Li43550822013-12-17 11:24:38 +0800149}
150
151static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
152 unsigned int fmt, int fsl_dir)
153{
Xiubo Li43550822013-12-17 11:24:38 +0800154 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Lie5d0fa92013-12-25 12:40:04 +0800155 u32 val_cr2, val_cr4, reg_cr2, reg_cr4;
Xiubo Li43550822013-12-17 11:24:38 +0800156
157 if (fsl_dir == FSL_FMT_TRANSMITTER) {
158 reg_cr2 = FSL_SAI_TCR2;
Xiubo Li43550822013-12-17 11:24:38 +0800159 reg_cr4 = FSL_SAI_TCR4;
160 } else {
161 reg_cr2 = FSL_SAI_RCR2;
Xiubo Li43550822013-12-17 11:24:38 +0800162 reg_cr4 = FSL_SAI_RCR4;
163 }
164
Xiubo Li78957fc2014-02-08 14:38:28 +0800165 regmap_read(sai->regmap, reg_cr2, &val_cr2);
166 regmap_read(sai->regmap, reg_cr4, &val_cr4);
Xiubo Li43550822013-12-17 11:24:38 +0800167
168 if (sai->big_endian_data)
Xiubo Li43550822013-12-17 11:24:38 +0800169 val_cr4 &= ~FSL_SAI_CR4_MF;
Xiubo Li72aa62b2013-12-31 15:33:22 +0800170 else
171 val_cr4 |= FSL_SAI_CR4_MF;
Xiubo Li43550822013-12-17 11:24:38 +0800172
Xiubo Li13cde092014-02-25 17:54:51 +0800173 /* DAI mode */
Xiubo Li43550822013-12-17 11:24:38 +0800174 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
175 case SND_SOC_DAIFMT_I2S:
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800176 /*
177 * Frame low, 1clk before data, one word length for frame sync,
178 * frame sync starts one serial clock cycle earlier,
179 * that is, together with the last bit of the previous
180 * data word.
181 */
Nicolin Chenef33bc32014-04-04 15:09:47 +0800182 val_cr2 |= FSL_SAI_CR2_BCP;
Xiubo Li13cde092014-02-25 17:54:51 +0800183 val_cr4 |= FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP;
Xiubo Li43550822013-12-17 11:24:38 +0800184 break;
Xiubo Li13cde092014-02-25 17:54:51 +0800185 case SND_SOC_DAIFMT_LEFT_J:
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800186 /*
187 * Frame high, one word length for frame sync,
188 * frame sync asserts with the first bit of the frame.
189 */
Nicolin Chenef33bc32014-04-04 15:09:47 +0800190 val_cr2 |= FSL_SAI_CR2_BCP;
Xiubo Li13cde092014-02-25 17:54:51 +0800191 val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
192 break;
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800193 case SND_SOC_DAIFMT_DSP_A:
194 /*
195 * Frame high, 1clk before data, one bit for frame sync,
196 * frame sync starts one serial clock cycle earlier,
197 * that is, together with the last bit of the previous
198 * data word.
199 */
Nicolin Chenef33bc32014-04-04 15:09:47 +0800200 val_cr2 |= FSL_SAI_CR2_BCP;
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800201 val_cr4 &= ~FSL_SAI_CR4_FSP;
202 val_cr4 |= FSL_SAI_CR4_FSE;
203 sai->is_dsp_mode = true;
204 break;
205 case SND_SOC_DAIFMT_DSP_B:
206 /*
207 * Frame high, one bit for frame sync,
208 * frame sync asserts with the first bit of the frame.
209 */
Nicolin Chenef33bc32014-04-04 15:09:47 +0800210 val_cr2 |= FSL_SAI_CR2_BCP;
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800211 val_cr4 &= ~(FSL_SAI_CR4_FSE | FSL_SAI_CR4_FSP);
212 sai->is_dsp_mode = true;
213 break;
Xiubo Li13cde092014-02-25 17:54:51 +0800214 case SND_SOC_DAIFMT_RIGHT_J:
215 /* To be done */
Xiubo Li43550822013-12-17 11:24:38 +0800216 default:
217 return -EINVAL;
218 }
219
Xiubo Li13cde092014-02-25 17:54:51 +0800220 /* DAI clock inversion */
Xiubo Li43550822013-12-17 11:24:38 +0800221 switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
222 case SND_SOC_DAIFMT_IB_IF:
Xiubo Li13cde092014-02-25 17:54:51 +0800223 /* Invert both clocks */
224 val_cr2 ^= FSL_SAI_CR2_BCP;
225 val_cr4 ^= FSL_SAI_CR4_FSP;
Xiubo Li43550822013-12-17 11:24:38 +0800226 break;
227 case SND_SOC_DAIFMT_IB_NF:
Xiubo Li13cde092014-02-25 17:54:51 +0800228 /* Invert bit clock */
229 val_cr2 ^= FSL_SAI_CR2_BCP;
Xiubo Li43550822013-12-17 11:24:38 +0800230 break;
231 case SND_SOC_DAIFMT_NB_IF:
Xiubo Li13cde092014-02-25 17:54:51 +0800232 /* Invert frame clock */
233 val_cr4 ^= FSL_SAI_CR4_FSP;
Xiubo Li43550822013-12-17 11:24:38 +0800234 break;
235 case SND_SOC_DAIFMT_NB_NF:
Xiubo Li13cde092014-02-25 17:54:51 +0800236 /* Nothing to do for both normal cases */
Xiubo Li43550822013-12-17 11:24:38 +0800237 break;
238 default:
239 return -EINVAL;
240 }
241
Xiubo Li13cde092014-02-25 17:54:51 +0800242 /* DAI clock master masks */
Xiubo Li43550822013-12-17 11:24:38 +0800243 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
244 case SND_SOC_DAIFMT_CBS_CFS:
245 val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
246 val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
247 break;
248 case SND_SOC_DAIFMT_CBM_CFM:
249 val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
250 val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
251 break;
Xiubo Li13cde092014-02-25 17:54:51 +0800252 case SND_SOC_DAIFMT_CBS_CFM:
253 val_cr2 |= FSL_SAI_CR2_BCD_MSTR;
254 val_cr4 &= ~FSL_SAI_CR4_FSD_MSTR;
255 break;
256 case SND_SOC_DAIFMT_CBM_CFS:
257 val_cr2 &= ~FSL_SAI_CR2_BCD_MSTR;
258 val_cr4 |= FSL_SAI_CR4_FSD_MSTR;
259 break;
Xiubo Li43550822013-12-17 11:24:38 +0800260 default:
261 return -EINVAL;
262 }
263
Xiubo Li78957fc2014-02-08 14:38:28 +0800264 regmap_write(sai->regmap, reg_cr2, val_cr2);
265 regmap_write(sai->regmap, reg_cr4, val_cr4);
Xiubo Li43550822013-12-17 11:24:38 +0800266
267 return 0;
268}
269
270static int fsl_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
271{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800272 int ret;
Xiubo Li43550822013-12-17 11:24:38 +0800273
Xiubo Li43550822013-12-17 11:24:38 +0800274 ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_TRANSMITTER);
275 if (ret) {
Nicolin Chen190af122013-12-20 16:41:04 +0800276 dev_err(cpu_dai->dev, "Cannot set tx format: %d\n", ret);
Xiubo Li78957fc2014-02-08 14:38:28 +0800277 return ret;
Xiubo Li43550822013-12-17 11:24:38 +0800278 }
279
280 ret = fsl_sai_set_dai_fmt_tr(cpu_dai, fmt, FSL_FMT_RECEIVER);
Xiubo Li78957fc2014-02-08 14:38:28 +0800281 if (ret)
Nicolin Chen190af122013-12-20 16:41:04 +0800282 dev_err(cpu_dai->dev, "Cannot set rx format: %d\n", ret);
Xiubo Li43550822013-12-17 11:24:38 +0800283
Nicolin Chen1fb2d9d2013-12-20 16:41:00 +0800284 return ret;
Xiubo Li43550822013-12-17 11:24:38 +0800285}
286
287static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
288 struct snd_pcm_hw_params *params,
289 struct snd_soc_dai *cpu_dai)
290{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800291 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Nicolin Chen1d700302013-12-20 16:41:01 +0800292 u32 val_cr4, val_cr5, val_mr, reg_cr4, reg_cr5, reg_mr;
Xiubo Li43550822013-12-17 11:24:38 +0800293 unsigned int channels = params_channels(params);
Nicolin Chen1d700302013-12-20 16:41:01 +0800294 u32 word_width = snd_pcm_format_width(params_format(params));
Xiubo Li43550822013-12-17 11:24:38 +0800295
296 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
297 reg_cr4 = FSL_SAI_TCR4;
298 reg_cr5 = FSL_SAI_TCR5;
299 reg_mr = FSL_SAI_TMR;
300 } else {
301 reg_cr4 = FSL_SAI_RCR4;
302 reg_cr5 = FSL_SAI_RCR5;
303 reg_mr = FSL_SAI_RMR;
304 }
305
Xiubo Li78957fc2014-02-08 14:38:28 +0800306 regmap_read(sai->regmap, reg_cr4, &val_cr4);
307 regmap_read(sai->regmap, reg_cr4, &val_cr5);
308
Xiubo Li43550822013-12-17 11:24:38 +0800309 val_cr4 &= ~FSL_SAI_CR4_SYWD_MASK;
310 val_cr4 &= ~FSL_SAI_CR4_FRSZ_MASK;
311
Xiubo Li43550822013-12-17 11:24:38 +0800312 val_cr5 &= ~FSL_SAI_CR5_WNW_MASK;
313 val_cr5 &= ~FSL_SAI_CR5_W0W_MASK;
314 val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
315
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800316 if (!sai->is_dsp_mode)
317 val_cr4 |= FSL_SAI_CR4_SYWD(word_width);
318
Xiubo Li43550822013-12-17 11:24:38 +0800319 val_cr5 |= FSL_SAI_CR5_WNW(word_width);
320 val_cr5 |= FSL_SAI_CR5_W0W(word_width);
321
Xiubo Li496a39d2013-12-31 15:33:21 +0800322 val_cr5 &= ~FSL_SAI_CR5_FBT_MASK;
Xiubo Li43550822013-12-17 11:24:38 +0800323 if (sai->big_endian_data)
Xiubo Li43550822013-12-17 11:24:38 +0800324 val_cr5 |= FSL_SAI_CR5_FBT(0);
Xiubo Li72aa62b2013-12-31 15:33:22 +0800325 else
326 val_cr5 |= FSL_SAI_CR5_FBT(word_width - 1);
Xiubo Li43550822013-12-17 11:24:38 +0800327
328 val_cr4 |= FSL_SAI_CR4_FRSZ(channels);
Nicolin Chend22e28c2013-12-20 16:41:02 +0800329 val_mr = ~0UL - ((1 << channels) - 1);
Xiubo Li43550822013-12-17 11:24:38 +0800330
Xiubo Li78957fc2014-02-08 14:38:28 +0800331 regmap_write(sai->regmap, reg_cr4, val_cr4);
332 regmap_write(sai->regmap, reg_cr5, val_cr5);
333 regmap_write(sai->regmap, reg_mr, val_mr);
Xiubo Li43550822013-12-17 11:24:38 +0800334
335 return 0;
336}
337
338static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
339 struct snd_soc_dai *cpu_dai)
340{
341 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Li78957fc2014-02-08 14:38:28 +0800342 u32 tcsr, rcsr;
Xiubo Li496a39d2013-12-31 15:33:21 +0800343
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800344 /*
345 * The transmitter bit clock and frame sync are to be
346 * used by both the transmitter and receiver.
347 */
Xiubo Li78957fc2014-02-08 14:38:28 +0800348 regmap_update_bits(sai->regmap, FSL_SAI_TCR2, FSL_SAI_CR2_SYNC,
349 ~FSL_SAI_CR2_SYNC);
350 regmap_update_bits(sai->regmap, FSL_SAI_RCR2, FSL_SAI_CR2_SYNC,
351 FSL_SAI_CR2_SYNC);
Xiubo Li496a39d2013-12-31 15:33:21 +0800352
Xiubo Li78957fc2014-02-08 14:38:28 +0800353 regmap_read(sai->regmap, FSL_SAI_TCSR, &tcsr);
354 regmap_read(sai->regmap, FSL_SAI_RCSR, &rcsr);
Xiubo Li43550822013-12-17 11:24:38 +0800355
356 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
357 tcsr |= FSL_SAI_CSR_FRDE;
358 rcsr &= ~FSL_SAI_CSR_FRDE;
359 } else {
360 rcsr |= FSL_SAI_CSR_FRDE;
361 tcsr &= ~FSL_SAI_CSR_FRDE;
362 }
363
Xiubo Lia3f7dcc2014-02-27 08:45:01 +0800364 /*
365 * It is recommended that the transmitter is the last enabled
366 * and the first disabled.
367 */
Xiubo Li43550822013-12-17 11:24:38 +0800368 switch (cmd) {
369 case SNDRV_PCM_TRIGGER_START:
370 case SNDRV_PCM_TRIGGER_RESUME:
371 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
372 tcsr |= FSL_SAI_CSR_TERE;
373 rcsr |= FSL_SAI_CSR_TERE;
Xiubo Lie5d0fa92013-12-25 12:40:04 +0800374
Xiubo Li78957fc2014-02-08 14:38:28 +0800375 regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
376 regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
Xiubo Li43550822013-12-17 11:24:38 +0800377 break;
Xiubo Li43550822013-12-17 11:24:38 +0800378 case SNDRV_PCM_TRIGGER_STOP:
379 case SNDRV_PCM_TRIGGER_SUSPEND:
380 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
381 if (!(cpu_dai->playback_active || cpu_dai->capture_active)) {
382 tcsr &= ~FSL_SAI_CSR_TERE;
383 rcsr &= ~FSL_SAI_CSR_TERE;
384 }
Xiubo Lie5d0fa92013-12-25 12:40:04 +0800385
Xiubo Li78957fc2014-02-08 14:38:28 +0800386 regmap_write(sai->regmap, FSL_SAI_TCSR, tcsr);
387 regmap_write(sai->regmap, FSL_SAI_RCSR, rcsr);
Xiubo Li43550822013-12-17 11:24:38 +0800388 break;
389 default:
390 return -EINVAL;
391 }
392
393 return 0;
394}
395
396static int fsl_sai_startup(struct snd_pcm_substream *substream,
397 struct snd_soc_dai *cpu_dai)
398{
Xiubo Li43550822013-12-17 11:24:38 +0800399 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Li78957fc2014-02-08 14:38:28 +0800400 u32 reg;
Xiubo Li43550822013-12-17 11:24:38 +0800401
Xiubo Li78957fc2014-02-08 14:38:28 +0800402 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
403 reg = FSL_SAI_TCR3;
404 else
405 reg = FSL_SAI_RCR3;
406
407 regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
408 FSL_SAI_CR3_TRCE);
409
410 return 0;
Xiubo Li43550822013-12-17 11:24:38 +0800411}
412
413static void fsl_sai_shutdown(struct snd_pcm_substream *substream,
414 struct snd_soc_dai *cpu_dai)
415{
416 struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
Xiubo Li78957fc2014-02-08 14:38:28 +0800417 u32 reg;
Xiubo Li43550822013-12-17 11:24:38 +0800418
Xiubo Li78957fc2014-02-08 14:38:28 +0800419 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
420 reg = FSL_SAI_TCR3;
421 else
422 reg = FSL_SAI_RCR3;
423
424 regmap_update_bits(sai->regmap, reg, FSL_SAI_CR3_TRCE,
425 ~FSL_SAI_CR3_TRCE);
Xiubo Li43550822013-12-17 11:24:38 +0800426}
427
428static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = {
429 .set_sysclk = fsl_sai_set_dai_sysclk,
430 .set_fmt = fsl_sai_set_dai_fmt,
431 .hw_params = fsl_sai_hw_params,
432 .trigger = fsl_sai_trigger,
433 .startup = fsl_sai_startup,
434 .shutdown = fsl_sai_shutdown,
435};
436
437static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai)
438{
439 struct fsl_sai *sai = dev_get_drvdata(cpu_dai->dev);
Xiubo Lie6dc12d2013-12-25 11:20:14 +0800440
Nicolin Chene2681a12014-03-27 19:06:59 +0800441 regmap_update_bits(sai->regmap, FSL_SAI_TCSR, 0xffffffff, FSL_SAI_FLAGS);
442 regmap_update_bits(sai->regmap, FSL_SAI_RCSR, 0xffffffff, FSL_SAI_FLAGS);
Xiubo Li78957fc2014-02-08 14:38:28 +0800443 regmap_update_bits(sai->regmap, FSL_SAI_TCR1, FSL_SAI_CR1_RFW_MASK,
444 FSL_SAI_MAXBURST_TX * 2);
445 regmap_update_bits(sai->regmap, FSL_SAI_RCR1, FSL_SAI_CR1_RFW_MASK,
446 FSL_SAI_MAXBURST_RX - 1);
Xiubo Li43550822013-12-17 11:24:38 +0800447
Xiubo Lidd9f4062013-12-20 12:35:33 +0800448 snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params_tx,
449 &sai->dma_params_rx);
Xiubo Li43550822013-12-17 11:24:38 +0800450
451 snd_soc_dai_set_drvdata(cpu_dai, sai);
452
453 return 0;
454}
455
Xiubo Li43550822013-12-17 11:24:38 +0800456static struct snd_soc_dai_driver fsl_sai_dai = {
457 .probe = fsl_sai_dai_probe,
Xiubo Li43550822013-12-17 11:24:38 +0800458 .playback = {
459 .channels_min = 1,
460 .channels_max = 2,
461 .rates = SNDRV_PCM_RATE_8000_96000,
462 .formats = FSL_SAI_FORMATS,
463 },
464 .capture = {
465 .channels_min = 1,
466 .channels_max = 2,
467 .rates = SNDRV_PCM_RATE_8000_96000,
468 .formats = FSL_SAI_FORMATS,
469 },
470 .ops = &fsl_sai_pcm_dai_ops,
471};
472
473static const struct snd_soc_component_driver fsl_component = {
474 .name = "fsl-sai",
475};
476
Xiubo Li78957fc2014-02-08 14:38:28 +0800477static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
478{
479 switch (reg) {
480 case FSL_SAI_TCSR:
481 case FSL_SAI_TCR1:
482 case FSL_SAI_TCR2:
483 case FSL_SAI_TCR3:
484 case FSL_SAI_TCR4:
485 case FSL_SAI_TCR5:
486 case FSL_SAI_TFR:
487 case FSL_SAI_TMR:
488 case FSL_SAI_RCSR:
489 case FSL_SAI_RCR1:
490 case FSL_SAI_RCR2:
491 case FSL_SAI_RCR3:
492 case FSL_SAI_RCR4:
493 case FSL_SAI_RCR5:
494 case FSL_SAI_RDR:
495 case FSL_SAI_RFR:
496 case FSL_SAI_RMR:
497 return true;
498 default:
499 return false;
500 }
501}
502
503static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
504{
505 switch (reg) {
506 case FSL_SAI_TFR:
507 case FSL_SAI_RFR:
508 case FSL_SAI_TDR:
509 case FSL_SAI_RDR:
510 return true;
511 default:
512 return false;
513 }
514
515}
516
517static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg)
518{
519 switch (reg) {
520 case FSL_SAI_TCSR:
521 case FSL_SAI_TCR1:
522 case FSL_SAI_TCR2:
523 case FSL_SAI_TCR3:
524 case FSL_SAI_TCR4:
525 case FSL_SAI_TCR5:
526 case FSL_SAI_TDR:
527 case FSL_SAI_TMR:
528 case FSL_SAI_RCSR:
529 case FSL_SAI_RCR1:
530 case FSL_SAI_RCR2:
531 case FSL_SAI_RCR3:
532 case FSL_SAI_RCR4:
533 case FSL_SAI_RCR5:
534 case FSL_SAI_RMR:
535 return true;
536 default:
537 return false;
538 }
539}
540
541static struct regmap_config fsl_sai_regmap_config = {
542 .reg_bits = 32,
543 .reg_stride = 4,
544 .val_bits = 32,
545
546 .max_register = FSL_SAI_RMR,
547 .readable_reg = fsl_sai_readable_reg,
548 .volatile_reg = fsl_sai_volatile_reg,
549 .writeable_reg = fsl_sai_writeable_reg,
550};
551
Xiubo Li43550822013-12-17 11:24:38 +0800552static int fsl_sai_probe(struct platform_device *pdev)
553{
Nicolin Chen4e3a99f2013-12-20 16:41:05 +0800554 struct device_node *np = pdev->dev.of_node;
Xiubo Li43550822013-12-17 11:24:38 +0800555 struct fsl_sai *sai;
556 struct resource *res;
Xiubo Li78957fc2014-02-08 14:38:28 +0800557 void __iomem *base;
Nicolin Chene2681a12014-03-27 19:06:59 +0800558 int irq, ret;
Xiubo Li43550822013-12-17 11:24:38 +0800559
560 sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
561 if (!sai)
562 return -ENOMEM;
563
Nicolin Chene2681a12014-03-27 19:06:59 +0800564 sai->pdev = pdev;
565
Xiubo Li78957fc2014-02-08 14:38:28 +0800566 sai->big_endian_regs = of_property_read_bool(np, "big-endian-regs");
567 if (sai->big_endian_regs)
568 fsl_sai_regmap_config.val_format_endian = REGMAP_ENDIAN_BIG;
Xiubo Li43550822013-12-17 11:24:38 +0800569
Xiubo Li78957fc2014-02-08 14:38:28 +0800570 sai->big_endian_data = of_property_read_bool(np, "big-endian-data");
571
572 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
573 base = devm_ioremap_resource(&pdev->dev, res);
574 if (IS_ERR(base))
575 return PTR_ERR(base);
576
577 sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
578 "sai", base, &fsl_sai_regmap_config);
579 if (IS_ERR(sai->regmap)) {
580 dev_err(&pdev->dev, "regmap init failed\n");
581 return PTR_ERR(sai->regmap);
Xiubo Li43550822013-12-17 11:24:38 +0800582 }
583
Nicolin Chene2681a12014-03-27 19:06:59 +0800584 irq = platform_get_irq(pdev, 0);
585 if (irq < 0) {
586 dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
587 return irq;
588 }
589
590 ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, 0, np->name, sai);
591 if (ret) {
592 dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
593 return ret;
594 }
595
Xiubo Li43550822013-12-17 11:24:38 +0800596 sai->dma_params_rx.addr = res->start + FSL_SAI_RDR;
597 sai->dma_params_tx.addr = res->start + FSL_SAI_TDR;
598 sai->dma_params_rx.maxburst = FSL_SAI_MAXBURST_RX;
599 sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
600
Xiubo Li43550822013-12-17 11:24:38 +0800601 platform_set_drvdata(pdev, sai);
602
603 ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
604 &fsl_sai_dai, 1);
605 if (ret)
606 return ret;
607
Xiubo Lie5180df32013-12-20 12:30:26 +0800608 return devm_snd_dmaengine_pcm_register(&pdev->dev, NULL,
Xiubo Li43550822013-12-17 11:24:38 +0800609 SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
Xiubo Li43550822013-12-17 11:24:38 +0800610}
611
612static const struct of_device_id fsl_sai_ids[] = {
613 { .compatible = "fsl,vf610-sai", },
614 { /* sentinel */ }
615};
616
617static struct platform_driver fsl_sai_driver = {
618 .probe = fsl_sai_probe,
Xiubo Li43550822013-12-17 11:24:38 +0800619 .driver = {
620 .name = "fsl-sai",
621 .owner = THIS_MODULE,
622 .of_match_table = fsl_sai_ids,
623 },
624};
625module_platform_driver(fsl_sai_driver);
626
627MODULE_DESCRIPTION("Freescale Soc SAI Interface");
628MODULE_AUTHOR("Xiubo Li, <Li.Xiubo@freescale.com>");
629MODULE_ALIAS("platform:fsl-sai");
630MODULE_LICENSE("GPL");