VirtualBox

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

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

DevHdaCodec: Combine all the node classification lists into an array with classification flags for each node and use this to implement the predicate functions (e.g. hdaCodecIsPortNode, hdaCodecIsSpdifOutNode, etc). This reduces .text$nm size (windows release build) from 19421 to 15760 bytes -> 3661 bytes / 18%. bugref:9890

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

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