]>
Commit | Line | Data |
---|---|---|
d61a4ce8 GH |
1 | /* |
2 | * Copyright (C) 2010 Red Hat, Inc. | |
3 | * | |
4 | * written by Gerd Hoffmann <[email protected]> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation; either version 2 or | |
9 | * (at your option) version 3 of the License. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "hw.h" | |
21 | #include "pci.h" | |
22 | #include "intel-hda.h" | |
23 | #include "intel-hda-defs.h" | |
24 | #include "audio/audio.h" | |
25 | ||
26 | /* -------------------------------------------------------------------------- */ | |
27 | ||
28 | typedef struct desc_param { | |
29 | uint32_t id; | |
30 | uint32_t val; | |
31 | } desc_param; | |
32 | ||
33 | typedef struct desc_node { | |
34 | uint32_t nid; | |
35 | const char *name; | |
36 | const desc_param *params; | |
37 | uint32_t nparams; | |
38 | uint32_t config; | |
39 | uint32_t pinctl; | |
40 | uint32_t *conn; | |
41 | uint32_t stindex; | |
42 | } desc_node; | |
43 | ||
44 | typedef struct desc_codec { | |
45 | const char *name; | |
46 | uint32_t iid; | |
47 | const desc_node *nodes; | |
48 | uint32_t nnodes; | |
49 | } desc_codec; | |
50 | ||
51 | static const desc_param* hda_codec_find_param(const desc_node *node, uint32_t id) | |
52 | { | |
53 | int i; | |
54 | ||
55 | for (i = 0; i < node->nparams; i++) { | |
56 | if (node->params[i].id == id) { | |
57 | return &node->params[i]; | |
58 | } | |
59 | } | |
60 | return NULL; | |
61 | } | |
62 | ||
63 | static const desc_node* hda_codec_find_node(const desc_codec *codec, uint32_t nid) | |
64 | { | |
65 | int i; | |
66 | ||
67 | for (i = 0; i < codec->nnodes; i++) { | |
68 | if (codec->nodes[i].nid == nid) { | |
69 | return &codec->nodes[i]; | |
70 | } | |
71 | } | |
72 | return NULL; | |
73 | } | |
74 | ||
75 | static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) | |
76 | { | |
77 | if (format & AC_FMT_TYPE_NON_PCM) { | |
78 | return; | |
79 | } | |
80 | ||
81 | as->freq = (format & AC_FMT_BASE_44K) ? 44100 : 48000; | |
82 | ||
83 | switch ((format & AC_FMT_MULT_MASK) >> AC_FMT_MULT_SHIFT) { | |
84 | case 1: as->freq *= 2; break; | |
85 | case 2: as->freq *= 3; break; | |
86 | case 3: as->freq *= 4; break; | |
87 | } | |
88 | ||
89 | switch ((format & AC_FMT_DIV_MASK) >> AC_FMT_DIV_SHIFT) { | |
90 | case 1: as->freq /= 2; break; | |
91 | case 2: as->freq /= 3; break; | |
92 | case 3: as->freq /= 4; break; | |
93 | case 4: as->freq /= 5; break; | |
94 | case 5: as->freq /= 6; break; | |
95 | case 6: as->freq /= 7; break; | |
96 | case 7: as->freq /= 8; break; | |
97 | } | |
98 | ||
99 | switch (format & AC_FMT_BITS_MASK) { | |
100 | case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break; | |
101 | case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break; | |
102 | case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break; | |
103 | } | |
104 | ||
105 | as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; | |
106 | } | |
107 | ||
108 | /* -------------------------------------------------------------------------- */ | |
109 | /* | |
110 | * HDA codec descriptions | |
111 | */ | |
112 | ||
113 | /* some defines */ | |
114 | ||
115 | #define QEMU_HDA_ID_VENDOR 0x1af4 | |
116 | #define QEMU_HDA_ID_OUTPUT ((QEMU_HDA_ID_VENDOR << 16) | 0x10) | |
117 | #define QEMU_HDA_ID_DUPLEX ((QEMU_HDA_ID_VENDOR << 16) | 0x20) | |
118 | ||
119 | #define QEMU_HDA_PCM_FORMATS (AC_SUPPCM_BITS_16 | \ | |
120 | 0x1fc /* 16 -> 96 kHz */) | |
121 | #define QEMU_HDA_AMP_NONE (0) | |
122 | #define QEMU_HDA_AMP_STEPS 0x4a | |
123 | ||
124 | #ifdef CONFIG_MIXEMU | |
125 | #define QEMU_HDA_AMP_CAPS \ | |
126 | (AC_AMPCAP_MUTE | \ | |
127 | (QEMU_HDA_AMP_STEPS << AC_AMPCAP_OFFSET_SHIFT) | \ | |
128 | (QEMU_HDA_AMP_STEPS << AC_AMPCAP_NUM_STEPS_SHIFT) | \ | |
129 | (3 << AC_AMPCAP_STEP_SIZE_SHIFT)) | |
130 | #else | |
131 | #define QEMU_HDA_AMP_CAPS QEMU_HDA_AMP_NONE | |
132 | #endif | |
133 | ||
134 | /* common: audio output widget */ | |
135 | static const desc_param common_params_audio_dac[] = { | |
136 | { | |
137 | .id = AC_PAR_AUDIO_WIDGET_CAP, | |
138 | .val = ((AC_WID_AUD_OUT << AC_WCAP_TYPE_SHIFT) | | |
139 | AC_WCAP_FORMAT_OVRD | | |
140 | AC_WCAP_AMP_OVRD | | |
141 | AC_WCAP_OUT_AMP | | |
142 | AC_WCAP_STEREO), | |
143 | },{ | |
144 | .id = AC_PAR_PCM, | |
145 | .val = QEMU_HDA_PCM_FORMATS, | |
146 | },{ | |
147 | .id = AC_PAR_STREAM, | |
148 | .val = AC_SUPFMT_PCM, | |
149 | },{ | |
150 | .id = AC_PAR_AMP_IN_CAP, | |
151 | .val = QEMU_HDA_AMP_NONE, | |
152 | },{ | |
153 | .id = AC_PAR_AMP_OUT_CAP, | |
154 | .val = QEMU_HDA_AMP_CAPS, | |
155 | }, | |
156 | }; | |
157 | ||
158 | /* common: pin widget (line-out) */ | |
159 | static const desc_param common_params_audio_lineout[] = { | |
160 | { | |
161 | .id = AC_PAR_AUDIO_WIDGET_CAP, | |
162 | .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | | |
163 | AC_WCAP_CONN_LIST | | |
164 | AC_WCAP_STEREO), | |
165 | },{ | |
166 | .id = AC_PAR_PIN_CAP, | |
167 | .val = AC_PINCAP_OUT, | |
168 | },{ | |
169 | .id = AC_PAR_CONNLIST_LEN, | |
170 | .val = 1, | |
171 | },{ | |
172 | .id = AC_PAR_AMP_IN_CAP, | |
173 | .val = QEMU_HDA_AMP_NONE, | |
174 | },{ | |
175 | .id = AC_PAR_AMP_OUT_CAP, | |
176 | .val = QEMU_HDA_AMP_NONE, | |
177 | }, | |
178 | }; | |
179 | ||
180 | /* output: root node */ | |
181 | static const desc_param output_params_root[] = { | |
182 | { | |
183 | .id = AC_PAR_VENDOR_ID, | |
184 | .val = QEMU_HDA_ID_OUTPUT, | |
185 | },{ | |
186 | .id = AC_PAR_SUBSYSTEM_ID, | |
187 | .val = QEMU_HDA_ID_OUTPUT, | |
188 | },{ | |
189 | .id = AC_PAR_REV_ID, | |
190 | .val = 0x00100101, | |
191 | },{ | |
192 | .id = AC_PAR_NODE_COUNT, | |
193 | .val = 0x00010001, | |
194 | }, | |
195 | }; | |
196 | ||
197 | /* output: audio function */ | |
198 | static const desc_param output_params_audio_func[] = { | |
199 | { | |
200 | .id = AC_PAR_FUNCTION_TYPE, | |
201 | .val = AC_GRP_AUDIO_FUNCTION, | |
202 | },{ | |
203 | .id = AC_PAR_SUBSYSTEM_ID, | |
204 | .val = QEMU_HDA_ID_OUTPUT, | |
205 | },{ | |
206 | .id = AC_PAR_NODE_COUNT, | |
207 | .val = 0x00020002, | |
208 | },{ | |
209 | .id = AC_PAR_PCM, | |
210 | .val = QEMU_HDA_PCM_FORMATS, | |
211 | },{ | |
212 | .id = AC_PAR_STREAM, | |
213 | .val = AC_SUPFMT_PCM, | |
214 | },{ | |
215 | .id = AC_PAR_AMP_IN_CAP, | |
216 | .val = QEMU_HDA_AMP_NONE, | |
217 | },{ | |
218 | .id = AC_PAR_AMP_OUT_CAP, | |
219 | .val = QEMU_HDA_AMP_NONE, | |
220 | },{ | |
221 | .id = AC_PAR_GPIO_CAP, | |
222 | .val = 0, | |
223 | },{ | |
224 | .id = AC_PAR_AUDIO_FG_CAP, | |
225 | .val = 0x00000808, | |
226 | },{ | |
227 | .id = AC_PAR_POWER_STATE, | |
228 | .val = 0, | |
229 | }, | |
230 | }; | |
231 | ||
232 | /* output: nodes */ | |
233 | static const desc_node output_nodes[] = { | |
234 | { | |
235 | .nid = AC_NODE_ROOT, | |
236 | .name = "root", | |
237 | .params = output_params_root, | |
238 | .nparams = ARRAY_SIZE(output_params_root), | |
239 | },{ | |
240 | .nid = 1, | |
241 | .name = "func", | |
242 | .params = output_params_audio_func, | |
243 | .nparams = ARRAY_SIZE(output_params_audio_func), | |
244 | },{ | |
245 | .nid = 2, | |
246 | .name = "dac", | |
247 | .params = common_params_audio_dac, | |
248 | .nparams = ARRAY_SIZE(common_params_audio_dac), | |
249 | .stindex = 0, | |
250 | },{ | |
251 | .nid = 3, | |
252 | .name = "out", | |
253 | .params = common_params_audio_lineout, | |
254 | .nparams = ARRAY_SIZE(common_params_audio_lineout), | |
255 | .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | | |
256 | (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | | |
257 | (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | | |
258 | (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | | |
259 | 0x10), | |
260 | .pinctl = AC_PINCTL_OUT_EN, | |
261 | .conn = (uint32_t[]) { 2 }, | |
262 | } | |
263 | }; | |
264 | ||
265 | /* output: codec */ | |
266 | static const desc_codec output = { | |
267 | .name = "output", | |
268 | .iid = QEMU_HDA_ID_OUTPUT, | |
269 | .nodes = output_nodes, | |
270 | .nnodes = ARRAY_SIZE(output_nodes), | |
271 | }; | |
272 | ||
273 | /* duplex: root node */ | |
274 | static const desc_param duplex_params_root[] = { | |
275 | { | |
276 | .id = AC_PAR_VENDOR_ID, | |
277 | .val = QEMU_HDA_ID_DUPLEX, | |
278 | },{ | |
279 | .id = AC_PAR_SUBSYSTEM_ID, | |
280 | .val = QEMU_HDA_ID_DUPLEX, | |
281 | },{ | |
282 | .id = AC_PAR_REV_ID, | |
283 | .val = 0x00100101, | |
284 | },{ | |
285 | .id = AC_PAR_NODE_COUNT, | |
286 | .val = 0x00010001, | |
287 | }, | |
288 | }; | |
289 | ||
290 | /* duplex: audio input widget */ | |
291 | static const desc_param duplex_params_audio_adc[] = { | |
292 | { | |
293 | .id = AC_PAR_AUDIO_WIDGET_CAP, | |
294 | .val = ((AC_WID_AUD_IN << AC_WCAP_TYPE_SHIFT) | | |
295 | AC_WCAP_CONN_LIST | | |
296 | AC_WCAP_FORMAT_OVRD | | |
297 | AC_WCAP_AMP_OVRD | | |
298 | AC_WCAP_IN_AMP | | |
299 | AC_WCAP_STEREO), | |
300 | },{ | |
301 | .id = AC_PAR_CONNLIST_LEN, | |
302 | .val = 1, | |
303 | },{ | |
304 | .id = AC_PAR_PCM, | |
305 | .val = QEMU_HDA_PCM_FORMATS, | |
306 | },{ | |
307 | .id = AC_PAR_STREAM, | |
308 | .val = AC_SUPFMT_PCM, | |
309 | },{ | |
310 | .id = AC_PAR_AMP_IN_CAP, | |
311 | .val = QEMU_HDA_AMP_CAPS, | |
312 | },{ | |
313 | .id = AC_PAR_AMP_OUT_CAP, | |
314 | .val = QEMU_HDA_AMP_NONE, | |
315 | }, | |
316 | }; | |
317 | ||
318 | /* duplex: pin widget (line-in) */ | |
319 | static const desc_param duplex_params_audio_linein[] = { | |
320 | { | |
321 | .id = AC_PAR_AUDIO_WIDGET_CAP, | |
322 | .val = ((AC_WID_PIN << AC_WCAP_TYPE_SHIFT) | | |
323 | AC_WCAP_STEREO), | |
324 | },{ | |
325 | .id = AC_PAR_PIN_CAP, | |
326 | .val = AC_PINCAP_IN, | |
327 | },{ | |
328 | .id = AC_PAR_AMP_IN_CAP, | |
329 | .val = QEMU_HDA_AMP_NONE, | |
330 | },{ | |
331 | .id = AC_PAR_AMP_OUT_CAP, | |
332 | .val = QEMU_HDA_AMP_NONE, | |
333 | }, | |
334 | }; | |
335 | ||
336 | /* duplex: audio function */ | |
337 | static const desc_param duplex_params_audio_func[] = { | |
338 | { | |
339 | .id = AC_PAR_FUNCTION_TYPE, | |
340 | .val = AC_GRP_AUDIO_FUNCTION, | |
341 | },{ | |
342 | .id = AC_PAR_SUBSYSTEM_ID, | |
343 | .val = QEMU_HDA_ID_DUPLEX, | |
344 | },{ | |
345 | .id = AC_PAR_NODE_COUNT, | |
346 | .val = 0x00020004, | |
347 | },{ | |
348 | .id = AC_PAR_PCM, | |
349 | .val = QEMU_HDA_PCM_FORMATS, | |
350 | },{ | |
351 | .id = AC_PAR_STREAM, | |
352 | .val = AC_SUPFMT_PCM, | |
353 | },{ | |
354 | .id = AC_PAR_AMP_IN_CAP, | |
355 | .val = QEMU_HDA_AMP_NONE, | |
356 | },{ | |
357 | .id = AC_PAR_AMP_OUT_CAP, | |
358 | .val = QEMU_HDA_AMP_NONE, | |
359 | },{ | |
360 | .id = AC_PAR_GPIO_CAP, | |
361 | .val = 0, | |
362 | },{ | |
363 | .id = AC_PAR_AUDIO_FG_CAP, | |
364 | .val = 0x00000808, | |
365 | },{ | |
366 | .id = AC_PAR_POWER_STATE, | |
367 | .val = 0, | |
368 | }, | |
369 | }; | |
370 | ||
371 | /* duplex: nodes */ | |
372 | static const desc_node duplex_nodes[] = { | |
373 | { | |
374 | .nid = AC_NODE_ROOT, | |
375 | .name = "root", | |
376 | .params = duplex_params_root, | |
377 | .nparams = ARRAY_SIZE(duplex_params_root), | |
378 | },{ | |
379 | .nid = 1, | |
380 | .name = "func", | |
381 | .params = duplex_params_audio_func, | |
382 | .nparams = ARRAY_SIZE(duplex_params_audio_func), | |
383 | },{ | |
384 | .nid = 2, | |
385 | .name = "dac", | |
386 | .params = common_params_audio_dac, | |
387 | .nparams = ARRAY_SIZE(common_params_audio_dac), | |
388 | .stindex = 0, | |
389 | },{ | |
390 | .nid = 3, | |
391 | .name = "out", | |
392 | .params = common_params_audio_lineout, | |
393 | .nparams = ARRAY_SIZE(common_params_audio_lineout), | |
394 | .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | | |
395 | (AC_JACK_LINE_OUT << AC_DEFCFG_DEVICE_SHIFT) | | |
396 | (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | | |
397 | (AC_JACK_COLOR_GREEN << AC_DEFCFG_COLOR_SHIFT) | | |
398 | 0x10), | |
399 | .pinctl = AC_PINCTL_OUT_EN, | |
400 | .conn = (uint32_t[]) { 2 }, | |
401 | },{ | |
402 | .nid = 4, | |
403 | .name = "adc", | |
404 | .params = duplex_params_audio_adc, | |
405 | .nparams = ARRAY_SIZE(duplex_params_audio_adc), | |
406 | .stindex = 1, | |
407 | .conn = (uint32_t[]) { 5 }, | |
408 | },{ | |
409 | .nid = 5, | |
410 | .name = "in", | |
411 | .params = duplex_params_audio_linein, | |
412 | .nparams = ARRAY_SIZE(duplex_params_audio_linein), | |
413 | .config = ((AC_JACK_PORT_COMPLEX << AC_DEFCFG_PORT_CONN_SHIFT) | | |
414 | (AC_JACK_LINE_IN << AC_DEFCFG_DEVICE_SHIFT) | | |
415 | (AC_JACK_CONN_UNKNOWN << AC_DEFCFG_CONN_TYPE_SHIFT) | | |
416 | (AC_JACK_COLOR_RED << AC_DEFCFG_COLOR_SHIFT) | | |
417 | 0x20), | |
418 | .pinctl = AC_PINCTL_IN_EN, | |
419 | } | |
420 | }; | |
421 | ||
422 | /* duplex: codec */ | |
423 | static const desc_codec duplex = { | |
424 | .name = "duplex", | |
425 | .iid = QEMU_HDA_ID_DUPLEX, | |
426 | .nodes = duplex_nodes, | |
427 | .nnodes = ARRAY_SIZE(duplex_nodes), | |
428 | }; | |
429 | ||
430 | /* -------------------------------------------------------------------------- */ | |
431 | ||
432 | static const char *fmt2name[] = { | |
433 | [ AUD_FMT_U8 ] = "PCM-U8", | |
434 | [ AUD_FMT_S8 ] = "PCM-S8", | |
435 | [ AUD_FMT_U16 ] = "PCM-U16", | |
436 | [ AUD_FMT_S16 ] = "PCM-S16", | |
437 | [ AUD_FMT_U32 ] = "PCM-U32", | |
438 | [ AUD_FMT_S32 ] = "PCM-S32", | |
439 | }; | |
440 | ||
441 | typedef struct HDAAudioState HDAAudioState; | |
442 | typedef struct HDAAudioStream HDAAudioStream; | |
443 | ||
444 | struct HDAAudioStream { | |
445 | HDAAudioState *state; | |
446 | const desc_node *node; | |
447 | bool output, running; | |
448 | uint32_t stream; | |
449 | uint32_t channel; | |
450 | uint32_t format; | |
451 | uint32_t gain_left, gain_right; | |
452 | bool mute_left, mute_right; | |
453 | struct audsettings as; | |
454 | union { | |
455 | SWVoiceIn *in; | |
456 | SWVoiceOut *out; | |
457 | } voice; | |
458 | uint8_t buf[HDA_BUFFER_SIZE]; | |
459 | uint32_t bpos; | |
460 | }; | |
461 | ||
462 | struct HDAAudioState { | |
463 | HDACodecDevice hda; | |
464 | const char *name; | |
465 | ||
466 | QEMUSoundCard card; | |
467 | const desc_codec *desc; | |
468 | HDAAudioStream st[4]; | |
ba43d289 MAL |
469 | bool running_compat[16]; |
470 | bool running_real[2 * 16]; | |
d61a4ce8 GH |
471 | |
472 | /* properties */ | |
473 | uint32_t debug; | |
474 | }; | |
475 | ||
476 | static void hda_audio_input_cb(void *opaque, int avail) | |
477 | { | |
478 | HDAAudioStream *st = opaque; | |
479 | int recv = 0; | |
480 | int len; | |
481 | bool rc; | |
482 | ||
483 | while (avail - recv >= sizeof(st->buf)) { | |
484 | if (st->bpos != sizeof(st->buf)) { | |
485 | len = AUD_read(st->voice.in, st->buf + st->bpos, | |
486 | sizeof(st->buf) - st->bpos); | |
487 | st->bpos += len; | |
488 | recv += len; | |
489 | if (st->bpos != sizeof(st->buf)) { | |
490 | break; | |
491 | } | |
492 | } | |
493 | rc = hda_codec_xfer(&st->state->hda, st->stream, false, | |
494 | st->buf, sizeof(st->buf)); | |
495 | if (!rc) { | |
496 | break; | |
497 | } | |
498 | st->bpos = 0; | |
499 | } | |
500 | } | |
501 | ||
502 | static void hda_audio_output_cb(void *opaque, int avail) | |
503 | { | |
504 | HDAAudioStream *st = opaque; | |
505 | int sent = 0; | |
506 | int len; | |
507 | bool rc; | |
508 | ||
509 | while (avail - sent >= sizeof(st->buf)) { | |
510 | if (st->bpos == sizeof(st->buf)) { | |
511 | rc = hda_codec_xfer(&st->state->hda, st->stream, true, | |
512 | st->buf, sizeof(st->buf)); | |
513 | if (!rc) { | |
514 | break; | |
515 | } | |
516 | st->bpos = 0; | |
517 | } | |
518 | len = AUD_write(st->voice.out, st->buf + st->bpos, | |
519 | sizeof(st->buf) - st->bpos); | |
520 | st->bpos += len; | |
521 | sent += len; | |
522 | if (st->bpos != sizeof(st->buf)) { | |
523 | break; | |
524 | } | |
525 | } | |
526 | } | |
527 | ||
528 | static void hda_audio_set_running(HDAAudioStream *st, bool running) | |
529 | { | |
530 | if (st->node == NULL) { | |
531 | return; | |
532 | } | |
533 | if (st->running == running) { | |
534 | return; | |
535 | } | |
536 | st->running = running; | |
537 | dprint(st->state, 1, "%s: %s (stream %d)\n", st->node->name, | |
538 | st->running ? "on" : "off", st->stream); | |
539 | if (st->output) { | |
540 | AUD_set_active_out(st->voice.out, st->running); | |
541 | } else { | |
542 | AUD_set_active_in(st->voice.in, st->running); | |
543 | } | |
544 | } | |
545 | ||
546 | static void hda_audio_set_amp(HDAAudioStream *st) | |
547 | { | |
548 | bool muted; | |
549 | uint32_t left, right; | |
550 | ||
551 | if (st->node == NULL) { | |
552 | return; | |
553 | } | |
554 | ||
555 | muted = st->mute_left && st->mute_right; | |
556 | left = st->mute_left ? 0 : st->gain_left; | |
557 | right = st->mute_right ? 0 : st->gain_right; | |
558 | ||
559 | left = left * 255 / QEMU_HDA_AMP_STEPS; | |
560 | right = right * 255 / QEMU_HDA_AMP_STEPS; | |
561 | ||
562 | if (st->output) { | |
9fe5497c | 563 | AUD_set_volume_out(st->voice.out, muted, left, right); |
d61a4ce8 | 564 | } else { |
9fe5497c | 565 | AUD_set_volume_in(st->voice.in, muted, left, right); |
d61a4ce8 GH |
566 | } |
567 | } | |
568 | ||
569 | static void hda_audio_setup(HDAAudioStream *st) | |
570 | { | |
571 | if (st->node == NULL) { | |
572 | return; | |
573 | } | |
574 | ||
575 | dprint(st->state, 1, "%s: format: %d x %s @ %d Hz\n", | |
576 | st->node->name, st->as.nchannels, | |
577 | fmt2name[st->as.fmt], st->as.freq); | |
578 | ||
579 | if (st->output) { | |
580 | st->voice.out = AUD_open_out(&st->state->card, st->voice.out, | |
581 | st->node->name, st, | |
582 | hda_audio_output_cb, &st->as); | |
583 | } else { | |
584 | st->voice.in = AUD_open_in(&st->state->card, st->voice.in, | |
585 | st->node->name, st, | |
586 | hda_audio_input_cb, &st->as); | |
587 | } | |
588 | } | |
589 | ||
590 | static void hda_audio_command(HDACodecDevice *hda, uint32_t nid, uint32_t data) | |
591 | { | |
592 | HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); | |
593 | HDAAudioStream *st; | |
594 | const desc_node *node = NULL; | |
595 | const desc_param *param; | |
596 | uint32_t verb, payload, response, count, shift; | |
597 | ||
598 | if ((data & 0x70000) == 0x70000) { | |
599 | /* 12/8 id/payload */ | |
600 | verb = (data >> 8) & 0xfff; | |
601 | payload = data & 0x00ff; | |
602 | } else { | |
603 | /* 4/16 id/payload */ | |
604 | verb = (data >> 8) & 0xf00; | |
605 | payload = data & 0xffff; | |
606 | } | |
607 | ||
608 | node = hda_codec_find_node(a->desc, nid); | |
609 | if (node == NULL) { | |
610 | goto fail; | |
611 | } | |
612 | dprint(a, 2, "%s: nid %d (%s), verb 0x%x, payload 0x%x\n", | |
613 | __FUNCTION__, nid, node->name, verb, payload); | |
614 | ||
615 | switch (verb) { | |
616 | /* all nodes */ | |
617 | case AC_VERB_PARAMETERS: | |
618 | param = hda_codec_find_param(node, payload); | |
619 | if (param == NULL) { | |
620 | goto fail; | |
621 | } | |
622 | hda_codec_response(hda, true, param->val); | |
623 | break; | |
624 | case AC_VERB_GET_SUBSYSTEM_ID: | |
625 | hda_codec_response(hda, true, a->desc->iid); | |
626 | break; | |
627 | ||
628 | /* all functions */ | |
629 | case AC_VERB_GET_CONNECT_LIST: | |
630 | param = hda_codec_find_param(node, AC_PAR_CONNLIST_LEN); | |
631 | count = param ? param->val : 0; | |
632 | response = 0; | |
633 | shift = 0; | |
634 | while (payload < count && shift < 32) { | |
635 | response |= node->conn[payload] << shift; | |
636 | payload++; | |
637 | shift += 8; | |
638 | } | |
639 | hda_codec_response(hda, true, response); | |
640 | break; | |
641 | ||
642 | /* pin widget */ | |
643 | case AC_VERB_GET_CONFIG_DEFAULT: | |
644 | hda_codec_response(hda, true, node->config); | |
645 | break; | |
646 | case AC_VERB_GET_PIN_WIDGET_CONTROL: | |
647 | hda_codec_response(hda, true, node->pinctl); | |
648 | break; | |
649 | case AC_VERB_SET_PIN_WIDGET_CONTROL: | |
650 | if (node->pinctl != payload) { | |
651 | dprint(a, 1, "unhandled pin control bit\n"); | |
652 | } | |
653 | hda_codec_response(hda, true, 0); | |
654 | break; | |
655 | ||
656 | /* audio in/out widget */ | |
657 | case AC_VERB_SET_CHANNEL_STREAMID: | |
658 | st = a->st + node->stindex; | |
659 | if (st->node == NULL) { | |
660 | goto fail; | |
661 | } | |
662 | hda_audio_set_running(st, false); | |
663 | st->stream = (payload >> 4) & 0x0f; | |
664 | st->channel = payload & 0x0f; | |
665 | dprint(a, 2, "%s: stream %d, channel %d\n", | |
666 | st->node->name, st->stream, st->channel); | |
ba43d289 | 667 | hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); |
d61a4ce8 GH |
668 | hda_codec_response(hda, true, 0); |
669 | break; | |
670 | case AC_VERB_GET_CONV: | |
671 | st = a->st + node->stindex; | |
672 | if (st->node == NULL) { | |
673 | goto fail; | |
674 | } | |
675 | response = st->stream << 4 | st->channel; | |
676 | hda_codec_response(hda, true, response); | |
677 | break; | |
678 | case AC_VERB_SET_STREAM_FORMAT: | |
679 | st = a->st + node->stindex; | |
680 | if (st->node == NULL) { | |
681 | goto fail; | |
682 | } | |
683 | st->format = payload; | |
684 | hda_codec_parse_fmt(st->format, &st->as); | |
685 | hda_audio_setup(st); | |
686 | hda_codec_response(hda, true, 0); | |
687 | break; | |
688 | case AC_VERB_GET_STREAM_FORMAT: | |
689 | st = a->st + node->stindex; | |
690 | if (st->node == NULL) { | |
691 | goto fail; | |
692 | } | |
693 | hda_codec_response(hda, true, st->format); | |
694 | break; | |
695 | case AC_VERB_GET_AMP_GAIN_MUTE: | |
696 | st = a->st + node->stindex; | |
697 | if (st->node == NULL) { | |
698 | goto fail; | |
699 | } | |
700 | if (payload & AC_AMP_GET_LEFT) { | |
701 | response = st->gain_left | (st->mute_left ? AC_AMP_MUTE : 0); | |
702 | } else { | |
703 | response = st->gain_right | (st->mute_right ? AC_AMP_MUTE : 0); | |
704 | } | |
705 | hda_codec_response(hda, true, response); | |
706 | break; | |
707 | case AC_VERB_SET_AMP_GAIN_MUTE: | |
708 | st = a->st + node->stindex; | |
709 | if (st->node == NULL) { | |
710 | goto fail; | |
711 | } | |
712 | dprint(a, 1, "amp (%s): %s%s%s%s index %d gain %3d %s\n", | |
713 | st->node->name, | |
714 | (payload & AC_AMP_SET_OUTPUT) ? "o" : "-", | |
715 | (payload & AC_AMP_SET_INPUT) ? "i" : "-", | |
716 | (payload & AC_AMP_SET_LEFT) ? "l" : "-", | |
717 | (payload & AC_AMP_SET_RIGHT) ? "r" : "-", | |
718 | (payload & AC_AMP_SET_INDEX) >> AC_AMP_SET_INDEX_SHIFT, | |
719 | (payload & AC_AMP_GAIN), | |
720 | (payload & AC_AMP_MUTE) ? "muted" : ""); | |
721 | if (payload & AC_AMP_SET_LEFT) { | |
722 | st->gain_left = payload & AC_AMP_GAIN; | |
723 | st->mute_left = payload & AC_AMP_MUTE; | |
724 | } | |
725 | if (payload & AC_AMP_SET_RIGHT) { | |
726 | st->gain_right = payload & AC_AMP_GAIN; | |
727 | st->mute_right = payload & AC_AMP_MUTE; | |
728 | } | |
729 | hda_audio_set_amp(st); | |
730 | hda_codec_response(hda, true, 0); | |
731 | break; | |
732 | ||
733 | /* not supported */ | |
734 | case AC_VERB_SET_POWER_STATE: | |
735 | case AC_VERB_GET_POWER_STATE: | |
736 | case AC_VERB_GET_SDI_SELECT: | |
737 | hda_codec_response(hda, true, 0); | |
738 | break; | |
739 | default: | |
740 | goto fail; | |
741 | } | |
742 | return; | |
743 | ||
744 | fail: | |
745 | dprint(a, 1, "%s: not handled: nid %d (%s), verb 0x%x, payload 0x%x\n", | |
746 | __FUNCTION__, nid, node ? node->name : "?", verb, payload); | |
747 | hda_codec_response(hda, true, 0); | |
748 | } | |
749 | ||
ba43d289 | 750 | static void hda_audio_stream(HDACodecDevice *hda, uint32_t stnr, bool running, bool output) |
d61a4ce8 GH |
751 | { |
752 | HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); | |
753 | int s; | |
754 | ||
ba43d289 MAL |
755 | a->running_compat[stnr] = running; |
756 | a->running_real[output * 16 + stnr] = running; | |
d61a4ce8 GH |
757 | for (s = 0; s < ARRAY_SIZE(a->st); s++) { |
758 | if (a->st[s].node == NULL) { | |
759 | continue; | |
760 | } | |
ba43d289 MAL |
761 | if (a->st[s].output != output) { |
762 | continue; | |
763 | } | |
d61a4ce8 GH |
764 | if (a->st[s].stream != stnr) { |
765 | continue; | |
766 | } | |
767 | hda_audio_set_running(&a->st[s], running); | |
768 | } | |
769 | } | |
770 | ||
771 | static int hda_audio_init(HDACodecDevice *hda, const struct desc_codec *desc) | |
772 | { | |
773 | HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); | |
774 | HDAAudioStream *st; | |
775 | const desc_node *node; | |
776 | const desc_param *param; | |
777 | uint32_t i, type; | |
778 | ||
779 | a->desc = desc; | |
f79f2bfc | 780 | a->name = object_get_typename(OBJECT(a)); |
d61a4ce8 GH |
781 | dprint(a, 1, "%s: cad %d\n", __FUNCTION__, a->hda.cad); |
782 | ||
783 | AUD_register_card("hda", &a->card); | |
784 | for (i = 0; i < a->desc->nnodes; i++) { | |
785 | node = a->desc->nodes + i; | |
786 | param = hda_codec_find_param(node, AC_PAR_AUDIO_WIDGET_CAP); | |
787 | if (NULL == param) | |
788 | continue; | |
789 | type = (param->val & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT; | |
790 | switch (type) { | |
791 | case AC_WID_AUD_OUT: | |
792 | case AC_WID_AUD_IN: | |
793 | assert(node->stindex < ARRAY_SIZE(a->st)); | |
794 | st = a->st + node->stindex; | |
795 | st->state = a; | |
796 | st->node = node; | |
797 | if (type == AC_WID_AUD_OUT) { | |
798 | /* unmute output by default */ | |
799 | st->gain_left = QEMU_HDA_AMP_STEPS; | |
800 | st->gain_right = QEMU_HDA_AMP_STEPS; | |
801 | st->bpos = sizeof(st->buf); | |
802 | st->output = true; | |
803 | } else { | |
804 | st->output = false; | |
805 | } | |
806 | st->format = AC_FMT_TYPE_PCM | AC_FMT_BITS_16 | | |
807 | (1 << AC_FMT_CHAN_SHIFT); | |
808 | hda_codec_parse_fmt(st->format, &st->as); | |
809 | hda_audio_setup(st); | |
810 | break; | |
811 | } | |
812 | } | |
813 | return 0; | |
814 | } | |
815 | ||
129dcd2c GH |
816 | static int hda_audio_exit(HDACodecDevice *hda) |
817 | { | |
818 | HDAAudioState *a = DO_UPCAST(HDAAudioState, hda, hda); | |
819 | HDAAudioStream *st; | |
820 | int i; | |
821 | ||
822 | dprint(a, 1, "%s\n", __FUNCTION__); | |
823 | for (i = 0; i < ARRAY_SIZE(a->st); i++) { | |
824 | st = a->st + i; | |
825 | if (st->node == NULL) { | |
826 | continue; | |
827 | } | |
828 | if (st->output) { | |
829 | AUD_close_out(&a->card, st->voice.out); | |
830 | } else { | |
831 | AUD_close_in(&a->card, st->voice.in); | |
832 | } | |
833 | } | |
834 | AUD_remove_card(&a->card); | |
835 | return 0; | |
836 | } | |
837 | ||
d61a4ce8 GH |
838 | static int hda_audio_post_load(void *opaque, int version) |
839 | { | |
840 | HDAAudioState *a = opaque; | |
841 | HDAAudioStream *st; | |
842 | int i; | |
843 | ||
844 | dprint(a, 1, "%s\n", __FUNCTION__); | |
ba43d289 MAL |
845 | if (version == 1) { |
846 | /* assume running_compat[] is for output streams */ | |
847 | for (i = 0; i < ARRAY_SIZE(a->running_compat); i++) | |
848 | a->running_real[16 + i] = a->running_compat[i]; | |
849 | } | |
850 | ||
d61a4ce8 GH |
851 | for (i = 0; i < ARRAY_SIZE(a->st); i++) { |
852 | st = a->st + i; | |
853 | if (st->node == NULL) | |
854 | continue; | |
855 | hda_codec_parse_fmt(st->format, &st->as); | |
856 | hda_audio_setup(st); | |
857 | hda_audio_set_amp(st); | |
ba43d289 | 858 | hda_audio_set_running(st, a->running_real[st->output * 16 + st->stream]); |
d61a4ce8 GH |
859 | } |
860 | return 0; | |
861 | } | |
862 | ||
863 | static const VMStateDescription vmstate_hda_audio_stream = { | |
864 | .name = "hda-audio-stream", | |
865 | .version_id = 1, | |
866 | .fields = (VMStateField []) { | |
867 | VMSTATE_UINT32(stream, HDAAudioStream), | |
868 | VMSTATE_UINT32(channel, HDAAudioStream), | |
869 | VMSTATE_UINT32(format, HDAAudioStream), | |
870 | VMSTATE_UINT32(gain_left, HDAAudioStream), | |
871 | VMSTATE_UINT32(gain_right, HDAAudioStream), | |
872 | VMSTATE_BOOL(mute_left, HDAAudioStream), | |
873 | VMSTATE_BOOL(mute_right, HDAAudioStream), | |
874 | VMSTATE_UINT32(bpos, HDAAudioStream), | |
875 | VMSTATE_BUFFER(buf, HDAAudioStream), | |
876 | VMSTATE_END_OF_LIST() | |
877 | } | |
878 | }; | |
879 | ||
880 | static const VMStateDescription vmstate_hda_audio = { | |
881 | .name = "hda-audio", | |
ba43d289 | 882 | .version_id = 2, |
d61a4ce8 GH |
883 | .post_load = hda_audio_post_load, |
884 | .fields = (VMStateField []) { | |
885 | VMSTATE_STRUCT_ARRAY(st, HDAAudioState, 4, 0, | |
886 | vmstate_hda_audio_stream, | |
887 | HDAAudioStream), | |
ba43d289 MAL |
888 | VMSTATE_BOOL_ARRAY(running_compat, HDAAudioState, 16), |
889 | VMSTATE_BOOL_ARRAY_V(running_real, HDAAudioState, 2 * 16, 2), | |
d61a4ce8 GH |
890 | VMSTATE_END_OF_LIST() |
891 | } | |
892 | }; | |
893 | ||
894 | static Property hda_audio_properties[] = { | |
895 | DEFINE_PROP_UINT32("debug", HDAAudioState, debug, 0), | |
896 | DEFINE_PROP_END_OF_LIST(), | |
897 | }; | |
898 | ||
899 | static int hda_audio_init_output(HDACodecDevice *hda) | |
900 | { | |
901 | return hda_audio_init(hda, &output); | |
902 | } | |
903 | ||
904 | static int hda_audio_init_duplex(HDACodecDevice *hda) | |
905 | { | |
906 | return hda_audio_init(hda, &duplex); | |
907 | } | |
908 | ||
dbaa7904 AL |
909 | static void hda_audio_output_class_init(ObjectClass *klass, void *data) |
910 | { | |
39bffca2 | 911 | DeviceClass *dc = DEVICE_CLASS(klass); |
dbaa7904 AL |
912 | HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); |
913 | ||
914 | k->init = hda_audio_init_output; | |
915 | k->exit = hda_audio_exit; | |
916 | k->command = hda_audio_command; | |
917 | k->stream = hda_audio_stream; | |
39bffca2 AL |
918 | dc->desc = "HDA Audio Codec, output-only"; |
919 | dc->vmsd = &vmstate_hda_audio; | |
920 | dc->props = hda_audio_properties; | |
dbaa7904 AL |
921 | } |
922 | ||
39bffca2 AL |
923 | static TypeInfo hda_audio_output_info = { |
924 | .name = "hda-output", | |
925 | .parent = TYPE_HDA_CODEC_DEVICE, | |
926 | .instance_size = sizeof(HDAAudioState), | |
927 | .class_init = hda_audio_output_class_init, | |
d61a4ce8 GH |
928 | }; |
929 | ||
dbaa7904 AL |
930 | static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) |
931 | { | |
39bffca2 | 932 | DeviceClass *dc = DEVICE_CLASS(klass); |
dbaa7904 AL |
933 | HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); |
934 | ||
935 | k->init = hda_audio_init_duplex; | |
936 | k->exit = hda_audio_exit; | |
937 | k->command = hda_audio_command; | |
938 | k->stream = hda_audio_stream; | |
39bffca2 AL |
939 | dc->desc = "HDA Audio Codec, duplex"; |
940 | dc->vmsd = &vmstate_hda_audio; | |
941 | dc->props = hda_audio_properties; | |
dbaa7904 AL |
942 | } |
943 | ||
39bffca2 AL |
944 | static TypeInfo hda_audio_duplex_info = { |
945 | .name = "hda-duplex", | |
946 | .parent = TYPE_HDA_CODEC_DEVICE, | |
947 | .instance_size = sizeof(HDAAudioState), | |
948 | .class_init = hda_audio_duplex_class_init, | |
d61a4ce8 GH |
949 | }; |
950 | ||
83f7d43a | 951 | static void hda_audio_register_types(void) |
d61a4ce8 | 952 | { |
39bffca2 AL |
953 | type_register_static(&hda_audio_output_info); |
954 | type_register_static(&hda_audio_duplex_info); | |
d61a4ce8 | 955 | } |
83f7d43a AF |
956 | |
957 | type_init(hda_audio_register_types) |