VirtualBox

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

Last change on this file since 104529 was 104529, checked in by vboxsync, 7 months ago

Audio/HDA: Fixed recording on newer Windows versions (Windows 10): Those control the recording volume and muted state using a different codec node ID (see comments in patch) for the STAC9220 codec. When rebooting the guest, Windows mutes recording and re-enables it on a not-

yet-handled ADC (analog to digital converter). bugref:10682

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