VirtualBox

source: vbox/trunk/src/VBox/Devices/Audio/DevHdaCommon.cpp@ 88269

最後變更 在這個檔案從88269是 88269,由 vboxsync 提交於 4 年 前

Audio: Made sure PDMAUDIOPCMPROPS is initialized using a helper function or the initializer macro, and that vital changes are made using setting helper functions. There are now two derived fields (frame size and shift count) that must be maintained, so this was the sanest way of doing it. Added a raw flag to PDMAUDIOPCMPROPS for VRDE/VRDP, since it wants the raw mixer content and we need a way of expressing this (PDMAUDIOSTREAMLAYOUT isn't the right place). The mixer buffers now uses PDMAUDIOPCMPROPS rather than the weird 32-bit format contraption for picking conversion functions. Simplify the drvAudioStreamPlay code by eliminating the PDMAUDIOSTREAMLAYOUT_RAW special case. bugref:9890

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 10.9 KB
 
1/* $Id: DevHdaCommon.cpp 88269 2021-03-24 11:45:54Z vboxsync $ */
2/** @file
3 * Intel HD Audio Controller Emulation - Common stuff.
4 *
5 * @todo r=bird: Shared with whom exactly?
6 */
7
8/*
9 * Copyright (C) 2017-2020 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.alldomusa.eu.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#define LOG_GROUP LOG_GROUP_DEV_HDA
25#include <iprt/assert.h>
26#include <iprt/errcore.h>
27#include <iprt/time.h>
28
29#include <VBox/AssertGuest.h>
30#include <VBox/vmm/pdmaudioinline.h>
31
32#include <VBox/log.h>
33
34#include "DevHda.h"
35#include "DevHdaCommon.h"
36#include "DevHdaStream.h"
37
38
39
40/**
41 * Processes (de/asserts) the interrupt according to the HDA's current state.
42 *
43 * @param pDevIns The device instance.
44 * @param pThis The shared HDA device state.
45 * @param pszSource Caller information.
46 */
47#if defined(LOG_ENABLED) || defined(DOXYGEN_RUNNING)
48void hdaProcessInterrupt(PPDMDEVINS pDevIns, PHDASTATE pThis, const char *pszSource)
49#else
50void hdaProcessInterrupt(PPDMDEVINS pDevIns, PHDASTATE pThis)
51#endif
52{
53 uint32_t uIntSts = hdaGetINTSTS(pThis);
54
55 HDA_REG(pThis, INTSTS) = uIntSts;
56
57 /* NB: It is possible to have GIS set even when CIE/SIEn are all zero; the GIS bit does
58 * not control the interrupt signal. See Figure 4 on page 54 of the HDA 1.0a spec.
59 */
60 /* Global Interrupt Enable (GIE) set? */
61 if ( (HDA_REG(pThis, INTCTL) & HDA_INTCTL_GIE)
62 && (HDA_REG(pThis, INTSTS) & HDA_REG(pThis, INTCTL) & (HDA_INTCTL_CIE | HDA_STRMINT_MASK)))
63 {
64 Log3Func(("Asserted (%s)\n", pszSource));
65
66 PDMDevHlpPCISetIrq(pDevIns, 0, 1 /* Assert */);
67 pThis->u8IRQL = 1;
68
69#ifdef DEBUG
70 pThis->Dbg.IRQ.tsAssertedNs = RTTimeNanoTS();
71 pThis->Dbg.IRQ.tsProcessedLastNs = pThis->Dbg.IRQ.tsAssertedNs;
72#endif
73 }
74 else
75 {
76 Log3Func(("Deasserted (%s)\n", pszSource));
77
78 PDMDevHlpPCISetIrq(pDevIns, 0, 0 /* Deassert */);
79 pThis->u8IRQL = 0;
80 }
81}
82
83/**
84 * Retrieves the number of bytes of a FIFOW register.
85 *
86 * @return Number of bytes of a given FIFOW register.
87 * @param u16RegFIFOW FIFOW register to convert.
88 */
89uint8_t hdaSDFIFOWToBytes(uint16_t u16RegFIFOW)
90{
91 uint32_t cb;
92 switch (u16RegFIFOW)
93 {
94 case HDA_SDFIFOW_8B: cb = 8; break;
95 case HDA_SDFIFOW_16B: cb = 16; break;
96 case HDA_SDFIFOW_32B: cb = 32; break;
97 default:
98 AssertFailedStmt(cb = 32); /* Paranoia. */
99 break;
100 }
101
102 Assert(RT_IS_POWER_OF_TWO(cb));
103 return cb;
104}
105
106#ifdef IN_RING3
107/**
108 * Returns the default (mixer) sink from a given SD#.
109 * Returns NULL if no sink is found.
110 *
111 * @return PHDAMIXERSINK
112 * @param pThisCC The ring-3 HDA device state.
113 * @param uSD SD# to return mixer sink for.
114 * NULL if not found / handled.
115 */
116PHDAMIXERSINK hdaR3GetDefaultSink(PHDASTATER3 pThisCC, uint8_t uSD)
117{
118 if (hdaGetDirFromSD(uSD) == PDMAUDIODIR_IN)
119 {
120 const uint8_t uFirstSDI = 0;
121
122 if (uSD == uFirstSDI) /* First SDI. */
123 return &pThisCC->SinkLineIn;
124# ifdef VBOX_WITH_AUDIO_HDA_MIC_IN
125 if (uSD == uFirstSDI + 1)
126 return &pThisCC->SinkMicIn;
127# else
128 /* If we don't have a dedicated Mic-In sink, use the always present Line-In sink. */
129 return &pThisCC->SinkLineIn;
130# endif
131 }
132 else
133 {
134 const uint8_t uFirstSDO = HDA_MAX_SDI;
135
136 if (uSD == uFirstSDO)
137 return &pThisCC->SinkFront;
138# ifdef VBOX_WITH_AUDIO_HDA_51_SURROUND
139 if (uSD == uFirstSDO + 1)
140 return &pThisCC->SinkCenterLFE;
141 if (uSD == uFirstSDO + 2)
142 return &pThisCC->SinkRear;
143# endif
144 }
145
146 return NULL;
147}
148#endif /* IN_RING3 */
149
150/**
151 * Returns the audio direction of a specified stream descriptor.
152 *
153 * The register layout specifies that input streams (SDI) come first,
154 * followed by the output streams (SDO). So every stream ID below HDA_MAX_SDI
155 * is an input stream, whereas everything >= HDA_MAX_SDI is an output stream.
156 *
157 * Note: SDnFMT register does not provide that information, so we have to judge
158 * for ourselves.
159 *
160 * @return Audio direction.
161 */
162PDMAUDIODIR hdaGetDirFromSD(uint8_t uSD)
163{
164 AssertReturn(uSD < HDA_MAX_STREAMS, PDMAUDIODIR_UNKNOWN);
165
166 if (uSD < HDA_MAX_SDI)
167 return PDMAUDIODIR_IN;
168
169 return PDMAUDIODIR_OUT;
170}
171
172/**
173 * Returns the HDA stream of specified stream descriptor number.
174 *
175 * @return Pointer to HDA stream, or NULL if none found.
176 */
177PHDASTREAM hdaGetStreamFromSD(PHDASTATE pThis, uint8_t uSD)
178{
179 AssertPtr(pThis);
180 ASSERT_GUEST_MSG_RETURN(uSD < HDA_MAX_STREAMS, ("uSD=%u (%#x)\n", uSD, uSD), NULL);
181 return &pThis->aStreams[uSD];
182}
183
184#ifdef IN_RING3
185
186/**
187 * Returns the HDA stream of specified HDA sink.
188 *
189 * @return Pointer to HDA stream, or NULL if none found.
190 */
191PHDASTREAMR3 hdaR3GetR3StreamFromSink(PHDAMIXERSINK pSink)
192{
193 AssertPtrReturn(pSink, NULL);
194
195 /** @todo Do something with the channel mapping here? */
196 return pSink->pStreamR3;
197}
198
199
200/**
201 * Returns the HDA stream of specified HDA sink.
202 *
203 * @return Pointer to HDA stream, or NULL if none found.
204 */
205PHDASTREAM hdaR3GetSharedStreamFromSink(PHDAMIXERSINK pSink)
206{
207 AssertPtrReturn(pSink, NULL);
208
209 /** @todo Do something with the channel mapping here? */
210 return pSink->pStreamShared;
211}
212
213#endif /* IN_RING3 */
214
215/**
216 * Returns a new INTSTS value based on the current device state.
217 *
218 * @returns Determined INTSTS register value.
219 * @param pThis The shared HDA device state.
220 *
221 * @remark This function does *not* set INTSTS!
222 */
223uint32_t hdaGetINTSTS(PHDASTATE pThis)
224{
225 uint32_t intSts = 0;
226
227 /* Check controller interrupts (RIRB, STATEST). */
228 if (HDA_REG(pThis, RIRBSTS) & HDA_REG(pThis, RIRBCTL) & (HDA_RIRBCTL_ROIC | HDA_RIRBCTL_RINTCTL))
229 {
230 intSts |= HDA_INTSTS_CIS; /* Set the Controller Interrupt Status (CIS). */
231 }
232
233 /* Check SDIN State Change Status Flags. */
234 if (HDA_REG(pThis, STATESTS) & HDA_REG(pThis, WAKEEN))
235 {
236 intSts |= HDA_INTSTS_CIS; /* Touch Controller Interrupt Status (CIS). */
237 }
238
239 /* For each stream, check if any interrupt status bit is set and enabled. */
240 for (uint8_t iStrm = 0; iStrm < HDA_MAX_STREAMS; ++iStrm)
241 {
242 if (HDA_STREAM_REG(pThis, STS, iStrm) & HDA_STREAM_REG(pThis, CTL, iStrm) & (HDA_SDCTL_DEIE | HDA_SDCTL_FEIE | HDA_SDCTL_IOCE))
243 {
244 Log3Func(("[SD%d] interrupt status set\n", iStrm));
245 intSts |= RT_BIT(iStrm);
246 }
247 }
248
249 if (intSts)
250 intSts |= HDA_INTSTS_GIS; /* Set the Global Interrupt Status (GIS). */
251
252 Log3Func(("-> 0x%x\n", intSts));
253
254 return intSts;
255}
256
257#ifdef IN_RING3
258
259/**
260 * Converts an HDA stream's SDFMT register into a given PCM properties structure.
261 *
262 * @return IPRT status code.
263 * @param u16SDFMT The HDA stream's SDFMT value to convert.
264 * @param pProps PCM properties structure to hold converted result on success.
265 */
266int hdaR3SDFMTToPCMProps(uint16_t u16SDFMT, PPDMAUDIOPCMPROPS pProps)
267{
268 AssertPtrReturn(pProps, VERR_INVALID_POINTER);
269
270# define EXTRACT_VALUE(v, mask, shift) ((v & ((mask) << (shift))) >> (shift))
271
272 int rc = VINF_SUCCESS;
273
274 uint32_t u32Hz = EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BASE_RATE_MASK, HDA_SDFMT_BASE_RATE_SHIFT)
275 ? 44100 : 48000;
276 uint32_t u32HzMult = 1;
277 uint32_t u32HzDiv = 1;
278
279 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT))
280 {
281 case 0: u32HzMult = 1; break;
282 case 1: u32HzMult = 2; break;
283 case 2: u32HzMult = 3; break;
284 case 3: u32HzMult = 4; break;
285 default:
286 LogFunc(("Unsupported multiplier %x\n",
287 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_MULT_MASK, HDA_SDFMT_MULT_SHIFT)));
288 rc = VERR_NOT_SUPPORTED;
289 break;
290 }
291 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT))
292 {
293 case 0: u32HzDiv = 1; break;
294 case 1: u32HzDiv = 2; break;
295 case 2: u32HzDiv = 3; break;
296 case 3: u32HzDiv = 4; break;
297 case 4: u32HzDiv = 5; break;
298 case 5: u32HzDiv = 6; break;
299 case 6: u32HzDiv = 7; break;
300 case 7: u32HzDiv = 8; break;
301 default:
302 LogFunc(("Unsupported divisor %x\n",
303 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_DIV_MASK, HDA_SDFMT_DIV_SHIFT)));
304 rc = VERR_NOT_SUPPORTED;
305 break;
306 }
307
308 uint8_t cbSample = 0;
309 switch (EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT))
310 {
311 case 0:
312 cbSample = 1;
313 break;
314 case 1:
315 cbSample = 2;
316 break;
317 case 4:
318 cbSample = 4;
319 break;
320 default:
321 AssertMsgFailed(("Unsupported bits per sample %x\n",
322 EXTRACT_VALUE(u16SDFMT, HDA_SDFMT_BITS_MASK, HDA_SDFMT_BITS_SHIFT)));
323 rc = VERR_NOT_SUPPORTED;
324 break;
325 }
326
327 if (RT_SUCCESS(rc))
328 PDMAudioPropsInit(pProps, cbSample, true /*fSigned*/, (u16SDFMT & 0xf) + 1 /*cChannels*/, u32Hz * u32HzMult / u32HzDiv);
329
330# undef EXTRACT_VALUE
331 return rc;
332}
333
334# ifdef LOG_ENABLED
335void hdaR3BDLEDumpAll(PPDMDEVINS pDevIns, PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE)
336{
337 LogFlowFunc(("BDLEs @ 0x%x (%RU16):\n", u64BDLBase, cBDLE));
338 if (!u64BDLBase)
339 return;
340
341 uint32_t cbBDLE = 0;
342 for (uint16_t i = 0; i < cBDLE; i++)
343 {
344 HDABDLEDESC bd;
345 PDMDevHlpPhysRead(pDevIns, u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
346
347 LogFunc(("\t#%03d BDLE(adr:0x%llx, size:%RU32, ioc:%RTbool)\n",
348 i, bd.u64BufAddr, bd.u32BufSize, bd.fFlags & HDA_BDLE_F_IOC));
349
350 cbBDLE += bd.u32BufSize;
351 }
352
353 LogFlowFunc(("Total: %RU32 bytes\n", cbBDLE));
354
355 if (!pThis->u64DPBase) /* No DMA base given? Bail out. */
356 return;
357
358 LogFlowFunc(("DMA counters:\n"));
359
360 for (int i = 0; i < cBDLE; i++)
361 {
362 uint32_t uDMACnt;
363 PDMDevHlpPhysRead(pDevIns, (pThis->u64DPBase & DPBASE_ADDR_MASK) + (i * 2 * sizeof(uint32_t)),
364 &uDMACnt, sizeof(uDMACnt));
365
366 LogFlowFunc(("\t#%03d DMA @ 0x%x\n", i , uDMACnt));
367 }
368}
369# endif /* LOG_ENABLED */
370
371#endif /* IN_RING3 */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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