Blame view

sound/soc/atmel/atmel_ssc_dai.c 22.9 KB
1a59d1b8e   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
2
3
4
5
6
7
8
9
10
11
12
13
  /*
   * atmel_ssc_dai.c  --  ALSA SoC ATMEL SSC Audio Layer Platform driver
   *
   * Copyright (C) 2005 SAN People
   * Copyright (C) 2008 Atmel
   *
   * Author: Sedji Gaouaou <sedji.gaouaou@atmel.com>
   *         ATMEL CORP.
   *
   * Based on at91-ssc.c by
   * Frank Mandarino <fmandarino@endrelia.com>
   * Based on pxa2xx Platform drivers by
64ca0404e   Liam Girdwood   ALSA: ASoC: email...
14
   * Liam Girdwood <lrg@slimlogic.co.uk>
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/interrupt.h>
  #include <linux/device.h>
  #include <linux/delay.h>
  #include <linux/clk.h>
  #include <linux/atmel_pdc.h>
  
  #include <linux/atmel-ssc.h>
  #include <sound/core.h>
  #include <sound/pcm.h>
  #include <sound/pcm_params.h>
  #include <sound/initval.h>
  #include <sound/soc.h>
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
31
32
  #include "atmel-pcm.h"
  #include "atmel_ssc_dai.h"
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
33
  #define NUM_SSC_DEVICES		3
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  
  /*
   * SSC PDC registers required by the PCM DMA engine.
   */
  static struct atmel_pdc_regs pdc_tx_reg = {
  	.xpr		= ATMEL_PDC_TPR,
  	.xcr		= ATMEL_PDC_TCR,
  	.xnpr		= ATMEL_PDC_TNPR,
  	.xncr		= ATMEL_PDC_TNCR,
  };
  
  static struct atmel_pdc_regs pdc_rx_reg = {
  	.xpr		= ATMEL_PDC_RPR,
  	.xcr		= ATMEL_PDC_RCR,
  	.xnpr		= ATMEL_PDC_RNPR,
  	.xncr		= ATMEL_PDC_RNCR,
  };
  
  /*
   * SSC & PDC status bits for transmit and receive.
   */
  static struct atmel_ssc_mask ssc_tx_mask = {
  	.ssc_enable	= SSC_BIT(CR_TXEN),
  	.ssc_disable	= SSC_BIT(CR_TXDIS),
  	.ssc_endx	= SSC_BIT(SR_ENDTX),
  	.ssc_endbuf	= SSC_BIT(SR_TXBUFE),
f1b0dd8b9   Bo Shen   ASoC: atmel_ssc_d...
60
  	.ssc_error	= SSC_BIT(SR_OVRUN),
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
61
62
63
64
65
66
67
68
69
  	.pdc_enable	= ATMEL_PDC_TXTEN,
  	.pdc_disable	= ATMEL_PDC_TXTDIS,
  };
  
  static struct atmel_ssc_mask ssc_rx_mask = {
  	.ssc_enable	= SSC_BIT(CR_RXEN),
  	.ssc_disable	= SSC_BIT(CR_RXDIS),
  	.ssc_endx	= SSC_BIT(SR_ENDRX),
  	.ssc_endbuf	= SSC_BIT(SR_RXBUFF),
f1b0dd8b9   Bo Shen   ASoC: atmel_ssc_d...
70
  	.ssc_error	= SSC_BIT(SR_OVRUN),
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
  	.pdc_enable	= ATMEL_PDC_RXTEN,
  	.pdc_disable	= ATMEL_PDC_RXTDIS,
  };
  
  
  /*
   * DMA parameters.
   */
  static struct atmel_pcm_dma_params ssc_dma_params[NUM_SSC_DEVICES][2] = {
  	{{
  	.name		= "SSC0 PCM out",
  	.pdc		= &pdc_tx_reg,
  	.mask		= &ssc_tx_mask,
  	},
  	{
  	.name		= "SSC0 PCM in",
  	.pdc		= &pdc_rx_reg,
  	.mask		= &ssc_rx_mask,
  	} },
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  	{{
  	.name		= "SSC1 PCM out",
  	.pdc		= &pdc_tx_reg,
  	.mask		= &ssc_tx_mask,
  	},
  	{
  	.name		= "SSC1 PCM in",
  	.pdc		= &pdc_rx_reg,
  	.mask		= &ssc_rx_mask,
  	} },
  	{{
  	.name		= "SSC2 PCM out",
  	.pdc		= &pdc_tx_reg,
  	.mask		= &ssc_tx_mask,
  	},
  	{
  	.name		= "SSC2 PCM in",
  	.pdc		= &pdc_rx_reg,
  	.mask		= &ssc_rx_mask,
  	} },
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
110
111
112
113
114
115
  };
  
  
  static struct atmel_ssc_info ssc_info[NUM_SSC_DEVICES] = {
  	{
  	.name		= "ssc0",
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
116
117
118
  	.dir_mask	= SSC_DIR_MASK_UNUSED,
  	.initialized	= 0,
  	},
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
119
120
  	{
  	.name		= "ssc1",
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
121
122
123
124
125
  	.dir_mask	= SSC_DIR_MASK_UNUSED,
  	.initialized	= 0,
  	},
  	{
  	.name		= "ssc2",
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
126
127
128
  	.dir_mask	= SSC_DIR_MASK_UNUSED,
  	.initialized	= 0,
  	},
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
  };
  
  
  /*
   * SSC interrupt handler.  Passes PDC interrupts to the DMA
   * interrupt handler in the PCM driver.
   */
  static irqreturn_t atmel_ssc_interrupt(int irq, void *dev_id)
  {
  	struct atmel_ssc_info *ssc_p = dev_id;
  	struct atmel_pcm_dma_params *dma_params;
  	u32 ssc_sr;
  	u32 ssc_substream_mask;
  	int i;
  
  	ssc_sr = (unsigned long)ssc_readl(ssc_p->ssc->regs, SR)
  			& (unsigned long)ssc_readl(ssc_p->ssc->regs, IMR);
  
  	/*
  	 * Loop through the substreams attached to this SSC.  If
  	 * a DMA-related interrupt occurred on that substream, call
  	 * the DMA interrupt handler function, if one has been
  	 * registered in the dma_params structure by the PCM driver.
  	 */
  	for (i = 0; i < ARRAY_SIZE(ssc_p->dma_params); i++) {
  		dma_params = ssc_p->dma_params[i];
  
  		if ((dma_params != NULL) &&
  			(dma_params->dma_intr_handler != NULL)) {
  			ssc_substream_mask = (dma_params->mask->ssc_endx |
  					dma_params->mask->ssc_endbuf);
  			if (ssc_sr & ssc_substream_mask) {
  				dma_params->dma_intr_handler(ssc_sr,
  						dma_params->
  						substream);
  			}
  		}
  	}
  
  	return IRQ_HANDLED;
  }
