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