aboutsummaryrefslogtreecommitdiffstats
path: root/sound/arm/aaci.c
diff options
context:
space:
mode:
authorKevin Hilman <khilman@mvista.com>2007-02-06 23:45:32 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2007-02-18 05:59:49 -0500
commit41762b8ca9e16c7443d8348ec53daddbe940cdcc (patch)
treebec07a1eac2a998d332c2f3870f85467d6550993 /sound/arm/aaci.c
parent62578cbfaa50df06b3bb6e4231adc3b911a3d4b4 (diff)
[ARM] 4139/1: AACI record support
Add PCM audio capture support for AACI audio on Versatile platform. Signed-off-by: Kevin Hilman <khilman@mvista.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'sound/arm/aaci.c')
-rw-r--r--sound/arm/aaci.c258
1 files changed, 232 insertions, 26 deletions
diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c
index b85df793cdce..a032aee82adf 100644
--- a/sound/arm/aaci.c
+++ b/sound/arm/aaci.c
@@ -166,6 +166,65 @@ static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun)
166 */ 166 */
167static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask) 167static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
168{ 168{
169 if (mask & ISR_ORINTR) {
170 dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel);
171 writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR);
172 }
173
174 if (mask & ISR_RXTOINTR) {
175 dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel);
176 writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR);
177 }
178
179 if (mask & ISR_RXINTR) {
180 struct aaci_runtime *aacirun = &aaci->capture;
181 void *ptr;
182
183 if (!aacirun->substream || !aacirun->start) {
184 dev_warn(&aaci->dev->dev, "RX interrupt???");
185 writel(0, aacirun->base + AACI_IE);
186 return;
187 }
188 ptr = aacirun->ptr;
189
190 do {
191 unsigned int len = aacirun->fifosz;
192 u32 val;
193
194 if (aacirun->bytes <= 0) {
195 aacirun->bytes += aacirun->period;
196 aacirun->ptr = ptr;
197 spin_unlock(&aaci->lock);
198 snd_pcm_period_elapsed(aacirun->substream);
199 spin_lock(&aaci->lock);
200 }
201 if (!(aacirun->cr & CR_EN))
202 break;
203
204 val = readl(aacirun->base + AACI_SR);
205 if (!(val & SR_RXHF))
206 break;
207 if (!(val & SR_RXFF))
208 len >>= 1;
209
210 aacirun->bytes -= len;
211
212 /* reading 16 bytes at a time */
213 for( ; len > 0; len -= 16) {
214 asm(
215 "ldmia %1, {r0, r1, r2, r3}\n\t"
216 "stmia %0!, {r0, r1, r2, r3}"
217 : "+r" (ptr)
218 : "r" (aacirun->fifo)
219 : "r0", "r1", "r2", "r3", "cc");
220
221 if (ptr >= aacirun->end)
222 ptr = aacirun->start;
223 }
224 } while(1);
225 aacirun->ptr = ptr;
226 }
227
169 if (mask & ISR_URINTR) { 228 if (mask & ISR_URINTR) {
170 dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel); 229 dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel);
171 writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR); 230 writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR);
@@ -193,7 +252,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
193 snd_pcm_period_elapsed(aacirun->substream); 252 snd_pcm_period_elapsed(aacirun->substream);
194 spin_lock(&aaci->lock); 253 spin_lock(&aaci->lock);
195 } 254 }
196 if (!(aacirun->cr & TXCR_TXEN)) 255 if (!(aacirun->cr & CR_EN))
197 break; 256 break;
198 257
199 val = readl(aacirun->base + AACI_SR); 258 val = readl(aacirun->base + AACI_SR);
@@ -331,8 +390,9 @@ static struct snd_pcm_hardware aaci_hw_info = {
331 .periods_max = PAGE_SIZE / 16, 390 .periods_max = PAGE_SIZE / 16,
332}; 391};
333 392
334static int aaci_pcm_open(struct aaci *aaci, struct snd_pcm_substream *substream, 393static int __aaci_pcm_open(struct aaci *aaci,
335 struct aaci_runtime *aacirun) 394 struct snd_pcm_substream *substream,
395 struct aaci_runtime *aacirun)
336{ 396{
337 struct snd_pcm_runtime *runtime = substream->runtime; 397 struct snd_pcm_runtime *runtime = substream->runtime;
338 int ret; 398 int ret;
@@ -381,7 +441,7 @@ static int aaci_pcm_close(struct snd_pcm_substream *substream)
381 struct aaci *aaci = substream->private_data; 441 struct aaci *aaci = substream->private_data;
382 struct aaci_runtime *aacirun = substream->runtime->private_data; 442 struct aaci_runtime *aacirun = substream->runtime->private_data;
383 443
384 WARN_ON(aacirun->cr & TXCR_TXEN); 444 WARN_ON(aacirun->cr & CR_EN);
385 445
386 aacirun->substream = NULL; 446 aacirun->substream = NULL;
387 free_irq(aaci->dev->irq[0], aaci); 447 free_irq(aaci->dev->irq[0], aaci);
@@ -396,7 +456,7 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
396 /* 456 /*
397 * This must not be called with the device enabled. 457 * This must not be called with the device enabled.
398 */ 458 */
399 WARN_ON(aacirun->cr & TXCR_TXEN); 459 WARN_ON(aacirun->cr & CR_EN);
400 460
401 if (aacirun->pcm_open) 461 if (aacirun->pcm_open)
402 snd_ac97_pcm_close(aacirun->pcm); 462 snd_ac97_pcm_close(aacirun->pcm);
@@ -423,9 +483,15 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
423 if (err < 0) 483 if (err < 0)
424 goto out; 484 goto out;
425 485
426 err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params), 486 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
427 params_channels(params), 487 err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
428 aacirun->pcm->r[0].slots); 488 params_channels(params),
489 aacirun->pcm->r[0].slots);
490 else
491 err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
492 params_channels(params),
493 aacirun->pcm->r[1].slots);
494
429 if (err) 495 if (err)
430 goto out; 496 goto out;
431 497
@@ -468,9 +534,9 @@ static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_str
468 * Playback specific ALSA stuff 534 * Playback specific ALSA stuff
469 */ 535 */
470static const u32 channels_to_txmask[] = { 536static const u32 channels_to_txmask[] = {
471 [2] = TXCR_TX3 | TXCR_TX4, 537 [2] = CR_SL3 | CR_SL4,
472 [4] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8, 538 [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
473 [6] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8 | TXCR_TX6 | TXCR_TX9, 539 [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
474}; 540};
475 541
476/* 542/*
@@ -505,7 +571,7 @@ aaci_rule_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
505 chan_mask); 571 chan_mask);
506} 572}
507 573
508static int aaci_pcm_playback_open(struct snd_pcm_substream *substream) 574static int aaci_pcm_open(struct snd_pcm_substream *substream)
509{ 575{
510 struct aaci *aaci = substream->private_data; 576 struct aaci *aaci = substream->private_data;
511 int ret; 577 int ret;
@@ -520,7 +586,12 @@ static int aaci_pcm_playback_open(struct snd_pcm_substream *substream)
520 if (ret) 586 if (ret)
521 return ret; 587 return ret;
522 588
523 return aaci_pcm_open(aaci, substream, &aaci->playback); 589 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
590 ret = __aaci_pcm_open(aaci, substream, &aaci->playback);
591 } else {
592 ret = __aaci_pcm_open(aaci, substream, &aaci->capture);
593 }
594 return ret;
524} 595}
525 596
526static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream, 597static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
@@ -541,11 +612,11 @@ static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
541 * FIXME: double rate slots? 612 * FIXME: double rate slots?
542 */ 613 */
543 if (ret >= 0) { 614 if (ret >= 0) {
544 aacirun->cr = TXCR_FEN | TXCR_COMPACT | TXCR_TSZ16; 615 aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
545 aacirun->cr |= channels_to_txmask[channels]; 616 aacirun->cr |= channels_to_txmask[channels];
546 617
547 aacirun->fifosz = aaci->fifosize * 4; 618 aacirun->fifosz = aaci->fifosize * 4;
548 if (aacirun->cr & TXCR_COMPACT) 619 if (aacirun->cr & CR_COMPACT)
549 aacirun->fifosz >>= 1; 620 aacirun->fifosz >>= 1;
550 } 621 }
551 return ret; 622 return ret;
@@ -558,7 +629,7 @@ static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
558 ie = readl(aacirun->base + AACI_IE); 629 ie = readl(aacirun->base + AACI_IE);
559 ie &= ~(IE_URIE|IE_TXIE); 630 ie &= ~(IE_URIE|IE_TXIE);
560 writel(ie, aacirun->base + AACI_IE); 631 writel(ie, aacirun->base + AACI_IE);
561 aacirun->cr &= ~TXCR_TXEN; 632 aacirun->cr &= ~CR_EN;
562 aaci_chan_wait_ready(aacirun); 633 aaci_chan_wait_ready(aacirun);
563 writel(aacirun->cr, aacirun->base + AACI_TXCR); 634 writel(aacirun->cr, aacirun->base + AACI_TXCR);
564} 635}
@@ -568,7 +639,7 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
568 u32 ie; 639 u32 ie;
569 640
570 aaci_chan_wait_ready(aacirun); 641 aaci_chan_wait_ready(aacirun);
571 aacirun->cr |= TXCR_TXEN; 642 aacirun->cr |= CR_EN;
572 643
573 ie = readl(aacirun->base + AACI_IE); 644 ie = readl(aacirun->base + AACI_IE);
574 ie |= IE_URIE | IE_TXIE; 645 ie |= IE_URIE | IE_TXIE;
@@ -616,7 +687,7 @@ static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cm
616} 687}
617 688
618static struct snd_pcm_ops aaci_playback_ops = { 689static struct snd_pcm_ops aaci_playback_ops = {
619 .open = aaci_pcm_playback_open, 690 .open = aaci_pcm_open,
620 .close = aaci_pcm_close, 691 .close = aaci_pcm_close,
621 .ioctl = snd_pcm_lib_ioctl, 692 .ioctl = snd_pcm_lib_ioctl,
622 .hw_params = aaci_pcm_playback_hw_params, 693 .hw_params = aaci_pcm_playback_hw_params,
@@ -627,7 +698,133 @@ static struct snd_pcm_ops aaci_playback_ops = {
627 .mmap = aaci_pcm_mmap, 698 .mmap = aaci_pcm_mmap,
628}; 699};
629 700
701static int aaci_pcm_capture_hw_params(snd_pcm_substream_t *substream,
702 snd_pcm_hw_params_t *params)
703{
704 struct aaci *aaci = substream->private_data;
705 struct aaci_runtime *aacirun = substream->runtime->private_data;
706 int ret;
707
708 ret = aaci_pcm_hw_params(substream, aacirun, params);
709
710 if (ret >= 0) {
711 aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
712
713 /* Line in record: slot 3 and 4 */
714 aacirun->cr |= CR_SL3 | CR_SL4;
715
716 aacirun->fifosz = aaci->fifosize * 4;
717
718 if (aacirun->cr & CR_COMPACT)
719 aacirun->fifosz >>= 1;
720 }
721 return ret;
722}
723
724static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
725{
726 u32 ie;
727
728 aaci_chan_wait_ready(aacirun);
729
730 ie = readl(aacirun->base + AACI_IE);
731 ie &= ~(IE_ORIE | IE_RXIE);
732 writel(ie, aacirun->base+AACI_IE);
733
734 aacirun->cr &= ~CR_EN;
630 735
736 writel(aacirun->cr, aacirun->base + AACI_RXCR);
737}
738
739static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
740{
741 u32 ie;
742
743 aaci_chan_wait_ready(aacirun);
744
745#ifdef DEBUG
746 /* RX Timeout value: bits 28:17 in RXCR */
747 aacirun->cr |= 0xf << 17;
748#endif
749
750 aacirun->cr |= CR_EN;
751 writel(aacirun->cr, aacirun->base + AACI_RXCR);
752
753 ie = readl(aacirun->base + AACI_IE);
754 ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full
755 writel(ie, aacirun->base + AACI_IE);
756}
757
758static int aaci_pcm_capture_trigger(snd_pcm_substream_t *substream, int cmd){
759
760 struct aaci *aaci = substream->private_data;
761 struct aaci_runtime *aacirun = substream->runtime->private_data;
762 unsigned long flags;
763 int ret = 0;
764
765 spin_lock_irqsave(&aaci->lock, flags);
766
767 switch (cmd) {
768 case SNDRV_PCM_TRIGGER_START:
769 aaci_pcm_capture_start(aacirun);
770 break;
771
772 case SNDRV_PCM_TRIGGER_RESUME:
773 aaci_pcm_capture_start(aacirun);
774 break;
775
776 case SNDRV_PCM_TRIGGER_STOP:
777 aaci_pcm_capture_stop(aacirun);
778 break;
779
780 case SNDRV_PCM_TRIGGER_SUSPEND:
781 aaci_pcm_capture_stop(aacirun);
782 break;
783
784 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
785 break;
786
787 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
788 break;
789
790 default:
791 ret = -EINVAL;
792 }
793
794 spin_unlock_irqrestore(&aaci->lock, flags);
795
796 return ret;
797}
798
799static int aaci_pcm_capture_prepare(snd_pcm_substream_t *substream)
800{
801 struct snd_pcm_runtime *runtime = substream->runtime;
802 struct aaci *aaci = substream->private_data;
803
804 aaci_pcm_prepare(substream);
805
806 /* allow changing of sample rate */
807 aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */
808 aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
809 aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
810
811 /* Record select: Mic: 0, Aux: 3, Line: 4 */
812 aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404);
813
814 return 0;
815}
816
817static snd_pcm_ops_t aaci_capture_ops = {
818 .open = aaci_pcm_open,
819 .close = aaci_pcm_close,
820 .ioctl = snd_pcm_lib_ioctl,
821 .hw_params = aaci_pcm_capture_hw_params,
822 .hw_free = aaci_pcm_hw_free,
823 .prepare = aaci_pcm_capture_prepare,
824 .trigger = aaci_pcm_capture_trigger,
825 .pointer = aaci_pcm_pointer,
826 .mmap = aaci_pcm_mmap,
827};
631 828
632/* 829/*
633 * Power Management. 830 * Power Management.
@@ -667,7 +864,7 @@ static int aaci_resume(struct amba_device *dev)
667 864
668 865
669static struct ac97_pcm ac97_defs[] __devinitdata = { 866static struct ac97_pcm ac97_defs[] __devinitdata = {
670 [0] = { /* Front PCM */ 867 [0] = { /* Front PCM */
671 .exclusive = 1, 868 .exclusive = 1,
672 .r = { 869 .r = {
673 [0] = { 870 [0] = {
@@ -741,6 +938,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
741 ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97); 938 ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
742 if (ret) 939 if (ret)
743 goto out; 940 goto out;
941 aaci->ac97 = ac97;
744 942
745 /* 943 /*
746 * Disable AC97 PC Beep input on audio codecs. 944 * Disable AC97 PC Beep input on audio codecs.
@@ -753,6 +951,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
753 goto out; 951 goto out;
754 952
755 aaci->playback.pcm = &ac97_bus->pcms[0]; 953 aaci->playback.pcm = &ac97_bus->pcms[0];
954 aaci->capture.pcm = &ac97_bus->pcms[1];
756 955
757 out: 956 out:
758 return ret; 957 return ret;
@@ -802,7 +1001,7 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
802 struct snd_pcm *pcm; 1001 struct snd_pcm *pcm;
803 int ret; 1002 int ret;
804 1003
805 ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 0, &pcm); 1004 ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm);
806 if (ret == 0) { 1005 if (ret == 0) {
807 aaci->pcm = pcm; 1006 aaci->pcm = pcm;
808 pcm->private_data = aaci; 1007 pcm->private_data = aaci;
@@ -811,6 +1010,7 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
811 strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name)); 1010 strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
812 1011
813 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops); 1012 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
1013 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
814 } 1014 }
815 1015
816 return ret; 1016 return ret;
@@ -818,15 +1018,15 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
818 1018
819static unsigned int __devinit aaci_size_fifo(struct aaci *aaci) 1019static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
820{ 1020{
821 void __iomem *base = aaci->base + AACI_CSCH1; 1021 struct aaci_runtime *aacirun = &aaci->playback;
822 int i; 1022 int i;
823 1023
824 writel(TXCR_FEN | TXCR_TSZ16 | TXCR_TXEN, base + AACI_TXCR); 1024 writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
825 1025
826 for (i = 0; !(readl(base + AACI_SR) & SR_TXFF) && i < 4096; i++) 1026 for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
827 writel(0, aaci->base + AACI_DR1); 1027 writel(0, aacirun->fifo);
828 1028
829 writel(0, base + AACI_TXCR); 1029 writel(0, aacirun->base + AACI_TXCR);
830 1030
831 /* 1031 /*
832 * Re-initialise the AACI after the FIFO depth test, to 1032 * Re-initialise the AACI after the FIFO depth test, to
@@ -873,6 +1073,12 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
873 aaci->playback.base = aaci->base + AACI_CSCH1; 1073 aaci->playback.base = aaci->base + AACI_CSCH1;
874 aaci->playback.fifo = aaci->base + AACI_DR1; 1074 aaci->playback.fifo = aaci->base + AACI_DR1;
875 1075
1076 /*
1077 * Capture uses AACI channel 0
1078 */
1079 aaci->capture.base = aaci->base + AACI_CSCH1;
1080 aaci->capture.fifo = aaci->base + AACI_DR1;
1081
876 for (i = 0; i < 4; i++) { 1082 for (i = 0; i < 4; i++) {
877 void __iomem *base = aaci->base + i * 0x14; 1083 void __iomem *base = aaci->base + i * 0x14;
878 1084
@@ -908,7 +1114,7 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
908 ret = snd_card_register(aaci->card); 1114 ret = snd_card_register(aaci->card);
909 if (ret == 0) { 1115 if (ret == 0) {
910 dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname, 1116 dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
911 aaci->fifosize); 1117 aaci->fifosize);
912 amba_set_drvdata(dev, aaci->card); 1118 amba_set_drvdata(dev, aaci->card);
913 return ret; 1119 return ret;
914 } 1120 }