source: mainline/uspace/drv/audio/hdaudio/codec.c@ fff4f21

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fff4f21 was fff4f21, checked in by Jiri Svoboda <jiri@…>, 11 years ago

And there was sound.

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/*
2 * Copyright (c) 2014 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup hdaudio
30 * @{
31 */
32/** @file High Definition Audio codec
33 */
34
35#include <async.h>
36#include <bitops.h>
37#include <ddf/log.h>
38#include <errno.h>
39#include <stdlib.h>
40
41#include "codec.h"
42#include "hdactl.h"
43#include "spec/codec.h"
44#include "spec/fmt.h"
45#include "stream.h"
46
47static int hda_ccmd(hda_codec_t *codec, int node, uint32_t vid, uint32_t payload,
48 uint32_t *resp)
49{
50 uint32_t verb;
51 uint32_t myresp;
52
53 if (resp == NULL)
54 resp = &myresp;
55
56 if ((vid & 0x700) != 0) {
57 verb = (codec->address << 28) |
58 ((node & 0x1ff) << 20) |
59 ((vid & 0xfff) << 8) |
60 (payload & 0xff);
61 } else {
62 verb = (codec->address << 28) |
63 ((node & 0x1ff) << 20) |
64 ((vid & 0xf) << 16) |
65 (payload & 0xffff);
66 }
67 int rc = hda_cmd(codec->hda, verb, resp);
68/*
69 if (resp != NULL) {
70 ddf_msg(LVL_NOTE, "verb 0x%" PRIx32 " -> 0x%" PRIx32, verb,
71 *resp);
72 } else {
73 ddf_msg(LVL_NOTE, "verb 0x%" PRIx32, verb);
74 }
75*/
76 return rc;
77}
78
79static int hda_get_parameter(hda_codec_t *codec, int node, hda_param_id_t param,
80 uint32_t *resp)
81{
82 return hda_ccmd(codec, node, hda_param_get, param, resp);
83}
84
85static int hda_get_subnc(hda_codec_t *codec, int node, int *startnode,
86 int *nodecount)
87{
88 int rc;
89 uint32_t resp;
90
91 rc = hda_get_parameter(codec, node, hda_sub_nc, &resp);
92 if (rc != EOK)
93 return rc;
94
95 *startnode = BIT_RANGE_EXTRACT(uint32_t, subnc_startnode_h,
96 subnc_startnode_l, resp);
97 *nodecount = BIT_RANGE_EXTRACT(uint32_t, subnc_nodecount_h,
98 subnc_nodecount_l, resp);
99
100 return EOK;
101}
102
103/** Get Function Group Type */
104static int hda_get_fgrp_type(hda_codec_t *codec, int node, bool *unsol,
105 hda_fgrp_type_t *type)
106{
107 int rc;
108 uint32_t resp;
109
110 rc = hda_get_parameter(codec, node, hda_fgrp_type, &resp);
111 if (rc != EOK)
112 return rc;
113
114 *unsol = (resp & BIT_V(uint32_t, fgrpt_unsol)) != 0;
115 *type = BIT_RANGE_EXTRACT(uint32_t, fgrpt_type_h, fgrpt_type_l, resp);
116
117 return EOK;
118}
119
120static int hda_get_clist_len(hda_codec_t *codec, int node, bool *longform,
121 int *items)
122{
123 int rc;
124 uint32_t resp;
125
126 rc = hda_get_parameter(codec, node, hda_clist_len, &resp);
127 if (rc != EOK)
128 return rc;
129
130// ddf_msg(LVL_NOTE, "hda_get_clist_len: resp=0x%x", resp);
131 *longform = resp & BIT_V(uint32_t, cll_longform);
132 *items = resp & BIT_RANGE_EXTRACT(uint32_t, cll_len_h, cll_len_l, resp);
133 return EOK;
134}
135
136static int hda_get_clist_entry(hda_codec_t *codec, int node, int n, uint32_t *resp)
137{
138 return hda_ccmd(codec, node, hda_clist_entry_get, n, resp);
139}
140
141static int hda_get_eapd_btl_enable(hda_codec_t *codec, int node, uint32_t *resp)
142{
143 return hda_ccmd(codec, node, hda_eapd_btl_enable_get, 0, resp);
144}
145
146static int hda_set_eapd_btl_enable(hda_codec_t *codec, int node, uint8_t payload)
147{
148 return hda_ccmd(codec, node, hda_eapd_btl_enable_set, payload, NULL);
149}
150
151/** Get Suppported PCM Size, Rates */
152static int hda_get_supp_rates(hda_codec_t *codec, int node, uint32_t *rates)
153{
154 return hda_get_parameter(codec, node, hda_supp_rates, rates);
155}
156
157/** Get Suppported Stream Formats */
158static int hda_get_supp_formats(hda_codec_t *codec, int node, uint32_t *fmts)
159{
160 return hda_get_parameter(codec, node, hda_supp_formats, fmts);
161}
162
163static int hda_set_converter_fmt(hda_codec_t *codec, int node, uint16_t fmt)
164{
165 return hda_ccmd(codec, node, hda_converter_fmt_set, fmt, NULL);
166}
167
168static int hda_set_converter_ctl(hda_codec_t *codec, int node, uint8_t stream,
169 uint8_t channel)
170{
171 uint32_t ctl;
172
173 ctl = (stream << cctl_stream_l) | (channel << cctl_channel_l);
174 return hda_ccmd(codec, node, hda_converter_ctl_set, ctl, NULL);
175}
176
177static int hda_set_pin_ctl(hda_codec_t *codec, int node, uint8_t pctl)
178{
179 return hda_ccmd(codec, node, hda_pin_ctl_set, pctl, NULL);
180}
181
182static int hda_get_pin_ctl(hda_codec_t *codec, int node, uint8_t *pctl)
183{
184 int rc;
185 uint32_t resp;
186
187 rc = hda_ccmd(codec, node, hda_pin_ctl_get, 0, &resp);
188 if (rc != EOK)
189 return rc;
190
191 *pctl = resp;
192 return EOK;
193}
194
195/** Get Audio Widget Capabilities */
196static int hda_get_aw_caps(hda_codec_t *codec, int node,
197 hda_awidget_type_t *type, uint32_t *caps)
198{
199 int rc;
200 uint32_t resp;
201
202 rc = hda_get_parameter(codec, node, hda_aw_caps, &resp);
203 if (rc != EOK)
204 return rc;
205
206 *type = BIT_RANGE_EXTRACT(uint32_t, awc_type_h, awc_type_l, resp);
207 *caps = resp;
208
209 return EOK;
210}
211
212/** Get Pin Capabilities */
213static int hda_get_pin_caps(hda_codec_t *codec, int node, uint32_t *caps)
214{
215 return hda_get_parameter(codec, node, hda_pin_caps, caps);
216}
217
218/** Get Power State */
219static int hda_get_power_state(hda_codec_t *codec, int node, uint32_t *pstate)
220{
221 return hda_ccmd(codec, node, hda_power_state_get, 0, pstate);
222}
223
224/** Get Configuration Default */
225static int hda_get_cfg_def(hda_codec_t *codec, int node, uint32_t *cfgdef)
226{
227 return hda_ccmd(codec, node, hda_cfg_def_get, 0, cfgdef);
228}
229
230static int hda_get_conn_sel(hda_codec_t *codec, int node, uint32_t *conn)
231{
232 return hda_ccmd(codec, node, hda_conn_sel_get, 0, conn);
233}
234
235/** Get Amplifier Gain / Mute */
236static int hda_get_amp_gain_mute(hda_codec_t *codec, int node, uint16_t payload,
237 uint32_t *resp)
238{
239// ddf_msg(LVL_NOTE, "hda_get_amp_gain_mute(codec, %d, %x)",
240// node, payload);
241 int rc = hda_ccmd(codec, node, hda_amp_gain_mute_get, payload, resp);
242// ddf_msg(LVL_NOTE, "hda_get_amp_gain_mute(codec, %d, %x, resp=%x)",
243// node, payload, *resp);
244 return rc;
245}
246
247/** Get GP I/O Count */
248static int hda_get_gpio_cnt(hda_codec_t *codec, int node, uint32_t *resp)
249{
250 return hda_get_parameter(codec, node, hda_gpio_cnt, resp);
251}
252
253static int hda_set_amp_gain_mute(hda_codec_t *codec, int node, uint16_t payload)
254{
255// ddf_msg(LVL_NOTE, "hda_set_amp_gain_mute(codec, %d, %x)",
256// node, payload);
257 return hda_ccmd(codec, node, hda_amp_gain_mute_set, payload, NULL);
258}
259
260static int hda_set_out_amp_max(hda_codec_t *codec, uint8_t aw)
261{
262 uint32_t ampcaps;
263 uint32_t gmleft, gmright;
264 uint32_t offset;
265 int rc;
266
267 rc = hda_get_parameter(codec, aw,
268 hda_out_amp_caps, &ampcaps);
269 if (rc != EOK)
270 goto error;
271
272 offset = ampcaps & 0x7f;
273 ddf_msg(LVL_NOTE, "out amp caps 0x%x (offset=0x%x)",
274 ampcaps, offset);
275
276 rc = hda_set_amp_gain_mute(codec, aw, 0xb000 + offset);
277 if (rc != EOK)
278 goto error;
279
280 rc = hda_get_amp_gain_mute(codec, aw, 0x8000, &gmleft);
281 if (rc != EOK)
282 goto error;
283
284 rc = hda_get_amp_gain_mute(codec, aw, 0xa000, &gmright);
285 if (rc != EOK)
286 goto error;
287
288 ddf_msg(LVL_NOTE, "gain/mute: L:0x%x R:0x%x", gmleft, gmright);
289
290 return EOK;
291error:
292 return rc;
293}
294
295static int hda_set_in_amp_max(hda_codec_t *codec, uint8_t aw)
296{
297 uint32_t ampcaps;
298 uint32_t gmleft, gmright;
299 uint32_t offset;
300 int i;
301 int rc;
302
303 rc = hda_get_parameter(codec, aw,
304 hda_out_amp_caps, &ampcaps);
305 if (rc != EOK)
306 goto error;
307
308 offset = ampcaps & 0x7f;
309 ddf_msg(LVL_NOTE, "in amp caps 0x%x (offset=0x%x)", ampcaps, offset);
310
311 for (i = 0; i < 15; i++) {
312 rc = hda_set_amp_gain_mute(codec, aw, 0x7000 + (i << 8) + offset);
313 if (rc != EOK)
314 goto error;
315
316 rc = hda_get_amp_gain_mute(codec, aw, 0x0000 + i, &gmleft);
317 if (rc != EOK)
318 goto error;
319
320 rc = hda_get_amp_gain_mute(codec, aw, 0x2000 + i, &gmright);
321 if (rc != EOK)
322 goto error;
323
324 ddf_msg(LVL_NOTE, "in:%d gain/mute: L:0x%x R:0x%x",
325 i, gmleft, gmright);
326 }
327
328 return EOK;
329error:
330 return rc;
331}
332
333static int hda_clist_dump(hda_codec_t *codec, uint8_t aw)
334{
335 int rc;
336 bool longform;
337 int len;
338 uint32_t resp;
339 uint32_t mask;
340 uint32_t cidx;
341 int shift;
342 int epresp;
343 int i, j;
344
345 ddf_msg(LVL_NOTE, "Connections for widget %d:", aw);
346
347 rc = hda_get_clist_len(codec, aw, &longform, &len);
348 if (rc != EOK) {
349 ddf_msg(LVL_ERROR, "Failed getting connection list length.");
350 return rc;
351 }
352
353 if (len > 1) {
354 rc = hda_get_conn_sel(codec, aw, &cidx);
355 if (rc != EOK) {
356 ddf_msg(LVL_ERROR, "Failed getting connection select");
357 return rc;
358 }
359 } else {
360 cidx = 0;
361 }
362
363// ddf_msg(LVL_NOTE, "longform:%d len:%d", longform, len);
364
365 if (longform) {
366 epresp = 2;
367 mask = 0xffff;
368 shift = 16;
369 } else {
370 epresp = 4;
371 mask = 0xff;
372 shift = 8;
373 }
374
375 i = 0;
376 while (i < len) {
377 rc = hda_get_clist_entry(codec, aw, i, &resp);
378 if (rc != EOK) {
379 ddf_msg(LVL_ERROR, "Failed getting connection list entry.");
380 return rc;
381 }
382
383 for (j = 0; j < epresp && i < len; j++) {
384 ddf_msg(LVL_NOTE, "<- %d%s", resp & mask,
385 (int)cidx == i ? " *** current *** " : "");
386 resp = resp >> shift;
387 ++i;
388 }
389
390 }
391
392 return rc;
393}
394
395static int hda_pin_init(hda_codec_t *codec, uint8_t aw)
396{
397 int rc;
398 uint32_t cfgdef;
399 uint32_t pcaps;
400 uint32_t eapd;
401 uint8_t pctl;
402
403 rc = hda_get_cfg_def(codec, aw, &cfgdef);
404 if (rc != EOK)
405 goto error;
406 ddf_msg(LVL_NOTE, "aw %d: PIN cdfgef=0x%x",
407 aw, cfgdef);
408
409 rc = hda_get_pin_caps(codec, aw, &pcaps);
410 if (rc != EOK)
411 goto error;
412 ddf_msg(LVL_NOTE, "aw %d : PIN caps=0x%x",
413 aw, pcaps);
414
415 if ((pcaps & BIT_V(uint32_t, pwc_eapd)) != 0) {
416 rc = hda_get_eapd_btl_enable(codec, aw, &eapd);
417 if (rc != EOK)
418 goto error;
419
420 ddf_msg(LVL_NOTE, "PIN %d had EAPD value=0x%x", aw, eapd);
421
422 rc = hda_set_eapd_btl_enable(codec, aw, eapd | 2);
423 if (rc != EOK)
424 goto error;
425
426 rc = hda_get_eapd_btl_enable(codec, aw, &eapd);
427 if (rc != EOK)
428 goto error;
429
430 ddf_msg(LVL_NOTE, "PIN %d now has EAPD value=0x%x", aw, eapd);
431 }
432
433 pctl = 0;
434 if ((pcaps & BIT_V(uint32_t, pwc_output)) != 0) {
435 ddf_msg(LVL_NOTE, "PIN %d will enable output", aw);
436 pctl = pctl | BIT_V(uint8_t, pctl_out_enable);
437 }
438
439 if ((pcaps & BIT_V(uint32_t, pwc_hpd)) != 0) {
440 ddf_msg(LVL_NOTE, "PIN %d will enable headphone drive", aw);
441 pctl = pctl | BIT_V(uint8_t, pctl_hpd_enable);
442 }
443
444/* if ((pcaps & BIT_V(uint32_t, pwc_input)) != 0) {
445 ddf_msg(LVL_NOTE, "PIN %d will enable input");
446 pctl = pctl | BIT_V(uint8_t, pctl_input_enable);
447 }
448*/
449 ddf_msg(LVL_NOTE, "Setting PIN %d ctl to 0x%x", aw, pctl);
450 rc = hda_set_pin_ctl(codec, aw, pctl);
451 if (rc != EOK)
452 goto error;
453
454 pctl = 0;
455 rc = hda_get_pin_ctl(codec, aw, &pctl);
456 if (rc != EOK)
457 goto error;
458
459 ddf_msg(LVL_NOTE, "PIN %d ctl reads as 0x%x", aw, pctl);
460
461 return EOK;
462error:
463 return rc;
464}
465
466/** Init power-control in wiget capable of doing so. */
467static int hda_power_ctl_init(hda_codec_t *codec, uint8_t aw)
468{
469 int rc;
470 uint32_t pwrstate;
471
472 ddf_msg(LVL_NOTE, "aw %d is power control-capable", aw);
473
474 rc = hda_get_power_state(codec, aw, &pwrstate);
475 if (rc != EOK)
476 goto error;
477 ddf_msg(LVL_NOTE, "aw %d: power state = 0x%x", aw, pwrstate);
478
479 return EOK;
480error:
481 return rc;
482}
483
484hda_codec_t *hda_codec_init(hda_t *hda, uint8_t address)
485{
486 hda_codec_t *codec;
487 int rc;
488 int sfg, nfg;
489 int saw, naw;
490 int fg, aw;
491 bool unsol;
492 hda_fgrp_type_t grptype;
493 hda_awidget_type_t awtype;
494 uint32_t awcaps;
495 uint32_t rates;
496 uint32_t formats;
497 uint32_t gpio;
498
499 codec = calloc(1, sizeof(hda_codec_t));
500 if (codec == NULL)
501 return NULL;
502
503 codec->hda = hda;
504 codec->address = address;
505
506 rc = hda_get_subnc(codec, 0, &sfg, &nfg);
507 if (rc != EOK)
508 goto error;
509
510 ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
511 ddf_msg(LVL_NOTE, "sfg=%d nfg=%d", sfg, nfg);
512
513 for (fg = sfg; fg < sfg + nfg; fg++) {
514 ddf_msg(LVL_NOTE, "Enumerate FG %d", fg);
515
516 rc = hda_get_fgrp_type(codec, fg, &unsol, &grptype);
517 if (rc != EOK)
518 goto error;
519
520 ddf_msg(LVL_NOTE, "hda_get_fgrp_type -> %d", rc);
521 ddf_msg(LVL_NOTE, "unsol: %d, grptype: %d", unsol, grptype);
522
523 rc = hda_get_gpio_cnt(codec, fg, &gpio);
524 if (rc != EOK)
525 goto error;
526
527 ddf_msg(LVL_NOTE, "GPIO: wake=%d unsol=%d gpis=%d gpos=%d gpios=%d",
528 (gpio & BIT_V(uint32_t, 31)) != 0,
529 (gpio & BIT_V(uint32_t, 30)) != 0,
530 BIT_RANGE_EXTRACT(uint32_t, 23, 16, gpio),
531 BIT_RANGE_EXTRACT(uint32_t, 15, 8, gpio),
532 BIT_RANGE_EXTRACT(uint32_t, 7, 0, gpio));
533
534 rc = hda_power_ctl_init(codec, fg);
535 if (rc != EOK)
536 goto error;
537
538 rc = hda_get_subnc(codec, fg, &saw, &naw);
539 if (rc != EOK)
540 goto error;
541
542 ddf_msg(LVL_NOTE, "hda_get_subnc -> %d", rc);
543 ddf_msg(LVL_NOTE, "saw=%d baw=%d", saw, naw);
544
545 for (aw = saw; aw < saw + naw; aw++) {
546 rc = hda_get_aw_caps(codec, aw, &awtype, &awcaps);
547 if (rc != EOK)
548 goto error;
549 ddf_msg(LVL_NOTE, "aw %d: type=0x%x caps=0x%x",
550 aw, awtype, awcaps);
551
552 if ((awcaps & BIT_V(uint32_t, awc_power_cntrl)) != 0) {
553 rc = hda_power_ctl_init(codec, aw);
554 if (rc != EOK)
555 goto error;
556 }
557
558 switch (awtype) {
559 case awt_audio_input:
560 case awt_audio_mixer:
561 case awt_audio_selector:
562 case awt_pin_complex:
563 case awt_power_widget:
564 rc = hda_clist_dump(codec, aw);
565 if (rc != EOK)
566 goto error;
567 break;
568 default:
569 break;
570 }
571
572 if (awtype == awt_pin_complex) {
573 rc = hda_pin_init(codec, aw);
574 if (rc != EOK)
575 goto error;
576 } else if (awtype == awt_audio_output) {
577 codec->out_aw_list[codec->out_aw_num++] = aw;
578
579 rc = hda_get_supp_rates(codec, aw, &rates);
580 if (rc != EOK)
581 goto error;
582
583 rc = hda_get_supp_formats(codec, aw, &formats);
584 if (rc != EOK)
585 goto error;
586
587 ddf_msg(LVL_NOTE, "Output widget %d: rates=0x%x formats=0x%x",
588 aw, rates, formats);
589 }
590
591 if ((awcaps & BIT_V(uint32_t, awc_out_amp_present)) != 0)
592 hda_set_out_amp_max(codec, aw);
593
594 if ((awcaps & BIT_V(uint32_t, awc_in_amp_present)) != 0)
595 hda_set_in_amp_max(codec, aw);
596 }
597 }
598
599 hda_ctl_dump_info(hda->ctl);
600
601 ddf_msg(LVL_NOTE, "Codec OK");
602 return codec;
603error:
604 free(codec);
605 return NULL;
606}
607
608void hda_codec_fini(hda_codec_t *codec)
609{
610 ddf_msg(LVL_NOTE, "hda_codec_fini()");
611 free(codec);
612}
613
614int hda_out_converter_setup(hda_codec_t *codec, hda_stream_t *stream)
615{
616 int rc;
617 int out_aw;
618 int i;
619
620 for (i = 0; i < codec->out_aw_num; i++) {
621 out_aw = codec->out_aw_list[i];
622
623 /* Configure converter */
624
625 ddf_msg(LVL_NOTE, "Configure converter format");
626 rc = hda_set_converter_fmt(codec, out_aw, stream->fmt);
627 if (rc != EOK)
628 goto error;
629
630 ddf_msg(LVL_NOTE, "Configure converter stream, channel");
631 rc = hda_set_converter_ctl(codec, out_aw, stream->sid, 0);
632 if (rc != EOK)
633 goto error;
634 }
635
636 return EOK;
637error:
638 return rc;
639}
640
641/** @}
642 */
Note: See TracBrowser for help on using the repository browser.