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