162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * linux/arch/m68k/amiga/amisound.c 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * amiga sound driver for Linux/m68k 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 762306a36Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 862306a36Sopenharmony_ci * for more details. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/jiffies.h> 1262306a36Sopenharmony_ci#include <linux/timer.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/string.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/amigahw.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_cistatic unsigned short *snd_data; 2062306a36Sopenharmony_cistatic const signed char sine_data[] = { 2162306a36Sopenharmony_ci 0, 39, 75, 103, 121, 127, 121, 103, 75, 39, 2262306a36Sopenharmony_ci 0, -39, -75, -103, -121, -127, -121, -103, -75, -39 2362306a36Sopenharmony_ci}; 2462306a36Sopenharmony_ci#define DATA_SIZE ARRAY_SIZE(sine_data) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define custom amiga_custom 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* 2962306a36Sopenharmony_ci * The minimum period for audio may be modified by the frame buffer 3062306a36Sopenharmony_ci * device since it depends on htotal (for OCS/ECS/AGA) 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_civolatile unsigned short amiga_audio_min_period = 124; /* Default for pre-OCS */ 3462306a36Sopenharmony_ciEXPORT_SYMBOL(amiga_audio_min_period); 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define MAX_PERIOD (65535) 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* 4062306a36Sopenharmony_ci * Current period (set by dmasound.c) 4162306a36Sopenharmony_ci */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciunsigned short amiga_audio_period = MAX_PERIOD; 4462306a36Sopenharmony_ciEXPORT_SYMBOL(amiga_audio_period); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic unsigned long clock_constant; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_civoid __init amiga_init_sound(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci static struct resource beep_res = { .name = "Beep" }; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci snd_data = amiga_chip_alloc_res(sizeof(sine_data), &beep_res); 5362306a36Sopenharmony_ci if (!snd_data) { 5462306a36Sopenharmony_ci pr_crit("amiga init_sound: failed to allocate chipmem\n"); 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci memcpy (snd_data, sine_data, sizeof(sine_data)); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* setup divisor */ 6062306a36Sopenharmony_ci clock_constant = (amiga_colorclock+DATA_SIZE/2)/DATA_SIZE; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci /* without amifb, turn video off and enable high quality sound */ 6362306a36Sopenharmony_ci#ifndef CONFIG_FB_AMIGA 6462306a36Sopenharmony_ci amifb_video_off(); 6562306a36Sopenharmony_ci#endif 6662306a36Sopenharmony_ci} 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void nosound(struct timer_list *unused); 6962306a36Sopenharmony_cistatic DEFINE_TIMER(sound_timer, nosound); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_civoid amiga_mksound( unsigned int hz, unsigned int ticks ) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci unsigned long flags; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (!snd_data) 7662306a36Sopenharmony_ci return; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci local_irq_save(flags); 7962306a36Sopenharmony_ci del_timer( &sound_timer ); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (hz > 20 && hz < 32767) { 8262306a36Sopenharmony_ci unsigned long period = (clock_constant / hz); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci if (period < amiga_audio_min_period) 8562306a36Sopenharmony_ci period = amiga_audio_min_period; 8662306a36Sopenharmony_ci if (period > MAX_PERIOD) 8762306a36Sopenharmony_ci period = MAX_PERIOD; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci /* setup pointer to data, period, length and volume */ 9062306a36Sopenharmony_ci custom.aud[2].audlc = snd_data; 9162306a36Sopenharmony_ci custom.aud[2].audlen = sizeof(sine_data)/2; 9262306a36Sopenharmony_ci custom.aud[2].audper = (unsigned short)period; 9362306a36Sopenharmony_ci custom.aud[2].audvol = 32; /* 50% of maxvol */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if (ticks) { 9662306a36Sopenharmony_ci sound_timer.expires = jiffies + ticks; 9762306a36Sopenharmony_ci add_timer( &sound_timer ); 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci /* turn on DMA for audio channel 2 */ 10162306a36Sopenharmony_ci custom.dmacon = DMAF_SETCLR | DMAF_AUD2; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci } else 10462306a36Sopenharmony_ci nosound( 0 ); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci local_irq_restore(flags); 10762306a36Sopenharmony_ci} 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic void nosound(struct timer_list *unused) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci /* turn off DMA for audio channel 2 */ 11362306a36Sopenharmony_ci custom.dmacon = DMAF_AUD2; 11462306a36Sopenharmony_ci /* restore period to previous value after beeping */ 11562306a36Sopenharmony_ci custom.aud[2].audper = amiga_audio_period; 11662306a36Sopenharmony_ci} 117