VirtualBox

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

Last change on this file since 89551 was 89406, checked in by vboxsync, 3 years ago

DevHda: Removed the stream mapping and leave such complications to the mixer buffer. (Still some cleanup to be done here.) 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 89406 2021-05-31 14:01:31Z 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.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#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 * @returns VBox 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 {
329 PDMAudioPropsInit(pProps, cbSample, true /*fSigned*/, (u16SDFMT & 0xf) + 1 /*cChannels*/, u32Hz * u32HzMult / u32HzDiv);
330 /** @todo is there anything we need to / can do about channel assignments? */
331 }
332
333# undef EXTRACT_VALUE
334 return rc;
335}
336
337# ifdef LOG_ENABLED
338void hdaR3BDLEDumpAll(PPDMDEVINS pDevIns, PHDASTATE pThis, uint64_t u64BDLBase, uint16_t cBDLE)
339{
340 LogFlowFunc(("BDLEs @ 0x%x (%RU16):\n", u64BDLBase, cBDLE));
341 if (!u64BDLBase)
342 return;
343
344 uint32_t cbBDLE = 0;
345 for (uint16_t i = 0; i < cBDLE; i++)
346 {
347 HDABDLEDESC bd;
348 PDMDevHlpPhysRead(pDevIns, u64BDLBase + i * sizeof(HDABDLEDESC), &bd, sizeof(bd));
349
350 LogFunc(("\t#%03d BDLE(adr:0x%llx, size:%RU32, ioc:%RTbool)\n",
351 i, bd.u64BufAddr, bd.u32BufSize, bd.fFlags & HDA_BDLE_F_IOC));
352
353 cbBDLE += bd.u32BufSize;
354 }
355
356 LogFlowFunc(("Total: %RU32 bytes\n", cbBDLE));
357
358 if (!pThis->u64DPBase) /* No DMA base given? Bail out. */
359 return;
360
361 LogFlowFunc(("DMA counters:\n"));
362
363 for (int i = 0; i < cBDLE; i++)
364 {
365 uint32_t uDMACnt;
366 PDMDevHlpPhysRead(pDevIns, (pThis->u64DPBase & DPBASE_ADDR_MASK) + (i * 2 * sizeof(uint32_t)),
367 &uDMACnt, sizeof(uDMACnt));
368
369 LogFlowFunc(("\t#%03d DMA @ 0x%x\n", i , uDMACnt));
370 }
371}
372# endif /* LOG_ENABLED */
373
374#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