VirtualBox

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

Last change on this file since 91861 was 91734, checked in by vboxsync, 3 years ago

Audio/HDA: Check if the node ID is the one we use for controlling the line-in volume (see commit for details). oem2ticketref:53.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use