VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevHdaCodec.cpp@ 89805

Last change on this file since 89805 was 89768, checked in by vboxsync, 4 years ago

Audio: Made PDMAUDIOVOLUME multichannel. bugref:9890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 111.1 KB
Line 
1/* $Id: DevHdaCodec.cpp 89768 2021-06-17 23:03:19Z vboxsync $ */
2/** @file
3 * Intel HD Audio Controller Emulation - Codec, Sigmatel/IDT STAC9220.
4 *
5 * Implemented based on the Intel HD Audio specification and the
6 * Sigmatel/IDT STAC9220 datasheet.
7 */
8
9/*
10 * Copyright (C) 2006-2020 Oracle Corporation
11 *
12 * This file is part of VirtualBox Open Source Edition (OSE), as
13 * available from http://www.virtualbox.org. This file is free software;
14 * you can redistribute it and/or modify it under the terms of the GNU
15 * General Public License (GPL) as published by the Free Software
16 * Foundation, in version 2 as it comes in the "COPYING" file of the
17 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
18 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
19 */
20
21
22/*********************************************************************************************************************************
23* Header Files *
24*********************************************************************************************************************************/
25#define LOG_GROUP LOG_GROUP_DEV_HDA_CODEC
26#include <VBox/log.h>
27
28#include <VBox/AssertGuest.h>
29#include <VBox/vmm/pdmdev.h>
30#include <VBox/vmm/pdmaudioifs.h>
31#include <VBox/vmm/pdmaudioinline.h>
32
33#include <iprt/assert.h>
34#include <iprt/uuid.h>
35#include <iprt/string.h>
36#include <iprt/mem.h>
37#include <iprt/asm.h>
38#include <iprt/cpp/utils.h>
39
40#include "VBoxDD.h"
41#include "AudioMixer.h"
42#include "DevHdaCodec.h"
43#include "DevHdaCommon.h"
44
45
46/*********************************************************************************************************************************
47* Defined Constants And Macros *
48*********************************************************************************************************************************/
49
50
51#define AMPLIFIER_IN 0
52#define AMPLIFIER_OUT 1
53#define AMPLIFIER_LEFT 1
54#define AMPLIFIER_RIGHT 0
55#define AMPLIFIER_REGISTER(amp, inout, side, index) ((amp)[30*(inout) + 15*(side) + (index)])
56
57
58/*********************************************************************************************************************************
59* Global Variables *
60*********************************************************************************************************************************/
61/* STAC9220 - Nodes IDs / names. */
62#define STAC9220_NID_ROOT 0x0 /* Root node */
63#define STAC9220_NID_AFG 0x1 /* Audio Configuration Group */
64#define STAC9220_NID_DAC0 0x2 /* Out */
65#define STAC9220_NID_DAC1 0x3 /* Out */
66#define STAC9220_NID_DAC2 0x4 /* Out */
67#define STAC9220_NID_DAC3 0x5 /* Out */
68#define STAC9220_NID_ADC0 0x6 /* In */
69#define STAC9220_NID_ADC1 0x7 /* In */
70#define STAC9220_NID_SPDIF_OUT 0x8 /* Out */
71#define STAC9220_NID_SPDIF_IN 0x9 /* In */
72/** Also known as PIN_A. */
73#define STAC9220_NID_PIN_HEADPHONE0 0xA /* In, Out */
74#define STAC9220_NID_PIN_B 0xB /* In, Out */
75#define STAC9220_NID_PIN_C 0xC /* In, Out */
76/** Also known as PIN D. */
77#define STAC9220_NID_PIN_HEADPHONE1 0xD /* In, Out */
78#define STAC9220_NID_PIN_E 0xE /* In */
79#define STAC9220_NID_PIN_F 0xF /* In, Out */
80/** Also known as DIGOUT0. */
81#define STAC9220_NID_PIN_SPDIF_OUT 0x10 /* Out */
82/** Also known as DIGIN. */
83#define STAC9220_NID_PIN_SPDIF_IN 0x11 /* In */
84#define STAC9220_NID_ADC0_MUX 0x12 /* In */
85#define STAC9220_NID_ADC1_MUX 0x13 /* In */
86#define STAC9220_NID_PCBEEP 0x14 /* Out */
87#define STAC9220_NID_PIN_CD 0x15 /* In */
88#define STAC9220_NID_VOL_KNOB 0x16
89#define STAC9220_NID_AMP_ADC0 0x17 /* In */
90#define STAC9220_NID_AMP_ADC1 0x18 /* In */
91/* Only for STAC9221. */
92#define STAC9221_NID_ADAT_OUT 0x19 /* Out */
93#define STAC9221_NID_I2S_OUT 0x1A /* Out */
94#define STAC9221_NID_PIN_I2S_OUT 0x1B /* Out */
95
96/** Number of total nodes emulated. */
97#define STAC9221_NUM_NODES 0x1C
98
99
100/*********************************************************************************************************************************
101* Global Variables *
102*********************************************************************************************************************************/
103#ifdef IN_RING3
104
105/* STAC9220 - Referenced through STAC9220WIDGET in the constructor below. */
106static uint8_t const g_abStac9220Ports[] = { STAC9220_NID_PIN_HEADPHONE0, STAC9220_NID_PIN_B, STAC9220_NID_PIN_C, STAC9220_NID_PIN_HEADPHONE1, STAC9220_NID_PIN_E, STAC9220_NID_PIN_F, 0 };
107static uint8_t const g_abStac9220Dacs[] = { STAC9220_NID_DAC0, STAC9220_NID_DAC1, STAC9220_NID_DAC2, STAC9220_NID_DAC3, 0 };
108static uint8_t const g_abStac9220Adcs[] = { STAC9220_NID_ADC0, STAC9220_NID_ADC1, 0 };
109static uint8_t const g_abStac9220SpdifOuts[] = { STAC9220_NID_SPDIF_OUT, 0 };
110static uint8_t const g_abStac9220SpdifIns[] = { STAC9220_NID_SPDIF_IN, 0 };
111static uint8_t const g_abStac9220DigOutPins[] = { STAC9220_NID_PIN_SPDIF_OUT, 0 };
112static uint8_t const g_abStac9220DigInPins[] = { STAC9220_NID_PIN_SPDIF_IN, 0 };
113static uint8_t const g_abStac9220AdcVols[] = { STAC9220_NID_AMP_ADC0, STAC9220_NID_AMP_ADC1, 0 };
114static uint8_t const g_abStac9220AdcMuxs[] = { STAC9220_NID_ADC0_MUX, STAC9220_NID_ADC1_MUX, 0 };
115static uint8_t const g_abStac9220Pcbeeps[] = { STAC9220_NID_PCBEEP, 0 };
116static uint8_t const g_abStac9220Cds[] = { STAC9220_NID_PIN_CD, 0 };
117static uint8_t const g_abStac9220VolKnobs[] = { STAC9220_NID_VOL_KNOB, 0 };
118/* STAC 9221. */
119/** @todo Is STAC9220_NID_SPDIF_IN really correct for reserved nodes? */
120static uint8_t const g_abStac9220Reserveds[] = { STAC9220_NID_SPDIF_IN, STAC9221_NID_ADAT_OUT, STAC9221_NID_I2S_OUT, STAC9221_NID_PIN_I2S_OUT, 0 };
121
122/** SSM description of CODECCOMMONNODE. */
123static SSMFIELD const g_aCodecNodeFields[] =
124{
125 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, Core.uID),
126 SSMFIELD_ENTRY_PAD_HC_AUTO(3, 3),
127 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, Core.au32F00_param),
128 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, Core.au32F02_param),
129 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, au32Params),
130 SSMFIELD_ENTRY_TERM()
131};
132
133/** Backward compatibility with v1 of CODECCOMMONNODE. */
134static SSMFIELD const g_aCodecNodeFieldsV1[] =
135{
136 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, Core.uID),
137 SSMFIELD_ENTRY_PAD_HC_AUTO(3, 7),
138 SSMFIELD_ENTRY_OLD_HCPTR(Core.name),
139 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, Core.au32F00_param),
140 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, Core.au32F02_param),
141 SSMFIELD_ENTRY( CODECSAVEDSTATENODE, au32Params),
142 SSMFIELD_ENTRY_TERM()
143};
144
145#endif /* IN_RING3 */
146
147
148
149#if 0 /* unused */
150static DECLCALLBACK(void) stac9220DbgNodes(PHDACODEC pThis, PCDBGFINFOHLP pHlp, const char *pszArgs)
151{
152 RT_NOREF(pszArgs);
153 uint8_t const cTotalNodes = RT_MIN(pThis->cTotalNodes, RT_ELEMENTS(pThis->aNodes));
154 for (uint8_t i = 1; i < cTotalNodes; i++)
155 {
156 PCODECNODE pNode = &pThis->aNodes[i];
157 AMPLIFIER *pAmp = &pNode->dac.B_params;
158
159 uint8_t lVol = AMPLIFIER_REGISTER(*pAmp, AMPLIFIER_OUT, AMPLIFIER_LEFT, 0) & 0x7f;
160 uint8_t rVol = AMPLIFIER_REGISTER(*pAmp, AMPLIFIER_OUT, AMPLIFIER_RIGHT, 0) & 0x7f;
161
162 pHlp->pfnPrintf(pHlp, "0x%x: lVol=%RU8, rVol=%RU8\n", i, lVol, rVol);
163 }
164}
165#endif
166
167
168/**
169 * Resets a single node of the codec.
170 *
171 * @param pThis HDA codec of node to reset.
172 * @param uNID Node ID to set node to.
173 * @param pNode Node to reset.
174 */
175static void stac9220NodeReset(PHDACODEC pThis, uint8_t uNID, PCODECNODE pNode)
176{
177 LogFlowFunc(("NID=0x%x (%RU8)\n", uNID, uNID));
178
179 if ( !pThis->fInReset
180 && ( uNID != STAC9220_NID_ROOT
181 && uNID != STAC9220_NID_AFG)
182 )
183 {
184 RT_ZERO(pNode->node);
185 }
186
187 /* Set common parameters across all nodes. */
188 pNode->node.uID = uNID;
189 pNode->node.uSD = 0;
190
191 switch (uNID)
192 {
193 /* Root node. */
194 case STAC9220_NID_ROOT:
195 {
196 /* Set the revision ID. */
197 pNode->root.node.au32F00_param[0x02] = CODEC_MAKE_F00_02(0x1, 0x0, 0x3, 0x4, 0x0, 0x1);
198 break;
199 }
200
201 /*
202 * AFG (Audio Function Group).
203 */
204 case STAC9220_NID_AFG:
205 {
206 pNode->afg.node.au32F00_param[0x08] = CODEC_MAKE_F00_08(1, 0xd, 0xd);
207 /* We set the AFG's PCM capabitilies fixed to 16kHz, 22.5kHz + 44.1kHz, 16-bit signed. */
208 pNode->afg.node.au32F00_param[0x0A] = CODEC_F00_0A_44_1KHZ /* 44.1 kHz */
209 | CODEC_F00_0A_44_1KHZ_1_2X /* Messed up way of saying 22.05 kHz */
210 | CODEC_F00_0A_48KHZ_1_3X /* Messed up way of saying 16 kHz. */
211 | CODEC_F00_0A_16_BIT; /* 16-bit signed */
212 /* Note! We do not set CODEC_F00_0A_48KHZ here because we end up with
213 S/PDIF output showing up in windows and it trying to configure
214 streams other than 0 and 4 and stuff going sideways in the
215 stream setup/removal area. */
216 pNode->afg.node.au32F00_param[0x0B] = CODEC_F00_0B_PCM;
217 pNode->afg.node.au32F00_param[0x0C] = CODEC_MAKE_F00_0C(0x17)
218 | CODEC_F00_0C_CAP_BALANCED_IO
219 | CODEC_F00_0C_CAP_INPUT
220 | CODEC_F00_0C_CAP_OUTPUT
221 | CODEC_F00_0C_CAP_PRESENCE_DETECT
222 | CODEC_F00_0C_CAP_TRIGGER_REQUIRED
223 | CODEC_F00_0C_CAP_IMPENDANCE_SENSE;
224
225 /* Default input amplifier capabilities. */
226 pNode->node.au32F00_param[0x0D] = CODEC_MAKE_F00_0D(CODEC_AMP_CAP_MUTE,
227 CODEC_AMP_STEP_SIZE,
228 CODEC_AMP_NUM_STEPS,
229 CODEC_AMP_OFF_INITIAL);
230 /* Default output amplifier capabilities. */
231 pNode->node.au32F00_param[0x12] = CODEC_MAKE_F00_12(CODEC_AMP_CAP_MUTE,
232 CODEC_AMP_STEP_SIZE,
233 CODEC_AMP_NUM_STEPS,
234 CODEC_AMP_OFF_INITIAL);
235
236 pNode->afg.node.au32F00_param[0x11] = CODEC_MAKE_F00_11(1, 1, 0, 0, 4);
237 pNode->afg.node.au32F00_param[0x0F] = CODEC_F00_0F_D3
238 | CODEC_F00_0F_D2
239 | CODEC_F00_0F_D1
240 | CODEC_F00_0F_D0;
241
242 pNode->afg.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D2, CODEC_F05_D2); /* PS-Act: D2, PS->Set D2. */
243 pNode->afg.u32F08_param = 0;
244 pNode->afg.u32F17_param = 0;
245 break;
246 }
247
248 /*
249 * DACs.
250 */
251 case STAC9220_NID_DAC0: /* DAC0: Headphones 0 + 1 */
252 case STAC9220_NID_DAC1: /* DAC1: PIN C */
253 case STAC9220_NID_DAC2: /* DAC2: PIN B */
254 case STAC9220_NID_DAC3: /* DAC3: PIN F */
255 {
256 pNode->dac.u32A_param = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
257 HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
258 HDA_SDFMT_CHAN_STEREO);
259
260 /* 7.3.4.6: Audio widget capabilities. */
261 pNode->dac.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_OUTPUT, 13, 0)
262 | CODEC_F00_09_CAP_L_R_SWAP
263 | CODEC_F00_09_CAP_POWER_CTRL
264 | CODEC_F00_09_CAP_OUT_AMP_PRESENT
265 | CODEC_F00_09_CAP_STEREO;
266
267 /* Connection list; must be 0 if the only connection for the widget is
268 * to the High Definition Audio Link. */
269 pNode->dac.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 0 /* Entries */);
270
271 pNode->dac.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3);
272
273 RT_ZERO(pNode->dac.B_params);
274 AMPLIFIER_REGISTER(pNode->dac.B_params, AMPLIFIER_OUT, AMPLIFIER_LEFT, 0) = 0x7F | RT_BIT(7);
275 AMPLIFIER_REGISTER(pNode->dac.B_params, AMPLIFIER_OUT, AMPLIFIER_RIGHT, 0) = 0x7F | RT_BIT(7);
276 break;
277 }
278
279 /*
280 * ADCs.
281 */
282 case STAC9220_NID_ADC0: /* Analog input. */
283 {
284 pNode->node.au32F02_param[0] = STAC9220_NID_AMP_ADC0;
285 goto adc_init;
286 }
287
288 case STAC9220_NID_ADC1: /* Analog input (CD). */
289 {
290 pNode->node.au32F02_param[0] = STAC9220_NID_AMP_ADC1;
291
292 /* Fall through is intentional. */
293 adc_init:
294
295 pNode->adc.u32A_param = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
296 HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
297 HDA_SDFMT_CHAN_STEREO);
298
299 pNode->adc.u32F03_param = RT_BIT(0);
300 pNode->adc.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3); /* PS-Act: D3 Set: D3 */
301
302 pNode->adc.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_INPUT, 0xD, 0)
303 | CODEC_F00_09_CAP_POWER_CTRL
304 | CODEC_F00_09_CAP_CONNECTION_LIST
305 | CODEC_F00_09_CAP_PROC_WIDGET
306 | CODEC_F00_09_CAP_STEREO;
307 /* Connection list entries. */
308 pNode->adc.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 1 /* Entries */);
309 break;
310 }
311
312 /*
313 * SP/DIF In/Out.
314 */
315 case STAC9220_NID_SPDIF_OUT:
316 {
317 pNode->spdifout.u32A_param = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
318 HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
319 HDA_SDFMT_CHAN_STEREO);
320 pNode->spdifout.u32F06_param = 0;
321 pNode->spdifout.u32F0d_param = 0;
322
323 pNode->spdifout.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_OUTPUT, 4, 0)
324 | CODEC_F00_09_CAP_DIGITAL
325 | CODEC_F00_09_CAP_FMT_OVERRIDE
326 | CODEC_F00_09_CAP_STEREO;
327
328 /* Use a fixed format from AFG. */
329 pNode->spdifout.node.au32F00_param[0xA] = pThis->aNodes[STAC9220_NID_AFG].node.au32F00_param[0xA];
330 pNode->spdifout.node.au32F00_param[0xB] = CODEC_F00_0B_PCM;
331 break;
332 }
333
334 case STAC9220_NID_SPDIF_IN:
335 {
336 pNode->spdifin.u32A_param = CODEC_MAKE_A(HDA_SDFMT_TYPE_PCM, HDA_SDFMT_BASE_44KHZ,
337 HDA_SDFMT_MULT_1X, HDA_SDFMT_DIV_2X, HDA_SDFMT_16_BIT,
338 HDA_SDFMT_CHAN_STEREO);
339
340 pNode->spdifin.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_INPUT, 4, 0)
341 | CODEC_F00_09_CAP_DIGITAL
342 | CODEC_F00_09_CAP_CONNECTION_LIST
343 | CODEC_F00_09_CAP_FMT_OVERRIDE
344 | CODEC_F00_09_CAP_STEREO;
345
346 /* Use a fixed format from AFG. */
347 pNode->spdifin.node.au32F00_param[0xA] = pThis->aNodes[STAC9220_NID_AFG].node.au32F00_param[0xA];
348 pNode->spdifin.node.au32F00_param[0xB] = CODEC_F00_0B_PCM;
349
350 /* Connection list entries. */
351 pNode->spdifin.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 1 /* Entries */);
352 pNode->spdifin.node.au32F02_param[0] = 0x11;
353 break;
354 }
355
356 /*
357 * PINs / Ports.
358 */
359 case STAC9220_NID_PIN_HEADPHONE0: /* Port A: Headphone in/out (front). */
360 {
361 pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(0 /*fPresent*/, CODEC_F09_ANALOG_NA);
362
363 pNode->port.node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
364 | CODEC_F00_0C_CAP_INPUT
365 | CODEC_F00_0C_CAP_OUTPUT
366 | CODEC_F00_0C_CAP_HEADPHONE_AMP
367 | CODEC_F00_0C_CAP_PRESENCE_DETECT
368 | CODEC_F00_0C_CAP_TRIGGER_REQUIRED;
369
370 /* Connection list entry 0: Goes to DAC0. */
371 pNode->port.node.au32F02_param[0] = STAC9220_NID_DAC0;
372
373 if (!pThis->fInReset)
374 pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
375 CODEC_F1C_LOCATION_FRONT,
376 CODEC_F1C_DEVICE_HP,
377 CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
378 CODEC_F1C_COLOR_GREEN,
379 CODEC_F1C_MISC_NONE,
380 CODEC_F1C_ASSOCIATION_GROUP_1, 0x0 /* Seq */);
381 goto port_init;
382 }
383
384 case STAC9220_NID_PIN_B: /* Port B: Rear CLFE (Center / Subwoofer). */
385 {
386 pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1 /*fPresent*/, CODEC_F09_ANALOG_NA);
387
388 pNode->port.node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
389 | CODEC_F00_0C_CAP_INPUT
390 | CODEC_F00_0C_CAP_OUTPUT
391 | CODEC_F00_0C_CAP_PRESENCE_DETECT
392 | CODEC_F00_0C_CAP_TRIGGER_REQUIRED;
393
394 /* Connection list entry 0: Goes to DAC2. */
395 pNode->port.node.au32F02_param[0] = STAC9220_NID_DAC2;
396
397 if (!pThis->fInReset)
398 pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
399 CODEC_F1C_LOCATION_REAR,
400 CODEC_F1C_DEVICE_SPEAKER,
401 CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
402 CODEC_F1C_COLOR_BLACK,
403 CODEC_F1C_MISC_NONE,
404 CODEC_F1C_ASSOCIATION_GROUP_0, 0x1 /* Seq */);
405 goto port_init;
406 }
407
408 case STAC9220_NID_PIN_C: /* Rear Speaker. */
409 {
410 pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1 /*fPresent*/, CODEC_F09_ANALOG_NA);
411
412 pNode->port.node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
413 | CODEC_F00_0C_CAP_INPUT
414 | CODEC_F00_0C_CAP_OUTPUT
415 | CODEC_F00_0C_CAP_PRESENCE_DETECT
416 | CODEC_F00_0C_CAP_TRIGGER_REQUIRED;
417
418 /* Connection list entry 0: Goes to DAC1. */
419 pNode->port.node.au32F02_param[0x0] = STAC9220_NID_DAC1;
420
421 if (!pThis->fInReset)
422 pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
423 CODEC_F1C_LOCATION_REAR,
424 CODEC_F1C_DEVICE_SPEAKER,
425 CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
426 CODEC_F1C_COLOR_GREEN,
427 CODEC_F1C_MISC_NONE,
428 CODEC_F1C_ASSOCIATION_GROUP_0, 0x0 /* Seq */);
429 goto port_init;
430 }
431
432 case STAC9220_NID_PIN_HEADPHONE1: /* Also known as PIN_D. */
433 {
434 pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1 /*fPresent*/, CODEC_F09_ANALOG_NA);
435
436 pNode->port.node.au32F00_param[0xC] = CODEC_MAKE_F00_0C(0x17)
437 | CODEC_F00_0C_CAP_INPUT
438 | CODEC_F00_0C_CAP_OUTPUT
439 | CODEC_F00_0C_CAP_HEADPHONE_AMP
440 | CODEC_F00_0C_CAP_PRESENCE_DETECT
441 | CODEC_F00_0C_CAP_TRIGGER_REQUIRED;
442
443 /* Connection list entry 0: Goes to DAC1. */
444 pNode->port.node.au32F02_param[0x0] = STAC9220_NID_DAC0;
445
446 if (!pThis->fInReset)
447 pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
448 CODEC_F1C_LOCATION_FRONT,
449 CODEC_F1C_DEVICE_MIC,
450 CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
451 CODEC_F1C_COLOR_PINK,
452 CODEC_F1C_MISC_NONE,
453 CODEC_F1C_ASSOCIATION_GROUP_15, 0x0 /* Ignored */);
454 /* Fall through is intentional. */
455
456 port_init:
457
458 pNode->port.u32F07_param = CODEC_F07_IN_ENABLE
459 | CODEC_F07_OUT_ENABLE;
460 pNode->port.u32F08_param = 0;
461
462 pNode->port.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
463 | CODEC_F00_09_CAP_CONNECTION_LIST
464 | CODEC_F00_09_CAP_UNSOL
465 | CODEC_F00_09_CAP_STEREO;
466 /* Connection list entries. */
467 pNode->port.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 1 /* Entries */);
468 break;
469 }
470
471 case STAC9220_NID_PIN_E:
472 {
473 pNode->port.u32F07_param = CODEC_F07_IN_ENABLE;
474 pNode->port.u32F08_param = 0;
475 /* If Line in is reported as enabled, OS X sees no speakers! Windows does
476 * not care either way, although Linux does.
477 */
478 pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(0 /* fPresent */, 0);
479
480 pNode->port.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
481 | CODEC_F00_09_CAP_UNSOL
482 | CODEC_F00_09_CAP_STEREO;
483
484 pNode->port.node.au32F00_param[0xC] = CODEC_F00_0C_CAP_INPUT
485 | CODEC_F00_0C_CAP_PRESENCE_DETECT;
486
487 if (!pThis->fInReset)
488 pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
489 CODEC_F1C_LOCATION_REAR,
490 CODEC_F1C_DEVICE_LINE_IN,
491 CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
492 CODEC_F1C_COLOR_BLUE,
493 CODEC_F1C_MISC_NONE,
494 CODEC_F1C_ASSOCIATION_GROUP_4, 0x1 /* Seq */);
495 break;
496 }
497
498 case STAC9220_NID_PIN_F:
499 {
500 pNode->port.u32F07_param = CODEC_F07_IN_ENABLE | CODEC_F07_OUT_ENABLE;
501 pNode->port.u32F08_param = 0;
502 pNode->port.u32F09_param = CODEC_MAKE_F09_ANALOG(1 /* fPresent */, CODEC_F09_ANALOG_NA);
503
504 pNode->port.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
505 | CODEC_F00_09_CAP_CONNECTION_LIST
506 | CODEC_F00_09_CAP_UNSOL
507 | CODEC_F00_09_CAP_OUT_AMP_PRESENT
508 | CODEC_F00_09_CAP_STEREO;
509
510 pNode->port.node.au32F00_param[0xC] = CODEC_F00_0C_CAP_INPUT
511 | CODEC_F00_0C_CAP_OUTPUT;
512
513 /* Connection list entry 0: Goes to DAC3. */
514 pNode->port.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 1 /* Entries */);
515 pNode->port.node.au32F02_param[0x0] = STAC9220_NID_DAC3;
516
517 if (!pThis->fInReset)
518 pNode->port.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
519 CODEC_F1C_LOCATION_INTERNAL,
520 CODEC_F1C_DEVICE_SPEAKER,
521 CODEC_F1C_CONNECTION_TYPE_1_8INCHES,
522 CODEC_F1C_COLOR_ORANGE,
523 CODEC_F1C_MISC_NONE,
524 CODEC_F1C_ASSOCIATION_GROUP_0, 0x2 /* Seq */);
525 break;
526 }
527
528 case STAC9220_NID_PIN_SPDIF_OUT: /* Rear SPDIF Out. */
529 {
530 pNode->digout.u32F07_param = CODEC_F07_OUT_ENABLE;
531 pNode->digout.u32F09_param = 0;
532
533 pNode->digout.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
534 | CODEC_F00_09_CAP_DIGITAL
535 | CODEC_F00_09_CAP_CONNECTION_LIST
536 | CODEC_F00_09_CAP_STEREO;
537 pNode->digout.node.au32F00_param[0xC] = CODEC_F00_0C_CAP_OUTPUT;
538
539 /* Connection list entries. */
540 pNode->digout.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 3 /* Entries */);
541 pNode->digout.node.au32F02_param[0x0] = RT_MAKE_U32_FROM_U8(STAC9220_NID_SPDIF_OUT,
542 STAC9220_NID_AMP_ADC0, STAC9221_NID_ADAT_OUT, 0);
543 if (!pThis->fInReset)
544 pNode->digout.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
545 CODEC_F1C_LOCATION_REAR,
546 CODEC_F1C_DEVICE_SPDIF_OUT,
547 CODEC_F1C_CONNECTION_TYPE_DIN,
548 CODEC_F1C_COLOR_BLACK,
549 CODEC_F1C_MISC_NONE,
550 CODEC_F1C_ASSOCIATION_GROUP_2, 0x0 /* Seq */);
551 break;
552 }
553
554 case STAC9220_NID_PIN_SPDIF_IN:
555 {
556 pNode->digin.u32F05_param = CODEC_MAKE_F05(0, 0, 0, CODEC_F05_D3, CODEC_F05_D3); /* PS-Act: D3 -> D3 */
557 pNode->digin.u32F07_param = CODEC_F07_IN_ENABLE;
558 pNode->digin.u32F08_param = 0;
559 pNode->digin.u32F09_param = CODEC_MAKE_F09_DIGITAL(0, 0);
560 pNode->digin.u32F0c_param = 0;
561
562 pNode->digin.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 3, 0)
563 | CODEC_F00_09_CAP_POWER_CTRL
564 | CODEC_F00_09_CAP_DIGITAL
565 | CODEC_F00_09_CAP_UNSOL
566 | CODEC_F00_09_CAP_STEREO;
567
568 pNode->digin.node.au32F00_param[0xC] = CODEC_F00_0C_CAP_EAPD
569 | CODEC_F00_0C_CAP_INPUT
570 | CODEC_F00_0C_CAP_PRESENCE_DETECT;
571 if (!pThis->fInReset)
572 pNode->digin.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_COMPLEX,
573 CODEC_F1C_LOCATION_REAR,
574 CODEC_F1C_DEVICE_SPDIF_IN,
575 CODEC_F1C_CONNECTION_TYPE_OTHER_DIGITAL,
576 CODEC_F1C_COLOR_BLACK,
577 CODEC_F1C_MISC_NONE,
578 CODEC_F1C_ASSOCIATION_GROUP_5, 0x0 /* Seq */);
579 break;
580 }
581
582 case STAC9220_NID_ADC0_MUX:
583 {
584 pNode->adcmux.u32F01_param = 0; /* Connection select control index (STAC9220_NID_PIN_E). */
585 goto adcmux_init;
586 }
587
588 case STAC9220_NID_ADC1_MUX:
589 {
590 pNode->adcmux.u32F01_param = 1; /* Connection select control index (STAC9220_NID_PIN_CD). */
591 /* Fall through is intentional. */
592
593 adcmux_init:
594
595 pNode->adcmux.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_SELECTOR, 0, 0)
596 | CODEC_F00_09_CAP_CONNECTION_LIST
597 | CODEC_F00_09_CAP_AMP_FMT_OVERRIDE
598 | CODEC_F00_09_CAP_OUT_AMP_PRESENT
599 | CODEC_F00_09_CAP_STEREO;
600
601 pNode->adcmux.node.au32F00_param[0xD] = CODEC_MAKE_F00_0D(0, 27, 4, 0);
602
603 /* Connection list entries. */
604 pNode->adcmux.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 7 /* Entries */);
605 pNode->adcmux.node.au32F02_param[0x0] = RT_MAKE_U32_FROM_U8(STAC9220_NID_PIN_E,
606 STAC9220_NID_PIN_CD,
607 STAC9220_NID_PIN_F,
608 STAC9220_NID_PIN_B);
609 pNode->adcmux.node.au32F02_param[0x4] = RT_MAKE_U32_FROM_U8(STAC9220_NID_PIN_C,
610 STAC9220_NID_PIN_HEADPHONE1,
611 STAC9220_NID_PIN_HEADPHONE0,
612 0x0 /* Unused */);
613
614 /* STAC 9220 v10 6.21-22.{4,5} both(left and right) out amplifiers initialized with 0. */
615 RT_ZERO(pNode->adcmux.B_params);
616 break;
617 }
618
619 case STAC9220_NID_PCBEEP:
620 {
621 pNode->pcbeep.u32F0a_param = 0;
622
623 pNode->pcbeep.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_BEEP_GEN, 0, 0)
624 | CODEC_F00_09_CAP_AMP_FMT_OVERRIDE
625 | CODEC_F00_09_CAP_OUT_AMP_PRESENT;
626 pNode->pcbeep.node.au32F00_param[0xD] = CODEC_MAKE_F00_0D(0, 17, 3, 3);
627
628 RT_ZERO(pNode->pcbeep.B_params);
629 break;
630 }
631
632 case STAC9220_NID_PIN_CD:
633 {
634 pNode->cdnode.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
635 | CODEC_F00_09_CAP_STEREO;
636 pNode->cdnode.node.au32F00_param[0xC] = CODEC_F00_0C_CAP_INPUT;
637
638 if (!pThis->fInReset)
639 pNode->cdnode.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_FIXED,
640 CODEC_F1C_LOCATION_INTERNAL,
641 CODEC_F1C_DEVICE_CD,
642 CODEC_F1C_CONNECTION_TYPE_ATAPI,
643 CODEC_F1C_COLOR_UNKNOWN,
644 CODEC_F1C_MISC_NONE,
645 CODEC_F1C_ASSOCIATION_GROUP_4, 0x2 /* Seq */);
646 break;
647 }
648
649 case STAC9220_NID_VOL_KNOB:
650 {
651 pNode->volumeKnob.u32F08_param = 0;
652 pNode->volumeKnob.u32F0f_param = 0x7f;
653
654 pNode->volumeKnob.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_VOLUME_KNOB, 0, 0);
655 pNode->volumeKnob.node.au32F00_param[0xD] = RT_BIT(7) | 0x7F;
656
657 /* Connection list entries. */
658 pNode->volumeKnob.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 4 /* Entries */);
659 pNode->volumeKnob.node.au32F02_param[0x0] = RT_MAKE_U32_FROM_U8(STAC9220_NID_DAC0,
660 STAC9220_NID_DAC1,
661 STAC9220_NID_DAC2,
662 STAC9220_NID_DAC3);
663 break;
664 }
665
666 case STAC9220_NID_AMP_ADC0: /* ADC0Vol */
667 {
668 pNode->adcvol.node.au32F02_param[0] = STAC9220_NID_ADC0_MUX;
669 goto adcvol_init;
670 }
671
672 case STAC9220_NID_AMP_ADC1: /* ADC1Vol */
673 {
674 pNode->adcvol.node.au32F02_param[0] = STAC9220_NID_ADC1_MUX;
675 /* Fall through is intentional. */
676
677 adcvol_init:
678
679 pNode->adcvol.node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_SELECTOR, 0, 0)
680 | CODEC_F00_09_CAP_L_R_SWAP
681 | CODEC_F00_09_CAP_CONNECTION_LIST
682 | CODEC_F00_09_CAP_IN_AMP_PRESENT
683 | CODEC_F00_09_CAP_STEREO;
684
685
686 pNode->adcvol.node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 1 /* Entries */);
687
688 RT_ZERO(pNode->adcvol.B_params);
689 AMPLIFIER_REGISTER(pNode->adcvol.B_params, AMPLIFIER_IN, AMPLIFIER_LEFT, 0) = RT_BIT(7);
690 AMPLIFIER_REGISTER(pNode->adcvol.B_params, AMPLIFIER_IN, AMPLIFIER_RIGHT, 0) = RT_BIT(7);
691 break;
692 }
693
694 /*
695 * STAC9221 nodes.
696 */
697
698 case STAC9221_NID_ADAT_OUT:
699 {
700 pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_VENDOR_DEFINED, 3, 0)
701 | CODEC_F00_09_CAP_DIGITAL
702 | CODEC_F00_09_CAP_STEREO;
703 break;
704 }
705
706 case STAC9221_NID_I2S_OUT:
707 {
708 pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_AUDIO_OUTPUT, 3, 0)
709 | CODEC_F00_09_CAP_DIGITAL
710 | CODEC_F00_09_CAP_STEREO;
711 break;
712 }
713
714 case STAC9221_NID_PIN_I2S_OUT:
715 {
716 pNode->node.au32F00_param[0x9] = CODEC_MAKE_F00_09(CODEC_F00_09_TYPE_PIN_COMPLEX, 0, 0)
717 | CODEC_F00_09_CAP_DIGITAL
718 | CODEC_F00_09_CAP_CONNECTION_LIST
719 | CODEC_F00_09_CAP_STEREO;
720
721 pNode->node.au32F00_param[0xC] = CODEC_F00_0C_CAP_OUTPUT;
722
723 /* Connection list entries. */
724 pNode->node.au32F00_param[0xE] = CODEC_MAKE_F00_0E(CODEC_F00_0E_LIST_NID_SHORT, 1 /* Entries */);
725 pNode->node.au32F02_param[0] = STAC9221_NID_I2S_OUT;
726
727 if (!pThis->fInReset)
728 pNode->reserved.u32F1c_param = CODEC_MAKE_F1C(CODEC_F1C_PORT_NO_PHYS,
729 CODEC_F1C_LOCATION_NA,
730 CODEC_F1C_DEVICE_LINE_OUT,
731 CODEC_F1C_CONNECTION_TYPE_UNKNOWN,
732 CODEC_F1C_COLOR_UNKNOWN,
733 CODEC_F1C_MISC_NONE,
734 CODEC_F1C_ASSOCIATION_GROUP_15, 0x0 /* Ignored */);
735 break;
736 }
737
738 default:
739 AssertMsgFailed(("Node %RU8 not implemented\n", uNID));
740 break;
741 }
742}
743
744/**
745 * Resets the codec with all its connected nodes.
746 *
747 * @param pThis HDA codec to reset.
748 */
749static DECLCALLBACK(void) stac9220Reset(PHDACODEC pThis)
750{
751 AssertPtrReturnVoid(pThis->aNodes);
752
753 LogRel(("HDA: Codec reset\n"));
754
755 pThis->fInReset = true;
756
757 uint8_t const cTotalNodes = (uint8_t)RT_MIN(pThis->cTotalNodes, RT_ELEMENTS(pThis->aNodes));
758 for (uint8_t i = 0; i < cTotalNodes; i++)
759 stac9220NodeReset(pThis, i, &pThis->aNodes[i]);
760
761 pThis->fInReset = false;
762}
763
764#ifdef IN_RING3
765
766static int stac9220Construct(PHDACODEC pThis)
767{
768 pThis->u16VendorId = 0x8384; /* SigmaTel */
769 /*
770 * Note: The Linux kernel uses "patch_stac922x" for the fixups,
771 * which in turn uses "ref922x_pin_configs" for the configuration
772 * defaults tweaking in sound/pci/hda/patch_sigmatel.c.
773 */
774 pThis->u16DeviceId = 0x7680; /* STAC9221 A1 */
775 pThis->u8BSKU = 0x76;
776 pThis->u8AssemblyId = 0x80;
777
778 pThis->fInReset = false;
779
780#define STAC9220WIDGET(type) memcpy(&pThis->au8##type##s, &g_abStac9220##type##s, sizeof(uint8_t) * RT_ELEMENTS(g_abStac9220##type##s));
781 STAC9220WIDGET(Port);
782 STAC9220WIDGET(Dac);
783 STAC9220WIDGET(Adc);
784 STAC9220WIDGET(AdcVol);
785 STAC9220WIDGET(AdcMux);
786 STAC9220WIDGET(Pcbeep);
787 STAC9220WIDGET(SpdifIn);
788 STAC9220WIDGET(SpdifOut);
789 STAC9220WIDGET(DigInPin);
790 STAC9220WIDGET(DigOutPin);
791 STAC9220WIDGET(Cd);
792 STAC9220WIDGET(VolKnob);
793 STAC9220WIDGET(Reserved);
794#undef STAC9220WIDGET
795
796 AssertCompile(STAC9221_NUM_NODES <= RT_ELEMENTS(pThis->aNodes));
797 pThis->cTotalNodes = STAC9221_NUM_NODES;
798
799 pThis->u8AdcVolsLineIn = STAC9220_NID_AMP_ADC0;
800 pThis->u8DacLineOut = STAC9220_NID_DAC1;
801
802 /*
803 * Initialize all codec nodes.
804 * This is specific to the codec, so do this here.
805 *
806 * Note: Do *not* call stac9220Reset() here, as this would not
807 * initialize the node default configuration values then!
808 */
809 for (uint8_t i = 0; i < STAC9221_NUM_NODES; i++)
810 stac9220NodeReset(pThis, i, &pThis->aNodes[i]);
811
812 /* Common root node initializers. */
813 pThis->aNodes[STAC9220_NID_ROOT].root.node.au32F00_param[0] = CODEC_MAKE_F00_00(pThis->u16VendorId, pThis->u16DeviceId);
814 pThis->aNodes[STAC9220_NID_ROOT].root.node.au32F00_param[4] = CODEC_MAKE_F00_04(0x1, 0x1);
815
816 /* Common AFG node initializers. */
817 pThis->aNodes[STAC9220_NID_AFG].afg.node.au32F00_param[0x4] = CODEC_MAKE_F00_04(0x2, STAC9221_NUM_NODES - 2);
818 pThis->aNodes[STAC9220_NID_AFG].afg.node.au32F00_param[0x5] = CODEC_MAKE_F00_05(1, CODEC_F00_05_AFG);
819 pThis->aNodes[STAC9220_NID_AFG].afg.node.au32F00_param[0xA] = CODEC_F00_0A_44_1KHZ | CODEC_F00_0A_16_BIT;
820 pThis->aNodes[STAC9220_NID_AFG].afg.u32F20_param = CODEC_MAKE_F20(pThis->u16VendorId, pThis->u8BSKU, pThis->u8AssemblyId);
821
822 return VINF_SUCCESS;
823}
824
825#endif /* IN_RING3 */
826
827/*
828 * Some generic predicate functions.
829 */
830
831#define DECLISNODEOFTYPE(type) \
832 DECLINLINE(bool) hdaCodecIs##type##Node(PHDACODEC pThis, uint8_t cNode) \
833 { \
834 Assert(pThis->au8##type##s); \
835 for (int i = 0; pThis->au8##type##s[i] != 0; ++i) \
836 if (pThis->au8##type##s[i] == cNode) \
837 return true; \
838 return false; \
839 }
840/* hdaCodecIsPortNode */
841DECLISNODEOFTYPE(Port)
842/* hdaCodecIsDacNode */
843DECLISNODEOFTYPE(Dac)
844/* hdaCodecIsAdcVolNode */
845DECLISNODEOFTYPE(AdcVol)
846/* hdaCodecIsAdcNode */
847DECLISNODEOFTYPE(Adc)
848/* hdaCodecIsAdcMuxNode */
849DECLISNODEOFTYPE(AdcMux)
850/* hdaCodecIsPcbeepNode */
851DECLISNODEOFTYPE(Pcbeep)
852/* hdaCodecIsSpdifOutNode */
853DECLISNODEOFTYPE(SpdifOut)
854/* hdaCodecIsSpdifInNode */
855DECLISNODEOFTYPE(SpdifIn)
856/* hdaCodecIsDigInPinNode */
857DECLISNODEOFTYPE(DigInPin)
858/* hdaCodecIsDigOutPinNode */
859DECLISNODEOFTYPE(DigOutPin)
860/* hdaCodecIsCdNode */
861DECLISNODEOFTYPE(Cd)
862/* hdaCodecIsVolKnobNode */
863DECLISNODEOFTYPE(VolKnob)
864/* hdaCodecIsReservedNode */
865DECLISNODEOFTYPE(Reserved)
866
867#ifdef IN_RING3
868
869/*
870 * Misc helpers.
871 */
872static int hdaR3CodecToAudVolume(PHDACODECR3 pThisCC, PCODECNODE pNode, AMPLIFIER *pAmp, PDMAUDIOMIXERCTL enmMixerCtl)
873{
874 RT_NOREF(pNode);
875
876 uint8_t iDir;
877 switch (enmMixerCtl)
878 {
879 case PDMAUDIOMIXERCTL_VOLUME_MASTER:
880 case PDMAUDIOMIXERCTL_FRONT:
881 iDir = AMPLIFIER_OUT;
882 break;
883 case PDMAUDIOMIXERCTL_LINE_IN:
884 case PDMAUDIOMIXERCTL_MIC_IN:
885 iDir = AMPLIFIER_IN;
886 break;
887 default:
888 AssertMsgFailedReturn(("Invalid mixer control %RU32\n", enmMixerCtl), VERR_INVALID_PARAMETER);
889 break;
890 }
891
892 int iMute;
893 iMute = AMPLIFIER_REGISTER(*pAmp, iDir, AMPLIFIER_LEFT, 0) & RT_BIT(7);
894 iMute |= AMPLIFIER_REGISTER(*pAmp, iDir, AMPLIFIER_RIGHT, 0) & RT_BIT(7);
895 iMute >>=7;
896 iMute &= 0x1;
897
898 uint8_t bLeft = AMPLIFIER_REGISTER(*pAmp, iDir, AMPLIFIER_LEFT, 0) & 0x7f;
899 uint8_t bRight = AMPLIFIER_REGISTER(*pAmp, iDir, AMPLIFIER_RIGHT, 0) & 0x7f;
900
901 /*
902 * The STAC9220 volume controls have 0 to -96dB attenuation range in 128 steps.
903 * We have 0 to -96dB range in 256 steps. HDA volume setting of 127 must map
904 * to 255 internally (0dB), while HDA volume setting of 0 (-96dB) should map
905 * to 1 (rather than zero) internally.
906 */
907 bLeft = (bLeft + 1) * (2 * 255) / 256;
908 bRight = (bRight + 1) * (2 * 255) / 256;
909
910 PDMAUDIOVOLUME Vol;
911 PDMAudioVolumeInitFromStereo(&Vol, RT_BOOL(iMute), bLeft, bRight);
912
913 LogFunc(("[NID0x%02x] %RU8/%RU8%s\n", pNode->node.uID, bLeft, bRight, Vol.fMuted ? "- Muted!" : ""));
914 LogRel2(("HDA: Setting volume for mixer control '%s' to %RU8/%RU8%s\n",
915 PDMAudioMixerCtlGetName(enmMixerCtl), bLeft, bRight, Vol.fMuted ? "- Muted!" : ""));
916
917 return pThisCC->pfnCbMixerSetVolume(pThisCC->pDevIns, enmMixerCtl, &Vol);
918}
919
920#endif /* IN_RING3 */
921
922DECLINLINE(void) hdaCodecSetRegister(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset, uint32_t mask)
923{
924 Assert((pu32Reg && u8Offset < 32));
925 *pu32Reg &= ~(mask << u8Offset);
926 *pu32Reg |= (u32Cmd & mask) << u8Offset;
927}
928
929DECLINLINE(void) hdaCodecSetRegisterU8(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset)
930{
931 hdaCodecSetRegister(pu32Reg, u32Cmd, u8Offset, CODEC_VERB_8BIT_DATA);
932}
933
934DECLINLINE(void) hdaCodecSetRegisterU16(uint32_t *pu32Reg, uint32_t u32Cmd, uint8_t u8Offset)
935{
936 hdaCodecSetRegister(pu32Reg, u32Cmd, u8Offset, CODEC_VERB_16BIT_DATA);
937}
938
939
940/*
941 * Verb processor functions.
942 */
943#if 0 /* unused */
944
945static DECLCALLBACK(int) vrbProcUnimplemented(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
946{
947 RT_NOREF(pThis, pThisCC, cmd);
948 LogFlowFunc(("cmd(raw:%x: cad:%x, d:%c, nid:%x, verb:%x)\n", cmd,
949 CODEC_CAD(cmd), CODEC_DIRECT(cmd) ? 'N' : 'Y', CODEC_NID(cmd), CODEC_VERBDATA(cmd)));
950 *pResp = 0;
951 return VINF_SUCCESS;
952}
953
954static DECLCALLBACK(int) vrbProcBreak(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
955{
956 int rc;
957 rc = vrbProcUnimplemented(pThis, pThisCC, cmd, pResp);
958 *pResp |= CODEC_RESPONSE_UNSOLICITED;
959 return rc;
960}
961
962#endif /* unused */
963
964
965/* B-- */
966static DECLCALLBACK(int) vrbProcGetAmplifier(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
967{
968 RT_NOREF(pThisCC);
969 *pResp = 0;
970
971 /* HDA spec 7.3.3.7 Note A */
972 /** @todo If index out of range response should be 0. */
973 uint8_t u8Index = CODEC_GET_AMP_DIRECTION(cmd) == AMPLIFIER_OUT ? 0 : CODEC_GET_AMP_INDEX(cmd);
974
975 PCODECNODE pNode = &pThis->aNodes[CODEC_NID(cmd)];
976 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
977 *pResp = AMPLIFIER_REGISTER(pNode->dac.B_params,
978 CODEC_GET_AMP_DIRECTION(cmd),
979 CODEC_GET_AMP_SIDE(cmd),
980 u8Index);
981 else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
982 *pResp = AMPLIFIER_REGISTER(pNode->adcvol.B_params,
983 CODEC_GET_AMP_DIRECTION(cmd),
984 CODEC_GET_AMP_SIDE(cmd),
985 u8Index);
986 else if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
987 *pResp = AMPLIFIER_REGISTER(pNode->adcmux.B_params,
988 CODEC_GET_AMP_DIRECTION(cmd),
989 CODEC_GET_AMP_SIDE(cmd),
990 u8Index);
991 else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
992 *pResp = AMPLIFIER_REGISTER(pNode->pcbeep.B_params,
993 CODEC_GET_AMP_DIRECTION(cmd),
994 CODEC_GET_AMP_SIDE(cmd),
995 u8Index);
996 else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
997 *pResp = AMPLIFIER_REGISTER(pNode->port.B_params,
998 CODEC_GET_AMP_DIRECTION(cmd),
999 CODEC_GET_AMP_SIDE(cmd),
1000 u8Index);
1001 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1002 *pResp = AMPLIFIER_REGISTER(pNode->adc.B_params,
1003 CODEC_GET_AMP_DIRECTION(cmd),
1004 CODEC_GET_AMP_SIDE(cmd),
1005 u8Index);
1006 else
1007 LogRel2(("HDA: Warning: Unhandled get amplifier command: 0x%x (NID=0x%x [%RU8])\n", cmd, CODEC_NID(cmd), CODEC_NID(cmd)));
1008
1009 return VINF_SUCCESS;
1010}
1011
1012static DECLCALLBACK(int) vrbProcGetParameter(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1013{
1014 RT_NOREF(pThisCC);
1015 Assert((cmd & CODEC_VERB_8BIT_DATA) < CODECNODE_F00_PARAM_LENGTH);
1016 if ((cmd & CODEC_VERB_8BIT_DATA) >= CODECNODE_F00_PARAM_LENGTH)
1017 {
1018 *pResp = 0;
1019
1020 LogFlowFunc(("invalid F00 parameter %d\n", (cmd & CODEC_VERB_8BIT_DATA)));
1021 return VINF_SUCCESS;
1022 }
1023
1024 *pResp = pThis->aNodes[CODEC_NID(cmd)].node.au32F00_param[cmd & CODEC_VERB_8BIT_DATA];
1025 return VINF_SUCCESS;
1026}
1027
1028/* F01 */
1029static DECLCALLBACK(int) vrbProcGetConSelectCtrl(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1030{
1031 RT_NOREF(pThisCC);
1032 *pResp = 0;
1033
1034 if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
1035 *pResp = pThis->aNodes[CODEC_NID(cmd)].adcmux.u32F01_param;
1036 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1037 *pResp = pThis->aNodes[CODEC_NID(cmd)].digout.u32F01_param;
1038 else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1039 *pResp = pThis->aNodes[CODEC_NID(cmd)].port.u32F01_param;
1040 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1041 *pResp = pThis->aNodes[CODEC_NID(cmd)].adc.u32F01_param;
1042 else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
1043 *pResp = pThis->aNodes[CODEC_NID(cmd)].adcvol.u32F01_param;
1044 else
1045 LogRel2(("HDA: Warning: Unhandled get connection select control command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1046
1047 return VINF_SUCCESS;
1048}
1049
1050/* 701 */
1051static DECLCALLBACK(int) vrbProcSetConSelectCtrl(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1052{
1053 RT_NOREF(pThisCC);
1054 *pResp = 0;
1055
1056 uint32_t *pu32Reg = NULL;
1057 if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
1058 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].adcmux.u32F01_param;
1059 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1060 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digout.u32F01_param;
1061 else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1062 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].port.u32F01_param;
1063 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1064 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].adc.u32F01_param;
1065 else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
1066 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].adcvol.u32F01_param;
1067 else
1068 LogRel2(("HDA: Warning: Unhandled set connection select control command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1069
1070 if (pu32Reg)
1071 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1072
1073 return VINF_SUCCESS;
1074}
1075
1076/* F07 */
1077static DECLCALLBACK(int) vrbProcGetPinCtrl(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1078{
1079 RT_NOREF(pThisCC);
1080 *pResp = 0;
1081
1082 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1083 *pResp = pThis->aNodes[CODEC_NID(cmd)].port.u32F07_param;
1084 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1085 *pResp = pThis->aNodes[CODEC_NID(cmd)].digout.u32F07_param;
1086 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1087 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F07_param;
1088 else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
1089 *pResp = pThis->aNodes[CODEC_NID(cmd)].cdnode.u32F07_param;
1090 else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
1091 *pResp = pThis->aNodes[CODEC_NID(cmd)].pcbeep.u32F07_param;
1092 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1093 *pResp = pThis->aNodes[CODEC_NID(cmd)].reserved.u32F07_param;
1094 else
1095 LogRel2(("HDA: Warning: Unhandled get pin control command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1096
1097 return VINF_SUCCESS;
1098}
1099
1100/* 707 */
1101static DECLCALLBACK(int) vrbProcSetPinCtrl(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1102{
1103 RT_NOREF(pThisCC);
1104 *pResp = 0;
1105
1106 uint32_t *pu32Reg = NULL;
1107 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1108 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].port.u32F07_param;
1109 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1110 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F07_param;
1111 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1112 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digout.u32F07_param;
1113 else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
1114 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].cdnode.u32F07_param;
1115 else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
1116 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].pcbeep.u32F07_param;
1117 else if ( hdaCodecIsReservedNode(pThis, CODEC_NID(cmd))
1118 && CODEC_NID(cmd) == 0x1b)
1119 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].reserved.u32F07_param;
1120 else
1121 LogRel2(("HDA: Warning: Unhandled set pin control command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1122
1123 if (pu32Reg)
1124 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1125
1126 return VINF_SUCCESS;
1127}
1128
1129/* F08 */
1130static DECLCALLBACK(int) vrbProcGetUnsolicitedEnabled(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1131{
1132 RT_NOREF(pThisCC);
1133 *pResp = 0;
1134
1135 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1136 *pResp = pThis->aNodes[CODEC_NID(cmd)].port.u32F08_param;
1137 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1138 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F08_param;
1139 else if ((cmd) == STAC9220_NID_AFG)
1140 *pResp = pThis->aNodes[CODEC_NID(cmd)].afg.u32F08_param;
1141 else if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
1142 *pResp = pThis->aNodes[CODEC_NID(cmd)].volumeKnob.u32F08_param;
1143 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1144 *pResp = pThis->aNodes[CODEC_NID(cmd)].digout.u32F08_param;
1145 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1146 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F08_param;
1147 else
1148 LogRel2(("HDA: Warning: Unhandled get unsolicited enabled command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1149
1150 return VINF_SUCCESS;
1151}
1152
1153/* 708 */
1154static DECLCALLBACK(int) vrbProcSetUnsolicitedEnabled(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1155{
1156 RT_NOREF(pThisCC);
1157 *pResp = 0;
1158
1159 uint32_t *pu32Reg = NULL;
1160 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1161 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].port.u32F08_param;
1162 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1163 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F08_param;
1164 else if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1165 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].afg.u32F08_param;
1166 else if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
1167 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].volumeKnob.u32F08_param;
1168 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1169 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F08_param;
1170 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1171 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digout.u32F08_param;
1172 else
1173 LogRel2(("HDA: Warning: Unhandled set unsolicited enabled command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1174
1175 if (pu32Reg)
1176 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1177
1178 return VINF_SUCCESS;
1179}
1180
1181/* F09 */
1182static DECLCALLBACK(int) vrbProcGetPinSense(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1183{
1184 RT_NOREF(pThisCC);
1185 *pResp = 0;
1186
1187 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1188 *pResp = pThis->aNodes[CODEC_NID(cmd)].port.u32F09_param;
1189 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1190 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F09_param;
1191 else
1192 {
1193 AssertFailed();
1194 LogRel2(("HDA: Warning: Unhandled get pin sense command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1195 }
1196
1197 return VINF_SUCCESS;
1198}
1199
1200/* 709 */
1201static DECLCALLBACK(int) vrbProcSetPinSense(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1202{
1203 RT_NOREF(pThisCC);
1204 *pResp = 0;
1205
1206 uint32_t *pu32Reg = NULL;
1207 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1208 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].port.u32F09_param;
1209 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1210 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F09_param;
1211 else
1212 LogRel2(("HDA: Warning: Unhandled set pin sense command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1213
1214 if (pu32Reg)
1215 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1216
1217 return VINF_SUCCESS;
1218}
1219
1220static DECLCALLBACK(int) vrbProcGetConnectionListEntry(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1221{
1222 RT_NOREF(pThisCC);
1223 *pResp = 0;
1224
1225 Assert((cmd & CODEC_VERB_8BIT_DATA) < CODECNODE_F02_PARAM_LENGTH);
1226 if ((cmd & CODEC_VERB_8BIT_DATA) >= CODECNODE_F02_PARAM_LENGTH)
1227 {
1228 LogFlowFunc(("access to invalid F02 index %d\n", (cmd & CODEC_VERB_8BIT_DATA)));
1229 return VINF_SUCCESS;
1230 }
1231 *pResp = pThis->aNodes[CODEC_NID(cmd)].node.au32F02_param[cmd & CODEC_VERB_8BIT_DATA];
1232 return VINF_SUCCESS;
1233}
1234
1235/* F03 */
1236static DECLCALLBACK(int) vrbProcGetProcessingState(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1237{
1238 RT_NOREF(pThisCC);
1239 *pResp = 0;
1240
1241 if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1242 *pResp = pThis->aNodes[CODEC_NID(cmd)].adc.u32F03_param;
1243
1244 return VINF_SUCCESS;
1245}
1246
1247/* 703 */
1248static DECLCALLBACK(int) vrbProcSetProcessingState(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1249{
1250 RT_NOREF(pThisCC);
1251 *pResp = 0;
1252
1253 if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1254 hdaCodecSetRegisterU8(&pThis->aNodes[CODEC_NID(cmd)].adc.u32F03_param, cmd, 0);
1255 return VINF_SUCCESS;
1256}
1257
1258/* F0D */
1259static DECLCALLBACK(int) vrbProcGetDigitalConverter(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1260{
1261 RT_NOREF(pThisCC);
1262 *pResp = 0;
1263
1264 if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1265 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F0d_param;
1266 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1267 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F0d_param;
1268
1269 return VINF_SUCCESS;
1270}
1271
1272static int codecSetDigitalConverter(PHDACODEC pThis, uint32_t cmd, uint8_t u8Offset, uint64_t *pResp)
1273{
1274 *pResp = 0;
1275
1276 if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1277 hdaCodecSetRegisterU8(&pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F0d_param, cmd, u8Offset);
1278 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1279 hdaCodecSetRegisterU8(&pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F0d_param, cmd, u8Offset);
1280 return VINF_SUCCESS;
1281}
1282
1283/* 70D */
1284static DECLCALLBACK(int) vrbProcSetDigitalConverter1(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1285{
1286 RT_NOREF(pThisCC);
1287 return codecSetDigitalConverter(pThis, cmd, 0, pResp);
1288}
1289
1290/* 70E */
1291static DECLCALLBACK(int) vrbProcSetDigitalConverter2(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1292{
1293 RT_NOREF(pThisCC);
1294 return codecSetDigitalConverter(pThis, cmd, 8, pResp);
1295}
1296
1297/* F20 */
1298static DECLCALLBACK(int) vrbProcGetSubId(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1299{
1300 RT_NOREF(pThisCC);
1301 Assert(CODEC_CAD(cmd) == pThis->id);
1302 uint8_t const cTotalNodes = (uint8_t)RT_MIN(pThis->cTotalNodes, RT_ELEMENTS(pThis->aNodes));
1303 Assert(CODEC_NID(cmd) < cTotalNodes);
1304 if (CODEC_NID(cmd) >= cTotalNodes)
1305 {
1306 LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
1307 *pResp = 0;
1308 return VINF_SUCCESS;
1309 }
1310 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1311 *pResp = pThis->aNodes[CODEC_NID(cmd)].afg.u32F20_param;
1312 else
1313 *pResp = 0;
1314 return VINF_SUCCESS;
1315}
1316
1317static int codecSetSubIdX(PHDACODEC pThis, uint32_t cmd, uint8_t u8Offset)
1318{
1319 Assert(CODEC_CAD(cmd) == pThis->id);
1320 uint8_t const cTotalNodes = (uint8_t)RT_MIN(pThis->cTotalNodes, RT_ELEMENTS(pThis->aNodes));
1321 Assert(CODEC_NID(cmd) < cTotalNodes);
1322 if (CODEC_NID(cmd) >= cTotalNodes)
1323 {
1324 LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
1325 return VINF_SUCCESS;
1326 }
1327 uint32_t *pu32Reg;
1328 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1329 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].afg.u32F20_param;
1330 else
1331 AssertFailedReturn(VINF_SUCCESS);
1332 hdaCodecSetRegisterU8(pu32Reg, cmd, u8Offset);
1333 return VINF_SUCCESS;
1334}
1335
1336/* 720 */
1337static DECLCALLBACK(int) vrbProcSetSubId0(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1338{
1339 RT_NOREF(pThisCC);
1340 *pResp = 0;
1341 return codecSetSubIdX(pThis, cmd, 0);
1342}
1343
1344/* 721 */
1345static DECLCALLBACK(int) vrbProcSetSubId1(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1346{
1347 RT_NOREF(pThisCC);
1348 *pResp = 0;
1349 return codecSetSubIdX(pThis, cmd, 8);
1350}
1351
1352/* 722 */
1353static DECLCALLBACK(int) vrbProcSetSubId2(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1354{
1355 RT_NOREF(pThisCC);
1356 *pResp = 0;
1357 return codecSetSubIdX(pThis, cmd, 16);
1358}
1359
1360/* 723 */
1361static DECLCALLBACK(int) vrbProcSetSubId3(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1362{
1363 RT_NOREF(pThisCC);
1364 *pResp = 0;
1365 return codecSetSubIdX(pThis, cmd, 24);
1366}
1367
1368static DECLCALLBACK(int) vrbProcReset(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1369{
1370 RT_NOREF(pThisCC);
1371 Assert(CODEC_CAD(cmd) == pThis->id);
1372
1373 if (pThis->enmType == CODEC_TYPE_STAC9220)
1374 {
1375 Assert(CODEC_NID(cmd) == STAC9220_NID_AFG);
1376
1377 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1378 stac9220Reset(pThis);
1379 }
1380 else
1381 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
1382
1383 *pResp = 0;
1384 return VINF_SUCCESS;
1385}
1386
1387/* F05 */
1388static DECLCALLBACK(int) vrbProcGetPowerState(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1389{
1390 RT_NOREF(pThisCC);
1391 *pResp = 0;
1392
1393 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1394 *pResp = pThis->aNodes[CODEC_NID(cmd)].afg.u32F05_param;
1395 else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1396 *pResp = pThis->aNodes[CODEC_NID(cmd)].dac.u32F05_param;
1397 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1398 *pResp = pThis->aNodes[CODEC_NID(cmd)].adc.u32F05_param;
1399 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1400 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F05_param;
1401 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1402 *pResp = pThis->aNodes[CODEC_NID(cmd)].digout.u32F05_param;
1403 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1404 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F05_param;
1405 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1406 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F05_param;
1407 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1408 *pResp = pThis->aNodes[CODEC_NID(cmd)].reserved.u32F05_param;
1409 else
1410 LogRel2(("HDA: Warning: Unhandled get power state command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1411
1412 LogFunc(("[NID0x%02x]: fReset=%RTbool, fStopOk=%RTbool, Act=D%RU8, Set=D%RU8\n",
1413 CODEC_NID(cmd), CODEC_F05_IS_RESET(*pResp), CODEC_F05_IS_STOPOK(*pResp), CODEC_F05_ACT(*pResp), CODEC_F05_SET(*pResp)));
1414 return VINF_SUCCESS;
1415}
1416
1417/* 705 */
1418#if 1
1419static DECLCALLBACK(int) vrbProcSetPowerState(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1420{
1421 RT_NOREF(pThisCC);
1422 *pResp = 0;
1423
1424 uint32_t *pu32Reg = NULL;
1425 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1426 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].afg.u32F05_param;
1427 else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1428 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].dac.u32F05_param;
1429 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1430 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F05_param;
1431 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1432 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digout.u32F05_param;
1433 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1434 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].adc.u32F05_param;
1435 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1436 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F05_param;
1437 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1438 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F05_param;
1439 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1440 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].reserved.u32F05_param;
1441 else
1442 {
1443 LogRel2(("HDA: Warning: Unhandled set power state command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1444 }
1445
1446 if (!pu32Reg)
1447 return VINF_SUCCESS;
1448
1449 uint8_t uPwrCmd = CODEC_F05_SET (cmd);
1450 bool fReset = CODEC_F05_IS_RESET (*pu32Reg);
1451 bool fStopOk = CODEC_F05_IS_STOPOK(*pu32Reg);
1452#ifdef LOG_ENABLED
1453 bool fError = CODEC_F05_IS_ERROR (*pu32Reg);
1454 uint8_t uPwrAct = CODEC_F05_ACT (*pu32Reg);
1455 uint8_t uPwrSet = CODEC_F05_SET (*pu32Reg);
1456 LogFunc(("[NID0x%02x] Cmd=D%RU8, fReset=%RTbool, fStopOk=%RTbool, fError=%RTbool, uPwrAct=D%RU8, uPwrSet=D%RU8\n",
1457 CODEC_NID(cmd), uPwrCmd, fReset, fStopOk, fError, uPwrAct, uPwrSet));
1458 LogFunc(("AFG: Act=D%RU8, Set=D%RU8\n",
1459 CODEC_F05_ACT(pThis->aNodes[STAC9220_NID_AFG].afg.u32F05_param),
1460 CODEC_F05_SET(pThis->aNodes[STAC9220_NID_AFG].afg.u32F05_param)));
1461#endif
1462
1463 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1464 *pu32Reg = CODEC_MAKE_F05(fReset, fStopOk, 0, uPwrCmd /* PS-Act */, uPwrCmd /* PS-Set */);
1465
1466 const uint8_t uAFGPwrAct = CODEC_F05_ACT(pThis->aNodes[STAC9220_NID_AFG].afg.u32F05_param);
1467 if (uAFGPwrAct == CODEC_F05_D0) /* Only propagate power state if AFG is on (D0). */
1468 {
1469 /* Propagate to all other nodes under this AFG. */
1470 LogFunc(("Propagating Act=D%RU8 (AFG), Set=D%RU8 to all AFG child nodes ...\n", uAFGPwrAct, uPwrCmd));
1471
1472#define PROPAGATE_PWR_STATE(_aList, _aMember) \
1473 { \
1474 const uint8_t *pu8NodeIndex = &_aList[0]; \
1475 while (*(++pu8NodeIndex)) \
1476 { \
1477 pThis->aNodes[*pu8NodeIndex]._aMember.u32F05_param = \
1478 CODEC_MAKE_F05(fReset, fStopOk, 0, uAFGPwrAct, uPwrCmd); \
1479 LogFunc(("\t[NID0x%02x]: Act=D%RU8, Set=D%RU8\n", *pu8NodeIndex, \
1480 CODEC_F05_ACT(pThis->aNodes[*pu8NodeIndex]._aMember.u32F05_param), \
1481 CODEC_F05_SET(pThis->aNodes[*pu8NodeIndex]._aMember.u32F05_param))); \
1482 } \
1483 }
1484
1485 PROPAGATE_PWR_STATE(pThis->au8Dacs, dac);
1486 PROPAGATE_PWR_STATE(pThis->au8Adcs, adc);
1487 PROPAGATE_PWR_STATE(pThis->au8DigInPins, digin);
1488 PROPAGATE_PWR_STATE(pThis->au8DigOutPins, digout);
1489 PROPAGATE_PWR_STATE(pThis->au8SpdifIns, spdifin);
1490 PROPAGATE_PWR_STATE(pThis->au8SpdifOuts, spdifout);
1491 PROPAGATE_PWR_STATE(pThis->au8Reserveds, reserved);
1492
1493#undef PROPAGATE_PWR_STATE
1494 }
1495 /*
1496 * If this node is a reqular node (not the AFG one), adopt PS-Set of the AFG node
1497 * as PS-Set of this node. PS-Act always is one level under PS-Set here.
1498 */
1499 else
1500 {
1501 *pu32Reg = CODEC_MAKE_F05(fReset, fStopOk, 0, uAFGPwrAct, uPwrCmd);
1502 }
1503
1504 LogFunc(("[NID0x%02x] fReset=%RTbool, fStopOk=%RTbool, Act=D%RU8, Set=D%RU8\n",
1505 CODEC_NID(cmd),
1506 CODEC_F05_IS_RESET(*pu32Reg), CODEC_F05_IS_STOPOK(*pu32Reg), CODEC_F05_ACT(*pu32Reg), CODEC_F05_SET(*pu32Reg)));
1507
1508 return VINF_SUCCESS;
1509}
1510#else
1511DECLINLINE(void) codecPropogatePowerState(uint32_t *pu32F05_param)
1512{
1513 Assert(pu32F05_param);
1514 if (!pu32F05_param)
1515 return;
1516 bool fReset = CODEC_F05_IS_RESET(*pu32F05_param);
1517 bool fStopOk = CODEC_F05_IS_STOPOK(*pu32F05_param);
1518 uint8_t u8SetPowerState = CODEC_F05_SET(*pu32F05_param);
1519 *pu32F05_param = CODEC_MAKE_F05(fReset, fStopOk, 0, u8SetPowerState, u8SetPowerState);
1520}
1521
1522static DECLCALLBACK(int) vrbProcSetPowerState(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1523{
1524 RT_NOREF(pThisCC);
1525 Assert(CODEC_CAD(cmd) == pThis->id);
1526 uint8_t const cTotalNodes = (uint8_t)RT_MIN(pThis->cTotalNodes, RT_ELEMENTS(pThis->aNodes));
1527 Assert(CODEC_NID(cmd) < cTotalNodes);
1528 if (CODEC_NID(cmd) >= cTotalNodes)
1529 {
1530 *pResp = 0;
1531 LogFlowFunc(("invalid node address %d\n", CODEC_NID(cmd)));
1532 return VINF_SUCCESS;
1533 }
1534 *pResp = 0;
1535 uint32_t *pu32Reg;
1536 if (CODEC_NID(cmd) == 1 /* AFG */)
1537 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].afg.u32F05_param;
1538 else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1539 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].dac.u32F05_param;
1540 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1541 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F05_param;
1542 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1543 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].adc.u32F05_param;
1544 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1545 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F05_param;
1546 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1547 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F05_param;
1548 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1549 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].reserved.u32F05_param;
1550 else
1551 AssertFailedReturn(VINF_SUCCESS);
1552
1553 bool fReset = CODEC_F05_IS_RESET(*pu32Reg);
1554 bool fStopOk = CODEC_F05_IS_STOPOK(*pu32Reg);
1555
1556 if (CODEC_NID(cmd) != 1 /* AFG */)
1557 {
1558 /*
1559 * We shouldn't propogate actual power state, which actual for AFG
1560 */
1561 *pu32Reg = CODEC_MAKE_F05(fReset, fStopOk, 0,
1562 CODEC_F05_ACT(pThis->aNodes[1].afg.u32F05_param),
1563 CODEC_F05_SET(cmd));
1564 }
1565
1566 /* Propagate next power state only if AFG is on or verb modifies AFG power state */
1567 if ( CODEC_NID(cmd) == 1 /* AFG */
1568 || !CODEC_F05_ACT(pThis->aNodes[1].afg.u32F05_param))
1569 {
1570 *pu32Reg = CODEC_MAKE_F05(fReset, fStopOk, 0, CODEC_F05_SET(cmd), CODEC_F05_SET(cmd));
1571 if ( CODEC_NID(cmd) == 1 /* AFG */
1572 && (CODEC_F05_SET(cmd)) == CODEC_F05_D0)
1573 {
1574 /* now we're powered on AFG and may propogate power states on nodes */
1575 const uint8_t *pu8NodeIndex = &pThis->au8Dacs[0];
1576 while (*(++pu8NodeIndex))
1577 codecPropogatePowerState(&pThis->aNodes[*pu8NodeIndex].dac.u32F05_param);
1578
1579 pu8NodeIndex = &pThis->au8Adcs[0];
1580 while (*(++pu8NodeIndex))
1581 codecPropogatePowerState(&pThis->aNodes[*pu8NodeIndex].adc.u32F05_param);
1582
1583 pu8NodeIndex = &pThis->au8DigInPins[0];
1584 while (*(++pu8NodeIndex))
1585 codecPropogatePowerState(&pThis->aNodes[*pu8NodeIndex].digin.u32F05_param);
1586 }
1587 }
1588 return VINF_SUCCESS;
1589}
1590#endif
1591
1592/* F06 */
1593static DECLCALLBACK(int) vrbProcGetStreamId(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1594{
1595 RT_NOREF(pThisCC);
1596 *pResp = 0;
1597
1598 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1599 *pResp = pThis->aNodes[CODEC_NID(cmd)].dac.u32F06_param;
1600 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1601 *pResp = pThis->aNodes[CODEC_NID(cmd)].adc.u32F06_param;
1602 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1603 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F06_param;
1604 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1605 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F06_param;
1606 else if (CODEC_NID(cmd) == STAC9221_NID_I2S_OUT)
1607 *pResp = pThis->aNodes[CODEC_NID(cmd)].reserved.u32F06_param;
1608 else
1609 LogRel2(("HDA: Warning: Unhandled get stream ID command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1610
1611 LogFlowFunc(("[NID0x%02x] Stream ID=%RU8, channel=%RU8\n",
1612 CODEC_NID(cmd), CODEC_F00_06_GET_STREAM_ID(cmd), CODEC_F00_06_GET_CHANNEL_ID(cmd)));
1613
1614 return VINF_SUCCESS;
1615}
1616
1617/* A0 */
1618static DECLCALLBACK(int) vrbProcGetConverterFormat(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1619{
1620 RT_NOREF(pThisCC);
1621 *pResp = 0;
1622
1623 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1624 *pResp = pThis->aNodes[CODEC_NID(cmd)].dac.u32A_param;
1625 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1626 *pResp = pThis->aNodes[CODEC_NID(cmd)].adc.u32A_param;
1627 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1628 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifout.u32A_param;
1629 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1630 *pResp = pThis->aNodes[CODEC_NID(cmd)].spdifin.u32A_param;
1631 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1632 *pResp = pThis->aNodes[CODEC_NID(cmd)].reserved.u32A_param;
1633 else
1634 LogRel2(("HDA: Warning: Unhandled get converter format command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1635
1636 return VINF_SUCCESS;
1637}
1638
1639/* Also see section 3.7.1. */
1640static DECLCALLBACK(int) vrbProcSetConverterFormat(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1641{
1642 RT_NOREF(pThisCC);
1643 *pResp = 0;
1644
1645 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1646 hdaCodecSetRegisterU16(&pThis->aNodes[CODEC_NID(cmd)].dac.u32A_param, cmd, 0);
1647 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1648 hdaCodecSetRegisterU16(&pThis->aNodes[CODEC_NID(cmd)].adc.u32A_param, cmd, 0);
1649 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
1650 hdaCodecSetRegisterU16(&pThis->aNodes[CODEC_NID(cmd)].spdifout.u32A_param, cmd, 0);
1651 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
1652 hdaCodecSetRegisterU16(&pThis->aNodes[CODEC_NID(cmd)].spdifin.u32A_param, cmd, 0);
1653 else
1654 LogRel2(("HDA: Warning: Unhandled set converter format command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1655
1656 return VINF_SUCCESS;
1657}
1658
1659/* F0C */
1660static DECLCALLBACK(int) vrbProcGetEAPD_BTLEnabled(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1661{
1662 RT_NOREF(pThisCC);
1663 *pResp = 0;
1664
1665 if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
1666 *pResp = pThis->aNodes[CODEC_NID(cmd)].adcvol.u32F0c_param;
1667 else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1668 *pResp = pThis->aNodes[CODEC_NID(cmd)].dac.u32F0c_param;
1669 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1670 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F0c_param;
1671 else
1672 LogRel2(("HDA: Warning: Unhandled get EAPD/BTL enabled command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1673
1674 return VINF_SUCCESS;
1675}
1676
1677/* 70C */
1678static DECLCALLBACK(int) vrbProcSetEAPD_BTLEnabled(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1679{
1680 RT_NOREF(pThisCC);
1681 *pResp = 0;
1682
1683 uint32_t *pu32Reg = NULL;
1684 if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
1685 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].adcvol.u32F0c_param;
1686 else if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1687 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].dac.u32F0c_param;
1688 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1689 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F0c_param;
1690 else
1691 LogRel2(("HDA: Warning: Unhandled set EAPD/BTL enabled command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1692
1693 if (pu32Reg)
1694 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1695
1696 return VINF_SUCCESS;
1697}
1698
1699/* F0F */
1700static DECLCALLBACK(int) vrbProcGetVolumeKnobCtrl(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1701{
1702 RT_NOREF(pThisCC);
1703 *pResp = 0;
1704
1705 if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
1706 *pResp = pThis->aNodes[CODEC_NID(cmd)].volumeKnob.u32F0f_param;
1707 else
1708 LogRel2(("HDA: Warning: Unhandled get volume knob control command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1709
1710 return VINF_SUCCESS;
1711}
1712
1713/* 70F */
1714static DECLCALLBACK(int) vrbProcSetVolumeKnobCtrl(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1715{
1716 RT_NOREF(pThisCC);
1717 *pResp = 0;
1718
1719 uint32_t *pu32Reg = NULL;
1720 if (hdaCodecIsVolKnobNode(pThis, CODEC_NID(cmd)))
1721 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].volumeKnob.u32F0f_param;
1722 else
1723 LogRel2(("HDA: Warning: Unhandled set volume knob control command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1724
1725 if (pu32Reg)
1726 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1727
1728 return VINF_SUCCESS;
1729}
1730
1731/* F15 */
1732static DECLCALLBACK(int) vrbProcGetGPIOData(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1733{
1734 RT_NOREF(pThis, pThisCC, cmd);
1735 *pResp = 0;
1736 return VINF_SUCCESS;
1737}
1738
1739/* 715 */
1740static DECLCALLBACK(int) vrbProcSetGPIOData(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1741{
1742 RT_NOREF(pThis, pThisCC, cmd);
1743 *pResp = 0;
1744 return VINF_SUCCESS;
1745}
1746
1747/* F16 */
1748static DECLCALLBACK(int) vrbProcGetGPIOEnableMask(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1749{
1750 RT_NOREF(pThis, pThisCC, cmd);
1751 *pResp = 0;
1752 return VINF_SUCCESS;
1753}
1754
1755/* 716 */
1756static DECLCALLBACK(int) vrbProcSetGPIOEnableMask(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1757{
1758 RT_NOREF(pThis, pThisCC, cmd);
1759 *pResp = 0;
1760 return VINF_SUCCESS;
1761}
1762
1763/* F17 */
1764static DECLCALLBACK(int) vrbProcGetGPIODirection(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1765{
1766 RT_NOREF(pThisCC);
1767 *pResp = 0;
1768
1769 /* Note: this is true for ALC885. */
1770 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1771 *pResp = pThis->aNodes[1].afg.u32F17_param;
1772 else
1773 LogRel2(("HDA: Warning: Unhandled get GPIO direction command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1774
1775 return VINF_SUCCESS;
1776}
1777
1778/* 717 */
1779static DECLCALLBACK(int) vrbProcSetGPIODirection(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1780{
1781 RT_NOREF(pThisCC);
1782 *pResp = 0;
1783
1784 uint32_t *pu32Reg = NULL;
1785 if (CODEC_NID(cmd) == STAC9220_NID_AFG)
1786 pu32Reg = &pThis->aNodes[1].afg.u32F17_param;
1787 else
1788 LogRel2(("HDA: Warning: Unhandled set GPIO direction command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1789
1790 if (pu32Reg)
1791 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1792
1793 return VINF_SUCCESS;
1794}
1795
1796/* F1C */
1797static DECLCALLBACK(int) vrbProcGetConfig(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1798{
1799 RT_NOREF(pThisCC);
1800 *pResp = 0;
1801
1802 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1803 *pResp = pThis->aNodes[CODEC_NID(cmd)].port.u32F1c_param;
1804 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1805 *pResp = pThis->aNodes[CODEC_NID(cmd)].digout.u32F1c_param;
1806 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1807 *pResp = pThis->aNodes[CODEC_NID(cmd)].digin.u32F1c_param;
1808 else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
1809 *pResp = pThis->aNodes[CODEC_NID(cmd)].pcbeep.u32F1c_param;
1810 else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
1811 *pResp = pThis->aNodes[CODEC_NID(cmd)].cdnode.u32F1c_param;
1812 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1813 *pResp = pThis->aNodes[CODEC_NID(cmd)].reserved.u32F1c_param;
1814 else
1815 LogRel2(("HDA: Warning: Unhandled get config command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1816
1817 return VINF_SUCCESS;
1818}
1819
1820static int codecSetConfigX(PHDACODEC pThis, uint32_t cmd, uint8_t u8Offset)
1821{
1822 uint32_t *pu32Reg = NULL;
1823 if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1824 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].port.u32F1c_param;
1825 else if (hdaCodecIsDigInPinNode(pThis, CODEC_NID(cmd)))
1826 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digin.u32F1c_param;
1827 else if (hdaCodecIsDigOutPinNode(pThis, CODEC_NID(cmd)))
1828 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].digout.u32F1c_param;
1829 else if (hdaCodecIsCdNode(pThis, CODEC_NID(cmd)))
1830 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].cdnode.u32F1c_param;
1831 else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
1832 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].pcbeep.u32F1c_param;
1833 else if (hdaCodecIsReservedNode(pThis, CODEC_NID(cmd)))
1834 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].reserved.u32F1c_param;
1835 else
1836 LogRel2(("HDA: Warning: Unhandled set config command (%RU8) for NID0x%02x: 0x%x\n", u8Offset, CODEC_NID(cmd), cmd));
1837
1838 if (pu32Reg)
1839 hdaCodecSetRegisterU8(pu32Reg, cmd, u8Offset);
1840
1841 return VINF_SUCCESS;
1842}
1843
1844/* 71C */
1845static DECLCALLBACK(int) vrbProcSetConfig0(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1846{
1847 RT_NOREF(pThisCC);
1848 *pResp = 0;
1849 return codecSetConfigX(pThis, cmd, 0);
1850}
1851
1852/* 71D */
1853static DECLCALLBACK(int) vrbProcSetConfig1(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1854{
1855 RT_NOREF(pThisCC);
1856 *pResp = 0;
1857 return codecSetConfigX(pThis, cmd, 8);
1858}
1859
1860/* 71E */
1861static DECLCALLBACK(int) vrbProcSetConfig2(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1862{
1863 RT_NOREF(pThisCC);
1864 *pResp = 0;
1865 return codecSetConfigX(pThis, cmd, 16);
1866}
1867
1868/* 71E */
1869static DECLCALLBACK(int) vrbProcSetConfig3(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1870{
1871 RT_NOREF(pThisCC);
1872 *pResp = 0;
1873 return codecSetConfigX(pThis, cmd, 24);
1874}
1875
1876/* F04 */
1877static DECLCALLBACK(int) vrbProcGetSDISelect(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1878{
1879 RT_NOREF(pThisCC);
1880 *pResp = 0;
1881
1882 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1883 *pResp = pThis->aNodes[CODEC_NID(cmd)].dac.u32F04_param;
1884 else
1885 LogRel2(("HDA: Warning: Unhandled get SDI select command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1886
1887 return VINF_SUCCESS;
1888}
1889
1890/* 704 */
1891static DECLCALLBACK(int) vrbProcSetSDISelect(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1892{
1893 RT_NOREF(pThisCC);
1894 *pResp = 0;
1895
1896 uint32_t *pu32Reg = NULL;
1897 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1898 pu32Reg = &pThis->aNodes[CODEC_NID(cmd)].dac.u32F04_param;
1899 else
1900 LogRel2(("HDA: Warning: Unhandled set SDI select command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
1901
1902 if (pu32Reg)
1903 hdaCodecSetRegisterU8(pu32Reg, cmd, 0);
1904
1905 return VINF_SUCCESS;
1906}
1907
1908#ifdef IN_RING3
1909
1910/* 3-- */
1911static DECLCALLBACK(int) vrbProcR3SetAmplifier(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1912{
1913 *pResp = 0;
1914
1915 PCODECNODE pNode = &pThis->aNodes[CODEC_NID(cmd)];
1916 AMPLIFIER *pAmplifier = NULL;
1917 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1918 pAmplifier = &pNode->dac.B_params;
1919 else if (hdaCodecIsAdcVolNode(pThis, CODEC_NID(cmd)))
1920 pAmplifier = &pNode->adcvol.B_params;
1921 else if (hdaCodecIsAdcMuxNode(pThis, CODEC_NID(cmd)))
1922 pAmplifier = &pNode->adcmux.B_params;
1923 else if (hdaCodecIsPcbeepNode(pThis, CODEC_NID(cmd)))
1924 pAmplifier = &pNode->pcbeep.B_params;
1925 else if (hdaCodecIsPortNode(pThis, CODEC_NID(cmd)))
1926 pAmplifier = &pNode->port.B_params;
1927 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1928 pAmplifier = &pNode->adc.B_params;
1929 else
1930 LogRel2(("HDA: Warning: Unhandled set amplifier command: 0x%x (Payload=%RU16, NID=0x%x [%RU8])\n",
1931 cmd, CODEC_VERB_PAYLOAD16(cmd), CODEC_NID(cmd), CODEC_NID(cmd)));
1932
1933 if (!pAmplifier)
1934 return VINF_SUCCESS;
1935
1936 bool fIsOut = CODEC_SET_AMP_IS_OUT_DIRECTION(cmd);
1937 bool fIsIn = CODEC_SET_AMP_IS_IN_DIRECTION(cmd);
1938 bool fIsLeft = CODEC_SET_AMP_IS_LEFT_SIDE(cmd);
1939 bool fIsRight = CODEC_SET_AMP_IS_RIGHT_SIDE(cmd);
1940 uint8_t u8Index = CODEC_SET_AMP_INDEX(cmd);
1941
1942 if ( (!fIsLeft && !fIsRight)
1943 || (!fIsOut && !fIsIn))
1944 return VINF_SUCCESS;
1945
1946 LogFunc(("[NID0x%02x] fIsOut=%RTbool, fIsIn=%RTbool, fIsLeft=%RTbool, fIsRight=%RTbool, Idx=%RU8\n",
1947 CODEC_NID(cmd), fIsOut, fIsIn, fIsLeft, fIsRight, u8Index));
1948
1949 if (fIsIn)
1950 {
1951 if (fIsLeft)
1952 hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_IN, AMPLIFIER_LEFT, u8Index), cmd, 0);
1953 if (fIsRight)
1954 hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_IN, AMPLIFIER_RIGHT, u8Index), cmd, 0);
1955
1956 // if (CODEC_NID(cmd) == pThis->u8AdcVolsLineIn)
1957 // {
1958 hdaR3CodecToAudVolume(pThisCC, pNode, pAmplifier, PDMAUDIOMIXERCTL_LINE_IN);
1959 // }
1960 }
1961 if (fIsOut)
1962 {
1963 if (fIsLeft)
1964 hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_OUT, AMPLIFIER_LEFT, u8Index), cmd, 0);
1965 if (fIsRight)
1966 hdaCodecSetRegisterU8(&AMPLIFIER_REGISTER(*pAmplifier, AMPLIFIER_OUT, AMPLIFIER_RIGHT, u8Index), cmd, 0);
1967
1968 if (CODEC_NID(cmd) == pThis->u8DacLineOut)
1969 hdaR3CodecToAudVolume(pThisCC, pNode, pAmplifier, PDMAUDIOMIXERCTL_FRONT);
1970 }
1971
1972 return VINF_SUCCESS;
1973}
1974
1975/* 706 */
1976static DECLCALLBACK(int) vrbProcR3SetStreamId(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t cmd, uint64_t *pResp)
1977{
1978 *pResp = 0;
1979
1980 uint8_t uSD = CODEC_F00_06_GET_STREAM_ID(cmd);
1981 uint8_t uChannel = CODEC_F00_06_GET_CHANNEL_ID(cmd);
1982
1983 LogFlowFunc(("[NID0x%02x] Setting to stream ID=%RU8, channel=%RU8\n",
1984 CODEC_NID(cmd), uSD, uChannel));
1985
1986 ASSERT_GUEST_LOGREL_MSG_RETURN(uSD < HDA_MAX_STREAMS,
1987 ("Setting stream ID #%RU8 is invalid\n", uSD), VERR_INVALID_PARAMETER);
1988
1989 PDMAUDIODIR enmDir;
1990 uint32_t *pu32Addr;
1991 if (hdaCodecIsDacNode(pThis, CODEC_NID(cmd)))
1992 {
1993 pu32Addr = &pThis->aNodes[CODEC_NID(cmd)].dac.u32F06_param;
1994 enmDir = PDMAUDIODIR_OUT;
1995 }
1996 else if (hdaCodecIsAdcNode(pThis, CODEC_NID(cmd)))
1997 {
1998 pu32Addr = &pThis->aNodes[CODEC_NID(cmd)].adc.u32F06_param;
1999 enmDir = PDMAUDIODIR_IN;
2000 }
2001 else if (hdaCodecIsSpdifOutNode(pThis, CODEC_NID(cmd)))
2002 {
2003 pu32Addr = &pThis->aNodes[CODEC_NID(cmd)].spdifout.u32F06_param;
2004 enmDir = PDMAUDIODIR_OUT;
2005 }
2006 else if (hdaCodecIsSpdifInNode(pThis, CODEC_NID(cmd)))
2007 {
2008 pu32Addr = &pThis->aNodes[CODEC_NID(cmd)].spdifin.u32F06_param;
2009 enmDir = PDMAUDIODIR_IN;
2010 }
2011 else
2012 {
2013 enmDir = PDMAUDIODIR_UNKNOWN;
2014 LogRel2(("HDA: Warning: Unhandled set stream ID command for NID0x%02x: 0x%x\n", CODEC_NID(cmd), cmd));
2015 return VINF_SUCCESS;
2016 }
2017
2018 /* Do we (re-)assign our input/output SDn (SDI/SDO) IDs? */
2019 pThis->aNodes[CODEC_NID(cmd)].node.uSD = uSD;
2020 pThis->aNodes[CODEC_NID(cmd)].node.uChannel = uChannel;
2021
2022 if (enmDir == PDMAUDIODIR_OUT)
2023 {
2024 /** @todo Check if non-interleaved streams need a different channel / SDn? */
2025
2026 /* Propagate to the controller. */
2027 pThisCC->pfnCbMixerControl(pThisCC->pDevIns, PDMAUDIOMIXERCTL_FRONT, uSD, uChannel);
2028# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
2029 pThisCC->pfnCbMixerControl(pThisCC->pDevIns, PDMAUDIOMIXERCTL_CENTER_LFE, uSD, uChannel);
2030 pThisCC->pfnCbMixerControl(pThisCC->pDevIns, PDMAUDIOMIXERCTL_REAR, uSD, uChannel);
2031# endif
2032 }
2033 else if (enmDir == PDMAUDIODIR_IN)
2034 {
2035 pThisCC->pfnCbMixerControl(pThisCC->pDevIns, PDMAUDIOMIXERCTL_LINE_IN, uSD, uChannel);
2036# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
2037 pThisCC->pfnCbMixerControl(pThisCC->pDevIns, PDMAUDIOMIXERCTL_MIC_IN, uSD, uChannel);
2038# endif
2039 }
2040
2041 hdaCodecSetRegisterU8(pu32Addr, cmd, 0);
2042
2043 return VINF_SUCCESS;
2044}
2045
2046#endif /* IN_RING3 */
2047
2048
2049/**
2050 * HDA codec verb descriptors.
2051 *
2052 * @note This must be ordered by uVerb so we can do a binary lookup.
2053 */
2054static const CODECVERB g_aCodecVerbs[] =
2055{
2056 /* Verb Verb mask Callback Name
2057 ---------- --------------------- ------------------------------------------------------------------- */
2058 { 0x00020000, CODEC_VERB_16BIT_CMD, vrbProcSetConverterFormat , "SetConverterFormat " },
2059 { 0x00030000, CODEC_VERB_16BIT_CMD, CTX_EXPR(vrbProcR3SetAmplifier,NULL,NULL), "SetAmplifier " },
2060 { 0x00070100, CODEC_VERB_8BIT_CMD , vrbProcSetConSelectCtrl , "SetConSelectCtrl " },
2061 { 0x00070300, CODEC_VERB_8BIT_CMD , vrbProcSetProcessingState , "SetProcessingState " },
2062 { 0x00070400, CODEC_VERB_8BIT_CMD , vrbProcSetSDISelect , "SetSDISelect " },
2063 { 0x00070500, CODEC_VERB_8BIT_CMD , vrbProcSetPowerState , "SetPowerState " },
2064 { 0x00070600, CODEC_VERB_8BIT_CMD , CTX_EXPR(vrbProcR3SetStreamId,NULL,NULL) , "SetStreamId " },
2065 { 0x00070700, CODEC_VERB_8BIT_CMD , vrbProcSetPinCtrl , "SetPinCtrl " },
2066 { 0x00070800, CODEC_VERB_8BIT_CMD , vrbProcSetUnsolicitedEnabled , "SetUnsolicitedEnabled " },
2067 { 0x00070900, CODEC_VERB_8BIT_CMD , vrbProcSetPinSense , "SetPinSense " },
2068 { 0x00070C00, CODEC_VERB_8BIT_CMD , vrbProcSetEAPD_BTLEnabled , "SetEAPD_BTLEnabled " },
2069 { 0x00070D00, CODEC_VERB_8BIT_CMD , vrbProcSetDigitalConverter1 , "SetDigitalConverter1 " },
2070 { 0x00070E00, CODEC_VERB_8BIT_CMD , vrbProcSetDigitalConverter2 , "SetDigitalConverter2 " },
2071 { 0x00070F00, CODEC_VERB_8BIT_CMD , vrbProcSetVolumeKnobCtrl , "SetVolumeKnobCtrl " },
2072 { 0x00071500, CODEC_VERB_8BIT_CMD , vrbProcSetGPIOData , "SetGPIOData " },
2073 { 0x00071600, CODEC_VERB_8BIT_CMD , vrbProcSetGPIOEnableMask , "SetGPIOEnableMask " },
2074 { 0x00071700, CODEC_VERB_8BIT_CMD , vrbProcSetGPIODirection , "SetGPIODirection " },
2075 { 0x00071C00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig0 , "SetConfig0 " },
2076 { 0x00071D00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig1 , "SetConfig1 " },
2077 { 0x00071E00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig2 , "SetConfig2 " },
2078 { 0x00071F00, CODEC_VERB_8BIT_CMD , vrbProcSetConfig3 , "SetConfig3 " },
2079 { 0x00072000, CODEC_VERB_8BIT_CMD , vrbProcSetSubId0 , "SetSubId0 " },
2080 { 0x00072100, CODEC_VERB_8BIT_CMD , vrbProcSetSubId1 , "SetSubId1 " },
2081 { 0x00072200, CODEC_VERB_8BIT_CMD , vrbProcSetSubId2 , "SetSubId2 " },
2082 { 0x00072300, CODEC_VERB_8BIT_CMD , vrbProcSetSubId3 , "SetSubId3 " },
2083 { 0x0007FF00, CODEC_VERB_8BIT_CMD , vrbProcReset , "Reset " },
2084 { 0x000A0000, CODEC_VERB_16BIT_CMD, vrbProcGetConverterFormat , "GetConverterFormat " },
2085 { 0x000B0000, CODEC_VERB_16BIT_CMD, vrbProcGetAmplifier , "GetAmplifier " },
2086 { 0x000F0000, CODEC_VERB_8BIT_CMD , vrbProcGetParameter , "GetParameter " },
2087 { 0x000F0100, CODEC_VERB_8BIT_CMD , vrbProcGetConSelectCtrl , "GetConSelectCtrl " },
2088 { 0x000F0200, CODEC_VERB_8BIT_CMD , vrbProcGetConnectionListEntry , "GetConnectionListEntry" },
2089 { 0x000F0300, CODEC_VERB_8BIT_CMD , vrbProcGetProcessingState , "GetProcessingState " },
2090 { 0x000F0400, CODEC_VERB_8BIT_CMD , vrbProcGetSDISelect , "GetSDISelect " },
2091 { 0x000F0500, CODEC_VERB_8BIT_CMD , vrbProcGetPowerState , "GetPowerState " },
2092 { 0x000F0600, CODEC_VERB_8BIT_CMD , vrbProcGetStreamId , "GetStreamId " },
2093 { 0x000F0700, CODEC_VERB_8BIT_CMD , vrbProcGetPinCtrl , "GetPinCtrl " },
2094 { 0x000F0800, CODEC_VERB_8BIT_CMD , vrbProcGetUnsolicitedEnabled , "GetUnsolicitedEnabled " },
2095 { 0x000F0900, CODEC_VERB_8BIT_CMD , vrbProcGetPinSense , "GetPinSense " },
2096 { 0x000F0C00, CODEC_VERB_8BIT_CMD , vrbProcGetEAPD_BTLEnabled , "GetEAPD_BTLEnabled " },
2097 { 0x000F0D00, CODEC_VERB_8BIT_CMD , vrbProcGetDigitalConverter , "GetDigitalConverter " },
2098 { 0x000F0F00, CODEC_VERB_8BIT_CMD , vrbProcGetVolumeKnobCtrl , "GetVolumeKnobCtrl " },
2099 { 0x000F1500, CODEC_VERB_8BIT_CMD , vrbProcGetGPIOData , "GetGPIOData " },
2100 { 0x000F1600, CODEC_VERB_8BIT_CMD , vrbProcGetGPIOEnableMask , "GetGPIOEnableMask " },
2101 { 0x000F1700, CODEC_VERB_8BIT_CMD , vrbProcGetGPIODirection , "GetGPIODirection " },
2102 { 0x000F1C00, CODEC_VERB_8BIT_CMD , vrbProcGetConfig , "GetConfig " },
2103 { 0x000F2000, CODEC_VERB_8BIT_CMD , vrbProcGetSubId , "GetSubId " },
2104 /** @todo Implement 0x7e7: IDT Set GPIO (STAC922x only). */
2105};
2106
2107
2108#ifdef IN_RING3
2109
2110/**
2111 * CODEC debug info item printing state.
2112 */
2113typedef struct CODECDEBUG
2114{
2115 /** DBGF info helpers. */
2116 PCDBGFINFOHLP pHlp;
2117 /** Current recursion level. */
2118 uint8_t uLevel;
2119 /** Pointer to codec state. */
2120 PHDACODEC pThis;
2121} CODECDEBUG;
2122/** Pointer to the debug info item printing state for the codec. */
2123typedef CODECDEBUG *PCODECDEBUG;
2124
2125#define CODECDBG_INDENT pInfo->uLevel++;
2126#define CODECDBG_UNINDENT if (pInfo->uLevel) pInfo->uLevel--;
2127
2128#define CODECDBG_PRINT(...) pInfo->pHlp->pfnPrintf(pInfo->pHlp, __VA_ARGS__)
2129#define CODECDBG_PRINTI(...) codecDbgPrintf(pInfo, __VA_ARGS__)
2130
2131/** Wrapper around DBGFINFOHLP::pfnPrintf that adds identation. */
2132static void codecDbgPrintf(PCODECDEBUG pInfo, const char *pszFormat, ...)
2133{
2134 va_list va;
2135 va_start(va, pszFormat);
2136 pInfo->pHlp->pfnPrintf(pInfo->pHlp, "%*s%N", pInfo->uLevel * 4, "", pszFormat, &va);
2137 va_end(va);
2138}
2139
2140/** Power state */
2141static void codecDbgPrintNodeRegF05(PCODECDEBUG pInfo, uint32_t u32Reg)
2142{
2143 codecDbgPrintf(pInfo, "Power (F05): fReset=%RTbool, fStopOk=%RTbool, Set=%RU8, Act=%RU8\n",
2144 CODEC_F05_IS_RESET(u32Reg), CODEC_F05_IS_STOPOK(u32Reg), CODEC_F05_SET(u32Reg), CODEC_F05_ACT(u32Reg));
2145}
2146
2147static void codecDbgPrintNodeRegA(PCODECDEBUG pInfo, uint32_t u32Reg)
2148{
2149 codecDbgPrintf(pInfo, "RegA: %x\n", u32Reg);
2150}
2151
2152static void codecDbgPrintNodeRegF00(PCODECDEBUG pInfo, uint32_t *paReg00)
2153{
2154 codecDbgPrintf(pInfo, "Parameters (F00):\n");
2155
2156 CODECDBG_INDENT
2157 codecDbgPrintf(pInfo, "Connections: %RU8\n", CODEC_F00_0E_COUNT(paReg00[0xE]));
2158 codecDbgPrintf(pInfo, "Amplifier Caps:\n");
2159 uint32_t uReg = paReg00[0xD];
2160 CODECDBG_INDENT
2161 codecDbgPrintf(pInfo, "Input Steps=%02RU8, StepSize=%02RU8, StepOff=%02RU8, fCanMute=%RTbool\n",
2162 CODEC_F00_0D_NUM_STEPS(uReg),
2163 CODEC_F00_0D_STEP_SIZE(uReg),
2164 CODEC_F00_0D_OFFSET(uReg),
2165 RT_BOOL(CODEC_F00_0D_IS_CAP_MUTE(uReg)));
2166
2167 uReg = paReg00[0x12];
2168 codecDbgPrintf(pInfo, "Output Steps=%02RU8, StepSize=%02RU8, StepOff=%02RU8, fCanMute=%RTbool\n",
2169 CODEC_F00_12_NUM_STEPS(uReg),
2170 CODEC_F00_12_STEP_SIZE(uReg),
2171 CODEC_F00_12_OFFSET(uReg),
2172 RT_BOOL(CODEC_F00_12_IS_CAP_MUTE(uReg)));
2173 CODECDBG_UNINDENT
2174 CODECDBG_UNINDENT
2175}
2176
2177static void codecDbgPrintNodeAmp(PCODECDEBUG pInfo, uint32_t *paReg, uint8_t uIdx, uint8_t uDir)
2178{
2179# define CODECDBG_AMP(reg, chan) \
2180 codecDbgPrintf(pInfo, "Amp %RU8 %s %s: In=%RTbool, Out=%RTbool, Left=%RTbool, Right=%RTbool, Idx=%RU8, fMute=%RTbool, uGain=%RU8\n", \
2181 uIdx, chan, uDir == AMPLIFIER_IN ? "In" : "Out", \
2182 RT_BOOL(CODEC_SET_AMP_IS_IN_DIRECTION(reg)), RT_BOOL(CODEC_SET_AMP_IS_OUT_DIRECTION(reg)), \
2183 RT_BOOL(CODEC_SET_AMP_IS_LEFT_SIDE(reg)), RT_BOOL(CODEC_SET_AMP_IS_RIGHT_SIDE(reg)), \
2184 CODEC_SET_AMP_INDEX(reg), RT_BOOL(CODEC_SET_AMP_MUTE(reg)), CODEC_SET_AMP_GAIN(reg))
2185
2186 uint32_t regAmp = AMPLIFIER_REGISTER(paReg, uDir, AMPLIFIER_LEFT, uIdx);
2187 CODECDBG_AMP(regAmp, "Left");
2188 regAmp = AMPLIFIER_REGISTER(paReg, uDir, AMPLIFIER_RIGHT, uIdx);
2189 CODECDBG_AMP(regAmp, "Right");
2190
2191# undef CODECDBG_AMP
2192}
2193
2194# if 0 /* unused */
2195static void codecDbgPrintNodeConnections(PCODECDEBUG pInfo, PCODECNODE pNode)
2196{
2197 if (pNode->node.au32F00_param[0xE] == 0) /* Directly connected to HDA link. */
2198 {
2199 codecDbgPrintf(pInfo, "[HDA LINK]\n");
2200 return;
2201 }
2202}
2203# endif
2204
2205static void codecDbgPrintNode(PCODECDEBUG pInfo, PCODECNODE pNode, bool fRecursive)
2206{
2207 codecDbgPrintf(pInfo, "Node 0x%02x (%02RU8): ", pNode->node.uID, pNode->node.uID);
2208
2209 if (pNode->node.uID == STAC9220_NID_ROOT)
2210 {
2211 CODECDBG_PRINT("ROOT\n");
2212 }
2213 else if (pNode->node.uID == STAC9220_NID_AFG)
2214 {
2215 CODECDBG_PRINT("AFG\n");
2216 CODECDBG_INDENT
2217 codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
2218 codecDbgPrintNodeRegF05(pInfo, pNode->afg.u32F05_param);
2219 CODECDBG_UNINDENT
2220 }
2221 else if (hdaCodecIsPortNode(pInfo->pThis, pNode->node.uID))
2222 {
2223 CODECDBG_PRINT("PORT\n");
2224 }
2225 else if (hdaCodecIsDacNode(pInfo->pThis, pNode->node.uID))
2226 {
2227 CODECDBG_PRINT("DAC\n");
2228 CODECDBG_INDENT
2229 codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
2230 codecDbgPrintNodeRegF05(pInfo, pNode->dac.u32F05_param);
2231 codecDbgPrintNodeRegA (pInfo, pNode->dac.u32A_param);
2232 codecDbgPrintNodeAmp (pInfo, pNode->dac.B_params, 0, AMPLIFIER_OUT);
2233 CODECDBG_UNINDENT
2234 }
2235 else if (hdaCodecIsAdcVolNode(pInfo->pThis, pNode->node.uID))
2236 {
2237 CODECDBG_PRINT("ADC VOLUME\n");
2238 CODECDBG_INDENT
2239 codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
2240 codecDbgPrintNodeRegA (pInfo, pNode->adcvol.u32A_params);
2241 codecDbgPrintNodeAmp (pInfo, pNode->adcvol.B_params, 0, AMPLIFIER_IN);
2242 CODECDBG_UNINDENT
2243 }
2244 else if (hdaCodecIsAdcNode(pInfo->pThis, pNode->node.uID))
2245 {
2246 CODECDBG_PRINT("ADC\n");
2247 CODECDBG_INDENT
2248 codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
2249 codecDbgPrintNodeRegF05(pInfo, pNode->adc.u32F05_param);
2250 codecDbgPrintNodeRegA (pInfo, pNode->adc.u32A_param);
2251 codecDbgPrintNodeAmp (pInfo, pNode->adc.B_params, 0, AMPLIFIER_IN);
2252 CODECDBG_UNINDENT
2253 }
2254 else if (hdaCodecIsAdcMuxNode(pInfo->pThis, pNode->node.uID))
2255 {
2256 CODECDBG_PRINT("ADC MUX\n");
2257 CODECDBG_INDENT
2258 codecDbgPrintNodeRegF00(pInfo, pNode->node.au32F00_param);
2259 codecDbgPrintNodeRegA (pInfo, pNode->adcmux.u32A_param);
2260 codecDbgPrintNodeAmp (pInfo, pNode->adcmux.B_params, 0, AMPLIFIER_IN);
2261 CODECDBG_UNINDENT
2262 }
2263 else if (hdaCodecIsPcbeepNode(pInfo->pThis, pNode->node.uID))
2264 CODECDBG_PRINT("PC BEEP\n");
2265 else if (hdaCodecIsSpdifOutNode(pInfo->pThis, pNode->node.uID))
2266 CODECDBG_PRINT("SPDIF OUT\n");
2267 else if (hdaCodecIsSpdifInNode(pInfo->pThis, pNode->node.uID))
2268 CODECDBG_PRINT("SPDIF IN\n");
2269 else if (hdaCodecIsDigInPinNode(pInfo->pThis, pNode->node.uID))
2270 CODECDBG_PRINT("DIGITAL IN PIN\n");
2271 else if (hdaCodecIsDigOutPinNode(pInfo->pThis, pNode->node.uID))
2272 CODECDBG_PRINT("DIGITAL OUT PIN\n");
2273 else if (hdaCodecIsCdNode(pInfo->pThis, pNode->node.uID))
2274 CODECDBG_PRINT("CD\n");
2275 else if (hdaCodecIsVolKnobNode(pInfo->pThis, pNode->node.uID))
2276 CODECDBG_PRINT("VOLUME KNOB\n");
2277 else if (hdaCodecIsReservedNode(pInfo->pThis, pNode->node.uID))
2278 CODECDBG_PRINT("RESERVED\n");
2279 else
2280 CODECDBG_PRINT("UNKNOWN TYPE 0x%x\n", pNode->node.uID);
2281
2282 if (fRecursive)
2283 {
2284# define CODECDBG_PRINT_CONLIST_ENTRY(_aNode, _aEntry) \
2285 if (cCnt >= _aEntry) \
2286 { \
2287 const uint8_t uID = RT_BYTE##_aEntry(_aNode->node.au32F02_param[0x0]); \
2288 if (pNode->node.uID == uID) \
2289 codecDbgPrintNode(pInfo, _aNode, false /* fRecursive */); \
2290 }
2291
2292 /* Slow recursion, but this is debug stuff anyway. */
2293 for (uint8_t i = 0; i < pInfo->pThis->cTotalNodes; i++)
2294 {
2295 const PCODECNODE pSubNode = &pInfo->pThis->aNodes[i];
2296 if (pSubNode->node.uID == pNode->node.uID)
2297 continue;
2298
2299 const uint8_t cCnt = CODEC_F00_0E_COUNT(pSubNode->node.au32F00_param[0xE]);
2300 if (cCnt == 0) /* No connections present? Skip. */
2301 continue;
2302
2303 CODECDBG_INDENT
2304 CODECDBG_PRINT_CONLIST_ENTRY(pSubNode, 1)
2305 CODECDBG_PRINT_CONLIST_ENTRY(pSubNode, 2)
2306 CODECDBG_PRINT_CONLIST_ENTRY(pSubNode, 3)
2307 CODECDBG_PRINT_CONLIST_ENTRY(pSubNode, 4)
2308 CODECDBG_UNINDENT
2309 }
2310
2311# undef CODECDBG_PRINT_CONLIST_ENTRY
2312 }
2313}
2314
2315static DECLCALLBACK(void) codecR3DbgListNodes(PHDACODEC pThis, PHDACODECR3 pThisCC, PCDBGFINFOHLP pHlp, const char *pszArgs)
2316{
2317 RT_NOREF(pThisCC, pszArgs);
2318
2319 pHlp->pfnPrintf(pHlp, "HDA LINK / INPUTS\n");
2320
2321 CODECDEBUG dbgInfo;
2322 dbgInfo.pHlp = pHlp;
2323 dbgInfo.pThis = pThis;
2324 dbgInfo.uLevel = 0;
2325
2326 PCODECDEBUG pInfo = &dbgInfo;
2327
2328 CODECDBG_INDENT
2329 for (uint8_t i = 0; i < pThis->cTotalNodes; i++)
2330 {
2331 PCODECNODE pNode = &pThis->aNodes[i];
2332
2333 /* Start with all nodes which have connection entries set. */
2334 if (CODEC_F00_0E_COUNT(pNode->node.au32F00_param[0xE]))
2335 codecDbgPrintNode(&dbgInfo, pNode, true /* fRecursive */);
2336 }
2337 CODECDBG_UNINDENT
2338}
2339
2340static DECLCALLBACK(void) codecR3DbgSelector(PHDACODEC pThis, PHDACODECR3 pThisCC, PCDBGFINFOHLP pHlp, const char *pszArgs)
2341{
2342 RT_NOREF(pThis, pThisCC, pHlp, pszArgs);
2343}
2344
2345#endif /* IN_RING3 */
2346
2347/**
2348 * Implements
2349 */
2350static DECLCALLBACK(int) codecLookup(PHDACODEC pThis, PHDACODECCC pThisCC, uint32_t uCmd, uint64_t *puResp)
2351{
2352 /*
2353 * Clear the return value and assert some sanity.
2354 */
2355 AssertPtr(puResp);
2356 *puResp = 0;
2357 AssertPtr(pThis);
2358 AssertPtr(pThisCC);
2359 AssertMsgReturn(CODEC_CAD(uCmd) == pThis->id,
2360 ("Unknown codec address 0x%x\n", CODEC_CAD(uCmd)),
2361 VERR_INVALID_PARAMETER);
2362 uint32_t const uCmdData = CODEC_VERBDATA(uCmd);
2363 AssertMsgReturn( uCmdData != 0
2364 && CODEC_NID(uCmd) < RT_MIN(pThis->cTotalNodes, RT_ELEMENTS(pThis->aNodes)),
2365 ("[NID0x%02x] Unknown / invalid node or data (0x%x)\n", CODEC_NID(uCmd), uCmdData),
2366 VERR_INVALID_PARAMETER);
2367 STAM_COUNTER_INC(&pThis->CTX_SUFF(StatLookups));
2368
2369 /*
2370 * Do a binary lookup of the verb.
2371 * Note! if we want other verb tables, add a table selector before the loop.
2372 */
2373 size_t iFirst = 0;
2374 size_t iEnd = RT_ELEMENTS(g_aCodecVerbs);
2375 for (;;)
2376 {
2377 size_t const iCur = iFirst + (iEnd - iFirst) / 2;
2378 uint32_t const uVerb = g_aCodecVerbs[iCur].uVerb;
2379 if (uCmdData < uVerb)
2380 {
2381 if (iCur > iFirst)
2382 iEnd = iCur;
2383 else
2384 break;
2385 }
2386 else if ((uCmdData & g_aCodecVerbs[iCur].fMask) != uVerb)
2387 {
2388 if (iCur + 1 < iEnd)
2389 iFirst = iCur + 1;
2390 else
2391 break;
2392 }
2393 else
2394 {
2395 /*
2396 * Found it! Run the callback and return.
2397 */
2398#ifndef IN_RING3
2399 if (!g_aCodecVerbs[iCur].pfn)
2400 {
2401 Log3Func(("[NID0x%02x] (0x%x) %s: 0x%x -> VERR_INVALID_CONTEXT\n", /* -> ring-3 */
2402 CODEC_NID(uCmd), g_aCodecVerbs[iCur].uVerb, g_aCodecVerbs[iCur].pszName, CODEC_VERB_PAYLOAD8(uCmd)));
2403 return VERR_INVALID_CONTEXT;
2404 }
2405#endif
2406 AssertPtrReturn(g_aCodecVerbs[iCur].pfn, VERR_INTERNAL_ERROR_5); /* Paranoia^2. */
2407
2408 int rc = g_aCodecVerbs[iCur].pfn(pThis, pThisCC, uCmd, puResp);
2409 AssertRC(rc);
2410 Log3Func(("[NID0x%02x] (0x%x) %s: 0x%x -> 0x%x\n",
2411 CODEC_NID(uCmd), g_aCodecVerbs[iCur].uVerb, g_aCodecVerbs[iCur].pszName, CODEC_VERB_PAYLOAD8(uCmd), *puResp));
2412 return rc;
2413 }
2414 }
2415
2416#ifdef VBOX_STRICT
2417 for (size_t i = 0; i < RT_ELEMENTS(g_aCodecVerbs); i++)
2418 {
2419 AssertMsg(i == 0 || g_aCodecVerbs[i - 1].uVerb < g_aCodecVerbs[i].uVerb,
2420 ("i=%#x uVerb[-1]=%#x uVerb=%#x - buggy table!\n", i, g_aCodecVerbs[i - 1].uVerb, g_aCodecVerbs[i].uVerb));
2421 AssertMsg((uCmdData & g_aCodecVerbs[i].fMask) != g_aCodecVerbs[i].uVerb,
2422 ("i=%#x uVerb=%#x uCmd=%#x - buggy binary search or table!\n", i, g_aCodecVerbs[i].uVerb, uCmd));
2423 }
2424#endif
2425 LogFunc(("[NID0x%02x] Callback for %x not found\n", CODEC_NID(uCmd), CODEC_VERBDATA(uCmd)));
2426 return VERR_NOT_FOUND;
2427}
2428
2429
2430/*
2431 * APIs exposed to DevHDA.
2432 */
2433
2434#ifdef IN_RING3
2435
2436int hdaR3CodecAddStream(PHDACODECR3 pThisCC, PDMAUDIOMIXERCTL enmMixerCtl, PPDMAUDIOSTREAMCFG pCfg)
2437{
2438 AssertPtrReturn(pThisCC, VERR_INVALID_POINTER);
2439 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2440
2441 int rc = VINF_SUCCESS;
2442
2443 switch (enmMixerCtl)
2444 {
2445 case PDMAUDIOMIXERCTL_VOLUME_MASTER:
2446 case PDMAUDIOMIXERCTL_FRONT:
2447#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
2448 case PDMAUDIOMIXERCTL_CENTER_LFE:
2449 case PDMAUDIOMIXERCTL_REAR:
2450#endif
2451 break;
2452
2453 case PDMAUDIOMIXERCTL_LINE_IN:
2454#ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
2455 case PDMAUDIOMIXERCTL_MIC_IN:
2456#endif
2457 break;
2458
2459 default:
2460 AssertMsgFailed(("Mixer control %#x not implemented\n", enmMixerCtl));
2461 rc = VERR_NOT_IMPLEMENTED;
2462 break;
2463 }
2464
2465 if (RT_SUCCESS(rc))
2466 rc = pThisCC->pfnCbMixerAddStream(pThisCC->pDevIns, enmMixerCtl, pCfg);
2467
2468 LogFlowFuncLeaveRC(rc);
2469 return rc;
2470}
2471
2472int hdaR3CodecRemoveStream(PHDACODECR3 pThisCC, PDMAUDIOMIXERCTL enmMixerCtl, bool fImmediate)
2473{
2474 AssertPtrReturn(pThisCC, VERR_INVALID_POINTER);
2475
2476 int rc = pThisCC->pfnCbMixerRemoveStream(pThisCC->pDevIns, enmMixerCtl, fImmediate);
2477
2478 LogFlowFuncLeaveRC(rc);
2479 return rc;
2480}
2481
2482int hdaCodecSaveState(PPDMDEVINS pDevIns, PHDACODEC pThis, PSSMHANDLE pSSM)
2483{
2484 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2485 AssertLogRelMsgReturn(pThis->cTotalNodes == STAC9221_NUM_NODES, ("cTotalNodes=%#x, should be 0x1c", pThis->cTotalNodes),
2486 VERR_INTERNAL_ERROR);
2487 pHlp->pfnSSMPutU32(pSSM, pThis->cTotalNodes);
2488 for (unsigned idxNode = 0; idxNode < pThis->cTotalNodes; ++idxNode)
2489 pHlp->pfnSSMPutStructEx(pSSM, &pThis->aNodes[idxNode].SavedState, sizeof(pThis->aNodes[idxNode].SavedState),
2490 0 /*fFlags*/, g_aCodecNodeFields, NULL /*pvUser*/);
2491 return VINF_SUCCESS;
2492}
2493
2494int hdaR3CodecLoadState(PPDMDEVINS pDevIns, PHDACODEC pThis, PHDACODECR3 pThisCC, PSSMHANDLE pSSM, uint32_t uVersion)
2495{
2496 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
2497 PCSSMFIELD pFields = NULL;
2498 uint32_t fFlags = 0;
2499 if (uVersion >= HDA_SAVED_STATE_VERSION_4)
2500 {
2501 /* Since version 4 a flexible node count is supported. */
2502 uint32_t cNodes;
2503 int rc2 = pHlp->pfnSSMGetU32(pSSM, &cNodes);
2504 AssertRCReturn(rc2, rc2);
2505 AssertReturn(cNodes == 0x1c, VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2506 AssertReturn(pThis->cTotalNodes == 0x1c, VERR_INTERNAL_ERROR);
2507
2508 pFields = g_aCodecNodeFields;
2509 fFlags = 0;
2510 }
2511 else if (uVersion >= HDA_SAVED_STATE_VERSION_2)
2512 {
2513 AssertReturn(pThis->cTotalNodes == 0x1c, VERR_INTERNAL_ERROR);
2514 pFields = g_aCodecNodeFields;
2515 fFlags = SSMSTRUCT_FLAGS_MEM_BAND_AID_RELAXED;
2516 }
2517 else if (uVersion >= HDA_SAVED_STATE_VERSION_1)
2518 {
2519 AssertReturn(pThis->cTotalNodes == 0x1c, VERR_INTERNAL_ERROR);
2520 pFields = g_aCodecNodeFieldsV1;
2521 fFlags = SSMSTRUCT_FLAGS_MEM_BAND_AID_RELAXED;
2522 }
2523 else
2524 AssertFailedReturn(VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
2525
2526 for (unsigned idxNode = 0; idxNode < pThis->cTotalNodes; ++idxNode)
2527 {
2528 uint8_t idOld = pThis->aNodes[idxNode].SavedState.Core.uID;
2529 int rc = pHlp->pfnSSMGetStructEx(pSSM, &pThis->aNodes[idxNode].SavedState, sizeof(pThis->aNodes[idxNode].SavedState),
2530 fFlags, pFields, NULL);
2531 AssertRCReturn(rc, rc);
2532 AssertLogRelMsgReturn(idOld == pThis->aNodes[idxNode].SavedState.Core.uID,
2533 ("loaded %#x, expected %#x\n", pThis->aNodes[idxNode].SavedState.Core.uID, idOld),
2534 VERR_SSM_DATA_UNIT_FORMAT_CHANGED);
2535 }
2536
2537 /*
2538 * Update stuff after changing the state.
2539 */
2540 PCODECNODE pNode;
2541 if (hdaCodecIsDacNode(pThis, pThis->u8DacLineOut))
2542 {
2543 pNode = &pThis->aNodes[pThis->u8DacLineOut];
2544 hdaR3CodecToAudVolume(pThisCC, pNode, &pNode->dac.B_params, PDMAUDIOMIXERCTL_FRONT);
2545 }
2546 else if (hdaCodecIsSpdifOutNode(pThis, pThis->u8DacLineOut))
2547 {
2548 pNode = &pThis->aNodes[pThis->u8DacLineOut];
2549 hdaR3CodecToAudVolume(pThisCC, pNode, &pNode->spdifout.B_params, PDMAUDIOMIXERCTL_FRONT);
2550 }
2551
2552 pNode = &pThis->aNodes[pThis->u8AdcVolsLineIn];
2553 hdaR3CodecToAudVolume(pThisCC, pNode, &pNode->adcvol.B_params, PDMAUDIOMIXERCTL_LINE_IN);
2554
2555 LogFlowFuncLeaveRC(VINF_SUCCESS);
2556 return VINF_SUCCESS;
2557}
2558
2559/**
2560 * Powers off the codec (ring-3).
2561 *
2562 * @param pThisCC Context-specific codec data (ring-3) to power off.
2563 */
2564void hdaR3CodecPowerOff(PHDACODECR3 pThisCC)
2565{
2566 if (!pThisCC)
2567 return;
2568
2569 LogFlowFuncEnter();
2570
2571 LogRel2(("HDA: Powering off codec ...\n"));
2572
2573 int rc2 = hdaR3CodecRemoveStream(pThisCC, PDMAUDIOMIXERCTL_FRONT, true /*fImmediate*/);
2574 AssertRC(rc2);
2575#ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
2576 rc2 = hdaR3CodecRemoveStream(pThisCC, PDMAUDIOMIXERCTL_CENTER_LFE, true /*fImmediate*/);
2577 AssertRC(rc2);
2578 rc2 = hdaR3CodecRemoveStream(pThisCC, PDMAUDIOMIXERCTL_REAR, true /*fImmediate*/);
2579 AssertRC(rc2);
2580#endif
2581
2582#ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
2583 rc2 = hdaR3CodecRemoveStream(pThisCC, PDMAUDIOMIXERCTL_MIC_IN, true /*fImmediate*/);
2584 AssertRC(rc2);
2585#endif
2586 rc2 = hdaR3CodecRemoveStream(pThisCC, PDMAUDIOMIXERCTL_LINE_IN, true /*fImmediate*/);
2587 AssertRC(rc2);
2588}
2589
2590/**
2591 * Constructs a codec (ring-3).
2592 *
2593 * @returns VBox status code.
2594 * @param pDevIns Associated device instance.
2595 * @param pThis Shared codec data beteen r0/r3.
2596 * @param pThisCC Context-specific codec data (ring-3).
2597 * @param uLUN Device LUN to assign.
2598 * @param pCfg CFGM node to use for configuration.
2599 */
2600int hdaR3CodecConstruct(PPDMDEVINS pDevIns, PHDACODEC pThis, PHDACODECR3 pThisCC, uint16_t uLUN, PCFGMNODE pCfg)
2601{
2602 AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
2603 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2604 AssertPtrReturn(pThisCC, VERR_INVALID_POINTER);
2605 AssertPtrReturn(pCfg, VERR_INVALID_POINTER);
2606
2607 pThis->id = uLUN;
2608 pThis->enmType = CODEC_TYPE_STAC9220; /** @todo Make this dynamic. */
2609
2610 int rc;
2611
2612 switch (pThis->enmType)
2613 {
2614 case CODEC_TYPE_STAC9220:
2615 {
2616 rc = stac9220Construct(pThis);
2617 AssertRCReturn(rc, rc);
2618 break;
2619 }
2620
2621 default:
2622 AssertFailedReturn(VERR_NOT_IMPLEMENTED);
2623 break;
2624 }
2625
2626 pThisCC->pfnDbgSelector = codecR3DbgSelector;
2627 pThisCC->pfnDbgListNodes = codecR3DbgListNodes;
2628 pThisCC->pfnLookup = codecLookup;
2629
2630 /*
2631 * Set initial volume.
2632 */
2633 PCODECNODE pNode = &pThis->aNodes[pThis->u8DacLineOut];
2634 rc = hdaR3CodecToAudVolume(pThisCC, pNode, &pNode->dac.B_params, PDMAUDIOMIXERCTL_FRONT);
2635 AssertRCReturn(rc, rc);
2636
2637 pNode = &pThis->aNodes[pThis->u8AdcVolsLineIn];
2638 rc = hdaR3CodecToAudVolume(pThisCC, pNode, &pNode->adcvol.B_params, PDMAUDIOMIXERCTL_LINE_IN);
2639 AssertRCReturn(rc, rc);
2640
2641# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
2642# error "Implement mic-in support!"
2643# endif
2644
2645 /*
2646 * Statistics
2647 */
2648 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatLookupsR3, STAMTYPE_COUNTER, "Codec/LookupsR0", STAMUNIT_OCCURENCES, "Number of R0 codecLookup calls");
2649# if 0 /* Codec is not yet kosher enough for ring-0. @bugref{9890c64} */
2650 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatLookupsR0, STAMTYPE_COUNTER, "Codec/LookupsR3", STAMUNIT_OCCURENCES, "Number of R3 codecLookup calls");
2651# endif
2652
2653 return rc;
2654}
2655
2656#else /* IN_RING0 */
2657
2658/**
2659 * Constructs a codec (ring-0).
2660 *
2661 * @returns VBox status code.
2662 * @param pDevIns Associated device instance.
2663 * @param pThis Shared codec data beteen r0/r3.
2664 * @param pThisCC Context-specific codec data (ring-0).
2665 */
2666int hdaR0CodecConstruct(PPDMDEVINS pDevIns, PHDACODEC pThis, PHDACODECR0 pThisCC)
2667{
2668 AssertPtrReturn(pDevIns, VERR_INVALID_POINTER);
2669 AssertPtrReturn(pThis, VERR_INVALID_POINTER);
2670 AssertPtrReturn(pThisCC, VERR_INVALID_POINTER);
2671
2672 pThisCC->pfnLookup = codecLookup;
2673
2674 /* Note: Everything else is done in the R3 part. */
2675
2676 return VINF_SUCCESS;
2677}
2678
2679#endif /* IN_RING0 */
2680
2681/**
2682 * Destructs a codec.
2683 *
2684 * @param pThis Codec to destruct.
2685 */
2686void hdaCodecDestruct(PHDACODEC pThis)
2687{
2688 if (!pThis)
2689 return;
2690
2691 /* Nothing to do here atm. */
2692
2693 LogFlowFuncEnter();
2694}
2695
2696/**
2697 * Resets a codec.
2698 *
2699 * @param pThis Codec to reset.
2700 */
2701void hdaCodecReset(PHDACODEC pThis)
2702{
2703 switch (pThis->enmType)
2704 {
2705 case CODEC_TYPE_STAC9220:
2706 stac9220Reset(pThis);
2707 break;
2708
2709 default:
2710 AssertFailed();
2711 break;
2712 }
2713}
2714
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette