Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:44

0001 /*  ac97.c
0002  *
0003  *  Sound driver for Milkymist SoC
0004  *
0005  *  The license and distribution terms for this file may be
0006  *  found in the file LICENSE in this distribution or at
0007  *  http://www.rtems.org/license/LICENSE.
0008  *
0009  *  COPYRIGHT (c) 2010, 2011 Sebastien Bourdeauducq
0010  */
0011 
0012 #define RTEMS_STATUS_CHECKS_USE_PRINTK
0013 
0014 #include <rtems.h>
0015 #include <bsp.h>
0016 #include <bsp/irq-generic.h>
0017 #include <rtems/libio.h>
0018 #include <rtems/status-checks.h>
0019 #include "../include/system_conf.h"
0020 #include <bsp/milkymist_ac97.h>
0021 
0022 #define SND_DEVICE_NAME "/dev/snd"
0023 #define MIXER_DEVICE_NAME "/dev/mixer"
0024 
0025 static rtems_id cr_write_sem;
0026 static rtems_id cr_read_sem;
0027 
0028 static rtems_isr crrequest_handler(rtems_vector_number n)
0029 {
0030   rtems_semaphore_release(cr_write_sem);
0031   lm32_interrupt_ack(1 << MM_IRQ_AC97CRREQUEST);
0032 }
0033 
0034 static rtems_isr crreply_handler(rtems_vector_number n)
0035 {
0036   rtems_semaphore_release(cr_read_sem);
0037   lm32_interrupt_ack(1 << MM_IRQ_AC97CRREPLY);
0038 }
0039 
0040 /* queued playback buffers */
0041 #define PLAY_Q_SIZE 8
0042 #define PLAY_Q_MASK (PLAY_Q_SIZE-1)
0043 
0044 static struct snd_buffer *play_q[PLAY_Q_SIZE];
0045 static int play_produce;
0046 static int play_consume;
0047 static int play_level;
0048 
0049 /* buffers played, for application to collect */
0050 static rtems_id play_q_done;
0051 
0052 static void play_start(struct snd_buffer *buf)
0053 {
0054   if (buf->nsamples > (AC97_MAX_DMASIZE/4))
0055     buf->nsamples = AC97_MAX_DMASIZE/4;
0056 
0057   MM_WRITE(MM_AC97_DADDRESS, (unsigned int)buf->samples);
0058   MM_WRITE(MM_AC97_DREMAINING, buf->nsamples*4);
0059   MM_WRITE(MM_AC97_DCTL, AC97_SCTL_EN);
0060 }
0061 
0062 static rtems_isr pcmplay_handler(rtems_vector_number n)
0063 {
0064   lm32_interrupt_ack(1 << MM_IRQ_AC97DMAR);
0065 
0066   rtems_message_queue_send(play_q_done, &play_q[play_consume],
0067     sizeof(void *));
0068 
0069   play_consume = (play_consume + 1) & PLAY_Q_MASK;
0070   play_level--;
0071 
0072   if(play_level > 0)
0073     play_start(play_q[play_consume]);
0074   else
0075     MM_WRITE(MM_AC97_DCTL, 0);
0076 }
0077 
0078 /* queued record buffers */
0079 #define RECORD_Q_SIZE 8
0080 #define RECORD_Q_MASK (RECORD_Q_SIZE-1)
0081 
0082 static struct snd_buffer *record_q[RECORD_Q_SIZE];
0083 static int record_produce;
0084 static int record_consume;
0085 static int record_level;
0086 
0087 /* buffers recorded, for application to collect */
0088 static rtems_id record_q_done;
0089 
0090 static void record_start(struct snd_buffer *buf)
0091 {
0092   if (buf->nsamples > (AC97_MAX_DMASIZE/4))
0093     buf->nsamples = AC97_MAX_DMASIZE/4;
0094 
0095   MM_WRITE(MM_AC97_UADDRESS, (unsigned int)buf->samples);
0096   MM_WRITE(MM_AC97_UREMAINING, buf->nsamples*4);
0097   MM_WRITE(MM_AC97_UCTL, AC97_SCTL_EN);
0098 }
0099 
0100 static rtems_isr pcmrecord_handler(rtems_vector_number n)
0101 {
0102   lm32_interrupt_ack(1 << MM_IRQ_AC97DMAW);
0103 
0104   __asm__ volatile( /* Invalidate Level-1 data cache */
0105       "wcsr DCC, r0\n"
0106       "nop\n"
0107     );
0108 
0109   rtems_message_queue_send(record_q_done, &record_q[record_consume],
0110     sizeof(void *));
0111 
0112   record_consume = (record_consume + 1) & RECORD_Q_MASK;
0113   record_level--;
0114 
0115   if(record_level > 0)
0116     record_start(record_q[record_consume]);
0117   else
0118     MM_WRITE(MM_AC97_UCTL, 0);
0119 }
0120 
0121 rtems_device_driver ac97_initialize(
0122   rtems_device_major_number major,
0123   rtems_device_minor_number minor,
0124   void *arg
0125 )
0126 {
0127   rtems_status_code sc;
0128   rtems_isr_entry dummy;
0129 
0130   sc = rtems_io_register_name(SND_DEVICE_NAME, major, 0);
0131   RTEMS_CHECK_SC(sc, "create snd device");
0132 
0133   sc = rtems_io_register_name(MIXER_DEVICE_NAME, major, 1);
0134   RTEMS_CHECK_SC(sc, "create mixer device");
0135 
0136   sc = rtems_semaphore_create(
0137     rtems_build_name('C', 'R', 'W', 'S'),
0138     0,
0139     RTEMS_SIMPLE_BINARY_SEMAPHORE,
0140     0,
0141     &cr_write_sem
0142   );
0143   RTEMS_CHECK_SC(sc, "create AC97 register write semaphore");
0144 
0145   sc = rtems_semaphore_create(
0146     rtems_build_name('C', 'R', 'R', 'S'),
0147     0,
0148     RTEMS_SIMPLE_BINARY_SEMAPHORE,
0149     0,
0150     &cr_read_sem
0151   );
0152   RTEMS_CHECK_SC(sc, "create AC97 register read semaphore");
0153 
0154   sc = rtems_message_queue_create(
0155     rtems_build_name('P', 'L', 'Y', 'Q'),
0156     PLAY_Q_SIZE*2,
0157     sizeof(void *),
0158     0,
0159     &play_q_done
0160   );
0161   RTEMS_CHECK_SC(sc, "create playback done queue");
0162 
0163   sc = rtems_message_queue_create(
0164     rtems_build_name('R', 'E', 'C', 'Q'),
0165     RECORD_Q_SIZE*2,
0166     sizeof(void *),
0167     0,
0168     &record_q_done
0169   );
0170   RTEMS_CHECK_SC(sc, "create record done queue");
0171 
0172   rtems_interrupt_catch(crrequest_handler, MM_IRQ_AC97CRREQUEST, &dummy);
0173   rtems_interrupt_catch(crreply_handler, MM_IRQ_AC97CRREPLY, &dummy);
0174   rtems_interrupt_catch(pcmplay_handler, MM_IRQ_AC97DMAR, &dummy);
0175   rtems_interrupt_catch(pcmrecord_handler, MM_IRQ_AC97DMAW, &dummy);
0176   bsp_interrupt_vector_enable(MM_IRQ_AC97CRREQUEST);
0177   bsp_interrupt_vector_enable(MM_IRQ_AC97CRREPLY);
0178   bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR);
0179   bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW);
0180 
0181   play_produce = 0;
0182   play_consume = 0;
0183   play_level = 0;
0184 
0185   record_produce = 0;
0186   record_consume = 0;
0187   record_level = 0;
0188 
0189   return RTEMS_SUCCESSFUL;
0190 }
0191 
0192 static rtems_status_code submit_play(struct snd_buffer *buf)
0193 {
0194   bsp_interrupt_vector_disable(MM_IRQ_AC97DMAR);
0195   if (play_level == PLAY_Q_SIZE) {
0196     bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR);
0197     return RTEMS_UNSATISFIED;
0198   }
0199   play_q[play_produce] = buf;
0200   play_produce = (play_produce + 1) & PLAY_Q_MASK;
0201   play_level++;
0202 
0203   if (play_level == 1)
0204     play_start(buf);
0205 
0206   bsp_interrupt_vector_enable(MM_IRQ_AC97DMAR);
0207   return RTEMS_SUCCESSFUL;
0208 }
0209 
0210 static rtems_status_code collect_play(struct snd_buffer **buf)
0211 {
0212   size_t s;
0213 
0214   return rtems_message_queue_receive(
0215     play_q_done,
0216     buf,
0217     &s,
0218     RTEMS_WAIT,
0219     RTEMS_NO_TIMEOUT
0220   );
0221 }
0222 
0223 static rtems_status_code submit_record(struct snd_buffer *buf)
0224 {
0225   bsp_interrupt_vector_disable(MM_IRQ_AC97DMAW);
0226   if (record_level == RECORD_Q_SIZE) {
0227     bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW);
0228     return RTEMS_UNSATISFIED;
0229   }
0230   record_q[record_produce] = buf;
0231   record_produce = (record_produce + 1) & RECORD_Q_MASK;
0232   record_level++;
0233 
0234   if (record_level == 1)
0235     record_start(buf);
0236 
0237   bsp_interrupt_vector_enable(MM_IRQ_AC97DMAW);
0238   return RTEMS_SUCCESSFUL;
0239 }
0240 
0241 static rtems_status_code collect_record(struct snd_buffer **buf)
0242 {
0243   size_t s;
0244 
0245   return rtems_message_queue_receive(
0246     record_q_done,
0247     buf,
0248     &s,
0249     RTEMS_WAIT,
0250     RTEMS_NO_TIMEOUT
0251   );
0252 }
0253 
0254 #define CR_TIMEOUT 10
0255 
0256 static int read_cr(unsigned int adr)
0257 {
0258   rtems_status_code sc;
0259 
0260   MM_WRITE(MM_AC97_CRADDR, adr);
0261   MM_WRITE(MM_AC97_CRCTL, AC97_CRCTL_RQEN);
0262   sc = rtems_semaphore_obtain(cr_write_sem, RTEMS_WAIT, CR_TIMEOUT);
0263   if (sc != RTEMS_SUCCESSFUL)
0264     return -1;
0265   sc = rtems_semaphore_obtain(cr_read_sem, RTEMS_WAIT, CR_TIMEOUT);
0266   if (sc != RTEMS_SUCCESSFUL)
0267     return -1;
0268   return MM_READ(MM_AC97_CRDATAIN);
0269 }
0270 
0271 static int write_cr(unsigned int adr, unsigned int val)
0272 {
0273   rtems_status_code sc;
0274 
0275   MM_WRITE(MM_AC97_CRADDR, adr);
0276   MM_WRITE(MM_AC97_CRDATAOUT, val);
0277   MM_WRITE(MM_AC97_CRCTL, AC97_CRCTL_RQEN|AC97_CRCTL_WRITE);
0278   sc = rtems_semaphore_obtain(cr_write_sem, RTEMS_WAIT, CR_TIMEOUT);
0279   if (sc != RTEMS_SUCCESSFUL)
0280     return 0;
0281   return 1;
0282 }
0283 
0284 rtems_device_driver ac97_open(
0285    rtems_device_major_number major,
0286    rtems_device_minor_number minor,
0287    void *arg
0288 )
0289 {
0290   int codec_id;
0291   
0292   if (minor == 0) {
0293     /* snd */
0294     return RTEMS_SUCCESSFUL;
0295   } else {
0296     /* mixer */
0297     codec_id = read_cr(0x00);
0298     if ((codec_id != 0x0d50) && (codec_id != 0x6150)) {
0299       printk("AC97 codec detection failed\n");
0300       return RTEMS_UNSATISFIED;
0301     }
0302     write_cr(0x02, 0x0000); /* master volume */
0303     write_cr(0x04, 0x0f0f); /* headphones volume */
0304     write_cr(0x18, 0x0000); /* PCM out volume */
0305     write_cr(0x1c, 0x0f0f); /* record gain */
0306 
0307     write_cr(0x1a, 0x0505); /* record select: stereo mix */
0308 
0309     return RTEMS_SUCCESSFUL;
0310   }
0311 }
0312 
0313 static rtems_status_code ioctl_read_channel(void *buf,
0314   unsigned int chan, int mono)
0315 {
0316   unsigned int *val = (unsigned int *)buf;
0317   int mic_boost;
0318   int codec;
0319   int left, right;
0320 
0321   codec = read_cr(chan);
0322   if (codec < 0)
0323     return RTEMS_UNSATISFIED;
0324   if (codec & 0x8000) {
0325     /* muted */
0326     *val = 0;
0327     return RTEMS_SUCCESSFUL;
0328   }
0329   if (mono) {
0330     left = 100-(((codec & 0x1f) + 1)*100)/32;
0331     mic_boost = (codec & (1 << 6)) >> 6;
0332     *val = left | mic_boost << 8;
0333   } else {
0334     right = 100-(((codec & 0x1f) + 1)*100)/32;
0335     left = 100-((((codec & 0x1f00) >> 8) + 1)*100)/32;
0336     *val = left | (right << 8);
0337   }
0338   return RTEMS_SUCCESSFUL;
0339 }
0340 
0341 static rtems_status_code ioctl_write_channel(void *buf,
0342   unsigned int chan, int mono)
0343 {
0344   unsigned int *val = (unsigned int *)buf;
0345   int mic_boost;
0346   int left, right;
0347   int codec;
0348   rtems_status_code sc;
0349 
0350   left = *val & 0xff;
0351   left = (left*32)/100 - 1;
0352   if (left < 0)
0353     left = 0;
0354 
0355   if (mono) {
0356     mic_boost = *val >> 8;
0357     right = 31;
0358   } else {
0359     right = (*val >> 8) & 0xff;
0360     right = (right*32)/100 - 1;
0361     if (right < 0)
0362       right = 0;
0363   }
0364 
0365   if ((left == 0) && (right == 0))
0366     /* mute */
0367     codec = 0x8000;
0368   else
0369     codec = (31-left) | ((31-right) << 8);
0370 
0371   if (mono) {
0372     if (mic_boost)
0373       codec |= (1 << 6);
0374     else
0375       codec &= ~(1 << 6);
0376   }
0377 
0378   if (!write_cr(chan, codec))
0379     sc = RTEMS_UNSATISFIED;
0380   else
0381     sc = RTEMS_SUCCESSFUL;
0382   return sc;
0383 }
0384 
0385 rtems_device_driver ac97_control(
0386   rtems_device_major_number major,
0387   rtems_device_minor_number minor,
0388   void *arg
0389 )
0390 {
0391   rtems_libio_ioctl_args_t *args = arg;
0392   rtems_status_code sc;
0393 
0394   args->ioctl_return = -1;
0395   if(minor == 0) {
0396     /* dsp */
0397     switch (args->command) {
0398       case SOUND_SND_SUBMIT_PLAY:
0399         return submit_play((struct snd_buffer *)args->buffer);
0400       case SOUND_SND_COLLECT_PLAY:
0401         return collect_play((struct snd_buffer **)args->buffer);
0402       case SOUND_SND_SUBMIT_RECORD:
0403         return submit_record((struct snd_buffer *)args->buffer);
0404       case SOUND_SND_COLLECT_RECORD:
0405         return collect_record((struct snd_buffer **)args->buffer);
0406       default:
0407         return RTEMS_UNSATISFIED;
0408     }
0409   } else {
0410     /* mixer */
0411     switch (args->command) {
0412       case SOUND_MIXER_READ(SOUND_MIXER_MIC):
0413         sc = ioctl_read_channel(args->buffer, 0x0e, 1);
0414         if(sc == RTEMS_SUCCESSFUL)
0415           args->ioctl_return = 0;
0416         return sc;
0417       case SOUND_MIXER_READ(SOUND_MIXER_LINE):
0418         sc = ioctl_read_channel(args->buffer, 0x10, 0);
0419         if(sc == RTEMS_SUCCESSFUL)
0420           args->ioctl_return = 0;
0421         return sc;
0422       case SOUND_MIXER_WRITE(SOUND_MIXER_MIC):
0423         sc = ioctl_write_channel(args->buffer, 0x0e, 1);
0424         if(sc == RTEMS_SUCCESSFUL)
0425           args->ioctl_return = 0;
0426         return sc;
0427       case SOUND_MIXER_WRITE(SOUND_MIXER_LINE):
0428         sc = ioctl_write_channel(args->buffer, 0x10, 0);
0429         if(sc == RTEMS_SUCCESSFUL)
0430           args->ioctl_return = 0;
0431         return sc;
0432       default:
0433         return RTEMS_UNSATISFIED;
0434     }
0435   }
0436 }