VirtualBox

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

Last change on this file since 103914 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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

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