b6d6c6e95   Peter Rosin   ASoC: atmel_ssc_d...
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
  /*
   * When the bit clock is input, limit the maximum rate according to the
   * Serial Clock Ratio Considerations section from the SSC documentation:
   *
   *   The Transmitter and the Receiver can be programmed to operate
   *   with the clock signals provided on either the TK or RK pins.
   *   This allows the SSC to support many slave-mode data transfers.
   *   In this case, the maximum clock speed allowed on the RK pin is:
   *   - Peripheral clock divided by 2 if Receiver Frame Synchro is input
   *   - Peripheral clock divided by 3 if Receiver Frame Synchro is output
   *   In addition, the maximum clock speed allowed on the TK pin is:
   *   - Peripheral clock divided by 6 if Transmit Frame Synchro is input
   *   - Peripheral clock divided by 2 if Transmit Frame Synchro is output
   *
   * When the bit clock is output, limit the rate according to the
   * SSC divider restrictions.
   */
  static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
  				  struct snd_pcm_hw_rule *rule)
  {
  	struct atmel_ssc_info *ssc_p = rule->private;
  	struct ssc_device *ssc = ssc_p->ssc;
  	struct snd_interval *i = hw_param_interval(params, rule->var);
  	struct snd_interval t;
  	struct snd_ratnum r = {
  		.den_min = 1,
  		.den_max = 4095,
  		.den_step = 1,
  	};
  	unsigned int num = 0, den = 0;
  	int frame_size;
  	int mck_div = 2;
  	int ret;
  
  	frame_size = snd_soc_params_to_frame_size(params);
  	if (frame_size < 0)
  		return frame_size;
  
  	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
  	case SND_SOC_DAIFMT_CBM_CFS:
  		if ((ssc_p->dir_mask & SSC_DIR_MASK_CAPTURE)
  		    && ssc->clk_from_rk_pin)
  			/* Receiver Frame Synchro (i.e. capture)
  			 * is output (format is _CFS) and the RK pin
  			 * is used for input (format is _CBM_).
  			 */
  			mck_div = 3;
  		break;
  
  	case SND_SOC_DAIFMT_CBM_CFM:
  		if ((ssc_p->dir_mask & SSC_DIR_MASK_PLAYBACK)
  		    && !ssc->clk_from_rk_pin)
  			/* Transmit Frame Synchro (i.e. playback)
  			 * is input (format is _CFM) and the TK pin
  			 * is used for input (format _CBM_ but not
  			 * using the RK pin).
  			 */
  			mck_div = 6;
  		break;
  	}
  
  	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
  	case SND_SOC_DAIFMT_CBS_CFS:
  		r.num = ssc_p->mck_rate / mck_div / frame_size;
  
  		ret = snd_interval_ratnum(i, 1, &r, &num, &den);
  		if (ret >= 0 && den && rule->var == SNDRV_PCM_HW_PARAM_RATE) {
  			params->rate_num = num;
  			params->rate_den = den;
  		}
  		break;
  
  	case SND_SOC_DAIFMT_CBM_CFS:
  	case SND_SOC_DAIFMT_CBM_CFM:
  		t.min = 8000;
  		t.max = ssc_p->mck_rate / mck_div / frame_size;
  		t.openmin = t.openmax = 0;
  		t.integer = 0;
  		ret = snd_interval_refine(i, &t);
  		break;
  
  	default:
  		ret = -EINVAL;
  		break;
  	}
  
  	return ret;
  }
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
258
259
260
261
262
263
264
  
  /*-------------------------------------------------------------------------*\
   * DAI functions
  \*-------------------------------------------------------------------------*/
  /*
   * Startup.  Only that one substream allowed in each direction.
   */
dee89c4d9   Mark Brown   ASoC: Merge snd_s...
265
266
  static int atmel_ssc_startup(struct snd_pcm_substream *substream,
  			     struct snd_soc_dai *dai)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
267
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
268
269
  	struct platform_device *pdev = to_platform_device(dai->dev);
  	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
01f00d55a   Bo Shen   ASoC: atmel_ssc_d...
270
271
  	struct atmel_pcm_dma_params *dma_params;
  	int dir, dir_mask;
