xref: /haiku/src/add-ons/kernel/drivers/audio/ac97/geode/geode_controller.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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 %ld, num channels %ld, buffer length %ld ****************\n",
301 		stream->sample_size, stream->num_channels, stream->buffer_length);
302 
303 	/* Calculate total size of all buffers (aligned to size of B_PAGE_SIZE) */
304 	alloc = bufferSize * stream->num_buffers;
305 	alloc = PAGE_ALIGN(alloc);
306 
307 	/* Allocate memory for buffers */
308 	stream->buffer_area = create_area("geode buffers", (void**)&buffer,
309 		B_ANY_KERNEL_ADDRESS, alloc, B_32_BIT_CONTIGUOUS,
310 		B_READ_AREA | B_WRITE_AREA);
311 		// TODO: The rest of the code doesn't deal correctly with physical
312 		// addresses > 4 GB, so we have to force 32 bit addresses here.
313 	if (stream->buffer_area < B_OK)
314 		return stream->buffer_area;
315 
316 	/* Get the physical address of memory */
317 	rc = get_memory_map(buffer, alloc, &pe, 1);
318 	if (rc != B_OK) {
319 		delete_area(stream->buffer_area);
320 		return rc;
321 	}
322 
323 	phys_addr_t bufferPhysicalAddress = pe.address;
324 
325 	dprintf("%s(%s): Allocated %lu bytes for %ld buffers\n", __func__, desc,
326 		alloc, stream->num_buffers);
327 
328 	/* Store pointers (both virtual/physical) */
329 	for (index = 0; index < stream->num_buffers; index++) {
330 		stream->buffers[index] = buffer + (index * bufferSize);
331 		stream->physical_buffers[index] = bufferPhysicalAddress
332 			+ (index * bufferSize);
333 	}
334 
335 	/* Now allocate BDL for buffer range */
336 	alloc = stream->num_buffers * sizeof(struct acc_prd) + 1;
337 	alloc = PAGE_ALIGN(alloc);
338 
339 	stream->buffer_descriptors_area = create_area("geode buffer descriptors",
340 		(void**)&bufferDescriptors, B_ANY_KERNEL_ADDRESS, alloc,
341 		B_32_BIT_CONTIGUOUS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA);
342 		// TODO: The rest of the code doesn't deal correctly with physical
343 		// addresses > 4 GB, so we have to force 32 bit addresses here.
344 	if (stream->buffer_descriptors_area < B_OK) {
345 		delete_area(stream->buffer_area);
346 		return stream->buffer_descriptors_area;
347 	}
348 
349 	/* Get the physical address of memory */
350 	rc = get_memory_map(bufferDescriptors, alloc, &pe, 1);
351 	if (rc != B_OK) {
352 		delete_area(stream->buffer_area);
353 		delete_area(stream->buffer_descriptors_area);
354 		return rc;
355 	}
356 
357 	stream->physical_buffer_descriptors = pe.address;
358 
359 	dprintf("%s(%s): Allocated %ld bytes for %ld BDLEs\n", __func__, desc,
360 		alloc, stream->num_buffers);
361 
362 	/* Setup buffer descriptor list (BDL) entries */
363 	for (index = 0; index < stream->num_buffers; index++, bufferDescriptors++) {
364 		bufferDescriptors->address = stream->physical_buffers[index];
365 		bufferDescriptors->ctrlsize = bufferSize | ACC_BMx_PRD_CTRL_EOP;
366 			// we want an interrupt after every buffer
367 	}
368 	bufferDescriptors->address = stream->physical_buffer_descriptors;
369 	bufferDescriptors->ctrlsize = ACC_BMx_PRD_CTRL_JMP;
370 
371 	for (index = 0; index < sizeof(kRates) / sizeof(kRates[0]); index++) {
372 		if (kRates[index].multi_rate == stream->sample_rate) {
373 			stream->rate = kRates[index].rate;
374 			break;
375 		}
376 	}
377 
378 	/* Configure stream registers */
379 	dprintf("IRA: %s: setup stream SR=%ld\n", __func__, stream->rate);
380 
381 	stream->Write32(STREAM_PRD, stream->physical_buffer_descriptors);
382 
383 	ac97_set_rate(stream->controller->ac97, stream->ac97_rate_reg, stream->rate);
384 	snooze(1000);
385 	return B_OK;
386 }
387 
388 
389 //	#pragma mark - public controller functions
390 
391 
392 /*! Setup hardware for use; detect codecs; etc */
393 status_t
394 geode_hw_init(geode_controller* controller)
395 {
396 	uint16 cmd;
397 	status_t status;
398 
399 	cmd = (gPci->read_pci_config)(controller->pci_info.bus,
400 		controller->pci_info.device, controller->pci_info.function, PCI_command, 2);
401 	if (!(cmd & PCI_command_master)) {
402 		(gPci->write_pci_config)(controller->pci_info.bus,
403 			controller->pci_info.device, controller->pci_info.function,
404 				PCI_command, 2, cmd | PCI_command_master);
405 		dprintf("geode: enabling PCI bus mastering\n");
406 	}
407 
408 	controller->nabmbar = controller->pci_info.u.h0.base_registers[0];
409 
410 	/* Absolute minimum hw is online; we can now install interrupt handler */
411 	controller->irq = controller->pci_info.u.h0.interrupt_line;
412 	status = install_io_interrupt_handler(controller->irq,
413 		(interrupt_handler)geode_interrupt_handler, controller, 0);
414 	if (status != B_OK)
415 		goto error;
416 
417 	/* Get controller into valid state */
418 	status = reset_controller(controller);
419 	if (status != B_OK) {
420 		dprintf("geode: reset_controller failed\n");
421 		goto reset_failed;
422 	}
423 
424 	/* attach the codec */
425 	ac97_attach(&controller->ac97, (codec_reg_read)geode_codec_read,
426 		(codec_reg_write)geode_codec_write, controller,
427 		controller->pci_info.u.h0.subsystem_vendor_id,
428 		controller->pci_info.u.h0.subsystem_id);
429 
430 	snooze(1000);
431 
432 	controller->multi = (geode_multi*)calloc(1, sizeof(geode_multi));
433         if (controller->multi == NULL)
434                 return B_NO_MEMORY;
435 
436 	controller->playback_stream = geode_stream_new(controller, STREAM_PLAYBACK);
437         controller->record_stream = geode_stream_new(controller, STREAM_RECORD);
438 
439 	return B_OK;
440 
441 reset_failed:
442 	remove_io_interrupt_handler(controller->irq,
443 		(interrupt_handler)geode_interrupt_handler, controller);
444 error:
445 	dprintf("geode: ERROR: %s(%ld)\n", strerror(status), status);
446 
447 	return status;
448 }
449 
450 
451 /*! Stop any activity */
452 void
453 geode_hw_stop(geode_controller* controller)
454 {
455 	int index;
456 
457 	/* Stop all audio streams */
458 	for (index = 0; index < GEODE_MAX_STREAMS; index++) {
459 		if (controller->streams[index] && controller->streams[index]->running)
460 			geode_stream_stop(controller->streams[index]);
461 	}
462 }
463 
464 
465 /*! Free resources */
466 void
467 geode_hw_uninit(geode_controller* controller)
468 {
469 	if (controller == NULL)
470 		return;
471 
472 	/* Stop all audio streams */
473 	geode_hw_stop(controller);
474 
475 	reset_controller(controller);
476 
477 	remove_io_interrupt_handler(controller->irq,
478 		(interrupt_handler)geode_interrupt_handler, controller);
479 
480 	free(controller->multi);
481 
482 	geode_stream_delete(controller->playback_stream);
483         geode_stream_delete(controller->record_stream);
484 
485 }
486 
487