VirtualBox

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

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

DevHda: Implemented DMA-on-WALCLK access too. Cleanups. bugref:9890

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

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