xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/geode/geode_controller.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 /*
2  * Copyright 2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *	Jérôme Duval (korli@users.berlios.de)
7  */
8 
9 
10 #include "driver.h"
11 
12 #define ALIGN(size, align)		(((size) + align - 1) & ~(align - 1))
13 #define PAGE_ALIGN(size)	(((size) + B_PAGE_SIZE - 1) & ~(B_PAGE_SIZE - 1))
14 
15 #define STREAM_CMD			0x0		/* Command */
16 #define STREAM_STATUS			0x1		/* IRQ Status */
17 #define STREAM_PRD			0x4		/* PRD Table Address */
18 
19 static const struct {
20 	uint32 multi_rate;
21 	uint32 rate;
22 } kRates[] = {
23 	{B_SR_8000, 8000},
24 	{B_SR_11025, 11025},
25 	{B_SR_16000, 16000},
26 	{B_SR_22050, 22050},
27 	{B_SR_32000, 32000},
28 	{B_SR_44100, 44100},
29 	{B_SR_48000, 48000},
30 	{B_SR_88200, 88200},
31 	{B_SR_96000, 96000},
32 	{B_SR_176400, 176400},
33 	{B_SR_192000, 192000},
34 };
35 
36 static int
37 geode_codec_wait(geode_controller *controller)
38 {
39 	int i;
40 
41 #define GCSCAUDIO_WAIT_READY_CODEC_TIMEOUT	500
42 	for (i = GCSCAUDIO_WAIT_READY_CODEC_TIMEOUT; (i >= 0)
43 	    && (controller->Read32(ACC_CODEC_CNTL) & ACC_CODEC_CNTL_CMD_NEW); i--)
44 		snooze(10);
45 
46 	return (i < 0);
47 }
48 
49 uint16
50 geode_codec_read(geode_controller *controller, uint8 regno)
51 {
52 	int i;
53 	uint32 v;
54 	ASSERT(regno >= 0);
55 
56 	controller->Write32(ACC_CODEC_CNTL,
57 		ACC_CODEC_CNTL_READ_CMD | ACC_CODEC_CNTL_CMD_NEW |
58 		ACC_CODEC_REG2ADDR(regno));
59 
60 	if (geode_codec_wait(controller) != B_OK) {
61 		dprintf("codec busy (2)\n");
62 		return 0xffff;
63 	}
64 
65 #define GCSCAUDIO_READ_CODEC_TIMEOUT	50
66 	for (i = GCSCAUDIO_READ_CODEC_TIMEOUT; i >= 0; i--) {
67 		v = controller->Read32(ACC_CODEC_STATUS);
68 		if ((v & ACC_CODEC_STATUS_STS_NEW) &&
69 		    (ACC_CODEC_ADDR2REG(v) == regno))
70 			break;
71 
72 		snooze(10);
73 	}
74 
75 	if (i < 0) {
76 		dprintf("codec busy (3)\n");
77 		return 0xffff;
78 	}
79 
80 	return v;
81 }
82 
83 void
84 geode_codec_write(geode_controller *controller, uint8 regno, uint16 value)
85 {
86 	ASSERT(regno >= 0);
87 
88 	controller->Write32(ACC_CODEC_CNTL,
89 	    ACC_CODEC_CNTL_WRITE_CMD |
90 	    ACC_CODEC_CNTL_CMD_NEW |
91 	    ACC_CODEC_REG2ADDR(regno) |
92 	    (value & ACC_CODEC_CNTL_CMD_DATA_MASK));
93 
94 	if (geode_codec_wait(controller) != B_OK) {
95 		dprintf("codec busy (4)\n");
96 	}
97 }
98 
99 
100 //! Called with interrupts off
101 static void
102 stream_handle_interrupt(geode_controller* controller, geode_stream* stream)
103 {
104 	uint8 status;
105 	uint32 position, bufferSize;
106 
107 	if (!stream->running)
108 		return;
109 
110 	status = stream->Read8(STREAM_STATUS);
111 
112 	if (status & ACC_BMx_STATUS_BM_EOP_ERR) {
113 		dprintf("geode: stream status bus master error\n");
114 	}
115 	if (status & ACC_BMx_STATUS_EOP) {
116 		dprintf("geode: stream status end of page\n");
117 	}
118 
119 	position = controller->Read32(ACC_BM0_PNTR + stream->dma_offset);
120 	bufferSize = ALIGN(stream->sample_size * stream->num_channels * stream->buffer_length, 128);
121 
122 	// Buffer Completed Interrupt
123 	acquire_spinlock(&stream->lock);
124 
125 	stream->real_time = system_time();
126 	stream->frames_count += stream->buffer_length;
127 	stream->buffer_cycle = 1 - (position / (bufferSize + 1)); // added 1 to avoid having 2
128 
129 	release_spinlock(&stream->lock);
130 
131 	release_sem_etc(stream->buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
132 }
133 
134 
135 static int32
136 geode_interrupt_handler(geode_controller* controller)
137 {
138 	uint16 intr = controller->Read16(ACC_IRQ_STATUS);
139 	if (intr == 0)
140 		return B_UNHANDLED_INTERRUPT;
141 
142 	for (uint32 index = 0; index < GEODE_MAX_STREAMS; index++) {
143 		if (controller->streams[index]
144 			&& (intr & controller->streams[index]->status) != 0) {
145 			stream_handle_interrupt(controller,
146 				controller->streams[index]);
147 		}
148 	}
149 
150 	return B_HANDLED_INTERRUPT;
151 }
152 
153 
154 static status_t
155 reset_controller(geode_controller* controller)
156 {
157 	controller->Write32(ACC_CODEC_CNTL, ACC_CODEC_CNTL_LNK_WRM_RST
158 	    | ACC_CODEC_CNTL_CMD_NEW);
159 
160 	if (geode_codec_wait(controller) != B_OK) {
161 		dprintf("codec reset busy (1)\n");
162 	}
163 
164 	// stop streams
165 
166 	// stop DMA
167 
168 	// reset DMA position buffer
169 
170 	return B_OK;
171 }
172 
173 //	#pragma mark - public stream functions
174 
175 
176 void
177 geode_stream_delete(geode_stream* stream)
178 {
179 	if (stream->buffer_ready_sem >= B_OK)
180 		delete_sem(stream->buffer_ready_sem);
181 
182 	if (stream->buffer_area >= B_OK)
183 		delete_area(stream->buffer_area);
184 
185 	if (stream->buffer_descriptors_area >= B_OK)
186 		delete_area(stream->buffer_descriptors_area);
187 
188 	free(stream);
189 }
190 
191 
192 geode_stream*
193 geode_stream_new(geode_controller* controller, int type)
194 {
195 	geode_stream* stream = (geode_stream*)calloc(1, sizeof(geode_stream));
196 	if (stream == NULL)
197 		return NULL;
198 
199 	stream->buffer_ready_sem = B_ERROR;
200 	stream->buffer_area = B_ERROR;
201 	stream->buffer_descriptors_area = B_ERROR;
202 	stream->type = type;
203 	stream->controller = controller;
204 
205 	switch (type) {
206 		case STREAM_PLAYBACK:
207 			stream->status = ACC_IRQ_STATUS_BM0_IRQ_STS;
208 			stream->offset = 0;
209 			stream->dma_offset = 0;
210 			stream->ac97_rate_reg = AC97_PCM_FRONT_DAC_RATE;
211 			break;
212 
213 		case STREAM_RECORD:
214 			stream->status = ACC_IRQ_STATUS_BM1_IRQ_STS;
215 			stream->offset = 0x8;
216 			stream->dma_offset = 0x4;
217 			stream->ac97_rate_reg = AC97_PCM_L_R_ADC_RATE;
218 			break;
219 
220 		default:
221 			dprintf("%s: Unknown stream type %d!\n", __func__, type);
222 			free(stream);
223 			stream = NULL;
224 	}
225 
226 	controller->streams[controller->num_streams++] = stream;
227 	return stream;
228 }
229 
230 
231 /*!	Starts a stream's DMA engine, and enables generating and receiving
232 	interrupts for this stream.
233 */
234 status_t
235 geode_stream_start(geode_stream* stream)
236 {
237 	uint8 value;
238 	dprintf("geode_stream_start()\n");
239 	stream->buffer_ready_sem = create_sem(0, stream->type == STREAM_PLAYBACK
240 		? "geode_playback_sem" : "geode_record_sem");
241 	if (stream->buffer_ready_sem < B_OK)
242 		return stream->buffer_ready_sem;
243 
244 	if (stream->type == STREAM_PLAYBACK)
245 		value = ACC_BMx_CMD_WRITE;
246 	else
247 		value = ACC_BMx_CMD_READ;
248 
249 	stream->Write8(STREAM_CMD, value | ACC_BMx_CMD_BYTE_ORD_EL
250 		| ACC_BMx_CMD_BM_CTL_ENABLE);
251 
252 	stream->running = true;
253 	return B_OK;
254 }
255 
256 
257 /*!	Stops the stream's DMA engine, and turns off interrupts for this
258 	stream.
259 */
260 status_t
261 geode_stream_stop(geode_stream* stream)
262 {
263 	dprintf("geode_stream_stop()\n");
264 	stream->Write8(STREAM_CMD, ACC_BMx_CMD_BM_CTL_DISABLE);
265 
266 	stream->running = false;
267 	delete_sem(stream->buffer_ready_sem);
268 	stream->buffer_ready_sem = -1;
269 
270 	return B_OK;
271 }
272 
273 
274 status_t
275 geode_stream_setup_buffers(geode_stream* stream, const char* desc)
276 {
277 	uint32 bufferSize, alloc;
278 	uint32 index;
279 	physical_entry pe;
280 	struct acc_prd* bufferDescriptors;
281 	uint8* buffer;
282 	status_t rc;
283 
284 	/* Clear previously allocated memory */
285 	if (stream->buffer_area >= B_OK) {
286 		delete_area(stream->buffer_area);
287 		stream->buffer_area = B_ERROR;
288 	}
289 
290 	if (stream->buffer_descriptors_area >= B_OK) {
291 		delete_area(stream->buffer_descriptors_area);
292 		stream->buffer_descriptors_area = B_ERROR;
293 	}
294 
295 	/* Calculate size of buffer (aligned to 128 bytes) */
296 	bufferSize = stream->sample_size * stream->num_channels
297 		* stream->buffer_length;
298 	bufferSize = ALIGN(bufferSize, 128);
299 
300 	dprintf("geode: sample size %" B_PRIu32 ", num channels %" B_PRIu32 ", buffer length %"
301 			B_PRIu32 "\n",
302 		stream->sample_size, stream->num_channels, stream->buffer_length);
303 
304 	/* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */
305 	alloc = bufferSize * stream->num_buffers;
306 	alloc = PAGE_ALIGN(alloc);
307 
308 	/* Allocate memory for buffers */
309 	stream->buffer_area = create_area("geode buffers", (void**)&buffer,
310 		B_ANY_KERNEL_ADDRESS, alloc, B_32_BIT_CONTIGUOUS,
311 		B_READ_AREA | B_WRITE_AREA);
312 		// TODO: The rest of the code doesn't deal correctly with physical
313 		// addresses > 4 GB, so we have to force 32 bit addresses here.
314 	if (stream->buffer_area < B_OK)
315 		return stream->buffer_area;
316 
317 	/* Get the physical address of memory */
318 	rc = get_memory_map(buffer, alloc, &pe, 1);
319 	if (rc != B_OK) {
320 		delete_area(stream->buffer_area);
321 		return rc;
322 	}
323 
324 	phys_addr_t bufferPhysicalAddress = pe.address;
325 
326 	dprintf("%s(%s): Allocated %" B_PRId32 " bytes for %" B_PRIu32 " buffers\n", __func__, desc,
327 		alloc, stream->num_buffers);
328 
329 	/* Store pointers (both virtual/physical) */
330 	for (index = 0; index < stream->num_buffers; index++) {
331 		stream->buffers[index] = buffer + (index * bufferSize);
332 		stream->physical_buffers[index] = bufferPhysicalAddress
333 			+ (index * bufferSize);
334 	}
335 
336 	/* Now allocate BDL for buffer range */
337 	alloc = stream->num_buffers * sizeof(struct acc_prd) + 1;
338 	alloc = PAGE_ALIGN(alloc);
339 
340 	stream->buffer_descriptors_area = create_area("geode buffer descriptors",
341 		(void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
342 		B_32_BIT_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
343 		// TODO: The rest of the code doesn't deal correctly with physical
344 		// addresses > 4 GB, so we have to force 32 bit addresses here.
345 	if (stream->buffer_descriptors_area < B_OK) {
346 		delete_area(stream->buffer_area);
347 		return stream->buffer_descriptors_area;
348 	}
349 
350 	/* Get the physical address of memory */
351 	rc = get_memory_map(bufferDescriptors, alloc, &pe, 1);
352 	if (rc != B_OK) {
353 		delete_area(stream->buffer_area);
354 		delete_area(stream->buffer_descriptors_area);
355 		return rc;
356 	}
357 
358 	stream->physical_buffer_descriptors = pe.address;
359 
360 	dprintf("%s(%s): Allocated %" B_PRIu32 " bytes for %" B_PRIu32 " BDLEs\n", __func__, desc,
361 		alloc, stream->num_buffers);
362 
363 	/* Setup buffer descriptor list (BDL) entries */
364 	for (index = 0; index < stream->num_buffers; index++, bufferDescriptors++) {
365 		bufferDescriptors->address = stream->physical_buffers[index];
366 		bufferDescriptors->ctrlsize = bufferSize | ACC_BMx_PRD_CTRL_EOP;
367 			// we want an interrupt after every buffer
368 	}
369 	bufferDescriptors->address = stream->physical_buffer_descriptors;
370 	bufferDescriptors->ctrlsize = ACC_BMx_PRD_CTRL_JMP;
371 
372 	for (index = 0; index < sizeof(kRates) / sizeof(kRates[0]); index++) {
373 		if (kRates[index].multi_rate == stream->sample_rate) {
374 			stream->rate = kRates[index].rate;
375 			break;
376 		}
377 	}
378 
379 	/* Configure stream registers */
380 	dprintf("IRA: %s: setup stream SR=%" B_PRIu32 "\n", __func__, stream->rate);
381 
382 	stream->Write32(STREAM_PRD, stream->physical_buffer_descriptors);
383 
384 	ac97_set_rate(stream->controller->ac97, stream->ac97_rate_reg, stream->rate);
385 	snooze(1000);
386 	return B_OK;
387 }
388 
389 
390 //	#pragma mark - public controller functions
391 
392 
393 /*! Setup hardware for use; detect codecs; etc */
394 status_t
395 geode_hw_init(geode_controller* controller)
396 {
397 	uint16 cmd;
398 	status_t status;
399 
400 	cmd = (gPci->read_pci_config)(controller->pci_info.bus,
401 		controller->pci_info.device, controller->pci_info.function, PCI_command, 2);
402 	if (!(cmd & PCI_command_master)) {
403 		(gPci->write_pci_config)(controller->pci_info.bus,
404 			controller->pci_info.device, controller->pci_info.function,
405 				PCI_command, 2, cmd | PCI_command_master);
406 		dprintf("geode: enabling PCI bus mastering\n");
407 	}
408 
409 	controller->nabmbar = controller->pci_info.u.h0.base_registers[0];
410 
411 	/* Absolute minimum hw is online; we can now install interrupt handler */
412 	controller->irq = controller->pci_info.u.h0.interrupt_line;
413 	status = install_io_interrupt_handler(controller->irq,
414 		(interrupt_handler)geode_interrupt_handler, controller, 0);
415 	if (status != B_OK)
416 		goto error;
417 
418 	/* Get controller into valid state */
419 	status = reset_controller(controller);
420 	if (status != B_OK) {
421 		dprintf("geode: reset_controller failed\n");
422 		goto reset_failed;
423 	}
424 
425 	/* attach the codec */
426 	ac97_attach(&controller->ac97, (codec_reg_read)geode_codec_read,
427 		(codec_reg_write)geode_codec_write, controller,
428 		controller->pci_info.u.h0.subsystem_vendor_id,
429 		controller->pci_info.u.h0.subsystem_id);
430 
431 	snooze(1000);
432 
433 	controller->multi = (geode_multi*)calloc(1, sizeof(geode_multi));
434         if (controller->multi == NULL)
435                 return B_NO_MEMORY;
436 
437 	controller->playback_stream = geode_stream_new(controller, STREAM_PLAYBACK);
438         controller->record_stream = geode_stream_new(controller, STREAM_RECORD);
439 
440 	return B_OK;
441 
442 reset_failed:
443 	remove_io_interrupt_handler(controller->irq,
444 		(interrupt_handler)geode_interrupt_handler, controller);
445 error:
446 	dprintf("geode: ERROR: %s(%" B_PRId32 ")\n", strerror(status), status);
447 
448 	return status;
449 }
450 
451 
452 /*! Stop any activity */
453 void
454 geode_hw_stop(geode_controller* controller)
455 {
456 	int index;
457 
458 	/* Stop all audio streams */
459 	for (index = 0; index < GEODE_MAX_STREAMS; index++) {
460 		if (controller->streams[index] && controller->streams[index]->running)
461 			geode_stream_stop(controller->streams[index]);
462 	}
463 }
464 
465 
466 /*! Free resources */
467 void
468 geode_hw_uninit(geode_controller* controller)
469 {
470 	if (controller == NULL)
471 		return;
472 
473 	/* Stop all audio streams */
474 	geode_hw_stop(controller);
475 
476 	reset_controller(controller);
477 
478 	remove_io_interrupt_handler(controller->irq,
479 		(interrupt_handler)geode_interrupt_handler, controller);
480 
481 	free(controller->multi);
482 
483 	geode_stream_delete(controller->playback_stream);
484         geode_stream_delete(controller->record_stream);
485 
486 }
487 
488