xref: /haiku/src/add-ons/kernel/drivers/audio/hda/hda_multi_audio.cpp (revision b06a48ab8f30b45916a9c157b992827779182163)
1 /*
2  * Copyright 2007-2008, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Ithamar Adema, ithamar AT unet DOT nl
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include "multi_audio.h"
12 #include "driver.h"
13 
14 
15 #ifdef TRACE
16 #	undef TRACE
17 #endif
18 
19 #ifdef TRACE_MULTI_AUDIO
20 #	define TRACE(a...) dprintf("\33[34mhda:\33[0m " a)
21 #else
22 #	define TRACE(a...) ;
23 #endif
24 
25 
26 static multi_channel_info sChannels[] = {
27 	{  0, B_MULTI_OUTPUT_CHANNEL,	B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
28 	{  1, B_MULTI_OUTPUT_CHANNEL,	B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
29 	{  2, B_MULTI_INPUT_CHANNEL,	B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
30 	{  3, B_MULTI_INPUT_CHANNEL,	B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
31 	{  4, B_MULTI_OUTPUT_BUS,		B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
32 			B_CHANNEL_MINI_JACK_STEREO },
33 	{  5, B_MULTI_OUTPUT_BUS,		B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
34 			B_CHANNEL_MINI_JACK_STEREO },
35 	{  6, B_MULTI_INPUT_BUS,		B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS,
36 			B_CHANNEL_MINI_JACK_STEREO },
37 	{  7, B_MULTI_INPUT_BUS,		B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS,
38 			B_CHANNEL_MINI_JACK_STEREO },
39 };
40 
41 
42 static int32
43 format2size(uint32 format)
44 {
45 	switch (format) {
46 		case B_FMT_8BIT_S:
47 		case B_FMT_16BIT:
48 			return 2;
49 
50 		case B_FMT_18BIT:
51 		case B_FMT_24BIT:
52 		case B_FMT_32BIT:
53 		case B_FMT_FLOAT:
54 			return 4;
55 
56 		default:
57 			return -1;
58 	}
59 }
60 
61 
62 static status_t
63 get_description(hda_audio_group* audioGroup, multi_description* data)
64 {
65 	data->interface_version = B_CURRENT_INTERFACE_VERSION;
66 	data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
67 
68 	strcpy(data->friendly_name, "HD Audio");
69 	strcpy(data->vendor_info, "Haiku");
70 
71 	int32 inChannels = 0;
72 	if (audioGroup->record_stream != NULL)
73 		inChannels = 2;
74 
75 	int32 outChannels = 0;
76 	if (audioGroup->playback_stream != NULL)
77 		outChannels = 2;
78 
79 	data->output_channel_count = outChannels;
80 	data->output_bus_channel_count = outChannels;
81 	data->input_channel_count = inChannels;
82 	data->input_bus_channel_count = inChannels;
83 	data->aux_bus_channel_count = 0;
84 
85 	dprintf("%s: request_channel_count: %ld\n", __func__,
86 		data->request_channel_count);
87 
88 	if (data->request_channel_count >= (int)(sizeof(sChannels)
89 			/ sizeof(sChannels[0]))) {
90 		memcpy(data->channels, &sChannels, sizeof(sChannels));
91 	}
92 
93 	/* determine output/input rates */
94 	data->output_rates = audioGroup->supported_rates;
95 	data->input_rates = audioGroup->supported_rates;
96 
97 	/* force existance of 48kHz if variable rates are not supported */
98 	if (data->output_rates == 0)
99 		data->output_rates = B_SR_48000;
100 	if (data->input_rates == 0)
101 		data->input_rates = B_SR_48000;
102 
103 	data->max_cvsr_rate = 0;
104 	data->min_cvsr_rate = 0;
105 
106 	data->output_formats = audioGroup->supported_formats;
107 	data->input_formats = audioGroup->supported_formats;
108 	data->lock_sources = B_MULTI_LOCK_INTERNAL;
109 	data->timecode_sources = 0;
110 	data->interface_flags = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD;
111 	data->start_latency = 30000;
112 
113 	strcpy(data->control_panel, "");
114 
115 	return B_OK;
116 }
117 
118 
119 static status_t
120 get_enabled_channels(hda_audio_group* audioGroup, multi_channel_enable* data)
121 {
122 	B_SET_CHANNEL(data->enable_bits, 0, true);
123 	B_SET_CHANNEL(data->enable_bits, 1, true);
124 	B_SET_CHANNEL(data->enable_bits, 2, true);
125 	B_SET_CHANNEL(data->enable_bits, 3, true);
126 	data->lock_source = B_MULTI_LOCK_INTERNAL;
127 
128 	return B_OK;
129 }
130 
131 
132 static status_t
133 get_global_format(hda_audio_group* audioGroup, multi_format_info* data)
134 {
135 	data->output_latency = 0;
136 	data->input_latency = 0;
137 	data->timecode_kind = 0;
138 
139 	if (audioGroup->playback_stream != NULL) {
140 		data->output.format = audioGroup->playback_stream->sample_format;
141 		data->output.rate = audioGroup->playback_stream->sample_rate;
142 	} else {
143 		data->output.format = 0;
144 		data->output.rate = 0;
145 	}
146 
147 	if (audioGroup->record_stream != NULL) {
148 		data->input.format = audioGroup->record_stream->sample_format;
149 		data->input.rate = audioGroup->record_stream->sample_format;
150 	} else {
151 		data->input.format = 0;
152 		data->input.rate = 0;
153 	}
154 
155 	return B_OK;
156 }
157 
158 
159 static status_t
160 set_global_format(hda_audio_group* audioGroup, multi_format_info* data)
161 {
162 	// TODO: it looks like we're not supposed to fail; fix this!
163 #if 0
164 	if ((data->output.format & audioGroup->supported_formats) == 0)
165 		|| (data->output.rate & audioGroup->supported_rates) == 0)
166 		return B_BAD_VALUE;
167 #endif
168 
169 	if (audioGroup->playback_stream != NULL) {
170 		audioGroup->playback_stream->sample_format = data->output.format;
171 		audioGroup->playback_stream->sample_rate = data->output.rate;
172 		audioGroup->playback_stream->sample_size = format2size(
173 			audioGroup->playback_stream->sample_format);
174 	}
175 
176 	if (audioGroup->record_stream != NULL) {
177 		audioGroup->record_stream->sample_rate = data->input.rate;
178 		audioGroup->record_stream->sample_format = data->input.format;
179 		audioGroup->record_stream->sample_size = format2size(
180 			audioGroup->record_stream->sample_format);
181 	}
182 
183 	return B_OK;
184 }
185 
186 
187 static status_t
188 list_mix_controls(hda_audio_group* audioGroup, multi_mix_control_info* data)
189 {
190 	data->control_count = 0;
191 	return B_OK;
192 }
193 
194 
195 static status_t
196 list_mix_connections(hda_audio_group* audioGroup,
197 	multi_mix_connection_info* data)
198 {
199 	data->actual_count = 0;
200 	return B_OK;
201 }
202 
203 
204 static status_t
205 list_mix_channels(hda_audio_group* audioGroup, multi_mix_channel_info *data)
206 {
207 	return B_OK;
208 }
209 
210 
211 static status_t
212 get_buffers(hda_audio_group* audioGroup, multi_buffer_list* data)
213 {
214 	TRACE("playback: %ld buffers, %ld channels, %ld samples\n",
215 		data->request_playback_buffers, data->request_playback_channels,
216 		data->request_playback_buffer_size);
217 	TRACE("record: %ld buffers, %ld channels, %ld samples\n",
218 		data->request_record_buffers, data->request_record_channels,
219 		data->request_record_buffer_size);
220 
221 	/* Determine what buffers we return given the request */
222 
223 	data->return_playback_buffers = data->request_playback_buffers;
224 	data->return_playback_channels = data->request_playback_channels;
225 	data->return_playback_buffer_size = data->request_playback_buffer_size;
226 	data->return_record_buffers = data->request_record_buffers;
227 	data->return_record_channels = data->request_record_channels;
228 	data->return_record_buffer_size = data->request_record_buffer_size;
229 
230 	/* Workaround for Haiku multi_audio API, since it prefers to let the
231 	   driver pick values, while the BeOS multi_audio actually gives the
232 	   user's defaults. */
233 	if (data->return_playback_buffers > STREAM_MAX_BUFFERS
234 		|| data->return_playback_buffers < STREAM_MIN_BUFFERS)
235 		data->return_playback_buffers = STREAM_MIN_BUFFERS;
236 
237 	if (data->return_record_buffers > STREAM_MAX_BUFFERS
238 		|| data->return_record_buffers < STREAM_MIN_BUFFERS)
239 		data->return_record_buffers = STREAM_MIN_BUFFERS;
240 
241 	if (data->return_playback_buffer_size == 0)
242 		data->return_playback_buffer_size = DEFAULT_FRAMES_PER_BUFFER;
243 
244 	if (data->return_record_buffer_size == 0)
245 		data->return_record_buffer_size = DEFAULT_FRAMES_PER_BUFFER;
246 
247 	/* ... from here on, we can assume again that a reasonable request is
248 	   being made */
249 
250 	data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD;
251 
252 	/* Copy the settings into the streams */
253 
254 	if (audioGroup->playback_stream != NULL) {
255 		audioGroup->playback_stream->num_buffers = data->return_playback_buffers;
256 		audioGroup->playback_stream->num_channels = data->return_playback_channels;
257 		audioGroup->playback_stream->buffer_length
258 			= data->return_playback_buffer_size;
259 
260 		status_t status = hda_stream_setup_buffers(audioGroup,
261 			audioGroup->playback_stream, "Playback");
262 		if (status != B_OK) {
263 			dprintf("hda: Error setting up playback buffers: %s\n",
264 				strerror(status));
265 			return status;
266 		}
267 	}
268 
269 	if (audioGroup->record_stream != NULL) {
270 		audioGroup->record_stream->num_buffers = data->return_record_buffers;
271 		audioGroup->record_stream->num_channels = data->return_record_channels;
272 		audioGroup->record_stream->buffer_length
273 			= data->return_record_buffer_size;
274 
275 		status_t status = hda_stream_setup_buffers(audioGroup,
276 			audioGroup->record_stream, "Recording");
277 		if (status != B_OK) {
278 			dprintf("hda: Error setting up recording buffers: %s\n",
279 				strerror(status));
280 			return status;
281 		}
282 	}
283 
284 	/* Setup data structure for multi_audio API... */
285 
286 	if (audioGroup->playback_stream != NULL) {
287 		uint32 playbackSampleSize = audioGroup->playback_stream->sample_size;
288 
289 		for (int32 i = 0; i < data->return_playback_buffers; i++) {
290 			for (int32 channelIndex = 0;
291 					channelIndex < data->return_playback_channels; channelIndex++) {
292 				data->playback_buffers[i][channelIndex].base
293 					= (char*)audioGroup->playback_stream->buffers[i]
294 						+ playbackSampleSize * channelIndex;
295 				data->playback_buffers[i][channelIndex].stride
296 					= playbackSampleSize * data->return_playback_channels;
297 			}
298 		}
299 	}
300 
301 	if (audioGroup->record_stream != NULL) {
302 		uint32 recordSampleSize = audioGroup->record_stream->sample_size;
303 
304 		for (int32 i = 0; i < data->return_record_buffers; i++) {
305 			for (int32 channelIndex = 0;
306 					channelIndex < data->return_record_channels; channelIndex++) {
307 				data->record_buffers[i][channelIndex].base
308 					= (char*)audioGroup->record_stream->buffers[i]
309 						+ recordSampleSize * channelIndex;
310 				data->record_buffers[i][channelIndex].stride
311 					= recordSampleSize * data->return_record_channels;
312 			}
313 		}
314 	}
315 
316 	return B_OK;
317 }
318 
319 
320 /*! playback_buffer_cycle is the buffer we want to have played */
321 static status_t
322 buffer_exchange(hda_audio_group* audioGroup, multi_buffer_info* data)
323 {
324 	static int debug_buffers_exchanged = 0;
325 	cpu_status status;
326 	status_t err;
327 
328 	// TODO: support recording!
329 	if (audioGroup->playback_stream == NULL)
330 		return B_ERROR;
331 
332 	if (!audioGroup->playback_stream->running) {
333 		hda_stream_start(audioGroup->codec->controller,
334 			audioGroup->playback_stream);
335 	}
336 	if (audioGroup->record_stream && !audioGroup->record_stream->running) {
337 		hda_stream_start(audioGroup->codec->controller,
338 			audioGroup->record_stream);
339 	}
340 
341 	/* do playback */
342 	err = acquire_sem_etc(audioGroup->playback_stream->buffer_ready_sem,
343 		1, B_CAN_INTERRUPT, 0);
344 	if (err != B_OK) {
345 		dprintf("%s: Error waiting for playback buffer to finish (%s)!\n", __func__,
346 			strerror(err));
347 		return err;
348 	}
349 
350 	status = disable_interrupts();
351 	acquire_spinlock(&audioGroup->playback_stream->lock);
352 
353 	data->playback_buffer_cycle = audioGroup->playback_stream->buffer_cycle;
354 	data->played_real_time = audioGroup->playback_stream->real_time;
355 	data->played_frames_count = audioGroup->playback_stream->frames_count;
356 
357 	release_spinlock(&audioGroup->playback_stream->lock);
358 
359 	if (audioGroup->record_stream) {
360 		acquire_spinlock(&audioGroup->record_stream->lock);
361 		data->record_buffer_cycle = audioGroup->record_stream->buffer_cycle;
362 		data->recorded_real_time = audioGroup->record_stream->real_time;
363 		data->recorded_frames_count = audioGroup->record_stream->frames_count;
364 		release_spinlock(&audioGroup->record_stream->lock);
365 	}
366 
367 	restore_interrupts(status);
368 
369 	debug_buffers_exchanged++;
370 	if (((debug_buffers_exchanged % 100) == 1) && (debug_buffers_exchanged < 1111)) {
371 		dprintf("%s: %d buffers processed\n", __func__, debug_buffers_exchanged);
372 	}
373 
374 	return B_OK;
375 }
376 
377 
378 static status_t
379 buffer_force_stop(hda_audio_group* audioGroup)
380 {
381 	if (audioGroup->playback_stream != NULL) {
382 		hda_stream_stop(audioGroup->codec->controller,
383 			audioGroup->playback_stream);
384 	}
385 	if (audioGroup->record_stream != NULL) {
386 		hda_stream_stop(audioGroup->codec->controller,
387 			audioGroup->record_stream);
388 	}
389 	//hda_stream_stop(audioGroup->codec->controller, audioGroup->record_stream);
390 
391 	return B_OK;
392 }
393 
394 
395 status_t
396 multi_audio_control(void* cookie, uint32 op, void* arg, size_t len)
397 {
398 	hda_codec* codec = (hda_codec*)cookie;
399 	hda_audio_group* audioGroup;
400 
401 	/* FIXME: We should simply pass the audioGroup into here... */
402 	if (!codec || codec->num_audio_groups == 0)
403 		return ENODEV;
404 
405 	audioGroup = codec->audio_groups[0];
406 
407 	// TODO: make userland-safe when built for Haiku!
408 
409 	switch (op) {
410 		case B_MULTI_GET_DESCRIPTION:
411 		{
412 #ifdef __HAIKU
413 			multi_description description;
414 			multi_channel_info channels[16];
415 			multi_channel_info* originalChannels;
416 
417 			if (user_memcpy(&description, arg, sizeof(multi_description))
418 					!= B_OK)
419 				return B_BAD_ADDRESS;
420 
421 			originalChannels = description.channels;
422 			description.channels = channels;
423 			if (description.request_channel_count > 16)
424 				description.request_channel_count = 16;
425 
426 			status_t status = get_description(audioGroup, &description);
427 			if (status != B_OK)
428 				return status;
429 
430 			description.channels = originalChannels;
431 			return user_memcpy(arg, &description, sizeof(multi_description))
432 				&& user_memcpy(originalChannels, channels,
433 					sizeof(multi_channel_info)
434 						* description.request_channel_count;
435 #else
436 			return get_description(audioGroup, (multi_description*)arg);
437 #endif
438 		}
439 
440 		case B_MULTI_GET_ENABLED_CHANNELS:
441 			return get_enabled_channels(audioGroup, (multi_channel_enable*)arg);
442 		case B_MULTI_SET_ENABLED_CHANNELS:
443 			return B_OK;
444 
445 		case B_MULTI_GET_GLOBAL_FORMAT:
446 			return get_global_format(audioGroup, (multi_format_info*)arg);
447 		case B_MULTI_SET_GLOBAL_FORMAT:
448 			return set_global_format(audioGroup, (multi_format_info*)arg);
449 
450 		case B_MULTI_LIST_MIX_CHANNELS:
451 			return list_mix_channels(audioGroup, (multi_mix_channel_info*)arg);
452 		case B_MULTI_LIST_MIX_CONTROLS:
453 			return list_mix_controls(audioGroup, (multi_mix_control_info*)arg);
454 		case B_MULTI_LIST_MIX_CONNECTIONS:
455 			return list_mix_connections(audioGroup,
456 				(multi_mix_connection_info*)arg);
457 		case B_MULTI_GET_BUFFERS:
458 			return get_buffers(audioGroup, (multi_buffer_list*)arg);
459 
460 		case B_MULTI_BUFFER_EXCHANGE:
461 			return buffer_exchange(audioGroup, (multi_buffer_info*)arg);
462 		case B_MULTI_BUFFER_FORCE_STOP:
463 			return buffer_force_stop(audioGroup);
464 
465 		case B_MULTI_GET_EVENT_INFO:
466 		case B_MULTI_SET_EVENT_INFO:
467 		case B_MULTI_GET_EVENT:
468 		case B_MULTI_GET_CHANNEL_FORMATS:
469 		case B_MULTI_SET_CHANNEL_FORMATS:
470 		case B_MULTI_GET_MIX:
471 		case B_MULTI_SET_MIX:
472 		case B_MULTI_SET_BUFFERS:
473 		case B_MULTI_SET_START_TIME:
474 			return B_ERROR;
475 	}
476 
477 	return B_BAD_VALUE;
478 }
479