b6d6c6e95   Peter Rosin   ASoC: atmel_ssc_d...
272
  	int ret;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
273

56113f6e6   Joe Perches   ASoC: atmel_ssc_d...
274
275
  	pr_debug("atmel_ssc_startup: SSC_SR=0x%x
  ",
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
276
  		ssc_readl(ssc_p->ssc->regs, SR));
cbaadf0f9   Bo Shen   ASoC: atmel_ssc_d...
277
278
279
280
  	/* Enable PMC peripheral clock for this SSC */
  	pr_debug("atmel_ssc_dai: Starting clock
  ");
  	clk_enable(ssc_p->ssc->clk);
b6d6c6e95   Peter Rosin   ASoC: atmel_ssc_d...
281
  	ssc_p->mck_rate = clk_get_rate(ssc_p->ssc->clk);
cbaadf0f9   Bo Shen   ASoC: atmel_ssc_d...
282

3e103a655   Christoph Huber   ASoC: atmel_ssc_d...
283
284
285
  	/* Reset the SSC unless initialized to keep it in a clean state */
  	if (!ssc_p->initialized)
  		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
cbaadf0f9   Bo Shen   ASoC: atmel_ssc_d...
286

01f00d55a   Bo Shen   ASoC: atmel_ssc_d...
287
288
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		dir = 0;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
289
  		dir_mask = SSC_DIR_MASK_PLAYBACK;
01f00d55a   Bo Shen   ASoC: atmel_ssc_d...
290
291
  	} else {
  		dir = 1;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
292
  		dir_mask = SSC_DIR_MASK_CAPTURE;
01f00d55a   Bo Shen   ASoC: atmel_ssc_d...
293
  	}
b6d6c6e95   Peter Rosin   ASoC: atmel_ssc_d...
294
295
296
297
298
299
300
301
302
303
304
  	ret = snd_pcm_hw_rule_add(substream->runtime, 0,
  				  SNDRV_PCM_HW_PARAM_RATE,
  				  atmel_ssc_hw_rule_rate,
  				  ssc_p,
  				  SNDRV_PCM_HW_PARAM_FRAME_BITS,
  				  SNDRV_PCM_HW_PARAM_CHANNELS, -1);
  	if (ret < 0) {
  		dev_err(dai->dev, "Failed to specify rate rule: %d
  ", ret);
  		return ret;
  	}
e401029e5   Peter Meerwald   ASoC: atmel_ssc_d...
305
  	dma_params = &ssc_dma_params[pdev->id][dir];
01f00d55a   Bo Shen   ASoC: atmel_ssc_d...
306
307
308
309
310
311
  	dma_params->ssc = ssc_p->ssc;
  	dma_params->substream = substream;
  
  	ssc_p->dma_params[dir] = dma_params;
  
  	snd_soc_dai_set_dma_data(dai, substream, dma_params);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
312

0dce49efc   Gregory CLEMENT   ASoC: atmel_ssc_d...
313
  	if (ssc_p->dir_mask & dir_mask)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
314
  		return -EBUSY;
0dce49efc   Gregory CLEMENT   ASoC: atmel_ssc_d...
315

6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
316
  	ssc_p->dir_mask |= dir_mask;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
317
318
319
320
321
322
323
324
  
  	return 0;
  }
  
  /*
   * Shutdown.  Clear DMA parameters and shutdown the SSC if there
   * are no other substreams open.
   */
dee89c4d9   Mark Brown   ASoC: Merge snd_s...
325
326
  static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
  			       struct snd_soc_dai *dai)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
327
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
328
329
  	struct platform_device *pdev = to_platform_device(dai->dev);
  	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
330
331
332
333
334
335
336
337
338
339
340
  	struct atmel_pcm_dma_params *dma_params;
  	int dir, dir_mask;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		dir = 0;
  	else
  		dir = 1;
  
  	dma_params = ssc_p->dma_params[dir];
  
  	if (dma_params != NULL) {
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
341
342
343
344
345
346
  		dma_params->ssc = NULL;
  		dma_params->substream = NULL;
  		ssc_p->dma_params[dir] = NULL;
  	}
  
  	dir_mask = 1 << dir;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
347
348
349
  	ssc_p->dir_mask &= ~dir_mask;
  	if (!ssc_p->dir_mask) {
  		if (ssc_p->initialized) {
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
350
351
352
353
354
355
356
357
  			free_irq(ssc_p->ssc->irq, ssc_p);
  			ssc_p->initialized = 0;
  		}
  
  		/* Reset the SSC */
  		ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_SWRST));
  		/* Clear the SSC dividers */
  		ssc_p->cmr_div = ssc_p->tcmr_period = ssc_p->rcmr_period = 0;
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
358
  		ssc_p->forced_divider = 0;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
359
  	}
cbaadf0f9   Bo Shen   ASoC: atmel_ssc_d...
360
361
362
363
364
  
  	/* Shutdown the SSC clock. */
  	pr_debug("atmel_ssc_dai: Stopping clock
  ");
  	clk_disable(ssc_p->ssc->clk);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
365
366
367
368
369
370
371
372
373
  }
  
  
  /*
   * Record the DAI format for use in hw_params().
   */
  static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
  		unsigned int fmt)
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
374
375
  	struct platform_device *pdev = to_platform_device(cpu_dai->dev);
  	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
376
377
378
379
380
381
382
383
384
385
386
  
  	ssc_p->daifmt = fmt;
  	return 0;
  }
  
  /*
   * Record SSC clock dividers for use in hw_params().
   */
  static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
  	int div_id, int div)
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
387
388
  	struct platform_device *pdev = to_platform_device(cpu_dai->dev);
  	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
