18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * linux/arch/m68k/amiga/amisound.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * amiga sound driver for Linux/m68k 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 78c2ecf20Sopenharmony_ci * License. See the file COPYING in the main directory of this archive 88c2ecf20Sopenharmony_ci * for more details. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 128c2ecf20Sopenharmony_ci#include <linux/timer.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/string.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <asm/amigahw.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic unsigned short *snd_data; 208c2ecf20Sopenharmony_cistatic const signed char sine_data[] = { 218c2ecf20Sopenharmony_ci 0, 39, 75, 103, 121, 127, 121, 103, 75, 39, 228c2ecf20Sopenharmony_ci 0, -39, -75, -103, -121, -127, -121, -103, -75, -39 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci#define DATA_SIZE ARRAY_SIZE(sine_data) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define custom amiga_custom 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci /* 298c2ecf20Sopenharmony_ci * The minimum period for audio may be modified by the frame buffer 308c2ecf20Sopenharmony_ci * device since it depends on htotal (for OCS/ECS/AGA) 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_civolatile unsigned short amiga_audio_min_period = 124; /* Default for pre-OCS */ 348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amiga_audio_min_period); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MAX_PERIOD (65535) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* 408c2ecf20Sopenharmony_ci * Current period (set by dmasound.c) 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ciunsigned short amiga_audio_period = MAX_PERIOD; 448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(amiga_audio_period); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic unsigned long clock_constant; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_civoid __init amiga_init_sound(void) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci static struct resource beep_res = { .name = "Beep" }; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci snd_data = amiga_chip_alloc_res(sizeof(sine_data), &beep_res); 538c2ecf20Sopenharmony_ci if (!snd_data) { 548c2ecf20Sopenharmony_ci pr_crit("amiga init_sound: failed to allocate chipmem\n"); 558c2ecf20Sopenharmony_ci return; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci memcpy (snd_data, sine_data, sizeof(sine_data)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci /* setup divisor */ 608c2ecf20Sopenharmony_ci clock_constant = (amiga_colorclock+DATA_SIZE/2)/DATA_SIZE; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci /* without amifb, turn video off and enable high quality sound */ 638c2ecf20Sopenharmony_ci#ifndef CONFIG_FB_AMIGA 648c2ecf20Sopenharmony_ci amifb_video_off(); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic void nosound(struct timer_list *unused); 698c2ecf20Sopenharmony_cistatic DEFINE_TIMER(sound_timer, nosound); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_civoid amiga_mksound( unsigned int hz, unsigned int ticks ) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci unsigned long flags; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (!snd_data) 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci local_irq_save(flags); 798c2ecf20Sopenharmony_ci del_timer( &sound_timer ); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (hz > 20 && hz < 32767) { 828c2ecf20Sopenharmony_ci unsigned long period = (clock_constant / hz); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (period < amiga_audio_min_period) 858c2ecf20Sopenharmony_ci period = amiga_audio_min_period; 868c2ecf20Sopenharmony_ci if (period > MAX_PERIOD) 878c2ecf20Sopenharmony_ci period = MAX_PERIOD; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* setup pointer to data, period, length and volume */ 908c2ecf20Sopenharmony_ci custom.aud[2].audlc = snd_data; 918c2ecf20Sopenharmony_ci custom.aud[2].audlen = sizeof(sine_data)/2; 928c2ecf20Sopenharmony_ci custom.aud[2].audper = (unsigned short)period; 938c2ecf20Sopenharmony_ci custom.aud[2].audvol = 32; /* 50% of maxvol */ 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (ticks) { 968c2ecf20Sopenharmony_ci sound_timer.expires = jiffies + ticks; 978c2ecf20Sopenharmony_ci add_timer( &sound_timer ); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* turn on DMA for audio channel 2 */ 1018c2ecf20Sopenharmony_ci custom.dmacon = DMAF_SETCLR | DMAF_AUD2; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci } else 1048c2ecf20Sopenharmony_ci nosound( 0 ); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci local_irq_restore(flags); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic void nosound(struct timer_list *unused) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci /* turn off DMA for audio channel 2 */ 1138c2ecf20Sopenharmony_ci custom.dmacon = DMAF_AUD2; 1148c2ecf20Sopenharmony_ci /* restore period to previous value after beeping */ 1158c2ecf20Sopenharmony_ci custom.aud[2].audper = amiga_audio_period; 1168c2ecf20Sopenharmony_ci} 117