1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdio.h>
6#include "codec_state.h"
7
8#include <fbl/algorithm.h>
9
10namespace audio {
11namespace intel_hda {
12
13// Don't allow this code to pollute the rest of the namespace.
14namespace {
15
16const char* ToString(const FunctionGroupState::Type& val) {
17    switch (val) {
18        case FunctionGroupState::Type::AUDIO: return "AUDIO";
19        case FunctionGroupState::Type::MODEM: return "MODEM";
20        default:
21            if ((val >= FunctionGroupState::Type::VENDOR_START) &&
22                (val <= FunctionGroupState::Type::VENDOR_END))
23                return "VENDOR";
24            else
25                return "<unknown>";
26    }
27}
28
29const char* ToString(const AudioWidgetCaps::Type& val) {
30    switch (val) {
31        case AudioWidgetCaps::Type::OUTPUT:      return "OUTPUT";
32        case AudioWidgetCaps::Type::INPUT:       return "INPUT";
33        case AudioWidgetCaps::Type::MIXER:       return "MIXER";
34        case AudioWidgetCaps::Type::SELECTOR:    return "SELECTOR";
35        case AudioWidgetCaps::Type::PIN_COMPLEX: return "PIN_COMPLEX";
36        case AudioWidgetCaps::Type::POWER:       return "POWER";
37        case AudioWidgetCaps::Type::VOLUME_KNOB: return "VOLUME_KNOB";
38        case AudioWidgetCaps::Type::BEEP_GEN:    return "BEEP_GEN";
39        case AudioWidgetCaps::Type::VENDOR:      return "VENDOR";
40        default:                                  return "<unknown>";
41    }
42}
43
44const char* PowerStateToString(uint8_t val) {
45    switch (val) {
46        case 0u: return "D0";
47        case 1u: return "D1";
48        case 2u: return "D2";
49        case 3u: return "D3HOT";
50        case 4u: return "D3COLD";
51        default: return "Unknown";
52    };
53}
54
55void Dump(const AmpCaps& caps, const AudioWidgetState::AmpState* amp_state = nullptr) {
56    float start = 0.0;
57    float stop  = 0.0;
58    float step  = 0.0;
59
60    if (!caps.step_size() || !caps.num_steps()) {
61        printf("none\n");
62        return;
63    } else if (caps.num_steps() == 1) {
64        printf("fixed 0 dB gain");
65    } else {
66        step  = (static_cast<float>(caps.step_size()) / 4.0f);
67        start = -static_cast<float>(caps.offset()) * step;
68        stop  = start + ((static_cast<float>(caps.num_steps()) - 1) * step);
69        printf("[%.2f, %.2f] dB in %.2f dB steps", start, stop, step);
70    }
71
72    printf(" (Can%s mute)", caps.can_mute() ? "" : "'t");
73
74    if (amp_state != nullptr) {
75        printf(" [");
76        for (size_t i = 0; i < fbl::count_of(amp_state->gain); ++i) {
77            if (i) printf(", ");
78            printf("%c:", !i ? 'L' : 'R');
79
80            if (caps.can_mute() && amp_state->mute[i]) {
81                printf("mute");
82            } else {
83                printf("%.2f dB", start + (step * amp_state->gain[0]));
84            }
85        }
86
87        printf("]");
88    }
89
90    printf("\n");
91}
92
93void Dump(const AudioWidgetState::StreamFormat& format) {
94    if (!format.is_pcm()) {
95        printf("Non-PCM (raw 0x%04hx)\n", format.raw_data_);
96        return;
97    }
98
99    printf("%u chan %u Hz %u bps (raw 0x%04hx)\n",
100        format.channels(),
101        format.sample_rate(),
102        format.bits_per_chan(),
103        format.raw_data_);
104}
105
106#define FMT(fmt) "%s%17s : " fmt, pad
107void Dump(const ConfigDefaults& cfg) {
108    static const char* pad = "+     \\-- ";
109    const char *tmp, *tmp2;
110
111    // Table 109
112    switch (cfg.port_connectivity()) {
113    case 0:  tmp = "Jack"; break;
114    case 1:  tmp = "Unconnected"; break;
115    case 2:  tmp = "Integrated"; break;
116    case 3:  tmp = "Jack+Integrated"; break;
117    default: tmp = "ERROR"; break;
118    }
119
120    printf(FMT("%s (%u)\n"), "Port Connectivity", tmp, cfg.port_connectivity());
121
122    // Table 110
123    uint32_t loc1 =  cfg.location() & 0xF;
124    uint32_t loc2 = (cfg.location() >> 4) & 0x3;
125    switch (loc1) {
126    case 0:  tmp = "N/A"; break;
127    case 1:  tmp = "Rear"; break;
128    case 2:  tmp = "Front"; break;
129    case 3:  tmp = "Left"; break;
130    case 4:  tmp = "Right"; break;
131    case 5:  tmp = "Top"; break;
132    case 6:  tmp = "Bottom"; break;
133    case 7:
134    case 8:
135    case 9:  tmp = "Special"; break;
136    default: tmp = "Unknown"; break;
137    }
138
139    switch (loc2) {
140    case 0:  tmp2 = " External"; break;
141    case 1:  tmp2 = " Internal"; break;
142    case 2:  tmp2 = " Separate Chassis"; break;
143    case 3:  tmp2 = " Other"; break;
144    default: tmp2 = " ERROR"; break;
145    }
146
147    switch (cfg.location()) {
148    case 0x07: tmp2 = ""; tmp = "Rear Panel"; break;
149    case 0x08: tmp2 = ""; tmp = "Drive Bay"; break;
150    case 0x17: tmp2 = ""; tmp = "Riser"; break;
151    case 0x18: tmp2 = ""; tmp = "Digital Display"; break;
152    case 0x19: tmp2 = ""; tmp = "ATAPI"; break;
153    case 0x37: tmp2 = ""; tmp = "Mobile Lid - Inside"; break;
154    case 0x38: tmp2 = ""; tmp = "Mobile Lid - Outside"; break;
155    default: break;
156    }
157
158    printf(FMT("%s%s (0x%02x)\n"), "Location", tmp, tmp2, cfg.location());
159
160    // Table 111
161    switch (cfg.default_device()) {
162    case 0x0: tmp = "Line Out"; break;
163    case 0x1: tmp = "Speaker"; break;
164    case 0x2: tmp = "Headphone Out"; break;
165    case 0x3: tmp = "CD"; break;
166    case 0x4: tmp = "S/PDIF Out"; break;
167    case 0x5: tmp = "Digital Other Out"; break;
168    case 0x6: tmp = "Modem Line Side"; break;
169    case 0x7: tmp = "Modem Handset Side"; break;
170    case 0x8: tmp = "Line In"; break;
171    case 0x9: tmp = "AUX"; break;
172    case 0xa: tmp = "Mic In"; break;
173    case 0xb: tmp = "Telephony"; break;
174    case 0xc: tmp = "S/PDIF In"; break;
175    case 0xd: tmp = "Digital Other In"; break;
176    case 0xf: tmp = "Other"; break;
177    default:  tmp = "Unknown"; break;
178    }
179
180    printf(FMT("%s (%u)\n"), "Default Device", tmp, cfg.default_device());
181
182    // Table 112
183    switch (cfg.connection_type()) {
184    case 0x1: tmp = "1/8 inch"; break;
185    case 0x2: tmp = "1/4 inch"; break;
186    case 0x3: tmp = "ATAPI Internal"; break;
187    case 0x4: tmp = "RCA"; break;
188    case 0x5: tmp = "Optical"; break;
189    case 0x6: tmp = "Other Digital"; break;
190    case 0x7: tmp = "Other Analog"; break;
191    case 0x8: tmp = "Multichannel Analog (DIN)"; break;
192    case 0x9: tmp = "XLR/Pro"; break;
193    case 0xa: tmp = "RJ-11 (Modem)"; break;
194    case 0xb: tmp = "Combination"; break;
195    case 0xf: tmp = "Other"; break;
196    default:  tmp = "Unknown"; break;
197    }
198
199    printf(FMT("%s (%u)\n"), "Connection Type", tmp, cfg.connection_type());
200
201    // Table 113
202    switch (cfg.color()) {
203    case 0x1: tmp = "Black"; break;
204    case 0x2: tmp = "Grey"; break;
205    case 0x3: tmp = "Blue"; break;
206    case 0x4: tmp = "Green"; break;
207    case 0x5: tmp = "Red"; break;
208    case 0x6: tmp = "Orange"; break;
209    case 0x7: tmp = "Yellow"; break;
210    case 0x8: tmp = "Purple"; break;
211    case 0x9: tmp = "Pink"; break;
212    case 0xe: tmp = "White"; break;
213    case 0xf: tmp = "Other"; break;
214    default:  tmp = "Unknown"; break;
215    }
216
217    printf(FMT("%s (%u)\n"), "Color", tmp, cfg.color());
218
219    // Associations and Flags
220    printf(FMT("Assoc Group (%u) Assoc Seq (%u)%s\n"), "Assoc/Flags",
221            cfg.default_assoc(),
222            cfg.sequence(),
223            cfg.misc() & 0x1 ? " JackDetectOverride" : "");
224
225}
226#undef FMT
227
228typedef struct flag_lut_entry {
229    uint32_t    flag_bit;
230    const char* flag_name;
231} flag_lut_entry_t;
232
233static void ihda_dump_delay(uint8_t delay) {
234    if (delay)
235        printf("%u samples\n", delay);
236    else
237        printf("unknown\n");
238}
239
240static void ihda_dump_flags(uint32_t flags,
241                            const flag_lut_entry_t* table,
242                            size_t table_size,
243                            const char* suffix,
244                            const char* no_flags_text) {
245    bool got_one = false;
246    for (size_t i = 0; i < table_size; ++i) {
247        if (flags & table[i].flag_bit) {
248            printf("%s%s", got_one ? " " : "", table[i].flag_name);
249            got_one = true;
250        }
251    }
252
253    printf("%s\n", got_one ? suffix : no_flags_text);
254}
255
256static const flag_lut_entry_t POWER_STATE_FLAGS[] = {
257    { IHDA_PWR_STATE_EPSS,     "EPSS" },
258    { IHDA_PWR_STATE_CLKSTOP,  "CLKSTOP" },
259    { IHDA_PWR_STATE_S3D3COLD, "S3D3COLD" },
260    { IHDA_PWR_STATE_D3COLD,   "D3COLD" },
261    { IHDA_PWR_STATE_D3,       "D3HOT" },
262    { IHDA_PWR_STATE_D2,       "D2" },
263    { IHDA_PWR_STATE_D1,       "D1" },
264    { IHDA_PWR_STATE_D0,       "D0" },
265};
266
267static const flag_lut_entry_t PCM_RATE_FLAGS[] = {
268    { IHDA_PCM_RATE_384000, "384000" },
269    { IHDA_PCM_RATE_192000, "192000" },
270    { IHDA_PCM_RATE_176400, "176400" },
271    { IHDA_PCM_RATE_96000,   "96000" },
272    { IHDA_PCM_RATE_88200,   "88200" },
273    { IHDA_PCM_RATE_48000,   "48000" },
274    { IHDA_PCM_RATE_44100,   "44100" },
275    { IHDA_PCM_RATE_32000,   "32000" },
276    { IHDA_PCM_RATE_22050,   "22050" },
277    { IHDA_PCM_RATE_16000,   "16000" },
278    { IHDA_PCM_RATE_11025,   "11025" },
279    { IHDA_PCM_RATE_8000,     "8000" },
280};
281
282static const flag_lut_entry_t PCM_SIZE_FLAGS[] = {
283    { IHDA_PCM_SIZE_32BITS, "32" },
284    { IHDA_PCM_SIZE_24BITS, "24" },
285    { IHDA_PCM_SIZE_20BITS, "20" },
286    { IHDA_PCM_SIZE_16BITS, "16" },
287    { IHDA_PCM_SIZE_8BITS,   "8" },
288};
289
290static const flag_lut_entry_t PCM_FMT_FLAGS[] = {
291    { IHDA_PCM_FORMAT_AC3,     "AC3" },
292    { IHDA_PCM_FORMAT_FLOAT32, "FLOAT32" },
293    { IHDA_PCM_FORMAT_PCM,     "PCM" },
294};
295
296static const flag_lut_entry_t AW_CAPS_FLAGS[] = {
297    { AudioWidgetCaps::FLAG_AMP_PARAM_OVERRIDE, "AmpParamOverride" },
298    { AudioWidgetCaps::FLAG_FORMAT_OVERRIDE,    "FormatOverride" },
299    { AudioWidgetCaps::FLAG_STRIPE_SUPPORTED,   "StripingSupported" },
300    { AudioWidgetCaps::FLAG_PROC_WIDGET,        "HasProcessingControls" },
301    { AudioWidgetCaps::FLAG_CAN_SEND_UNSOL,     "CanSendUnsolicited" },
302    { AudioWidgetCaps::FLAG_DIGITAL,            "Digital" },
303    { AudioWidgetCaps::FLAG_CAN_LR_SWAP,        "CanSwapLR" },
304    { AudioWidgetCaps::FLAG_HAS_CONTENT_PROT,   "HasContentProtection" },
305};
306
307static const flag_lut_entry_t PIN_CAPS_FLAGS[] = {
308    { AW_PIN_CAPS_FLAG_CAN_IMPEDANCE_SENSE,  "ImpedanceSense" },
309    { AW_PIN_CAPS_FLAG_TRIGGER_REQUIRED,     "TrigReq" },
310    { AW_PIN_CAPS_FLAG_CAN_PRESENCE_DETECT,  "PresDetect" },
311    { AW_PIN_CAPS_FLAG_CAN_DRIVE_HEADPHONES, "HeadphoneDrive" },
312    { AW_PIN_CAPS_FLAG_CAN_OUTPUT,           "CanOutput" },
313    { AW_PIN_CAPS_FLAG_CAN_INPUT,            "CanInput" },
314    { AW_PIN_CAPS_FLAG_BALANCED_IO,          "Balanced" },
315    { AW_PIN_CAPS_FLAG_HDMI,                 "HDMI" },
316    { AW_PIN_CAPS_FLAG_VREF_HIZ,             "VREF_HIZ" },
317    { AW_PIN_CAPS_FLAG_VREF_50_PERCENT,      "VREF_50%" },
318    { AW_PIN_CAPS_FLAG_VREF_GROUND,          "VREF_GND" },
319    { AW_PIN_CAPS_FLAG_VREF_80_PERCENT,      "VREF_80%" },
320    { AW_PIN_CAPS_FLAG_VREF_100_PERCENT,     "VREF_100%" },
321    { AW_PIN_CAPS_FLAG_CAN_EAPD,             "EAPD" },
322    { AW_PIN_CAPS_FLAG_DISPLAY_PORT,         "DisplayPort" },
323    { AW_PIN_CAPS_FLAG_HIGH_BIT_RATE,        "HighBitRate" },
324};
325
326#define DUMP_FLAGS(flags, table, suffix, no_flags_text) \
327    ihda_dump_flags(flags, table, fbl::count_of(table), suffix, no_flags_text)
328
329static void ihda_dump_conn_list(const AudioWidgetState& widget) {
330    if (!widget.conn_list_len_) {
331        printf("empty\n");
332        return;
333    }
334
335    ZX_DEBUG_ASSERT(widget.conn_list_);
336    for (uint32_t i = 0; i < widget.conn_list_len_; ++i) {
337        if(i > 0)
338            printf(" ");
339
340        const auto& first = widget.conn_list_[i];
341        ZX_DEBUG_ASSERT(!first.range_);
342
343        // Is this entry the start of a range or not?
344        if ((i + 1) < widget.conn_list_len_) {
345            const auto& second = widget.conn_list_[i + 1];
346            if (second.range_) {
347                printf("[%hu, %hu]", first.nid_, second.nid_);
348                i++;
349                continue;
350            }
351        }
352
353        printf("%hu", first.nid_);
354    }
355
356    // Mixers are always connected to all of the inputs on their connection lists.
357    if (widget.caps_.type() != AudioWidgetCaps::Type::MIXER) {
358        if (widget.connected_nid_ndx_ < widget.conn_list_len_) {
359            printf(" : [*%hu, ndx %u]\n", widget.connected_nid_, widget.connected_nid_ndx_);
360        } else {
361            printf(" : [*INVALID, ndx %u]\n", widget.connected_nid_ndx_);
362        }
363    } else {
364        printf("\n");
365    }
366
367}
368
369#define FMT(fmt) "%s%20s : " fmt, pad
370static void ihda_dump_widget(const AudioWidgetState& widget, uint32_t id, uint32_t count) {
371    static const char* pad = "+----- ";
372
373    printf("%sWidget %u/%u\n", pad, id, count);
374    printf(FMT("%hu\n"), "Node ID", widget.nid_);
375    printf(FMT("[%02x] %s\n"),  "Type",
376            static_cast<uint32_t>(widget.caps_.type()),
377            ToString(widget.caps_.type()));
378
379    printf(FMT("%08x\n"), "Raw Caps", widget.caps_.raw_data_);
380
381    printf(FMT(""), "Flags");
382    DUMP_FLAGS(widget.caps_.raw_data_, AW_CAPS_FLAGS, "", "none");
383
384    if (widget.caps_.can_send_unsol()) {
385        printf(FMT("%s [tag 0x%02x]\n"),  "Unsolicited Ctrl",
386                   widget.unsol_resp_ctrl_.enabled() ? "enabled" : "disabled",
387                   widget.unsol_resp_ctrl_.tag());
388    }
389
390    printf(FMT(""), "Delay");
391    ihda_dump_delay(widget.caps_.delay());
392
393    printf(FMT("%u\n"), "MaxChan", widget.caps_.ch_count());
394
395    if (widget.caps_.input_amp_present()) {
396        if (widget.caps_.type() != AudioWidgetCaps::Type::MIXER) {
397            printf(FMT(""), "InputAmp");
398            Dump(widget.input_amp_caps_, &widget.input_amp_state_);
399        } else {
400            for (uint8_t i = 0; i < widget.conn_list_len_; ++i) {
401                char tag[32];
402                snprintf(tag, fbl::count_of(tag), "InputAmp[nid %hu]", widget.conn_list_[i].nid_);
403                printf(FMT(""), tag);
404                Dump(widget.input_amp_caps_, &widget.conn_list_[i].amp_state_);
405            }
406        }
407    }
408
409    if (widget.caps_.output_amp_present()) {
410        printf(FMT(""), "OutputAmp");
411        Dump(widget.output_amp_caps_, &widget.output_amp_state_);
412    }
413
414    if (widget.caps_.format_override()) {
415        printf(FMT(""), "PCM Rates");
416        DUMP_FLAGS(widget.pcm_size_rate_, PCM_RATE_FLAGS, "", "none");
417
418        printf(FMT(""), "PCM Sizes");
419        DUMP_FLAGS(widget.pcm_size_rate_, PCM_SIZE_FLAGS, " bits", "none");
420
421        printf(FMT(""), "PCM Formats");
422        DUMP_FLAGS(widget.pcm_formats_, PCM_FMT_FLAGS, "", "none");
423    }
424
425    if ((widget.caps_.type() == AudioWidgetCaps::Type::INPUT) ||
426        (widget.caps_.type() == AudioWidgetCaps::Type::OUTPUT)) {
427        printf(FMT(""), "Cur Format");
428        Dump(widget.cur_format_);
429        printf(FMT("tag (%u) chan (%u)\n"), "Tag/Chan", widget.stream_tag_, widget.stream_chan_);
430    }
431
432    if (widget.caps_.type() == AudioWidgetCaps::Type::PIN_COMPLEX) {
433        if (widget.pin_sense_valid_) {
434            const char* pstring = widget.pin_sense_.presence_detect() ? "Plugged" : "Unplugged";
435            if (widget.caps_.digital()) {
436                printf(FMT("%s, ELD %s [raw 0x%08x]\n"), "Pin Sense",
437                        pstring,
438                        widget.pin_sense_.eld_valid() ? "Valid" : "Invalid",
439                        widget.pin_sense_.raw_data_);
440            } else {
441                if (widget.pin_caps_ & AW_PIN_CAPS_FLAG_CAN_IMPEDANCE_SENSE) {
442                    printf(FMT("%s, Impedance %u [raw 0x%08x]\n"), "Pin Sense",
443                            pstring, widget.pin_sense_.impedance(), widget.pin_sense_.raw_data_);
444                } else {
445                    printf(FMT("%s [raw 0x%08x]\n"), "Pin Sense",
446                            pstring, widget.pin_sense_.raw_data_);
447                }
448            }
449        }
450
451        printf(FMT(""), "Pin Caps");
452        DUMP_FLAGS(widget.pin_caps_, PIN_CAPS_FLAGS, "", "none");
453    }
454
455   if (widget.caps_.can_lr_swap())
456       printf(FMT("%s\n"), "L/R Swap", widget.eapd_state_.lr_swap() ? "Swapped" : "Normal");
457
458   if (widget.caps_.type() == AudioWidgetCaps::Type::PIN_COMPLEX) {
459       if (widget.pin_caps_ & AW_PIN_CAPS_FLAG_CAN_INPUT)
460           printf(FMT("%s\n"), "Input",
461                   widget.pin_widget_ctrl_.input_enb() ? "Enabled" : "Disabled");
462
463       if (widget.pin_caps_ & AW_PIN_CAPS_FLAG_CAN_OUTPUT)
464           printf(FMT("%s\n"), "Output",
465                   widget.pin_widget_ctrl_.output_enb() ? "Enabled" : "Disabled");
466
467       if (widget.pin_caps_ & AW_PIN_CAPS_FLAG_CAN_DRIVE_HEADPHONES)
468           printf(FMT("%s\n"), "Headphone Amp",
469                   widget.pin_widget_ctrl_.hp_amp_enb() ? "Enabled" : "Disabled");
470
471       if (!widget.caps_.digital() &&
472           (widget.pin_caps_ & (AW_PIN_CAPS_FLAG_VREF_HIZ         |
473                                AW_PIN_CAPS_FLAG_VREF_50_PERCENT  |
474                                AW_PIN_CAPS_FLAG_VREF_GROUND      |
475                                AW_PIN_CAPS_FLAG_VREF_80_PERCENT  |
476                                AW_PIN_CAPS_FLAG_VREF_100_PERCENT))) {
477           const char* tmp;
478           switch (widget.pin_widget_ctrl_.vref_enb()) {
479           case VRefEn::HiZ:  tmp = "Hi-Z";     break;
480           case VRefEn::P50:  tmp = "50%";      break;
481           case VRefEn::Gnd:  tmp = "Grounded"; break;
482           case VRefEn::P80:  tmp = "80%";      break;
483           case VRefEn::P100: tmp = "100%";     break;
484           default:           tmp = "Unknown";  break;
485           }
486           printf(FMT("%s\n"), "VRef", tmp);
487       }
488
489       if (widget.caps_.digital()) {
490           const char* tmp;
491           switch (widget.pin_widget_ctrl_.ept()) {
492           case EPT::Native: tmp = "Native";        break;
493           case EPT::HBR:    tmp = "High Bit Rate"; break;
494           default:          tmp = "Unknown";       break;
495           }
496           printf(FMT("%s\n"), "Encoded Pkt Type", tmp);
497       }
498
499       if (widget.pin_caps_ & AW_PIN_CAPS_FLAG_BALANCED_IO)
500           printf(FMT("%s\n"), "Balanced Output", widget.eapd_state_.btl() ? "Yes" : "No");
501
502       if (widget.pin_caps_ & AW_PIN_CAPS_FLAG_CAN_EAPD)
503           printf(FMT("Powered %s\n"), "External Amp", widget.eapd_state_.eapd() ? "Up" : "Down");
504
505        printf(FMT("0x%08x\n"), "Raw Cfg Defaults", widget.cfg_defaults_.raw_data_);
506        Dump(widget.cfg_defaults_);
507    }
508
509    if (widget.caps_.has_power_ctl()) {
510        printf(FMT(""), "Sup. Pwr States");
511        DUMP_FLAGS(widget.power_.supported_states_, POWER_STATE_FLAGS, "", "none");
512        printf(FMT("Set %s(%u) Active %s(%u)%s%s%s\n"), "Cur Pwr State",
513                    PowerStateToString(widget.power_.set_), widget.power_.set_,
514                    PowerStateToString(widget.power_.active_), widget.power_.active_,
515                    widget.power_.error_ ? " [ERROR]" : "",
516                    widget.power_.clock_stop_ok_ ? " [ClkStopOK]" : "",
517                    widget.power_.settings_reset_ ? " [Settings Reset]" : "");
518    }
519
520    if (widget.caps_.has_conn_list()) {
521        printf(FMT(""), "ConnList");
522        ihda_dump_conn_list(widget);
523    }
524
525    if (widget.caps_.proc_widget()) {
526        printf(FMT("%s\n"), "Can Bypass Proc", widget.can_bypass_processing_ ? "yes" : "no");
527        printf(FMT("%u\n"), "Proc Coefficients", widget.processing_coefficient_count_);
528    }
529
530    if (widget.caps_.type() == AudioWidgetCaps::Type::VOLUME_KNOB) {
531        printf(FMT("%s\n"), "Vol Knob Type",  widget.vol_knob_is_delta_ ? "delta" : "absolute");
532        printf(FMT("%u\n"), "Vol Knob Steps", widget.vol_knob_steps_);
533    }
534
535    printf("%s\n", pad);
536}
537#undef FMT
538
539#define FMT(fmt) "%s%26s : " fmt, pad
540static void ihda_dump_codec_fn_group(const CodecState& codec, uint32_t id) {
541    ZX_DEBUG_ASSERT(codec.fn_groups_ && (id < codec.fn_group_count_) && codec.fn_groups_[id]);
542    static const char* pad = "+--- ";
543    const auto& fn_group = *codec.fn_groups_[id];
544
545    printf("%sFunction Group %u/%u\n", pad, id + 1, codec.fn_group_count_);
546    printf(FMT("%hu\n"), "Node ID", fn_group.nid_);
547    printf(FMT("%s\n"),  "Type", ToString(fn_group.type_));
548
549    if (fn_group.can_send_unsolicited_) {
550        printf(FMT("%s [tag 0x%02x]\n"),  "Unsolicited Ctrl",
551                   fn_group.unsol_resp_ctrl_.enabled() ? "enabled" : "disabled",
552                   fn_group.unsol_resp_ctrl_.tag());
553    }
554
555    if (fn_group.type_ != FunctionGroupState::Type::AUDIO)
556        return;
557
558    const auto& afg = *reinterpret_cast<AudioFunctionGroupState*>(codec.fn_groups_[id].get());
559
560    printf(FMT("%08x\n"), "Raw Caps", afg.caps_.raw_data_);
561    printf(FMT("%s\n"),   "Beep Gen", afg.caps_.has_beep_gen() ? "yes" : "no");
562
563    printf(FMT(""), "Input Path Delay");
564    ihda_dump_delay(afg.caps_.path_input_delay());
565
566    printf(FMT(""), "Output Path Delay");
567    ihda_dump_delay(afg.caps_.path_output_delay());
568
569    printf(FMT(""), "Default PCM Rates");
570    DUMP_FLAGS(afg.default_pcm_size_rate_, PCM_RATE_FLAGS, "", "none");
571
572    printf(FMT(""), "Default PCM Sizes");
573    DUMP_FLAGS(afg.default_pcm_size_rate_, PCM_SIZE_FLAGS, " bits", "none");
574
575    printf(FMT(""), "Default PCM Formats");
576    DUMP_FLAGS(afg.default_pcm_formats_, PCM_FMT_FLAGS, "", "none");
577
578    printf(FMT(""), "Default Input Amp Caps");
579    Dump(afg.default_input_amp_caps_);
580
581    printf(FMT(""), "Default Output Amp Caps");
582    Dump(afg.default_output_amp_caps_);
583
584    printf(FMT(""), "Sup. Pwr States");
585    DUMP_FLAGS(afg.power_.supported_states_, POWER_STATE_FLAGS, "", "none");
586    printf(FMT("Set %s(%u) Active %s(%u)%s%s%s\n"), "Cur Pwr State",
587                PowerStateToString(afg.power_.set_), afg.power_.set_,
588                PowerStateToString(afg.power_.active_), afg.power_.active_,
589                afg.power_.error_ ? " [ERROR]" : "",
590                afg.power_.clock_stop_ok_ ? " [ClkStopOK]" : "",
591                afg.power_.settings_reset_ ? " [Settings Reset]" : "");
592
593    printf(FMT("%u\n"), "GPIOs", afg.gpio_count_);
594    printf(FMT("%u\n"), "GPIs",  afg.gpi_count_);
595    printf(FMT("%u\n"), "GPOs",  afg.gpo_count_);
596    printf(FMT("%s\n"), "GPIOs can wake", afg.gpio_can_wake_ ? "yes" : "no");
597    printf(FMT("%s\n"), "GPIOs can send unsolicited",
598           afg.gpio_can_send_unsolicited_ ? "yes" : "no");
599
600
601    printf(FMT("BMID(%04hx) BSKU(%02x) AssyID(%02x) : Raw 0x%08x\n"), "Impl ID",
602               fn_group.impl_id_.BoardMfrID(),
603               fn_group.impl_id_.BoardSKU(),
604               fn_group.impl_id_.AssemblyID(),
605               fn_group.impl_id_.raw_data_);
606
607    printf(FMT("%u\n"), "Widgets", afg.widget_count_);
608
609    for (uint32_t i = 0; i < afg.widget_count_; ++i) {
610        ZX_DEBUG_ASSERT(afg.widgets_[i]);
611        ihda_dump_widget(*afg.widgets_[i], i + 1, afg.widget_count_);
612    }
613}
614#undef FMT
615}  // namespace
616
617#define FMT(fmt) "%s%10s : " fmt, pad
618void print_codec_state(const CodecState& codec) {
619    static const char* pad = "+- ";
620
621    printf(FMT("0x%04hx:0x%04hx\n"), "VID/DID", codec.vendor_id_, codec.device_id_);
622    printf(FMT("%u.%u\n"), "Rev", codec.major_rev_, codec.minor_rev_);
623    printf(FMT("%u.%u\n"), "Vendor Rev", codec.vendor_rev_id_, codec.vendor_stepping_id_);
624    printf("%s%u function group%s\n",
625           pad,  codec.fn_group_count_, codec.fn_group_count_ == 1 ? "" : "s");
626
627    for (uint32_t i = 0; i < codec.fn_group_count_; ++i)
628        ihda_dump_codec_fn_group(codec, i);
629}
630#undef FMT
631
632}  // namespace audio
633}  // namespace intel_hda
634