Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/soc/omap/omap-mcbsp.c
10817 views
1
/*
2
* omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port
3
*
4
* Copyright (C) 2008 Nokia Corporation
5
*
6
* Contact: Jarkko Nikula <[email protected]>
7
* Peter Ujfalusi <[email protected]>
8
*
9
* This program is free software; you can redistribute it and/or
10
* modify it under the terms of the GNU General Public License
11
* version 2 as published by the Free Software Foundation.
12
*
13
* This program is distributed in the hope that it will be useful, but
14
* WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
* General Public License for more details.
17
*
18
* You should have received a copy of the GNU General Public License
19
* along with this program; if not, write to the Free Software
20
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21
* 02110-1301 USA
22
*
23
*/
24
25
#include <linux/init.h>
26
#include <linux/module.h>
27
#include <linux/device.h>
28
#include <sound/core.h>
29
#include <sound/pcm.h>
30
#include <sound/pcm_params.h>
31
#include <sound/initval.h>
32
#include <sound/soc.h>
33
34
#include <plat/dma.h>
35
#include <plat/mcbsp.h>
36
#include "omap-mcbsp.h"
37
#include "omap-pcm.h"
38
39
#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000)
40
41
#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \
42
xhandler_get, xhandler_put) \
43
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
44
.info = omap_mcbsp_st_info_volsw, \
45
.get = xhandler_get, .put = xhandler_put, \
46
.private_value = (unsigned long) &(struct soc_mixer_control) \
47
{.min = xmin, .max = xmax} }
48
49
struct omap_mcbsp_data {
50
unsigned int bus_id;
51
struct omap_mcbsp_reg_cfg regs;
52
unsigned int fmt;
53
/*
54
* Flags indicating is the bus already activated and configured by
55
* another substream
56
*/
57
int active;
58
int configured;
59
unsigned int in_freq;
60
int clk_div;
61
int wlen;
62
};
63
64
static struct omap_mcbsp_data mcbsp_data[NUM_LINKS];
65
66
/*
67
* Stream DMA parameters. DMA request line and port address are set runtime
68
* since they are different between OMAP1 and later OMAPs
69
*/
70
static struct omap_pcm_dma_data omap_mcbsp_dai_dma_params[NUM_LINKS][2];
71
72
static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream)
73
{
74
struct snd_soc_pcm_runtime *rtd = substream->private_data;
75
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
76
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
77
struct omap_pcm_dma_data *dma_data;
78
int dma_op_mode = omap_mcbsp_get_dma_op_mode(mcbsp_data->bus_id);
79
int words;
80
81
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
82
83
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
84
if (dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
85
/*
86
* Configure McBSP threshold based on either:
87
* packet_size, when the sDMA is in packet mode, or
88
* based on the period size.
89
*/
90
if (dma_data->packet_size)
91
words = dma_data->packet_size;
92
else
93
words = snd_pcm_lib_period_bytes(substream) /
94
(mcbsp_data->wlen / 8);
95
else
96
words = 1;
97
98
/* Configure McBSP internal buffer usage */
99
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
100
omap_mcbsp_set_tx_threshold(mcbsp_data->bus_id, words);
101
else
102
omap_mcbsp_set_rx_threshold(mcbsp_data->bus_id, words);
103
}
104
105
static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params,
106
struct snd_pcm_hw_rule *rule)
107
{
108
struct snd_interval *buffer_size = hw_param_interval(params,
109
SNDRV_PCM_HW_PARAM_BUFFER_SIZE);
110
struct snd_interval *channels = hw_param_interval(params,
111
SNDRV_PCM_HW_PARAM_CHANNELS);
112
struct omap_mcbsp_data *mcbsp_data = rule->private;
113
struct snd_interval frames;
114
int size;
115
116
snd_interval_any(&frames);
117
size = omap_mcbsp_get_fifo_size(mcbsp_data->bus_id);
118
119
frames.min = size / channels->min;
120
frames.integer = 1;
121
return snd_interval_refine(buffer_size, &frames);
122
}
123
124
static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
125
struct snd_soc_dai *cpu_dai)
126
{
127
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
128
int bus_id = mcbsp_data->bus_id;
129
int err = 0;
130
131
if (!cpu_dai->active)
132
err = omap_mcbsp_request(bus_id);
133
134
/*
135
* OMAP3 McBSP FIFO is word structured.
136
* McBSP2 has 1024 + 256 = 1280 word long buffer,
137
* McBSP1,3,4,5 has 128 word long buffer
138
* This means that the size of the FIFO depends on the sample format.
139
* For example on McBSP3:
140
* 16bit samples: size is 128 * 2 = 256 bytes
141
* 32bit samples: size is 128 * 4 = 512 bytes
142
* It is simpler to place constraint for buffer and period based on
143
* channels.
144
* McBSP3 as example again (16 or 32 bit samples):
145
* 1 channel (mono): size is 128 frames (128 words)
146
* 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words)
147
* 4 channels: size is 128 / 4 = 32 frames (4 * 32 words)
148
*/
149
if (cpu_is_omap34xx() || cpu_is_omap44xx()) {
150
/*
151
* Rule for the buffer size. We should not allow
152
* smaller buffer than the FIFO size to avoid underruns
153
*/
154
snd_pcm_hw_rule_add(substream->runtime, 0,
155
SNDRV_PCM_HW_PARAM_CHANNELS,
156
omap_mcbsp_hwrule_min_buffersize,
157
mcbsp_data,
158
SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1);
159
160
/* Make sure, that the period size is always even */
161
snd_pcm_hw_constraint_step(substream->runtime, 0,
162
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2);
163
}
164
165
return err;
166
}
167
168
static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream,
169
struct snd_soc_dai *cpu_dai)
170
{
171
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
172
173
if (!cpu_dai->active) {
174
omap_mcbsp_free(mcbsp_data->bus_id);
175
mcbsp_data->configured = 0;
176
}
177
}
178
179
static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd,
180
struct snd_soc_dai *cpu_dai)
181
{
182
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
183
int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
184
185
switch (cmd) {
186
case SNDRV_PCM_TRIGGER_START:
187
case SNDRV_PCM_TRIGGER_RESUME:
188
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
189
mcbsp_data->active++;
190
omap_mcbsp_start(mcbsp_data->bus_id, play, !play);
191
break;
192
193
case SNDRV_PCM_TRIGGER_STOP:
194
case SNDRV_PCM_TRIGGER_SUSPEND:
195
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
196
omap_mcbsp_stop(mcbsp_data->bus_id, play, !play);
197
mcbsp_data->active--;
198
break;
199
default:
200
err = -EINVAL;
201
}
202
203
return err;
204
}
205
206
static snd_pcm_sframes_t omap_mcbsp_dai_delay(
207
struct snd_pcm_substream *substream,
208
struct snd_soc_dai *dai)
209
{
210
struct snd_soc_pcm_runtime *rtd = substream->private_data;
211
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
212
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
213
u16 fifo_use;
214
snd_pcm_sframes_t delay;
215
216
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
217
fifo_use = omap_mcbsp_get_tx_delay(mcbsp_data->bus_id);
218
else
219
fifo_use = omap_mcbsp_get_rx_delay(mcbsp_data->bus_id);
220
221
/*
222
* Divide the used locations with the channel count to get the
223
* FIFO usage in samples (don't care about partial samples in the
224
* buffer).
225
*/
226
delay = fifo_use / substream->runtime->channels;
227
228
return delay;
229
}
230
231
static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
232
struct snd_pcm_hw_params *params,
233
struct snd_soc_dai *cpu_dai)
234
{
235
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
236
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
237
struct omap_pcm_dma_data *dma_data;
238
int dma, bus_id = mcbsp_data->bus_id;
239
int wlen, channels, wpf, sync_mode = OMAP_DMA_SYNC_ELEMENT;
240
int pkt_size = 0;
241
unsigned long port;
242
unsigned int format, div, framesize, master;
243
244
dma_data = &omap_mcbsp_dai_dma_params[cpu_dai->id][substream->stream];
245
246
dma = omap_mcbsp_dma_ch_params(bus_id, substream->stream);
247
port = omap_mcbsp_dma_reg_params(bus_id, substream->stream);
248
249
switch (params_format(params)) {
250
case SNDRV_PCM_FORMAT_S16_LE:
251
dma_data->data_type = OMAP_DMA_DATA_TYPE_S16;
252
wlen = 16;
253
break;
254
case SNDRV_PCM_FORMAT_S32_LE:
255
dma_data->data_type = OMAP_DMA_DATA_TYPE_S32;
256
wlen = 32;
257
break;
258
default:
259
return -EINVAL;
260
}
261
if (cpu_is_omap34xx()) {
262
dma_data->set_threshold = omap_mcbsp_set_threshold;
263
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
264
if (omap_mcbsp_get_dma_op_mode(bus_id) ==
265
MCBSP_DMA_MODE_THRESHOLD) {
266
int period_words, max_thrsh;
267
268
period_words = params_period_bytes(params) / (wlen / 8);
269
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
270
max_thrsh = omap_mcbsp_get_max_tx_threshold(
271
mcbsp_data->bus_id);
272
else
273
max_thrsh = omap_mcbsp_get_max_rx_threshold(
274
mcbsp_data->bus_id);
275
/*
276
* If the period contains less or equal number of words,
277
* we are using the original threshold mode setup:
278
* McBSP threshold = sDMA frame size = period_size
279
* Otherwise we switch to sDMA packet mode:
280
* McBSP threshold = sDMA packet size
281
* sDMA frame size = period size
282
*/
283
if (period_words > max_thrsh) {
284
int divider = 0;
285
286
/*
287
* Look for the biggest threshold value, which
288
* divides the period size evenly.
289
*/
290
divider = period_words / max_thrsh;
291
if (period_words % max_thrsh)
292
divider++;
293
while (period_words % divider &&
294
divider < period_words)
295
divider++;
296
if (divider == period_words)
297
return -EINVAL;
298
299
pkt_size = period_words / divider;
300
sync_mode = OMAP_DMA_SYNC_PACKET;
301
} else {
302
sync_mode = OMAP_DMA_SYNC_FRAME;
303
}
304
}
305
}
306
307
dma_data->name = substream->stream ? "Audio Capture" : "Audio Playback";
308
dma_data->dma_req = dma;
309
dma_data->port_addr = port;
310
dma_data->sync_mode = sync_mode;
311
dma_data->packet_size = pkt_size;
312
313
snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data);
314
315
if (mcbsp_data->configured) {
316
/* McBSP already configured by another stream */
317
return 0;
318
}
319
320
format = mcbsp_data->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
321
wpf = channels = params_channels(params);
322
if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
323
format == SND_SOC_DAIFMT_LEFT_J)) {
324
/* Use dual-phase frames */
325
regs->rcr2 |= RPHASE;
326
regs->xcr2 |= XPHASE;
327
/* Set 1 word per (McBSP) frame for phase1 and phase2 */
328
wpf--;
329
regs->rcr2 |= RFRLEN2(wpf - 1);
330
regs->xcr2 |= XFRLEN2(wpf - 1);
331
}
332
333
regs->rcr1 |= RFRLEN1(wpf - 1);
334
regs->xcr1 |= XFRLEN1(wpf - 1);
335
336
switch (params_format(params)) {
337
case SNDRV_PCM_FORMAT_S16_LE:
338
/* Set word lengths */
339
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16);
340
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16);
341
regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16);
342
regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16);
343
break;
344
case SNDRV_PCM_FORMAT_S32_LE:
345
/* Set word lengths */
346
regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32);
347
regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32);
348
regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32);
349
regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32);
350
break;
351
default:
352
/* Unsupported PCM format */
353
return -EINVAL;
354
}
355
356
/* In McBSP master modes, FRAME (i.e. sample rate) is generated
357
* by _counting_ BCLKs. Calculate frame size in BCLKs */
358
master = mcbsp_data->fmt & SND_SOC_DAIFMT_MASTER_MASK;
359
if (master == SND_SOC_DAIFMT_CBS_CFS) {
360
div = mcbsp_data->clk_div ? mcbsp_data->clk_div : 1;
361
framesize = (mcbsp_data->in_freq / div) / params_rate(params);
362
363
if (framesize < wlen * channels) {
364
printk(KERN_ERR "%s: not enough bandwidth for desired rate and "
365
"channels\n", __func__);
366
return -EINVAL;
367
}
368
} else
369
framesize = wlen * channels;
370
371
/* Set FS period and length in terms of bit clock periods */
372
switch (format) {
373
case SND_SOC_DAIFMT_I2S:
374
case SND_SOC_DAIFMT_LEFT_J:
375
regs->srgr2 |= FPER(framesize - 1);
376
regs->srgr1 |= FWID((framesize >> 1) - 1);
377
break;
378
case SND_SOC_DAIFMT_DSP_A:
379
case SND_SOC_DAIFMT_DSP_B:
380
regs->srgr2 |= FPER(framesize - 1);
381
regs->srgr1 |= FWID(0);
382
break;
383
}
384
385
omap_mcbsp_config(bus_id, &mcbsp_data->regs);
386
mcbsp_data->wlen = wlen;
387
mcbsp_data->configured = 1;
388
389
return 0;
390
}
391
392
/*
393
* This must be called before _set_clkdiv and _set_sysclk since McBSP register
394
* cache is initialized here
395
*/
396
static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
397
unsigned int fmt)
398
{
399
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
400
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
401
unsigned int temp_fmt = fmt;
402
403
if (mcbsp_data->configured)
404
return 0;
405
406
mcbsp_data->fmt = fmt;
407
memset(regs, 0, sizeof(*regs));
408
/* Generic McBSP register settings */
409
regs->spcr2 |= XINTM(3) | FREE;
410
regs->spcr1 |= RINTM(3);
411
/* RFIG and XFIG are not defined in 34xx */
412
if (!cpu_is_omap34xx() && !cpu_is_omap44xx()) {
413
regs->rcr2 |= RFIG;
414
regs->xcr2 |= XFIG;
415
}
416
if (cpu_is_omap2430() || cpu_is_omap34xx() || cpu_is_omap44xx()) {
417
regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE;
418
regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE;
419
}
420
421
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
422
case SND_SOC_DAIFMT_I2S:
423
/* 1-bit data delay */
424
regs->rcr2 |= RDATDLY(1);
425
regs->xcr2 |= XDATDLY(1);
426
break;
427
case SND_SOC_DAIFMT_LEFT_J:
428
/* 0-bit data delay */
429
regs->rcr2 |= RDATDLY(0);
430
regs->xcr2 |= XDATDLY(0);
431
regs->spcr1 |= RJUST(2);
432
/* Invert FS polarity configuration */
433
temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
434
break;
435
case SND_SOC_DAIFMT_DSP_A:
436
/* 1-bit data delay */
437
regs->rcr2 |= RDATDLY(1);
438
regs->xcr2 |= XDATDLY(1);
439
/* Invert FS polarity configuration */
440
temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
441
break;
442
case SND_SOC_DAIFMT_DSP_B:
443
/* 0-bit data delay */
444
regs->rcr2 |= RDATDLY(0);
445
regs->xcr2 |= XDATDLY(0);
446
/* Invert FS polarity configuration */
447
temp_fmt ^= SND_SOC_DAIFMT_NB_IF;
448
break;
449
default:
450
/* Unsupported data format */
451
return -EINVAL;
452
}
453
454
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
455
case SND_SOC_DAIFMT_CBS_CFS:
456
/* McBSP master. Set FS and bit clocks as outputs */
457
regs->pcr0 |= FSXM | FSRM |
458
CLKXM | CLKRM;
459
/* Sample rate generator drives the FS */
460
regs->srgr2 |= FSGM;
461
break;
462
case SND_SOC_DAIFMT_CBM_CFM:
463
/* McBSP slave */
464
break;
465
default:
466
/* Unsupported master/slave configuration */
467
return -EINVAL;
468
}
469
470
/* Set bit clock (CLKX/CLKR) and FS polarities */
471
switch (temp_fmt & SND_SOC_DAIFMT_INV_MASK) {
472
case SND_SOC_DAIFMT_NB_NF:
473
/*
474
* Normal BCLK + FS.
475
* FS active low. TX data driven on falling edge of bit clock
476
* and RX data sampled on rising edge of bit clock.
477
*/
478
regs->pcr0 |= FSXP | FSRP |
479
CLKXP | CLKRP;
480
break;
481
case SND_SOC_DAIFMT_NB_IF:
482
regs->pcr0 |= CLKXP | CLKRP;
483
break;
484
case SND_SOC_DAIFMT_IB_NF:
485
regs->pcr0 |= FSXP | FSRP;
486
break;
487
case SND_SOC_DAIFMT_IB_IF:
488
break;
489
default:
490
return -EINVAL;
491
}
492
493
return 0;
494
}
495
496
static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai,
497
int div_id, int div)
498
{
499
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
500
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
501
502
if (div_id != OMAP_MCBSP_CLKGDV)
503
return -ENODEV;
504
505
mcbsp_data->clk_div = div;
506
regs->srgr1 |= CLKGDV(div - 1);
507
508
return 0;
509
}
510
511
static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
512
int clk_id, unsigned int freq,
513
int dir)
514
{
515
struct omap_mcbsp_data *mcbsp_data = snd_soc_dai_get_drvdata(cpu_dai);
516
struct omap_mcbsp_reg_cfg *regs = &mcbsp_data->regs;
517
int err = 0;
518
519
/* The McBSP signal muxing functions are only available on McBSP1 */
520
if (clk_id == OMAP_MCBSP_CLKR_SRC_CLKR ||
521
clk_id == OMAP_MCBSP_CLKR_SRC_CLKX ||
522
clk_id == OMAP_MCBSP_FSR_SRC_FSR ||
523
clk_id == OMAP_MCBSP_FSR_SRC_FSX)
524
if (cpu_class_is_omap1() || mcbsp_data->bus_id != 0)
525
return -EINVAL;
526
527
mcbsp_data->in_freq = freq;
528
529
switch (clk_id) {
530
case OMAP_MCBSP_SYSCLK_CLK:
531
regs->srgr2 |= CLKSM;
532
break;
533
case OMAP_MCBSP_SYSCLK_CLKS_FCLK:
534
if (cpu_class_is_omap1()) {
535
err = -EINVAL;
536
break;
537
}
538
err = omap2_mcbsp_set_clks_src(mcbsp_data->bus_id,
539
MCBSP_CLKS_PRCM_SRC);
540
break;
541
case OMAP_MCBSP_SYSCLK_CLKS_EXT:
542
if (cpu_class_is_omap1()) {
543
err = 0;
544
break;
545
}
546
err = omap2_mcbsp_set_clks_src(mcbsp_data->bus_id,
547
MCBSP_CLKS_PAD_SRC);
548
break;
549
550
case OMAP_MCBSP_SYSCLK_CLKX_EXT:
551
regs->srgr2 |= CLKSM;
552
case OMAP_MCBSP_SYSCLK_CLKR_EXT:
553
regs->pcr0 |= SCLKME;
554
break;
555
556
557
case OMAP_MCBSP_CLKR_SRC_CLKR:
558
if (cpu_class_is_omap1())
559
break;
560
omap2_mcbsp1_mux_clkr_src(CLKR_SRC_CLKR);
561
break;
562
case OMAP_MCBSP_CLKR_SRC_CLKX:
563
if (cpu_class_is_omap1())
564
break;
565
omap2_mcbsp1_mux_clkr_src(CLKR_SRC_CLKX);
566
break;
567
case OMAP_MCBSP_FSR_SRC_FSR:
568
if (cpu_class_is_omap1())
569
break;
570
omap2_mcbsp1_mux_fsr_src(FSR_SRC_FSR);
571
break;
572
case OMAP_MCBSP_FSR_SRC_FSX:
573
if (cpu_class_is_omap1())
574
break;
575
omap2_mcbsp1_mux_fsr_src(FSR_SRC_FSX);
576
break;
577
default:
578
err = -ENODEV;
579
}
580
581
return err;
582
}
583
584
static struct snd_soc_dai_ops mcbsp_dai_ops = {
585
.startup = omap_mcbsp_dai_startup,
586
.shutdown = omap_mcbsp_dai_shutdown,
587
.trigger = omap_mcbsp_dai_trigger,
588
.delay = omap_mcbsp_dai_delay,
589
.hw_params = omap_mcbsp_dai_hw_params,
590
.set_fmt = omap_mcbsp_dai_set_dai_fmt,
591
.set_clkdiv = omap_mcbsp_dai_set_clkdiv,
592
.set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
593
};
594
595
static int mcbsp_dai_probe(struct snd_soc_dai *dai)
596
{
597
mcbsp_data[dai->id].bus_id = dai->id;
598
snd_soc_dai_set_drvdata(dai, &mcbsp_data[dai->id].bus_id);
599
return 0;
600
}
601
602
static struct snd_soc_dai_driver omap_mcbsp_dai =
603
{
604
.probe = mcbsp_dai_probe,
605
.playback = {
606
.channels_min = 1,
607
.channels_max = 16,
608
.rates = OMAP_MCBSP_RATES,
609
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
610
},
611
.capture = {
612
.channels_min = 1,
613
.channels_max = 16,
614
.rates = OMAP_MCBSP_RATES,
615
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
616
},
617
.ops = &mcbsp_dai_ops,
618
};
619
620
static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol,
621
struct snd_ctl_elem_info *uinfo)
622
{
623
struct soc_mixer_control *mc =
624
(struct soc_mixer_control *)kcontrol->private_value;
625
int max = mc->max;
626
int min = mc->min;
627
628
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
629
uinfo->count = 1;
630
uinfo->value.integer.min = min;
631
uinfo->value.integer.max = max;
632
return 0;
633
}
634
635
#define OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(id, channel) \
636
static int \
637
omap_mcbsp##id##_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \
638
struct snd_ctl_elem_value *uc) \
639
{ \
640
struct soc_mixer_control *mc = \
641
(struct soc_mixer_control *)kc->private_value; \
642
int max = mc->max; \
643
int min = mc->min; \
644
int val = uc->value.integer.value[0]; \
645
\
646
if (val < min || val > max) \
647
return -EINVAL; \
648
\
649
/* OMAP McBSP implementation uses index values 0..4 */ \
650
return omap_st_set_chgain((id)-1, channel, val); \
651
}
652
653
#define OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(id, channel) \
654
static int \
655
omap_mcbsp##id##_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \
656
struct snd_ctl_elem_value *uc) \
657
{ \
658
s16 chgain; \
659
\
660
if (omap_st_get_chgain((id)-1, channel, &chgain)) \
661
return -EAGAIN; \
662
\
663
uc->value.integer.value[0] = chgain; \
664
return 0; \
665
}
666
667
OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 0)
668
OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(2, 1)
669
OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 0)
670
OMAP_MCBSP_ST_SET_CHANNEL_VOLUME(3, 1)
671
OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 0)
672
OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(2, 1)
673
OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 0)
674
OMAP_MCBSP_ST_GET_CHANNEL_VOLUME(3, 1)
675
676
static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol,
677
struct snd_ctl_elem_value *ucontrol)
678
{
679
struct soc_mixer_control *mc =
680
(struct soc_mixer_control *)kcontrol->private_value;
681
u8 value = ucontrol->value.integer.value[0];
682
683
if (value == omap_st_is_enabled(mc->reg))
684
return 0;
685
686
if (value)
687
omap_st_enable(mc->reg);
688
else
689
omap_st_disable(mc->reg);
690
691
return 1;
692
}
693
694
static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol,
695
struct snd_ctl_elem_value *ucontrol)
696
{
697
struct soc_mixer_control *mc =
698
(struct soc_mixer_control *)kcontrol->private_value;
699
700
ucontrol->value.integer.value[0] = omap_st_is_enabled(mc->reg);
701
return 0;
702
}
703
704
static const struct snd_kcontrol_new omap_mcbsp2_st_controls[] = {
705
SOC_SINGLE_EXT("McBSP2 Sidetone Switch", 1, 0, 1, 0,
706
omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),
707
OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 0 Volume",
708
-32768, 32767,
709
omap_mcbsp2_get_st_ch0_volume,
710
omap_mcbsp2_set_st_ch0_volume),
711
OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP2 Sidetone Channel 1 Volume",
712
-32768, 32767,
713
omap_mcbsp2_get_st_ch1_volume,
714
omap_mcbsp2_set_st_ch1_volume),
715
};
716
717
static const struct snd_kcontrol_new omap_mcbsp3_st_controls[] = {
718
SOC_SINGLE_EXT("McBSP3 Sidetone Switch", 2, 0, 1, 0,
719
omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode),
720
OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 0 Volume",
721
-32768, 32767,
722
omap_mcbsp3_get_st_ch0_volume,
723
omap_mcbsp3_set_st_ch0_volume),
724
OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP3 Sidetone Channel 1 Volume",
725
-32768, 32767,
726
omap_mcbsp3_get_st_ch1_volume,
727
omap_mcbsp3_set_st_ch1_volume),
728
};
729
730
int omap_mcbsp_st_add_controls(struct snd_soc_codec *codec, int mcbsp_id)
731
{
732
if (!cpu_is_omap34xx())
733
return -ENODEV;
734
735
switch (mcbsp_id) {
736
case 1: /* McBSP 2 */
737
return snd_soc_add_controls(codec, omap_mcbsp2_st_controls,
738
ARRAY_SIZE(omap_mcbsp2_st_controls));
739
case 2: /* McBSP 3 */
740
return snd_soc_add_controls(codec, omap_mcbsp3_st_controls,
741
ARRAY_SIZE(omap_mcbsp3_st_controls));
742
default:
743
break;
744
}
745
746
return -EINVAL;
747
}
748
EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls);
749
750
static __devinit int asoc_mcbsp_probe(struct platform_device *pdev)
751
{
752
return snd_soc_register_dai(&pdev->dev, &omap_mcbsp_dai);
753
}
754
755
static int __devexit asoc_mcbsp_remove(struct platform_device *pdev)
756
{
757
snd_soc_unregister_dai(&pdev->dev);
758
return 0;
759
}
760
761
static struct platform_driver asoc_mcbsp_driver = {
762
.driver = {
763
.name = "omap-mcbsp-dai",
764
.owner = THIS_MODULE,
765
},
766
767
.probe = asoc_mcbsp_probe,
768
.remove = __devexit_p(asoc_mcbsp_remove),
769
};
770
771
static int __init snd_omap_mcbsp_init(void)
772
{
773
return platform_driver_register(&asoc_mcbsp_driver);
774
}
775
module_init(snd_omap_mcbsp_init);
776
777
static void __exit snd_omap_mcbsp_exit(void)
778
{
779
platform_driver_unregister(&asoc_mcbsp_driver);
780
}
781
module_exit(snd_omap_mcbsp_exit);
782
783
MODULE_AUTHOR("Jarkko Nikula <[email protected]>");
784
MODULE_DESCRIPTION("OMAP I2S SoC Interface");
785
MODULE_LICENSE("GPL");
786
787