diff options
author | Dong Aisheng <b29396@freescale.com> | 2011-09-07 08:51:50 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-09-19 13:31:59 -0400 |
commit | 76067540c642b1a14679ab74bd027a074c23e63b (patch) | |
tree | abfed6d3a416cd00904371cb4977e672e77230cf /sound/soc/mxs | |
parent | 5d42940c25ac69c4f5240392cf5e26bf08029e7a (diff) |
ASoC: mxs-saif: add record function
1. add different clkmux mode handling
SAIF can use two instances to implement full duplex (playback &
recording) and record saif may work on EXTMASTER mode which is
using other saif's BITCLK&LRCLK.
The clkmux mode could be set in pdata->init() in mach-specific code.
For generic saif driver, it only needs to know who is his master
and the master id is also provided in mach-specific code.
2. support playback and capture simutaneously however the sample
rates can not be different due to hw limitation.
Signed-off-by: Dong Aisheng <b29396@freescale.com>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
Acked-by: Liam Girdwood <lrg@ti.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/mxs')
-rw-r--r-- | sound/soc/mxs/mxs-saif.c | 145 | ||||
-rw-r--r-- | sound/soc/mxs/mxs-saif.h | 4 |
2 files changed, 135 insertions, 14 deletions
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c index af5734f6dab7..401944cf4560 100644 --- a/sound/soc/mxs/mxs-saif.c +++ b/sound/soc/mxs/mxs-saif.c | |||
@@ -23,10 +23,12 @@ | |||
23 | #include <linux/dma-mapping.h> | 23 | #include <linux/dma-mapping.h> |
24 | #include <linux/clk.h> | 24 | #include <linux/clk.h> |
25 | #include <linux/delay.h> | 25 | #include <linux/delay.h> |
26 | #include <linux/time.h> | ||
26 | #include <sound/core.h> | 27 | #include <sound/core.h> |
27 | #include <sound/pcm.h> | 28 | #include <sound/pcm.h> |
28 | #include <sound/pcm_params.h> | 29 | #include <sound/pcm_params.h> |
29 | #include <sound/soc.h> | 30 | #include <sound/soc.h> |
31 | #include <sound/saif.h> | ||
30 | #include <mach/dma.h> | 32 | #include <mach/dma.h> |
31 | #include <asm/mach-types.h> | 33 | #include <asm/mach-types.h> |
32 | #include <mach/hardware.h> | 34 | #include <mach/hardware.h> |
@@ -36,6 +38,24 @@ | |||
36 | 38 | ||
37 | static struct mxs_saif *mxs_saif[2]; | 39 | static struct mxs_saif *mxs_saif[2]; |
38 | 40 | ||
41 | /* | ||
42 | * SAIF is a little different with other normal SOC DAIs on clock using. | ||
43 | * | ||
44 | * For MXS, two SAIF modules are instantiated on-chip. | ||
45 | * Each SAIF has a set of clock pins and can be operating in master | ||
46 | * mode simultaneously if they are connected to different off-chip codecs. | ||
47 | * Also, one of the two SAIFs can master or drive the clock pins while the | ||
48 | * other SAIF, in slave mode, receives clocking from the master SAIF. | ||
49 | * This also means that both SAIFs must operate at the same sample rate. | ||
50 | * | ||
51 | * We abstract this as each saif has a master, the master could be | ||
52 | * himself or other saifs. In the generic saif driver, saif does not need | ||
53 | * to know the different clkmux. Saif only needs to know who is his master | ||
54 | * and operating his master to generate the proper clock rate for him. | ||
55 | * The master id is provided in mach-specific layer according to different | ||
56 | * clkmux setting. | ||
57 | */ | ||
58 | |||
39 | static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | 59 | static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, |
40 | int clk_id, unsigned int freq, int dir) | 60 | int clk_id, unsigned int freq, int dir) |
41 | { | 61 | { |
@@ -52,6 +72,17 @@ static int mxs_saif_set_dai_sysclk(struct snd_soc_dai *cpu_dai, | |||
52 | } | 72 | } |
53 | 73 | ||
54 | /* | 74 | /* |
75 | * Since SAIF may work on EXTMASTER mode, IOW, it's working BITCLK&LRCLK | ||
76 | * is provided by other SAIF, we provide a interface here to get its master | ||
77 | * from its master_id. | ||
78 | * Note that the master could be himself. | ||
79 | */ | ||
80 | static inline struct mxs_saif *mxs_saif_get_master(struct mxs_saif * saif) | ||
81 | { | ||
82 | return mxs_saif[saif->master_id]; | ||
83 | } | ||
84 | |||
85 | /* | ||
55 | * Set SAIF clock and MCLK | 86 | * Set SAIF clock and MCLK |
56 | */ | 87 | */ |
57 | static int mxs_saif_set_clk(struct mxs_saif *saif, | 88 | static int mxs_saif_set_clk(struct mxs_saif *saif, |
@@ -60,8 +91,26 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, | |||
60 | { | 91 | { |
61 | u32 scr; | 92 | u32 scr; |
62 | int ret; | 93 | int ret; |
94 | struct mxs_saif *master_saif; | ||
63 | 95 | ||
64 | scr = __raw_readl(saif->base + SAIF_CTRL); | 96 | dev_dbg(saif->dev, "mclk %d rate %d\n", mclk, rate); |
97 | |||
98 | /* Set master saif to generate proper clock */ | ||
99 | master_saif = mxs_saif_get_master(saif); | ||
100 | if (!master_saif) | ||
101 | return -EINVAL; | ||
102 | |||
103 | dev_dbg(saif->dev, "master saif%d\n", master_saif->id); | ||
104 | |||
105 | /* Checking if can playback and capture simutaneously */ | ||
106 | if (master_saif->ongoing && rate != master_saif->cur_rate) { | ||
107 | dev_err(saif->dev, | ||
108 | "can not change clock, master saif%d(rate %d) is ongoing\n", | ||
109 | master_saif->id, master_saif->cur_rate); | ||
110 | return -EINVAL; | ||
111 | } | ||
112 | |||
113 | scr = __raw_readl(master_saif->base + SAIF_CTRL); | ||
65 | scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; | 114 | scr &= ~BM_SAIF_CTRL_BITCLK_MULT_RATE; |
66 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; | 115 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; |
67 | 116 | ||
@@ -75,27 +124,29 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, | |||
75 | * | 124 | * |
76 | * If MCLK is not used, we just set saif clk to 512*fs. | 125 | * If MCLK is not used, we just set saif clk to 512*fs. |
77 | */ | 126 | */ |
78 | if (saif->mclk_in_use) { | 127 | if (master_saif->mclk_in_use) { |
79 | if (mclk % 32 == 0) { | 128 | if (mclk % 32 == 0) { |
80 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; | 129 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; |
81 | ret = clk_set_rate(saif->clk, 512 * rate); | 130 | ret = clk_set_rate(master_saif->clk, 512 * rate); |
82 | } else if (mclk % 48 == 0) { | 131 | } else if (mclk % 48 == 0) { |
83 | scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; | 132 | scr |= BM_SAIF_CTRL_BITCLK_BASE_RATE; |
84 | ret = clk_set_rate(saif->clk, 384 * rate); | 133 | ret = clk_set_rate(master_saif->clk, 384 * rate); |
85 | } else { | 134 | } else { |
86 | /* SAIF MCLK should be either 32x or 48x */ | 135 | /* SAIF MCLK should be either 32x or 48x */ |
87 | return -EINVAL; | 136 | return -EINVAL; |
88 | } | 137 | } |
89 | } else { | 138 | } else { |
90 | ret = clk_set_rate(saif->clk, 512 * rate); | 139 | ret = clk_set_rate(master_saif->clk, 512 * rate); |
91 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; | 140 | scr &= ~BM_SAIF_CTRL_BITCLK_BASE_RATE; |
92 | } | 141 | } |
93 | 142 | ||
94 | if (ret) | 143 | if (ret) |
95 | return ret; | 144 | return ret; |
96 | 145 | ||
97 | if (!saif->mclk_in_use) { | 146 | master_saif->cur_rate = rate; |
98 | __raw_writel(scr, saif->base + SAIF_CTRL); | 147 | |
148 | if (!master_saif->mclk_in_use) { | ||
149 | __raw_writel(scr, master_saif->base + SAIF_CTRL); | ||
99 | return 0; | 150 | return 0; |
100 | } | 151 | } |
101 | 152 | ||
@@ -137,7 +188,7 @@ static int mxs_saif_set_clk(struct mxs_saif *saif, | |||
137 | return -EINVAL; | 188 | return -EINVAL; |
138 | } | 189 | } |
139 | 190 | ||
140 | __raw_writel(scr, saif->base + SAIF_CTRL); | 191 | __raw_writel(scr, master_saif->base + SAIF_CTRL); |
141 | 192 | ||
142 | return 0; | 193 | return 0; |
143 | } | 194 | } |
@@ -183,6 +234,7 @@ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, | |||
183 | struct mxs_saif *saif = mxs_saif[saif_id]; | 234 | struct mxs_saif *saif = mxs_saif[saif_id]; |
184 | u32 stat; | 235 | u32 stat; |
185 | int ret; | 236 | int ret; |
237 | struct mxs_saif *master_saif; | ||
186 | 238 | ||
187 | if (!saif) | 239 | if (!saif) |
188 | return -EINVAL; | 240 | return -EINVAL; |
@@ -195,6 +247,12 @@ int mxs_saif_get_mclk(unsigned int saif_id, unsigned int mclk, | |||
195 | __raw_writel(BM_SAIF_CTRL_CLKGATE, | 247 | __raw_writel(BM_SAIF_CTRL_CLKGATE, |
196 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | 248 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); |
197 | 249 | ||
250 | master_saif = mxs_saif_get_master(saif); | ||
251 | if (saif != master_saif) { | ||
252 | dev_err(saif->dev, "can not get mclk from a non-master saif\n"); | ||
253 | return -EINVAL; | ||
254 | } | ||
255 | |||
198 | stat = __raw_readl(saif->base + SAIF_STAT); | 256 | stat = __raw_readl(saif->base + SAIF_STAT); |
199 | if (stat & BM_SAIF_STAT_BUSY) { | 257 | if (stat & BM_SAIF_STAT_BUSY) { |
200 | dev_err(saif->dev, "error: busy\n"); | 258 | dev_err(saif->dev, "error: busy\n"); |
@@ -278,10 +336,17 @@ static int mxs_saif_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) | |||
278 | /* | 336 | /* |
279 | * Note: We simply just support master mode since SAIF TX can only | 337 | * Note: We simply just support master mode since SAIF TX can only |
280 | * work as master. | 338 | * work as master. |
339 | * Here the master is relative to codec side. | ||
340 | * Saif internally could be slave when working on EXTMASTER mode. | ||
341 | * We just hide this to machine driver. | ||
281 | */ | 342 | */ |
282 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | 343 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
283 | case SND_SOC_DAIFMT_CBS_CFS: | 344 | case SND_SOC_DAIFMT_CBS_CFS: |
284 | scr &= ~BM_SAIF_CTRL_SLAVE_MODE; | 345 | if (saif->id == saif->master_id) |
346 | scr &= ~BM_SAIF_CTRL_SLAVE_MODE; | ||
347 | else | ||
348 | scr |= BM_SAIF_CTRL_SLAVE_MODE; | ||
349 | |||
285 | __raw_writel(scr | scr0, saif->base + SAIF_CTRL); | 350 | __raw_writel(scr | scr0, saif->base + SAIF_CTRL); |
286 | break; | 351 | break; |
287 | default: | 352 | default: |
@@ -396,6 +461,12 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, | |||
396 | struct snd_soc_dai *cpu_dai) | 461 | struct snd_soc_dai *cpu_dai) |
397 | { | 462 | { |
398 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); | 463 | struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai); |
464 | struct mxs_saif *master_saif; | ||
465 | u32 delay; | ||
466 | |||
467 | master_saif = mxs_saif_get_master(saif); | ||
468 | if (!master_saif) | ||
469 | return -EINVAL; | ||
399 | 470 | ||
400 | switch (cmd) { | 471 | switch (cmd) { |
401 | case SNDRV_PCM_TRIGGER_START: | 472 | case SNDRV_PCM_TRIGGER_START: |
@@ -403,10 +474,20 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, | |||
403 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | 474 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: |
404 | dev_dbg(cpu_dai->dev, "start\n"); | 475 | dev_dbg(cpu_dai->dev, "start\n"); |
405 | 476 | ||
406 | clk_enable(saif->clk); | 477 | clk_enable(master_saif->clk); |
407 | if (!saif->mclk_in_use) | 478 | if (!master_saif->mclk_in_use) |
479 | __raw_writel(BM_SAIF_CTRL_RUN, | ||
480 | master_saif->base + SAIF_CTRL + MXS_SET_ADDR); | ||
481 | |||
482 | /* | ||
483 | * If the saif's master is not himself, we also need to enable | ||
484 | * itself clk for its internal basic logic to work. | ||
485 | */ | ||
486 | if (saif != master_saif) { | ||
487 | clk_enable(saif->clk); | ||
408 | __raw_writel(BM_SAIF_CTRL_RUN, | 488 | __raw_writel(BM_SAIF_CTRL_RUN, |
409 | saif->base + SAIF_CTRL + MXS_SET_ADDR); | 489 | saif->base + SAIF_CTRL + MXS_SET_ADDR); |
490 | } | ||
410 | 491 | ||
411 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 492 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
412 | /* | 493 | /* |
@@ -422,20 +503,39 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd, | |||
422 | __raw_readl(saif->base + SAIF_DATA); | 503 | __raw_readl(saif->base + SAIF_DATA); |
423 | } | 504 | } |
424 | 505 | ||
425 | dev_dbg(cpu_dai->dev, "CTRL 0x%x STAT 0x%x\n", | 506 | master_saif->ongoing = 1; |
507 | |||
508 | dev_dbg(saif->dev, "CTRL 0x%x STAT 0x%x\n", | ||
426 | __raw_readl(saif->base + SAIF_CTRL), | 509 | __raw_readl(saif->base + SAIF_CTRL), |
427 | __raw_readl(saif->base + SAIF_STAT)); | 510 | __raw_readl(saif->base + SAIF_STAT)); |
428 | 511 | ||
512 | dev_dbg(master_saif->dev, "CTRL 0x%x STAT 0x%x\n", | ||
513 | __raw_readl(master_saif->base + SAIF_CTRL), | ||
514 | __raw_readl(master_saif->base + SAIF_STAT)); | ||
429 | break; | 515 | break; |
430 | case SNDRV_PCM_TRIGGER_SUSPEND: | 516 | case SNDRV_PCM_TRIGGER_SUSPEND: |
431 | case SNDRV_PCM_TRIGGER_STOP: | 517 | case SNDRV_PCM_TRIGGER_STOP: |
432 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 518 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
433 | dev_dbg(cpu_dai->dev, "stop\n"); | 519 | dev_dbg(cpu_dai->dev, "stop\n"); |
434 | 520 | ||
435 | clk_disable(saif->clk); | 521 | /* wait a while for the current sample to complete */ |
436 | if (!saif->mclk_in_use) | 522 | delay = USEC_PER_SEC / master_saif->cur_rate; |
523 | |||
524 | if (!master_saif->mclk_in_use) { | ||
525 | __raw_writel(BM_SAIF_CTRL_RUN, | ||
526 | master_saif->base + SAIF_CTRL + MXS_CLR_ADDR); | ||
527 | udelay(delay); | ||
528 | } | ||
529 | clk_disable(master_saif->clk); | ||
530 | |||
531 | if (saif != master_saif) { | ||
437 | __raw_writel(BM_SAIF_CTRL_RUN, | 532 | __raw_writel(BM_SAIF_CTRL_RUN, |
438 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); | 533 | saif->base + SAIF_CTRL + MXS_CLR_ADDR); |
534 | udelay(delay); | ||
535 | clk_disable(saif->clk); | ||
536 | } | ||
537 | |||
538 | master_saif->ongoing = 0; | ||
439 | 539 | ||
440 | break; | 540 | break; |
441 | default: | 541 | default: |
@@ -519,16 +619,33 @@ static int mxs_saif_probe(struct platform_device *pdev) | |||
519 | { | 619 | { |
520 | struct resource *res; | 620 | struct resource *res; |
521 | struct mxs_saif *saif; | 621 | struct mxs_saif *saif; |
622 | struct mxs_saif_platform_data *pdata; | ||
522 | int ret = 0; | 623 | int ret = 0; |
523 | 624 | ||
524 | if (pdev->id >= ARRAY_SIZE(mxs_saif)) | 625 | if (pdev->id >= ARRAY_SIZE(mxs_saif)) |
525 | return -EINVAL; | 626 | return -EINVAL; |
526 | 627 | ||
628 | pdata = pdev->dev.platform_data; | ||
629 | if (pdata && pdata->init) { | ||
630 | ret = pdata->init(); | ||
631 | if (ret) | ||
632 | return ret; | ||
633 | } | ||
634 | |||
527 | saif = kzalloc(sizeof(*saif), GFP_KERNEL); | 635 | saif = kzalloc(sizeof(*saif), GFP_KERNEL); |
528 | if (!saif) | 636 | if (!saif) |
529 | return -ENOMEM; | 637 | return -ENOMEM; |
530 | 638 | ||
531 | mxs_saif[pdev->id] = saif; | 639 | mxs_saif[pdev->id] = saif; |
640 | saif->id = pdev->id; | ||
641 | |||
642 | saif->master_id = saif->id; | ||
643 | if (pdata && pdata->get_master_id) { | ||
644 | saif->master_id = pdata->get_master_id(saif->id); | ||
645 | if (saif->master_id < 0 || | ||
646 | saif->master_id >= ARRAY_SIZE(mxs_saif)) | ||
647 | return -EINVAL; | ||
648 | } | ||
532 | 649 | ||
533 | saif->clk = clk_get(&pdev->dev, NULL); | 650 | saif->clk = clk_get(&pdev->dev, NULL); |
534 | if (IS_ERR(saif->clk)) { | 651 | if (IS_ERR(saif->clk)) { |
diff --git a/sound/soc/mxs/mxs-saif.h b/sound/soc/mxs/mxs-saif.h index 0e2ff8cdbfee..12c91e4eb941 100644 --- a/sound/soc/mxs/mxs-saif.h +++ b/sound/soc/mxs/mxs-saif.h | |||
@@ -118,6 +118,10 @@ struct mxs_saif { | |||
118 | void __iomem *base; | 118 | void __iomem *base; |
119 | int irq; | 119 | int irq; |
120 | struct mxs_pcm_dma_params dma_param; | 120 | struct mxs_pcm_dma_params dma_param; |
121 | unsigned int id; | ||
122 | unsigned int master_id; | ||
123 | unsigned int cur_rate; | ||
124 | unsigned int ongoing; | ||
121 | 125 | ||
122 | struct platform_device *soc_platform_pdev; | 126 | struct platform_device *soc_platform_pdev; |
123 | u32 fifo_underrun; | 127 | u32 fifo_underrun; |