389
390
391
392
393
394
395
396
  
  	switch (div_id) {
  	case ATMEL_SSC_CMR_DIV:
  		/*
  		 * The same master clock divider is used for both
  		 * transmit and receive, so if a value has already
  		 * been set, it must match this value.
  		 */
eb58960e9   Peter Rosin   ASoC: atmel_ssc_d...
397
398
399
400
  		if (ssc_p->dir_mask !=
  			(SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE))
  			ssc_p->cmr_div = div;
  		else if (ssc_p->cmr_div == 0)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
401
402
403
404
  			ssc_p->cmr_div = div;
  		else
  			if (div != ssc_p->cmr_div)
  				return -EBUSY;
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
405
  		ssc_p->forced_divider |= BIT(ATMEL_SSC_CMR_DIV);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
406
407
408
409
  		break;
  
  	case ATMEL_SSC_TCMR_PERIOD:
  		ssc_p->tcmr_period = div;
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
410
  		ssc_p->forced_divider |= BIT(ATMEL_SSC_TCMR_PERIOD);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
411
412
413
414
  		break;
  
  	case ATMEL_SSC_RCMR_PERIOD:
  		ssc_p->rcmr_period = div;
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
415
  		ssc_p->forced_divider |= BIT(ATMEL_SSC_RCMR_PERIOD);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
416
417
418
419
420
421
422
423
  		break;
  
  	default:
  		return -EINVAL;
  	}
  
  	return 0;
  }
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  /* Is the cpu-dai master of the frame clock? */
  static int atmel_ssc_cfs(struct atmel_ssc_info *ssc_p)
  {
  	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
  	case SND_SOC_DAIFMT_CBM_CFS:
  	case SND_SOC_DAIFMT_CBS_CFS:
  		return 1;
  	}
  	return 0;
  }
  
  /* Is the cpu-dai master of the bit clock? */
  static int atmel_ssc_cbs(struct atmel_ssc_info *ssc_p)
  {
  	switch (ssc_p->daifmt & SND_SOC_DAIFMT_MASTER_MASK) {
  	case SND_SOC_DAIFMT_CBS_CFM:
  	case SND_SOC_DAIFMT_CBS_CFS:
  		return 1;
  	}
  	return 0;
  }
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
445
446
447
448
  /*
   * Configure the SSC.
   */
  static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
dee89c4d9   Mark Brown   ASoC: Merge snd_s...
449
450
  	struct snd_pcm_hw_params *params,
  	struct snd_soc_dai *dai)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
451
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
452
453
  	struct platform_device *pdev = to_platform_device(dai->dev);
  	int id = pdev->id;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
454
  	struct atmel_ssc_info *ssc_p = &ssc_info[id];
048d4ff81   Bo Shen   ASoC: atmel_ssc_d...
455
  	struct ssc_device *ssc = ssc_p->ssc;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
456
457
458
  	struct atmel_pcm_dma_params *dma_params;
  	int dir, channels, bits;
  	u32 tfmr, rfmr, tcmr, rcmr;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
459
  	int ret;
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
460
  	int fslen, fslen_ext, fs_osync, fs_edge;
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
461
462
463
  	u32 cmr_div;
  	u32 tcmr_period;
  	u32 rcmr_period;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
464
465
466
467
468
469
470
471
472
473
  
  	/*
  	 * Currently, there is only one set of dma params for
  	 * each direction.  If more are added, this code will
  	 * have to be changed to select the proper set.
  	 */
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		dir = 0;
  	else
  		dir = 1;
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
  	/*
  	 * If the cpu dai should provide BCLK, but noone has provided the
  	 * divider needed for that to work, fall back to something sensible.
  	 */
  	cmr_div = ssc_p->cmr_div;
  	if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_CMR_DIV)) &&
  	    atmel_ssc_cbs(ssc_p)) {
  		int bclk_rate = snd_soc_params_to_bclk(params);
  
  		if (bclk_rate < 0) {
  			dev_err(dai->dev, "unable to calculate cmr_div: %d
  ",
  				bclk_rate);
  			return bclk_rate;
  		}
  
  		cmr_div = DIV_ROUND_CLOSEST(ssc_p->mck_rate, 2 * bclk_rate);
  	}
  
  	/*
  	 * If the cpu dai should provide LRCLK, but noone has provided the
  	 * dividers needed for that to work, fall back to something sensible.
  	 */
  	tcmr_period = ssc_p->tcmr_period;
  	rcmr_period = ssc_p->rcmr_period;
  	if (atmel_ssc_cfs(ssc_p)) {
  		int frame_size = snd_soc_params_to_frame_size(params);
  
  		if (frame_size < 0) {
  			dev_err(dai->dev,
  				"unable to calculate tx/rx cmr_period: %d
  ",
  				frame_size);
  			return frame_size;
  		}
  
  		if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_TCMR_PERIOD)))
  			tcmr_period = frame_size / 2 - 1;
  		if (!(ssc_p->forced_divider & BIT(ATMEL_SSC_RCMR_PERIOD)))
  			rcmr_period = frame_size / 2 - 1;
  	}
