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