VirtualBox

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

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

DevHdaCodec: Looping over node index arrays must do proper bound checking (two cases). Doxygen and similar cleanups. bugref:9890

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

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