01f00d55a   Bo Shen   ASoC: atmel_ssc_d...
515
  	dma_params = ssc_p->dma_params[dir];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  
  	channels = params_channels(params);
  
  	/*
  	 * Determine sample size in bits and the PDC increment.
  	 */
  	switch (params_format(params)) {
  	case SNDRV_PCM_FORMAT_S8:
  		bits = 8;
  		dma_params->pdc_xfer_size = 1;
  		break;
  	case SNDRV_PCM_FORMAT_S16_LE:
  		bits = 16;
  		dma_params->pdc_xfer_size = 2;
  		break;
  	case SNDRV_PCM_FORMAT_S24_LE:
  		bits = 24;
  		dma_params->pdc_xfer_size = 4;
  		break;
  	case SNDRV_PCM_FORMAT_S32_LE:
  		bits = 32;
  		dma_params->pdc_xfer_size = 4;
  		break;
  	default:
  		printk(KERN_WARNING "atmel_ssc_dai: unsupported PCM format");
  		return -EINVAL;
  	}
  
  	/*
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
545
546
  	 * Compute SSC register settings.
  	 */
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
547

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
548
549
550
551
  	fslen_ext = (bits - 1) / 16;
  	fslen = (bits - 1) % 16;
  
  	switch (ssc_p->daifmt & SND_SOC_DAIFMT_FORMAT_MASK) {
376142b7b   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
552
553
554
555
556
557
558
559
  	case SND_SOC_DAIFMT_LEFT_J:
  		fs_osync = SSC_FSOS_POSITIVE;
  		fs_edge = SSC_START_RISING_RF;
  
  		rcmr =	  SSC_BF(RCMR_STTDLY, 0);
  		tcmr =	  SSC_BF(TCMR_STTDLY, 0);
  
  		break;
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
560
561
562
563
564
565
566
567
568
569
  	case SND_SOC_DAIFMT_I2S:
  		fs_osync = SSC_FSOS_NEGATIVE;
  		fs_edge = SSC_START_FALLING_RF;
  
  		rcmr =	  SSC_BF(RCMR_STTDLY, 1);
  		tcmr =	  SSC_BF(TCMR_STTDLY, 1);
  
  		break;
  
  	case SND_SOC_DAIFMT_DSP_A:
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
570
  		/*
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
571
  		 * DSP/PCM Mode A format
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
572
  		 *
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
573
574
575
  		 * Data is transferred on first BCLK after LRC pulse rising
  		 * edge.If stereo, the right channel data is contiguous with
  		 * the left channel data.
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
576
  		 */
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
577
578
579
  		fs_osync = SSC_FSOS_POSITIVE;
  		fs_edge = SSC_START_RISING_RF;
  		fslen = fslen_ext = 0;
dfaf53566   Bo Shen   ASoC: atmel_ssc_d...
580

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
581
582
  		rcmr =	  SSC_BF(RCMR_STTDLY, 1);
  		tcmr =	  SSC_BF(TCMR_STTDLY, 1);
dfaf53566   Bo Shen   ASoC: atmel_ssc_d...
583

6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
584
  		break;
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
585
586
587
588
589
590
  	default:
  		printk(KERN_WARNING "atmel_ssc_dai: unsupported DAI format 0x%x
  ",
  			ssc_p->daifmt);
  		return -EINVAL;
  	}
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
591

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
592
593
594
595
596
  	if (!atmel_ssc_cfs(ssc_p)) {
  		fslen = fslen_ext = 0;
  		rcmr_period = tcmr_period = 0;
  		fs_osync = SSC_FSOS_NONE;
  	}
eb5271497   Peter Rosin   ASoC: atmel_ssc_d...
597

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
598
599
  	rcmr |=	  SSC_BF(RCMR_START, fs_edge);
  	tcmr |=	  SSC_BF(TCMR_START, fs_edge);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
