• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * pas2_pcm.c Audio routines for PAS16
3  *
4  *
5  * Copyright (C) by Hannu Savolainen 1993-1997
6  *
7  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
8  * Version 2 (June 1991). See the "COPYING" file distributed with this software
9  * for more info.
10  *
11  *
12  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
13  * Alan Cox	   : Swatted a double allocation of device bug. Made a few
14  *		     more things module options.
15  * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init()
16  */
17 
18 #include <linux/init.h>
19 #include <linux/spinlock.h>
20 #include <asm/timex.h>
21 #include "sound_config.h"
22 
23 #include "pas2.h"
24 
25 #ifndef DEB
26 #define DEB(WHAT)
27 #endif
28 
29 #define PAS_PCM_INTRBITS (0x08)
30 /*
31  * Sample buffer timer interrupt enable
32  */
33 
34 #define PCM_NON	0
35 #define PCM_DAC	1
36 #define PCM_ADC	2
37 
38 static unsigned long pcm_speed; 	/* sampling rate */
39 static unsigned char pcm_channels = 1;	/* channels (1 or 2) */
40 static unsigned char pcm_bits = 8;	/* bits/sample (8 or 16) */
41 static unsigned char pcm_filter;	/* filter FLAG */
42 static unsigned char pcm_mode = PCM_NON;
43 static unsigned long pcm_count;
44 static unsigned short pcm_bitsok = 8;	/* mask of OK bits */
45 static int      pcm_busy;
46 int             pas_audiodev = -1;
47 static int      open_mode;
48 
49 extern spinlock_t pas_lock;
50 
pcm_set_speed(int arg)51 static int pcm_set_speed(int arg)
52 {
53 	int foo, tmp;
54 	unsigned long flags;
55 
56 	if (arg == 0)
57 		return pcm_speed;
58 
59 	if (arg > 44100)
60 		arg = 44100;
61 	if (arg < 5000)
62 		arg = 5000;
63 
64 	if (pcm_channels & 2)
65 	{
66 		foo = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
67 		arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
68 	}
69 	else
70 	{
71 		foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
72 		arg = (CLOCK_TICK_RATE + (foo / 2)) / foo;
73 	}
74 
75 	pcm_speed = arg;
76 
77 	tmp = pas_read(0x0B8A);
78 
79 	/*
80 	 * Set anti-aliasing filters according to sample rate. You really *NEED*
81 	 * to enable this feature for all normal recording unless you want to
82 	 * experiment with aliasing effects.
83 	 * These filters apply to the selected "recording" source.
84 	 * I (pfw) don't know the encoding of these 5 bits. The values shown
85 	 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.
86 	 *
87 	 * I cleared bit 5 of these values, since that bit controls the master
88 	 * mute flag. (Olav Wölfelschneider)
89 	 *
90 	 */
91 #if !defined NO_AUTO_FILTER_SET
92 	tmp &= 0xe0;
93 	if (pcm_speed >= 2 * 17897)
94 		tmp |= 0x01;
95 	else if (pcm_speed >= 2 * 15909)
96 		tmp |= 0x02;
97 	else if (pcm_speed >= 2 * 11931)
98 		tmp |= 0x09;
99 	else if (pcm_speed >= 2 * 8948)
100 		tmp |= 0x11;
101 	else if (pcm_speed >= 2 * 5965)
102 		tmp |= 0x19;
103 	else if (pcm_speed >= 2 * 2982)
104 		tmp |= 0x04;
105 	pcm_filter = tmp;
106 #endif
107 
108 	spin_lock_irqsave(&pas_lock, flags);
109 
110 	pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
111 	pas_write(0x00 | 0x30 | 0x04, 0x138B);
112 	pas_write(foo & 0xff, 0x1388);
113 	pas_write((foo >> 8) & 0xff, 0x1388);
114 	pas_write(tmp, 0x0B8A);
115 
116 	spin_unlock_irqrestore(&pas_lock, flags);
117 
118 	return pcm_speed;
119 }
120 
pcm_set_channels(int arg)121 static int pcm_set_channels(int arg)
122 {
123 
124 	if ((arg != 1) && (arg != 2))
125 		return pcm_channels;
126 
127 	if (arg != pcm_channels)
128 	{
129 		pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
130 
131 		pcm_channels = arg;
132 		pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */
133 	}
134 	return pcm_channels;
135 }
136 
pcm_set_bits(int arg)137 static int pcm_set_bits(int arg)
138 {
139 	if (arg == 0)
140 		return pcm_bits;
141 
142 	if ((arg & pcm_bitsok) != arg)
143 		return pcm_bits;
144 
145 	if (arg != pcm_bits)
146 	{
147 		pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
148 
149 		pcm_bits = arg;
150 	}
151 	return pcm_bits;
152 }
153 
pas_audio_ioctl(int dev,unsigned int cmd,void __user * arg)154 static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg)
155 {
156 	int val, ret;
157 	int __user *p = arg;
158 
159 	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
160 
161 	switch (cmd)
162 	{
163 	case SOUND_PCM_WRITE_RATE:
164 		if (get_user(val, p))
165 			return -EFAULT;
166 		ret = pcm_set_speed(val);
167 		break;
168 
169 	case SOUND_PCM_READ_RATE:
170 		ret = pcm_speed;
171 		break;
172 
173 	case SNDCTL_DSP_STEREO:
174 		if (get_user(val, p))
175 			return -EFAULT;
176 		ret = pcm_set_channels(val + 1) - 1;
177 		break;
178 
179 	case SOUND_PCM_WRITE_CHANNELS:
180 		if (get_user(val, p))
181 			return -EFAULT;
182 		ret = pcm_set_channels(val);
183 		break;
184 
185 	case SOUND_PCM_READ_CHANNELS:
186 		ret = pcm_channels;
187 		break;
188 
189 	case SNDCTL_DSP_SETFMT:
190 		if (get_user(val, p))
191 			return -EFAULT;
192 		ret = pcm_set_bits(val);
193 		break;
194 
195 	case SOUND_PCM_READ_BITS:
196 		ret = pcm_bits;
197 		break;
198 
199 	default:
200 		return -EINVAL;
201 	}
202 	return put_user(ret, p);
203 }
204 
pas_audio_reset(int dev)205 static void pas_audio_reset(int dev)
206 {
207 	DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
208 
209 	pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */
210 }
211 
pas_audio_open(int dev,int mode)212 static int pas_audio_open(int dev, int mode)
213 {
214 	int             err;
215 	unsigned long   flags;
216 
217 	DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
218 
219 	spin_lock_irqsave(&pas_lock, flags);
220 	if (pcm_busy)
221 	{
222 		spin_unlock_irqrestore(&pas_lock, flags);
223 		return -EBUSY;
224 	}
225 	pcm_busy = 1;
226 	spin_unlock_irqrestore(&pas_lock, flags);
227 
228 	if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
229 		return err;
230 
231 
232 	pcm_count = 0;
233 	open_mode = mode;
234 
235 	return 0;
236 }
237 
pas_audio_close(int dev)238 static void pas_audio_close(int dev)
239 {
240 	unsigned long   flags;
241 
242 	DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
243 
244 	spin_lock_irqsave(&pas_lock, flags);
245 
246 	pas_audio_reset(dev);
247 	pas_remove_intr(PAS_PCM_INTRBITS);
248 	pcm_mode = PCM_NON;
249 
250 	pcm_busy = 0;
251 	spin_unlock_irqrestore(&pas_lock, flags);
252 }
253 
pas_audio_output_block(int dev,unsigned long buf,int count,int intrflag)254 static void pas_audio_output_block(int dev, unsigned long buf, int count,
255 		       int intrflag)
256 {
257 	unsigned long   flags, cnt;
258 
259 	DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
260 
261 	cnt = count;
262 	if (audio_devs[dev]->dmap_out->dma > 3)
263 		cnt >>= 1;
264 
265 	if (audio_devs[dev]->flags & DMA_AUTOMODE &&
266 	    intrflag &&
267 	    cnt == pcm_count)
268 		return;
269 
270 	spin_lock_irqsave(&pas_lock, flags);
271 
272 	pas_write(pas_read(0xF8A) & ~0x40,
273 		  0xF8A);
274 
275 	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
276 
277 	if (audio_devs[dev]->dmap_out->dma > 3)
278 		count >>= 1;
279 
280 	if (count != pcm_count)
281 	{
282 		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
283 		pas_write(0x40 | 0x30 | 0x04, 0x138B);
284 		pas_write(count & 0xff, 0x1389);
285 		pas_write((count >> 8) & 0xff, 0x1389);
286 		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
287 
288 		pcm_count = count;
289 	}
290 	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
291 #ifdef NO_TRIGGER
292 	pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
293 #endif
294 
295 	pcm_mode = PCM_DAC;
296 
297 	spin_unlock_irqrestore(&pas_lock, flags);
298 }
299 
pas_audio_start_input(int dev,unsigned long buf,int count,int intrflag)300 static void pas_audio_start_input(int dev, unsigned long buf, int count,
301 		      int intrflag)
302 {
303 	unsigned long   flags;
304 	int             cnt;
305 
306 	DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
307 
308 	cnt = count;
309 	if (audio_devs[dev]->dmap_out->dma > 3)
310 		cnt >>= 1;
311 
312 	if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
313 	    intrflag &&
314 	    cnt == pcm_count)
315 		return;
316 
317 	spin_lock_irqsave(&pas_lock, flags);
318 
319 	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
320 
321 	if (audio_devs[dev]->dmap_out->dma > 3)
322 		count >>= 1;
323 
324 	if (count != pcm_count)
325 	{
326 		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
327 		pas_write(0x40 | 0x30 | 0x04, 0x138B);
328 		pas_write(count & 0xff, 0x1389);
329 		pas_write((count >> 8) & 0xff, 0x1389);
330 		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
331 
332 		pcm_count = count;
333 	}
334 	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
335 #ifdef NO_TRIGGER
336 	pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
337 #endif
338 
339 	pcm_mode = PCM_ADC;
340 
341 	spin_unlock_irqrestore(&pas_lock, flags);
342 }
343 
344 #ifndef NO_TRIGGER
pas_audio_trigger(int dev,int state)345 static void pas_audio_trigger(int dev, int state)
346 {
347 	unsigned long   flags;
348 
349 	spin_lock_irqsave(&pas_lock, flags);
350 	state &= open_mode;
351 
352 	if (state & PCM_ENABLE_OUTPUT)
353 		pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
354 	else if (state & PCM_ENABLE_INPUT)
355 		pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
356 	else
357 		pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
358 
359 	spin_unlock_irqrestore(&pas_lock, flags);
360 }
361 #endif
362 
pas_audio_prepare_for_input(int dev,int bsize,int bcount)363 static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
364 {
365 	pas_audio_reset(dev);
366 	return 0;
367 }
368 
pas_audio_prepare_for_output(int dev,int bsize,int bcount)369 static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
370 {
371 	pas_audio_reset(dev);
372 	return 0;
373 }
374 
375 static struct audio_driver pas_audio_driver =
376 {
377 	.owner			= THIS_MODULE,
378 	.open			= pas_audio_open,
379 	.close			= pas_audio_close,
380 	.output_block		= pas_audio_output_block,
381 	.start_input		= pas_audio_start_input,
382 	.ioctl			= pas_audio_ioctl,
383 	.prepare_for_input	= pas_audio_prepare_for_input,
384 	.prepare_for_output	= pas_audio_prepare_for_output,
385 	.halt_io		= pas_audio_reset,
386 	.trigger		= pas_audio_trigger
387 };
388 
pas_pcm_init(struct address_info * hw_config)389 void __init pas_pcm_init(struct address_info *hw_config)
390 {
391 	DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
392 
393 	pcm_bitsok = 8;
394 	if (pas_read(0xEF8B) & 0x08)
395 		pcm_bitsok |= 16;
396 
397 	pcm_set_speed(DSP_DEFAULT_SPEED);
398 
399 	if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
400 					"Pro Audio Spectrum",
401 					&pas_audio_driver,
402 					sizeof(struct audio_driver),
403 					DMA_AUTOMODE,
404 					AFMT_U8 | AFMT_S16_LE,
405 					NULL,
406 					hw_config->dma,
407 					hw_config->dma)) < 0)
408 		printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
409 }
410 
pas_pcm_interrupt(unsigned char status,int cause)411 void pas_pcm_interrupt(unsigned char status, int cause)
412 {
413 	if (cause == 1)
414 	{
415 		/*
416 		 * Halt the PCM first. Otherwise we don't have time to start a new
417 		 * block before the PCM chip proceeds to the next sample
418 		 */
419 
420 		if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
421 			pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
422 
423 		switch (pcm_mode)
424 		{
425 			case PCM_DAC:
426 				DMAbuf_outputintr(pas_audiodev, 1);
427 				break;
428 
429 			case PCM_ADC:
430 				DMAbuf_inputintr(pas_audiodev);
431 				break;
432 
433 			default:
434 				printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
435 		}
436 	}
437 }
438