VirtualBox

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

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

Audio: Renamed DrvAudio.h to DrvAudioCommon.h and move the DrvAudio.h structures into DrvAudio.cpp. bugref:9890

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

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