600

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
601
  	if (atmel_ssc_cbs(ssc_p)) {
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
602
  		/*
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
603
  		 * SSC provides BCLK
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
604
605
606
607
608
  		 *
  		 * The SSC transmit and receive clocks are generated from the
  		 * MCK divider, and the BCLK signal is output
  		 * on the SSC TK line.
  		 */
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
609
610
  		rcmr |=	  SSC_BF(RCMR_CKS, SSC_CKS_DIV)
  			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
611

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
612
613
614
615
616
617
  		tcmr |=	  SSC_BF(TCMR_CKS, SSC_CKS_DIV)
  			| SSC_BF(TCMR_CKO, SSC_CKO_CONTINUOUS);
  	} else {
  		rcmr |=	  SSC_BF(RCMR_CKS, ssc->clk_from_rk_pin ?
  					SSC_CKS_PIN : SSC_CKS_CLOCK)
  			| SSC_BF(RCMR_CKO, SSC_CKO_NONE);
f6a75d950   Zoltan Puskas   ASoC: atmel: Add ...
618

db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
  		tcmr |=	  SSC_BF(TCMR_CKS, ssc->clk_from_rk_pin ?
  					SSC_CKS_CLOCK : SSC_CKS_PIN)
  			| SSC_BF(TCMR_CKO, SSC_CKO_NONE);
  	}
  
  	rcmr |=	  SSC_BF(RCMR_PERIOD, rcmr_period)
  		| SSC_BF(RCMR_CKI, SSC_CKI_RISING);
  
  	tcmr |=   SSC_BF(TCMR_PERIOD, tcmr_period)
  		| SSC_BF(TCMR_CKI, SSC_CKI_FALLING);
  
  	rfmr =    SSC_BF(RFMR_FSLEN_EXT, fslen_ext)
  		| SSC_BF(RFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
  		| SSC_BF(RFMR_FSOS, fs_osync)
  		| SSC_BF(RFMR_FSLEN, fslen)
  		| SSC_BF(RFMR_DATNB, (channels - 1))
  		| SSC_BIT(RFMR_MSBF)
  		| SSC_BF(RFMR_LOOP, 0)
  		| SSC_BF(RFMR_DATLEN, (bits - 1));
  
  	tfmr =    SSC_BF(TFMR_FSLEN_EXT, fslen_ext)
  		| SSC_BF(TFMR_FSEDGE, SSC_FSEDGE_POSITIVE)
  		| SSC_BF(TFMR_FSDEN, 0)
  		| SSC_BF(TFMR_FSOS, fs_osync)
  		| SSC_BF(TFMR_FSLEN, fslen)
  		| SSC_BF(TFMR_DATNB, (channels - 1))
  		| SSC_BIT(TFMR_MSBF)
  		| SSC_BF(TFMR_DATDEF, 0)
  		| SSC_BF(TFMR_DATLEN, (bits - 1));
  
  	if (fslen_ext && !ssc->pdata->has_fslen_ext) {
  		dev_err(dai->dev, "sample size %d is too large for SSC device
  ",
  			bits);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
653
  		return -EINVAL;
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
654
  	}
db8e3e209   MichaÅ‚ MirosÅ‚aw   ASoC: atmel_ssc_d...
655

6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
656
657
658
659
660
661
  	pr_debug("atmel_ssc_hw_params: "
  			"RCMR=%08x RFMR=%08x TCMR=%08x TFMR=%08x
  ",
  			rcmr, rfmr, tcmr, tfmr);
  
  	if (!ssc_p->initialized) {
3fd5b30c5   Bo Shen   ASoC: atmel_ssc_d...
662
663
664
665
666
667
668
669
670
671
672
  		if (!ssc_p->ssc->pdata->use_dma) {
  			ssc_writel(ssc_p->ssc->regs, PDC_RPR, 0);
  			ssc_writel(ssc_p->ssc->regs, PDC_RCR, 0);
  			ssc_writel(ssc_p->ssc->regs, PDC_RNPR, 0);
  			ssc_writel(ssc_p->ssc->regs, PDC_RNCR, 0);
  
  			ssc_writel(ssc_p->ssc->regs, PDC_TPR, 0);
  			ssc_writel(ssc_p->ssc->regs, PDC_TCR, 0);
  			ssc_writel(ssc_p->ssc->regs, PDC_TNPR, 0);
  			ssc_writel(ssc_p->ssc->regs, PDC_TNCR, 0);
  		}
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
673
674
675
676
677
678
679
  
  		ret = request_irq(ssc_p->ssc->irq, atmel_ssc_interrupt, 0,
  				ssc_p->name, ssc_p);
  		if (ret < 0) {
  			printk(KERN_WARNING
  					"atmel_ssc_dai: request_irq failure
  ");
7bdeac2e6   Colin Ian King   ASoC: atmel_ssc_d...
680
681
  			pr_debug("Atmel_ssc_dai: Stopping clock
  ");
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
682
683
684
685
686
687
688
689
  			clk_disable(ssc_p->ssc->clk);
  			return ret;
  		}
  
  		ssc_p->initialized = 1;
  	}
  
  	/* set SSC clock mode register */
a85787eda   Peter Rosin   ASoC: atmel_ssc_d...
690
  	ssc_writel(ssc_p->ssc->regs, CMR, cmr_div);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
691
692
693
694
695
696
697
698
699
700
701
702
703
  
  	/* set receive clock mode and format */
  	ssc_writel(ssc_p->ssc->regs, RCMR, rcmr);
  	ssc_writel(ssc_p->ssc->regs, RFMR, rfmr);
  
  	/* set transmit clock mode and format */
  	ssc_writel(ssc_p->ssc->regs, TCMR, tcmr);
  	ssc_writel(ssc_p->ssc->regs, TFMR, tfmr);
  
  	pr_debug("atmel_ssc_dai,hw_params: SSC initialized
  ");
  	return 0;
  }
dee89c4d9   Mark Brown   ASoC: Merge snd_s...
704
705
  static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
  			     struct snd_soc_dai *dai)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
706
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
707
708
  	struct platform_device *pdev = to_platform_device(dai->dev);
  	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
709
710
711
712
713
714
715
716
717
  	struct atmel_pcm_dma_params *dma_params;
  	int dir;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		dir = 0;
  	else
  		dir = 1;
  
  	dma_params = ssc_p->dma_params[dir];
a8f1f100a   Bo Shen   ASoC: atmel_ssc_d...
718
  	ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
e01114345   Bo Shen   ASoC: atmel: disa...
719
  	ssc_writel(ssc_p->ssc->regs, IDR, dma_params->mask->ssc_error);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
720
721
722
723
724
725
726
  
  	pr_debug("%s enabled SSC_SR=0x%08x
  ",
  			dir ? "receive" : "transmit",
  			ssc_readl(ssc_p->ssc->regs, SR));
  	return 0;
  }
a8f1f100a   Bo Shen   ASoC: atmel_ssc_d...
727
728
729
  static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
  			     int cmd, struct snd_soc_dai *dai)
  {
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
730
731
  	struct platform_device *pdev = to_platform_device(dai->dev);
  	struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
a8f1f100a   Bo Shen   ASoC: atmel_ssc_d...
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
  	struct atmel_pcm_dma_params *dma_params;
  	int dir;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		dir = 0;
  	else
  		dir = 1;
  
  	dma_params = ssc_p->dma_params[dir];
  
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  	case SNDRV_PCM_TRIGGER_RESUME:
  	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  		ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
  		break;
  	default:
  		ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_disable);
  		break;
  	}
  
  	return 0;
  }
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
755
756
  
  #ifdef CONFIG_PM
