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 #include "pas.h"
  34 
  35 #if defined(CONFIG_PAS) && defined(CONFIG_AUDIO)
  36 
  37 #define TRACE(WHAT)             /*
  38                                    * * * (WHAT)   */
  39 
  40 #define PAS_PCM_INTRBITS (0x08)
  41 /*
  42  * Sample buffer timer interrupt enable
  43  */
  44 
  45 #define PCM_NON 0
  46 #define PCM_DAC 1
  47 #define PCM_ADC 2
  48 
  49 static unsigned long pcm_speed = 0;     /* sampling rate */
  50 static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
  51 static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
  52 static unsigned char pcm_filter = 0;    /* filter FLAG */
  53 static unsigned char pcm_mode = PCM_NON;
  54 static unsigned long pcm_count = 0;
  55 static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
  56 static int      my_devnum = 0;
  57 
  58 int
  59 pcm_set_speed (int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61   int             foo, tmp;
  62   unsigned long   flags;
  63 
  64   if (arg > 44100)
  65     arg = 44100;
  66   if (arg < 5000)
  67     arg = 5000;
  68 
  69   foo = (1193180 + (arg / 2)) / arg;
  70   arg = 1193180 / foo;
  71 
  72   if (pcm_channels & 2)
  73     foo = foo >> 1;
  74 
  75   pcm_speed = arg;
  76 
  77   tmp = pas_read (FILTER_FREQUENCY);
  78 
  79   /*
  80      * Set anti-aliasing filters according to sample rate. You reall *NEED*
  81      * to enable this feature for all normal recording unless you want to
  82      * experiment with aliasing effects.
  83      * These filters apply to the selected "recording" source.
  84      * I (pfw) don't know the encoding of these 5 bits. The values shown
  85      * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
  86    */
  87 #if !defined NO_AUTO_FILTER_SET
  88   tmp &= 0xe0;
  89   if (pcm_speed >= 2 * 17897)
  90     tmp |= 0x21;
  91   else if (pcm_speed >= 2 * 15909)
  92     tmp |= 0x22;
  93   else if (pcm_speed >= 2 * 11931)
  94     tmp |= 0x29;
  95   else if (pcm_speed >= 2 * 8948)
  96     tmp |= 0x31;
  97   else if (pcm_speed >= 2 * 5965)
  98     tmp |= 0x39;
  99   else if (pcm_speed >= 2 * 2982)
 100     tmp |= 0x24;
 101   pcm_filter = tmp;
 102 #endif
 103 
 104   save_flags (flags);
 105   cli ();
 106 
 107   pas_write (tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
 108   pas_write (S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
 109   pas_write (foo & 0xff, SAMPLE_RATE_TIMER);
 110   pas_write ((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
 111   pas_write (tmp, FILTER_FREQUENCY);
 112 
 113   restore_flags (flags);
 114 
 115   return pcm_speed;
 116 }
 117 
 118 int
 119 pcm_set_channels (int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 120 {
 121 
 122   if ((arg != 1) && (arg != 2))
 123     return pcm_channels;
 124 
 125   if (arg != pcm_channels)
 126     {
 127       pas_write (pas_read (PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
 128 
 129       pcm_channels = arg;
 130       pcm_set_speed (pcm_speed);        /*
 131                                            * The speed must be reinitialized
 132                                          */
 133     }
 134 
 135   return pcm_channels;
 136 }
 137 
 138 int
 139 pcm_set_bits (int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
 140 {
 141   if ((arg & pcm_bitsok) != arg)
 142     return pcm_bits;
 143 
 144   if (arg != pcm_bits)
 145     {
 146       pas_write (pas_read (SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
 147 
 148       pcm_bits = arg;
 149     }
 150 
 151   return pcm_bits;
 152 }
 153 
 154 static int
 155 pas_pcm_ioctl (int dev, unsigned int cmd, ioctl_arg arg, int local)
     /* [previous][next][first][last][top][bottom][index][help] */
 156 {
 157   TRACE (printk ("pas2_pcm.c: static int pas_pcm_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
 158 
 159   switch (cmd)
 160     {
 161     case SOUND_PCM_WRITE_RATE:
 162       if (local)
 163         return pcm_set_speed ((int) arg);
 164       return snd_ioctl_return ((int *) arg, pcm_set_speed (get_fs_long ((long *) arg)));
 165       break;
 166 
 167     case SOUND_PCM_READ_RATE:
 168       if (local)
 169         return pcm_speed;
 170       return snd_ioctl_return ((int *) arg, pcm_speed);
 171       break;
 172 
 173     case SNDCTL_DSP_STEREO:
 174       if (local)
 175         return pcm_set_channels ((int) arg + 1) - 1;
 176       return snd_ioctl_return ((int *) arg, pcm_set_channels (get_fs_long ((long *) arg) + 1) - 1);
 177       break;
 178 
 179     case SOUND_PCM_WRITE_CHANNELS:
 180       if (local)
 181         return pcm_set_channels ((int) arg);
 182       return snd_ioctl_return ((int *) arg, pcm_set_channels (get_fs_long ((long *) arg)));
 183       break;
 184 
 185     case SOUND_PCM_READ_CHANNELS:
 186       if (local)
 187         return pcm_channels;
 188       return snd_ioctl_return ((int *) arg, pcm_channels);
 189       break;
 190 
 191     case SNDCTL_DSP_SETFMT:
 192       if (local)
 193         return pcm_set_bits ((int) arg);
 194       return snd_ioctl_return ((int *) arg, pcm_set_bits (get_fs_long ((long *) arg)));
 195       break;
 196 
 197     case SOUND_PCM_READ_BITS:
 198       if (local)
 199         return pcm_bits;
 200       return snd_ioctl_return ((int *) arg, pcm_bits);
 201 
 202     case SOUND_PCM_WRITE_FILTER:        /*
 203                                          * NOT YET IMPLEMENTED
 204                                          */
 205       if (get_fs_long ((long *) arg) > 1)
 206         return -EINVAL;
 207       pcm_filter = get_fs_long ((long *) arg);
 208       break;
 209 
 210     case SOUND_PCM_READ_FILTER:
 211       return snd_ioctl_return ((int *) arg, pcm_filter);
 212       break;
 213 
 214     default:
 215       return -EINVAL;
 216     }
 217 
 218   return -EINVAL;
 219 }
 220 
 221 static void
 222 pas_pcm_reset (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 223 {
 224   TRACE (printk ("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
 225 
 226   pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
 227 }
 228 
 229 static int
 230 pas_pcm_open (int dev, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 231 {
 232   int             err;
 233 
 234   TRACE (printk ("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
 235 
 236   if ((err = pas_set_intr (PAS_PCM_INTRBITS)) < 0)
 237     return err;
 238 
 239 
 240   pcm_count = 0;
 241 
 242   return 0;
 243 }
 244 
 245 static void
 246 pas_pcm_close (int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248   unsigned long   flags;
 249 
 250   TRACE (printk ("pas2_pcm.c: static void pas_pcm_close(void)\n"));
 251 
 252   save_flags (flags);
 253   cli ();
 254 
 255   pas_pcm_reset (dev);
 256   pas_remove_intr (PAS_PCM_INTRBITS);
 257   pcm_mode = PCM_NON;
 258 
 259   restore_flags (flags);
 260 }
 261 
 262 static void
 263 pas_pcm_output_block (int dev, unsigned long buf, int count,
     /* [previous][next][first][last][top][bottom][index][help] */
 264                       int intrflag, int restart_dma)
 265 {
 266   unsigned long   flags, cnt;
 267 
 268   TRACE (printk ("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
 269 
 270   cnt = count;
 271   if (audio_devs[dev]->dmachan1 > 3)
 272     cnt >>= 1;
 273 
 274   if (audio_devs[dev]->flags & DMA_AUTOMODE &&
 275       intrflag &&
 276       cnt == pcm_count)
 277     return;                     /*
 278                                  * Auto mode on. No need to react
 279                                  */
 280 
 281   save_flags (flags);
 282   cli ();
 283 
 284   pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
 285              PCM_CONTROL);
 286 
 287   if (restart_dma)
 288     DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE);
 289 
 290   if (audio_devs[dev]->dmachan1 > 3)
 291     count >>= 1;
 292 
 293   if (count != pcm_count)
 294     {
 295       pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 296       pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
 297       pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
 298       pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
 299       pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 300 
 301       pcm_count = count;
 302     }
 303   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
 304   pas_write (pas_read (PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
 305 
 306   pcm_mode = PCM_DAC;
 307 
 308   restore_flags (flags);
 309 }
 310 
 311 static void
 312 pas_pcm_start_input (int dev, unsigned long buf, int count,
     /* [previous][next][first][last][top][bottom][index][help] */
 313                      int intrflag, int restart_dma)
 314 {
 315   unsigned long   flags;
 316   int             cnt;
 317 
 318   TRACE (printk ("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
 319 
 320   cnt = count;
 321   if (audio_devs[dev]->dmachan1 > 3)
 322     cnt >>= 1;
 323 
 324   if (audio_devs[my_devnum]->flags & DMA_AUTOMODE &&
 325       intrflag &&
 326       cnt == pcm_count)
 327     return;                     /*
 328                                  * Auto mode on. No need to react
 329                                  */
 330 
 331   save_flags (flags);
 332   cli ();
 333 
 334   if (restart_dma)
 335     DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ);
 336 
 337   if (audio_devs[dev]->dmachan1 > 3)
 338     count >>= 1;
 339 
 340   if (count != pcm_count)
 341     {
 342       pas_write (pas_read (FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 343       pas_write (S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
 344       pas_write (count & 0xff, SAMPLE_BUFFER_COUNTER);
 345       pas_write ((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
 346       pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
 347 
 348       pcm_count = count;
 349     }
 350   pas_write (pas_read (FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
 351   pas_write ((pas_read (PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
 352 
 353   pcm_mode = PCM_ADC;
 354 
 355   restore_flags (flags);
 356 }
 357 
 358 static int
 359 pas_pcm_prepare_for_input (int dev, int bsize, int bcount)
     /* [previous][next][first][last][top][bottom][index][help] */
 360 {
 361   return 0;
 362 }
 363 static int
 364 pas_pcm_prepare_for_output (int dev, int bsize, int bcount)
     /* [previous][next][first][last][top][bottom][index][help] */
 365 {
 366   return 0;
 367 }
 368 
 369 static struct audio_operations pas_pcm_operations =
 370 {
 371   "Pro Audio Spectrum",
 372   DMA_AUTOMODE,
 373   AFMT_U8 | AFMT_S16_LE,
 374   NULL,
 375   pas_pcm_open,
 376   pas_pcm_close,
 377   pas_pcm_output_block,
 378   pas_pcm_start_input,
 379   pas_pcm_ioctl,
 380   pas_pcm_prepare_for_input,
 381   pas_pcm_prepare_for_output,
 382   pas_pcm_reset,
 383   pas_pcm_reset,
 384   NULL,
 385   NULL
 386 };
 387 
 388 long
 389 pas_pcm_init (long mem_start, struct address_info *hw_config)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391   TRACE (printk ("pas2_pcm.c: long pas_pcm_init(long mem_start = %X)\n", mem_start));
 392 
 393   pcm_bitsok = 8;
 394   if (pas_read (OPERATION_MODE_1) & O_M_1_PCM_TYPE)
 395     pcm_bitsok |= 16;
 396 
 397   pcm_set_speed (DSP_DEFAULT_SPEED);
 398 
 399   if (num_audiodevs < MAX_AUDIO_DEV)
 400     {
 401       audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations;
 402       audio_devs[my_devnum]->dmachan1 = hw_config->dma;
 403       audio_devs[my_devnum]->buffsize = DSP_BUFFSIZE;
 404     }
 405   else
 406     printk ("PAS2: Too many PCM devices available\n");
 407 
 408   return mem_start;
 409 }
 410 
 411 void
 412 pas_pcm_interrupt (unsigned char status, int cause)
     /* [previous][next][first][last][top][bottom][index][help] */
 413 {
 414   if (cause == 1)               /*
 415                                  * PCM buffer done
 416                                  */
 417     {
 418       /*
 419        * Halt the PCM first. Otherwise we don't have time to start a new
 420        * block before the PCM chip proceeds to the next sample
 421        */
 422 
 423       if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE))
 424         {
 425           pas_write (pas_read (PCM_CONTROL) & ~P_C_PCM_ENABLE,
 426                      PCM_CONTROL);
 427         }
 428 
 429       switch (pcm_mode)
 430         {
 431 
 432         case PCM_DAC:
 433           DMAbuf_outputintr (my_devnum, 1);
 434           break;
 435 
 436         case PCM_ADC:
 437           DMAbuf_inputintr (my_devnum);
 438           break;
 439 
 440         default:
 441           printk ("PAS: Unexpected PCM interrupt\n");
 442         }
 443     }
 444 }
 445 
 446 #endif

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