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