xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/sis7018/Device.cpp (revision 23d878482ed22e55dad6d1fca1df7bea42eb157c)
1 /*
2  *	SiS 7018, Trident 4D Wave DX/NX, Acer Lab M5451 Sound Driver.
3  *	Copyright (c) 2002, 2008-2011 S.Zharski <imker@gmx.li>
4  *	Distributed under the terms of the MIT license.
5  *
6  *	Copyright for ali5451 support:
7  *		(c) 2009, Krzysztof Ćwiertnia (krzysiek.bmkx_gmail_com).
8  */
9 
10 
11 #include "Device.h"
12 
13 #include <string.h>
14 
15 #include "Settings.h"
16 #include "Registers.h"
17 
18 
19 Device::Device(Device::Info &DeviceInfo, pci_info &PCIInfo)
20 		:
21 		fStatus(B_ERROR),
22 		fPCIInfo(PCIInfo),
23 		fInfo(DeviceInfo),
24 		fIOBase(0),
25 		fHWSpinlock(0),
26 		fInterruptsNest(0),
27 		fBuffersReadySem(-1),
28 		fMixer(this),
29 		fPlaybackStream(this, false),
30 		fRecordStream(this, true)
31 {
32 	fStatus = _ReserveDeviceOnBus(true);
33 	if (fStatus != B_OK)
34 		return; // InitCheck will handle the rest
35 
36 	uint32 cmdRegister = gPCI->read_pci_config(PCIInfo.bus,
37 							PCIInfo.device,	PCIInfo.function, PCI_command, 2);
38 	TRACE("cmdRegister:%#010x\n", cmdRegister);
39 	cmdRegister |= PCI_command_io | PCI_command_memory | PCI_command_master;
40 	gPCI->write_pci_config(PCIInfo.bus, PCIInfo.device,
41 							PCIInfo.function, PCI_command, 2, cmdRegister);
42 
43 	fIOBase = PCIInfo.u.h0.base_registers[0];
44 	TRACE("fIOBase:%#010x\n", fIOBase);
45 
46 	fStatus = B_OK;
47 }
48 
49 
50 Device::~Device()
51 {
52 	fMixer.Free();
53 	_ReserveDeviceOnBus(false);
54 
55 	if (fBuffersReadySem > B_OK) {
56 		delete_sem(fBuffersReadySem);
57 	}
58 }
59 
60 
61 void
62 Device::_ResetCard(uint32 resetMask, uint32 releaseMask)
63 {
64 	// disable Legacy Control
65 	gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
66 						fPCIInfo.function, 0x40, 4, 0);
67 	uint32 cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device,
68 						fPCIInfo.function, 0x44, 4);
69 	gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
70 						fPCIInfo.function, 0x44, 4, cmdReg & 0xffff0000);
71 	snooze(100);
72 
73 	// audio engine reset
74 	cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device,
75 						fPCIInfo.function, 0x44, 4);
76 	gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
77 						fPCIInfo.function, 0x44, 4, cmdReg | resetMask);
78 	snooze(100);
79 
80 	// release reset
81 	cmdReg = gPCI->read_pci_config(fPCIInfo.bus, fPCIInfo.device,
82 						fPCIInfo.function, 0x44, 4);
83 	gPCI->write_pci_config(fPCIInfo.bus, fPCIInfo.device,
84 						fPCIInfo.function, 0x44, 4, cmdReg & ~releaseMask);
85 	snooze(100);
86 }
87 
88 
89 status_t
90 Device::Setup()
91 {
92 	cpu_status cp = 0;
93 	uint32 channelsIndex = ChIndexMidEna | ChIndexEndEna;
94 
95 	switch (HardwareId()) {
96 		case SiS7018:
97 			_ResetCard(0x000c0000, 0x00040000);
98 
99 			cp = Lock();
100 
101 			WritePCI32(RegSiSCodecGPIO, 0x00000000);
102 			WritePCI32(RegSiSCodecStatus, SiSCodecResetOff);
103 			channelsIndex |= ChIndexSiSEnaB;
104 
105 			Unlock(cp);
106 			break;
107 
108 		case ALi5451:
109 			_ResetCard(0x000c0000, 0x00040000);
110 
111 			cp = Lock();
112 			WritePCI32(RegALiDigiMixer, ALiDigiMixerPCMIn);
113 			WritePCI32(RegALiVolumeA, 0);
114 			Unlock(cp);
115 			break;
116 
117 		case TridentNX:
118 			_ResetCard(0x00010000, 0x00010000);
119 
120 			cp = Lock();
121 			WritePCI32(RegNXCodecStatus, NXCodecStatusDAC1ON);
122 			Unlock(cp);
123 			break;
124 
125 		case TridentDX:
126 			_ResetCard(0x00040000, 0x00040000);
127 
128 			cp = Lock();
129 			WritePCI32(RegCodecStatus, CodecStatusDACON);
130 			Unlock(cp);
131 			break;
132 	}
133 
134 	// clear channels status
135 	WritePCI32(RegStopA, 0xffffffff);
136 	WritePCI32(RegStopB, 0xffffffff);
137 
138 	// disable channels interrupt
139 	WritePCI32(RegEnaINTA, 0x00000000);
140 	WritePCI32(RegEnaINTB, 0x00000000);
141 
142 	// enable loop interrupts
143 	WritePCI32(RegChIndex, channelsIndex);
144 
145 	fRecordStream.Init();
146 	fPlaybackStream.Init();
147 
148 	fBuffersReadySem = create_sem(0, DRIVER_NAME "_buffers_ready");
149 
150 	fMixer.Init();
151 
152 	return B_OK;
153 }
154 
155 
156 status_t
157 Device::Open(uint32 flags)
158 {
159 	TRACE("flags:%x\n", flags);
160 
161 	if (atomic_add(&fInterruptsNest, 1) == 0) {
162 		install_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line,
163 												InterruptHandler, this, 0);
164 		TRACE("Interrupt handler installed at line %d.\n",
165 												fPCIInfo.u.h0.interrupt_line);
166 	}
167 
168 	status_t status = fRecordStream.Start();
169 	if (status != B_OK) {
170 		ERROR("Error of starting record stream:%#010x\n", status);
171 	}
172 
173 	status = fPlaybackStream.Start();
174 	if (status != B_OK) {
175 		ERROR("Error of starting playback stream:%#010x\n", status);
176 	}
177 
178 	return B_OK;
179 }
180 
181 
182 status_t
183 Device::Close()
184 {
185 	TRACE("closed!\n");
186 
187 	status_t status = fPlaybackStream.Stop();
188 	if (status != B_OK) {
189 		ERROR("Error of stopping playback stream:%#010x\n", status);
190 	}
191 
192 	status = fRecordStream.Stop();
193 	if (status != B_OK) {
194 		ERROR("Error of stopping record stream:%#010x\n", status);
195 	}
196 
197 	if (atomic_add(&fInterruptsNest, -1) == 1) {
198 		remove_io_interrupt_handler(fPCIInfo.u.h0.interrupt_line,
199 												InterruptHandler, this);
200 		TRACE("Interrupt handler at line %d uninstalled.\n",
201 												fPCIInfo.u.h0.interrupt_line);
202 	}
203 
204 	return B_OK;
205 }
206 
207 
208 status_t
209 Device::Free()
210 {
211 	TRACE("freed\n");
212 	return B_OK;
213 }
214 
215 
216 status_t
217 Device::Read(uint8 *buffer, size_t *numBytes)
218 {
219 	*numBytes = 0;
220 	return B_IO_ERROR;
221 }
222 
223 
224 status_t
225 Device::Write(const uint8 *buffer, size_t *numBytes)
226 {
227 	*numBytes = 0;
228 	return B_IO_ERROR;
229 }
230 
231 
232 status_t
233 Device::Control(uint32 op, void *buffer, size_t length)
234 {
235 	switch (op) {
236 		case B_MULTI_GET_DESCRIPTION:
237 			return _MultiGetDescription((multi_description*)buffer);
238 
239 		case B_MULTI_GET_EVENT_INFO:
240 			TRACE(("B_MULTI_GET_EVENT_INFO\n"));
241 			return B_ERROR;
242 
243 		case B_MULTI_SET_EVENT_INFO:
244 			TRACE(("B_MULTI_SET_EVENT_INFO\n"));
245 			return B_ERROR;
246 
247 		case B_MULTI_GET_EVENT:
248 			TRACE(("B_MULTI_GET_EVENT\n"));
249 			return B_ERROR;
250 
251 		case B_MULTI_GET_ENABLED_CHANNELS:
252 			return _MultiGetEnabledChannels((multi_channel_enable*)buffer);
253 
254 		case B_MULTI_SET_ENABLED_CHANNELS:
255 			return _MultiSetEnabledChannels((multi_channel_enable*)buffer);
256 
257 		case B_MULTI_GET_GLOBAL_FORMAT:
258 			return _MultiGetGlobalFormat((multi_format_info*)buffer);
259 
260 		case B_MULTI_SET_GLOBAL_FORMAT:
261 			return _MultiSetGlobalFormat((multi_format_info*)buffer);
262 
263 		case B_MULTI_GET_CHANNEL_FORMATS:
264 			TRACE(("B_MULTI_GET_CHANNEL_FORMATS\n"));
265 			return B_ERROR;
266 
267 		case B_MULTI_SET_CHANNEL_FORMATS:
268 			TRACE(("B_MULTI_SET_CHANNEL_FORMATS\n"));
269 			return B_ERROR;
270 
271 		case B_MULTI_GET_MIX:
272 			return _MultiGetMix((multi_mix_value_info *)buffer);
273 
274 		case B_MULTI_SET_MIX:
275 			return _MultiSetMix((multi_mix_value_info *)buffer);
276 
277 		case B_MULTI_LIST_MIX_CHANNELS:
278 			TRACE(("B_MULTI_LIST_MIX_CHANNELS\n"));
279 			return B_ERROR;
280 
281 		case B_MULTI_LIST_MIX_CONTROLS:
282 			return _MultiListMixControls((multi_mix_control_info*)buffer);
283 
284 		case B_MULTI_LIST_MIX_CONNECTIONS:
285 			TRACE(("B_MULTI_LIST_MIX_CONNECTIONS\n"));
286 			return B_ERROR;
287 
288 		case B_MULTI_GET_BUFFERS:
289 			return _MultiGetBuffers((multi_buffer_list*)buffer);
290 
291 		case B_MULTI_SET_BUFFERS:
292 			TRACE(("B_MULTI_SET_BUFFERS\n"));
293 			return B_ERROR;
294 
295 		case B_MULTI_SET_START_TIME:
296 			TRACE(("B_MULTI_SET_START_TIME\n"));
297 			return B_ERROR;
298 
299 		case B_MULTI_BUFFER_EXCHANGE:
300 			return _MultiBufferExchange((multi_buffer_info*)buffer);
301 
302 		case B_MULTI_BUFFER_FORCE_STOP:
303 			TRACE(("B_MULTI_BUFFER_FORCE_STOP\n"));
304 			return B_ERROR;
305 
306 		default:
307 			ERROR("Unhandled IOCTL catched: %#010x\n", op);
308 	}
309 
310 	return B_DEV_INVALID_IOCTL;
311 }
312 
313 
314 status_t
315 Device::_MultiGetDescription(multi_description *multiDescription)
316 {
317 	multi_channel_info channel_descriptions[] = {
318 		{ 0, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,	 0 },
319 		{ 1, B_MULTI_OUTPUT_CHANNEL, B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
320 		{ 2, B_MULTI_INPUT_CHANNEL,	 B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,	 0 },
321 		{ 3, B_MULTI_INPUT_CHANNEL,	 B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
322 		{ 4, B_MULTI_OUTPUT_BUS, 	 B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
323 										B_CHANNEL_MINI_JACK_STEREO },
324 		{ 5, B_MULTI_OUTPUT_BUS,	 B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
325 										B_CHANNEL_MINI_JACK_STEREO },
326 		{ 6, B_MULTI_INPUT_BUS, 	 B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
327 										B_CHANNEL_MINI_JACK_STEREO },
328 		{ 7, B_MULTI_INPUT_BUS, 	 B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
329 										B_CHANNEL_MINI_JACK_STEREO },
330 	};
331 
332 	multi_description Description;
333 	if (user_memcpy(&Description,
334 			multiDescription, sizeof(multi_description)) != B_OK)
335 		return B_BAD_ADDRESS;
336 
337 	Description.interface_version = B_CURRENT_INTERFACE_VERSION;
338 	Description.interface_minimum = B_CURRENT_INTERFACE_VERSION;
339 
340 	strlcpy(Description.friendly_name, fInfo.Name(),
341 									sizeof(Description.friendly_name));
342 
343 	strlcpy(Description.vendor_info, "Haiku.Inc.",
344 									sizeof(Description.vendor_info));
345 
346 	Description.output_channel_count		= 2;
347 	Description.input_channel_count			= 2;
348 	Description.output_bus_channel_count	= 2;
349 	Description.input_bus_channel_count		= 2;
350 	Description.aux_bus_channel_count		= 0;
351 
352 	Description.output_rates	= fMixer.OutputRates();
353 	Description.input_rates		= fMixer.InputRates();
354 
355 	Description.output_formats	= fMixer.OutputFormats();
356 	Description.input_formats	= fMixer.InputFormats();
357 
358 	Description.min_cvsr_rate	= 0;
359 	Description.max_cvsr_rate	= 0;
360 
361 	Description.lock_sources = B_MULTI_LOCK_INTERNAL;
362 	Description.timecode_sources = 0;
363 	Description.interface_flags
364 		= B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD;
365 	Description.start_latency = 3000;
366 
367 	Description.control_panel[0] = '\0';
368 
369 	if (user_memcpy(multiDescription,
370 			&Description, sizeof(multi_description)) != B_OK)
371 		return B_BAD_ADDRESS;
372 
373 	if (Description.request_channel_count
374 			>= (int)(_countof(channel_descriptions))) {
375 		if (user_memcpy(multiDescription->channels,
376 					&channel_descriptions, sizeof(channel_descriptions)) != B_OK)
377 			return B_BAD_ADDRESS;
378 	}
379 
380 	return B_OK;
381 }
382 
383 
384 status_t
385 Device::_MultiGetEnabledChannels(multi_channel_enable *Enable)
386 {
387 	B_SET_CHANNEL(Enable->enable_bits, 0, true);
388 	B_SET_CHANNEL(Enable->enable_bits, 1, true);
389 	B_SET_CHANNEL(Enable->enable_bits, 2, true);
390 	B_SET_CHANNEL(Enable->enable_bits, 3, true);
391 	Enable->lock_source = B_MULTI_LOCK_INTERNAL;
392 	return B_OK;
393 }
394 
395 
396 status_t
397 Device::_MultiSetEnabledChannels(multi_channel_enable *Enable)
398 {
399 	TRACE("0:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 0) ? "en" : "dis");
400 	TRACE("1:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 1) ? "en" : "dis");
401 	TRACE("2:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 2) ? "en" : "dis");
402 	TRACE("3:%s\n", B_TEST_CHANNEL(Enable->enable_bits, 3) ? "en" : "dis");
403 	return B_OK;
404 }
405 
406 
407 status_t
408 Device::_MultiGetGlobalFormat(multi_format_info *Format)
409 {
410 	fPlaybackStream.GetFormat(Format);
411 	fRecordStream.GetFormat(Format);
412 
413 	return B_OK;
414 }
415 
416 
417 status_t
418 Device::_MultiSetGlobalFormat(multi_format_info *Format)
419 {
420 	status_t status = fPlaybackStream.SetFormat(Format->output,
421 							fMixer.OutputFormats(), fMixer.OutputRates());
422 	if (status != B_OK)
423 		return status;
424 
425 	return fRecordStream.SetFormat(Format->input,
426 				fMixer.InputFormats(), fMixer.InputRates());
427 }
428 
429 
430 status_t
431 Device::_MultiListMixControls(multi_mix_control_info* Info)
432 {
433 	return fMixer.ListMixControls(Info);
434 }
435 
436 
437 status_t
438 Device::_MultiGetMix(multi_mix_value_info *Info)
439 {
440 	return fMixer.GetMix(Info);
441 }
442 
443 
444 status_t
445 Device::_MultiSetMix(multi_mix_value_info *Info)
446 {
447 	return fMixer.SetMix(Info);
448 }
449 
450 
451 status_t
452 Device::_MultiGetBuffers(multi_buffer_list* List)
453 {
454 	fPlaybackStream.GetBuffers(List->flags, List->return_playback_buffers,
455 			List->return_playback_channels,	List->return_playback_buffer_size,
456 													List->playback_buffers);
457 
458 	fRecordStream.GetBuffers(List->flags, List->return_record_buffers,
459 			List->return_record_channels, List->return_record_buffer_size,
460 													List->record_buffers);
461 	return B_OK;
462 }
463 
464 
465 status_t
466 Device::_MultiBufferExchange(multi_buffer_info* bufferInfo)
467 {
468 	multi_buffer_info BufferInfo;
469 	if (user_memcpy(&BufferInfo, bufferInfo, sizeof(multi_buffer_info)) != B_OK) {
470 		return B_BAD_ADDRESS;
471 	}
472 
473 	status_t status = B_NO_INIT;
474 
475 	if (!fRecordStream.IsActive()) {
476 		status = fRecordStream.Start();
477 		if (status != B_OK) {
478 			ERROR("Error of starting record stream:%#010x\n", status);
479 			return status;
480 		}
481 	}
482 
483 	if (!fPlaybackStream.IsActive()) {
484 		status = fPlaybackStream.Start();
485 		if (status != B_OK) {
486 			ERROR("Error of starting playback stream:%#010x\n", status);
487 			return status;
488 		}
489 	}
490 
491 	status = acquire_sem_etc(fBuffersReadySem, 1,
492 					B_RELATIVE_TIMEOUT | B_CAN_INTERRUPT, 50000);
493 	if (status == B_TIMED_OUT) {
494 		ERROR("Timeout during buffers exchange.\n");
495 	}
496 
497 	cpu_status cst = Lock();
498 
499 	fRecordStream.ExchangeBuffers(BufferInfo.recorded_real_time,
500 			BufferInfo.recorded_frames_count, BufferInfo.record_buffer_cycle);
501 
502 	fPlaybackStream.ExchangeBuffers(BufferInfo.played_real_time,
503 			BufferInfo.played_frames_count, BufferInfo.playback_buffer_cycle);
504 
505 	Unlock(cst);
506 
507 	if (user_memcpy(bufferInfo, &BufferInfo, sizeof(multi_buffer_info)) != B_OK) {
508 		return B_BAD_ADDRESS;
509 	}
510 
511 	return B_OK;
512 }
513 
514 
515 int32
516 Device::InterruptHandler(void *interruptParam)
517 {
518 	Device *device = reinterpret_cast<Device*>(interruptParam);
519 	if (device == 0) {
520 		ERROR("Invalid parameter in the interrupt handler.\n");
521 		return B_HANDLED_INTERRUPT;
522 	}
523 
524 	bool wasHandled = false;
525 
526 	acquire_spinlock(&device->fHWSpinlock);
527 
528 	uint32 mask = device->ReadPCI32(RegMiscINT);
529 	if (mask & 0x00000020) {
530 		wasHandled = device->fRecordStream.InterruptHandler();
531 		wasHandled = device->fPlaybackStream.InterruptHandler() || wasHandled;
532 	}
533 
534 	release_spinlock(&device->fHWSpinlock);
535 
536 	return wasHandled ? B_INVOKE_SCHEDULER : B_UNHANDLED_INTERRUPT;
537 }
538 
539 
540 void
541 Device::SignalReadyBuffers()
542 {
543 	release_sem_etc(fBuffersReadySem, 1, B_DO_NOT_RESCHEDULE);
544 }
545 
546 
547 status_t
548 Device::_ReserveDeviceOnBus(bool reserve)
549 {
550 	status_t result = B_NO_INIT;
551 	if (reserve) {
552 		result = gPCI->reserve_device(fPCIInfo.bus, fPCIInfo.device,
553 							fPCIInfo.function, DRIVER_NAME, this);
554 		if (result != B_OK)
555 			ERROR("Unable to reserve PCI device %d:%d:%d on bus:%#010x\n",
556 					fPCIInfo.bus, fPCIInfo.device, fPCIInfo.function, result);
557 	} else {
558 		result = gPCI->unreserve_device(fPCIInfo.bus, fPCIInfo.device,
559 							fPCIInfo.function, DRIVER_NAME, this);
560 	}
561 
562 	return result;
563 }
564 
565 
566 uint8
567 Device::ReadPCI8(int offset)
568 {
569 	return gPCI->read_io_8(fIOBase + offset);
570 }
571 
572 
573 uint16
574 Device::ReadPCI16(int offset)
575 {
576 	return gPCI->read_io_16(fIOBase + offset);
577 }
578 
579 
580 uint32
581 Device::ReadPCI32(int offset)
582 {
583 	return gPCI->read_io_32(fIOBase + offset);
584 }
585 
586 
587 void
588 Device::WritePCI8(int offset, uint8 value)
589 {
590 	gPCI->write_io_8(fIOBase + offset, value);
591 }
592 
593 
594 void
595 Device::WritePCI16(int offset, uint16 value)
596 {
597 	gPCI->write_io_16(fIOBase + offset, value);
598 }
599 
600 
601 void
602 Device::WritePCI32(int offset, uint32 value)
603 {
604 	gPCI->write_io_32(fIOBase + offset, value);
605 }
606 
607 
608 cpu_status
609 Device::Lock()
610 {
611 	cpu_status st = disable_interrupts();
612 	acquire_spinlock(&fHWSpinlock);
613 	return st;
614 }
615 
616 
617 void
618 Device::Unlock(cpu_status st)
619 {
620 	release_spinlock(&fHWSpinlock);
621 	restore_interrupts(st);
622 }
623 
624 
625