xref: /haiku/src/add-ons/kernel/drivers/audio/cmedia/pcm.c (revision 1214ef1b2100f2b3299fc9d8d6142e46f70a4c3f)
1 /*
2 	Copyright 1999, Be Incorporated.   All Rights Reserved.
3 	This file may be used under the terms of the Be Sample Code License.
4 */
5 #include <string.h>
6 
7 #include <ByteOrder.h>
8 #include "R3MediaDefs.h"
9 
10 #include "cm_private.h"
11 #include "sound.h"
12 
13 #if !defined(_KERNEL_EXPORT_H)
14 #include <KernelExport.h>
15 #endif /* _KERNEL_EXPORT_H */
16 
17 extern int sprintf(char *, const char *, ...);
18 
19 
20 extern void dump_card(cmedia_pci_dev * card);
21 
22 #if !defined(OLDAPI)
23  #if DEBUG
24   #define OLDAPI(x) dprintf x
25  #else
26   #define OLDAPI(x)
27  #endif
28 #endif
29 
30 #if DEBUG
31 int32 int_cnt;
32 int32 put_cnt;
33 bigtime_t the_time;
34 #endif
35 
36 #if 0
37 /* early Intel kernels forgot to export these functions */
38 #undef B_HOST_TO_LENDIAN_FLOAT
39 #undef B_HOST_TO_BENDIAN_FLOAT
40 #undef B_LENDIAN_TO_HOST_FLOAT
41 #undef B_BENDIAN_TO_HOST_FLOAT
42 
43 static float
44 _swap_float_(float x)
45 {
46 	uint32 temp1 = *(uint32*)&x;
47 	uint32 temp2 = ((temp1>>24)|((temp1>>8)&0xff00)|((temp1<<8)&0xff0000)|
48 					(temp1<<24));
49 	return *(float *)&temp2;
50 }
51 
52 #if B_HOST_IS_BENDIAN
53 #define B_HOST_TO_LENDIAN_FLOAT(x) _swap_float_(x)
54 #define B_HOST_TO_BENDIAN_FLOAT(x) ((float)(x))
55 #define B_LENDIAN_TO_HOST_FLOAT(x) _swap_float_(x)
56 #define B_BENDIAN_TO_HOST_FLOAT(x) ((float)(x))
57 #else
58 #define B_HOST_TO_LENDIAN_FLOAT(x) ((float)(x))
59 #define B_HOST_TO_BENDIAN_FLOAT(x) _swap_float_(x)
60 #define B_LENDIAN_TO_HOST_FLOAT(x) ((float)(x))
61 #define B_BENDIAN_TO_HOST_FLOAT(x) _swap_float_(x)
62 #endif
63 
64 #endif
65 
66 
67 static status_t pcm_open(const char *name, uint32 flags, void **cookie);
68 static status_t pcm_close(void *cookie);
69 static status_t pcm_free(void *cookie);
70 static status_t pcm_control(void *cookie, uint32 op, void *data, size_t len);
71 static status_t pcm_read(void *cookie, off_t pos, void *data, size_t *len);
72 static status_t pcm_write(void *cookie, off_t pos, const void *data, size_t *len);
73 //static status_t pcm_writev(void *cookie, off_t pos, const iovec *vec, size_t count, size_t *len); /* */
74 
75 device_hooks pcm_hooks = {
76     &pcm_open,
77     &pcm_close,
78     &pcm_free,
79     &pcm_control,
80     &pcm_read,
81     &pcm_write,
82     NULL,			/* select */
83     NULL,			/* deselect */
84     NULL,			/* readv */
85 	NULL	// &pcm_writev		/* writev */
86 };
87 
88 static pcm_cfg default_pcm = {
89 	44100.0,	/* sample rate */
90 	2,			/* channels */
91 	0x2,		/* format */
92 #if B_HOST_IS_BENDIAN
93 	1,			/* endian (big) */
94 #else
95 	0,			/* endian (little) */
96 #endif
97 	0,			/* header size */
98 	PLAYBACK_BUF_SIZE,	/* these are currently hard-coded */
99 	RECORD_BUF_SIZE		/* and cannot be changed without re-compile */
100 };
101 
102 
103 #if 0
104 
105 typedef struct {
106 	uint8 control;
107 	uint8 imask;
108 	uint8 regs[0x2e];
109 } chip_state;
110 
111 static void
112 save_state(
113 	pcm_dev * port,
114 	chip_state * state)
115 {
116 	int ix;
117 	state->control = get_direct(port->card, 0);
118 	state->imask = get_direct(port->card, 1);
119 	for (ix=0; ix<0x0e; ix++) {
120 		if (ix != 0x28 && ix != 0x29 && ix != 0x1a && ix != 0x1b) {
121 			state->regs[ix] = get_indirect(port->card, ix+0x30);
122 		}
123 	}
124 }
125 
126 
127 static void
128 restore_state(
129 	pcm_dev * port,
130 	const chip_state * state)
131 {
132 	int ix;
133 	set_direct(port->card, 0, state->control, 0xff);
134 	for (ix=0; ix<0x0e; ix++) {
135 		if (ix != 0x28 && ix != 0x29 && ix != 0x1a && ix != 0x1b) {
136 			set_indirect(port->card, ix, state->regs[ix]+0x30, 0xff);
137 		}
138 	}
139 	set_direct(port->card, 1, state->imask, 0xff);
140 }
141 
142 
143 static void
144 reset_chip(
145 	pcm_dev * port)
146 {
147 	set_direct(port->card, 0x1b, 0x40, 0x40);
148 	snooze(2);
149 	set_direct(port->card, 0x1b, 0x00, 0x40);
150 }
151 
152 #endif
153 
154 
155 static void
156 stop_dma(
157 	pcm_dev * port)
158 {
159 	set_direct(port->card, 0x24, 0x40, 0x40);	// mute wave stream
160 	set_direct(port->card, 0x02, 0, 0x03);		// stop both ch0 and ch1
161 
162 	set_direct(port->card, 0x02, 0x0c, 0x0c);	// reset both ch0 and ch1
163 	set_direct(port->card, 0x02, 0, 0x0c);
164 	ddprintf(("cmedia_pci: DMA stopped\n"));
165 }
166 
167 
168 static void
169 start_dma(
170 	pcm_dev * port)
171 {
172 	int	sample_size = 1;
173 
174 	/* start out with a clean slate */
175 
176 	KTRACE();
177 	ddprintf(("cmedia_pci: start_dma()\n"));
178 	if (port->config.format == 0x11) {
179 		memset((void*)port->card->low_mem, 0x80, port->config.play_buf_size +
180 			port->config.rec_buf_size);
181 	}
182 	else {
183 		memset((void *)port->card->low_mem, 0, port->config.play_buf_size +
184 			port->config.rec_buf_size);
185 	}
186 
187 	port->wr_cur = port->wr_2;
188 	port->wr_silence = port->config.play_buf_size;
189 	port->was_written = 0;
190 	port->rd_cur = port->rd_2;
191 	port->was_read = port->config.rec_buf_size/2; /* how much has been read */
192 	port->wr_total = 0;
193 	port->rd_total = 0;
194 
195 	/* we split the available low memory buffer in a small chunk */
196 	/* for playback, and a large chunk for recording. Then we split */
197 	/* each of those in half for double-buffering. While the DMA */
198 	/* just runs the entire range of the buffer, wrapping around when */
199 	/* done, the count register is set up to generate interrupt after */
200 	/* each half of the buffer. Because of latency requirements, we */
201 	/* will get 187 interrupts a second from playback, and 94 interrupts */
202 	/* a second from recording, at 48 kHz sampling rate, when buffers */
203 	/* are 2048 for playback and 4096 for record. */
204 
205 	ddprintf(("play_buf_size %lx   rec_buf_size %lx\n",
206 		port->config.play_buf_size/2, port->config.rec_buf_size/2));
207 
208 	PCI_IO_WR(port->dma_c, ((uint32)port->card->low_phys+
209 		port->config.play_buf_size)&0xff);
210 	PCI_IO_WR(port->dma_c+1, (((uint32)port->card->low_phys+
211 		port->config.play_buf_size)>>8)&0xff);
212 	PCI_IO_WR(port->dma_c+2, (((uint32)port->card->low_phys+
213 		port->config.play_buf_size)>>16)&0xff);
214 	PCI_IO_WR(port->dma_c+3, 0);
215 	/* if this is a 16 bit channel, divide transfer count in 2 */
216 	if (port->config.format != 0x11)
217 		sample_size *= 2;
218 	/* if this is a stereo channel, divide transfer count in 2 */
219 	if (port->config.channels == 2)
220 		sample_size *= 2;
221 	PCI_IO_WR(port->dma_c+4, (port->config.rec_buf_size/sample_size-1)&0xff);
222 	PCI_IO_WR(port->dma_c+5, ((port->config.rec_buf_size/sample_size-1)>>8)&0xff);
223 	PCI_IO_WR(port->dma_c+6, (port->rd_size/sample_size-1)&0xff);
224 	PCI_IO_WR(port->dma_c+7, ((port->rd_size/sample_size-1)>>8)&0xff);
225 
226 	PCI_IO_WR(port->dma_a, ((uint32)port->card->low_phys)&0xff);
227 	PCI_IO_WR(port->dma_a+1, ((uint32)port->card->low_phys>>8)&0xff);
228 	PCI_IO_WR(port->dma_a+2, ((uint32)port->card->low_phys>>16)&0xff);
229 	PCI_IO_WR(port->dma_a+3, 0);
230 	PCI_IO_WR(port->dma_a+4, (port->config.play_buf_size/sample_size-1)&0xff);
231 	PCI_IO_WR(port->dma_a+5, ((port->config.play_buf_size/sample_size-1)>>8)&0xff);
232 	PCI_IO_WR(port->dma_a+6, (port->wr_size/sample_size-1)&0xff);
233 	PCI_IO_WR(port->dma_a+7, ((port->wr_size/sample_size-1)>>8)&0xff);
234 
235 /* here, we should mute the PCM output to avoid clicking */
236 
237 	ddprintf(("cmedia_pci: DMA starts as %lx/%lx\n", port->config.format, port->open_mode));
238 	set_direct(port->card, 0x24, 0x00, 0x40);
239 
240 /* enable ch0 as play, and ch1 as record */
241 	set_direct(port->card, 0, 0x02, 0x03);
242 	set_direct(port->card, 0x02, 0x03, 0x03);
243 
244 /* here, we should snooze for 16 samples' time, then un-mute the PCM output */
245 	KTRACE();
246 }
247 
248 
249 static status_t
250 configure_pcm(
251 	pcm_dev * port,
252 	pcm_cfg * config,
253 	bool force)
254 {
255 	status_t err = B_OK;
256 	int m = 0, n = 0, r = 0;	/* parameters for the PLL sample rate synthesizer */
257 	int asr = -1;	/* alternate sample rate divisor */
258 	uint32 s;	/* size of buffer */
259 
260 	ddprintf(("cmedia_pci: configure_pcm()\n"));
261 
262 	/* check args */
263 	if (config->sample_rate < 4000.0) {
264 		config->sample_rate = default_pcm.sample_rate;
265 	}
266 	if (config->sample_rate > 48000.0) {
267 		config->sample_rate = 48000.0;
268 	}
269 	if (config->channels < 1) {
270 		config->channels = default_pcm.channels;
271 	}
272 	if (config->channels > 2) {
273 		config->channels = default_pcm.channels;
274 	}
275 	/* secret format of format: upper nybble = signed, unsigned, float */
276 	/* lower nybble = bytes per sample */
277 	if ((config->format != 0x11) && (config->format != 0x2) &&
278 		(config->format != 0x24) && (config->format != 0x4)) {
279 		config->format = default_pcm.format;
280 	}
281 	if (config->buf_header < 0) {
282 		config->buf_header = 0;
283 	}
284 
285 	/* figure out buffer size that's a power of two and within size limits */
286 	if (!config->play_buf_size) {
287 		/* default is 256 samples for a comfy 6 ms latency */
288 		s = 256*config->channels*(config->format&0xf);
289 	}	/* minimum is 32 samples for a more extreme 0.75ms latency */
290 	else for (s = 32*config->channels*(config->format&0xf); s < MIN_MEMORY_SIZE/2; s = (s<<1)) {
291 		if (s >= config->play_buf_size) {
292 			break;
293 		}
294 	}
295 	config->play_buf_size = s;
296 	if (!config->rec_buf_size) {
297 		s = 256*config->channels*(config->format&0xf);
298 	}
299 	else for (s = 32*config->channels*(config->format&0xf); s < MIN_MEMORY_SIZE/2; s = (s<<1)) {
300 		if (s >= config->rec_buf_size) {
301 			break;
302 		}
303 	}
304 	config->rec_buf_size = s;
305 
306 	/* calculate m, n and r (and asr) */
307 
308 	if (!force && abs(config->sample_rate - port->config.sample_rate) < config->sample_rate/250) {
309 		n = -1;
310 	}
311 	else if (config->sample_rate == 48000.0) {
312 		asr = 7;
313 	}
314 	else if (config->sample_rate == 32000.0) {
315 		asr = 6;
316 	}
317 	else if (config->sample_rate == 16000.0) {
318 		asr = 5;
319 	}
320 	else if (config->sample_rate == 8000.0) {
321 		asr = 4;
322 	}
323 	else if (config->sample_rate == 44100.0) {
324 		asr = 3;
325 	}
326 	else if (config->sample_rate == 22050.0) {
327 		asr = 2;
328 	}
329 	else if (config->sample_rate == 11025.0) {
330 		asr = 1;
331 	}
332 	else {
333 		float freq;
334 		float delta = 1000000.0;
335 		int sr = -1;
336 		int sn = -1;
337 		int sm = -1;
338 		float diff;
339 
340 		for (r=0; r<8; r++) {
341 			if ((1<<r)*config->sample_rate*512 < MIN_FREQ) {
342 				continue;
343 			}
344 			break;
345 		}
346 		if (r == 8) {
347 			OLDAPI(("cmedia_pci: r value is 8!\n"));
348 			r = 7;
349 		}
350 		n = 0;
351 		do {
352 			n++;
353 			m = config->sample_rate*512/1000*(n+2)*(1<<r)/(F_REF/1000)-2;
354 			if (m < 1) {
355 				continue;
356 			}
357 			if (m > 255) {
358 				ddprintf(("cmedia_pci: m > 255; outahere\n"));
359 				break;
360 			}
361 			freq = (m+2)*(F_REF/1000)/(512*(n+2)*(1<<r)/1000);
362 			diff = freq-config->sample_rate;
363 			if (diff < 0) {
364 				diff = -diff;
365 			}
366 			if (diff < delta) {
367 				sr = r;
368 				sn = n;
369 				sm = m;
370 			}
371 		} while (n < 31);
372 		r = sr;
373 		n = sn;
374 		m = sm;
375 		ddprintf(("cmedia_pci: m = %d   r = %d   n = %d\n", m, r, n));
376 	}
377 
378 	/* configure device */
379 
380 	if (!force) {
381 		stop_dma(port);
382 		/* should mute PCM out, too */
383 	}
384 	if (asr > -1 || n > -1) { /* new sampling rate */
385 		if (asr > -1) {
386 			port->config.sample_rate = config->sample_rate;
387 			set_direct(port->card, 0x05, (asr<<5)|(asr<<2), 0xfc);
388 		}
389 		else {
390 			port->config.sample_rate = ((float)m+2.0)*(F_REF/1000.0)/
391 				(0.512*(n+2.0)*(1<<r));
392 			config->sample_rate = port->config.sample_rate;
393 #if 1
394 			/* not exact the frequency supported */
395 #else
396 			set_indirect(port->card, 0x24, m, 0xff);
397 			set_indirect(port->card, 0x25, (r<<5)|n, 0xff);
398 			set_indirect(port->card, 0x22, 0x00, 0xff);
399 #endif
400 		}
401 	}
402 	if (force || config->channels != port->config.channels ||
403 		config->format != port->config.format) {
404 		uchar val = 0;
405 		if (config->channels == 2) {
406 			val |= 0x01;	/* stereo */
407 		}
408 		if (config->format != 0x11) {
409 			val |= 0x02;	/* 16 bits */
410 		}
411 		set_direct(port->card, 0x08, (val<<2)|val, 0x0f); /* MCE -- may take time to take effect */
412 		port->config.channels = config->channels;
413 		port->config.format = config->format;
414 	}
415 	if (force || config->big_endian != port->config.big_endian) {
416 		port->config.big_endian = config->big_endian;
417 	}
418 	if (force || config->buf_header != port->config.buf_header) {
419 		port->config.buf_header = config->buf_header;
420 	}
421 	if (force || config->play_buf_size != port->config.play_buf_size*2) {
422 		port->config.play_buf_size = config->play_buf_size*2;	/* because we break it in two */
423 	}
424 	if (force || config->rec_buf_size != port->config.rec_buf_size*2) {
425 		port->config.rec_buf_size = config->rec_buf_size*2;	/* because we break it in two */
426 	}
427 
428 /* here is where we should care about record and playback buffer sizes */
429 
430 	ddprintf(("cmedia_pci: play %04lx rec %04lx\n", port->config.play_buf_size/2,
431 		port->config.rec_buf_size/2));
432 
433 	port->wr_1 = port->card->low_mem;
434 	port->wr_2 = port->wr_1+port->config.play_buf_size/2;
435 	port->wr_size = port->config.play_buf_size/2;
436 
437 	port->rd_1 = port->card->low_mem+port->config.play_buf_size;
438 	port->rd_2 = port->rd_1+port->config.rec_buf_size/2;
439 	port->rd_size = port->config.rec_buf_size/2;
440 
441 	if (!force) {
442 		/* should un-mute PCM out, if we muted it */
443 		start_dma(port);
444 	}
445 	return err;
446 }
447 
448 
449 static status_t
450 pcm_open(
451 	const char * name,
452 	uint32 flags,
453 	void ** cookie)
454 {
455 	int ix;
456 	pcm_dev * port = NULL;
457 	char name_buf[256];
458 	int32 prev_mode;
459 
460 	ddprintf(("cmedia_pci: pcm_open()\n"));
461 
462 	*cookie = NULL;
463 	for (ix=0; ix<num_cards; ix++) {
464 		if (!strcmp(name, cards[ix].pcm.name)) {
465 			goto gotit;
466 		}
467 	}
468 	for (ix=0; ix<num_cards; ix++) {
469 		if (!strcmp(name, cards[ix].pcm.oldname)) {
470 			goto gotit;
471 		}
472 	}
473 	ddprintf(("cmedia_pci: %s not found\n", name));
474 	return ENODEV;
475 
476 gotit:
477 	*cookie = port = &cards[ix].pcm;
478 
479 	acquire_sem(port->init_sem);
480 
481 	prev_mode = port->open_mode;
482 	if ((flags & 3) == O_RDONLY) {
483 		atomic_or(&port->open_mode, kRecord);
484 	}
485 	else if ((flags & 3) == O_WRONLY) {
486 		atomic_or(&port->open_mode, kPlayback);
487 	}
488 	else {
489 		atomic_or(&port->open_mode, kPlayback|kRecord);
490 	}
491 
492 	if (atomic_add(&port->open_count, 1) == 0) {
493 
494 		/* initialize device first time */
495 
496 		port->card = &cards[ix];
497 		port->config = default_pcm;
498 		port->config.play_buf_size *= 2;
499 		port->config.rec_buf_size *= 2;
500 
501 		/* playback */
502 
503 		port->wr_lock = 0;
504 		port->dma_a = cards[ix].dma_base;
505 		port->wr_1 = cards[ix].low_mem;
506 		port->wr_2 = cards[ix].low_mem+port->config.play_buf_size/2;
507 		port->wr_size = port->config.play_buf_size/2;
508 		port->write_waiting = 0;
509 		sprintf(name_buf, "WS:%s", port->name);
510 		name_buf[B_OS_NAME_LENGTH-1] = 0;
511 		port->write_sem = create_sem(0, name_buf);
512 		if (port->write_sem < B_OK) {
513 			port->open_count = 0;
514 			return port->write_sem;
515 		}
516 		set_sem_owner(port->write_sem, B_SYSTEM_TEAM);
517 		name_buf[0] = 'W'; name_buf[1] = 'E';
518 		port->wr_entry = create_sem(1, name_buf);
519 		if (port->wr_entry < B_OK) {
520 			delete_sem(port->write_sem);
521 			port->open_count = 0;
522 			return port->wr_entry;
523 		}
524 		set_sem_owner(port->wr_entry, B_SYSTEM_TEAM);
525 		name_buf[1] = 'T';
526 		port->wr_time_wait = 0;
527 		port->wr_time_sem = create_sem(0, name_buf);
528 		if (port->wr_time_sem < B_OK) {
529 			delete_sem(port->write_sem);
530 			delete_sem(port->wr_entry);
531 			port->open_count = 0;
532 			return port->wr_time_sem;
533 		}
534 		set_sem_owner(port->wr_time_sem, B_SYSTEM_TEAM);
535 
536 		/* recording */
537 
538 		port->rd_lock = 0;
539 		port->dma_c = cards[ix].dma_base+0x08;
540 		port->rd_1 = cards[ix].low_mem+port->config.play_buf_size;
541 		port->rd_2 = cards[ix].low_mem+port->config.play_buf_size+port->config.rec_buf_size/2;
542 		port->rd_size = port->config.rec_buf_size/2;
543 		port->read_waiting = 0;
544 		name_buf[0] = 'R'; name_buf[1] = 'S';
545 		port->read_sem = create_sem(0, name_buf);
546 		if (port->read_sem < B_OK) {
547 			delete_sem(port->write_sem);
548 			delete_sem(port->wr_entry);
549 			delete_sem(port->wr_time_sem);
550 			port->open_count = 0;
551 			return port->read_sem;
552 		}
553 		set_sem_owner(port->read_sem, B_SYSTEM_TEAM);
554 		name_buf[0] = 'R'; name_buf[1] = 'E';
555 		port->rd_entry = create_sem(1, name_buf);
556 		if (port->rd_entry < B_OK) {
557 			delete_sem(port->write_sem);
558 			delete_sem(port->wr_entry);
559 			delete_sem(port->read_sem);
560 			delete_sem(port->wr_time_sem);
561 			port->open_count = 0;
562 			return port->rd_entry;
563 		}
564 		set_sem_owner(port->rd_entry, B_SYSTEM_TEAM);
565 		name_buf[1] = 'T';
566 		port->rd_time_wait = 0;
567 		port->rd_time_sem = create_sem(0, name_buf);
568 		if (port->rd_time_sem < B_OK) {
569 			delete_sem(port->write_sem);
570 			delete_sem(port->wr_entry);
571 			delete_sem(port->read_sem);
572 			delete_sem(port->wr_time_sem);
573 			delete_sem(port->rd_entry);
574 			port->open_count = 0;
575 			return port->rd_time_sem;
576 		}
577 		set_sem_owner(port->rd_time_sem, B_SYSTEM_TEAM);
578 
579 		port->rd_time = 0;
580 		port->next_rd_time = 0;
581 		port->wr_time = 0;
582 
583 		/* old API */
584 
585 		port->old_cap_sem = -1;
586 		port->old_play_sem = -1;
587 
588 		/* configuration */
589 
590 		configure_pcm(port, &default_pcm, true);
591 
592 		/* interrupts */
593 		KTRACE();
594 		increment_interrupt_handler(port->card);
595 
596 		set_direct(port->card, 0x0e, 0x03, 0x03);	/* */
597 		start_dma(port);
598 
599 		/* initialization is done, let other clients of the driver go */
600 	} else {
601 		if (prev_mode != port->open_mode) {
602 			pcm_cfg temp = port->config;
603 			temp.play_buf_size /= 2;
604 			temp.rec_buf_size /= 2;
605 			configure_pcm(port, &temp, false);	/* change rec/play if needed */
606 		}
607 	}
608 	release_sem(port->init_sem);
609 
610 #if DEBUG
611 	dump_card(&cards[ix]);
612 #endif
613 
614 	return B_OK;
615 }
616 
617 
618 static status_t
619 pcm_close(
620 	void * cookie)
621 {
622 	pcm_dev * port = (pcm_dev *)cookie;
623 	cpu_status cp;
624 	int spin = 0;
625 
626 	ddprintf(("cmedia_pci: pcm_close()\n"));
627 
628 	acquire_sem(port->init_sem);
629 
630 	if (atomic_add(&port->open_count, -1) == 1) {
631 
632 		KTRACE();
633 		cp = disable_interrupts();
634 		acquire_spinlock(&port->card->hardware);
635 
636 		/* turn off interrupts */
637 		stop_dma(port);
638 		set_direct(port->card, 0x0e, 0x00, 0x03);	/* */
639 
640 		if (port->config.format == 0x11) {
641 			memset((void *)port->wr_1, 0x80, port->config.play_buf_size);	/* play silence */
642 		}
643 		else {
644 			memset((void *)port->wr_1, 0, port->config.play_buf_size);	/* play silence */
645 		}
646 		spin = 1;
647 
648 		release_spinlock(&port->card->hardware);
649 		restore_interrupts(cp);
650 
651 		delete_sem(port->write_sem);
652 		delete_sem(port->read_sem);
653 		delete_sem(port->wr_entry);
654 		delete_sem(port->rd_entry);
655 		delete_sem(port->rd_time_sem);
656 		delete_sem(port->wr_time_sem);
657 		port->write_sem = -1;
658 		port->read_sem = -1;
659 		port->wr_entry = -1;
660 		port->rd_entry = -1;
661 		port->rd_time_sem = -1;
662 		port->wr_time_sem = -1;
663 	}
664 	release_sem(port->init_sem);
665 
666 	if (spin) {
667 		/* wait so we know FIFO gets filled with silence */
668 		snooze(port->config.play_buf_size*1000/(port->config.sample_rate*
669 			(port->config.format&0xf)*port->config.channels/1000));
670 	}
671 	return B_OK;
672 }
673 
674 
675 static status_t
676 pcm_free(
677 	void * cookie)
678 {
679 	cpu_status cp;
680 	pcm_dev * port = (pcm_dev *)cookie;
681 
682 	ddprintf(("cmedia_pci: pcm_free()\n"));
683 
684 	acquire_sem(port->init_sem);
685 
686 	if (((pcm_dev *)cookie)->open_count == 0) {
687 
688 		/* the last free will actually stop everything  */
689 
690 		KTRACE();
691 		cp = disable_interrupts();
692 		acquire_spinlock(&port->card->hardware);
693 
694 		decrement_interrupt_handler(port->card);
695 
696 		release_spinlock(&port->card->hardware);
697 		restore_interrupts(cp);
698 	}
699 	release_sem(port->init_sem);
700 
701 	return B_OK;
702 }
703 
704 
705 static status_t
706 pcm_control(
707 	void * cookie,
708 	uint32 iop,
709 	void * data,
710 	size_t len)
711 {
712 	// declarations for SPDIF settings I/O
713 	static int32 chipinfo[] = { 0,0 };
714 	uchar reg_value;
715 	char DriverVersion[] = "1.3.2 (Jul 17, 2001)";
716 
717 	pcm_dev * port = (pcm_dev *)cookie;
718 	status_t err = B_BAD_VALUE;
719 	pcm_cfg config = port->config;
720 	static float rates[7] = { 48000.0, 44100.0, 32000.0, 22050.0, 16000.0, 11025.0, 8000.0 };
721 	bool configure = false;
722 	config.play_buf_size /= 2;
723 	config.rec_buf_size /= 2;
724 
725 	ddprintf(("cmedia_pci: pcm_control()\n"));
726 
727 	switch (iop) {
728 	case B_AUDIO_GET_AUDIO_FORMAT:
729 		memcpy(data, &config, sizeof(port->config));
730 		err = B_OK;
731 		break;
732 	case B_AUDIO_GET_PREFERRED_SAMPLE_RATES:
733 		memcpy(data, rates, sizeof(rates));
734 		err = B_OK;
735 		break;
736 	case B_AUDIO_SET_AUDIO_FORMAT:
737 		memcpy(&config, data, sizeof(config));
738 		configure = true;
739 		err = B_OK;
740 		break;
741 	case SV_RD_TIME_WAIT:
742 		atomic_add(&port->rd_time_wait, 1);
743 		err = acquire_sem(port->rd_time_sem);
744 		if (err >= B_OK) {
745 			cpu_status cp;
746 			KTRACE();
747 			cp = disable_interrupts();
748 			acquire_spinlock(&port->rd_lock);
749 			((sv_timing *)data)->time = port->rd_time;
750 			((sv_timing *)data)->bytes = port->rd_total;
751 			((sv_timing *)data)->skipped = port->rd_skipped;
752 			((sv_timing *)data)->_reserved_[0] = 0xffffffffUL;
753 			release_spinlock(&port->rd_lock);
754 			restore_interrupts(cp);
755 		}
756 		break;
757 	case SV_WR_TIME_WAIT:
758 		atomic_add(&port->wr_time_wait, 1);
759 		err = acquire_sem(port->wr_time_sem);
760 		if (err >= B_OK) {
761 			cpu_status cp;
762 			KTRACE();
763 			cp = disable_interrupts();
764 			acquire_spinlock(&port->wr_lock);
765 			((sv_timing *)data)->time = port->wr_time;
766 			((sv_timing *)data)->bytes = port->wr_total;
767 			((sv_timing *)data)->skipped = port->wr_skipped;
768 			((sv_timing *)data)->_reserved_[0] = 0xffffffffUL;
769 			release_spinlock(&port->wr_lock);
770 			restore_interrupts(cp);
771 		}
772 		break;
773 	case SV_SECRET_HANDSHAKE: {
774 		cpu_status cp;
775 		KTRACE();
776 		cp = disable_interrupts();
777 		acquire_spinlock(&port->wr_lock);
778 		acquire_spinlock(&port->rd_lock);
779 		((sv_handshake *)data)->wr_time = port->wr_time;
780 		((sv_handshake *)data)->wr_skipped = port->wr_skipped;
781 		((sv_handshake *)data)->rd_time = port->rd_time;
782 		((sv_handshake *)data)->rd_skipped = port->rd_skipped;
783 		((sv_handshake *)data)->wr_total = port->wr_total;
784 		((sv_handshake *)data)->rd_total = port->rd_total;
785 		((sv_handshake *)data)->_reserved_[0] = 0xffffffffUL;
786 		err = B_OK;
787 		release_spinlock(&port->rd_lock);
788 		release_spinlock(&port->wr_lock);
789 		restore_interrupts(cp);
790 		} break;
791 	case SOUND_GET_PARAMS: {
792 		cpu_status cp;
793 		uchar u;
794 		sound_setup * sound = (sound_setup *)data;
795 		err = B_OK;
796 		cp = disable_interrupts();
797 		acquire_spinlock(&port->card->hardware);
798 		/* Here we get to hard-code the mix/mux values. */
799 		/* Huh-huh; he said "hard-code"! */
800 		sound->sample_rate = kHz_44_1;
801 		if (!port->config.big_endian == !B_HOST_IS_BENDIAN) {
802 			sound->playback_format = linear_16bit_big_endian_stereo;
803 			sound->capture_format = linear_16bit_big_endian_stereo;
804 		}
805 		else {
806 			sound->playback_format = linear_16bit_little_endian_stereo;
807 			sound->capture_format = linear_16bit_little_endian_stereo;
808 		}
809 		sound->dither_enable = false;
810 		sound->loop_attn = 0;
811 		sound->loop_enable = 0;
812 		sound->output_boost = 0;
813 		sound->highpass_enable = 0;
814 		/* this is a master control on C-Media... */
815 		u = get_indirect(port->card, 0x30)>>2;
816 		sound->mono_gain = u&63;
817 		sound->mono_mute = 0;
818 
819 		/* left channel */
820 		u = get_indirect(port->card, 0x3d); // Legacy SB compatible Mixer
821 		switch (u)
822 		{
823 		case 0x10:
824 			sound->left.adc_source = line;		//	record line left
825 			break;
826 
827 		case 4:
828 			sound->left.adc_source = aux1;		// record CD left ??
829 			break;
830 
831 		case 1:
832 			sound->left.adc_source = mic;		// record mic left
833 			break;
834 
835 		default:
836 			sound->left.adc_source = loopback;
837 			break;
838 		}
839 		u = get_indirect(port->card, 0x3f)>>4;
840 		sound->left.adc_gain = u&15;
841 
842 		u = get_direct(port->card, 0x25)<<4;
843 		sound->left.mic_gain_enable = u&16;
844 
845 		u = get_indirect(port->card, 0x36)>>3;
846 		sound->left.aux1_mix_gain = 31-(u&31);
847 
848 		u = get_indirect(port->card, 0x3c)<<5;
849 		sound->left.aux1_mix_mute = ~u&128;
850 
851 		u = get_indirect(port->card, 0x34)>>3;
852 		sound->left.aux2_mix_gain = 31-(u&31);
853 
854 		u = get_direct(port->card, 0x24);
855 		sound->left.aux2_mix_mute = u&128;
856 
857 		u = get_indirect(port->card, 0x38)>>3;
858 		sound->left.line_mix_gain = 31-(u&31);
859 
860 		u = get_indirect(port->card, 0x3c)<<3;
861 		sound->left.line_mix_mute = ~u&128;
862 
863 		u = get_indirect(port->card, 0x32)>>2;
864 		sound->left.dac_attn = 63-(u&63);
865 
866 		u = get_direct(port->card, 0x24)<<1;
867 		sound->left.dac_mute = u&128;
868 
869 		/* right channel */
870 		u = get_indirect(port->card, 0x3e);
871 		switch (u)
872 		{
873 		case 8:
874 			sound->right.adc_source = line;		//record line right
875 			break;
876 
877 		case 2:
878 			sound->right.adc_source = aux1;		// record CD right?
879 			break;
880 
881 		case 1:
882 			sound->right.adc_source = mic;		// record mic right
883 			break;
884 
885 		default:
886 			sound->right.adc_source = loopback;
887 			break;
888 		}
889 		u = get_indirect(port->card, 0x40)>>4;
890 		sound->right.adc_gain = u&15;
891 		sound->right.mic_gain_enable = sound->left.mic_gain_enable;
892 		u = get_indirect(port->card, 0x37)>>3;
893 		sound->right.aux1_mix_gain = 31-(u&31);
894 		u = get_indirect(port->card, 0x3c)<<6;
895 		sound->right.aux1_mix_mute = ~u&128;
896 		u = get_indirect(port->card, 0x35)>>3;
897 		sound->right.aux2_mix_gain = 31-(u&31);
898 		u = get_direct(port->card, 0x24);
899 		sound->right.aux2_mix_mute = u&128;
900 		u = get_indirect(port->card, 0x39)>>3;
901 		sound->right.line_mix_gain = 31-(u&31);
902 		u = get_indirect(port->card, 0x3c)<<4;
903 		sound->right.line_mix_mute = ~u&128;
904 		u = get_indirect(port->card, 0x33)>>2;
905 		sound->right.dac_attn = 63-(u&63);
906 		u = get_direct(port->card, 0x24)<<1;
907 		sound->right.dac_mute = u&128;
908 		/* done */
909 		release_spinlock(&port->card->hardware);
910 		restore_interrupts(cp);
911 		} break;
912 	case SOUND_SET_PARAMS: {
913 		cpu_status cp;
914 		uchar u;
915 		sound_setup * sound = (sound_setup *)data;
916 		err = B_OK;
917 		cp = disable_interrupts();
918 		acquire_spinlock(&port->card->hardware);
919 		/* Here we get to hard-code the mix/mux values. */
920 		/* Huh-huh; he said "hard-code"! */
921 
922 		/* ignore sample rate */
923 		sound->sample_rate = kHz_44_1;
924 		if (config.sample_rate < 43999 || config.sample_rate > 44201) {
925 			config.sample_rate = 44100.0;
926 			configure = true;
927 		}
928 		/* we only support 16-bit formats */
929 		if (sound->playback_format == linear_16bit_big_endian_stereo &&
930 			sound->capture_format == linear_16bit_big_endian_stereo) {
931 			if (!config.big_endian != !B_HOST_IS_BENDIAN || config.format != 0x2) {
932 				config.big_endian = B_HOST_IS_BENDIAN;
933 				config.format = 0x2;
934 				configure = true;
935 			}
936 			OLDAPI(("same_endian\n"));
937 		}
938 		else if (sound->playback_format == linear_16bit_little_endian_stereo &&
939 			sound->capture_format == linear_16bit_little_endian_stereo) {
940 			if (!config.big_endian != !!B_HOST_IS_BENDIAN || config.format != 0x2) {
941 				config.big_endian = !B_HOST_IS_BENDIAN;
942 				config.format = 0x2;
943 				configure = true;
944 			}
945 			OLDAPI(("other_endian\n"));
946 		}
947 		else {
948 			config.big_endian = !!B_HOST_IS_BENDIAN;
949 			configure = true;
950 			OLDAPI(("other format!!!\n"));
951 		}
952 		/* ignore these values */
953 		sound->dither_enable = false;
954 		sound->loop_attn = 0;
955 		sound->loop_enable = 0;
956 		sound->output_boost = 0;
957 		sound->highpass_enable = 0;
958 		/* this is a stereo control on C-Media... */
959 		u = (sound->mono_gain>>1)&0x1f;
960 		OLDAPI(("output: %x\n", u));
961 		set_indirect(port->card, 0x30, u<<3, 0xff);
962 		set_indirect(port->card, 0x31, u<<3, 0xff);
963 		/* left channel */
964 		switch (sound->left.adc_source)
965 		{
966 		case line:
967 			u = 1<<4;
968 			break;
969 		case aux1:
970 			u = 1<<2;
971 			break;
972 		case mic:
973 			u = 1<<0;
974 			break;
975 		default:
976 			u = 0x15;
977 			break;
978 		}
979 		OLDAPI(("input: %x\n", u));
980 		set_indirect(port->card, 0x3d, u, 0xff);
981 		u = (sound->left.adc_gain&15);
982 		set_indirect(port->card, 0x3f, u<<4, 0xff);
983 		u = sound->left.mic_gain_enable ? 0 : 0x01;
984 		set_direct(port->card, 0x25, u, 0x01);
985 		u = 31-(sound->left.aux1_mix_gain&31);
986 		OLDAPI(("cd: %x\n", u));
987 		set_indirect(port->card, 0x36, u<<3, 0xff);
988 		u = sound->left.aux1_mix_mute ? 0 : 0x04;
989 		set_indirect(port->card, 0x3c, u, 0x04);
990 		u = 31-(sound->left.aux2_mix_gain&31);
991 		OLDAPI(("aux2: %x\n", u));
992 		set_indirect(port->card, 0x34, u<<3, 0xff);
993 		u = sound->left.aux2_mix_mute ? 0x80 : 0;
994 		set_direct(port->card, 0x24, u, 0x80);
995 		u = 31-(sound->left.line_mix_gain&31);
996 		OLDAPI(("line: %x\n", u));
997 		set_indirect(port->card, 0x38, u<<3, 0xff);
998 		u = sound->left.line_mix_mute ? 0 : 0x10;
999 		set_indirect(port->card, 0x3c, u, 0x10);
1000 		u = 63-(sound->left.dac_attn & 63);
1001 		OLDAPI(("PCM: %x\n", u));
1002 		set_indirect(port->card, 0x32, u<<2, 0xff);
1003 		u = sound->left.dac_mute ? 0x40 : 0;
1004 		set_direct(port->card, 0x24, u, 0x40);
1005 		/* right channel */
1006 		switch (sound->right.adc_source) {
1007 		case line:
1008 			u = 1<<3;
1009 			break;
1010 		case aux1:
1011 			u = 1<<1;
1012 			break;
1013 		case mic:
1014 			u = 1<<0;
1015 			break;
1016 		default:
1017 			u = 0x0a;
1018 			break;
1019 		}
1020 		sound->right.mic_gain_enable = sound->left.mic_gain_enable;
1021 		set_indirect(port->card, 0x3e, u, 0xff);
1022 		u = (sound->right.adc_gain&15);
1023 		set_indirect(port->card, 0x40, u<<4, 0xff);
1024 		u = sound->right.mic_gain_enable ? 0 : 0x01;
1025 		set_direct(port->card, 0x25, u, 0x01);
1026 		u = 31-(sound->right.aux1_mix_gain&31);
1027 		set_indirect(port->card, 0x37, u<<3, 0xff);
1028 		u = sound->right.aux1_mix_mute ? 0 : 0x02;
1029 		set_indirect(port->card, 0x3c, u, 0x02);
1030 		u = 31-(sound->right.aux2_mix_gain&31);
1031 		set_indirect(port->card, 0x35, u<<3, 0xff);
1032 		u = sound->right.aux2_mix_mute ? 0x80 : 0;
1033 		set_direct(port->card, 0x24, u, 0x80);
1034 		u = 31-(sound->right.line_mix_gain&31);
1035 		set_indirect(port->card, 0x39, u<<3, 0xff);
1036 		u = sound->right.line_mix_mute ? 0 : 0x08;
1037 		set_indirect(port->card, 0x3c, u, 0x08);
1038 		u = 63-(sound->right.dac_attn & 63);
1039 		set_indirect(port->card, 0x33, u<<2, 0xff);
1040 		u = sound->right.dac_mute ? 0x40 : 0;
1041 		set_direct(port->card, 0x24, u, 0x40);
1042 		/* done */
1043 		release_spinlock(&port->card->hardware);
1044 		restore_interrupts(cp);
1045 		} break;
1046 	case SOUND_SET_PLAYBACK_COMPLETION_SEM:
1047 		port->old_play_sem = *(sem_id *)data;
1048 		err = B_OK;
1049 		break;
1050 	case SOUND_SET_CAPTURE_COMPLETION_SEM:
1051 		port->old_cap_sem = *(sem_id *)data;
1052 		err = B_OK;
1053 		break;
1054 //	case SOUND_GET_PLAYBACK_TIMESTAMP:
1055 //		break;
1056 //	case SOUND_GET_CAPTURE_TIMESTAMP:
1057 //		break;
1058 //	case SOUND_DEBUG_ON:
1059 //		break;
1060 //	case SOUND_DEBUG_OFF:
1061 //		break;
1062 	case SOUND_UNSAFE_WRITE: {
1063 		audio_buffer_header * buf = (audio_buffer_header *)data;
1064 		size_t n = buf->reserved_1-sizeof(*buf);
1065 		pcm_write(cookie, 0, buf+1, &n);
1066 		buf->time = port->wr_time;
1067 		buf->sample_clock = port->wr_total/4 * 10000 / 441;
1068 		err = release_sem(port->old_play_sem);
1069 		} break;
1070 	case SOUND_UNSAFE_READ: {
1071 		audio_buffer_header * buf = (audio_buffer_header *)data;
1072 		size_t n = buf->reserved_1-sizeof(*buf);
1073 		pcm_read(cookie, 0, buf+1, &n);
1074 		buf->time = port->rd_time;
1075 		buf->sample_clock = port->rd_total/4 * 10000 / 441;
1076 		err = release_sem(port->old_cap_sem);
1077 		} break;
1078 	case SOUND_LOCK_FOR_DMA:
1079 		err = B_OK;
1080 		break;
1081 	case SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE:
1082 		config.play_buf_size = (int32)data;
1083 		configure = true;
1084 		err = B_OK;
1085 		break;
1086 	case SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE:
1087 		config.rec_buf_size = (int32)data;
1088 		configure = true;
1089 		err = B_OK;
1090 		break;
1091 	case SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE:
1092 		*(int32*)data = config.play_buf_size;
1093 		err = B_OK;
1094 		break;
1095 	case SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE:
1096 		*(int32*)data = config.rec_buf_size;
1097 		err = B_OK;
1098 		break;
1099 
1100 
1101 // control ports for SPDIF settings
1102 	case SOUND_GET_SPDIF_IN_OUT_LOOPBACK:
1103 		*(int8 *)data = 0;
1104 		reg_value = get_direct( port->card, 0x04 );
1105 		if( reg_value && 0x80 ) *(int8 *)data = 1;
1106 		err = B_OK;
1107 		break;
1108 
1109 	case SOUND_SET_SPDIF_IN_OUT_LOOPBACK:
1110 		if( *(int8 *)data == 0 ) // disable SPDIF-IN loopback to SPDIF (bypass)
1111 			set_direct( port->card, 0x04, 0x00, 0x80 );
1112 		else // enable SPDIF-IN loopback to SPDIF (bypass)
1113 			set_direct( port->card, 0x04, 0x80, 0x80 );
1114 		err = B_OK;
1115 		break;
1116 
1117 
1118 
1119 
1120 	case SOUND_GET_SPDIF_OUT:
1121 		*(int8 *)data = 0;
1122 		reg_value = get_direct( port->card, 0x16 );			// Adresse 0x16
1123 		if( reg_value && 0x80 ) *(int8 *)data = 1;
1124 		err = B_OK;
1125 		break;
1126 
1127 	case SOUND_SET_SPDIF_OUT:
1128 		if( *(int8 *)data == 0 ) // disable SPDIF-OUT
1129 			set_direct( port->card, 0x16, 0x00, 0x80);
1130 		else // enable SPDIF-OUT
1131 			set_direct( port->card, 0x16, 0x80, 0x80 );
1132 		err = B_OK;
1133 		break;
1134 
1135 
1136 
1137 	case SOUND_GET_SPDIF_MONITOR:
1138 		*(int8 *)data = 0;
1139 		reg_value = get_direct( port->card, 0x24 );
1140 		if( reg_value && 0x01 ) *(int8 *)data = 1;
1141 		err = B_OK;
1142 		break;
1143 
1144 
1145 	case SOUND_SET_SPDIF_MONITOR:
1146 		if( *(int8 *)data == 0 ) // disable SPDIF_IN PCM to DAC (CDPlay)
1147 			set_direct( port->card, 0x24, 0x00, 0x01 );
1148 		else // enable SPDIF_IN PCM to DAC (CDPlay)
1149 			set_direct( port->card, 0x24, 0x01, 0x01 );
1150 		err = B_OK;
1151 		break;
1152 
1153 	case SOUND_GET_SPDIF_OUT_LEVEL:
1154 		*(int8 *)data = 0;
1155 		reg_value = get_direct( port->card, 0x1b );
1156 		if( reg_value && 0x02 ) *(int8 *)data = 1;
1157 		err = B_OK;
1158 		break;
1159 
1160 	case SOUND_SET_SPDIF_OUT_LEVEL:
1161 		if( *(int8 *)data == 0 ) // enable SPDIF-OUT optical
1162 			set_direct( port->card, 0x1b, 0x00, 0x02 );
1163 		else // enable SPDIF-OUT coaxial
1164 			set_direct( port->card, 0x1b, 0x02, 0x02 );
1165 		break;
1166 
1167 	case SOUND_GET_SPDIF_IN_FORMAT:
1168 		*(int8 *)data = 0;
1169 		reg_value = get_direct( port->card, 0x08 );		// Adresse 0x08
1170 		if( reg_value && 0x80 ) *(int8 *)data = 1;
1171 		err = B_OK;
1172 		break;
1173 
1174 
1175 	case SOUND_SET_SPDIF_IN_FORMAT:
1176 		if( *(int8 *)data == 0 ) // disable SPDIF inverse (SPDIF normal)
1177 			set_direct( port->card, 0x08, 0x00, 0x80 );
1178 		else // enable SPDIF inverse
1179 			set_direct( port->card, 0x08, 0x80, 0x80 );	// Adresse 0x08, Daten 0x80
1180 		err = B_OK;
1181 		break;
1182 
1183 
1184 	case SOUND_GET_SPDIF_IN_OUT_COPYRIGHT:
1185 		*(int8 *)data = 0;
1186 		reg_value = get_direct( port->card, 0x16 );
1187 		if( reg_value && 0x40 ) *(int8 *)data = 1;
1188 		err = B_OK;
1189 		break;
1190 
1191 	case SOUND_SET_SPDIF_IN_OUT_COPYRIGHT:
1192 		if( *(int8 *)data == 0 ) // disable SPDIF-IN/OUT copyright protection
1193 			set_direct( port->card, 0x16, 0x00, 0x40 );
1194 		else // enable SPDIF-IN/OUT copyright protection
1195 			set_direct( port->card, 0x16, 0x40, 0x40 );
1196 		err = B_OK;
1197 		break;
1198 
1199 	case SOUND_GET_SPDIF_IN_VALIDITY:
1200 		*(int8 *)data = 0;
1201 		reg_value = get_direct( port->card, 0x27 );
1202 		if( reg_value && 0x02 ) *(int8 *)data = 1;
1203 		err = B_OK;
1204 		break;
1205 
1206 	case SOUND_SET_SPDIF_IN_VALIDITY:
1207 		if( *(int8 *)data == 0 ) // disable SPDIF-IN validity detection
1208 			set_direct( port->card, 0x27, 0x00, 0x02 );
1209 		else // enable SPDIF-IN validity detection
1210 			set_direct( port->card, 0x27, 0x02, 0x02 );
1211 		err = B_OK;
1212 		break;
1213 // control ports for analog settings
1214 
1215 	case SOUND_GET_4_CHANNEL_DUPLICATE:
1216 		*(int8 *)data = 0;
1217 		reg_value = get_direct( port->card, 0x1b );
1218 		if( reg_value && 0x04 ) *(int8 *)data = 1;
1219 
1220 //		0x1b, 0x04, 0x04,	/* dual channel mode enable */
1221 //		0x1a, 0x00, 0x80,	/* Double DAC structure disable */
1222 
1223 		err = B_OK;
1224 		break;
1225 
1226 	case SOUND_SET_4_CHANNEL_DUPLICATE:
1227 		if( *(int8 *)data == 0 ) // disable 4 channel analog duplicate mode
1228 			set_direct( port->card, 0x1b, 0x00, 0x04 );
1229 		else // enable 4 channel analog duplicate mode
1230 			set_direct( port->card, 0x1b, 0x04, 0x04 );
1231 		err = B_OK;
1232 		break;
1233 // control ports for additional info
1234 
1235 	case SOUND_GET_DEVICE_ID:
1236 //		*(int32*)data.vendor_id = cards[0].info.vendor_id;
1237 //		*(int32*)data.device_id = cards[0].info.device_id;
1238 
1239 //		chipinfo[0] = cards[0].info.vendor_id;
1240 		*(int32 *)data = cards[0].info.device_id;
1241 
1242 //		memcpy(data, &chipinfo, sizeof(chipinfo));
1243 		err = B_OK;
1244 		break;
1245 
1246 	case SOUND_GET_INTERNAL_CHIP_ID:
1247 		// XXX
1248 		break;
1249 
1250 	case SOUND_GET_DRIVER_VERSION:
1251 		memcpy(data, &DriverVersion, sizeof(DriverVersion));
1252 		break;
1253 
1254 	default:
1255 		OLDAPI(("cmedia_pci: unknown code %ld\n", iop));
1256 		err = B_BAD_VALUE;
1257 		break;
1258 	}
1259 	if ((err == B_OK) && configure) {
1260 		cpu_status cp;
1261 		KTRACE();
1262 		cp = disable_interrupts();
1263 		acquire_spinlock(&port->card->hardware);
1264 		err = configure_pcm(port, &config, false);
1265 		release_spinlock(&port->card->hardware);
1266 		restore_interrupts(cp);
1267 	}
1268 	return err;
1269 }
1270 
1271 
1272 static void
1273 copy_short_to_float(
1274 	float * f,
1275 	const short * s,
1276 	int c,
1277 	int endian)	/*	endian means float data in big-endian	*/
1278 {
1279 	if (endian) {
1280 		while (c > 1) {
1281 			short sh = B_LENDIAN_TO_HOST_FLOAT(*s);
1282 			*(f++) = B_HOST_TO_BENDIAN_FLOAT((float)B_LENDIAN_TO_HOST_INT16(sh));
1283 			s++;
1284 			c -= 2;
1285 		}
1286 	}
1287 	else {
1288 		while (c > 1) {
1289 			short sh = B_LENDIAN_TO_HOST_FLOAT(*s);
1290 			*(f++) = B_HOST_TO_LENDIAN_FLOAT((float)B_LENDIAN_TO_HOST_INT16(sh));
1291 			s++;
1292 			c -= 2;
1293 		}
1294 	}
1295 }
1296 
1297 
1298 static void
1299 copy_float_to_short(
1300 	short * s,
1301 	const float * f,
1302 	int c,
1303 	int endian)	/*	endian means float data in big-endian	*/
1304 {
1305 	if (endian) {
1306 		while (c > 1) {
1307 			float fl = *(f++);
1308 			*(s++) = B_HOST_TO_LENDIAN_INT16((float)B_BENDIAN_TO_HOST_FLOAT(fl));
1309 			c -= 2;
1310 		}
1311 	}
1312 	else {
1313 		while (c > 1) {
1314 			float fl = *(f++);
1315 			*(s++) = B_HOST_TO_LENDIAN_INT16((float)B_LENDIAN_TO_HOST_FLOAT(fl));
1316 			c -= 2;
1317 		}
1318 	}
1319 }
1320 
1321 
1322 static void
1323 swap_copy(
1324 	short * dest,
1325 	const short * src,
1326 	int c)
1327 {
1328 	while (c > 1) {
1329 		unsigned short sh = *(src++);
1330 		*(dest++) = ((sh << 8) | (sh >> 8));
1331 		c -= 2;
1332 	}
1333 }
1334 
1335 
1336 static status_t
1337 pcm_read(
1338 	void * cookie,
1339 	off_t pos,
1340 	void * data,
1341 	size_t * nread)
1342 {
1343 	pcm_dev * port = (pcm_dev *)cookie;
1344 	size_t to_read = *nread;
1345 	status_t err;
1346 	int block;
1347 	cpu_status cp;
1348 	int bytes_xferred;
1349 	void * hdrptr = data;
1350 	int hdrsize = port->config.buf_header;
1351 	cmedia_pci_audio_buf_header hdr;
1352 
1353 //	ddprintf(("cmedia_pci: pcm_read()\n")); /* we're here */
1354 
1355 	*nread = 0;
1356 	data = ((char *)data)+hdrsize;
1357 	to_read -= hdrsize;
1358 
1359 	err = acquire_sem_etc(port->rd_entry, 1, B_CAN_INTERRUPT, 0);
1360 	if (err < B_OK) {
1361 		return err;
1362 	}
1363 
1364 	hdr.capture_time = port->rd_time;
1365 
1366 	goto first_time;
1367 
1368 	while (to_read > 0) {
1369 		/* wait for more data */
1370 		atomic_add(&port->read_waiting, 1);
1371 		err = acquire_sem_etc(port->read_sem, 1, B_CAN_INTERRUPT, 0);
1372 		if (err < B_OK) {
1373 			release_sem(port->rd_entry);
1374 			return err;
1375 		}
1376 
1377 first_time:	/* we need to check whether anything's available first */
1378 		KTRACE();
1379 		cp = disable_interrupts();
1380 		acquire_spinlock(&port->rd_lock);
1381 
1382 		block = port->rd_size-port->was_read;
1383 
1384 		if (port->config.format == 0x24) {
1385 			if (block > (to_read>>1)) {	/*	floats expand by factor 2	*/
1386 				block = to_read>>1;
1387 			}
1388 		}
1389 		else if (block > to_read) {
1390 			block = to_read;
1391 		}
1392 		switch (port->config.format) {
1393 		case 0x24:	/*	floats	*/
1394 			copy_short_to_float((float *)data, (const short *)(port->rd_cur+port->was_read),
1395 				block, !B_HOST_IS_LENDIAN == !port->config.big_endian);
1396 			bytes_xferred = block * 2;
1397 			break;
1398 		case 0x02:	/*	shorts	*/
1399 			if (!B_HOST_IS_LENDIAN == !port->config.big_endian) {
1400 				/*	we need to swap	*/
1401 				swap_copy((short *)data, (const short *)(port->rd_cur+port->was_read), block);
1402 				bytes_xferred = block;
1403 				break;
1404 			}
1405 			/*	else fall through to default case	*/
1406 		case 0x11:	/*	bytes	*/
1407 		default:
1408 			memcpy(data, (void *)(port->rd_cur+port->was_read), block);
1409 			bytes_xferred = block;
1410 			break;
1411 		}
1412 		port->was_read += block;
1413 
1414 		release_spinlock(&port->rd_lock);
1415 		restore_interrupts(cp);
1416 
1417 		to_read -= bytes_xferred;
1418 		data = ((char *)data)+bytes_xferred;
1419 		*nread += bytes_xferred;
1420 	}
1421 
1422 	/*	provide header if requested	*/
1423 	if (hdrsize > 0) {
1424 		ddprintf(("header %d\n", hdrsize));
1425 		*nread += hdrsize;
1426 		hdr.capture_size = *nread;
1427 		hdr.sample_rate = port->config.sample_rate;
1428 		if (hdrsize > sizeof(hdr)) {
1429 			hdrsize = sizeof(hdr);
1430 		}
1431 		memcpy(hdrptr, &hdr, hdrsize);
1432 	}
1433 
1434 	release_sem(port->rd_entry);
1435 
1436 	return B_OK;
1437 }
1438 
1439 
1440 static status_t
1441 pcm_write(
1442 	void * cookie,
1443 	off_t pos,
1444 	const void * data,
1445 	size_t * nwritten)
1446 {
1447 	pcm_dev * port = (pcm_dev *)cookie;
1448 	status_t err;
1449 	cpu_status cp;
1450 	int written = 0;
1451 	int to_write = *nwritten;	/*	 in play bytes, not input bytes!	*/
1452 	int block;
1453 	int bytes_xferred;
1454 
1455 //	ddprintf(("cmedia_pci: pcm_write()\n")); /* we're here */
1456 
1457 	*nwritten = 0;
1458 
1459 	err = acquire_sem_etc(port->wr_entry, 1, B_CAN_INTERRUPT, 0);
1460 	if (err < B_OK) {
1461 		return err;
1462 	}
1463 
1464 	atomic_add(&port->write_waiting, 1);
1465 	if (port->config.format == 0x24) {
1466 		to_write >>= 1;	/*	floats collapse by 2	*/
1467 	}
1468 	while (to_write > 0) {
1469 
1470 		/* wait to write */
1471 
1472 		err = acquire_sem_etc(port->write_sem, 1, B_CAN_INTERRUPT, 0);
1473 		if (err < B_OK) {
1474 			release_sem(port->wr_entry);
1475 			return err;
1476 		}
1477 
1478 #if DEBUG
1479 		put_cnt++;
1480 		{
1481 			bigtime_t delta = system_time() - the_time;
1482 			if (delta < 1) {
1483 				ddprintf(("cmedia_pci: delta %Ld (low!) #%ld\n", delta, put_cnt));
1484 			}
1485 			else if (delta > 2000) {
1486 				ddprintf(("cmedia_pci: delta %Ld (high!) #%ld\n", delta, put_cnt));
1487 			}
1488 		}
1489 		if (put_cnt != int_cnt) {
1490 	static int last;
1491 			if (last != int_cnt-put_cnt)
1492 				OLDAPI(("cmedia_pci: %ld mismatch\n", int_cnt-put_cnt));
1493 			last = int_cnt-put_cnt;
1494 		}
1495 #endif /* DEBUG */
1496 
1497 		KTRACE();
1498 		cp = disable_interrupts();
1499 		acquire_spinlock(&port->wr_lock);
1500 
1501 		block = port->wr_size-port->was_written;
1502 		if (block > to_write) {
1503 			/* must let next guy in */
1504 			if (atomic_add(&port->write_waiting, -1) > 0) {
1505 				release_sem_etc(port->write_sem, 1, B_DO_NOT_RESCHEDULE);
1506 			}
1507 			else {
1508 				atomic_add(&port->write_waiting, 1); /* undo damage */
1509 			}
1510 			block = to_write;
1511 		}
1512 		else if (block < to_write) {
1513 			atomic_add(&port->write_waiting, 1); /* we will loop back */
1514 		}
1515 		switch (port->config.format) {
1516 		case 0x24:	/*	floats	*/
1517 			copy_float_to_short((short *)(port->wr_cur+port->was_written), (const float *)data,
1518 				block, !B_HOST_IS_LENDIAN == !port->config.big_endian);
1519 			bytes_xferred = block * 2;
1520 			break;
1521 		case 0x02:	/*	shorts	*/
1522 			if (!B_HOST_IS_LENDIAN == !port->config.big_endian) {
1523 				/*	we need to swap	*/
1524 				swap_copy((short *)(port->wr_cur+port->was_written), (const short *)data, block);
1525 				bytes_xferred = block;
1526 				break;
1527 			}
1528 			/*	else fall through to default case	*/
1529 		case 0x11:	/*	bytes	*/
1530 		default:
1531 			memcpy((void *)(port->wr_cur+port->was_written), data, block);
1532 			bytes_xferred = block;
1533 			break;
1534 		}
1535 		port->was_written += block;
1536 		port->wr_silence = 0;
1537 
1538 		release_spinlock(&port->wr_lock);
1539 		restore_interrupts(cp);
1540 
1541 		data = ((char *)data)+bytes_xferred;
1542 		written += bytes_xferred;
1543 		to_write -= block;
1544 	}
1545 
1546 	*nwritten = written;
1547 	release_sem(port->wr_entry);
1548 
1549 	return B_OK;
1550 }
1551 
1552 
1553 bool
1554 dma_a_interrupt(
1555 	cmedia_pci_dev * dev)
1556 {
1557 	bool ret = false;
1558 	pcm_dev * port = &dev->pcm;
1559 	volatile uchar * ptr;
1560 	uint32 addr;
1561 	uint32 offs;
1562 	bigtime_t st = system_time();
1563 	int32 ww;
1564 
1565 #if 0
1566 ddprintf(("cmedia_pci: dma_a 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_a), PCI_IO_RD_32((int)port->dma_a+4)));
1567 #endif
1568 //	KTRACE(); /* */
1569 	acquire_spinlock(&port->wr_lock);
1570 
1571 	if (port->write_sem < 0) {
1572 		kprintf("cmedia_pci: spurious DMA A interrupt!\n");
1573 		release_spinlock(&port->wr_lock);
1574 		return false;
1575 	}
1576 	/* copy possible silence into playback buffer */
1577 
1578 	if (port->was_written > 0 && port->was_written < port->wr_size) {
1579 		if (port->config.format == 0x11) {
1580 			memset((void *)(port->wr_cur+port->was_written), 0x80, port->wr_size-port->was_written);
1581 		}
1582 		else {
1583 			memset((void *)(port->wr_cur+port->was_written), 0, port->wr_size-port->was_written);
1584 		}
1585 	}
1586 
1587 	/* because the system may be lacking and not hand us the */
1588 	/* interrupt in time, we check which half is currently being */
1589 	/* played, and set the pointer to the other half */
1590 
1591 	addr = PCI_IO_RD_32((uint32)port->dma_a);
1592 	if ((offs = addr-(uint32)port->card->low_phys) < port->wr_size) {
1593 		ptr = port->wr_2;
1594 	}
1595 	else {
1596 		ptr = port->wr_1;
1597 	}
1598 	port->wr_total += port->config.play_buf_size/2;
1599 	/* compensate for interrupt latency */
1600 	/* assuming 4 byte frames */
1601 	port->wr_time = st-(offs&(port->config.play_buf_size/2-1))*250000LL/(int64)port->config.sample_rate;
1602 	if ((ww = atomic_add(&port->wr_time_wait, -1)) > 0) {
1603 		release_sem_etc(port->wr_time_sem, 1, B_DO_NOT_RESCHEDULE);
1604 		ret = true;
1605 	}
1606 	else {
1607 		atomic_add(&port->wr_time_wait, 1); /* re-set to 0 */
1608 	}
1609 
1610 	if (port->wr_cur == ptr) {
1611 		port->wr_skipped++;
1612 		OLDAPI(("cmedia_pci: write skipped %ld\n", port->wr_skipped));
1613 	}
1614 	port->wr_cur = ptr;
1615 	port->was_written = 0;
1616 
1617 	/* check for client there to write into buffer */
1618 
1619 	if (atomic_add(&port->write_waiting, -1) > 0) {
1620 #if DEBUG
1621 		int_cnt++;
1622 		the_time = st;
1623 #endif
1624 		release_sem_etc(port->write_sem, 1, B_DO_NOT_RESCHEDULE);
1625 		ret = true;
1626 	}
1627 	else {
1628 		atomic_add(&port->write_waiting, 1);
1629 		/* if none there, fill with silence */
1630 		if (port->wr_silence < port->config.play_buf_size*2) {
1631 			if (port->config.format == 0x11) {
1632 				memset((void *)ptr, 0x80, port->wr_size);
1633 			}
1634 			else {
1635 				memset((void *)ptr, 0, port->wr_size);
1636 			}
1637 			port->wr_silence += port->wr_size;
1638 		}
1639 	}
1640 	/* copying will be done in user thread */
1641 
1642 	release_spinlock(&port->wr_lock);
1643 	return ret;
1644 }
1645 
1646 
1647 bool
1648 dma_c_interrupt(
1649 	cmedia_pci_dev * dev)
1650 {
1651 	bool ret = false;
1652 	pcm_dev * port = &dev->pcm;
1653 	volatile uchar * ptr;
1654 	uint32 addr;
1655 	uint32 offs;
1656 	int32 rr;
1657 	bigtime_t st = system_time();
1658 
1659 	/* mark data as readable in record buffer */
1660 #if 0
1661 ddprintf(("cmedia_pci: dma_c 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_c), PCI_IO_RD_32((int)port->dma_c+4)));
1662 #endif
1663 //	KTRACE(); /* */
1664 	acquire_spinlock(&port->rd_lock);
1665 
1666 	if (port->read_sem < 0) {
1667 		kprintf("cmedia_pci: spurious DMA C interrupt!\n");
1668 		release_spinlock(&port->rd_lock);
1669 		return false;
1670 	}
1671 	/* if we lose an interrupt, we automatically avoid constant glitching by setting */
1672 	/* the write pointer based on where the DMA counter is everytime */
1673 	addr = PCI_IO_RD_32((uint32)port->dma_c);
1674 	if ((offs = addr-port->config.play_buf_size-(uint32)port->card->low_phys) < port->rd_size) {
1675 		ptr = port->rd_2;
1676 	}
1677 	else {
1678 		ptr = port->rd_1;
1679 	}
1680 	if (port->rd_cur == ptr) {
1681 		port->rd_skipped++;
1682 		OLDAPI(("cmedia_pci: read skipped %ld\n", port->rd_skipped));
1683 	}
1684 	port->rd_total += port->rd_size;
1685 
1686 	port->rd_cur = ptr;
1687 	port->was_read = 0;
1688 	port->rd_time = port->next_rd_time;
1689 	/*	time stamp when this buffer became available -- compensate for interrupt latency	*/
1690 	port->next_rd_time = st-(offs&(port->config.rec_buf_size/2-1))*1000000LL/(int64)port->config.sample_rate;
1691 	if ((rr = atomic_add(&port->rd_time_wait, -1)) > 0) {
1692 		release_sem_etc(port->rd_time_sem, 1, B_DO_NOT_RESCHEDULE);
1693 		ret = true;
1694 	}
1695 	else {
1696 		atomic_add(&port->rd_time_wait, 1); /* re-set to 0 */
1697 	}
1698 
1699 	if (atomic_add(&port->read_waiting, -1) > 0) {
1700 		release_sem_etc(port->read_sem, 1, B_DO_NOT_RESCHEDULE);
1701 		ret = true;
1702 	}
1703 	else {
1704 		atomic_add(&port->read_waiting, 1);
1705 	}
1706 	/* copying will be done in the user thread */
1707 	release_spinlock(&port->rd_lock);
1708 	return ret;
1709 }
1710 
1711