e4f8bb37e   Kuninori Morimoto   ASoC: atmel: atme...
757
  static int atmel_ssc_suspend(struct snd_soc_component *component)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
758
759
  {
  	struct atmel_ssc_info *ssc_p;
e4f8bb37e   Kuninori Morimoto   ASoC: atmel: atme...
760
  	struct platform_device *pdev = to_platform_device(component->dev);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
761

e1c7e1faa   Kuninori Morimoto   ASoC: atomel: use...
762
  	if (!snd_soc_component_active(component))
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
763
  		return 0;
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
764
  	ssc_p = &ssc_info[pdev->id];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  
  	/* Save the status register before disabling transmit and receive */
  	ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
  	ssc_writel(ssc_p->ssc->regs, CR, SSC_BIT(CR_TXDIS) | SSC_BIT(CR_RXDIS));
  
  	/* Save the current interrupt mask, then disable unmasked interrupts */
  	ssc_p->ssc_state.ssc_imr = ssc_readl(ssc_p->ssc->regs, IMR);
  	ssc_writel(ssc_p->ssc->regs, IDR, ssc_p->ssc_state.ssc_imr);
  
  	ssc_p->ssc_state.ssc_cmr = ssc_readl(ssc_p->ssc->regs, CMR);
  	ssc_p->ssc_state.ssc_rcmr = ssc_readl(ssc_p->ssc->regs, RCMR);
  	ssc_p->ssc_state.ssc_rfmr = ssc_readl(ssc_p->ssc->regs, RFMR);
  	ssc_p->ssc_state.ssc_tcmr = ssc_readl(ssc_p->ssc->regs, TCMR);
  	ssc_p->ssc_state.ssc_tfmr = ssc_readl(ssc_p->ssc->regs, TFMR);
  
  	return 0;
  }
e4f8bb37e   Kuninori Morimoto   ASoC: atmel: atme...
782
  static int atmel_ssc_resume(struct snd_soc_component *component)
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
783
784
  {
  	struct atmel_ssc_info *ssc_p;
e4f8bb37e   Kuninori Morimoto   ASoC: atmel: atme...
785
  	struct platform_device *pdev = to_platform_device(component->dev);
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
786
  	u32 cr;
e1c7e1faa   Kuninori Morimoto   ASoC: atomel: use...
787
  	if (!snd_soc_component_active(component))
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
788
  		return 0;
c706f2e55   Songjun Wu   ASoC: atmel_ssc_d...
789
  	ssc_p = &ssc_info[pdev->id];
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
790
791
792
793
794
795
796
797
798
799
  
  	/* restore SSC register settings */
  	ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
  	ssc_writel(ssc_p->ssc->regs, TCMR, ssc_p->ssc_state.ssc_tcmr);
  	ssc_writel(ssc_p->ssc->regs, RFMR, ssc_p->ssc_state.ssc_rfmr);
  	ssc_writel(ssc_p->ssc->regs, RCMR, ssc_p->ssc_state.ssc_rcmr);
  	ssc_writel(ssc_p->ssc->regs, CMR, ssc_p->ssc_state.ssc_cmr);
  
  	/* re-enable interrupts */
  	ssc_writel(ssc_p->ssc->regs, IER, ssc_p->ssc_state.ssc_imr);
25985edce   Lucas De Marchi   Fix common misspe...
800
  	/* Re-enable receive and transmit as appropriate */
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
801
802
803
804
805
806
807
808
809
810
811
812
813
  	cr = 0;
  	cr |=
  	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_RXEN)) ? SSC_BIT(CR_RXEN) : 0;
  	cr |=
  	    (ssc_p->ssc_state.ssc_sr & SSC_BIT(SR_TXEN)) ? SSC_BIT(CR_TXEN) : 0;
  	ssc_writel(ssc_p->ssc->regs, CR, cr);
  
  	return 0;
  }
  #else /* CONFIG_PM */
  #  define atmel_ssc_suspend	NULL
  #  define atmel_ssc_resume	NULL
  #endif /* CONFIG_PM */
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
814
815
  #define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8     | SNDRV_PCM_FMTBIT_S16_LE |\
  			  SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
85e7652d8   Lars-Peter Clausen   ASoC: Constify sn...
816
  static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
6335d0554   Eric Miao   ASoC: make ops a ...
817
818
819
  	.startup	= atmel_ssc_startup,
  	.shutdown	= atmel_ssc_shutdown,
  	.prepare	= atmel_ssc_prepare,
a8f1f100a   Bo Shen   ASoC: atmel_ssc_d...
820
  	.trigger	= atmel_ssc_trigger,
6335d0554   Eric Miao   ASoC: make ops a ...
821
822
823
824
  	.hw_params	= atmel_ssc_hw_params,
  	.set_fmt	= atmel_ssc_set_dai_fmt,
  	.set_clkdiv	= atmel_ssc_set_dai_clkdiv,
  };
be681a827   Bo Shen   ASoC: atmel-ssc-d...
825
  static struct snd_soc_dai_driver atmel_ssc_dai = {
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
826
827
828
  		.playback = {
  			.channels_min = 1,
  			.channels_max = 2,
b6d6c6e95   Peter Rosin   ASoC: atmel_ssc_d...
829
830
831
  			.rates = SNDRV_PCM_RATE_CONTINUOUS,
  			.rate_min = 8000,
  			.rate_max = 384000,
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
832
833
834
835
  			.formats = ATMEL_SSC_FORMATS,},
  		.capture = {
  			.channels_min = 1,
  			.channels_max = 2,
b6d6c6e95   Peter Rosin   ASoC: atmel_ssc_d...
836
837
838
  			.rates = SNDRV_PCM_RATE_CONTINUOUS,
  			.rate_min = 8000,
  			.rate_max = 384000,
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
839
  			.formats = ATMEL_SSC_FORMATS,},
6335d0554   Eric Miao   ASoC: make ops a ...
840
  		.ops = &atmel_ssc_dai_ops,
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
841
  };
