root/drivers/sound/pas2_pcm.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcm_set_speed
  2. pcm_set_channels
  3. pcm_set_bits
  4. pas_pcm_ioctl
  5. pas_pcm_reset
  6. pas_pcm_open
  7. pas_pcm_close
  8. pas_pcm_output_block
  9. pas_pcm_start_input
  10. pas_pcm_prepare_for_input
  11. pas_pcm_prepare_for_output
  12. pas_pcm_init
  13. pas_pcm_interrupt

   1 #define _PAS2_PCM_C_
   2 /*
   3  * sound/pas2_pcm.c
   4  *
   5  * The low level driver for the Pro Audio Spectrum ADC/DAC.
   6  *
   7  * Copyright by Hannu Savolainen 1993
   8  *
   9  * Redistribution and use in source and binary forms, with or without
  10  * modification, are permitted provided that the following conditions are
  11  * met: 1. Redistributions of source code must retain the above copyright
  12  * notice, this list of conditions and the following disclaimer. 2.
  13  * Redistributions in binary form must reproduce the above copyright notice,
  14  * this list of conditions and the following disclaimer in the documentation
  15  * and/or other materials provided with the distribution.
  16  *
  17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
  18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
  21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27  * SUCH DAMAGE.
  28  *
  29  */
  30 
  31 #include "sound_config.h"
  32 
  33 #ifdef CONFIGURE_SOUNDCARD
  34 
  35 #include "pas.h"
  36 
  37 #if !defined(EXCLUDE_PAS) && !defined(EXCLUDE_AUDIO)
  38 
  39 #define TRACE(WHAT)             /*
  40                                    * * * (WHAT)   */
  41 
  42 #define PAS_PCM_INTRBITS (0x08)
  43 /*
  44  * Sample buffer timer interrupt enable
  45  */
  46 
  47 #define PCM_NON 0
  48 #define PCM_DAC 1
  49 #define PCM_ADC 2
  50 
  51 static unsigned long pcm_speed = 0;     /* sampling rate */
  52 static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
  53 static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
  54 static unsigned char pcm_filter = 0;    /* filter FLAG */
  55 static unsigned char pcm_mode = PCM_NON;
  56 static unsigned long pcm_count = 0;
  57 static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
  58 static int      my_devnum = 0;
  59 
  60 int
  61 pcm_set_speed (int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63   int             foo, tmp;
  64   unsigned long   flags;
  65 
  66   if (arg > 44100)
  67     arg = 44100;
  68   if (arg < 5000)
  69     arg = 5000;
  70 
  71   foo = (1193180 + (arg / 2)) / arg;
  72   arg = 1193180 / foo;
  73 
  74   if (pcm_channels & 2)
  75     foo = foo >> 1;
  76 
  77   pcm_speed = arg;
  78 
  79   tmp = pas_read (FILTER_FREQUENCY);
  80 
  81   /*
  82  * Set anti-aliasing filters according to sample rate. You really *NEED*
  83  * to enable this feature for all normal recording unless you want to
  84  * experiment with aliasing effects.
  85  * These filters apply to the selected "recording" source.
  86  * I (pfw) don't know the encoding of these 5 bits. The values shown
  87  * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
  88 */
  89 #if !defined NO_AUTO_FILTER_SET
  90   tmp &= 0xe0;
  91   if (pcm_speed >= 2 * 17897)
  92     tmp |= 0x21;
  93   else if (pcm_speed >= 2 * 15909)
  94     tmp |= 0x22;
  95   else if (pcm_speed >= 2 * 11931)
  96     tmp |= 0x29;
  97   else if (pcm_speed >= 2 * 8948)
  98     tmp |= 0x31;
  99   else if (pcm_speed >= 2 * 5965)
 100     tmp |= 0x39;
 101   else if (pcm_speed >= 2 * 2982)
 102     tmp |= 0x24;
 103   pcm_filter = tmp;
 104 #endif
 105 
 106   DISABLE_INTR (flags);
 107 
 108   pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
 109   pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
 110   pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
 111   pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
 112   pas_write (tmp, FILTER_FREQUENCY);
 113 
 114   RESTORE_INTR (flags);
 115 
 116   return pcm_speed;
 117 }
 118 
 119 int
 120 pcm_set_channels (int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122 
 123   if ((arg != 1) && (arg != 2))
 124     return pcm_channels;
 125 
 126   if (arg != pcm_channels)
 127     {
 128       pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
 129 
 130       pcm_channels = arg;
 131       pcm_set_speed (pcm_speed);/*
 132                                          * The speed must be reinitialized
 133                                          */
 134     }
 135 
 136   return pcm_channels;
 137 }
 138 
 139 int
 140 pcm_set_bits (int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 141 {
 142   if ((arg & pcm_bitsok) != arg)
 143     return pcm_bits;
 144 
 145   if (arg != pcm_bits)
 146     {
 147       pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
 148 
 149       pcm_bits = arg;
 150     }
 151 
 152   return pcm_bits;
 153 }
 154 
 155 static int
 156 pas_pcm_ioctl (int dev, unsigned int cmd, unsigned int arg, int local)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158   TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 159 
 160   switch (cmd)
 161     {
 162     case SOUND_PCM_WRITE_RATE:
 163       if (local)
 164         return pcm_set_speed (arg);
 165       return IOCTL_OUT (arg, pcm_set_speed (IOCTL_IN (arg)));
 166       break;
 167 
 168     case SOUND_PCM_READ_RATE:
 169       if (local)
 170         return pcm_speed;
 171       return IOCTL_OUT (arg, pcm_speed);
 172       break;
 173 
 174     case SNDCTL_DSP_STEREO:
 175       if (local)
 176         return pcm_set_channels (arg + 1) - 1;
 177       return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg) + 1) - 1);
 178       break;
 179 
 180     case SOUND_PCM_WRITE_CHANNELS:
 181       if (local)
 182         return pcm_set_channels (arg);
 183       return IOCTL_OUT (arg, pcm_set_channels (IOCTL_IN (arg)));
 184       break;
 185 
 186     case SOUND_PCM_READ_CHANNELS:
 187       if (local)
 188         return pcm_channels;
 189       return IOCTL_OUT (arg, pcm_channels);
 190       break;
 191 
 192     case SNDCTL_DSP_SETFMT:
 193       if (local)
 194         return pcm_set_bits (arg);
 195       return IOCTL_OUT (arg, pcm_set_bits (IOCTL_IN (arg)));
 196       break;
 197 
 198     case SOUND_PCM_READ_BITS:
 199       if (local)
 200         return pcm_bits;
 201       return IOCTL_OUT (arg, pcm_bits);
 202 
 203     case SOUND_PCM_WRITE_FILTER:        /*
 204                                          * NOT YET IMPLEMENTED
 205                                          */
 206       if (IOCTL_IN (arg) > 1)
 207         return IOCTL_OUT (arg, RET_ERROR (EINVAL));
 208       break;
 209 
 210       pcm_filter = IOCTL_IN (arg);
 211     case SOUND_PCM_READ_FILTER:
 212       return IOCTL_OUT (arg, pcm_filter);
 213       break;
 214 
 215     default:
 216       return RET_ERROR (EINVAL);
 217     }
 218 
 219   return RET_ERROR (EINVAL);
 220 }
 221 
 222 static void
 223 pas_pcm_reset (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225   TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
 226 
 227   pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
 228 }
 229 
 230 static int
 231 pas_pcm_open (int dev, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 232 {
 233   int             err;
 234 
 235   TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
 236 
 237   if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
 238     return err;
 239 
 240   if (DMAbuf_open_dma (dev) < 0)
 241     {
 242       pas_remove_intr (PAS_PCM_INTRBITS);
 243       return RET_ERROR (EBUSY);
 244     }
 245 
 246   pcm_count = 0;
 247 
 248   return 0;
 249 }
 250 
 251 static void
 252 pas_pcm_close (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254   unsigned long   flags;
 255 
 256   TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
 257 
 258   DISABLE_INTR (flags);
 259 
 260   pas_pcm_reset (dev);
 261   DMAbuf_close_dma (dev);
 262   pas_remove_intr (PAS_PCM_INTRBITS);
 263   pcm_mode = PCM_NON;
 264 
 265   RESTORE_INTR (flags);
 266 }
 267 
 268 static void
 269 pas_pcm_output_block (int dev, unsigned long buf, int count,
     /* [previous][next][first][last][top][bottom][index][help] */
 270                       int intrflag, int restart_dma)
 271 {
 272   unsigned long   flags, cnt;
 273 
 274   TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
 275 
 276   cnt = count;
 277   if (audio_devs[dev]->dmachan > 3)
 278     cnt >>= 1;
 279 
 280   if (audio_devs[dev]->flags & DMA_AUTOMODE &&
 281       intrflag &&
 282       cnt == pcm_count)
 283     return;                     /*
 284                                  * Auto mode on. No need to react
 285                                  */
 286 
 287   DISABLE_INTR (flags);
 288 
 289   pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
 290              PCM_CONTROL);
 291 
 292   if (restart_dma)
 293     DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
 294 
 295   if (audio_devs[dev]->dmachan > 3)
 296     count >>= 1;
 297 
 298   if (count != pcm_count)
 299     {
 300       pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 301       pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
 302       pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
 303       pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
 304       pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 305 
 306       pcm_count = count;
 307     }
 308   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
 309   pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
 310 
 311   pcm_mode = PCM_DAC;
 312 
 313   RESTORE_INTR (flags);
 314 }
 315 
 316 static void
 317 pas_pcm_start_input (int dev, unsigned long buf, int count,
     /* [previous][next][first][last][top][bottom][index][help] */
 318                      int intrflag, int restart_dma)
 319 {
 320   unsigned long   flags;
 321   int             cnt;
 322 
 323   TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
 324 
 325   cnt = count;
 326   if (audio_devs[dev]->dmachan > 3)
 327     cnt >>= 1;
 328 
 329   if (audio_devs[my_devnum]->flags & DMA_AUTOMODE &&
 330       intrflag &&
 331       cnt == pcm_count)
 332     return;                     /*
 333                                  * Auto mode on. No need to react
 334                                  */
 335 
 336   DISABLE_INTR (flags);
 337 
 338   if (restart_dma)
 339     DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
 340 
 341   if (audio_devs[dev]->dmachan > 3)
 342     count >>= 1;
 343 
 344   if (count != pcm_count)
 345     {
 346       pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 347       pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
 348       pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
 349       pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
 350       pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 351 
 352       pcm_count = count;
 353     }
 354   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
 355   pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
 356 
 357   pcm_mode = PCM_ADC;
 358 
 359   RESTORE_INTR (flags);
 360 }
 361 
 362 static int
 363 pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
     /* [previous][next][first][last][top][bottom][index][help] */
 364 {
 365   return 0;
 366 }
 367 static int
 368 pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
     /* [previous][next][first][last][top][bottom][index][help] */
 369 {
 370   return 0;
 371 }
 372 
 373 static struct audio_operations pas_pcm_operations =
 374 {
 375   "Pro Audio Spectrum",
 376   DMA_AUTOMODE,
 377   AFMT_U8 | AFMT_S16_LE,
 378   NULL,
 379   pas_pcm_open,
 380   pas_pcm_close,
 381   pas_pcm_output_block,
 382   pas_pcm_start_input,
 383   pas_pcm_ioctl,
 384   pas_pcm_prepare_for_input,
 385   pas_pcm_prepare_for_output,
 386   pas_pcm_reset,
 387   pas_pcm_reset,
 388   NULL,
 389   NULL
 390 };
 391 
 392 long
 393 pas_pcm_init (long mem_start, struct address_info *hw_config)
     /* [previous][next][first][last][top][bottom][index][help] */
 394 {
 395   TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
 396 
 397   pcm_bitsok = 8;
 398   if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
 399     pcm_bitsok |= 16;
 400 
 401   pcm_set_speed (DSP_DEFAULT_SPEED);
 402 
 403   if (num_audiodevs < MAX_AUDIO_DEV)
 404     {
 405       audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations;
 406       audio_devs[my_devnum]->dmachan = hw_config->dma;
 407       audio_devs[my_devnum]->buffcount = 1;
 408       audio_devs[my_devnum]->buffsize = 2 * DSP_BUFFSIZE;
 409     }
 410   else
 411     printk ("PAS2: Too many PCM devices available\n");
 412 
 413   return mem_start;
 414 }
 415 
 416 void
 417 pas_pcm_interrupt (unsigned char status, int cause)
     /* [previous][next][first][last][top][bottom][index][help] */
 418 {
 419   if (cause == 1)               /*
 420                                  * PCM buffer done
 421                                  */
 422     {
 423       /*
 424        * Halt the PCM first. Otherwise we don't have time to start a new
 425        * block before the PCM chip proceeds to the next sample
 426        */
 427 
 428       if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE))
 429         {
 430           pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
 431                      PCM_CONTROL);
 432         }
 433 
 434       switch (pcm_mode)
 435         {
 436 
 437         case PCM_DAC:
 438           DMAbuf_outputintr (my_devnum, 1);
 439           break;
 440 
 441         case PCM_ADC:
 442           DMAbuf_inputintr (my_devnum);
 443           break;
 444 
 445         default:
 446           printk ("PAS: Unexpected PCM interrupt\n");
 447         }
 448     }
 449 }
 450 
 451 #endif
 452 
 453 #endif

/* [previous][next][first][last][top][bottom][index][help] */