linux/arch/m68k/amiga/amisound.c
<<
>>
Prefs
   1/*
   2 * linux/arch/m68k/amiga/amisound.c
   3 *
   4 * amiga sound driver for Linux/m68k
   5 *
   6 * This file is subject to the terms and conditions of the GNU General Public
   7 * License.  See the file COPYING in the main directory of this archive
   8 * for more details.
   9 */
  10
  11#include <linux/jiffies.h>
  12#include <linux/timer.h>
  13#include <linux/init.h>
  14#include <linux/string.h>
  15#include <linux/module.h>
  16
  17#include <asm/system.h>
  18#include <asm/amigahw.h>
  19
  20static unsigned short *snd_data;
  21static const signed char sine_data[] = {
  22        0,  39,  75,  103,  121,  127,  121,  103,  75,  39,
  23        0, -39, -75, -103, -121, -127, -121, -103, -75, -39
  24};
  25#define DATA_SIZE       ARRAY_SIZE(sine_data)
  26
  27#define custom amiga_custom
  28
  29    /*
  30     * The minimum period for audio may be modified by the frame buffer
  31     * device since it depends on htotal (for OCS/ECS/AGA)
  32     */
  33
  34volatile unsigned short amiga_audio_min_period = 124; /* Default for pre-OCS */
  35EXPORT_SYMBOL(amiga_audio_min_period);
  36
  37#define MAX_PERIOD      (65535)
  38
  39
  40    /*
  41     *  Current period (set by dmasound.c)
  42     */
  43
  44unsigned short amiga_audio_period = MAX_PERIOD;
  45EXPORT_SYMBOL(amiga_audio_period);
  46
  47static unsigned long clock_constant;
  48
  49void __init amiga_init_sound(void)
  50{
  51        static struct resource beep_res = { .name = "Beep" };
  52
  53        snd_data = amiga_chip_alloc_res(sizeof(sine_data), &beep_res);
  54        if (!snd_data) {
  55                printk (KERN_CRIT "amiga init_sound: failed to allocate chipmem\n");
  56                return;
  57        }
  58        memcpy (snd_data, sine_data, sizeof(sine_data));
  59
  60        /* setup divisor */
  61        clock_constant = (amiga_colorclock+DATA_SIZE/2)/DATA_SIZE;
  62
  63        /* without amifb, turn video off and enable high quality sound */
  64#ifndef CONFIG_FB_AMIGA
  65        amifb_video_off();
  66#endif
  67}
  68
  69static void nosound( unsigned long ignored );
  70static DEFINE_TIMER(sound_timer, nosound, 0, 0);
  71
  72void amiga_mksound( unsigned int hz, unsigned int ticks )
  73{
  74        unsigned long flags;
  75
  76        if (!snd_data)
  77                return;
  78
  79        local_irq_save(flags);
  80        del_timer( &sound_timer );
  81
  82        if (hz > 20 && hz < 32767) {
  83                unsigned long period = (clock_constant / hz);
  84
  85                if (period < amiga_audio_min_period)
  86                        period = amiga_audio_min_period;
  87                if (period > MAX_PERIOD)
  88                        period = MAX_PERIOD;
  89
  90                /* setup pointer to data, period, length and volume */
  91                custom.aud[2].audlc = snd_data;
  92                custom.aud[2].audlen = sizeof(sine_data)/2;
  93                custom.aud[2].audper = (unsigned short)period;
  94                custom.aud[2].audvol = 32; /* 50% of maxvol */
  95
  96                if (ticks) {
  97                        sound_timer.expires = jiffies + ticks;
  98                        add_timer( &sound_timer );
  99                }
 100
 101                /* turn on DMA for audio channel 2 */
 102                custom.dmacon = DMAF_SETCLR | DMAF_AUD2;
 103
 104        } else
 105                nosound( 0 );
 106
 107        local_irq_restore(flags);
 108}
 109
 110
 111static void nosound( unsigned long ignored )
 112{
 113        /* turn off DMA for audio channel 2 */
 114        custom.dmacon = DMAF_AUD2;
 115        /* restore period to previous value after beeping */
 116        custom.aud[2].audper = amiga_audio_period;
 117}
 118