6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
842

a2c662c0e   Kuninori Morimoto   ASoC: switch over...
843
844
  static const struct snd_soc_component_driver atmel_ssc_component = {
  	.name		= "atmel-ssc",
e4f8bb37e   Kuninori Morimoto   ASoC: atmel: atme...
845
846
  	.suspend	= atmel_ssc_suspend,
  	.resume		= atmel_ssc_resume,
a2c662c0e   Kuninori Morimoto   ASoC: switch over...
847
  };
be681a827   Bo Shen   ASoC: atmel-ssc-d...
848
  static int asoc_ssc_init(struct device *dev)
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
849
  {
c03804781   Wolfram Sang   ASoC: atmel: simp...
850
  	struct ssc_device *ssc = dev_get_drvdata(dev);
be681a827   Bo Shen   ASoC: atmel-ssc-d...
851
  	int ret;
e14614dc5   Kuninori Morimoto   ASoC: atmel_ssc_d...
852
  	ret = devm_snd_soc_register_component(dev, &atmel_ssc_component,
a2c662c0e   Kuninori Morimoto   ASoC: switch over...
853
  					 &atmel_ssc_dai, 1);
be681a827   Bo Shen   ASoC: atmel-ssc-d...
854
855
856
  	if (ret) {
  		dev_err(dev, "Could not register DAI: %d
  ", ret);
e14614dc5   Kuninori Morimoto   ASoC: atmel_ssc_d...
857
  		return ret;
be681a827   Bo Shen   ASoC: atmel-ssc-d...
858
  	}
3951e4aae   Bo Shen   ASoC: atmel-pcm: ...
859
860
861
862
  	if (ssc->pdata->use_dma)
  		ret = atmel_pcm_dma_platform_register(dev);
  	else
  		ret = atmel_pcm_pdc_platform_register(dev);
be681a827   Bo Shen   ASoC: atmel-ssc-d...
863
864
865
  	if (ret) {
  		dev_err(dev, "Could not register PCM: %d
  ", ret);
e14614dc5   Kuninori Morimoto   ASoC: atmel_ssc_d...
866
  		return ret;
1d198f26c   Joe Perches   sound: Remove unn...
867
  	}
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
868

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
869
  	return 0;
be681a827   Bo Shen   ASoC: atmel-ssc-d...
870
  }
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
871

abfa4eae0   Mark Brown   ASoC: Add simplfi...
872
873
  /**
   * atmel_ssc_set_audio - Allocate the specified SSC for audio use.
0bdaef8a9   Pierre-Louis Bossart   ASoC: atmel: fix ...
874
   * @ssc_id: SSD ID in [0, NUM_SSC_DEVICES[
abfa4eae0   Mark Brown   ASoC: Add simplfi...
875
876
877
878
   */
  int atmel_ssc_set_audio(int ssc_id)
  {
  	struct ssc_device *ssc;
abfa4eae0   Mark Brown   ASoC: Add simplfi...
879
  	int ret;
abfa4eae0   Mark Brown   ASoC: Add simplfi...
880
881
  	/* If we can grab the SSC briefly to parent the DAI device off it */
  	ssc = ssc_request(ssc_id);
be681a827   Bo Shen   ASoC: atmel-ssc-d...
882
883
884
  	if (IS_ERR(ssc)) {
  		pr_err("Unable to parent ASoC SSC DAI on SSC: %ld
  ",
abfa4eae0   Mark Brown   ASoC: Add simplfi...
885
  			PTR_ERR(ssc));
be681a827   Bo Shen   ASoC: atmel-ssc-d...
886
887
888
  		return PTR_ERR(ssc);
  	} else {
  		ssc_info[ssc_id].ssc = ssc;
840d8e5e9   Joachim Eastwood   ASoC: atmel_ssc: ...
889
  	}
abfa4eae0   Mark Brown   ASoC: Add simplfi...
890

be681a827   Bo Shen   ASoC: atmel-ssc-d...
891
  	ret = asoc_ssc_init(&ssc->pdev->dev);
abfa4eae0   Mark Brown   ASoC: Add simplfi...
892
893
894
895
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(atmel_ssc_set_audio);
be681a827   Bo Shen   ASoC: atmel-ssc-d...
896
897
898
  void atmel_ssc_put_audio(int ssc_id)
  {
  	struct ssc_device *ssc = ssc_info[ssc_id].ssc;
69706028b   Bo Shen   ASoC: atmel_ssc_d...
899
  	ssc_free(ssc);
be681a827   Bo Shen   ASoC: atmel-ssc-d...
900
901
  }
  EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
3f4b783cf   Mark Brown   ASoC: Register pl...
902

6c7425095   Sedji Gaouaou   ASoC: Merge AT91 ...
903
904
905
906
  /* Module information */
  MODULE_AUTHOR("Sedji Gaouaou, sedji.gaouaou@atmel.com, www.atmel.com");
  MODULE_DESCRIPTION("ATMEL SSC ASoC Interface");
  MODULE_LICENSE("GPL");