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