xref: /haiku/src/add-ons/kernel/drivers/audio/echo/echo.cpp (revision 9eb55bc1d104b8fda80898f8b25c94d8000c8255)
1 //------------------------------------------------------------------------------
2 //
3 //  EchoGals/Echo24 BeOS Driver for Echo audio cards
4 //
5 //	Copyright (c) 2003, Jérôme Duval
6 //
7 //	Permission is hereby granted, free of charge, to any person obtaining a
8 //	copy of this software and associated documentation files (the "Software"),
9 //	to deal in the Software without restriction, including without limitation
10 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 //	and/or sell copies of the Software, and to permit persons to whom the
12 //	Software is furnished to do so, subject to the following conditions:
13 //
14 //	The above copyright notice and this permission notice shall be included in
15 //	all copies or substantial portions of the Software.
16 //
17 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 //	DEALINGS IN THE SOFTWARE.
24 
25 #include <KernelExport.h>
26 #include <Drivers.h>
27 #include <malloc.h>
28 #include <unistd.h>
29 #include "OsSupportBeOS.h"
30 #include "EchoGalsXface.h"
31 #include "CDarla24.h"
32 #include "CDarla.h"
33 #include "CGina.h"
34 #include "CGina24.h"
35 #include "CLayla.h"
36 #include "CLayla24.h"
37 #include "CMia.h"
38 #include "CMona.h"
39 #include "echo.h"
40 #include "debug.h"
41 #include "util.h"
42 
43 static char pci_name[] = B_PCI_MODULE_NAME;
44 static pci_module_info	*pci;
45 int32 num_cards;
46 echo_dev cards[NUM_CARDS];
47 int32 num_names;
48 char * names[NUM_CARDS*20+1];
49 
50 extern device_hooks multi_hooks;
51 
52 int32 echo_int(void *arg);
53 status_t init_hardware(void);
54 status_t init_driver(void);
55 static void make_device_names(echo_dev * card);
56 
57 static status_t echo_setup(echo_dev * card);
58 static void echo_shutdown(echo_dev *card);
59 
60 void uninit_driver(void);
61 const char ** publish_devices(void);
62 device_hooks * find_device(const char * name);
63 
64 
65 /* Echo Memory management */
66 
67 echo_mem *
68 echo_mem_new(echo_dev *card, size_t size)
69 {
70 	echo_mem *mem;
71 
72 	if ((mem = (echo_mem *) malloc(sizeof(*mem))) == NULL)
73 		return (NULL);
74 
75 	mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "echo buffer");
76 	mem->size = size;
77 	if (mem->area < B_OK) {
78 		free(mem);
79 		return NULL;
80 	}
81 	return mem;
82 }
83 
84 void
85 echo_mem_delete(echo_mem *mem)
86 {
87 	if(mem->area > B_OK)
88 		delete_area(mem->area);
89 	free(mem);
90 }
91 
92 echo_mem *
93 echo_mem_alloc(echo_dev *card, size_t size)
94 {
95 	echo_mem *mem;
96 
97 	mem = echo_mem_new(card, size);
98 	if (mem == NULL)
99 		return (NULL);
100 
101 	LIST_INSERT_HEAD(&(card->mems), mem, next);
102 
103 	return mem;
104 }
105 
106 void
107 echo_mem_free(echo_dev *card, void *ptr)
108 {
109 	echo_mem 		*mem;
110 
111 	LIST_FOREACH(mem, &card->mems, next) {
112 		if (mem->log_base != ptr)
113 			continue;
114 		LIST_REMOVE(mem, next);
115 
116 		echo_mem_delete(mem);
117 		break;
118 	}
119 }
120 
121 /*	Echo stream functions */
122 
123 status_t
124 echo_stream_set_audioparms(echo_stream *stream, uint8 channels,
125      uint8 b16, uint32 sample_rate)
126 {
127 	int32 			i;
128 	uint8 			sample_size, frame_size;
129 	ECHOGALS_OPENAUDIOPARAMETERS	open_params;
130 	ECHOGALS_CLOSEAUDIOPARAMETERS  close_params;
131 	ECHOGALS_AUDIOFORMAT			format_params;
132 
133 	LOG(("echo_stream_set_audioparms\n"));
134 
135 	close_params.wPipeIndex = stream->pipe;
136 	stream->card->pEG->CloseAudio(&close_params);
137 
138 	open_params.bIsCyclic = TRUE;
139 	open_params.Pipe.nPipe = 0;
140 	open_params.Pipe.bIsInput = stream->use == ECHO_USE_RECORD ? TRUE : FALSE;
141 	open_params.Pipe.wInterleave = stream->channels;
142 
143 	stream->card->pEG->OpenAudio(&open_params, &stream->pipe);
144 
145 	if ((stream->channels == channels) &&
146 		(stream->b16 == b16) &&
147 		(stream->sample_rate == sample_rate))
148 		return B_OK;
149 
150 	format_params.wBitsPerSample = b16 == 0 ? 8 : 16;
151 	format_params.byDataAreBigEndian = 0;
152 	format_params.byMonoToStereo = 0;
153 	format_params.wDataInterleave = channels == 1 ? 1 : 2;
154 
155 	if(stream->card->pEG->QueryAudioFormat(stream->pipe, &format_params)!=ECHOSTATUS_OK) {
156 		PRINT(("echo_stream_set_audioparms : bad format when querying\n"));
157 		return B_ERROR;
158 	}
159 
160 	/* XXXX : setting sample rate is global in this driver */
161 	if(stream->card->pEG->QueryAudioSampleRate(sample_rate)!=ECHOSTATUS_OK) {
162 		PRINT(("echo_stream_set_audioparms : bad sample rate when querying\n"));
163 		return B_ERROR;
164 	}
165 
166 	if(stream->card->pEG->SetAudioFormat(stream->pipe, &format_params)!=ECHOSTATUS_OK) {
167 		PRINT(("echo_stream_set_audioparms : bad format when setting\n"));
168 		return B_ERROR;
169 	}
170 
171 	/* XXXX : setting sample rate is global in this driver */
172 	if(stream->card->pEG->SetAudioSampleRate(sample_rate)!=ECHOSTATUS_OK) {
173 		PRINT(("echo_stream_set_audioparms : bad sample rate when setting\n"));
174 		return B_ERROR;
175 	}
176 
177 	if(stream->buffer)
178 		echo_mem_free(stream->card, stream->buffer->log_base);
179 
180 	stream->b16 = b16;
181 	stream->sample_rate = sample_rate;
182 	stream->channels = channels;
183 
184 	sample_size = stream->b16 + 1;
185 	frame_size = sample_size * stream->channels;
186 
187 	stream->buffer = echo_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
188 
189 	stream->trigblk = 0;	/* This shouldn't be needed */
190 	stream->blkmod = stream->bufcount;
191 	stream->blksize = stream->bufframes * frame_size;
192 
193 	CDaffyDuck *duck = stream->card->pEG->GetDaffyDuck(stream->pipe);
194 	if(duck == NULL) {
195 		PRINT(("echo_stream_set_audioparms : Could not get daffy duck pointer\n"));
196 		return B_ERROR;
197 	}
198 
199 	uint32 dwNumFreeEntries = 0;
200 
201 	for(i=0; i<stream->bufcount; i++) {
202 		duck->AddMapping(((uint32)stream->buffer->phy_base) +
203 			i * stream->blksize, stream->blksize, 0, TRUE, dwNumFreeEntries);
204 	}
205 
206 	duck->Wrap();
207 
208 	if(stream->card->pEG->GetAudioPositionPtr(stream->pipe, stream->position)!=ECHOSTATUS_OK) {
209 		PRINT(("echo_stream_set_audioparms : Could not get audio position ptr\n"));
210 		return B_ERROR;
211 	}
212 
213 	return B_OK;
214 }
215 
216 
217 status_t
218 echo_stream_get_nth_buffer(echo_stream *stream, uint8 chan, uint8 buf,
219 					char** buffer, size_t *stride)
220 {
221 	uint8 			sample_size, frame_size;
222 	LOG(("echo_stream_get_nth_buffer\n"));
223 
224 	sample_size = stream->b16 + 1;
225 	frame_size = sample_size * stream->channels;
226 
227 	*buffer = (char*)stream->buffer->log_base + (buf * stream->bufframes * frame_size)
228 		+ chan * sample_size;
229 	*stride = frame_size;
230 
231 	return B_OK;
232 }
233 
234 
235 static uint32
236 echo_stream_curaddr(echo_stream *stream)
237 {
238 	uint32 addr = *stream->position - (uint32)stream->buffer->phy_base;
239 	TRACE(("stream_curaddr %p, phy_base %p\n", addr, (uint32)stream->buffer->phy_base));
240 	return addr;
241 }
242 
243 
244 void
245 echo_stream_start(echo_stream *stream, void (*inth) (void *), void *inthparam)
246 {
247 	LOG(("echo_stream_start\n"));
248 
249 	stream->inth = inth;
250 	stream->inthparam = inthparam;
251 
252 	stream->state |= ECHO_STATE_STARTED;
253 
254 	if(stream->card->pEG->Start(stream->pipe)!=ECHOSTATUS_OK) {
255 		PRINT(("echo_stream_start : Could not start the pipe\n"));
256 	}
257 }
258 
259 void
260 echo_stream_halt(echo_stream *stream)
261 {
262 	LOG(("echo_stream_halt\n"));
263 
264 	stream->state &= ~ECHO_STATE_STARTED;
265 
266 	if(stream->card->pEG->Stop(stream->pipe)!=ECHOSTATUS_OK) {
267 		PRINT(("echo_stream_halt : Could not stop the pipe\n"));
268 	}
269 }
270 
271 echo_stream *
272 echo_stream_new(echo_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
273 {
274 	echo_stream *stream;
275 	cpu_status status;
276 	LOG(("echo_stream_new\n"));
277 
278 	stream = (echo_stream *) malloc(sizeof(echo_stream));
279 	if (stream == NULL)
280 		return (NULL);
281 	stream->card = card;
282 	stream->use = use;
283 	stream->state = !ECHO_STATE_STARTED;
284 	stream->b16 = 0;
285 	stream->sample_rate = 0;
286 	stream->channels = 0;
287 	stream->bufframes = bufframes;
288 	stream->bufcount = bufcount;
289 	stream->inth = NULL;
290 	stream->inthparam = NULL;
291 	stream->buffer = NULL;
292 	stream->blksize = 0;
293 	stream->trigblk = 0;
294 	stream->blkmod = 0;
295 
296 	stream->pipe = 0;
297 
298 	stream->frames_count = 0;
299 	stream->real_time = 0;
300 	stream->update_needed = false;
301 
302 	status = lock();
303 	LIST_INSERT_HEAD((&card->streams), stream, next);
304 	unlock(status);
305 
306 	return stream;
307 }
308 
309 void
310 echo_stream_delete(echo_stream *stream)
311 {
312 	cpu_status status;
313 	LOG(("echo_stream_delete\n"));
314 
315 	echo_stream_halt(stream);
316 
317 	if(stream->buffer)
318 		echo_mem_free(stream->card, stream->buffer->log_base);
319 
320 	status = lock();
321 	LIST_REMOVE(stream, next);
322 	unlock(status);
323 
324 	free(stream);
325 }
326 
327 
328 /* Echo interrupt */
329 
330 int32 echo_int(void *arg)
331 {
332 	echo_dev	 	*card = (echo_dev*)arg;
333 	BOOL			midiReceived;
334 	ECHOSTATUS 		err;
335 
336 	echo_stream *stream;
337 	uint32       curblk;
338 
339 	err = card->pEG->ServiceIrq(midiReceived);
340 
341 	if(err == ECHOSTATUS_OK) {
342 		LIST_FOREACH(stream, &card->streams, next) {
343 			if ((stream->use & ECHO_USE_PLAY) == 0 ||
344 				(stream->state & ECHO_STATE_STARTED) == 0 ||
345 				(stream->inth == NULL))
346 					continue;
347 
348 			TRACE(("echo_int stream %p\n", stream));
349 			curblk = echo_stream_curaddr(stream) / stream->blksize;
350 			TRACE(("echo_int at trigblk %lu\n", curblk));
351 			TRACE(("echo_int at stream->trigblk %lu\n", stream->trigblk));
352 			if (curblk == stream->trigblk) {
353 				if(stream->inth)
354 					stream->inth(stream->inthparam);
355 
356 				stream->trigblk++;
357 				stream->trigblk %= stream->blkmod;
358 			}
359 		}
360 
361 		return B_HANDLED_INTERRUPT;
362 	} else
363 		return B_UNHANDLED_INTERRUPT;
364 }
365 
366 /* detect presence of our hardware */
367 status_t
368 init_hardware(void)
369 {
370 	int ix=0;
371 	pci_info info;
372 	status_t err = ENODEV;
373 
374 	LOG_CREATE();
375 
376 	PRINT(("init_hardware()\n"));
377 
378 	if (get_module(pci_name, (module_info **)&pci))
379 		return ENOSYS;
380 
381 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
382 
383 		ushort card_type = info.u.h0.subsystem_id & 0xfff0;
384 
385 		if (info.vendor_id == VENDOR_ID &&
386 			((info.device_id == DEVICE_ID_56301)
387 			|| (info.device_id == DEVICE_ID_56361)) &&
388 			(info.u.h0.subsystem_vendor_id == SUBVENDOR_ID) &&
389 			(
390 #ifdef ECHOGALS_FAMILY
391 			(card_type == DARLA)
392 			|| (card_type == GINA)
393 			|| (card_type == LAYLA)
394 			|| (card_type == DARLA24)
395 #endif
396 #ifdef ECHO24_FAMILY
397 			(card_type == GINA24)
398 			|| (card_type == LAYLA24)
399 			|| (card_type == MONA)
400 			|| (card_type == MIA)
401 			|| (card_type == INDIGO)
402 #endif
403 			 )) {
404 			err = B_OK;
405 		}
406 		ix++;
407 	}
408 
409 	put_module(pci_name);
410 
411 	if(err!=B_OK) {
412 		PRINT(("no card found\n"));
413 	}
414 
415 	return err;
416 }
417 
418 
419 status_t
420 init_driver(void)
421 {
422 	int ix=0;
423 
424 	pci_info info;
425 	num_cards = 0;
426 
427 	PRINT(("init_driver()\n"));
428 	load_driver_symbols(DRIVER_NAME);
429 
430 	if (get_module(pci_name, (module_info **) &pci))
431 		return ENOSYS;
432 
433 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
434 		ushort card_type = info.u.h0.subsystem_id & 0xfff0;
435 
436 		if (info.vendor_id == VENDOR_ID &&
437 			((info.device_id == DEVICE_ID_56301)
438 			|| (info.device_id == DEVICE_ID_56361)) &&
439 			(info.u.h0.subsystem_vendor_id == SUBVENDOR_ID) &&
440 			(
441 #ifdef ECHOGALS_FAMILY
442 			(card_type == DARLA)
443 			|| (card_type == GINA)
444 			|| (card_type == LAYLA)
445 			|| (card_type == DARLA24)
446 #endif
447 #ifdef ECHO24_FAMILY
448 			(card_type == GINA24)
449 			|| (card_type == LAYLA24)
450 			|| (card_type == MONA)
451 			|| (card_type == MIA)
452 			|| (card_type == INDIGO)
453 #endif
454 			)) {
455 
456 			if (num_cards == NUM_CARDS) {
457 				PRINT(("Too many "DRIVER_NAME" cards installed!\n"));
458 				break;
459 			}
460 			memset(&cards[num_cards], 0, sizeof(echo_dev));
461 			cards[num_cards].info = info;
462 			cards[num_cards].type = card_type;
463 			if (echo_setup(&cards[num_cards])) {
464 				PRINT(("Setup of "DRIVER_NAME" %ld failed\n", num_cards+1));
465 			}
466 			else {
467 				num_cards++;
468 			}
469 		}
470 		ix++;
471 	}
472 	if (!num_cards) {
473 		PRINT(("no cards\n"));
474 		put_module(pci_name);
475 		PRINT(("no suitable cards found\n"));
476 		return ENODEV;
477 	}
478 
479 	return B_OK;
480 }
481 
482 
483 static void
484 make_device_names(
485 	echo_dev * card)
486 {
487 	sprintf(card->name, "audio/multi/"DRIVER_NAME"/%ld", card-cards+1);
488 	names[num_names++] = card->name;
489 
490 	names[num_names] = NULL;
491 }
492 
493 
494 static status_t
495 echo_setup(echo_dev * card)
496 {
497 	status_t err = B_OK;
498 	unsigned char cmd;
499 
500 	PRINT(("echo_setup(%p)\n", card));
501 
502 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function,
503 		PCI_latency, 1, 0xc0 );
504 
505 	make_device_names(card);
506 
507 	card->bmbar = card->info.u.h0.base_registers[0];
508 	card->irq = card->info.u.h0.interrupt_line;
509 
510 	card->pOSS = new COsSupport(card->info.device_id);
511 	if(card->pOSS == NULL)
512 		return B_ERROR;
513 
514 	switch (card->type) {
515 #ifdef ECHOGALS_FAMILY
516 		case DARLA:
517 			card->pEG = new CDarla(card->pOSS);
518 			break;
519 		case GINA:
520 			card->pEG = new CGina(card->pOSS);
521 			break;
522 		case LAYLA:
523 			card->pEG = new CLayla(card->pOSS);
524 			break;
525 		case DARLA24:
526 			card->pEG = new CDarla24(card->pOSS);
527 			break;
528 #endif
529 #ifdef ECHO24_FAMILY
530 		case GINA24:
531 			card->pEG = new CGina24(card->pOSS);
532 			break;
533 		case LAYLA24:
534 			card->pEG = new CLayla24(card->pOSS);
535 			break;
536 		case MONA:
537 			card->pEG = new CMona(card->pOSS);
538 			break;
539 		case MIA:
540 			card->pEG = new CMia(card->pOSS);
541 			break;
542 #endif
543 		default:
544 			PRINT(("card type 0x%x not supported by "DRIVER_NAME"\n", card->type));
545 			delete card->pOSS;
546 			return B_ERROR;
547 	}
548 
549 	if (card->pEG == NULL)
550 		return B_ERROR;
551 
552 	card->area_bmbar = map_mem(&card->log_bmbar, (void *)card->bmbar,
553 		card->info.u.h0.base_register_sizes[0], DRIVER_NAME" bmbar io");
554 	if (card->area_bmbar <= B_OK) {
555 		LOG(("mapping of bmbar io failed, error = %#x\n",card->area_bmbar));
556 		return B_ERROR;
557 	}
558 	LOG(("mapping of bmbar: area %#x, phys %#x, log %#x\n", card->area_bmbar, card->bmbar, card->log_bmbar));
559 
560 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
561 	PRINT(("PCI command before: %x\n", cmd));
562 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io);
563 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
564 	PRINT(("PCI command after: %x\n", cmd));
565 
566 	card->pEG->AssignResources(card->log_bmbar, "no name");
567 
568 	ECHOSTATUS status;
569 	status = card->pEG->InitHw();
570 	if(status != ECHOSTATUS_OK)
571 		return B_ERROR;
572 
573 	/* Init streams list */
574 	LIST_INIT(&(card->streams));
575 
576 	/* Init mems list */
577 	LIST_INIT(&(card->mems));
578 
579 	PRINT(("installing interrupt : %x\n", card->irq));
580 	install_io_interrupt_handler(card->irq, echo_int, card, 0);
581 
582 	PRINT(("echo_setup done\n"));
583 
584 	return err;
585 }
586 
587 static void
588 echo_shutdown(echo_dev *card)
589 {
590 	PRINT(("shutdown(%p)\n", card));
591 	remove_io_interrupt_handler(card->irq, echo_int, card);
592 
593 	delete card->pEG;
594 	delete card->pOSS;
595 
596 	delete_area(card->area_bmbar);
597 }
598 
599 
600 
601 void
602 uninit_driver(void)
603 {
604 	int ix, cnt = num_cards;
605 	num_cards = 0;
606 
607 	PRINT(("uninit_driver()\n"));
608 
609 	for (ix=0; ix<cnt; ix++) {
610 		echo_shutdown(&cards[ix]);
611 	}
612 	memset(&cards, 0, sizeof(cards));
613 	put_module(pci_name);
614 }
615 
616 
617 const char **
618 publish_devices(void)
619 {
620 	int ix = 0;
621 	PRINT(("publish_devices()\n"));
622 
623 	for (ix=0; names[ix]; ix++) {
624 		PRINT(("publish %s\n", names[ix]));
625 	}
626 	return (const char **)names;
627 }
628 
629 
630 device_hooks *
631 find_device(const char * name)
632 {
633 	int ix;
634 
635 	PRINT(("find_device(%s)\n", name));
636 
637 	for (ix=0; ix<num_cards; ix++) {
638 		if (!strcmp(cards[ix].name, name)) {
639 			return &multi_hooks;
640 		}
641 	}
642 	PRINT(("find_device(%s) failed\n", name));
643 	return NULL;
644 }
645 
646 int32	api_version = B_CUR_DRIVER_API_VERSION;
647