1
Requesting to see if we can get some Acked-By tags, and merge on usb-next.
2
1
Several Qualcomm based chipsets can support USB audio offloading to a
3
Several Qualcomm based chipsets can support USB audio offloading to a
2
dedicated audio DSP, which can take over issuing transfers to the USB
4
dedicated audio DSP, which can take over issuing transfers to the USB
3
host controller. The intention is to reduce the load on the main
5
host controller. The intention is to reduce the load on the main
4
processors in the SoC, and allow them to be placed into lower power modes.
6
processors in the SoC, and allow them to be placed into lower power modes.
5
There are several parts to this design:
7
There are several parts to this design:
...
...
36
___________V_____________ | | |_______________________|
38
___________V_____________ | | |_______________________|
37
|XHCI HCD |<- |
39
|XHCI HCD |<- |
38
|_________________________| |
40
|_________________________| |
39
41
40
42
41
Adding ASoC binding layer:
43
Adding ASoC binding layer
44
=========================
42
soc-usb: Intention is to treat a USB port similar to a headphone jack.
45
soc-usb: Intention is to treat a USB port similar to a headphone jack.
43
The port is always present on the device, but cable/pin status can be
46
The port is always present on the device, but cable/pin status can be
44
enabled/disabled. Expose mechanisms for USB backend ASoC drivers to
47
enabled/disabled. Expose mechanisms for USB backend ASoC drivers to
45
communicate with USB SND.
48
communicate with USB SND.
46
49
47
Create a USB backend for Q6DSP:
50
Create a USB backend for Q6DSP
51
==============================
48
q6usb: Basic backend driver that will be responsible for maintaining the
52
q6usb: Basic backend driver that will be responsible for maintaining the
49
resources needed to initiate a playback stream using the Q6DSP. Will
53
resources needed to initiate a playback stream using the Q6DSP. Will
50
be the entity that checks to make sure the connected USB audio device
54
be the entity that checks to make sure the connected USB audio device
51
supports the requested PCM format. If it does not, the PCM open call will
55
supports the requested PCM format. If it does not, the PCM open call will
52
fail, and userpsace ALSA can take action accordingly.
56
fail, and userspace ALSA can take action accordingly.
53
57
54
Introduce XHCI interrupter support:
58
Introduce XHCI interrupter support
59
==================================
55
XHCI HCD supports multiple interrupters, which allows for events to be routed
60
XHCI HCD supports multiple interrupters, which allows for events to be routed
56
to different event rings. This is determined by "Interrupter Target" field
61
to different event rings. This is determined by "Interrupter Target" field
57
specified in Section "6.4.1.1 Normal TRB" of the XHCI specification.
62
specified in Section "6.4.1.1 Normal TRB" of the XHCI specification.
58
63
59
Events in the offloading case will be routed to an event ring that is assigned
64
Events in the offloading case will be routed to an event ring that is assigned
60
to the audio DSP.
65
to the audio DSP.
61
66
62
Create vendor ops for the USB SND driver:
67
Create vendor ops for the USB SND driver
68
========================================
63
qc_audio_offload: This particular driver has several components associated
69
qc_audio_offload: This particular driver has several components associated
64
with it:
70
with it:
65
- QMI stream request handler
71
- QMI stream request handler
66
- XHCI interrupter and resource management
72
- XHCI interrupter and resource management
67
- audio DSP memory management
73
- audio DSP memory management
...
...
80
86
81
and pass it along to the audio DSP. All endpoint management will now be handed
87
and pass it along to the audio DSP. All endpoint management will now be handed
82
over to the DSP, and the main processor is not involved in transfers.
88
over to the DSP, and the main processor is not involved in transfers.
83
89
84
Overall, implementing this feature will still expose separate sound card and PCM
90
Overall, implementing this feature will still expose separate sound card and PCM
85
devices for both the platorm card and USB audio device:
91
devices for both the platform card and USB audio device:
86
0 [SM8250MTPWCD938]: sm8250 - SM8250-MTP-WCD9380-WSA8810-VA-D
92
0 [SM8250MTPWCD938]: sm8250 - SM8250-MTP-WCD9380-WSA8810-VA-D
87
SM8250-MTP-WCD9380-WSA8810-VA-DMIC
93
SM8250-MTP-WCD9380-WSA8810-VA-DMIC
88
1 [Audio ]: USB-Audio - USB Audio
94
1 [Audio ]: USB-Audio - USB Audio
89
Generic USB Audio at usb-xhci-hcd.1.auto-1.4, high speed
95
Generic USB Audio at usb-xhci-hcd.1.auto-1.4, high speed
90
96
91
This is to ensure that userspace ALSA entities can decide which route to take
97
This is to ensure that userspace ALSA entities can decide which route to take
92
when executing the audio playback. In the above, if card#1 is selected, then
98
when executing the audio playback. In the above, if card#1 is selected, then
93
USB audio data will take the legacy path over the USB PCM drivers, etc...
99
USB audio data will take the legacy path over the USB PCM drivers, etc...
94
100
101
The current limitation is that the latest USB audio device that is identified
102
will be automatically selected by the Q6USB BE DAI for offloading. Future
103
patches can be added to possibly add for more flexibility, but until the userpace
104
applications can be better defined, having these mechanisms will complicate the
105
overall implementation.
106
107
USB offload Kcontrols
108
=====================
109
Part of the vendor offload package will have a mixer driver associated with it
110
(mixer_usb_offload.c). This entity will be responsible for coordinating with
111
SOC USB and the Q6USB backend DAI to fetch information about the sound card
112
and PCM device indices associated with the offload path. The logic is done
113
based on the current implementation of how paths are controlled within the QC
114
ASoC implementation.
115
116
QC ASoC Q6Routing
117
-----------------
118
Within the Q6 ASOC design, the registered ASoC platform card will expose a set
119
of kcontrols for enabling the BE DAI links to the FE DAI link. For example:
120
121
tinymix -D 0 contents
122
Number of controls: 1033
123
ctl type num name value
124
...
125
1025 BOOL 1 USB Mixer MultiMedia1 Off
126
1026 BOOL 1 USB Mixer MultiMedia2 Off
127
1027 BOOL 1 USB Mixer MultiMedia3 Off
128
1028 BOOL 1 USB Mixer MultiMedia4 Off
129
1029 BOOL 1 USB Mixer MultiMedia5 Off
130
1030 BOOL 1 USB Mixer MultiMedia6 Off
131
1031 BOOL 1 USB Mixer MultiMedia7 Off
132
1032 BOOL 1 USB Mixer MultiMedia8 Off
133
134
Each of these kcontrols will enable the USB BE DAI link (q6usb) to be connected
135
to a FE DAI link (q6asm). Since each of these controls are DAPM widgets, when
136
it is enabled, the DAPM widget's "connect" flag is updated accordingly.
137
138
USB Offload Mapping
139
-------------------
140
Based on the Q6routing, the USB BE DAI link can determine which sound card and
141
PCM device is enabled for offloading. Fetching the ASoC platform sound card's
142
information is fairly straightforward, and the bulk of the work goes to finding
143
the corresponding PCM device index. As mentioned above, the USB BE DAI can
144
traverse the DAPM widgets to find the DAPM path that is related to the control
145
for the "USB Mixer." Based on which "USB Mixer" is enabled, it can find the
146
corresponding DAPM widget associated w/ the FE DAI link (Multimedia*). From there
147
it can find the PCM device created for the Multimedia* stream.
148
149
Only one BE DAI link can be enabled per FE DAI. For example, if the HDMI path is
150
enabled for Multimedia1, the USB Mixer will be disabled and switched over.
151
152
Examples of kcontrol
153
--------------------
154
tinymix -D 0 contents
155
Number of controls: 1033
156
ctl type num name
157
...
158
1025 BOOL 1 USB Mixer MultiMedia1 Off
159
1026 BOOL 1 USB Mixer MultiMedia2 On
160
1027 BOOL 1 USB Mixer MultiMedia3 Off
161
1028 BOOL 1 USB Mixer MultiMedia4 Off
162
1029 BOOL 1 USB Mixer MultiMedia5 Off
163
1030 BOOL 1 USB Mixer MultiMedia6 Off
164
1031 BOOL 1 USB Mixer MultiMedia7 Off
165
1032 BOOL 1 USB Mixer MultiMedia8 Off
166
167
tinymix -D 2 contents
168
Number of controls: 10
169
ctl type num name value
170
0 INT 2 Capture Channel Map 0, 0 (range 0->36)
171
1 INT 2 Playback Channel Map 0, 0 (range 0->36)
172
2 BOOL 1 Headset Capture Switch On
173
3 INT 1 Headset Capture Volume 10 (range 0->13)
174
4 BOOL 1 Sidetone Playback Switch On
175
5 INT 1 Sidetone Playback Volume 4096 (range 0->8192)
176
6 BOOL 1 Headset Playback Switch On
177
7 INT 2 Headset Playback Volume 20, 20 (range 0->24)
178
8 INT 1 USB Offload Playback Card Route PCM#0 0 (range -1->32)
179
9 INT 1 USB Offload Playback PCM Route PCM#0 1 (range -1->255)
180
181
The example highlights that the userspace/application can utilize the offload path
182
for the USB device on card#0 PCM device#1.
183
184
When dealing with multiple USB audio devices, only the latest USB device identified
185
is going to be selected for offload capable.
186
187
tinymix -D 1 contents
188
Number of controls: 9
189
ctl type num name value
190
0 INT 2 Capture Channel Map 0, 0 (range 0->36)
191
1 INT 2 Playback Channel Map 0, 0 (range 0->36)
192
2 BOOL 1 Headset Capture Switch On
193
3 INT 1 Headset Capture Volume 1 (range 0->4)
194
4 BOOL 1 Sidetone Playback Switch On
195
5 INT 1 Sidetone Playback Volume 4096 (range 0->8192)
196
6 BOOL 1 Headset Playback Switch On
197
7 INT 2 Headset Playback Volume 20, 20 (range 0->24)
198
8 INT 1 USB Offload Playback Card Route PCM#0 -1 (range -1->32)
199
9 INT 1 USB Offload Playback PCM Route PCM#0 -1 (range -1->255)
200
201
"-1, -1" shows that this device has no route to the offload path.
202
95
This feature was validated using:
203
This feature was validated using:
96
- tinymix: set/enable the multimedia path to route to USB backend
204
- tinymix: set/enable the multimedia path to route to USB backend
97
- tinyplay: issue playback on platform card
205
- tinyplay: issue playback on platform card
98
206
99
Changelog
207
Changelog
100
--------------------------------------------
208
--------------------------------------------
209
Changes in v35:
210
- Fixed kernel bot and sparse errors.
211
212
Changes in v34:
213
- Fixed comments to align to kernel doc format.
214
- Rebased to usb-next
215
216
Changes in v33:
217
- Removed patch#1, which introduced the xhci skip_events flag. Refactored this as
218
an argument to the xhci_handle_events() API. Events will be skipped accordingly.
219
- Updated patch#2 and patch#3 to accommodate for the patch#1 removal.
220
- Replaced sec intr term from xHCI sideband commit message.
221
222
Changes in v32:
223
- Updated license year. Happy new years, 2025!
224
225
Changes in v31:
226
- Rebased to usb-next, which required some minor updates to APIs and structures
227
changed upstream.
228
- Moved USB SND offload mixer as part of the QCOM vendor USB offload package.
229
- Have separate kcontrols for PCM and sound card offload mapping versus one kcontrol
230
returning a pair.
231
- Added a xHCI sideband notifier into sideband client drivers, so that clients can
232
handle certain xHCI sequences properly. Currently, track the xfer ring free, so
233
the client can ensure transfers are fully stopped by the DSP.
234
- Updated documentation for #3
235
- Removed SoC USB enable/disable sound jack calls, and replaced with direct calls to
236
SoC jack.
237
238
Changes in v30:
239
- Rebased to usb-next tip
240
- Renamed the xhci-sideband driver to xhci-sec-intr to avoid confusion with the xHCI
241
audio sideband feature mentioned within the spec.
242
- Squashed the xhci-sec-intr change to set IMOD for secondary interrupters into the main
243
patch that introduces the overall driver.
244
245
Changes in v29:
246
- Fixed some phrases/wording within the SOC USB documentation, and also added an output
247
with aplay -l for the example output.
248
- Fixed allocated string buffer for creating the USB SND offload mixer, and added
249
a PCM index check to ensure that the pcm index is less than the expected number.
250
- Added a complement enable jack call if USB backend DAI link drivers need access
251
to it.
252
253
Changes in v28:
254
- Updated comments and commit log in the stop endpoint sync patch. Clarified that
255
the default stop endpoint completion routine won't fully run as expected since it
256
has a completion associated w/ the command.
257
- Added a null check for sb->xhci within xhci_sideband_create_interrupter(). This
258
is to just ensure that caller has registered sideband before calling create
259
interrupter.
260
261
Changes in v27:
262
- Added some comments and notes about the offload design. Enforcing the q6routing
263
to only allow one USB mixer (PCM device) to be enabled at a time.
264
- Modified SND_JACK_USB notifications for all USB audio offloadable devices plugged
265
in
266
- Rebased on latest XHCI secondary interrupter IMOD changes upstream. Modified the
267
change in this series to allow for XHCI sideband to set the IMOD for sideband
268
clients.
269
- Updated documentation on how USB SND kcontrols are involved in the overall design.
270
- Remove mutex locking from suspend/resume platform ops, as USB core ensures that the
271
interface and device are in the RPM_ACTIVE state while disconnect is handled.
272
273
Changes in v26:
274
- Cleaned up drivers based on errors from checkpatch
275
- Fixed several typos using codespell
276
- Removed any vendor specific notation from USB SND offload mixer patch
277
278
Changes in v25:
279
- Cleanups on typos mentioned within the xHCI layers
280
- Modified the xHCI interrupter search if clients specify interrupter index
281
- Moved mixer_usb_offload into its own module, so that other vendor offload USB
282
modules can utilize it also.
283
- Added support for USB audio devices that may have multiple PCM streams, as
284
previous implementation only assumed a single PCM device. SOC USB will be
285
able to handle an array of PCM indexes supported by the USB audio device.
286
- Added some additional checks in the QC USB offload driver to check that device
287
has at least one playback stream before allowing to bind
288
- Reordered DT bindings to fix the error found by Rob's bot. The patch that
289
added USB_RX was after the example was updated.
290
- Updated comments within SOC USB to clarify terminology and to keep it consistent
291
- Added SND_USB_JACK type for notifying of USB device audio connections
292
293
Changes in v24:
294
- Simplified the kcontrols involved in determining how to utilize the offload
295
path.
296
- There is one kcontrol registered to each USB audio device that will
297
output which card/pcm device it is mapped to for the offload route.
298
- Removed kcontrols to track offload status and device selection.
299
- Default to last USB audio device plugged in as offload capable.
300
- kcontrol will reside on USB SND device.
301
- Reworked the tracking of connected USB devices from the Q6USB BE DAI link.
302
Previously, it was convoluted by doing it over an array, but moved to using
303
a list made it much simpler. Logic is still unchanged in that the last USB
304
headset plugged in will be selected for offloading.
305
- Updated the USB SOC RST documentation accordingly with new kcontrol updates.
306
- Added logic to fetch mapped ASoC card and pcm device index that the offload
307
path is mapped to for the USB SND kcontrol (for offload route).
308
- Re-ordered series to hopefully make reviews more readable by combining
309
patches based on the layer modified (ie QC ASoC, ASoC, USB sound, and USB XHCI).
310
311
Changes in v23:
312
- Added MODULE_DESCRIPTION() fields to drivers that needed it.
313
314
Changes in v22:
315
- Removed components tag for the ASoC platform card, as the USB SND kcontrol for
316
notifying userspace of offload capable card achieves similar results.
317
- Due to the above, had to remove the review-by tag for the RST documentation,
318
as changes were made to remove the components tag section.
319
- Took in feedback to make the SOC USB add/remove ports void.
320
- Fixed an issue w/ the USB SND kcontrol management for devices that have multi
321
UAC interfaces. (would attempt to create the kcontrol more than once)
322
- Modified SOC USB card and PCM index select to be based off the num_supported
323
streams that is specified by the USB BE DAI.
324
- Modified comments on selecting the latest USB headset for offloading.
325
326
Changes in v21:
327
- Added an offload jack disable path from the ASoC platform driver and SOC USB.
328
- Refactored some of the existing SOC USB context look up APIs and created some
329
new helpers to search for the USB context.
330
- Renamed snd_soc_usb_find_format to snd_soc_usb_find_supported_format
331
- Removed some XHCI sideband calls that would allow clients to actually enable
332
the IRQ line associated w/ the secondary interrupter. This is removed because
333
there are other dependencies that are required for that to happen, which are not
334
covered as part of this series, and to avoid confusion.
335
- Due to the above, removed the need to export IMOD setting, and enable/disable
336
interrupter APIs.
337
338
Changes in v20:
339
- Fixed up some formatting changes pointed out in the usb.rst
340
- Added SB null check during XHCI sideband unregister in case caller passes
341
improper argument (xhci_sideband_unregister())
342
343
Changes in v19:
344
- Rebased to usb-next to account for some new changes in dependent drivers.
345
346
Changes in v18:
347
- Rebased to usb-next, which merged in part of the series. Removed these patches.
348
- Reworked Kconfigs for the ASoC USB related components from QCOM Q6DSP drivers
349
to keep dependencies in place for SoC USB and USB SND.
350
- Removed the repurposing of the stop ep sync API into existing XHCI operations.
351
This will be solely used by the XHCI sideband for now.
352
353
Changes in v17:
354
- Fixed an issue where one patch was squashed into another.
355
- Re-added some kconfig checks for helpers exposed in USB SND for the soc usb
356
driver, after running different kconfigs.
357
358
Changes in v16:
359
- Modified some code layer dependencies so that soc usb can be split as a separate
360
module.
361
- Split the kcontrols from ASoC QCOM common layer into a separate driver
362
- Reworked SOC USB kcontrols for controlling card + pcm offload routing and status
363
so that there are individual controls for card and pcm devices.
364
- Added a kcontrol remove API in SOC USB to remove the controls on the fly. This
365
required to add some kcontrol management to SOC USB.
366
- Removed the disconnect work and workqueue for the QC USB offload as it is not
367
required, since QMI interface driver ensures events are handled in its own WQ.
368
369
Changes in v15:
370
- Removed some already merged XHCI changes
371
- Separated SOC USB driver from being always compiled into SOC core. Now
372
configurable from kconfig.
373
- Fixed up ASoC kcontrol naming to fit guidelines.
374
- Removed some unnecessary dummy ifdefs.
375
- Moved usb snd offload capable kcontrol to be initialized by the platform offloading
376
driver.
377
378
Changes in v14:
379
- Cleaned up some USB SND related feedback:
380
- Renamed SNDUSB OFFLD playback available --> USB offload capable card
381
- Fixed locking while checking if stream is in use
382
- Replaced some mutex pairs with guard(mutex)
383
384
Changes in v13:
385
- Pulled in secondary/primary interrupter rework from Mathias from:
386
https://git.kernel.org/pub/scm/linux/kernel/git/mnyman/xhci.git/log/drivers/usb/host?h=fix_eventhandling
387
- Did some cleanup and commit message updates, and tested on current code base.
388
- Added mutex locking to xhci sideband to help prevent any race conditions, esp. for when accessing shared
389
references.
390
- Addressed concerns from Hillf about gfp_flags and locking used in qc_usb_audio_offload.
391
- Rebased onto usb-next
392
393
Changes in v12:
394
- Updated copyright year to 2024. Happy new years!
395
- Fixed newline format on mixer offload driver.
396
397
Changes in v11:
398
- Modified QMI format structures to be const
399
400
Changes in v10:
401
- Added new mixer for exposing kcontrol for sound card created by USB SND. This
402
allows for applications to know which platform sound card has offload support.
403
Will return the card number.
404
- Broke down and cleaned up some functions/APIs within qc_audio_offload driver.
405
- Exported xhci_initialize_ring_info(), and modified XHCI makefile to allow for
406
the XHCI sideband to exist as a module.
407
- Reworked the jack registration and moved it to the QCOM platform card driver,
408
ie sm8250.
409
- Added an SOC USB API to fetch a standard component tag that can be appended to
410
the platform sound card. Added this tag to sm8250 if any USB path exists within
411
the DT node.
412
- Moved kcontrols that existed in the Q6USB driver, and made it a bit more generic,
413
so that naming can be standardized across solutions. SOC USB is now responsible
414
for creation of these kcontrols.
415
- Added a SOC USB RST document explaining some code flows and implementation details
416
so that other vendors can utilize the framework.
417
- Addressed a case where USB device connection events are lost if usb offload driver
418
(qc_audio_offload) is not probed when everything else has been initialized, ie
419
USB SND, SOC USB and ASoC sound card. Add a rediscover device call during module
420
init, to ensure that connection events will be propagated.
421
- Rebased to usb-next.
422
101
Changes in v9:
423
Changes in v9:
102
- Fixed the dt binding check issue with regards to num-hc-interrupters.
424
- Fixed the dt binding check issue with regards to num-hc-interrupters.
103
425
104
Changes in v8:
426
Changes in v8:
105
- Cleaned up snd_soc_usb_find_priv_data() based on Mark's feedback. Removed some of
427
- Cleaned up snd_soc_usb_find_priv_data() based on Mark's feedback. Removed some of
...
...
131
probe calls.
453
probe calls.
132
- Renamed offload module name and kconfig to fit within the SND domain.
454
- Renamed offload module name and kconfig to fit within the SND domain.
133
- Renamed attach/detach endpoint API to keep the hw_params notation.
455
- Renamed attach/detach endpoint API to keep the hw_params notation.
134
456
135
Changes in v5:
457
Changes in v5:
136
- Removed some unnescessary files that were included
458
- Removed some unnecessary files that were included
137
- Fixed some typos mentioned
459
- Fixed some typos mentioned
138
- Addressed dt-binding issues and added hc-interrupters definition to usb-xhci.yaml
460
- Addressed dt-binding issues and added hc-interrupters definition to usb-xhci.yaml
139
461
140
XHCI:
462
XHCI:
141
- Moved secondary skip events API to xhci-ring and updated implementation
463
- Moved secondary skip events API to xhci-ring and updated implementation
...
...
168
- Removed some fixme tags for conditions that may not be needed/addressed.
490
- Removed some fixme tags for conditions that may not be needed/addressed.
169
- Repurposed the new endpoint stop sync API to be utilized in other places.
491
- Repurposed the new endpoint stop sync API to be utilized in other places.
170
- Fixed potential compile issue if XHCI sideband config is not defined.
492
- Fixed potential compile issue if XHCI sideband config is not defined.
171
493
172
ASoC:
494
ASoC:
173
- Added sound jack control into the Q6USB driver. Allows for userpsace to know when
495
- Added sound jack control into the Q6USB driver. Allows for userspsace to know when
174
an offload capable device is connected.
496
an offload capable device is connected.
175
497
176
USB SND:
498
USB SND:
177
- Avoided exporting _snd_pcm_hw_param_set based on Takashi's recommendation.
499
- Avoided exporting _snd_pcm_hw_param_set based on Takashi's recommendation.
178
- Split USB QMI packet header definitions into a separate commit. This is used to
500
- Split USB QMI packet header definitions into a separate commit. This is used to
...
...
237
- Created separate dt-bindings for defining USB_RX port.
559
- Created separate dt-bindings for defining USB_RX port.
238
- Increased APR timeout to accommodate the situation where the AFE port start
560
- Increased APR timeout to accommodate the situation where the AFE port start
239
command could be delayed due to having to issue a USB bus resume while
561
command could be delayed due to having to issue a USB bus resume while
240
handling the QMI stream start command.
562
handling the QMI stream start command.
241
563
242
USB SND:
564
Mathias Nyman (1):
243
- Added a platform ops during usb_audio_suspend(). This allows for the USB
565
xhci: sideband: add initial api to register a secondary interrupter
244
offload driver to halt the audio stream when system enters PM suspend. This
566
entity
245
ensures the audio DSP is not issuing transfers on the USB bus.
246
- Do not override platform ops if they are already populated.
247
- Introduce a shared status variable between the USB offload and USB SND layers,
248
to ensure that only one path is active at a time. If the USB bus is occupied,
249
then userspace is notified that the path is busy.
250
251
Mathias Nyman (4):
252
xhci: split free interrupter into separate remove and free parts
253
xhci: add support to allocate several interrupters
254
xhci: add helper to stop endpoint and wait for completion
255
xhci: sideband: add initial api to register a sideband entity
256
567
257
Wesley Cheng (30):
568
Wesley Cheng (30):
258
usb: host: xhci-mem: Cleanup pending secondary event ring events
569
usb: host: xhci-mem: Cleanup pending secondary event ring events
259
usb: host: xhci-mem: Allow for interrupter clients to choose specific
570
usb: host: xhci-mem: Allow for interrupter clients to choose specific
260
index
571
index
261
ASoC: Add SOC USB APIs for adding an USB backend
572
usb: host: xhci-plat: Set XHCI max interrupters if property is present
573
usb: host: xhci: Notify xHCI sideband on transfer ring free
574
usb: dwc3: Specify maximum number of XHCI interrupters
575
ALSA: Add USB audio device jack type
576
ALSA: usb-audio: Export USB SND APIs for modules
577
ALSA: usb-audio: Check for support for requested audio format
578
ALSA: usb-audio: Save UAC sample size information
579
ALSA: usb-audio: Prevent starting of audio stream if in use
580
ALSA: usb-audio: Introduce USB SND platform op callbacks
581
ALSA: usb-audio: Allow for rediscovery of connected USB SND devices
582
ASoC: Add SoC USB APIs for adding an USB backend
583
ASoC: usb: Add PCM format check API for USB backend
584
ASoC: usb: Create SOC USB SND jack kcontrol
585
ASoC: usb: Fetch ASoC card and pcm device information
586
ASoC: usb: Rediscover USB SND devices on USB port add
587
ASoC: doc: Add documentation for SOC USB
262
ASoC: dt-bindings: qcom,q6dsp-lpass-ports: Add USB_RX port
588
ASoC: dt-bindings: qcom,q6dsp-lpass-ports: Add USB_RX port
589
ASoC: dt-bindings: Update example for enabling USB offload on SM8250
263
ASoC: qcom: qdsp6: Introduce USB AFE port to q6dsp
590
ASoC: qcom: qdsp6: Introduce USB AFE port to q6dsp
264
ASoC: qdsp6: q6afe: Increase APR timeout
591
ASoC: qcom: qdsp6: q6afe: Increase APR timeout
265
ASoC: qcom: qdsp6: Add USB backend ASoC driver for Q6
592
ASoC: qcom: qdsp6: Add USB backend ASoC driver for Q6
266
ALSA: usb-audio: Introduce USB SND platform op callbacks
593
ASoC: qcom: qdsp6: Add headphone jack for offload connection status
267
ALSA: usb-audio: Export USB SND APIs for modules
594
ASoC: qcom: qdsp6: Fetch USB offload mapped card and PCM device
268
dt-bindings: usb: xhci: Add num-hc-interrupters definition
269
dt-bindings: usb: dwc3: Limit num-hc-interrupters definition
270
usb: dwc3: Specify maximum number of XHCI interrupters
271
usb: host: xhci-plat: Set XHCI max interrupters if property is present
272
ALSA: usb-audio: qcom: Add USB QMI definitions
595
ALSA: usb-audio: qcom: Add USB QMI definitions
273
ALSA: usb-audio: qcom: Introduce QC USB SND offloading support
596
ALSA: usb-audio: qcom: Introduce QC USB SND offloading support
274
ALSA: usb-audio: Check for support for requested audio format
597
ALSA: usb-audio: qcom: Don't allow USB offload path if PCM device is
275
ASoC: usb: Add PCM format check API for USB backend
598
in use
276
ASoC: qcom: qdsp6: Ensure PCM format is supported by USB audio device
599
ALSA: usb-audio: qcom: Add USB offload route kcontrol
277
ALSA: usb-audio: Prevent starting of audio stream if in use
600
ALSA: usb-audio: qcom: Notify USB audio devices on USB offload probing
278
ASoC: dt-bindings: Add Q6USB backend
601
279
ASoC: dt-bindings: Update example for enabling USB offload on SM8250
280
ASoC: qcom: qdsp6: q6afe: Split USB AFE dev_token param into separate
281
API
282
ALSA: usb-audio: qcom: Populate PCM and USB chip information
283
ASoC: qcom: qdsp6: Add support to track available USB PCM devices
284
ASoC: qcom: qdsp6: Add SND kcontrol to select offload device
285
ASoC: qcom: qdsp6: Add SND kcontrol for fetching offload status
286
ASoC: qcom: qdsp6: Add headphone jack for offload connection status
287
ALSA: usb-audio: qcom: Use card and PCM index from QMI request
288
ALSA: usb-audio: Allow for rediscovery of connected USB SND devices
289
ASoC: usb: Rediscover USB SND devices on USB port add
290
291
.../devicetree/bindings/sound/qcom,q6usb.yaml | 55 +
292
.../bindings/sound/qcom,sm8250.yaml | 15 +
602
.../bindings/sound/qcom,sm8250.yaml | 15 +
293
.../devicetree/bindings/usb/snps,dwc3.yaml | 4 +
603
Documentation/sound/soc/index.rst | 1 +
294
.../devicetree/bindings/usb/usb-xhci.yaml | 6 +
604
Documentation/sound/soc/usb.rst | 482 ++++
295
drivers/usb/dwc3/core.c | 12 +
605
drivers/usb/dwc3/core.c | 12 +
296
drivers/usb/dwc3/core.h | 2 +
606
drivers/usb/dwc3/core.h | 2 +
297
drivers/usb/dwc3/host.c | 5 +-
607
drivers/usb/dwc3/host.c | 3 +
298
drivers/usb/host/Kconfig | 9 +
608
drivers/usb/host/Kconfig | 9 +
299
drivers/usb/host/Makefile | 4 +
609
drivers/usb/host/Makefile | 2 +
300
drivers/usb/host/xhci-debugfs.c | 2 +-
610
drivers/usb/host/xhci-mem.c | 31 +-
301
drivers/usb/host/xhci-mem.c | 136 +-
302
drivers/usb/host/xhci-plat.c | 2 +
611
drivers/usb/host/xhci-plat.c | 2 +
303
drivers/usb/host/xhci-ring.c | 53 +-
612
drivers/usb/host/xhci-ring.c | 47 +-
304
drivers/usb/host/xhci-sideband.c | 372 ++++
613
drivers/usb/host/xhci-sideband.c | 457 ++++
305
drivers/usb/host/xhci.c | 114 +-
614
drivers/usb/host/xhci.c | 5 +-
306
drivers/usb/host/xhci.h | 18 +-
615
drivers/usb/host/xhci.h | 13 +-
307
.../sound/qcom,q6dsp-lpass-ports.h | 1 +
616
.../sound/qcom,q6dsp-lpass-ports.h | 1 +
308
include/linux/usb/xhci-sideband.h | 67 +
617
include/linux/mod_devicetable.h | 2 +-
618
include/linux/usb/xhci-sideband.h | 102 +
619
include/sound/jack.h | 4 +-
309
include/sound/q6usboffload.h | 20 +
620
include/sound/q6usboffload.h | 20 +
310
include/sound/soc-usb.h | 51 +
621
include/sound/soc-usb.h | 138 ++
311
sound/soc/Makefile | 2 +-
622
include/uapi/linux/input-event-codes.h | 3 +-
312
sound/soc/qcom/Kconfig | 4 +
623
sound/core/jack.c | 6 +-
624
sound/soc/Kconfig | 10 +
625
sound/soc/Makefile | 2 +
626
sound/soc/qcom/Kconfig | 15 +
627
sound/soc/qcom/Makefile | 2 +
313
sound/soc/qcom/qdsp6/Makefile | 1 +
628
sound/soc/qcom/qdsp6/Makefile | 1 +
314
sound/soc/qcom/qdsp6/q6afe-dai.c | 56 +
629
sound/soc/qcom/qdsp6/q6afe-dai.c | 60 +
315
sound/soc/qcom/qdsp6/q6afe.c | 206 +-
630
sound/soc/qcom/qdsp6/q6afe.c | 194 +-
316
sound/soc/qcom/qdsp6/q6afe.h | 36 +-
631
sound/soc/qcom/qdsp6/q6afe.h | 36 +-
317
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 23 +
632
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 23 +
318
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h | 1 +
633
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h | 1 +
319
sound/soc/qcom/qdsp6/q6routing.c | 9 +
634
sound/soc/qcom/qdsp6/q6routing.c | 32 +-
320
sound/soc/qcom/qdsp6/q6usb.c | 450 ++++
635
sound/soc/qcom/qdsp6/q6usb.c | 385 ++++
321
sound/soc/soc-usb.c | 201 ++
636
sound/soc/qcom/sm8250.c | 24 +-
322
sound/usb/Kconfig | 15 +
637
sound/soc/qcom/usb_offload_utils.c | 56 +
638
sound/soc/qcom/usb_offload_utils.h | 30 +
639
sound/soc/soc-usb.c | 322 +++
640
sound/usb/Kconfig | 24 +
323
sound/usb/Makefile | 2 +-
641
sound/usb/Makefile | 2 +-
324
sound/usb/card.c | 123 ++
642
sound/usb/card.c | 106 +
325
sound/usb/card.h | 23 +
643
sound/usb/card.h | 17 +
326
sound/usb/endpoint.c | 1 +
644
sound/usb/endpoint.c | 1 +
645
sound/usb/format.c | 1 +
327
sound/usb/helper.c | 1 +
646
sound/usb/helper.c | 1 +
328
sound/usb/pcm.c | 94 +-
647
sound/usb/pcm.c | 104 +-
329
sound/usb/pcm.h | 11 +
648
sound/usb/pcm.h | 11 +
330
sound/usb/qcom/Makefile | 2 +
649
sound/usb/qcom/Makefile | 6 +
331
sound/usb/qcom/qc_audio_offload.c | 1867 +++++++++++++++++
650
sound/usb/qcom/mixer_usb_offload.c | 158 ++
332
sound/usb/qcom/usb_audio_qmi_v01.c | 892 ++++++++
651
sound/usb/qcom/mixer_usb_offload.h | 17 +
333
sound/usb/qcom/usb_audio_qmi_v01.h | 162 ++
652
sound/usb/qcom/qc_audio_offload.c | 2005 +++++++++++++++++
334
43 files changed, 5041 insertions(+), 89 deletions(-)
653
sound/usb/qcom/usb_audio_qmi_v01.c | 863 +++++++
335
create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6usb.yaml
654
sound/usb/qcom/usb_audio_qmi_v01.h | 164 ++
655
53 files changed, 5975 insertions(+), 55 deletions(-)
656
create mode 100644 Documentation/sound/soc/usb.rst
336
create mode 100644 drivers/usb/host/xhci-sideband.c
657
create mode 100644 drivers/usb/host/xhci-sideband.c
337
create mode 100644 include/linux/usb/xhci-sideband.h
658
create mode 100644 include/linux/usb/xhci-sideband.h
338
create mode 100644 include/sound/q6usboffload.h
659
create mode 100644 include/sound/q6usboffload.h
339
create mode 100644 include/sound/soc-usb.h
660
create mode 100644 include/sound/soc-usb.h
340
create mode 100644 sound/soc/qcom/qdsp6/q6usb.c
661
create mode 100644 sound/soc/qcom/qdsp6/q6usb.c
662
create mode 100644 sound/soc/qcom/usb_offload_utils.c
663
create mode 100644 sound/soc/qcom/usb_offload_utils.h
341
create mode 100644 sound/soc/soc-usb.c
664
create mode 100644 sound/soc/soc-usb.c
342
create mode 100644 sound/usb/qcom/Makefile
665
create mode 100644 sound/usb/qcom/Makefile
666
create mode 100644 sound/usb/qcom/mixer_usb_offload.c
667
create mode 100644 sound/usb/qcom/mixer_usb_offload.h
343
create mode 100644 sound/usb/qcom/qc_audio_offload.c
668
create mode 100644 sound/usb/qcom/qc_audio_offload.c
344
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.c
669
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.c
345
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.h
670
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.h
diff view generated by jsdifflib
Deleted patch
1
From: Mathias Nyman <mathias.nyman@linux.intel.com>
2
1
3
The current function that both removes and frees an interrupter isn't
4
optimal when using several interrupters. The array of interrupters need
5
to be protected with a lock while removing interrupters, but the default
6
xhci spin lock can't be used while freeing the interrupters event ring
7
segment table as dma_free_coherent() should be called with IRQs enabled.
8
9
There is no need to free the interrupter under the lock, so split this
10
code into separate unlocked free part, and a lock protected remove part.
11
12
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
13
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
14
---
15
drivers/usb/host/xhci-mem.c | 32 +++++++++++++++++++++-----------
16
1 file changed, 21 insertions(+), 11 deletions(-)
17
18
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
19
index XXXXXXX..XXXXXXX 100644
20
--- a/drivers/usb/host/xhci-mem.c
21
+++ b/drivers/usb/host/xhci-mem.c
22
@@ -XXX,XX +XXX,XX @@ int xhci_alloc_erst(struct xhci_hcd *xhci,
23
}
24
25
static void
26
-xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
27
+xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
28
{
29
-    struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
30
-    size_t erst_size;
31
    u64 tmp64;
32
    u32 tmp;
33
34
    if (!ir)
35
        return;
36
37
-    erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries;
38
-    if (ir->erst.entries)
39
-        dma_free_coherent(dev, erst_size,
40
-                 ir->erst.entries,
41
-                 ir->erst.erst_dma_addr);
42
-    ir->erst.entries = NULL;
43
-
44
    /*
45
     * Clean out interrupter registers except ERSTBA. Clearing either the
46
     * low or high 32 bits of ERSTBA immediately causes the controller to
47
@@ -XXX,XX +XXX,XX @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
48
        tmp64 &= (u64) ERST_PTR_MASK;
49
        xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
50
    }
51
+}
52
+
53
+static void
54
+xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
55
+{
56
+    struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
57
+    size_t erst_size;
58
+
59
+    if (!ir)
60
+        return;
61
+
62
+    erst_size = sizeof(struct xhci_erst_entry) * ir->erst.num_entries;
63
+    if (ir->erst.entries)
64
+        dma_free_coherent(dev, erst_size,
65
+                 ir->erst.entries,
66
+                 ir->erst.erst_dma_addr);
67
+    ir->erst.entries = NULL;
68
69
-    /* free interrrupter event ring */
70
+    /* free interrupter event ring */
71
    if (ir->event_ring)
72
        xhci_ring_free(xhci, ir->event_ring);
73
+
74
    ir->event_ring = NULL;
75
76
    kfree(ir);
77
@@ -XXX,XX +XXX,XX @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
78
79
    cancel_delayed_work_sync(&xhci->cmd_timer);
80
81
+    xhci_remove_interrupter(xhci, xhci->interrupter);
82
    xhci_free_interrupter(xhci, xhci->interrupter);
83
    xhci->interrupter = NULL;
84
    xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");
diff view generated by jsdifflib
1
From: Mathias Nyman <mathias.nyman@linux.intel.com>
1
From: Mathias Nyman <mathias.nyman@linux.intel.com>
2
2
3
Introduce XHCI sideband, which manages the USB endpoints being requested by
3
Introduce XHCI sideband, which manages the USB endpoints being requested by
4
a client driver. This is used for when client drivers are attempting to
4
a client driver. This is used for when client drivers are attempting to
5
offload USB endpoints to another entity for handling USB transfers. XHCI
5
offload USB endpoints to another entity for handling USB transfers. XHCI
6
sideband will allow for drivers to fetch the required information about the
6
sec intr will allow for drivers to fetch the required information about the
7
transfer ring, so the user can submit transfers independently. Expose the
7
transfer ring, so the user can submit transfers independently. Expose the
8
required APIs for drivers to register and request for a USB endpoint and to
8
required APIs for drivers to register and request for a USB endpoint and to
9
manage XHCI secondary interrupters.
9
manage XHCI secondary interrupters.
10
10
11
Multiple ring segment page linking and proper endpoint clean up added by
11
Driver renaming, multiple ring segment page linking, proper endpoint clean
12
Wesley Cheng to complete original concept code by Mathias Nyman.
12
up, and allowing module compilation added by Wesley Cheng to complete
13
original concept code by Mathias Nyman.
13
14
14
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
15
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
15
Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com>
16
Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com>
16
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
17
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
17
---
18
---
18
drivers/usb/host/Kconfig | 9 +
19
drivers/usb/host/Kconfig | 9 +
19
drivers/usb/host/Makefile | 4 +
20
drivers/usb/host/Makefile | 2 +
20
drivers/usb/host/xhci-sideband.c | 371 ++++++++++++++++++++++++++++++
21
drivers/usb/host/xhci-sideband.c | 429 ++++++++++++++++++++++++++++++
21
drivers/usb/host/xhci.h | 4 +
22
drivers/usb/host/xhci.h | 4 +
22
include/linux/usb/xhci-sideband.h | 66 ++++++
23
include/linux/usb/xhci-sideband.h | 74 ++++++
23
5 files changed, 454 insertions(+)
24
5 files changed, 518 insertions(+)
24
create mode 100644 drivers/usb/host/xhci-sideband.c
25
create mode 100644 drivers/usb/host/xhci-sideband.c
25
create mode 100644 include/linux/usb/xhci-sideband.h
26
create mode 100644 include/linux/usb/xhci-sideband.h
26
27
27
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
28
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
28
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
...
...
31
@@ -XXX,XX +XXX,XX @@ config USB_XHCI_RZV2M
32
@@ -XXX,XX +XXX,XX @@ config USB_XHCI_RZV2M
32
     Say 'Y' to enable the support for the xHCI host controller
33
     Say 'Y' to enable the support for the xHCI host controller
33
     found in Renesas RZ/V2M SoC.
34
     found in Renesas RZ/V2M SoC.
34
35
35
+config USB_XHCI_SIDEBAND
36
+config USB_XHCI_SIDEBAND
36
+    bool "xHCI support for sideband"
37
+    tristate "xHCI support for sideband"
37
+    help
38
+    help
38
+     Say 'Y' to enable the support for the xHCI sideband capability.
39
+     Say 'Y' to enable the support for the xHCI sideband capability.
39
+     provide a mechanism for a sideband datapath for payload associated
40
+     Provide a mechanism for a sideband datapath for payload associated
40
+     with audio class endpoints. This allows for an audio DSP to use
41
+     with audio class endpoints. This allows for an audio DSP to use
41
+     xHCI USB endpoints directly, allowing CPU to sleep while playing
42
+     xHCI USB endpoints directly, allowing CPU to sleep while playing
42
+     audio
43
+     audio.
43
+
44
+
44
config USB_XHCI_TEGRA
45
config USB_XHCI_TEGRA
45
    tristate "xHCI support for NVIDIA Tegra SoCs"
46
    tristate "xHCI support for NVIDIA Tegra SoCs"
46
    depends on PHY_TEGRA_XUSB
47
    depends on PHY_TEGRA_XUSB
47
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
48
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
...
...
50
+++ b/drivers/usb/host/Makefile
51
+++ b/drivers/usb/host/Makefile
51
@@ -XXX,XX +XXX,XX @@ endif
52
@@ -XXX,XX +XXX,XX @@ endif
52
xhci-rcar-hcd-y                += xhci-rcar.o
53
xhci-rcar-hcd-y                += xhci-rcar.o
53
xhci-rcar-hcd-$(CONFIG_USB_XHCI_RZV2M)    += xhci-rzv2m.o
54
xhci-rcar-hcd-$(CONFIG_USB_XHCI_RZV2M)    += xhci-rzv2m.o
54
55
55
+ifneq ($(CONFIG_USB_XHCI_SIDEBAND),)
56
+obj-$(CONFIG_USB_XHCI_SIDEBAND) += xhci-sideband.o
56
+    xhci-hcd-y        += xhci-sideband.o
57
+endif
58
+
57
+
59
obj-$(CONFIG_USB_PCI)    += pci-quirks.o
58
obj-$(CONFIG_USB_PCI)    += pci-quirks.o
60
59
61
obj-$(CONFIG_USB_EHCI_HCD)    += ehci-hcd.o
60
obj-$(CONFIG_USB_EHCI_HCD)    += ehci-hcd.o
62
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
61
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
...
...
68
+// SPDX-License-Identifier: GPL-2.0
67
+// SPDX-License-Identifier: GPL-2.0
69
+
68
+
70
+/*
69
+/*
71
+ * xHCI host controller sideband support
70
+ * xHCI host controller sideband support
72
+ *
71
+ *
73
+ * Copyright (c) 2023, Intel Corporation.
72
+ * Copyright (c) 2023-2025, Intel Corporation.
74
+ *
73
+ *
75
+ * Author: Mathias Nyman
74
+ * Author: Mathias Nyman
76
+ */
75
+ */
77
+
76
+
78
+#include <linux/usb/xhci-sideband.h>
77
+#include <linux/usb/xhci-sideband.h>
...
...
97
+    n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
96
+    n_pages = PAGE_ALIGN(sz) >> PAGE_SHIFT;
98
+    pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
97
+    pages = kvmalloc_array(n_pages, sizeof(struct page *), GFP_KERNEL);
99
+    if (!pages)
98
+    if (!pages)
100
+        return NULL;
99
+        return NULL;
101
+
100
+
102
+    sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
101
+    sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
103
+    if (!sgt) {
102
+    if (!sgt) {
104
+        kvfree(pages);
103
+        kvfree(pages);
105
+        return NULL;
104
+        return NULL;
106
+    }
105
+    }
107
+
106
+
108
+    seg = ring->first_seg;
107
+    seg = ring->first_seg;
108
+    if (!seg)
109
+        goto err;
109
+    /*
110
+    /*
110
+     * Rings can potentially have multiple segments, create an array that
111
+     * Rings can potentially have multiple segments, create an array that
111
+     * carries page references to allocated segments. Utilize the
112
+     * carries page references to allocated segments. Utilize the
112
+     * sg_alloc_table_from_pages() to create the sg table, and to ensure
113
+     * sg_alloc_table_from_pages() to create the sg table, and to ensure
113
+     * that page links are created.
114
+     * that page links are created.
114
+     */
115
+     */
115
+    for (i = 0; i < ring->num_segs; i++) {
116
+    for (i = 0; i < ring->num_segs; i++) {
116
+        dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
117
+        dma_get_sgtable(dev, sgt, seg->trbs, seg->dma,
117
+                    TRB_SEGMENT_SIZE);
118
+                TRB_SEGMENT_SIZE);
118
+        pages[i] = sg_page(sgt->sgl);
119
+        pages[i] = sg_page(sgt->sgl);
119
+        sg_free_table(sgt);
120
+        sg_free_table(sgt);
120
+        seg = seg->next;
121
+        seg = seg->next;
121
+    }
122
+    }
122
+
123
+
123
+    if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL)) {
124
+    if (sg_alloc_table_from_pages(sgt, pages, n_pages, 0, sz, GFP_KERNEL))
124
+        kvfree(pages);
125
+        goto err;
125
+        kfree(sgt);
126
+
126
+
127
+        return NULL;
128
+    }
129
+    /*
127
+    /*
130
+     * Save first segment dma address to sg dma_address field for the sideband
128
+     * Save first segment dma address to sg dma_address field for the sideband
131
+     * client to have access to the IOVA of the ring.
129
+     * client to have access to the IOVA of the ring.
132
+     */
130
+     */
133
+    sg_dma_address(sgt->sgl) = ring->first_seg->dma;
131
+    sg_dma_address(sgt->sgl) = ring->first_seg->dma;
134
+
132
+
135
+    return sgt;
133
+    return sgt;
134
+
135
+err:
136
+    kvfree(pages);
137
+    kfree(sgt);
138
+
139
+    return NULL;
136
+}
140
+}
137
+
141
+
138
+static void
142
+static void
139
+__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
143
+__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
140
+{
144
+{
...
...
167
+             struct usb_host_endpoint *host_ep)
171
+             struct usb_host_endpoint *host_ep)
168
+{
172
+{
169
+    struct xhci_virt_ep *ep;
173
+    struct xhci_virt_ep *ep;
170
+    unsigned int ep_index;
174
+    unsigned int ep_index;
171
+
175
+
176
+    mutex_lock(&sb->mutex);
172
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
177
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
173
+    ep = &sb->vdev->eps[ep_index];
178
+    ep = &sb->vdev->eps[ep_index];
174
+
179
+
175
+    if (ep->ep_state & EP_HAS_STREAMS)
180
+    if (ep->ep_state & EP_HAS_STREAMS) {
181
+        mutex_unlock(&sb->mutex);
176
+        return -EINVAL;
182
+        return -EINVAL;
183
+    }
177
+
184
+
178
+    /*
185
+    /*
179
+     * Note, we don't know the DMA mask of the audio DSP device, if its
186
+     * Note, we don't know the DMA mask of the audio DSP device, if its
180
+     * smaller than for xhci it won't be able to access the endpoint ring
187
+     * smaller than for xhci it won't be able to access the endpoint ring
181
+     * buffer. This could be solved by not allowing the audio class driver
188
+     * buffer. This could be solved by not allowing the audio class driver
182
+     * to add the endpoint the normal way, but instead offload it immediately,
189
+     * to add the endpoint the normal way, but instead offload it immediately,
183
+     * and let this function add the endpoint and allocate the ring buffer
190
+     * and let this function add the endpoint and allocate the ring buffer
184
+     * with the smallest common DMA mask
191
+     * with the smallest common DMA mask
185
+     */
192
+     */
186
+
193
+    if (sb->eps[ep_index] || ep->sideband) {
187
+    if (sb->eps[ep_index] || ep->sideband)
194
+        mutex_unlock(&sb->mutex);
188
+        return -EBUSY;
195
+        return -EBUSY;
196
+    }
189
+
197
+
190
+    ep->sideband = sb;
198
+    ep->sideband = sb;
191
+    sb->eps[ep_index] = ep;
199
+    sb->eps[ep_index] = ep;
200
+    mutex_unlock(&sb->mutex);
192
+
201
+
193
+    return 0;
202
+    return 0;
194
+}
203
+}
195
+EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
204
+EXPORT_SYMBOL_GPL(xhci_sideband_add_endpoint);
196
+
205
+
...
...
211
+             struct usb_host_endpoint *host_ep)
220
+             struct usb_host_endpoint *host_ep)
212
+{
221
+{
213
+    struct xhci_virt_ep *ep;
222
+    struct xhci_virt_ep *ep;
214
+    unsigned int ep_index;
223
+    unsigned int ep_index;
215
+
224
+
225
+    mutex_lock(&sb->mutex);
216
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
226
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
217
+    ep = sb->eps[ep_index];
227
+    ep = sb->eps[ep_index];
218
+
228
+
219
+    if (!ep || !ep->sideband)
229
+    if (!ep || !ep->sideband || ep->sideband != sb) {
230
+        mutex_unlock(&sb->mutex);
220
+        return -ENODEV;
231
+        return -ENODEV;
232
+    }
221
+
233
+
222
+    __xhci_sideband_remove_endpoint(sb, ep);
234
+    __xhci_sideband_remove_endpoint(sb, ep);
223
+    xhci_initialize_ring_info(ep->ring, 1);
235
+    xhci_initialize_ring_info(ep->ring);
236
+    mutex_unlock(&sb->mutex);
224
+
237
+
225
+    return 0;
238
+    return 0;
226
+}
239
+}
227
+EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint);
240
+EXPORT_SYMBOL_GPL(xhci_sideband_remove_endpoint);
228
+
241
+
...
...
234
+    unsigned int ep_index;
247
+    unsigned int ep_index;
235
+
248
+
236
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
249
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
237
+    ep = sb->eps[ep_index];
250
+    ep = sb->eps[ep_index];
238
+
251
+
239
+    if (!ep || ep->sideband != sb)
252
+    if (!ep || !ep->sideband || ep->sideband != sb)
240
+        return -EINVAL;
253
+        return -EINVAL;
241
+
254
+
242
+    return xhci_stop_endpoint_sync(sb->xhci, ep, 0);
255
+    return xhci_stop_endpoint_sync(sb->xhci, ep, 0, GFP_KERNEL);
243
+}
256
+}
244
+EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint);
257
+EXPORT_SYMBOL_GPL(xhci_sideband_stop_endpoint);
245
+
258
+
246
+/**
259
+/**
247
+ * xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address
260
+ * xhci_sideband_get_endpoint_buffer - gets the endpoint transfer buffer address
248
+ * @sb: sideband instance for this usb device
261
+ * @sb: sideband instance for this usb device
249
+ * @host_ep: usb host endpoint
262
+ * @host_ep: usb host endpoint
250
+ *
263
+ *
251
+ * Returns the address of the endpoint buffer where xHC controller reads queued
264
+ * Returns the address of the endpoint buffer where xHC controller reads queued
252
+ * transfer TRBs from. This is the starting address of the ringbuffer where the
265
+ * transfer TRBs from. This is the starting address of the ringbuffer where the
253
+ * sidband cliend should write TRBs to.
266
+ * sideband client should write TRBs to.
254
+ *
267
+ *
255
+ * Caller needs to free the returned sg_table
268
+ * Caller needs to free the returned sg_table
256
+ *
269
+ *
257
+ * Return: struct sg_table * if successful. NULL otherwise.
270
+ * Return: struct sg_table * if successful. NULL otherwise.
258
+ */
271
+ */
259
+struct sg_table *
272
+struct sg_table *
260
+xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
273
+xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
261
+             struct usb_host_endpoint *host_ep)
274
+                 struct usb_host_endpoint *host_ep)
262
+{
275
+{
263
+    struct xhci_virt_ep *ep;
276
+    struct xhci_virt_ep *ep;
264
+    unsigned int ep_index;
277
+    unsigned int ep_index;
265
+
278
+
266
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
279
+    ep_index = xhci_get_endpoint_index(&host_ep->desc);
267
+    ep = sb->eps[ep_index];
280
+    ep = sb->eps[ep_index];
268
+
281
+
269
+    if (!ep)
282
+    if (!ep || !ep->ring || !ep->sideband || ep->sideband != sb)
270
+        return NULL;
283
+        return NULL;
271
+
284
+
272
+    return xhci_ring_to_sgtable(sb, ep->ring);
285
+    return xhci_ring_to_sgtable(sb, ep->ring);
273
+}
286
+}
274
+EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer);
287
+EXPORT_SYMBOL_GPL(xhci_sideband_get_endpoint_buffer);
...
...
286
+ * Return: struct sg_table * if successful. NULL otherwise.
299
+ * Return: struct sg_table * if successful. NULL otherwise.
287
+ */
300
+ */
288
+struct sg_table *
301
+struct sg_table *
289
+xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
302
+xhci_sideband_get_event_buffer(struct xhci_sideband *sb)
290
+{
303
+{
291
+    if (!sb->ir)
304
+    if (!sb || !sb->ir)
292
+        return NULL;
305
+        return NULL;
293
+
306
+
294
+    return xhci_ring_to_sgtable(sb, sb->ir->event_ring);
307
+    return xhci_ring_to_sgtable(sb, sb->ir->event_ring);
295
+}
308
+}
296
+EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
309
+EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
297
+
310
+
298
+/**
311
+/**
299
+ * xhci_sideband_create_interrupter - creates a new interrupter for this sideband
312
+ * xhci_sideband_create_interrupter - creates a new interrupter for this sideband
300
+ * @sb: sideband instance for this usb device
313
+ * @sb: sideband instance for this usb device
314
+ * @num_seg: number of event ring segments to allocate
315
+ * @ip_autoclear: IP autoclearing support such as MSI implemented
301
+ *
316
+ *
302
+ * Sets up a xhci interrupter that can be used for this sideband accessed usb
317
+ * Sets up a xhci interrupter that can be used for this sideband accessed usb
303
+ * device. Transfer events for this device can be routed to this interrupters
318
+ * device. Transfer events for this device can be routed to this interrupters
304
+ * event ring by setting the 'Interrupter Target' field correctly when queueing
319
+ * event ring by setting the 'Interrupter Target' field correctly when queueing
305
+ * the transfer TRBs.
320
+ * the transfer TRBs.
306
+ * Once this interrupter is created the interrupter target ID can be obtained
321
+ * Once this interrupter is created the interrupter target ID can be obtained
307
+ * by calling xhci_sideband_interrupter_id()
322
+ * by calling xhci_sideband_interrupter_id()
308
+ *
323
+ *
309
+ * Returns 0 on success, negative error otherwise
324
+ * Returns 0 on success, negative error otherwise
310
+ */
325
+ */
311
+int
326
+int
312
+xhci_sideband_create_interrupter(struct xhci_sideband *sb)
327
+xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
313
+{
328
+                 bool ip_autoclear, u32 imod_interval)
314
+    if (sb->ir)
329
+{
315
+        return -EBUSY;
330
+    int ret = 0;
316
+
331
+
317
+    sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci));
332
+    if (!sb || !sb->xhci)
318
+    if (!sb->ir)
333
+        return -ENODEV;
319
+        return -ENOMEM;
334
+
320
+
335
+    mutex_lock(&sb->mutex);
321
+    return 0;
336
+    if (sb->ir) {
337
+        ret = -EBUSY;
338
+        goto out;
339
+    }
340
+
341
+    sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
342
+                         num_seg, imod_interval);
343
+    if (!sb->ir) {
344
+        ret = -ENOMEM;
345
+        goto out;
346
+    }
347
+
348
+    sb->ir->ip_autoclear = ip_autoclear;
349
+
350
+out:
351
+    mutex_unlock(&sb->mutex);
352
+
353
+    return ret;
322
+}
354
+}
323
+EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
355
+EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
324
+
356
+
325
+/**
357
+/**
326
+ * xhci_sideband_remove_interrupter - remove the interrupter from a sideband
358
+ * xhci_sideband_remove_interrupter - remove the interrupter from a sideband
...
...
333
+xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
365
+xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
334
+{
366
+{
335
+    if (!sb || !sb->ir)
367
+    if (!sb || !sb->ir)
336
+        return;
368
+        return;
337
+
369
+
370
+    mutex_lock(&sb->mutex);
338
+    xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
371
+    xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
339
+
372
+
340
+    sb->ir = NULL;
373
+    sb->ir = NULL;
374
+    mutex_unlock(&sb->mutex);
341
+}
375
+}
342
+EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
376
+EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
343
+
377
+
344
+/**
378
+/**
345
+ * xhci_sideband_interrupter_id - return the interrupter target id
379
+ * xhci_sideband_interrupter_id - return the interrupter target id
...
...
363
+}
397
+}
364
+EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
398
+EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
365
+
399
+
366
+/**
400
+/**
367
+ * xhci_sideband_register - register a sideband for a usb device
401
+ * xhci_sideband_register - register a sideband for a usb device
368
+ * @udev: usb device to be accessed via sideband
402
+ * @intf: usb interface associated with the sideband device
369
+ *
403
+ *
370
+ * Allows for clients to utilize XHCI interrupters and fetch transfer and event
404
+ * Allows for clients to utilize XHCI interrupters and fetch transfer and event
371
+ * ring parameters for executing data transfers.
405
+ * ring parameters for executing data transfers.
372
+ *
406
+ *
373
+ * Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
407
+ * Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
374
+ */
408
+ */
375
+struct xhci_sideband *
409
+struct xhci_sideband *
376
+xhci_sideband_register(struct usb_device *udev)
410
+xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type)
377
+{
411
+{
412
+    struct usb_device *udev = interface_to_usbdev(intf);
378
+    struct usb_hcd *hcd = bus_to_hcd(udev->bus);
413
+    struct usb_hcd *hcd = bus_to_hcd(udev->bus);
379
+    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
414
+    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
380
+    struct xhci_virt_device *vdev;
415
+    struct xhci_virt_device *vdev;
381
+    struct xhci_sideband *sb;
416
+    struct xhci_sideband *sb;
382
+
417
+
383
+    /* make sure the usb device is connected to a xhci controller */
418
+    /*
384
+    if (!udev->slot_id)
419
+     * Make sure the usb device is connected to a xhci controller. Fail
420
+     * registration if the type is anything other than XHCI_SIDEBAND_VENDOR,
421
+     * as this is the only type that is currently supported by xhci-sideband.
422
+     */
423
+    if (!udev->slot_id || type != XHCI_SIDEBAND_VENDOR)
385
+        return NULL;
424
+        return NULL;
386
+
425
+
387
+    sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev));
426
+    sb = kzalloc_node(sizeof(*sb), GFP_KERNEL, dev_to_node(hcd->self.sysdev));
388
+    if (!sb)
427
+    if (!sb)
389
+        return NULL;
428
+        return NULL;
429
+
430
+    mutex_init(&sb->mutex);
390
+
431
+
391
+    /* check this device isn't already controlled via sideband */
432
+    /* check this device isn't already controlled via sideband */
392
+    spin_lock_irq(&xhci->lock);
433
+    spin_lock_irq(&xhci->lock);
393
+
434
+
394
+    vdev = xhci->devs[udev->slot_id];
435
+    vdev = xhci->devs[udev->slot_id];
...
...
401
+        return NULL;
442
+        return NULL;
402
+    }
443
+    }
403
+
444
+
404
+    sb->xhci = xhci;
445
+    sb->xhci = xhci;
405
+    sb->vdev = vdev;
446
+    sb->vdev = vdev;
447
+    sb->intf = intf;
448
+    sb->type = type;
406
+    vdev->sideband = sb;
449
+    vdev->sideband = sb;
407
+
450
+
408
+    spin_unlock_irq(&xhci->lock);
451
+    spin_unlock_irq(&xhci->lock);
409
+
452
+
410
+    return sb;
453
+    return sb;
...
...
422
+ * the buffers.
465
+ * the buffers.
423
+ */
466
+ */
424
+void
467
+void
425
+xhci_sideband_unregister(struct xhci_sideband *sb)
468
+xhci_sideband_unregister(struct xhci_sideband *sb)
426
+{
469
+{
470
+    struct xhci_hcd *xhci;
427
+    int i;
471
+    int i;
428
+
472
+
473
+    if (!sb)
474
+        return;
475
+
476
+    xhci = sb->xhci;
477
+
478
+    mutex_lock(&sb->mutex);
429
+    for (i = 0; i < EP_CTX_PER_DEV; i++)
479
+    for (i = 0; i < EP_CTX_PER_DEV; i++)
430
+        if (sb->eps[i])
480
+        if (sb->eps[i])
431
+            __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
481
+            __xhci_sideband_remove_endpoint(sb, sb->eps[i]);
482
+    mutex_unlock(&sb->mutex);
432
+
483
+
433
+    xhci_sideband_remove_interrupter(sb);
484
+    xhci_sideband_remove_interrupter(sb);
434
+
485
+
486
+    spin_lock_irq(&xhci->lock);
487
+    sb->xhci = NULL;
435
+    sb->vdev->sideband = NULL;
488
+    sb->vdev->sideband = NULL;
489
+    spin_unlock_irq(&xhci->lock);
490
+
436
+    kfree(sb);
491
+    kfree(sb);
437
+}
492
+}
438
+EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
493
+EXPORT_SYMBOL_GPL(xhci_sideband_unregister);
494
+MODULE_DESCRIPTION("xHCI sideband driver for secondary interrupter management");
495
+MODULE_LICENSE("GPL");
439
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
496
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
440
index XXXXXXX..XXXXXXX 100644
497
index XXXXXXX..XXXXXXX 100644
441
--- a/drivers/usb/host/xhci.h
498
--- a/drivers/usb/host/xhci.h
442
+++ b/drivers/usb/host/xhci.h
499
+++ b/drivers/usb/host/xhci.h
443
@@ -XXX,XX +XXX,XX @@ struct xhci_virt_ep {
500
@@ -XXX,XX +XXX,XX @@ struct xhci_virt_ep {
444
    int            next_frame_id;
501
    int            next_frame_id;
445
    /* Use new Isoch TRB layout needed for extended TBC support */
502
    /* Use new Isoch TRB layout needed for extended TBC support */
446
    bool            use_extended_tbc;
503
    bool            use_extended_tbc;
447
+    /* set if this endpoint is controlled via sideband access*/
504
+    /* set if this endpoint is controlled via sideband access*/
448
+    struct xhci_sideband            *sideband;
505
+    struct xhci_sideband    *sideband;
449
};
506
};
450
507
451
enum xhci_overhead_type {
508
enum xhci_overhead_type {
452
@@ -XXX,XX +XXX,XX @@ struct xhci_virt_device {
509
@@ -XXX,XX +XXX,XX @@ struct xhci_virt_device {
453
    u16                current_mel;
510
    u16                current_mel;
454
    /* Used for the debugfs interfaces. */
511
    /* Used for the debugfs interfaces. */
455
    void                *debugfs_private;
512
    void                *debugfs_private;
456
+    /* set if this device is registered for sideband access */
513
+    /* set if this endpoint is controlled via sideband access*/
457
+    struct xhci_sideband            *sideband;
514
+    struct xhci_sideband    *sideband;
458
};
515
};
459
516
460
/*
517
/*
461
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
518
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
462
new file mode 100644
519
new file mode 100644
...
...
466
@@ -XXX,XX +XXX,XX @@
523
@@ -XXX,XX +XXX,XX @@
467
+/* SPDX-License-Identifier: GPL-2.0 */
524
+/* SPDX-License-Identifier: GPL-2.0 */
468
+/*
525
+/*
469
+ * xHCI host controller sideband support
526
+ * xHCI host controller sideband support
470
+ *
527
+ *
471
+ * Copyright (c) 2023, Intel Corporation.
528
+ * Copyright (c) 2023-2025, Intel Corporation.
472
+ *
529
+ *
473
+ * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
530
+ * Author: Mathias Nyman <mathias.nyman@linux.intel.com>
474
+ */
531
+ */
475
+
476
+#ifndef __LINUX_XHCI_SIDEBAND_H
532
+#ifndef __LINUX_XHCI_SIDEBAND_H
477
+#define __LINUX_XHCI_SIDEBAND_H
533
+#define __LINUX_XHCI_SIDEBAND_H
478
+
534
+
479
+#include <linux/scatterlist.h>
535
+#include <linux/scatterlist.h>
480
+#include <linux/usb.h>
536
+#include <linux/usb.h>
481
+
537
+
482
+#define    EP_CTX_PER_DEV        31    /* FIMXME defined twice, from xhci.h */
538
+#define    EP_CTX_PER_DEV        31    /* FIXME defined twice, from xhci.h */
483
+
539
+
484
+struct xhci_sideband;
540
+struct xhci_sideband;
541
+
542
+enum xhci_sideband_type {
543
+    XHCI_SIDEBAND_AUDIO,
544
+    XHCI_SIDEBAND_VENDOR,
545
+};
485
+
546
+
486
+/**
547
+/**
487
+ * struct xhci_sideband - representation of a sideband accessed usb device.
548
+ * struct xhci_sideband - representation of a sideband accessed usb device.
488
+ * @xhci: The xhci host controller the usb device is connected to
549
+ * @xhci: The xhci host controller the usb device is connected to
489
+ * @vdev: the usb device accessed via sideband
550
+ * @vdev: the usb device accessed via sideband
490
+ * @eps: array of endpoints controlled via sideband
551
+ * @eps: array of endpoints controlled via sideband
491
+ * @ir: event handling and buffer for sideband accessed device
552
+ * @ir: event handling and buffer for sideband accessed device
553
+ * @type: xHCI sideband type
554
+ * @mutex: mutex for sideband operations
555
+ * @intf: USB sideband client interface
492
+ *
556
+ *
493
+ * FIXME usb device accessed via sideband Keeping track of sideband accessed usb devices.
557
+ * FIXME usb device accessed via sideband Keeping track of sideband accessed usb devices.
494
+ */
558
+ */
495
+
496
+struct xhci_sideband {
559
+struct xhci_sideband {
497
+    struct xhci_hcd *xhci;
560
+    struct xhci_hcd *xhci;
498
+    struct xhci_virt_device *vdev;
561
+    struct xhci_virt_device *vdev;
499
+    struct xhci_virt_ep *eps[EP_CTX_PER_DEV];
562
+    struct xhci_virt_ep *eps[EP_CTX_PER_DEV];
500
+    struct xhci_interrupter *ir;
563
+    struct xhci_interrupter *ir;
564
+    enum xhci_sideband_type        type;
565
+
566
+    /* Synchronizing xHCI sideband operations with client drivers operations */
567
+    struct mutex            mutex;
568
+
569
+    struct usb_interface        *intf;
501
+};
570
+};
502
+
571
+
503
+struct xhci_sideband *
572
+struct xhci_sideband *
504
+xhci_sideband_register(struct usb_device *udev);
573
+xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type);
505
+void
574
+void
506
+xhci_sideband_unregister(struct xhci_sideband *sb);
575
+xhci_sideband_unregister(struct xhci_sideband *sb);
507
+int
576
+int
508
+xhci_sideband_add_endpoint(struct xhci_sideband *sb,
577
+xhci_sideband_add_endpoint(struct xhci_sideband *sb,
509
+             struct usb_host_endpoint *host_ep);
578
+             struct usb_host_endpoint *host_ep);
...
...
516
+struct sg_table *
585
+struct sg_table *
517
+xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
586
+xhci_sideband_get_endpoint_buffer(struct xhci_sideband *sb,
518
+                 struct usb_host_endpoint *host_ep);
587
+                 struct usb_host_endpoint *host_ep);
519
+struct sg_table *
588
+struct sg_table *
520
+xhci_sideband_get_event_buffer(struct xhci_sideband *sb);
589
+xhci_sideband_get_event_buffer(struct xhci_sideband *sb);
521
+
590
+int
522
+int
591
+xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
523
+xhci_sideband_create_interrupter(struct xhci_sideband *sb);
592
+                 bool ip_autoclear, u32 imod_interval);
524
+
525
+void
593
+void
526
+xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
594
+xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
527
+
528
+int
595
+int
529
+xhci_sideband_interrupter_id(struct xhci_sideband *sb);
596
+xhci_sideband_interrupter_id(struct xhci_sideband *sb);
530
+
531
+#endif /* __LINUX_XHCI_SIDEBAND_H */
597
+#endif /* __LINUX_XHCI_SIDEBAND_H */
532
+
diff view generated by jsdifflib
1
As part of xHCI bus suspend, the XHCI is halted. However, if there are
1
As part of xHCI bus suspend, the xHCI is halted. However, if there are
2
pending events in the secondary event ring, it is observed that the xHCI
2
pending events in the secondary event ring, it is observed that the xHCI
3
controller stops responding to further commands upon host or device
3
controller stops responding to further commands upon host or device
4
initiated bus resume. Iterate through all pending events and update the
4
initiated bus resume. Iterate through all pending events and update the
5
dequeue pointer to the beginning of the event ring.
5
dequeue pointer to the beginning of the event ring.
6
6
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
---
8
---
9
drivers/usb/host/xhci-mem.c | 13 +++++----
9
drivers/usb/host/xhci-mem.c | 7 +++++-
10
drivers/usb/host/xhci-ring.c | 51 +++++++++++++++++++++++++++++++++++-
10
drivers/usb/host/xhci-ring.c | 47 ++++++++++++++++++++++++++++++------
11
drivers/usb/host/xhci.c | 2 +-
11
drivers/usb/host/xhci.c | 2 +-
12
drivers/usb/host/xhci.h | 6 +++++
12
drivers/usb/host/xhci.h | 7 ++++++
13
4 files changed, 63 insertions(+), 9 deletions(-)
13
4 files changed, 54 insertions(+), 9 deletions(-)
14
14
15
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
15
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
16
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
17
--- a/drivers/usb/host/xhci-mem.c
17
--- a/drivers/usb/host/xhci-mem.c
18
+++ b/drivers/usb/host/xhci-mem.c
18
+++ b/drivers/usb/host/xhci-mem.c
19
@@ -XXX,XX +XXX,XX @@ xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
19
@@ -XXX,XX +XXX,XX @@ xhci_remove_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
20
        tmp &= ERST_SIZE_MASK;
20
        tmp &= ERST_SIZE_MASK;
21
        writel(tmp, &ir->ir_set->erst_size);
21
        writel(tmp, &ir->ir_set->erst_size);
22
22
23
-        tmp64 = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
23
-        xhci_write_64(xhci, ERST_EHB, &ir->ir_set->erst_dequeue);
24
-        tmp64 &= (u64) ERST_PTR_MASK;
24
+        xhci_update_erst_dequeue(xhci, ir, true);
25
-        xhci_write_64(xhci, tmp64, &ir->ir_set->erst_dequeue);
26
+        xhci_update_erst_dequeue(xhci, ir, ir->event_ring->first_seg->trbs);
27
    }
25
    }
28
}
26
}
29
27
30
@@ -XXX,XX +XXX,XX @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
28
@@ -XXX,XX +XXX,XX @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
31
    if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
29
        return;
32
        xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
30
    }
33
31
34
-    /* fixme, should we check xhci->interrupter[intr_num] == ir */
35
-    /* fixme locking */
36
-
37
    spin_lock_irq(&xhci->lock);
38
-
39
+    /*
32
+    /*
40
+     * Cleanup secondary interrupter to ensure there are no pending events.
33
+     * Cleanup secondary interrupter to ensure there are no pending events.
41
+     * This also updates event ring dequeue pointer back to the start.
34
+     * This also updates event ring dequeue pointer back to the start.
42
+     */
35
+     */
43
+    xhci_skip_sec_intr_events(xhci, ir->event_ring, ir);
36
+    xhci_skip_sec_intr_events(xhci, ir->event_ring, ir);
...
...
46
    xhci_remove_interrupter(xhci, ir);
39
    xhci_remove_interrupter(xhci, ir);
47
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
40
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
48
index XXXXXXX..XXXXXXX 100644
41
index XXXXXXX..XXXXXXX 100644
49
--- a/drivers/usb/host/xhci-ring.c
42
--- a/drivers/usb/host/xhci-ring.c
50
+++ b/drivers/usb/host/xhci-ring.c
43
+++ b/drivers/usb/host/xhci-ring.c
51
@@ -XXX,XX +XXX,XX @@ static int xhci_handle_event(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
44
@@ -XXX,XX +XXX,XX @@ static int xhci_handle_event_trb(struct xhci_hcd *xhci, struct xhci_interrupter
52
* - When all events have finished
45
* - When all events have finished
53
* - To avoid "Event Ring Full Error" condition
46
* - To avoid "Event Ring Full Error" condition
54
*/
47
*/
55
-static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
48
-static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
49
-                 struct xhci_interrupter *ir,
50
-                 bool clear_ehb)
56
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
51
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
57
                 struct xhci_interrupter *ir,
52
+             struct xhci_interrupter *ir,
58
                 union xhci_trb *event_ring_deq,
53
+             bool clear_ehb)
59
                 bool clear_ehb)
54
{
60
@@ -XXX,XX +XXX,XX @@ static void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
55
    u64 temp_64;
61
    xhci_write_64(xhci, temp_64, &ir->ir_set->erst_dequeue);
56
    dma_addr_t deq;
57
@@ -XXX,XX +XXX,XX @@ static void xhci_clear_interrupt_pending(struct xhci_interrupter *ir)
58
* Handle all OS-owned events on an interrupter event ring. It may drop
59
* and reaquire xhci->lock between event processing.
60
*/
61
-static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
62
+static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
63
+             bool skip_events)
64
{
65
    int event_loop = 0;
66
-    int err;
67
+    int err = 0;
68
    u64 temp;
69
70
    xhci_clear_interrupt_pending(ir);
71
@@ -XXX,XX +XXX,XX @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
72
73
    /* Process all OS owned event TRBs on this event ring */
74
    while (unhandled_event_trb(ir->event_ring)) {
75
-        err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);
76
+        if (!skip_events)
77
+            err = xhci_handle_event_trb(xhci, ir, ir->event_ring->dequeue);
78
79
        /*
80
         * If half a segment of events have been handled in one go then
81
@@ -XXX,XX +XXX,XX @@ static int xhci_handle_events(struct xhci_hcd *xhci, struct xhci_interrupter *ir
82
    return 0;
62
}
83
}
63
84
64
+/*
85
+/*
65
+ * Move the event ring dequeue pointer to skip events kept in the secondary
86
+ * Move the event ring dequeue pointer to skip events kept in the secondary
66
+ * event ring. This is used to ensure that pending events in the ring are
87
+ * event ring. This is used to ensure that pending events in the ring are
67
+ * acknowledged, so the XHCI HCD can properly enter suspend/resume. The
88
+ * acknowledged, so the xHCI HCD can properly enter suspend/resume. The
68
+ * secondary ring is typically maintained by an external component.
89
+ * secondary ring is typically maintained by an external component.
69
+ */
90
+ */
70
+void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
91
+void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
71
+    struct xhci_ring *ring,    struct xhci_interrupter *ir)
92
+             struct xhci_ring *ring,    struct xhci_interrupter *ir)
72
+{
93
+{
73
+    union xhci_trb *erdp_trb, *current_trb;
94
+    union xhci_trb *current_trb;
74
+    struct xhci_segment    *seg;
75
+    u64 erdp_reg;
95
+    u64 erdp_reg;
76
+    u32 iman_reg;
77
+    dma_addr_t deq;
96
+    dma_addr_t deq;
78
+    unsigned long segment_offset;
79
+
97
+
80
+    /* disable irq, ack pending interrupt and ack all pending events */
98
+    /* disable irq, ack pending interrupt and ack all pending events */
81
+    xhci_disable_interrupter(ir);
99
+    xhci_disable_interrupter(ir);
82
+    iman_reg = readl_relaxed(&ir->ir_set->irq_pending);
83
+    if (iman_reg & IMAN_IP)
84
+        writel_relaxed(iman_reg, &ir->ir_set->irq_pending);
85
+
100
+
86
+    /* last acked event trb is in erdp reg */
101
+    /* last acked event trb is in erdp reg */
87
+    erdp_reg = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
102
+    erdp_reg = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
88
+    deq = (dma_addr_t)(erdp_reg & ~ERST_PTR_MASK);
103
+    deq = (dma_addr_t)(erdp_reg & ERST_PTR_MASK);
89
+    if (!deq) {
104
+    if (!deq) {
90
+        xhci_dbg(xhci, "event ring handling not required\n");
105
+        xhci_err(xhci, "event ring handling not required\n");
91
+        return;
106
+        return;
92
+    }
107
+    }
93
+
108
+
94
+    seg = ring->first_seg;
109
+    current_trb = ir->event_ring->dequeue;
95
+    segment_offset = deq - seg->dma;
96
+
97
+    erdp_trb = current_trb = ir->event_ring->dequeue;
98
+    /* read cycle state of the last acked trb to find out CCS */
110
+    /* read cycle state of the last acked trb to find out CCS */
99
+    ring->cycle_state = le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE;
111
+    ring->cycle_state = le32_to_cpu(current_trb->event_cmd.flags) & TRB_CYCLE;
100
+
112
+
101
+    while (1) {
113
+    xhci_handle_events(xhci, ir, true);
102
+        inc_deq(xhci, ir->event_ring);
103
+        erdp_trb = ir->event_ring->dequeue;
104
+        /* cycle state transition */
105
+        if ((le32_to_cpu(erdp_trb->event_cmd.flags) & TRB_CYCLE) !=
106
+         ring->cycle_state)
107
+            break;
108
+    }
109
+
110
+    xhci_update_erst_dequeue(xhci, ir, current_trb);
111
+}
114
+}
112
+
115
+
113
/*
116
/*
114
* xHCI spec says we can get an interrupt, and if the HC has an error condition,
117
* xHCI spec says we can get an interrupt, and if the HC has an error condition,
115
* we might get bad data out of the event ring. Section 4.10.2.7 has a list of
118
* we might get bad data out of the event ring. Section 4.10.2.7 has a list of
119
@@ -XXX,XX +XXX,XX @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
120
    writel(status, &xhci->op_regs->status);
121
122
    /* This is the handler of the primary interrupter */
123
-    xhci_handle_events(xhci, xhci->interrupters[0]);
124
+    xhci_handle_events(xhci, xhci->interrupters[0], false);
125
out:
126
    spin_unlock(&xhci->lock);
127
116
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
128
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
117
index XXXXXXX..XXXXXXX 100644
129
index XXXXXXX..XXXXXXX 100644
118
--- a/drivers/usb/host/xhci.c
130
--- a/drivers/usb/host/xhci.c
119
+++ b/drivers/usb/host/xhci.c
131
+++ b/drivers/usb/host/xhci.c
120
@@ -XXX,XX +XXX,XX @@ static int xhci_enable_interrupter(struct xhci_interrupter *ir)
132
@@ -XXX,XX +XXX,XX @@ static int xhci_enable_interrupter(struct xhci_interrupter *ir)
...
...
128
140
129
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
141
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
130
index XXXXXXX..XXXXXXX 100644
142
index XXXXXXX..XXXXXXX 100644
131
--- a/drivers/usb/host/xhci.h
143
--- a/drivers/usb/host/xhci.h
132
+++ b/drivers/usb/host/xhci.h
144
+++ b/drivers/usb/host/xhci.h
133
@@ -XXX,XX +XXX,XX @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
145
@@ -XXX,XX +XXX,XX @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
134
struct xhci_interrupter *xhci_create_secondary_interrupter(struct usb_hcd *hcd);
146
                 u32 imod_interval);
135
void xhci_remove_secondary_interrupter(struct usb_hcd
147
void xhci_remove_secondary_interrupter(struct usb_hcd
136
                 *hcd, struct xhci_interrupter *ir);
148
                 *hcd, struct xhci_interrupter *ir);
137
+void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
149
+void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
138
+    struct xhci_ring *ring,    struct xhci_interrupter *ir);
150
+             struct xhci_ring *ring,
139
+int xhci_disable_interrupter(struct xhci_interrupter *ir);
151
+             struct xhci_interrupter *ir);
140
152
141
/* xHCI host controller glue */
153
/* xHCI host controller glue */
142
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
154
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
143
@@ -XXX,XX +XXX,XX @@ void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring);
155
@@ -XXX,XX +XXX,XX @@ int xhci_alloc_tt_info(struct xhci_hcd *xhci,
144
unsigned int count_trbs(u64 addr, u64 len);
156
        struct usb_tt *tt, gfp_t mem_flags);
157
int xhci_set_interrupter_moderation(struct xhci_interrupter *ir,
158
                 u32 imod_interval);
159
+int xhci_disable_interrupter(struct xhci_interrupter *ir);
160
161
/* xHCI ring, segment, TRB, and TD functions */
162
dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, union xhci_trb *trb);
163
@@ -XXX,XX +XXX,XX @@ unsigned int count_trbs(u64 addr, u64 len);
145
int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
164
int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
146
             int suspend, gfp_t gfp_flags);
165
             int suspend, gfp_t gfp_flags);
166
void xhci_process_cancelled_tds(struct xhci_virt_ep *ep);
147
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
167
+void xhci_update_erst_dequeue(struct xhci_hcd *xhci,
148
+                 struct xhci_interrupter *ir,
168
+             struct xhci_interrupter *ir,
149
+                 union xhci_trb *event_ring_deq);
169
+             bool clear_ehb);
150
170
151
/* xHCI roothub code */
171
/* xHCI roothub code */
152
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
172
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
diff view generated by jsdifflib
1
Some clients may operate only on a specific XHCI interrupter instance.
1
Some clients may operate only on a specific XHCI interrupter instance.
2
Allow for the associated class driver to request for the interrupter that
2
Allow for the associated class driver to request for the interrupter that
3
it requires.
3
it requires.
4
4
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
---
6
---
7
drivers/usb/host/xhci-mem.c | 30 ++++++++++++++++--------------
7
drivers/usb/host/xhci-mem.c | 24 ++++++++++++++----------
8
drivers/usb/host/xhci-sideband.c | 5 +++--
8
drivers/usb/host/xhci-sideband.c | 5 +++--
9
drivers/usb/host/xhci.h | 3 ++-
9
drivers/usb/host/xhci.h | 2 +-
10
include/linux/usb/xhci-sideband.h | 3 ++-
10
include/linux/usb/xhci-sideband.h | 2 +-
11
4 files changed, 23 insertions(+), 18 deletions(-)
11
4 files changed, 19 insertions(+), 14 deletions(-)
12
12
13
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
13
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
14
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
15
--- a/drivers/usb/host/xhci-mem.c
15
--- a/drivers/usb/host/xhci-mem.c
16
+++ b/drivers/usb/host/xhci-mem.c
16
+++ b/drivers/usb/host/xhci-mem.c
17
@@ -XXX,XX +XXX,XX @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
17
@@ -XXX,XX +XXX,XX @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
18
}
19
18
20
struct xhci_interrupter *
19
struct xhci_interrupter *
21
-xhci_create_secondary_interrupter(struct usb_hcd *hcd)
20
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
22
+xhci_create_secondary_interrupter(struct usb_hcd *hcd, int intr_num)
21
-                 u32 imod_interval)
22
+                 u32 imod_interval, unsigned int intr_num)
23
{
23
{
24
    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
24
    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
25
    struct xhci_interrupter *ir;
25
    struct xhci_interrupter *ir;
26
@@ -XXX,XX +XXX,XX @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd)
26
    unsigned int i;
27
    int err = -ENOSPC;
28
29
-    if (!xhci->interrupters || xhci->max_interrupters <= 1)
30
+    if (!xhci->interrupters || xhci->max_interrupters <= 1 ||
31
+     intr_num >= xhci->max_interrupters)
27
        return NULL;
32
        return NULL;
33
34
    ir = xhci_alloc_interrupter(xhci, segs, GFP_KERNEL);
35
@@ -XXX,XX +XXX,XX @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
36
        return NULL;
28
37
29
    spin_lock_irq(&xhci->lock);
38
    spin_lock_irq(&xhci->lock);
30
-
39
-
31
-    /* Find available secondary interrupter, interrupter 0 is reserved for primary */
40
-    /* Find available secondary interrupter, interrupter 0 is reserved for primary */
32
+    /* Find available secondary interrupter, interrupter 0 is reserverd for primary */
41
-    for (i = 1; i < xhci->max_interrupters; i++) {
33
    for (i = 1; i < xhci->max_interrupters; i++) {
34
-        if (xhci->interrupters[i] == NULL) {
42
-        if (xhci->interrupters[i] == NULL) {
35
-            err = xhci_add_interrupter(xhci, ir, i);
43
-            err = xhci_add_interrupter(xhci, ir, i);
36
-            break;
44
-            break;
37
+        if ((intr_num > 0 && i == intr_num) || intr_num <= 0) {
45
+    if (!intr_num) {
38
+            if (xhci->interrupters[i] == NULL) {
46
+        /* Find available secondary interrupter, interrupter 0 is reserved for primary */
47
+        for (i = 1; i < xhci->max_interrupters; i++) {
48
+            if (!xhci->interrupters[i]) {
39
+                err = xhci_add_interrupter(xhci, ir, i);
49
+                err = xhci_add_interrupter(xhci, ir, i);
40
+                if (err) {
41
+                    spin_unlock_irq(&xhci->lock);
42
+                    goto free_ir;
43
+                }
44
+                break;
50
+                break;
45
+            }
51
+            }
46
        }
52
        }
53
+    } else {
54
+        if (!xhci->interrupters[intr_num])
55
+            err = xhci_add_interrupter(xhci, ir, intr_num);
47
    }
56
    }
48
-
57
-
49
    spin_unlock_irq(&xhci->lock);
58
    spin_unlock_irq(&xhci->lock);
50
59
51
-    if (err) {
60
    if (err) {
52
-        xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n",
61
@@ -XXX,XX +XXX,XX @@ xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
53
-             xhci->max_interrupters);
62
             i, imod_interval);
54
-        xhci_free_interrupter(xhci, ir);
63
55
-        return NULL;
56
-    }
57
-
58
    xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
64
    xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
59
         i, xhci->max_interrupters);
65
-         i, xhci->max_interrupters);
66
+         ir->intr_num, xhci->max_interrupters);
60
67
61
    return ir;
68
    return ir;
62
+
63
+free_ir:
64
+    xhci_free_interrupter(xhci, ir);
65
+
66
+    return NULL;
67
}
69
}
68
EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
69
70
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
70
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
71
index XXXXXXX..XXXXXXX 100644
71
index XXXXXXX..XXXXXXX 100644
72
--- a/drivers/usb/host/xhci-sideband.c
72
--- a/drivers/usb/host/xhci-sideband.c
73
+++ b/drivers/usb/host/xhci-sideband.c
73
+++ b/drivers/usb/host/xhci-sideband.c
74
@@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
74
@@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(xhci_sideband_get_event_buffer);
75
* Returns 0 on success, negative error otherwise
76
*/
75
*/
77
int
76
int
78
-xhci_sideband_create_interrupter(struct xhci_sideband *sb)
77
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
79
+xhci_sideband_create_interrupter(struct xhci_sideband *sb, int intr_num)
78
-                 bool ip_autoclear, u32 imod_interval)
79
+                 bool ip_autoclear, u32 imod_interval, int intr_num)
80
{
80
{
81
    if (sb->ir)
81
    int ret = 0;
82
        return -EBUSY;
82
83
83
@@ -XXX,XX +XXX,XX @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
84
-    sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci));
84
    }
85
+    sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
85
86
+            intr_num);
86
    sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
87
    if (!sb->ir)
87
-                         num_seg, imod_interval);
88
        return -ENOMEM;
88
+                         num_seg, imod_interval,
89
89
+                         intr_num);
90
    if (!sb->ir) {
91
        ret = -ENOMEM;
92
        goto out;
90
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
93
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
91
index XXXXXXX..XXXXXXX 100644
94
index XXXXXXX..XXXXXXX 100644
92
--- a/drivers/usb/host/xhci.h
95
--- a/drivers/usb/host/xhci.h
93
+++ b/drivers/usb/host/xhci.h
96
+++ b/drivers/usb/host/xhci.h
94
@@ -XXX,XX +XXX,XX @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
97
@@ -XXX,XX +XXX,XX @@ void xhci_free_container_ctx(struct xhci_hcd *xhci,
95
        int type, gfp_t flags);
96
void xhci_free_container_ctx(struct xhci_hcd *xhci,
97
        struct xhci_container_ctx *ctx);
98
        struct xhci_container_ctx *ctx);
98
-struct xhci_interrupter *xhci_create_secondary_interrupter(struct usb_hcd *hcd);
99
struct xhci_interrupter *
99
+struct xhci_interrupter *xhci_create_secondary_interrupter(struct usb_hcd *hcd,
100
xhci_create_secondary_interrupter(struct usb_hcd *hcd, unsigned int segs,
100
+        int intr_num);
101
-                 u32 imod_interval);
102
+                 u32 imod_interval, unsigned int intr_num);
101
void xhci_remove_secondary_interrupter(struct usb_hcd
103
void xhci_remove_secondary_interrupter(struct usb_hcd
102
                 *hcd, struct xhci_interrupter *ir);
104
                 *hcd, struct xhci_interrupter *ir);
103
void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
105
void xhci_skip_sec_intr_events(struct xhci_hcd *xhci,
104
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
106
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
105
index XXXXXXX..XXXXXXX 100644
107
index XXXXXXX..XXXXXXX 100644
106
--- a/include/linux/usb/xhci-sideband.h
108
--- a/include/linux/usb/xhci-sideband.h
107
+++ b/include/linux/usb/xhci-sideband.h
109
+++ b/include/linux/usb/xhci-sideband.h
108
@@ -XXX,XX +XXX,XX @@ struct sg_table *
110
@@ -XXX,XX +XXX,XX @@ struct sg_table *
109
xhci_sideband_get_event_buffer(struct xhci_sideband *sb);
111
xhci_sideband_get_event_buffer(struct xhci_sideband *sb);
110
111
int
112
int
112
-xhci_sideband_create_interrupter(struct xhci_sideband *sb);
113
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
113
+xhci_sideband_create_interrupter(struct xhci_sideband *sb,
114
-                 bool ip_autoclear, u32 imod_interval);
114
+                 int intr_num);
115
+                 bool ip_autoclear, u32 imod_interval, int intr_num);
115
116
void
116
void
117
xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
117
xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
118
int
diff view generated by jsdifflib
...
...
17
                     &xhci->imod_interval);
17
                     &xhci->imod_interval);
18
+        device_property_read_u16(tmpdev, "num-hc-interrupters",
18
+        device_property_read_u16(tmpdev, "num-hc-interrupters",
19
+                     &xhci->max_interrupters);
19
+                     &xhci->max_interrupters);
20
    }
20
    }
21
21
22
    hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
22
    /*
diff view generated by jsdifflib
1
The USB backend should know about which sound resources are being shared
1
In the case of handling a USB bus reset, the xhci_discover_or_reset_device
2
between the ASoC and USB SND paths. This can be utilized to properly
2
can run without first notifying the xHCI sideband client driver to stop or
3
select and maintain the offloading devices.
3
prevent the use of the transfer ring. It was seen that when a bus reset
4
situation happened, the USB offload driver was attempting to fetch the xHCI
5
transfer ring information, which was already freed.
4
6
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
---
8
---
7
sound/soc/qcom/qdsp6/q6usb.c | 16 ++++++++++++++++
9
drivers/usb/host/xhci-sideband.c | 29 ++++++++++++++++++++++++++++-
8
1 file changed, 16 insertions(+)
10
drivers/usb/host/xhci.c | 3 +++
11
include/linux/usb/xhci-sideband.h | 30 +++++++++++++++++++++++++++++-
12
3 files changed, 60 insertions(+), 2 deletions(-)
9
13
10
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
14
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
11
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
12
--- a/sound/soc/qcom/qdsp6/q6usb.c
16
--- a/drivers/usb/host/xhci-sideband.c
13
+++ b/sound/soc/qcom/qdsp6/q6usb.c
17
+++ b/drivers/usb/host/xhci-sideband.c
18
@@ -XXX,XX +XXX,XX @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
19
20
/* sideband api functions */
21
22
+/**
23
+ * xhci_sideband_notify_ep_ring_free - notify client of xfer ring free
24
+ * @sb: sideband instance for this usb device
25
+ * @ep_index: usb endpoint index
26
+ *
27
+ * Notifies the xHCI sideband client driver of a xHCI transfer ring free
28
+ * routine. This will allow for the client to ensure that all transfers
29
+ * are completed.
30
+ *
31
+ * The callback should be synchronous, as the ring free happens after.
32
+ */
33
+void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
34
+                 unsigned int ep_index)
35
+{
36
+    struct xhci_sideband_event evt;
37
+
38
+    evt.type = XHCI_SIDEBAND_XFER_RING_FREE;
39
+    evt.evt_data = &ep_index;
40
+
41
+    if (sb->notify_client)
42
+        sb->notify_client(sb->intf, &evt);
43
+}
44
+EXPORT_SYMBOL_GPL(xhci_sideband_notify_ep_ring_free);
45
+
46
/**
47
* xhci_sideband_add_endpoint - add endpoint to sideband access list
48
* @sb: sideband instance for this usb device
49
@@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(xhci_sideband_interrupter_id);
50
* Return: pointer to a new xhci_sideband instance if successful. NULL otherwise.
51
*/
52
struct xhci_sideband *
53
-xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type)
54
+xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
55
+         int (*notify_client)(struct usb_interface *intf,
56
+                 struct xhci_sideband_event *evt))
57
{
58
    struct usb_device *udev = interface_to_usbdev(intf);
59
    struct usb_hcd *hcd = bus_to_hcd(udev->bus);
60
@@ -XXX,XX +XXX,XX @@ xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type)
61
    sb->vdev = vdev;
62
    sb->intf = intf;
63
    sb->type = type;
64
+    sb->notify_client = notify_client;
65
    vdev->sideband = sb;
66
67
    spin_unlock_irq(&xhci->lock);
68
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
69
index XXXXXXX..XXXXXXX 100644
70
--- a/drivers/usb/host/xhci.c
71
+++ b/drivers/usb/host/xhci.c
14
@@ -XXX,XX +XXX,XX @@
72
@@ -XXX,XX +XXX,XX @@
15
73
#include <linux/string_choices.h>
16
#define SID_MASK    0xF
74
#include <linux/dmi.h>
17
75
#include <linux/dma-mapping.h>
18
+struct q6usb_status {
76
+#include <linux/usb/xhci-sideband.h>
19
+    unsigned int num_pcm;
77
20
+    unsigned int chip_index;
78
#include "xhci.h"
21
+    unsigned int pcm_index;
79
#include "xhci-trace.h"
80
@@ -XXX,XX +XXX,XX @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
81
        }
82
83
        if (ep->ring) {
84
+            if (ep->sideband)
85
+                xhci_sideband_notify_ep_ring_free(ep->sideband, i);
86
            xhci_debugfs_remove_endpoint(xhci, virt_dev, i);
87
            xhci_free_endpoint_ring(xhci, virt_dev, i);
88
        }
89
diff --git a/include/linux/usb/xhci-sideband.h b/include/linux/usb/xhci-sideband.h
90
index XXXXXXX..XXXXXXX 100644
91
--- a/include/linux/usb/xhci-sideband.h
92
+++ b/include/linux/usb/xhci-sideband.h
93
@@ -XXX,XX +XXX,XX @@ enum xhci_sideband_type {
94
    XHCI_SIDEBAND_VENDOR,
95
};
96
97
+enum xhci_sideband_notify_type {
98
+    XHCI_SIDEBAND_XFER_RING_FREE,
22
+};
99
+};
23
+
100
+
24
struct q6usb_port_data {
101
+/**
25
    struct q6afe_usb_cfg usb_cfg;
102
+ * struct xhci_sideband_event - sideband event
26
    struct snd_soc_usb *usb;
103
+ * @type: notifier type
27
    struct q6usb_offload priv;
104
+ * @evt_data: event data
28
+    unsigned long available_card_slot;
105
+ */
29
+    struct q6usb_status status[SNDRV_CARDS];
106
+struct xhci_sideband_event {
30
    int active_idx;
107
+    enum xhci_sideband_notify_type type;
108
+    void *evt_data;
109
+};
110
+
111
/**
112
* struct xhci_sideband - representation of a sideband accessed usb device.
113
* @xhci: The xhci host controller the usb device is connected to
114
@@ -XXX,XX +XXX,XX @@ enum xhci_sideband_type {
115
* @type: xHCI sideband type
116
* @mutex: mutex for sideband operations
117
* @intf: USB sideband client interface
118
+ * @notify_client: callback for xHCI sideband sequences
119
*
120
* FIXME usb device accessed via sideband Keeping track of sideband accessed usb devices.
121
*/
122
@@ -XXX,XX +XXX,XX @@ struct xhci_sideband {
123
    struct mutex            mutex;
124
125
    struct usb_interface        *intf;
126
+    int (*notify_client)(struct usb_interface *intf,
127
+             struct xhci_sideband_event *evt);
31
};
128
};
32
129
33
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
130
struct xhci_sideband *
34
    if (connected) {
131
-xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type);
35
        /* We only track the latest USB headset plugged in */
132
+xhci_sideband_register(struct usb_interface *intf, enum xhci_sideband_type type,
36
        data->active_idx = sdev->card_idx;
133
+         int (*notify_client)(struct usb_interface *intf,
134
+                 struct xhci_sideband_event *evt));
135
void
136
xhci_sideband_unregister(struct xhci_sideband *sb);
137
int
138
@@ -XXX,XX +XXX,XX @@ void
139
xhci_sideband_remove_interrupter(struct xhci_sideband *sb);
140
int
141
xhci_sideband_interrupter_id(struct xhci_sideband *sb);
37
+
142
+
38
+        set_bit(sdev->card_idx, &data->available_card_slot);
143
+#if IS_ENABLED(CONFIG_USB_XHCI_SIDEBAND)
39
+        data->status[sdev->card_idx].num_pcm = sdev->num_playback;
144
+void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
40
+        data->status[sdev->card_idx].chip_index = sdev->chip_idx;
145
+                 unsigned int ep_index);
41
+    } else {
146
+#else
42
+        clear_bit(sdev->card_idx, &data->available_card_slot);
147
+static inline void xhci_sideband_notify_ep_ring_free(struct xhci_sideband *sb,
43
+        data->status[sdev->card_idx].num_pcm = 0;
148
+                         unsigned int ep_index)
44
+        data->status[sdev->card_idx].chip_index = 0;
149
+{ }
45
    }
150
+#endif /* IS_ENABLED(CONFIG_USB_XHCI_SIDEBAND) */
46
151
#endif /* __LINUX_XHCI_SIDEBAND_H */
47
    return 0;
diff view generated by jsdifflib
...
...
6
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
6
Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
---
8
---
9
drivers/usb/dwc3/core.c | 12 ++++++++++++
9
drivers/usb/dwc3/core.c | 12 ++++++++++++
10
drivers/usb/dwc3/core.h | 2 ++
10
drivers/usb/dwc3/core.h | 2 ++
11
drivers/usb/dwc3/host.c | 5 ++++-
11
drivers/usb/dwc3/host.c | 3 +++
12
3 files changed, 18 insertions(+), 1 deletion(-)
12
3 files changed, 17 insertions(+)
13
13
14
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
14
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
15
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
16
--- a/drivers/usb/dwc3/core.c
16
--- a/drivers/usb/dwc3/core.c
17
+++ b/drivers/usb/dwc3/core.c
17
+++ b/drivers/usb/dwc3/core.c
18
@@ -XXX,XX +XXX,XX @@ static void dwc3_get_properties(struct dwc3 *dwc)
18
@@ -XXX,XX +XXX,XX @@ static void dwc3_get_properties(struct dwc3 *dwc)
19
    u8            tx_thr_num_pkt_prd = 0;
19
    u8            tx_thr_num_pkt_prd = 0;
20
    u8            tx_max_burst_prd = 0;
20
    u8            tx_max_burst_prd = 0;
21
    u8            tx_fifo_resize_max_num;
21
    u8            tx_fifo_resize_max_num;
22
+    u16            num_hc_interrupters;
22
+    u16            num_hc_interrupters;
23
    const char        *usb_psy_name;
23
24
    int            ret;
24
    /* default to highest possible threshold */
25
25
    lpm_nyet_threshold = 0xf;
26
@@ -XXX,XX +XXX,XX @@ static void dwc3_get_properties(struct dwc3 *dwc)
26
@@ -XXX,XX +XXX,XX @@ static void dwc3_get_properties(struct dwc3 *dwc)
27
     */
27
     */
28
    tx_fifo_resize_max_num = 6;
28
    tx_fifo_resize_max_num = 6;
29
29
30
+    /* default to a single XHCI interrupter */
30
+    /* default to a single XHCI interrupter */
...
...
36
@@ -XXX,XX +XXX,XX @@ static void dwc3_get_properties(struct dwc3 *dwc)
36
@@ -XXX,XX +XXX,XX @@ static void dwc3_get_properties(struct dwc3 *dwc)
37
                &tx_thr_num_pkt_prd);
37
                &tx_thr_num_pkt_prd);
38
    device_property_read_u8(dev, "snps,tx-max-burst-prd",
38
    device_property_read_u8(dev, "snps,tx-max-burst-prd",
39
                &tx_max_burst_prd);
39
                &tx_max_burst_prd);
40
+    device_property_read_u16(dev, "num-hc-interrupters",
40
+    device_property_read_u16(dev, "num-hc-interrupters",
41
+                &num_hc_interrupters);
41
+                 &num_hc_interrupters);
42
+    /* DWC3 core allowed to have a max of 8 interrupters */
42
+    /* DWC3 core allowed to have a max of 8 interrupters */
43
+    if (num_hc_interrupters > 8)
43
+    if (num_hc_interrupters > 8)
44
+        num_hc_interrupters = 8;
44
+        num_hc_interrupters = 8;
45
+
45
+
46
    dwc->do_fifo_resize = device_property_read_bool(dev,
46
    dwc->do_fifo_resize = device_property_read_bool(dev,
...
...
77
77
78
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
78
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
79
index XXXXXXX..XXXXXXX 100644
79
index XXXXXXX..XXXXXXX 100644
80
--- a/drivers/usb/dwc3/host.c
80
--- a/drivers/usb/dwc3/host.c
81
+++ b/drivers/usb/dwc3/host.c
81
+++ b/drivers/usb/dwc3/host.c
82
@@ -XXX,XX +XXX,XX @@ static int dwc3_host_get_irq(struct dwc3 *dwc)
83
84
int dwc3_host_init(struct dwc3 *dwc)
85
{
86
-    struct property_entry    props[4];
87
+    struct property_entry    props[5];
88
    struct platform_device    *xhci;
89
    int            ret, irq;
90
    int            prop_idx = 0;
91
@@ -XXX,XX +XXX,XX @@ int dwc3_host_init(struct dwc3 *dwc)
82
@@ -XXX,XX +XXX,XX @@ int dwc3_host_init(struct dwc3 *dwc)
92
    if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
83
    if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
93
        props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
84
        props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
94
85
95
+    props[prop_idx++] = PROPERTY_ENTRY_U16("num-hc-interrupters",
86
+    props[prop_idx++] = PROPERTY_ENTRY_U16("num-hc-interrupters",
96
+                        dwc->num_hc_interrupters);
87
+                     dwc->num_hc_interrupters);
97
+
88
+
98
    if (prop_idx) {
89
    if (prop_idx) {
99
        ret = device_create_managed_software_node(&xhci->dev, props, NULL);
90
        ret = device_create_managed_software_node(&xhci->dev, props, NULL);
100
        if (ret) {
91
        if (ret) {
diff view generated by jsdifflib
1
Check for if the PCM format is supported during the hw_params callback. If
1
Add an USB jack type, in order to support notifying of a valid USB audio
2
the profile is not supported then the userspace ALSA entity will receive an
2
device. Since USB audio devices can have a slew of different
3
error, and can take further action.
3
configurations that reach beyond the basic headset and headphone use cases,
4
classify these devices differently.
4
5
6
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
---
8
---
7
sound/soc/qcom/qdsp6/q6usb.c | 5 ++++-
9
include/linux/mod_devicetable.h | 2 +-
8
1 file changed, 4 insertions(+), 1 deletion(-)
10
include/sound/jack.h | 4 +++-
11
include/uapi/linux/input-event-codes.h | 3 ++-
12
sound/core/jack.c | 6 ++++--
13
4 files changed, 10 insertions(+), 5 deletions(-)
9
14
10
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
15
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
11
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
12
--- a/sound/soc/qcom/qdsp6/q6usb.c
17
--- a/include/linux/mod_devicetable.h
13
+++ b/sound/soc/qcom/qdsp6/q6usb.c
18
+++ b/include/linux/mod_devicetable.h
14
@@ -XXX,XX +XXX,XX @@ static int q6usb_hw_params(struct snd_pcm_substream *substream,
19
@@ -XXX,XX +XXX,XX @@ struct pcmcia_device_id {
15
             struct snd_pcm_hw_params *params,
20
#define INPUT_DEVICE_ID_LED_MAX        0x0f
16
             struct snd_soc_dai *dai)
21
#define INPUT_DEVICE_ID_SND_MAX        0x07
17
{
22
#define INPUT_DEVICE_ID_FF_MAX        0x7f
18
-    return 0;
23
-#define INPUT_DEVICE_ID_SW_MAX        0x10
19
+    struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
24
+#define INPUT_DEVICE_ID_SW_MAX        0x11
20
+    int direction = substream->stream;
25
#define INPUT_DEVICE_ID_PROP_MAX    0x1f
21
+
26
22
+    return snd_soc_usb_find_format(data->active_idx, params, direction);
27
#define INPUT_DEVICE_ID_MATCH_BUS    1
23
}
28
diff --git a/include/sound/jack.h b/include/sound/jack.h
24
29
index XXXXXXX..XXXXXXX 100644
25
static const struct snd_soc_dai_ops q6usb_ops = {
30
--- a/include/sound/jack.h
31
+++ b/include/sound/jack.h
32
@@ -XXX,XX +XXX,XX @@ struct input_dev;
33
* @SND_JACK_VIDEOOUT: Video out
34
* @SND_JACK_AVOUT: AV (Audio Video) out
35
* @SND_JACK_LINEIN: Line in
36
+ * @SND_JACK_USB: USB audio device
37
* @SND_JACK_BTN_0: Button 0
38
* @SND_JACK_BTN_1: Button 1
39
* @SND_JACK_BTN_2: Button 2
40
@@ -XXX,XX +XXX,XX @@ enum snd_jack_types {
41
    SND_JACK_VIDEOOUT    = 0x0010,
42
    SND_JACK_AVOUT        = SND_JACK_LINEOUT | SND_JACK_VIDEOOUT,
43
    SND_JACK_LINEIN        = 0x0020,
44
+    SND_JACK_USB        = 0x0040,
45
46
    /* Kept separate from switches to facilitate implementation */
47
    SND_JACK_BTN_0        = 0x4000,
48
@@ -XXX,XX +XXX,XX @@ enum snd_jack_types {
49
};
50
51
/* Keep in sync with definitions above */
52
-#define SND_JACK_SWITCH_TYPES 6
53
+#define SND_JACK_SWITCH_TYPES 7
54
55
struct snd_jack {
56
    struct list_head kctl_list;
57
diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
58
index XXXXXXX..XXXXXXX 100644
59
--- a/include/uapi/linux/input-event-codes.h
60
+++ b/include/uapi/linux/input-event-codes.h
61
@@ -XXX,XX +XXX,XX @@
62
#define SW_MUTE_DEVICE        0x0e /* set = device disabled */
63
#define SW_PEN_INSERTED        0x0f /* set = pen inserted */
64
#define SW_MACHINE_COVER    0x10 /* set = cover closed */
65
-#define SW_MAX            0x10
66
+#define SW_USB_INSERT        0x11 /* set = USB audio device connected */
67
+#define SW_MAX            0x11
68
#define SW_CNT            (SW_MAX+1)
69
70
/*
71
diff --git a/sound/core/jack.c b/sound/core/jack.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/sound/core/jack.c
74
+++ b/sound/core/jack.c
75
@@ -XXX,XX +XXX,XX @@ static const int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
76
    SW_JACK_PHYSICAL_INSERT,
77
    SW_VIDEOOUT_INSERT,
78
    SW_LINEIN_INSERT,
79
+    SW_USB_INSERT,
80
};
81
#endif /* CONFIG_SND_JACK_INPUT_DEV */
82
83
@@ -XXX,XX +XXX,XX @@ static ssize_t jack_kctl_id_read(struct file *file,
84
static const char * const jack_events_name[] = {
85
    "HEADPHONE(0x0001)", "MICROPHONE(0x0002)", "LINEOUT(0x0004)",
86
    "MECHANICAL(0x0008)", "VIDEOOUT(0x0010)", "LINEIN(0x0020)",
87
-    "", "", "", "BTN_5(0x0200)", "BTN_4(0x0400)", "BTN_3(0x0800)",
88
-    "BTN_2(0x1000)", "BTN_1(0x2000)", "BTN_0(0x4000)", "",
89
+    "USB(0x0040)", "", "", "BTN_5(0x0200)", "BTN_4(0x0400)",
90
+    "BTN_3(0x0800)", "BTN_2(0x1000)", "BTN_1(0x2000)", "BTN_0(0x4000)",
91
+    "",
92
};
93
94
/* the recommended buffer size is 256 */
diff view generated by jsdifflib
1
Some vendor modules will utilize useful parsing and endpoint management
1
Some vendor modules will utilize useful parsing and endpoint management
2
APIs to start audio playback/capture.
2
APIs to start audio playback/capture.
3
3
4
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
4
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
5
---
6
---
6
sound/usb/card.c | 4 +++
7
sound/usb/card.c | 4 +++
7
sound/usb/endpoint.c | 1 +
8
sound/usb/endpoint.c | 1 +
8
sound/usb/helper.c | 1 +
9
sound/usb/helper.c | 1 +
...
...
78
    return found;
79
    return found;
79
}
80
}
80
81
81
+const struct audioformat *
82
+const struct audioformat *
82
+snd_usb_find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
83
+snd_usb_find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
83
+     unsigned int rate, unsigned int channels, bool strict_match,
84
+         unsigned int rate, unsigned int channels, bool strict_match,
84
+     struct snd_usb_substream *subs)
85
+         struct snd_usb_substream *subs)
85
+{
86
+{
86
+    return find_format(fmt_list_head, format, rate, channels, strict_match,
87
+    return find_format(fmt_list_head, format, rate, channels, strict_match,
87
+            subs);
88
+            subs);
88
+}
89
+}
89
+EXPORT_SYMBOL_GPL(snd_usb_find_format);
90
+EXPORT_SYMBOL_GPL(snd_usb_find_format);
...
...
95
             true, subs);
96
             true, subs);
96
}
97
}
97
98
98
+const struct audioformat *
99
+const struct audioformat *
99
+snd_usb_find_substream_format(struct snd_usb_substream *subs,
100
+snd_usb_find_substream_format(struct snd_usb_substream *subs,
100
+         const struct snd_pcm_hw_params *params)
101
+             const struct snd_pcm_hw_params *params)
101
+{
102
+{
102
+    return find_substream_format(subs, params);
103
+    return find_substream_format(subs, params);
103
+}
104
+}
104
+EXPORT_SYMBOL_GPL(snd_usb_find_substream_format);
105
+EXPORT_SYMBOL_GPL(snd_usb_find_substream_format);
105
+
106
+
...
...
121
- * that.
122
- * that.
122
- */
123
- */
123
-static int snd_usb_hw_params(struct snd_pcm_substream *substream,
124
-static int snd_usb_hw_params(struct snd_pcm_substream *substream,
124
-             struct snd_pcm_hw_params *hw_params)
125
-             struct snd_pcm_hw_params *hw_params)
125
+int snd_usb_hw_params(struct snd_usb_substream *subs,
126
+int snd_usb_hw_params(struct snd_usb_substream *subs,
126
+                struct snd_pcm_hw_params *hw_params)
127
+         struct snd_pcm_hw_params *hw_params)
127
{
128
{
128
-    struct snd_usb_substream *subs = substream->runtime->private_data;
129
-    struct snd_usb_substream *subs = substream->runtime->private_data;
129
    struct snd_usb_audio *chip = subs->stream->chip;
130
    struct snd_usb_audio *chip = subs->stream->chip;
130
    const struct audioformat *fmt;
131
    const struct audioformat *fmt;
131
    const struct audioformat *sync_fmt;
132
    const struct audioformat *sync_fmt;
...
...
156
+ * if sg buffer is supported on the later version of alsa, we'll follow
157
+ * if sg buffer is supported on the later version of alsa, we'll follow
157
+ * that.
158
+ * that.
158
*/
159
*/
159
-static int snd_usb_hw_free(struct snd_pcm_substream *substream)
160
-static int snd_usb_hw_free(struct snd_pcm_substream *substream)
160
+static int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream,
161
+static int snd_usb_pcm_hw_params(struct snd_pcm_substream *substream,
161
+             struct snd_pcm_hw_params *hw_params)
162
+                 struct snd_pcm_hw_params *hw_params)
162
{
163
{
163
    struct snd_usb_substream *subs = substream->runtime->private_data;
164
    struct snd_usb_substream *subs = substream->runtime->private_data;
164
+
165
+
165
+    return snd_usb_hw_params(subs, hw_params);
166
+    return snd_usb_hw_params(subs, hw_params);
166
+}
167
+}
...
...
220
int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip,
221
int snd_usb_audioformat_set_sync_ep(struct snd_usb_audio *chip,
221
                 struct audioformat *fmt);
222
                 struct audioformat *fmt);
222
223
223
+const struct audioformat *
224
+const struct audioformat *
224
+snd_usb_find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
225
+snd_usb_find_format(struct list_head *fmt_list_head, snd_pcm_format_t format,
225
+     unsigned int rate, unsigned int channels, bool strict_match,
226
+         unsigned int rate, unsigned int channels, bool strict_match,
226
+     struct snd_usb_substream *subs);
227
+         struct snd_usb_substream *subs);
227
+const struct audioformat *
228
+const struct audioformat *
228
+snd_usb_find_substream_format(struct snd_usb_substream *subs,
229
+snd_usb_find_substream_format(struct snd_usb_substream *subs,
229
+         const struct snd_pcm_hw_params *params);
230
+             const struct snd_pcm_hw_params *params);
230
+
231
+
231
+int snd_usb_hw_params(struct snd_usb_substream *subs,
232
+int snd_usb_hw_params(struct snd_usb_substream *subs,
232
+                struct snd_pcm_hw_params *hw_params);
233
+         struct snd_pcm_hw_params *hw_params);
233
+int snd_usb_hw_free(struct snd_usb_substream *subs);
234
+int snd_usb_hw_free(struct snd_usb_substream *subs);
234
#endif /* __USBAUDIO_PCM_H */
235
#endif /* __USBAUDIO_PCM_H */
diff view generated by jsdifflib
1
Allow for checks on a specific USB audio device to see if a requested PCM
1
Allow for checks on a specific USB audio device to see if a requested PCM
2
format is supported. This is needed for support for when playback is
2
format is supported. This is needed for support when playback is
3
initiated by the ASoC USB backend path.
3
initiated by the ASoC USB backend path.
4
4
5
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
---
7
---
7
sound/usb/card.c | 40 ++++++++++++++++++++++++++++++++++++++++
8
sound/usb/card.c | 32 ++++++++++++++++++++++++++++++++
8
sound/usb/card.h | 11 +++++++++++
9
sound/usb/card.h | 3 +++
9
2 files changed, 51 insertions(+)
10
2 files changed, 35 insertions(+)
10
11
11
diff --git a/sound/usb/card.c b/sound/usb/card.c
12
diff --git a/sound/usb/card.c b/sound/usb/card.c
12
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
13
--- a/sound/usb/card.c
14
--- a/sound/usb/card.c
14
+++ b/sound/usb/card.c
15
+++ b/sound/usb/card.c
15
@@ -XXX,XX +XXX,XX @@ int snd_usb_unregister_platform_ops(void)
16
@@ -XXX,XX +XXX,XX @@ static DEFINE_MUTEX(register_mutex);
16
}
17
static struct snd_usb_audio *usb_chip[SNDRV_CARDS];
17
EXPORT_SYMBOL_GPL(snd_usb_unregister_platform_ops);
18
static struct usb_driver usb_audio_driver;
18
19
19
+/*
20
+/*
20
+ * Checks to see if requested audio profile, i.e sample rate, # of
21
+ * Checks to see if requested audio profile, i.e sample rate, # of
21
+ * channels, etc... is supported by the substream associated to the
22
+ * channels, etc... is supported by the substream associated to the
22
+ * USB audio device.
23
+ * USB audio device.
23
+ */
24
+ */
24
+struct snd_usb_stream *snd_usb_find_suppported_substream(int card_idx,
25
+struct snd_usb_stream *
25
+            struct snd_pcm_hw_params *params, int direction)
26
+snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params,
27
+                 int direction)
26
+{
28
+{
27
+    struct snd_usb_audio *chip;
29
+    struct snd_usb_audio *chip;
28
+    struct snd_usb_substream *subs = NULL;
30
+    struct snd_usb_substream *subs;
29
+    struct snd_usb_stream *as;
31
+    struct snd_usb_stream *as;
30
+    const struct audioformat *fmt;
31
+
32
+
32
+    /*
33
+    /*
33
+     * Register mutex is held when populating and clearing usb_chip
34
+     * Register mutex is held when populating and clearing usb_chip
34
+     * array.
35
+     * array.
35
+     */
36
+     */
36
+    mutex_lock(&register_mutex);
37
+    guard(mutex)(&register_mutex);
37
+    chip = usb_chip[card_idx];
38
+    chip = usb_chip[card_idx];
38
+    if (!chip) {
39
+        mutex_unlock(&register_mutex);
40
+        return NULL;
41
+    }
42
+
39
+
43
+    if (enable[card_idx]) {
40
+    if (chip && enable[card_idx]) {
44
+        list_for_each_entry(as, &chip->pcm_list, list) {
41
+        list_for_each_entry(as, &chip->pcm_list, list) {
45
+            subs = &as->substream[direction];
42
+            subs = &as->substream[direction];
46
+            fmt = snd_usb_find_substream_format(subs, params);
43
+            if (snd_usb_find_substream_format(subs, params))
47
+            if (fmt) {
48
+                mutex_unlock(&register_mutex);
49
+                return as;
44
+                return as;
50
+            }
51
+        }
45
+        }
52
+    }
46
+    }
53
+    mutex_unlock(&register_mutex);
54
+
47
+
55
+    return NULL;
48
+    return NULL;
56
+}
49
+}
57
+EXPORT_SYMBOL_GPL(snd_usb_find_suppported_substream);
50
+EXPORT_SYMBOL_GPL(snd_usb_find_suppported_substream);
58
+
51
+
...
...
61
* called from usb_audio_disconnect()
54
* called from usb_audio_disconnect()
62
diff --git a/sound/usb/card.h b/sound/usb/card.h
55
diff --git a/sound/usb/card.h b/sound/usb/card.h
63
index XXXXXXX..XXXXXXX 100644
56
index XXXXXXX..XXXXXXX 100644
64
--- a/sound/usb/card.h
57
--- a/sound/usb/card.h
65
+++ b/sound/usb/card.h
58
+++ b/sound/usb/card.h
66
@@ -XXX,XX +XXX,XX @@ struct snd_usb_platform_ops {
59
@@ -XXX,XX +XXX,XX @@ struct snd_usb_stream {
67
60
    struct list_head list;
68
int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops);
61
};
69
int snd_usb_unregister_platform_ops(void);
62
70
+
63
+struct snd_usb_stream *
71
+#if IS_ENABLED(CONFIG_SND_USB_AUDIO)
64
+snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params,
72
+struct snd_usb_stream *snd_usb_find_suppported_substream(int card_idx,
65
+                 int direction);
73
+            struct snd_pcm_hw_params *params, int direction);
74
+#else
75
+static struct snd_usb_stream *snd_usb_find_suppported_substream(int card_idx,
76
+            struct snd_pcm_hw_params *params, int direction)
77
+{
78
+    return NULL;
79
+}
80
+#endif /* IS_ENABLED(CONFIG_SND_USB_AUDIO) */
81
#endif /* __USBAUDIO_CARD_H */
66
#endif /* __USBAUDIO_CARD_H */
diff view generated by jsdifflib
1
The Q6USB backend can carry information about the available USB SND cards
1
Within the UAC descriptor, there is information describing the size of a
2
and PCM devices discovered on the USB bus. The dev_token field is used by
2
sample (bSubframeSize/bSubslotSize) and the number of relevant bits
3
the audio DSP to notify the USB offload driver of which card and PCM index
3
(bBitResolution). Currently, fmt_bits carries only the bit resolution,
4
to enable playback on. Separate this into a dedicated API, so the USB
4
however, some offloading entities may also require the overall size of the
5
backend can set the dev_token accordingly. The audio DSP does not utilize
5
sample. Save this information in a separate parameter, as depending on the
6
this information until the AFE port start command is sent, which is done
6
UAC format type, the sample size can not easily be decoded from other
7
during the PCM prepare phase.
7
existing parameters.
8
8
9
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
9
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
10
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
10
---
11
---
11
sound/soc/qcom/qdsp6/q6afe.c | 49 +++++++++++++++++++++++++-----------
12
sound/usb/card.h | 1 +
12
sound/soc/qcom/qdsp6/q6afe.h | 1 +
13
sound/usb/format.c | 1 +
13
2 files changed, 36 insertions(+), 14 deletions(-)
14
2 files changed, 2 insertions(+)
14
15
15
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
16
diff --git a/sound/usb/card.h b/sound/usb/card.h
16
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
17
--- a/sound/soc/qcom/qdsp6/q6afe.c
18
--- a/sound/usb/card.h
18
+++ b/sound/soc/qcom/qdsp6/q6afe.c
19
+++ b/sound/usb/card.h
19
@@ -XXX,XX +XXX,XX @@ void q6afe_tdm_port_prepare(struct q6afe_port *port,
20
@@ -XXX,XX +XXX,XX @@ struct audioformat {
20
}
21
    unsigned int channels;        /* # channels */
21
EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare);
22
    unsigned int fmt_type;        /* USB audio format type (1-3) */
22
23
    unsigned int fmt_bits;        /* number of significant bits */
23
-static int afe_port_send_usb_dev_param(struct q6afe_port *port, struct q6afe_usb_cfg *cfg)
24
+    unsigned int fmt_sz;        /* overall audio sub frame/slot size */
24
+/**
25
    unsigned int frame_size;    /* samples per frame for non-audio */
25
+ * afe_port_send_usb_dev_param() - Send USB dev token
26
    unsigned char iface;        /* interface number */
26
+ *
27
    unsigned char altsetting;    /* corresponding alternate setting */
27
+ * @port: Instance of afe port
28
diff --git a/sound/usb/format.c b/sound/usb/format.c
28
+ * @cardidx: USB SND card index to reference
29
index XXXXXXX..XXXXXXX 100644
29
+ * @pcmidx: USB SND PCM device index to reference
30
--- a/sound/usb/format.c
30
+ *
31
+++ b/sound/usb/format.c
31
+ * The USB dev token carries information about which USB SND card instance and
32
@@ -XXX,XX +XXX,XX @@ static u64 parse_audio_format_i_type(struct snd_usb_audio *chip,
32
+ * PCM device to execute the offload on. This information is carried through
33
+ * to the stream enable QMI request, which is handled by the offload class
34
+ * driver. The information is parsed to determine which USB device to query
35
+ * the required resources for.
36
+ */
37
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx)
38
{
39
-    union afe_port_config *pcfg = &port->port_cfg;
40
    struct afe_param_id_usb_audio_dev_params usb_dev;
41
+    int ret;
42
+
43
+    memset(&usb_dev, 0, sizeof(usb_dev));
44
+
45
+    usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
46
+    usb_dev.dev_token = (cardidx << 16) | (pcmidx << 8);
47
+    ret = q6afe_port_set_param_v2(port, &usb_dev,
48
+                AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS,
49
+                AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(usb_dev));
50
+    if (ret)
51
+        dev_err(port->afe->dev, "%s: AFE device param cmd failed %d\n",
52
+            __func__, ret);
53
+
54
+    return ret;
55
+}
56
+EXPORT_SYMBOL_GPL(afe_port_send_usb_dev_param);
57
+
58
+static int afe_port_send_usb_params(struct q6afe_port *port, struct q6afe_usb_cfg *cfg)
59
+{
60
+    union afe_port_config *pcfg = &port->port_cfg;
61
    struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt;
62
    struct afe_param_id_usb_audio_svc_interval svc_int;
63
    int ret = 0;
64
@@ -XXX,XX +XXX,XX @@ static int afe_port_send_usb_dev_param(struct q6afe_port *port, struct q6afe_usb
65
        goto exit;
66
    }
33
    }
67
34
68
-    memset(&usb_dev, 0, sizeof(usb_dev));
35
    fp->fmt_bits = sample_width;
69
    memset(&lpcm_fmt, 0, sizeof(lpcm_fmt));
36
+    fp->fmt_sz = sample_bytes;
70
    memset(&svc_int, 0, sizeof(svc_int));
37
71
38
    if ((pcm_formats == 0) &&
72
-    usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
39
     (format == 0 || format == BIT(UAC_FORMAT_TYPE_I_UNDEFINED))) {
73
-    ret = q6afe_port_set_param_v2(port, &usb_dev,
74
-                 AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS,
75
-                 AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(usb_dev));
76
-    if (ret) {
77
-        dev_err(port->afe->dev, "%s: AFE device param cmd failed %d\n",
78
-            __func__, ret);
79
-        goto exit;
80
-    }
81
-
82
    lpcm_fmt.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
83
    lpcm_fmt.endian = pcfg->usb_cfg.endian;
84
    ret = q6afe_port_set_param_v2(port, &lpcm_fmt,
85
@@ -XXX,XX +XXX,XX @@ void q6afe_usb_port_prepare(struct q6afe_port *port,
86
    pcfg->usb_cfg.num_channels = cfg->num_channels;
87
    pcfg->usb_cfg.bit_width = cfg->bit_width;
88
89
-    afe_port_send_usb_dev_param(port, cfg);
90
+    afe_port_send_usb_params(port, cfg);
91
}
92
EXPORT_SYMBOL_GPL(q6afe_usb_port_prepare);
93
94
diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h
95
index XXXXXXX..XXXXXXX 100644
96
--- a/sound/soc/qcom/qdsp6/q6afe.h
97
+++ b/sound/soc/qcom/qdsp6/q6afe.h
98
@@ -XXX,XX +XXX,XX @@ void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg);
99
void q6afe_cdc_dma_port_prepare(struct q6afe_port *port,
100
                struct q6afe_cdc_dma_cfg *cfg);
101
102
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx);
103
int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id,
104
             int clk_src, int clk_root,
105
             unsigned int freq, int dir);
diff view generated by jsdifflib
...
...
6
6
7
If a PCM device is already in use, the check will return an error to
7
If a PCM device is already in use, the check will return an error to
8
userspace notifying that the stream is currently busy. This ensures that
8
userspace notifying that the stream is currently busy. This ensures that
9
only one path is using the USB substream.
9
only one path is using the USB substream.
10
10
11
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
11
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
12
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
12
---
13
---
13
sound/usb/card.h | 1 +
14
sound/usb/card.h | 1 +
14
sound/usb/pcm.c | 19 +++++++++++++++++--
15
sound/usb/pcm.c | 29 ++++++++++++++++++++++++++---
15
sound/usb/qcom/qc_audio_offload.c | 15 ++++++++++++++-
16
2 files changed, 27 insertions(+), 3 deletions(-)
16
3 files changed, 32 insertions(+), 3 deletions(-)
17
17
18
diff --git a/sound/usb/card.h b/sound/usb/card.h
18
diff --git a/sound/usb/card.h b/sound/usb/card.h
19
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
20
--- a/sound/usb/card.h
20
--- a/sound/usb/card.h
21
+++ b/sound/usb/card.h
21
+++ b/sound/usb/card.h
...
...
41
+    mutex_lock(&chip->mutex);
41
+    mutex_lock(&chip->mutex);
42
+    if (subs->opened) {
42
+    if (subs->opened) {
43
+        mutex_unlock(&chip->mutex);
43
+        mutex_unlock(&chip->mutex);
44
+        return -EBUSY;
44
+        return -EBUSY;
45
+    }
45
+    }
46
+    subs->opened = 1;
47
+    mutex_unlock(&chip->mutex);
46
+
48
+
47
    runtime->hw = snd_usb_hardware;
49
    runtime->hw = snd_usb_hardware;
48
    /* need an explicit sync to catch applptr update in low-latency mode */
50
    /* need an explicit sync to catch applptr update in low-latency mode */
49
    if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
51
    if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
50
@@ -XXX,XX +XXX,XX @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
52
@@ -XXX,XX +XXX,XX @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream)
51
53
52
    ret = setup_hw_info(runtime, subs);
54
    ret = setup_hw_info(runtime, subs);
53
    if (ret < 0)
55
    if (ret < 0)
54
-        return ret;
56
-        return ret;
55
+        goto out;
57
+        goto err_open;
56
    ret = snd_usb_autoresume(subs->stream->chip);
58
    ret = snd_usb_autoresume(subs->stream->chip);
57
    if (ret < 0)
59
    if (ret < 0)
58
-        return ret;
60
-        return ret;
59
+        goto out;
61
+        goto err_open;
60
    ret = snd_media_stream_init(subs, as->pcm, direction);
62
    ret = snd_media_stream_init(subs, as->pcm, direction);
61
    if (ret < 0)
63
    if (ret < 0)
62
        snd_usb_autosuspend(subs->stream->chip);
64
-        snd_usb_autosuspend(subs->stream->chip);
63
+    subs->opened = 1;
65
+        goto err_resume;
64
+out:
66
+
67
+    return 0;
68
+
69
+err_resume:
70
+    snd_usb_autosuspend(subs->stream->chip);
71
+err_open:
72
+    mutex_lock(&chip->mutex);
73
+    subs->opened = 0;
65
+    mutex_unlock(&chip->mutex);
74
+    mutex_unlock(&chip->mutex);
66
+
75
+
67
    return ret;
76
    return ret;
68
}
77
}
69
78
...
...
83
+    subs->opened = 0;
92
+    subs->opened = 0;
84
+    mutex_unlock(&chip->mutex);
93
+    mutex_unlock(&chip->mutex);
85
94
86
    return 0;
95
    return 0;
87
}
96
}
88
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
89
index XXXXXXX..XXXXXXX 100644
90
--- a/sound/usb/qcom/qc_audio_offload.c
91
+++ b/sound/usb/qcom/qc_audio_offload.c
92
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
93
        goto response;
94
    }
95
96
+    mutex_lock(&chip->mutex);
97
    if (req_msg->enable) {
98
-        if (info_idx < 0 || chip->system_suspend) {
99
+        if (info_idx < 0 || chip->system_suspend || subs->opened) {
100
            ret = -EBUSY;
101
+            mutex_unlock(&chip->mutex);
102
+
103
            goto response;
104
        }
105
+        subs->opened = 1;
106
    }
107
+    mutex_unlock(&chip->mutex);
108
109
    if (req_msg->service_interval_valid) {
110
        ret = get_data_interval_from_si(subs,
111
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
112
        if (!ret)
113
            ret = prepare_qmi_response(subs, req_msg, &resp,
114
                    info_idx);
115
+        if (ret < 0) {
116
+            mutex_lock(&chip->mutex);
117
+            subs->opened = 0;
118
+            mutex_unlock(&chip->mutex);
119
+        }
120
    } else {
121
        info = &uadev[pcm_card_num].info[info_idx];
122
        if (info->data_ep_pipe) {
123
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
124
        }
125
126
        disable_audio_stream(subs);
127
+        mutex_lock(&chip->mutex);
128
+        subs->opened = 0;
129
+        mutex_unlock(&chip->mutex);
130
    }
131
132
response:
diff view generated by jsdifflib
1
Allow for different platforms to be notified on USB SND connect/disconnect
1
Allow for different platforms to be notified on USB SND connect/disconnect
2
seqeunces. This allows for platform USB SND modules to properly initialize
2
sequences. This allows for platform USB SND modules to properly initialize
3
and populate internal structures with references to the USB SND chip
3
and populate internal structures with references to the USB SND chip
4
device.
4
device.
5
5
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
7
---
8
sound/usb/card.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
8
sound/usb/card.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++
9
sound/usb/card.h | 9 ++++++++
9
sound/usb/card.h | 10 ++++++++++
10
2 files changed, 69 insertions(+)
10
2 files changed, 59 insertions(+)
11
11
12
diff --git a/sound/usb/card.c b/sound/usb/card.c
12
diff --git a/sound/usb/card.c b/sound/usb/card.c
13
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
14
--- a/sound/usb/card.c
14
--- a/sound/usb/card.c
15
+++ b/sound/usb/card.c
15
+++ b/sound/usb/card.c
...
...
28
+ * Only one set of platform operations can be registered to USB SND. The
28
+ * Only one set of platform operations can be registered to USB SND. The
29
+ * platform register operation is protected by the register_mutex.
29
+ * platform register operation is protected by the register_mutex.
30
+ */
30
+ */
31
+int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops)
31
+int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops)
32
+{
32
+{
33
+    int ret;
33
+    guard(mutex)(&register_mutex);
34
+
34
+    if (platform_ops)
35
+    mutex_lock(&register_mutex);
35
+        return -EEXIST;
36
+    if (platform_ops) {
37
+        ret = -EEXIST;
38
+        goto out;
39
+    }
40
+
36
+
41
+    platform_ops = ops;
37
+    platform_ops = ops;
42
+out:
43
+    mutex_unlock(&register_mutex);
44
+    return 0;
38
+    return 0;
45
+}
39
+}
46
+EXPORT_SYMBOL_GPL(snd_usb_register_platform_ops);
40
+EXPORT_SYMBOL_GPL(snd_usb_register_platform_ops);
47
+
41
+
48
+/*
42
+/*
...
...
51
+ *
45
+ *
52
+ * The platform unregister operation is protected by the register_mutex.
46
+ * The platform unregister operation is protected by the register_mutex.
53
+ */
47
+ */
54
+int snd_usb_unregister_platform_ops(void)
48
+int snd_usb_unregister_platform_ops(void)
55
+{
49
+{
56
+    mutex_lock(&register_mutex);
50
+    guard(mutex)(&register_mutex);
57
+    platform_ops = NULL;
51
+    platform_ops = NULL;
58
+    mutex_unlock(&register_mutex);
59
+
52
+
60
+    return 0;
53
+    return 0;
61
+}
54
+}
62
+EXPORT_SYMBOL_GPL(snd_usb_unregister_platform_ops);
55
+EXPORT_SYMBOL_GPL(snd_usb_unregister_platform_ops);
63
56
64
/*
57
/*
65
* disconnect streams
58
* Checks to see if requested audio profile, i.e sample rate, # of
66
@@ -XXX,XX +XXX,XX @@ static int usb_audio_probe(struct usb_interface *intf,
59
@@ -XXX,XX +XXX,XX @@ static int usb_audio_probe(struct usb_interface *intf,
67
    chip->num_interfaces++;
60
    chip->num_interfaces++;
68
    usb_set_intfdata(intf, chip);
61
    usb_set_intfdata(intf, chip);
69
    atomic_dec(&chip->active);
62
    atomic_dec(&chip->active);
70
+
63
+
...
...
87
        struct snd_usb_endpoint *ep;
80
        struct snd_usb_endpoint *ep;
88
@@ -XXX,XX +XXX,XX @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
81
@@ -XXX,XX +XXX,XX @@ static int usb_audio_suspend(struct usb_interface *intf, pm_message_t message)
89
        chip->system_suspend = chip->num_suspended_intf;
82
        chip->system_suspend = chip->num_suspended_intf;
90
    }
83
    }
91
84
92
+    mutex_lock(&register_mutex);
93
+    if (platform_ops && platform_ops->suspend_cb)
85
+    if (platform_ops && platform_ops->suspend_cb)
94
+        platform_ops->suspend_cb(intf, message);
86
+        platform_ops->suspend_cb(intf, message);
95
+    mutex_unlock(&register_mutex);
96
+
87
+
97
    return 0;
88
    return 0;
98
}
89
}
99
90
100
@@ -XXX,XX +XXX,XX @@ static int usb_audio_resume(struct usb_interface *intf)
91
@@ -XXX,XX +XXX,XX @@ static int usb_audio_resume(struct usb_interface *intf)
101
92
102
    snd_usb_midi_v2_resume_all(chip);
93
    snd_usb_midi_v2_resume_all(chip);
103
94
104
+    mutex_lock(&register_mutex);
105
+    if (platform_ops && platform_ops->resume_cb)
95
+    if (platform_ops && platform_ops->resume_cb)
106
+        platform_ops->resume_cb(intf);
96
+        platform_ops->resume_cb(intf);
107
+    mutex_unlock(&register_mutex);
108
+
97
+
109
out:
98
out:
110
    if (chip->num_suspended_intf == chip->system_suspend) {
99
    if (chip->num_suspended_intf == chip->system_suspend) {
111
        snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
100
        snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0);
112
diff --git a/sound/usb/card.h b/sound/usb/card.h
101
diff --git a/sound/usb/card.h b/sound/usb/card.h
...
...
122
+    void (*disconnect_cb)(struct snd_usb_audio *chip);
111
+    void (*disconnect_cb)(struct snd_usb_audio *chip);
123
+    void (*suspend_cb)(struct usb_interface *intf, pm_message_t message);
112
+    void (*suspend_cb)(struct usb_interface *intf, pm_message_t message);
124
+    void (*resume_cb)(struct usb_interface *intf);
113
+    void (*resume_cb)(struct usb_interface *intf);
125
+};
114
+};
126
+
115
+
116
struct snd_usb_stream *
117
snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params,
118
                 int direction);
119
+
127
+int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops);
120
+int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops);
128
+int snd_usb_unregister_platform_ops(void);
121
+int snd_usb_unregister_platform_ops(void);
129
#endif /* __USBAUDIO_CARD_H */
122
#endif /* __USBAUDIO_CARD_H */
diff view generated by jsdifflib
...
...
8
to call the respective connection callback registered to the SND platform
8
to call the respective connection callback registered to the SND platform
9
driver.
9
driver.
10
10
11
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
11
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
12
---
12
---
13
sound/usb/card.c | 19 +++++++++++++++++++
13
sound/usb/card.c | 21 +++++++++++++++++++++
14
sound/usb/card.h | 2 ++
14
sound/usb/card.h | 2 ++
15
2 files changed, 21 insertions(+)
15
2 files changed, 23 insertions(+)
16
16
17
diff --git a/sound/usb/card.c b/sound/usb/card.c
17
diff --git a/sound/usb/card.c b/sound/usb/card.c
18
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
19
--- a/sound/usb/card.c
19
--- a/sound/usb/card.c
20
+++ b/sound/usb/card.c
20
+++ b/sound/usb/card.c
21
@@ -XXX,XX +XXX,XX @@ struct snd_usb_stream *snd_usb_find_suppported_substream(int card_idx,
21
@@ -XXX,XX +XXX,XX @@ int snd_usb_unregister_platform_ops(void)
22
}
22
}
23
EXPORT_SYMBOL_GPL(snd_usb_find_suppported_substream);
23
EXPORT_SYMBOL_GPL(snd_usb_unregister_platform_ops);
24
24
25
+/*
25
+/*
26
+ * in case the platform driver was not ready at the time of USB SND
26
+ * in case the platform driver was not ready at the time of USB SND
27
+ * device connect, expose an API to discover all connected USB devices
27
+ * device connect, expose an API to discover all connected USB devices
28
+ * so it can populate any dependent resources/structures.
28
+ * so it can populate any dependent resources/structures.
29
+ */
29
+ */
30
+void snd_usb_rediscover_devices(void)
30
+void snd_usb_rediscover_devices(void)
31
+{
31
+{
32
+    int i;
32
+    int i;
33
+
33
+
34
+    mutex_lock(&register_mutex);
34
+    guard(mutex)(&register_mutex);
35
+
36
+    if (!platform_ops || !platform_ops->connect_cb)
37
+        return;
38
+
35
+    for (i = 0; i < SNDRV_CARDS; i++) {
39
+    for (i = 0; i < SNDRV_CARDS; i++) {
36
+        if (usb_chip[i])
40
+        if (usb_chip[i])
37
+            if (platform_ops && platform_ops->connect_cb)
41
+            platform_ops->connect_cb(usb_chip[i]);
38
+                platform_ops->connect_cb(usb_chip[i]);
39
+    }
42
+    }
40
+    mutex_unlock(&register_mutex);
41
+}
43
+}
42
+EXPORT_SYMBOL_GPL(snd_usb_rediscover_devices);
44
+EXPORT_SYMBOL_GPL(snd_usb_rediscover_devices);
43
+
45
+
44
/*
46
/*
45
* disconnect streams
47
* Checks to see if requested audio profile, i.e sample rate, # of
46
* called from usb_audio_disconnect()
48
* channels, etc... is supported by the substream associated to the
47
diff --git a/sound/usb/card.h b/sound/usb/card.h
49
diff --git a/sound/usb/card.h b/sound/usb/card.h
48
index XXXXXXX..XXXXXXX 100644
50
index XXXXXXX..XXXXXXX 100644
49
--- a/sound/usb/card.h
51
--- a/sound/usb/card.h
50
+++ b/sound/usb/card.h
52
+++ b/sound/usb/card.h
51
@@ -XXX,XX +XXX,XX @@ int snd_usb_unregister_platform_ops(void);
53
@@ -XXX,XX +XXX,XX @@ snd_usb_find_suppported_substream(int card_idx, struct snd_pcm_hw_params *params
52
#if IS_ENABLED(CONFIG_SND_USB_AUDIO)
54
53
struct snd_usb_stream *snd_usb_find_suppported_substream(int card_idx,
55
int snd_usb_register_platform_ops(struct snd_usb_platform_ops *ops);
54
            struct snd_pcm_hw_params *params, int direction);
56
int snd_usb_unregister_platform_ops(void);
57
+
55
+void snd_usb_rediscover_devices(void);
58
+void snd_usb_rediscover_devices(void);
56
#else
57
static struct snd_usb_stream *snd_usb_find_suppported_substream(int card_idx,
58
            struct snd_pcm_hw_params *params, int direction)
59
{
60
    return NULL;
61
}
62
+static void snd_usb_rediscover_devices(void) { }
63
#endif /* IS_ENABLED(CONFIG_SND_USB_AUDIO) */
64
#endif /* __USBAUDIO_CARD_H */
59
#endif /* __USBAUDIO_CARD_H */
diff view generated by jsdifflib
1
Some platforms may have support for offloading USB audio devices to a
1
Some platforms may have support for offloading USB audio devices to a
2
dedicated audio DSP. Introduce a set of APIs that allow for management of
2
dedicated audio DSP. Introduce a set of APIs that allow for management of
3
USB sound card and PCM devices enumerated by the USB SND class driver.
3
USB sound card and PCM devices enumerated by the USB SND class driver.
4
This allows for the ASoC components to be aware of what USB devices are
4
This allows for the ASoC components to be aware of what USB devices are
5
available for offloading.
5
available for offloading.
6
6
7
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
---
9
---
9
include/sound/soc-usb.h | 48 +++++++++++
10
include/sound/soc-usb.h | 93 +++++++++++++++++
10
sound/soc/Makefile | 2 +-
11
sound/soc/Kconfig | 10 ++
11
sound/soc/soc-usb.c | 186 ++++++++++++++++++++++++++++++++++++++++
12
sound/soc/Makefile | 2 +
12
3 files changed, 235 insertions(+), 1 deletion(-)
13
sound/soc/soc-usb.c | 219 ++++++++++++++++++++++++++++++++++++++++
14
4 files changed, 324 insertions(+)
13
create mode 100644 include/sound/soc-usb.h
15
create mode 100644 include/sound/soc-usb.h
14
create mode 100644 sound/soc/soc-usb.c
16
create mode 100644 sound/soc/soc-usb.c
15
17
16
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
18
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
17
new file mode 100644
19
new file mode 100644
18
index XXXXXXX..XXXXXXX
20
index XXXXXXX..XXXXXXX
19
--- /dev/null
21
--- /dev/null
20
+++ b/include/sound/soc-usb.h
22
+++ b/include/sound/soc-usb.h
21
@@ -XXX,XX +XXX,XX @@
23
@@ -XXX,XX +XXX,XX @@
22
+/* SPDX-License-Identifier: GPL-2.0
24
+/* SPDX-License-Identifier: GPL-2.0
23
+ *
25
+ *
24
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
26
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
25
+ */
27
+ */
26
+
28
+
27
+#ifndef __LINUX_SND_SOC_USB_H
29
+#ifndef __LINUX_SND_SOC_USB_H
28
+#define __LINUX_SND_SOC_USB_H
30
+#define __LINUX_SND_SOC_USB_H
29
+
31
+
30
+/**
32
+#include <sound/soc.h>
31
+ * struct snd_soc_usb_device
33
+
32
+ * @card_idx - sound card index associated with USB device
34
+/**
33
+ * @chip_idx - USB sound chip array index
35
+ * struct snd_soc_usb_device - SoC USB representation of a USB sound device
34
+ * @num_playback - number of playback streams
36
+ * @card_idx: sound card index associated with USB device
35
+ * @num_capture - number of capture streams
37
+ * @chip_idx: USB sound chip array index
38
+ * @cpcm_idx: capture PCM index array associated with USB device
39
+ * @ppcm_idx: playback PCM index array associated with USB device
40
+ * @num_capture: number of capture streams
41
+ * @num_playback: number of playback streams
42
+ * @list: list head for SoC USB devices
36
+ **/
43
+ **/
37
+struct snd_soc_usb_device {
44
+struct snd_soc_usb_device {
38
+    int card_idx;
45
+    int card_idx;
39
+    int chip_idx;
46
+    int chip_idx;
47
+
48
+    /* PCM index arrays */
49
+    unsigned int *cpcm_idx; /* TODO: capture path is not tested yet */
50
+    unsigned int *ppcm_idx;
51
+    int num_capture; /* TODO: capture path is not tested yet */
40
+    int num_playback;
52
+    int num_playback;
41
+    int num_capture;
53
+
54
+    struct list_head list;
42
+};
55
+};
43
+
56
+
44
+/**
57
+/**
45
+ * struct snd_soc_usb
58
+ * struct snd_soc_usb - representation of a SoC USB backend entity
46
+ * @list - list head for SND SOC struct list
59
+ * @list: list head for SND SOC struct list
47
+ * @dev - USB backend device reference
60
+ * @component: reference to ASoC component
48
+ * @component - reference to ASoC component
61
+ * @connection_status_cb: callback to notify connection events
49
+ * @connection_status_cb - callback to notify connection events
62
+ * @priv_data: driver data
50
+ * @priv_data - driver data
51
+ **/
63
+ **/
52
+struct snd_soc_usb {
64
+struct snd_soc_usb {
53
+    struct list_head list;
65
+    struct list_head list;
54
+    struct device *dev;
55
+    struct snd_soc_component *component;
66
+    struct snd_soc_component *component;
56
+    int (*connection_status_cb)(struct snd_soc_usb *usb,
67
+    int (*connection_status_cb)(struct snd_soc_usb *usb,
57
+            struct snd_soc_usb_device *sdev, bool connected);
68
+                 struct snd_soc_usb_device *sdev,
69
+                 bool connected);
58
+    void *priv_data;
70
+    void *priv_data;
59
+};
71
+};
60
+
72
+
73
+#if IS_ENABLED(CONFIG_SND_SOC_USB)
61
+int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev);
74
+int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev);
62
+int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev);
75
+int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev);
63
+void *snd_soc_usb_get_priv_data(struct device *usbdev);
76
+void *snd_soc_usb_find_priv_data(struct device *usbdev);
64
+
77
+
65
+struct snd_soc_usb *snd_soc_usb_add_port(struct device *dev, void *priv,
78
+struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
66
+            int (*connection_cb)(struct snd_soc_usb *usb,
79
+                     void *data);
67
+            struct snd_soc_usb_device *sdev, bool connected));
80
+void snd_soc_usb_free_port(struct snd_soc_usb *usb);
68
+int snd_soc_usb_remove_port(struct device *dev);
81
+void snd_soc_usb_add_port(struct snd_soc_usb *usb);
69
+#endif
82
+void snd_soc_usb_remove_port(struct snd_soc_usb *usb);
83
+#else
84
+static inline int snd_soc_usb_connect(struct device *usbdev,
85
+                 struct snd_soc_usb_device *sdev)
86
+{
87
+    return -ENODEV;
88
+}
89
+
90
+static inline int snd_soc_usb_disconnect(struct device *usbdev,
91
+                     struct snd_soc_usb_device *sdev)
92
+{
93
+    return -EINVAL;
94
+}
95
+
96
+static inline void *snd_soc_usb_find_priv_data(struct device *usbdev)
97
+{
98
+    return NULL;
99
+}
100
+
101
+static inline struct snd_soc_usb *
102
+snd_soc_usb_allocate_port(struct snd_soc_component *component, void *data)
103
+{
104
+    return ERR_PTR(-ENOMEM);
105
+}
106
+
107
+static inline void snd_soc_usb_free_port(struct snd_soc_usb *usb)
108
+{ }
109
+
110
+static inline void snd_soc_usb_add_port(struct snd_soc_usb *usb)
111
+{ }
112
+
113
+static inline void snd_soc_usb_remove_port(struct snd_soc_usb *usb)
114
+{ }
115
+#endif /* IS_ENABLED(CONFIG_SND_SOC_USB) */
116
+#endif /*__LINUX_SND_SOC_USB_H */
117
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
118
index XXXXXXX..XXXXXXX 100644
119
--- a/sound/soc/Kconfig
120
+++ b/sound/soc/Kconfig
121
@@ -XXX,XX +XXX,XX @@ config SND_SOC_UTILS_KUNIT_TEST
122
config SND_SOC_ACPI
123
    tristate
124
125
+config SND_SOC_USB
126
+    tristate "SoC based USB audio offloading"
127
+    depends on SND_USB_AUDIO
128
+    help
129
+     Enable this option if an ASoC platform card has support to handle
130
+     USB audio offloading. This enables the SoC USB layer, which will
131
+     notify the ASoC USB DPCM backend DAI link about available USB audio
132
+     devices. Based on the notifications, sequences to enable the audio
133
+     stream can be taken based on the design.
134
+
135
# All the supported SoCs
136
source "sound/soc/adi/Kconfig"
137
source "sound/soc/amd/Kconfig"
70
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
138
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
71
index XXXXXXX..XXXXXXX 100644
139
index XXXXXXX..XXXXXXX 100644
72
--- a/sound/soc/Makefile
140
--- a/sound/soc/Makefile
73
+++ b/sound/soc/Makefile
141
+++ b/sound/soc/Makefile
74
@@ -XXX,XX +XXX,XX @@
142
@@ -XXX,XX +XXX,XX @@ endif
75
# SPDX-License-Identifier: GPL-2.0
143
76
-snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-utils.o soc-dai.o soc-component.o
144
obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o
77
+snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-usb.o soc-utils.o soc-dai.o soc-component.o
145
78
snd-soc-core-objs += soc-pcm.o soc-devres.o soc-ops.o soc-link.o soc-card.o
146
+obj-$(CONFIG_SND_SOC_USB) += soc-usb.o
79
snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
147
+
80
148
obj-$(CONFIG_SND_SOC)    += snd-soc-core.o
149
obj-$(CONFIG_SND_SOC)    += codecs/
150
obj-$(CONFIG_SND_SOC)    += generic/
81
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
151
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
82
new file mode 100644
152
new file mode 100644
83
index XXXXXXX..XXXXXXX
153
index XXXXXXX..XXXXXXX
84
--- /dev/null
154
--- /dev/null
85
+++ b/sound/soc/soc-usb.c
155
+++ b/sound/soc/soc-usb.c
86
@@ -XXX,XX +XXX,XX @@
156
@@ -XXX,XX +XXX,XX @@
87
+// SPDX-License-Identifier: GPL-2.0
157
+// SPDX-License-Identifier: GPL-2.0
88
+/*
158
+/*
89
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
159
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
90
+ */
160
+ */
91
+#include <linux/of.h>
161
+#include <linux/of.h>
92
+#include <linux/usb.h>
162
+#include <linux/usb.h>
93
+#include <sound/soc.h>
94
+#include <sound/soc-usb.h>
163
+#include <sound/soc-usb.h>
95
+#include "../usb/card.h"
164
+#include "../usb/card.h"
96
+
165
+
97
+static DEFINE_MUTEX(ctx_mutex);
166
+static DEFINE_MUTEX(ctx_mutex);
98
+static LIST_HEAD(usb_ctx_list);
167
+static LIST_HEAD(usb_ctx_list);
...
...
106
+        return ERR_PTR(-ENODEV);
175
+        return ERR_PTR(-ENODEV);
107
+
176
+
108
+    return node;
177
+    return node;
109
+}
178
+}
110
+
179
+
111
+static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device_node *node)
180
+static struct snd_soc_usb *snd_soc_usb_ctx_lookup(struct device_node *node)
112
+{
181
+{
113
+    struct snd_soc_usb *ctx;
182
+    struct snd_soc_usb *ctx;
114
+
183
+
115
+    mutex_lock(&ctx_mutex);
184
+    if (!node)
185
+        return NULL;
186
+
116
+    list_for_each_entry(ctx, &usb_ctx_list, list) {
187
+    list_for_each_entry(ctx, &usb_ctx_list, list) {
117
+        if (ctx->dev->of_node == node) {
188
+        if (ctx->component->dev->of_node == node)
118
+            mutex_unlock(&ctx_mutex);
119
+            return ctx;
189
+            return ctx;
120
+        }
121
+    }
190
+    }
122
+    mutex_unlock(&ctx_mutex);
123
+
191
+
124
+    return NULL;
192
+    return NULL;
125
+}
193
+}
126
+
194
+
127
+/**
195
+static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device *dev)
128
+ * snd_soc_usb_find_priv_data() - Retrieve private data stored
129
+ * @dev: device reference
130
+ *
131
+ * Fetch the private data stored in the USB SND SOC structure.
132
+ *
133
+ */
134
+void *snd_soc_usb_find_priv_data(struct device *dev)
135
+{
196
+{
136
+    struct snd_soc_usb *ctx;
197
+    struct snd_soc_usb *ctx;
137
+    struct device_node *node;
198
+    struct device_node *node;
138
+
199
+
139
+    node = snd_soc_find_phandle(dev);
200
+    node = snd_soc_find_phandle(dev);
140
+    if (!IS_ERR(node)) {
201
+    if (!IS_ERR(node)) {
141
+        ctx = snd_soc_find_usb_ctx(node);
202
+        ctx = snd_soc_usb_ctx_lookup(node);
142
+        of_node_put(node);
203
+        of_node_put(node);
143
+    } else {
204
+    } else {
144
+        /* Check if backend device */
205
+        ctx = snd_soc_usb_ctx_lookup(dev->of_node);
145
+        ctx = snd_soc_find_usb_ctx(dev->of_node);
146
+    }
206
+    }
147
+
207
+
208
+    return ctx ? ctx : NULL;
209
+}
210
+
211
+/**
212
+ * snd_soc_usb_find_priv_data() - Retrieve private data stored
213
+ * @usbdev: device reference
214
+ *
215
+ * Fetch the private data stored in the USB SND SoC structure.
216
+ *
217
+ */
218
+void *snd_soc_usb_find_priv_data(struct device *usbdev)
219
+{
220
+    struct snd_soc_usb *ctx;
221
+
222
+    mutex_lock(&ctx_mutex);
223
+    ctx = snd_soc_find_usb_ctx(usbdev);
224
+    mutex_unlock(&ctx_mutex);
225
+
148
+    return ctx ? ctx->priv_data : NULL;
226
+    return ctx ? ctx->priv_data : NULL;
149
+}
227
+}
150
+EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
228
+EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
151
+
229
+
152
+/**
230
+/**
153
+ * snd_soc_usb_add_port() - Add a USB backend port
231
+ * snd_soc_usb_allocate_port() - allocate a SoC USB port for offloading support
154
+ * @dev: USB backend device
232
+ * @component: USB DPCM backend DAI component
155
+ * @priv: private data
233
+ * @data: private data
156
+ * @connection_cb: connection status callback
234
+ *
157
+ *
235
+ * Allocate and initialize a SoC USB port. The SoC USB port is used to communicate
158
+ * Register a USB backend device to the SND USB SOC framework. Memory is
236
+ * different USB audio devices attached, in order to start audio offloading handled
159
+ * allocated as part of the USB backend device.
237
+ * by an ASoC entity. USB device plug in/out events are signaled with a
160
+ *
238
+ * notification, but don't directly impact the memory allocated for the SoC USB
161
+ */
239
+ * port.
162
+struct snd_soc_usb *snd_soc_usb_add_port(struct device *dev, void *priv,
240
+ *
163
+            int (*connection_cb)(struct snd_soc_usb *usb,
241
+ */
164
+            struct snd_soc_usb_device *sdev, bool connected))
242
+struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
243
+                     void *data)
165
+{
244
+{
166
+    struct snd_soc_usb *usb;
245
+    struct snd_soc_usb *usb;
167
+
246
+
168
+    usb = devm_kzalloc(dev, sizeof(*usb), GFP_KERNEL);
247
+    usb = kzalloc(sizeof(*usb), GFP_KERNEL);
169
+    if (!usb)
248
+    if (!usb)
170
+        return ERR_PTR(-ENOMEM);
249
+        return ERR_PTR(-ENOMEM);
171
+
250
+
172
+    usb->connection_status_cb = connection_cb;
251
+    usb->component = component;
173
+    usb->dev = dev;
252
+    usb->priv_data = data;
174
+    usb->priv_data = priv;
253
+
175
+
254
+    return usb;
255
+}
256
+EXPORT_SYMBOL_GPL(snd_soc_usb_allocate_port);
257
+
258
+/**
259
+ * snd_soc_usb_free_port() - free a SoC USB port used for offloading support
260
+ * @usb: allocated SoC USB port
261
+ *
262
+ * Free and remove the SoC USB port from the available list of ports. This will
263
+ * ensure that the communication between USB SND and ASoC is halted.
264
+ *
265
+ */
266
+void snd_soc_usb_free_port(struct snd_soc_usb *usb)
267
+{
268
+    snd_soc_usb_remove_port(usb);
269
+    kfree(usb);
270
+}
271
+EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);
272
+
273
+/**
274
+ * snd_soc_usb_add_port() - Add a USB backend port
275
+ * @usb: soc usb port to add
276
+ *
277
+ * Register a USB backend DAI link to the USB SoC framework. Memory is allocated
278
+ * as part of the USB backend DAI link.
279
+ *
280
+ */
281
+void snd_soc_usb_add_port(struct snd_soc_usb *usb)
282
+{
176
+    mutex_lock(&ctx_mutex);
283
+    mutex_lock(&ctx_mutex);
177
+    list_add_tail(&usb->list, &usb_ctx_list);
284
+    list_add_tail(&usb->list, &usb_ctx_list);
178
+    mutex_unlock(&ctx_mutex);
285
+    mutex_unlock(&ctx_mutex);
179
+
180
+    return usb;
181
+}
286
+}
182
+EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
287
+EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
183
+
288
+
184
+/**
289
+/**
185
+ * snd_soc_usb_remove_port() - Remove a USB backend port
290
+ * snd_soc_usb_remove_port() - Remove a USB backend port
186
+ * @dev: USB backend device
291
+ * @usb: soc usb port to remove
187
+ *
292
+ *
188
+ * Remove a USB backend device from USB SND SOC. Memory is freed when USB
293
+ * Remove a USB backend DAI link from USB SoC. Memory is freed when USB backend
189
+ * backend is removed.
294
+ * DAI is removed, or when snd_soc_usb_free_port() is called.
190
+ *
295
+ *
191
+ */
296
+ */
192
+int snd_soc_usb_remove_port(struct device *dev)
297
+void snd_soc_usb_remove_port(struct snd_soc_usb *usb)
193
+{
298
+{
194
+    struct snd_soc_usb *ctx, *tmp;
299
+    struct snd_soc_usb *ctx, *tmp;
195
+
300
+
196
+    mutex_lock(&ctx_mutex);
301
+    mutex_lock(&ctx_mutex);
197
+    list_for_each_entry_safe(ctx, tmp, &usb_ctx_list, list) {
302
+    list_for_each_entry_safe(ctx, tmp, &usb_ctx_list, list) {
198
+        if (ctx->dev == dev) {
303
+        if (ctx == usb) {
199
+            list_del(&ctx->list);
304
+            list_del(&ctx->list);
200
+            break;
305
+            break;
201
+        }
306
+        }
202
+    }
307
+    }
203
+    mutex_unlock(&ctx_mutex);
308
+    mutex_unlock(&ctx_mutex);
204
+
205
+    return 0;
206
+}
309
+}
207
+EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);
310
+EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);
208
+
311
+
209
+/**
312
+/**
210
+ * snd_soc_usb_connect() - Notification of USB device connection
313
+ * snd_soc_usb_connect() - Notification of USB device connection
211
+ * @usbdev: USB bus device
314
+ * @usbdev: USB bus device
212
+ * @card_idx: USB SND card instance
315
+ * @sdev: USB SND device to add
213
+ *
316
+ *
214
+ * Notify of a new USB SND device connection. The card_idx can be used to
317
+ * Notify of a new USB SND device connection. The sdev->card_idx can be used to
215
+ * handle how the DPCM backend selects, which device to enable USB offloading
318
+ * handle how the DPCM backend selects, which device to enable USB offloading
216
+ * on.
319
+ * on.
217
+ *
320
+ *
218
+ */
321
+ */
219
+int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
322
+int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
220
+{
323
+{
221
+    struct snd_soc_usb *ctx;
324
+    struct snd_soc_usb *ctx;
222
+    struct device_node *node;
223
+
325
+
224
+    if (!usbdev)
326
+    if (!usbdev)
225
+        return -ENODEV;
327
+        return -ENODEV;
226
+
328
+
227
+    node = snd_soc_find_phandle(usbdev);
329
+    mutex_lock(&ctx_mutex);
228
+    if (IS_ERR(node))
330
+    ctx = snd_soc_find_usb_ctx(usbdev);
229
+        return -ENODEV;
230
+
231
+    ctx = snd_soc_find_usb_ctx(node);
232
+    of_node_put(node);
233
+    if (!ctx)
331
+    if (!ctx)
234
+        return -ENODEV;
332
+        goto exit;
235
+
333
+
236
+    if (ctx->connection_status_cb)
334
+    if (ctx->connection_status_cb)
237
+        ctx->connection_status_cb(ctx, sdev, true);
335
+        ctx->connection_status_cb(ctx, sdev, true);
238
+
336
+
337
+exit:
338
+    mutex_unlock(&ctx_mutex);
339
+
239
+    return 0;
340
+    return 0;
240
+}
341
+}
241
+EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
342
+EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
242
+
343
+
243
+/**
344
+/**
244
+ * snd_soc_usb_disconnect() - Notification of USB device disconnection
345
+ * snd_soc_usb_disconnect() - Notification of USB device disconnection
245
+ * @usbdev: USB bus device
346
+ * @usbdev: USB bus device
347
+ * @sdev: USB SND device to remove
246
+ *
348
+ *
247
+ * Notify of a new USB SND device disconnection to the USB backend.
349
+ * Notify of a new USB SND device disconnection to the USB backend.
248
+ *
350
+ *
249
+ */
351
+ */
250
+int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
352
+int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
251
+{
353
+{
252
+    struct snd_soc_usb *ctx;
354
+    struct snd_soc_usb *ctx;
253
+    struct device_node *node;
254
+
355
+
255
+    if (!usbdev)
356
+    if (!usbdev)
256
+        return -ENODEV;
357
+        return -ENODEV;
257
+
358
+
258
+    node = snd_soc_find_phandle(usbdev);
359
+    mutex_lock(&ctx_mutex);
259
+    if (IS_ERR(node))
360
+    ctx = snd_soc_find_usb_ctx(usbdev);
260
+        return -ENODEV;
261
+
262
+    ctx = snd_soc_find_usb_ctx(node);
263
+    of_node_put(node);
264
+    if (!ctx)
361
+    if (!ctx)
265
+        return -ENODEV;
362
+        goto exit;
266
+
363
+
267
+    if (ctx->connection_status_cb)
364
+    if (ctx->connection_status_cb)
268
+        ctx->connection_status_cb(ctx, sdev, false);
365
+        ctx->connection_status_cb(ctx, sdev, false);
269
+
366
+
367
+exit:
368
+    mutex_unlock(&ctx_mutex);
369
+
270
+    return 0;
370
+    return 0;
271
+}
371
+}
272
+EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);
372
+EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);
373
+
374
+MODULE_LICENSE("GPL");
375
+MODULE_DESCRIPTION("SoC USB driver for offloading");
diff view generated by jsdifflib
1
Introduce a check for if a particular PCM format is supported by the USB
1
Introduce a helper to check if a particular PCM format is supported by the
2
audio device connected. If the USB audio device does not have an audio
2
USB audio device connected. If the USB audio device does not have an
3
profile which can support the requested format, then notify the USB
3
audio profile which can support the requested format, then notify the USB
4
backend.
4
backend.
5
5
6
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
8
---
8
include/sound/soc-usb.h | 3 +++
9
include/sound/soc-usb.h | 11 +++++++++++
9
sound/soc/soc-usb.c | 13 +++++++++++++
10
sound/soc/soc-usb.c | 26 ++++++++++++++++++++++++++
10
2 files changed, 16 insertions(+)
11
2 files changed, 37 insertions(+)
11
12
12
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
13
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
13
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
14
--- a/include/sound/soc-usb.h
15
--- a/include/sound/soc-usb.h
15
+++ b/include/sound/soc-usb.h
16
+++ b/include/sound/soc-usb.h
16
@@ -XXX,XX +XXX,XX @@ struct snd_soc_usb {
17
@@ -XXX,XX +XXX,XX @@ struct snd_soc_usb {
17
    void *priv_data;
18
};
18
};
19
19
20
+int snd_soc_usb_find_format(int card_idx, struct snd_pcm_hw_params *params,
20
#if IS_ENABLED(CONFIG_SND_SOC_USB)
21
+            int direction);
21
+int snd_soc_usb_find_supported_format(int card_idx,
22
+                 struct snd_pcm_hw_params *params,
23
+                 int direction);
22
+
24
+
23
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev);
25
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev);
24
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev);
26
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev);
25
void *snd_soc_usb_find_priv_data(struct device *usbdev);
27
void *snd_soc_usb_find_priv_data(struct device *usbdev);
28
@@ -XXX,XX +XXX,XX @@ void snd_soc_usb_free_port(struct snd_soc_usb *usb);
29
void snd_soc_usb_add_port(struct snd_soc_usb *usb);
30
void snd_soc_usb_remove_port(struct snd_soc_usb *usb);
31
#else
32
+static inline int
33
+snd_soc_usb_find_supported_format(int card_idx, struct snd_pcm_hw_params *params,
34
+                 int direction)
35
+{
36
+    return -EINVAL;
37
+}
38
+
39
static inline int snd_soc_usb_connect(struct device *usbdev,
40
                 struct snd_soc_usb_device *sdev)
41
{
26
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
42
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
27
index XXXXXXX..XXXXXXX 100644
43
index XXXXXXX..XXXXXXX 100644
28
--- a/sound/soc/soc-usb.c
44
--- a/sound/soc/soc-usb.c
29
+++ b/sound/soc/soc-usb.c
45
+++ b/sound/soc/soc-usb.c
30
@@ -XXX,XX +XXX,XX @@ void *snd_soc_usb_find_priv_data(struct device *dev)
46
@@ -XXX,XX +XXX,XX @@ void *snd_soc_usb_find_priv_data(struct device *usbdev)
31
}
47
}
32
EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
48
EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
33
49
34
+int snd_soc_usb_find_format(int card_idx, struct snd_pcm_hw_params *params,
50
+/**
35
+            int direction)
51
+ * snd_soc_usb_find_supported_format() - Check if audio format is supported
52
+ * @card_idx: USB sound chip array index
53
+ * @params: PCM parameters
54
+ * @direction: capture or playback
55
+ *
56
+ * Ensure that a requested audio profile from the ASoC side is able to be
57
+ * supported by the USB device.
58
+ *
59
+ * Return 0 on success, negative on error.
60
+ *
61
+ */
62
+int snd_soc_usb_find_supported_format(int card_idx,
63
+                 struct snd_pcm_hw_params *params,
64
+                 int direction)
36
+{
65
+{
37
+    struct snd_usb_stream *as;
66
+    struct snd_usb_stream *as;
38
+
67
+
39
+    as = snd_usb_find_suppported_substream(card_idx, params, direction);
68
+    as = snd_usb_find_suppported_substream(card_idx, params, direction);
40
+    if (!as)
69
+    if (!as)
41
+        return -EOPNOTSUPP;
70
+        return -EOPNOTSUPP;
42
+
71
+
43
+    return 0;
72
+    return 0;
44
+}
73
+}
45
+EXPORT_SYMBOL_GPL(snd_soc_usb_find_format);
74
+EXPORT_SYMBOL_GPL(snd_soc_usb_find_supported_format);
46
+
75
+
47
/**
76
/**
48
* snd_soc_usb_add_port() - Add a USB backend port
77
* snd_soc_usb_allocate_port() - allocate a SoC USB port for offloading support
49
* @dev: USB backend device
78
* @component: USB DPCM backend DAI component
diff view generated by jsdifflib
1
From: Mathias Nyman <mathias.nyman@linux.intel.com>
1
Expose API for creation of a jack control for notifying of available
2
devices that are plugged in/discovered, and that support offloading. This
3
allows for control names to be standardized across implementations of USB
4
audio offloading.
2
5
3
Modify the XHCI drivers to accommodate for handling multiple event rings in
6
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
4
case there are multiple interrupters. Add the required APIs so clients are
5
able to allocate/request for an interrupter ring, and pass this information
6
back to the client driver. This allows for users to handle the resource
7
accordingly, such as passing the event ring base address to an audio DSP.
8
There is no actual support for multiple MSI/MSI-X vectors.
9
10
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
11
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
12
---
8
---
13
drivers/usb/host/xhci-debugfs.c | 2 +-
9
include/sound/soc-usb.h | 9 +++++++++
14
drivers/usb/host/xhci-mem.c | 99 ++++++++++++++++++++++++++++++---
10
sound/soc/soc-usb.c | 38 ++++++++++++++++++++++++++++++++++++++
15
drivers/usb/host/xhci-ring.c | 2 +-
11
2 files changed, 47 insertions(+)
16
drivers/usb/host/xhci.c | 51 +++++++++++------
17
drivers/usb/host/xhci.h | 5 +-
18
5 files changed, 130 insertions(+), 29 deletions(-)
19
12
20
diff --git a/drivers/usb/host/xhci-debugfs.c b/drivers/usb/host/xhci-debugfs.c
13
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
21
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
22
--- a/drivers/usb/host/xhci-debugfs.c
15
--- a/include/sound/soc-usb.h
23
+++ b/drivers/usb/host/xhci-debugfs.c
16
+++ b/include/sound/soc-usb.h
24
@@ -XXX,XX +XXX,XX @@ void xhci_debugfs_init(struct xhci_hcd *xhci)
17
@@ -XXX,XX +XXX,XX @@ int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev);
25
                 "command-ring",
18
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev);
26
                 xhci->debugfs_root);
19
void *snd_soc_usb_find_priv_data(struct device *usbdev);
27
20
28
-    xhci_debugfs_create_ring_dir(xhci, &xhci->interrupter->event_ring,
21
+int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
29
+    xhci_debugfs_create_ring_dir(xhci, &xhci->interrupters[0]->event_ring,
22
+                 struct snd_soc_jack *jack);
30
                 "event-ring",
23
+
31
                 xhci->debugfs_root);
24
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
32
25
                     void *data);
33
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
26
void snd_soc_usb_free_port(struct snd_soc_usb *usb);
27
@@ -XXX,XX +XXX,XX @@ static inline void *snd_soc_usb_find_priv_data(struct device *usbdev)
28
    return NULL;
29
}
30
31
+static inline int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
32
+                         struct snd_soc_jack *jack)
33
+{
34
+    return 0;
35
+}
36
+
37
static inline struct snd_soc_usb *
38
snd_soc_usb_allocate_port(struct snd_soc_component *component, void *data)
39
{
40
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
34
index XXXXXXX..XXXXXXX 100644
41
index XXXXXXX..XXXXXXX 100644
35
--- a/drivers/usb/host/xhci-mem.c
42
--- a/sound/soc/soc-usb.c
36
+++ b/drivers/usb/host/xhci-mem.c
43
+++ b/sound/soc/soc-usb.c
37
@@ -XXX,XX +XXX,XX @@ xhci_free_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir)
44
@@ -XXX,XX +XXX,XX @@
38
    kfree(ir);
45
*/
46
#include <linux/of.h>
47
#include <linux/usb.h>
48
+
49
+#include <sound/jack.h>
50
#include <sound/soc-usb.h>
51
+
52
#include "../usb/card.h"
53
54
static DEFINE_MUTEX(ctx_mutex);
55
@@ -XXX,XX +XXX,XX @@ static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device *dev)
56
    return ctx ? ctx : NULL;
39
}
57
}
40
58
41
+void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrupter *ir)
59
+/* SOC USB sound kcontrols */
60
+/**
61
+ * snd_soc_usb_setup_offload_jack() - Create USB offloading jack
62
+ * @component: USB DPCM backend DAI component
63
+ * @jack: jack structure to create
64
+ *
65
+ * Creates a jack device for notifying userspace of the availability
66
+ * of an offload capable device.
67
+ *
68
+ * Returns 0 on success, negative on error.
69
+ *
70
+ */
71
+int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
72
+                 struct snd_soc_jack *jack)
42
+{
73
+{
43
+    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
74
+    int ret;
44
+    unsigned int intr_num;
45
+
75
+
46
+    /* interrupter 0 is primary interrupter, don't touch it */
76
+    ret = snd_soc_card_jack_new(component->card, "USB Offload Jack",
47
+    if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
77
+                 SND_JACK_USB, jack);
48
+        xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
78
+    if (ret < 0) {
49
+
79
+        dev_err(component->card->dev, "Unable to add USB offload jack: %d\n",
50
+    /* fixme, should we check xhci->interrupter[intr_num] == ir */
80
+            ret);
51
+    /* fixme locking */
81
+        return ret;
52
+
53
+    spin_lock_irq(&xhci->lock);
54
+
55
+    intr_num = ir->intr_num;
56
+
57
+    xhci_remove_interrupter(xhci, ir);
58
+    xhci->interrupters[intr_num] = NULL;
59
+
60
+    spin_unlock_irq(&xhci->lock);
61
+
62
+    xhci_free_interrupter(xhci, ir);
63
+}
64
+EXPORT_SYMBOL_GPL(xhci_remove_secondary_interrupter);
65
+
66
void xhci_mem_cleanup(struct xhci_hcd *xhci)
67
{
68
    struct device    *dev = xhci_to_hcd(xhci)->self.sysdev;
69
@@ -XXX,XX +XXX,XX @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
70
71
    cancel_delayed_work_sync(&xhci->cmd_timer);
72
73
-    xhci_remove_interrupter(xhci, xhci->interrupter);
74
-    xhci_free_interrupter(xhci, xhci->interrupter);
75
-    xhci->interrupter = NULL;
76
-    xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed primary event ring");
77
+    for (i = 0; i < xhci->max_interrupters; i++) {
78
+        if (xhci->interrupters[i]) {
79
+            xhci_remove_interrupter(xhci, xhci->interrupters[i]);
80
+            xhci_free_interrupter(xhci, xhci->interrupters[i]);
81
+            xhci->interrupters[i] = NULL;
82
+        }
83
+    }
84
+    xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed interrupters");
85
86
    if (xhci->cmd_ring)
87
        xhci_ring_free(xhci, xhci->cmd_ring);
88
@@ -XXX,XX +XXX,XX @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
89
    for (i = 0; i < xhci->num_port_caps; i++)
90
        kfree(xhci->port_caps[i].psi);
91
    kfree(xhci->port_caps);
92
+    kfree(xhci->interrupters);
93
    xhci->num_port_caps = 0;
94
95
    xhci->usb2_rhub.ports = NULL;
96
@@ -XXX,XX +XXX,XX @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
97
    xhci->rh_bw = NULL;
98
    xhci->ext_caps = NULL;
99
    xhci->port_caps = NULL;
100
+    xhci->interrupters = NULL;
101
102
    xhci->page_size = 0;
103
    xhci->page_shift = 0;
104
@@ -XXX,XX +XXX,XX @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
105
        return -EINVAL;
106
    }
107
108
+    if (xhci->interrupters[intr_num]) {
109
+        xhci_warn(xhci, "Interrupter %d\n already set up", intr_num);
110
+        return -EINVAL;
111
+    }
82
+    }
112
+
83
+
113
+    xhci->interrupters[intr_num] = ir;
84
+    ret = snd_soc_component_set_jack(component, jack, NULL);
114
+    ir->intr_num = intr_num;
85
+    if (ret) {
115
    ir->ir_set = &xhci->run_regs->ir_set[intr_num];
86
+        dev_err(component->card->dev, "Failed to set jack: %d\n", ret);
116
87
+        return ret;
117
    /* set ERST count with the number of entries in the segment table */
118
@@ -XXX,XX +XXX,XX @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
119
    return 0;
120
}
121
122
+struct xhci_interrupter *
123
+xhci_create_secondary_interrupter(struct usb_hcd *hcd)
124
+{
125
+    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
126
+    struct xhci_interrupter *ir;
127
+    unsigned int i;
128
+    int err = -ENOSPC;
129
+
130
+    if (!xhci->interrupters)
131
+        return NULL;
132
+
133
+    ir = xhci_alloc_interrupter(xhci, GFP_KERNEL);
134
+    if (!ir)
135
+        return NULL;
136
+
137
+    spin_lock_irq(&xhci->lock);
138
+
139
+    /* Find available secondary interrupter, interrupter 0 is reserved for primary */
140
+    for (i = 1; i < xhci->max_interrupters; i++) {
141
+        if (xhci->interrupters[i] == NULL) {
142
+            err = xhci_add_interrupter(xhci, ir, i);
143
+            break;
144
+        }
145
+    }
88
+    }
146
+
89
+
147
+    spin_unlock_irq(&xhci->lock);
90
+    return 0;
91
+}
92
+EXPORT_SYMBOL_GPL(snd_soc_usb_setup_offload_jack);
148
+
93
+
149
+    if (err) {
94
/**
150
+        xhci_warn(xhci, "Failed to add secondary interrupter, max interrupters %d\n",
95
* snd_soc_usb_find_priv_data() - Retrieve private data stored
151
+             xhci->max_interrupters);
96
* @usbdev: device reference
152
+        xhci_free_interrupter(xhci, ir);
153
+        return NULL;
154
+    }
155
+
156
+    xhci_dbg(xhci, "Add secondary interrupter %d, max interrupters %d\n",
157
+         i, xhci->max_interrupters);
158
+
159
+    return ir;
160
+}
161
+EXPORT_SYMBOL_GPL(xhci_create_secondary_interrupter);
162
+
163
int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
164
{
165
-    dma_addr_t    dma;
166
+    struct xhci_interrupter *ir;
167
    struct device    *dev = xhci_to_hcd(xhci)->self.sysdev;
168
+    dma_addr_t    dma;
169
    unsigned int    val, val2;
170
    u64        val_64;
171
    u32        page_size, temp;
172
@@ -XXX,XX +XXX,XX @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
173
    /* Allocate and set up primary interrupter 0 with an event ring. */
174
    xhci_dbg_trace(xhci, trace_xhci_dbg_init,
175
         "Allocating primary event ring");
176
-    xhci->interrupter = xhci_alloc_interrupter(xhci, flags);
177
-    if (!xhci->interrupter)
178
+    xhci->interrupters = kcalloc_node(xhci->max_interrupters, sizeof(*xhci->interrupters),
179
+                     flags, dev_to_node(dev));
180
+
181
+    ir = xhci_alloc_interrupter(xhci, flags);
182
+    if (!ir)
183
        goto fail;
184
185
-    if (xhci_add_interrupter(xhci, xhci->interrupter, 0))
186
+    if (xhci_add_interrupter(xhci, ir, 0))
187
        goto fail;
188
189
    xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;
190
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
191
index XXXXXXX..XXXXXXX 100644
192
--- a/drivers/usb/host/xhci-ring.c
193
+++ b/drivers/usb/host/xhci-ring.c
194
@@ -XXX,XX +XXX,XX @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
195
    writel(status, &xhci->op_regs->status);
196
197
    /* This is the handler of the primary interrupter */
198
-    ir = xhci->interrupter;
199
+    ir = xhci->interrupters[0];
200
    if (!hcd->msi_enabled) {
201
        u32 irq_pending;
202
        irq_pending = readl(&ir->ir_set->irq_pending);
203
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
204
index XXXXXXX..XXXXXXX 100644
205
--- a/drivers/usb/host/xhci.c
206
+++ b/drivers/usb/host/xhci.c
207
@@ -XXX,XX +XXX,XX @@ static int xhci_init(struct usb_hcd *hcd)
208
209
static int xhci_run_finished(struct xhci_hcd *xhci)
210
{
211
-    struct xhci_interrupter *ir = xhci->interrupter;
212
+    struct xhci_interrupter *ir = xhci->interrupters[0];
213
    unsigned long    flags;
214
    u32        temp;
215
216
@@ -XXX,XX +XXX,XX @@ int xhci_run(struct usb_hcd *hcd)
217
    u64 temp_64;
218
    int ret;
219
    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
220
-    struct xhci_interrupter *ir = xhci->interrupter;
221
+    struct xhci_interrupter *ir = xhci->interrupters[0];
222
    /* Start the xHCI host controller running only after the USB 2.0 roothub
223
     * is setup.
224
     */
225
@@ -XXX,XX +XXX,XX @@ void xhci_stop(struct usb_hcd *hcd)
226
{
227
    u32 temp;
228
    struct xhci_hcd *xhci = hcd_to_xhci(hcd);
229
-    struct xhci_interrupter *ir = xhci->interrupter;
230
+    struct xhci_interrupter *ir = xhci->interrupters[0];
231
232
    mutex_lock(&xhci->mutex);
233
234
@@ -XXX,XX +XXX,XX @@ EXPORT_SYMBOL_GPL(xhci_shutdown);
235
#ifdef CONFIG_PM
236
static void xhci_save_registers(struct xhci_hcd *xhci)
237
{
238
-    struct xhci_interrupter *ir = xhci->interrupter;
239
+    struct xhci_interrupter *ir;
240
+    unsigned int i;
241
242
    xhci->s3.command = readl(&xhci->op_regs->command);
243
    xhci->s3.dev_nt = readl(&xhci->op_regs->dev_notification);
244
    xhci->s3.dcbaa_ptr = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
245
    xhci->s3.config_reg = readl(&xhci->op_regs->config_reg);
246
247
-    if (!ir)
248
-        return;
249
+    /* save both primary and all secondary interrupters */
250
+    /* fixme, shold we lock to prevent race with remove secondary interrupter? */
251
+    for (i = 0; i < xhci->max_interrupters; i++) {
252
+        ir = xhci->interrupters[i];
253
+        if (!ir)
254
+            continue;
255
256
-    ir->s3_erst_size = readl(&ir->ir_set->erst_size);
257
-    ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
258
-    ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
259
-    ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
260
-    ir->s3_irq_control = readl(&ir->ir_set->irq_control);
261
+        ir->s3_erst_size = readl(&ir->ir_set->erst_size);
262
+        ir->s3_erst_base = xhci_read_64(xhci, &ir->ir_set->erst_base);
263
+        ir->s3_erst_dequeue = xhci_read_64(xhci, &ir->ir_set->erst_dequeue);
264
+        ir->s3_irq_pending = readl(&ir->ir_set->irq_pending);
265
+        ir->s3_irq_control = readl(&ir->ir_set->irq_control);
266
+    }
267
}
268
269
static void xhci_restore_registers(struct xhci_hcd *xhci)
270
{
271
-    struct xhci_interrupter *ir = xhci->interrupter;
272
+    struct xhci_interrupter *ir;
273
+    unsigned int i;
274
275
    writel(xhci->s3.command, &xhci->op_regs->command);
276
    writel(xhci->s3.dev_nt, &xhci->op_regs->dev_notification);
277
    xhci_write_64(xhci, xhci->s3.dcbaa_ptr, &xhci->op_regs->dcbaa_ptr);
278
    writel(xhci->s3.config_reg, &xhci->op_regs->config_reg);
279
-    writel(ir->s3_erst_size, &ir->ir_set->erst_size);
280
-    xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
281
-    xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
282
-    writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
283
-    writel(ir->s3_irq_control, &ir->ir_set->irq_control);
284
+
285
+    /* FIXME should we lock to protect against freeing of interrupters */
286
+    for (i = 0; i < xhci->max_interrupters; i++) {
287
+        ir = xhci->interrupters[i];
288
+        if (!ir)
289
+            continue;
290
+
291
+        writel(ir->s3_erst_size, &ir->ir_set->erst_size);
292
+        xhci_write_64(xhci, ir->s3_erst_base, &ir->ir_set->erst_base);
293
+        xhci_write_64(xhci, ir->s3_erst_dequeue, &ir->ir_set->erst_dequeue);
294
+        writel(ir->s3_irq_pending, &ir->ir_set->irq_pending);
295
+        writel(ir->s3_irq_control, &ir->ir_set->irq_control);
296
+    }
297
}
298
299
static void xhci_set_cmd_ring_deq(struct xhci_hcd *xhci)
300
@@ -XXX,XX +XXX,XX @@ int xhci_resume(struct xhci_hcd *xhci, pm_message_t msg)
301
        xhci_dbg(xhci, "// Disabling event ring interrupts\n");
302
        temp = readl(&xhci->op_regs->status);
303
        writel((temp & ~0x1fff) | STS_EINT, &xhci->op_regs->status);
304
-        xhci_disable_interrupter(xhci->interrupter);
305
+        xhci_disable_interrupter(xhci->interrupters[0]);
306
307
        xhci_dbg(xhci, "cleaning up memory\n");
308
        xhci_mem_cleanup(xhci);
309
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
310
index XXXXXXX..XXXXXXX 100644
311
--- a/drivers/usb/host/xhci.h
312
+++ b/drivers/usb/host/xhci.h
313
@@ -XXX,XX +XXX,XX @@ struct xhci_hcd {
314
    struct reset_control *reset;
315
    /* data structures */
316
    struct xhci_device_context_array *dcbaa;
317
-    struct xhci_interrupter *interrupter;
318
+    struct xhci_interrupter **interrupters;
319
    struct xhci_ring    *cmd_ring;
320
    unsigned int cmd_ring_state;
321
#define CMD_RING_STATE_RUNNING (1 << 0)
322
@@ -XXX,XX +XXX,XX @@ struct xhci_container_ctx *xhci_alloc_container_ctx(struct xhci_hcd *xhci,
323
        int type, gfp_t flags);
324
void xhci_free_container_ctx(struct xhci_hcd *xhci,
325
        struct xhci_container_ctx *ctx);
326
+struct xhci_interrupter *xhci_create_secondary_interrupter(struct usb_hcd *hcd);
327
+void xhci_remove_secondary_interrupter(struct usb_hcd
328
+                 *hcd, struct xhci_interrupter *ir);
329
330
/* xHCI host controller glue */
331
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
diff view generated by jsdifflib
1
From: Mathias Nyman <mathias.nyman@linux.intel.com>
1
USB SND needs to know how the USB offload path is being routed. This would
2
allow for applications to open the corresponding sound card and pcm device
3
when it wants to take the audio offload path. This callback should return
4
the mapped indexes based on the USB SND device information.
2
5
3
Expose xhci_stop_endpoint_sync() which is a synchronous variant of
6
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
4
xhci_queue_stop_endpoint(). This is useful for client drivers that are
5
using the secondary interrupters, and need to stop/clean up the current
6
session. The stop endpoint command handler will also take care of cleaning
7
up the ring.
8
9
Modifications to repurpose the new API into existing stop endpoint
10
sequences was implemented by Wesley Cheng.
11
12
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
13
Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com>
14
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
15
---
8
---
16
drivers/usb/host/xhci.c | 61 ++++++++++++++++++++++++++++++-----------
9
include/sound/soc-usb.h | 25 +++++++++++++++++++++++++
17
drivers/usb/host/xhci.h | 2 ++
10
sound/soc/soc-usb.c | 37 +++++++++++++++++++++++++++++++++++++
18
2 files changed, 47 insertions(+), 16 deletions(-)
11
2 files changed, 62 insertions(+)
19
12
20
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
13
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
21
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
22
--- a/drivers/usb/host/xhci.c
15
--- a/include/sound/soc-usb.h
23
+++ b/drivers/usb/host/xhci.c
16
+++ b/include/sound/soc-usb.h
24
@@ -XXX,XX +XXX,XX @@ static int xhci_reserve_bandwidth(struct xhci_hcd *xhci,
17
@@ -XXX,XX +XXX,XX @@
25
    return -ENOMEM;
18
19
#include <sound/soc.h>
20
21
+enum snd_soc_usb_kctl {
22
+    SND_SOC_USB_KCTL_CARD_ROUTE,
23
+    SND_SOC_USB_KCTL_PCM_ROUTE,
24
+};
25
+
26
/**
27
* struct snd_soc_usb_device - SoC USB representation of a USB sound device
28
* @card_idx: sound card index associated with USB device
29
@@ -XXX,XX +XXX,XX @@ struct snd_soc_usb_device {
30
* @list: list head for SND SOC struct list
31
* @component: reference to ASoC component
32
* @connection_status_cb: callback to notify connection events
33
+ * @update_offload_route_info: callback to fetch mapped ASoC card and pcm
34
+ *             device pair. This is unrelated to the concept
35
+ *             of DAPM route. The "route" argument carries
36
+ *             an array used for a kcontrol output for either
37
+ *             the card or pcm index. "path" determines the
38
+ *             which entry to look for. (ie mapped card or pcm)
39
* @priv_data: driver data
40
**/
41
struct snd_soc_usb {
42
@@ -XXX,XX +XXX,XX @@ struct snd_soc_usb {
43
    int (*connection_status_cb)(struct snd_soc_usb *usb,
44
                 struct snd_soc_usb_device *sdev,
45
                 bool connected);
46
+    int (*update_offload_route_info)(struct snd_soc_component *component,
47
+                     int card, int pcm, int direction,
48
+                     enum snd_soc_usb_kctl path,
49
+                     long *route);
50
    void *priv_data;
51
};
52
53
@@ -XXX,XX +XXX,XX @@ void *snd_soc_usb_find_priv_data(struct device *usbdev);
54
55
int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
56
                 struct snd_soc_jack *jack);
57
+int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
58
+                 int direction, enum snd_soc_usb_kctl path,
59
+                 long *route);
60
61
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
62
                     void *data);
63
@@ -XXX,XX +XXX,XX @@ static inline int snd_soc_usb_setup_offload_jack(struct snd_soc_component *compo
64
    return 0;
26
}
65
}
27
66
28
+/*
67
+static int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
29
+ * Synchronous XHCI stop endpoint helper. Issues the stop endpoint command and
68
+                     int direction, enum snd_soc_usb_kctl path,
30
+ * waits for the command completion before returning.
69
+                     long *route)
70
+{
71
+    return -ENODEV;
72
+}
73
+
74
static inline struct snd_soc_usb *
75
snd_soc_usb_allocate_port(struct snd_soc_component *component, void *data)
76
{
77
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
78
index XXXXXXX..XXXXXXX 100644
79
--- a/sound/soc/soc-usb.c
80
+++ b/sound/soc/soc-usb.c
81
@@ -XXX,XX +XXX,XX @@ int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
82
}
83
EXPORT_SYMBOL_GPL(snd_soc_usb_setup_offload_jack);
84
85
+/**
86
+ * snd_soc_usb_update_offload_route - Find active USB offload path
87
+ * @dev: USB device to get offload status
88
+ * @card: USB card index
89
+ * @pcm: USB PCM device index
90
+ * @direction: playback or capture direction
91
+ * @path: pcm or card index
92
+ * @route: pointer to route output array
93
+ *
94
+ * Fetch the current status for the USB SND card and PCM device indexes
95
+ * specified. The "route" argument should be an array of integers being
96
+ * used for a kcontrol output. The first element should have the selected
97
+ * card index, and the second element should have the selected pcm device
98
+ * index.
31
+ */
99
+ */
32
+int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, int suspend,
100
+int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
33
+             gfp_t gfp_flags)
101
+                 int direction, enum snd_soc_usb_kctl path,
102
+                 long *route)
34
+{
103
+{
35
+    struct xhci_command *command;
104
+    struct snd_soc_usb *ctx;
36
+    unsigned long flags;
105
+    int ret = -ENODEV;
37
+    int ret;
38
+
106
+
39
+    command = xhci_alloc_command(xhci, true, GFP_KERNEL);
107
+    mutex_lock(&ctx_mutex);
40
+    if (!command)
108
+    ctx = snd_soc_find_usb_ctx(dev);
41
+        return -ENOMEM;
109
+    if (!ctx)
110
+        goto exit;
42
+
111
+
43
+    spin_lock_irqsave(&xhci->lock, flags);
112
+    if (ctx->update_offload_route_info)
44
+    ret = xhci_queue_stop_endpoint(xhci, command, ep->vdev->slot_id,
113
+        ret = ctx->update_offload_route_info(ctx->component, card, pcm,
45
+                 ep->ep_index, suspend);
114
+                         direction, path, route);
46
+    if (ret < 0) {
115
+exit:
47
+        spin_unlock_irqrestore(&xhci->lock, flags);
116
+    mutex_unlock(&ctx_mutex);
48
+        goto out;
49
+    }
50
+
51
+    xhci_ring_cmd_db(xhci);
52
+    spin_unlock_irqrestore(&xhci->lock, flags);
53
+
54
+    ret = wait_for_completion_timeout(command->completion, msecs_to_jiffies(3000));
55
+    if (!ret)
56
+        xhci_warn(xhci, "%s: Unable to stop endpoint.\n",
57
+                __func__);
58
+
59
+    if (command->status == COMP_COMMAND_ABORTED ||
60
+     command->status == COMP_COMMAND_RING_STOPPED) {
61
+        xhci_warn(xhci, "Timeout while waiting for stop endpoint command\n");
62
+        ret = -ETIME;
63
+    }
64
+out:
65
+    xhci_free_command(xhci, command);
66
+
117
+
67
+    return ret;
118
+    return ret;
68
+}
119
+}
69
120
+EXPORT_SYMBOL_GPL(snd_soc_usb_update_offload_route);
70
/* Issue a configure endpoint command or evaluate context command
71
* and wait for it to finish.
72
@@ -XXX,XX +XXX,XX @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
73
    struct xhci_virt_device *vdev;
74
    struct xhci_virt_ep *ep;
75
    struct xhci_input_control_ctx *ctrl_ctx;
76
-    struct xhci_command *stop_cmd, *cfg_cmd;
77
+    struct xhci_command *cfg_cmd;
78
    unsigned int ep_index;
79
    unsigned long flags;
80
    u32 ep_flag;
81
@@ -XXX,XX +XXX,XX @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
82
    if (ep_flag == SLOT_FLAG || ep_flag == EP0_FLAG)
83
        return;
84
85
-    stop_cmd = xhci_alloc_command(xhci, true, GFP_NOWAIT);
86
-    if (!stop_cmd)
87
-        return;
88
-
89
    cfg_cmd = xhci_alloc_command_with_ctx(xhci, true, GFP_NOWAIT);
90
    if (!cfg_cmd)
91
        goto cleanup;
92
@@ -XXX,XX +XXX,XX @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
93
        goto cleanup;
94
    }
95
96
-    err = xhci_queue_stop_endpoint(xhci, stop_cmd, udev->slot_id,
97
-                    ep_index, 0);
98
+    spin_unlock_irqrestore(&xhci->lock, flags);
99
+
121
+
100
+    err = xhci_stop_endpoint_sync(xhci, ep, 0, GFP_NOWAIT);
122
/**
101
    if (err < 0) {
123
* snd_soc_usb_find_priv_data() - Retrieve private data stored
102
-        spin_unlock_irqrestore(&xhci->lock, flags);
124
* @usbdev: device reference
103
-        xhci_free_command(xhci, cfg_cmd);
104
        xhci_dbg(xhci, "%s: Failed to queue stop ep command, %d ",
105
                __func__, err);
106
        goto cleanup;
107
    }
108
109
-    xhci_ring_cmd_db(xhci);
110
-    spin_unlock_irqrestore(&xhci->lock, flags);
111
-
112
-    wait_for_completion(stop_cmd->completion);
113
-
114
    spin_lock_irqsave(&xhci->lock, flags);
115
-
116
    /* config ep command clears toggle if add and drop ep flags are set */
117
    ctrl_ctx = xhci_get_input_control_ctx(cfg_cmd->in_ctx);
118
    if (!ctrl_ctx) {
119
@@ -XXX,XX +XXX,XX @@ static void xhci_endpoint_reset(struct usb_hcd *hcd,
120
121
    xhci_free_command(xhci, cfg_cmd);
122
cleanup:
123
-    xhci_free_command(xhci, stop_cmd);
124
    spin_lock_irqsave(&xhci->lock, flags);
125
    if (ep->ep_state & EP_SOFT_CLEAR_TOGGLE)
126
        ep->ep_state &= ~EP_SOFT_CLEAR_TOGGLE;
127
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
128
index XXXXXXX..XXXXXXX 100644
129
--- a/drivers/usb/host/xhci.h
130
+++ b/drivers/usb/host/xhci.h
131
@@ -XXX,XX +XXX,XX @@ void xhci_ring_doorbell_for_active_rings(struct xhci_hcd *xhci,
132
void xhci_cleanup_command_queue(struct xhci_hcd *xhci);
133
void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring);
134
unsigned int count_trbs(u64 addr, u64 len);
135
+int xhci_stop_endpoint_sync(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
136
+             int suspend, gfp_t gfp_flags);
137
138
/* xHCI roothub code */
139
void xhci_set_link_state(struct xhci_hcd *xhci, struct xhci_port *port,
diff view generated by jsdifflib
...
...
36
36
37
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
37
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
38
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
39
--- a/sound/soc/soc-usb.c
39
--- a/sound/soc/soc-usb.c
40
+++ b/sound/soc/soc-usb.c
40
+++ b/sound/soc/soc-usb.c
41
@@ -XXX,XX +XXX,XX @@ struct snd_soc_usb *snd_soc_usb_add_port(struct device *dev, void *priv,
41
@@ -XXX,XX +XXX,XX @@ void snd_soc_usb_add_port(struct snd_soc_usb *usb)
42
    mutex_lock(&ctx_mutex);
42
    list_add_tail(&usb->list, &usb_ctx_list);
43
    list_add_tail(&usb->list, &usb_ctx_list);
43
    mutex_unlock(&ctx_mutex);
44
    mutex_unlock(&ctx_mutex);
44
45
+
45
+    snd_usb_rediscover_devices();
46
+    snd_usb_rediscover_devices();
46
+
47
    return usb;
48
}
47
}
49
EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
48
EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
diff view generated by jsdifflib
1
Add a dt-binding to describe the definition of enabling the Q6 USB backend
1
With the introduction of the soc-usb driver, add documentation highlighting
2
device for audio offloading. The node carries information, which is passed
2
details on how to utilize the new driver and how it interacts with
3
along to the QC USB SND class driver counterpart. These parameters will be
3
different components in USB SND and ASoC. It provides examples on how to
4
utilized during QMI stream enable requests.
4
implement the drivers that will need to be introduced in order to enable
5
USB audio offloading.
5
6
6
Reviewed-by: Rob Herring <robh@kernel.org>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
---
8
---
9
.../devicetree/bindings/sound/qcom,q6usb.yaml | 55 +++++++++++++++++++
9
Documentation/sound/soc/index.rst | 1 +
10
1 file changed, 55 insertions(+)
10
Documentation/sound/soc/usb.rst | 482 ++++++++++++++++++++++++++++++
11
create mode 100644 Documentation/devicetree/bindings/sound/qcom,q6usb.yaml
11
2 files changed, 483 insertions(+)
12
create mode 100644 Documentation/sound/soc/usb.rst
12
13
13
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6usb.yaml b/Documentation/devicetree/bindings/sound/qcom,q6usb.yaml
14
diff --git a/Documentation/sound/soc/index.rst b/Documentation/sound/soc/index.rst
15
index XXXXXXX..XXXXXXX 100644
16
--- a/Documentation/sound/soc/index.rst
17
+++ b/Documentation/sound/soc/index.rst
18
@@ -XXX,XX +XXX,XX @@ The documentation is spilt into the following sections:-
19
jack
20
dpcm
21
codec-to-codec
22
+ usb
23
diff --git a/Documentation/sound/soc/usb.rst b/Documentation/sound/soc/usb.rst
14
new file mode 100644
24
new file mode 100644
15
index XXXXXXX..XXXXXXX
25
index XXXXXXX..XXXXXXX
16
--- /dev/null
26
--- /dev/null
17
+++ b/Documentation/devicetree/bindings/sound/qcom,q6usb.yaml
27
+++ b/Documentation/sound/soc/usb.rst
18
@@ -XXX,XX +XXX,XX @@
28
@@ -XXX,XX +XXX,XX @@
19
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
29
+================
20
+%YAML 1.2
30
+ASoC USB support
21
+---
31
+================
22
+$id: http://devicetree.org/schemas/sound/qcom,q6usb.yaml#
32
+
23
+$schema: http://devicetree.org/meta-schemas/core.yaml#
33
+Overview
24
+
34
+========
25
+title: Qualcomm ASoC DPCM USB backend DAI
35
+In order to leverage the existing USB sound device support in ALSA, the
26
+
36
+ASoC USB APIs are introduced to allow the subsystems to exchange
27
+maintainers:
37
+configuration information.
28
+ - Wesley Cheng <quic_wcheng@quicinc.com>
38
+
29
+
39
+One potential use case would be to support USB audio offloading, which is
30
+description:
40
+an implementation that allows for an alternate power-optimized path in the audio
31
+ The USB port is a supported AFE path on the Q6 DSP. This ASoC DPCM
41
+subsystem to handle the transfer of audio data over the USB bus. This would
32
+ backend DAI will communicate the required settings to initialize the
42
+let the main processor to stay in lower power modes for longer duration. The
33
+ XHCI host controller properly for enabling the offloaded audio stream.
43
+following is an example design of how the ASoC and ALSA pieces can be connected
34
+ Parameters defined under this node will carry settings, which will be
44
+together to achieve this:
35
+ passed along during the QMI stream enable request and configuration of
45
+
36
+ the XHCI host controller.
46
+::
37
+
47
+
38
+allOf:
48
+ USB | ASoC
39
+ - $ref: dai-common.yaml#
49
+ | _________________________
40
+
50
+ | | ASoC Platform card |
41
+properties:
51
+ | |_________________________|
42
+ compatible:
52
+ | | |
43
+ enum:
53
+ | ___V____ ____V____
44
+ - qcom,q6usb
54
+ | |ASoC BE | |ASoC FE |
45
+
55
+ | |DAI LNK | |DAI LNK |
46
+ iommus:
56
+ | |________| |_________|
47
+ maxItems: 1
57
+ | ^ ^ ^
48
+
58
+ | | |________|
49
+ "#sound-dai-cells":
59
+ | ___V____ |
50
+ const: 1
60
+ | |SoC-USB | |
51
+
61
+ ________ ________ | | |
52
+ qcom,usb-audio-intr-idx:
62
+ |USB SND |<--->|USBSND |<------------>|________| |
53
+ description:
63
+ |(card.c)| |offld |<---------- |
54
+ Desired XHCI interrupter number to use. Depending on the audio DSP
64
+ |________| |________|___ | | |
55
+ on the platform, it will operate on a specific XHCI interrupter.
65
+ ^ ^ | | | ____________V_________
56
+ $ref: /schemas/types.yaml#/definitions/uint16
66
+ | | | | | |IPC |
57
+ maximum: 8
67
+ __ V_______________V_____ | | | |______________________|
58
+
68
+ |USB SND (endpoint.c) | | | | ^
59
+required:
69
+ |_________________________| | | | |
60
+ - compatible
70
+ ^ | | | ___________V___________
61
+ - "#sound-dai-cells"
71
+ | | | |->|audio DSP |
62
+ - qcom,usb-audio-intr-idx
72
+ ___________V_____________ | | |_______________________|
63
+
73
+ |XHCI HCD |<- |
64
+additionalProperties: false
74
+ |_________________________| |
65
+
75
+
66
+examples:
76
+
67
+ - |
77
+SoC USB driver
68
+ dais {
78
+==============
69
+ compatible = "qcom,q6usb";
79
+Structures
70
+ #sound-dai-cells = <1>;
80
+----------
71
+ iommus = <&apps_smmu 0x180f 0x0>;
81
+``struct snd_soc_usb``
72
+ qcom,usb-audio-intr-idx = /bits/ 16 <2>;
82
+
73
+ };
83
+ - ``list``: list head for SND SoC struct list
84
+ - ``component``: reference to ASoC component
85
+ - ``connection_status_cb``: callback to notify connection events
86
+ - ``update_offload_route_info``: callback to fetch selected USB sound card/PCM
87
+ device
88
+ - ``priv_data``: driver data
89
+
90
+The snd_soc_usb structure can be referenced using the ASoC platform card
91
+device, or a USB device (udev->dev). This is created by the ASoC BE DAI
92
+link, and the USB sound entity will be able to pass information to the
93
+ASoC BE DAI link using this structure.
94
+
95
+``struct snd_soc_usb_device``
96
+
97
+ - ``card_idx``: sound card index associated with USB sound device
98
+ - ``chip_idx``: USB sound chip array index
99
+ - ``cpcm_idx``: capture pcm device indexes associated with the USB sound device
100
+ - ``ppcm_idx``: playback pcm device indexes associated with the USB sound device
101
+ - ``num_playback``: number of playback streams
102
+ - ``num_capture``: number of capture streams
103
+ - ``list``: list head for the USB sound device list
104
+
105
+The struct snd_soc_usb_device is created by the USB sound offload driver.
106
+This will carry basic parameters/limitations that will be used to
107
+determine the possible offloading paths for this USB audio device.
108
+
109
+Functions
110
+---------
111
+.. code-block:: rst
112
+
113
+    int snd_soc_usb_find_supported_format(int card_idx,
114
+            struct snd_pcm_hw_params *params, int direction)
115
+..
116
+
117
+ - ``card_idx``: the index into the USB sound chip array.
118
+ - ``params``: Requested PCM parameters from the USB DPCM BE DAI link
119
+ - ``direction``: capture or playback
120
+
121
+**snd_soc_usb_find_supported_format()** ensures that the requested audio profile
122
+being requested by the external DSP is supported by the USB device.
123
+
124
+Returns 0 on success, and -EOPNOTSUPP on failure.
125
+
126
+.. code-block:: rst
127
+
128
+    int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
129
+..
130
+
131
+ - ``usbdev``: the usb device that was discovered
132
+ - ``sdev``: capabilities of the device
133
+
134
+**snd_soc_usb_connect()** notifies the ASoC USB DCPM BE DAI link of a USB
135
+audio device detection. This can be utilized in the BE DAI
136
+driver to keep track of available USB audio devices. This is intended
137
+to be called by the USB offload driver residing in USB SND.
138
+
139
+Returns 0 on success, negative error code on failure.
140
+
141
+.. code-block:: rst
142
+
143
+    int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
144
+..
145
+
146
+ - ``usbdev``: the usb device that was removed
147
+ - ``sdev``: capabilities to free
148
+
149
+**snd_soc_usb_disconnect()** notifies the ASoC USB DCPM BE DAI link of a USB
150
+audio device removal. This is intended to be called by the USB offload
151
+driver that resides in USB SND.
152
+
153
+.. code-block:: rst
154
+
155
+    void *snd_soc_usb_find_priv_data(struct device *usbdev)
156
+..
157
+
158
+ - ``usbdev``: the usb device to reference to find private data
159
+
160
+**snd_soc_usb_find_priv_data()** fetches the private data saved to the SoC USB
161
+device.
162
+
163
+Returns pointer to priv_data on success, NULL on failure.
164
+
165
+.. code-block:: rst
166
+
167
+    int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
168
+                    struct snd_soc_jack *jack)
169
+..
170
+
171
+ - ``component``: ASoC component to add the jack
172
+ - ``jack``: jack component to populate
173
+
174
+**snd_soc_usb_setup_offload_jack()** is a helper to add a sound jack control to
175
+the platform sound card. This will allow for consistent naming to be used on
176
+designs that support USB audio offloading. Additionally, this will enable the
177
+jack to notify of changes.
178
+
179
+Returns 0 on success, negative otherwise.
180
+
181
+.. code-block:: rst
182
+
183
+    int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
184
+                     int direction, enum snd_soc_usb_kctl path,
185
+                     long *route)
186
+..
187
+
188
+ - ``dev``: USB device to look up offload path mapping
189
+ - ``card``: USB sound card index
190
+ - ``pcm``: USB sound PCM device index
191
+ - ``direction``: direction to fetch offload routing information
192
+ - ``path``: kcontrol selector - pcm device or card index
193
+ - ``route``: mapping of sound card and pcm indexes for the offload path. This is
194
+     an array of two integers that will carry the card and pcm device indexes
195
+     in that specific order. This can be used as the array for the kcontrol
196
+     output.
197
+
198
+**snd_soc_usb_update_offload_route()** calls a registered callback to the USB BE DAI
199
+link to fetch the information about the mapped ASoC devices for executing USB audio
200
+offload for the device. ``route`` may be a pointer to a kcontrol value output array,
201
+which carries values when the kcontrol is read.
202
+
203
+Returns 0 on success, negative otherwise.
204
+
205
+.. code-block:: rst
206
+
207
+    struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
208
+            void *data);
209
+..
210
+
211
+ - ``component``: DPCM BE DAI link component
212
+ - ``data``: private data
213
+
214
+**snd_soc_usb_allocate_port()** allocates a SoC USB device and populates standard
215
+parameters that is used for further operations.
216
+
217
+Returns a pointer to struct soc_usb on success, negative on error.
218
+
219
+.. code-block:: rst
220
+
221
+    void snd_soc_usb_free_port(struct snd_soc_usb *usb);
222
+..
223
+
224
+ - ``usb``: SoC USB device to free
225
+
226
+**snd_soc_usb_free_port()** frees a SoC USB device.
227
+
228
+.. code-block:: rst
229
+
230
+    void snd_soc_usb_add_port(struct snd_soc_usb *usb);
231
+..
232
+
233
+ - ``usb``: SoC USB device to add
234
+
235
+**snd_soc_usb_add_port()** add an allocated SoC USB device to the SOC USB framework.
236
+Once added, this device can be referenced by further operations.
237
+
238
+.. code-block:: rst
239
+
240
+    void snd_soc_usb_remove_port(struct snd_soc_usb *usb);
241
+..
242
+
243
+ - ``usb``: SoC USB device to remove
244
+
245
+**snd_soc_usb_remove_port()** removes a SoC USB device from the SoC USB framework.
246
+After removing a device, any SOC USB operations would not be able to reference the
247
+device removed.
248
+
249
+How to Register to SoC USB
250
+--------------------------
251
+The ASoC DPCM USB BE DAI link is the entity responsible for allocating and
252
+registering the SoC USB device on the component bind. Likewise, it will
253
+also be responsible for freeing the allocated resources. An example can
254
+be shown below:
255
+
256
+.. code-block:: rst
257
+
258
+    static int q6usb_component_probe(struct snd_soc_component *component)
259
+    {
260
+        ...
261
+        data->usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
262
+        if (!data->usb)
263
+            return -ENOMEM;
264
+
265
+        usb->connection_status_cb = q6usb_alsa_connection_cb;
266
+
267
+        ret = snd_soc_usb_add_port(usb);
268
+        if (ret < 0) {
269
+            dev_err(component->dev, "failed to add usb port\n");
270
+            goto free_usb;
271
+        }
272
+        ...
273
+    }
274
+
275
+    static void q6usb_component_remove(struct snd_soc_component *component)
276
+    {
277
+        ...
278
+        snd_soc_usb_remove_port(data->usb);
279
+        snd_soc_usb_free_port(data->usb);
280
+    }
281
+
282
+    static const struct snd_soc_component_driver q6usb_dai_component = {
283
+        .probe = q6usb_component_probe,
284
+        .remove = q6usb_component_remove,
285
+        .name = "q6usb-dai-component",
286
+        ...
287
+    };
288
+..
289
+
290
+BE DAI links can pass along vendor specific information as part of the
291
+call to allocate the SoC USB device. This will allow any BE DAI link
292
+parameters or settings to be accessed by the USB offload driver that
293
+resides in USB SND.
294
+
295
+USB Audio Device Connection Flow
296
+--------------------------------
297
+USB devices can be hotplugged into the USB ports at any point in time.
298
+The BE DAI link should be aware of the current state of the physical USB
299
+port, i.e. if there are any USB devices with audio interface(s) connected.
300
+connection_status_cb() can be used to notify the BE DAI link of any change.
301
+
302
+This is called whenever there is a USB SND interface bind or remove event,
303
+using snd_soc_usb_connect() or snd_soc_usb_disconnect():
304
+
305
+.. code-block:: rst
306
+
307
+    static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
308
+    {
309
+        ...
310
+        snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
311
+        ...
312
+    }
313
+
314
+    static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
315
+    {
316
+        ...
317
+        snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev);
318
+        ...
319
+    }
320
+..
321
+
322
+In order to account for conditions where driver or device existence is
323
+not guaranteed, USB SND exposes snd_usb_rediscover_devices() to resend the
324
+connect events for any identified USB audio interfaces. Consider the
325
+the following situation:
326
+
327
+    **usb_audio_probe()**
328
+     | --> USB audio streams allocated and saved to usb_chip[]
329
+     | --> Propagate connect event to USB offload driver in USB SND
330
+     | --> **snd_soc_usb_connect()** exits as USB BE DAI link is not ready
331
+
332
+    BE DAI link component probe
333
+     | --> DAI link is probed and SoC USB port is allocated
334
+     | --> The USB audio device connect event is missed
335
+
336
+To ensure connection events are not missed, **snd_usb_rediscover_devices()**
337
+is executed when the SoC USB device is registered. Now, when the BE DAI
338
+link component probe occurs, the following highlights the sequence:
339
+
340
+    BE DAI link component probe
341
+     | --> DAI link is probed and SoC USB port is allocated
342
+     | --> SoC USB device added, and **snd_usb_rediscover_devices()** runs
343
+
344
+    **snd_usb_rediscover_devices()**
345
+     | --> Traverses through usb_chip[] and for non-NULL entries issue
346
+     | **connection_status_cb()**
347
+
348
+In the case where the USB offload driver is unbound, while USB SND is ready,
349
+the **snd_usb_rediscover_devices()** is called during module init. This allows
350
+for the offloading path to also be enabled with the following flow:
351
+
352
+    **usb_audio_probe()**
353
+     | --> USB audio streams allocated and saved to usb_chip[]
354
+     | --> Propagate connect event to USB offload driver in USB SND
355
+     | --> USB offload driver **NOT** ready!
356
+
357
+    BE DAI link component probe
358
+     | --> DAI link is probed and SoC USB port is allocated
359
+     | --> No USB connect event due to missing USB offload driver
360
+
361
+    USB offload driver probe
362
+     | --> **qc_usb_audio_offload_init()**
363
+     | --> Calls **snd_usb_rediscover_devices()** to notify of devices
364
+
365
+USB Offload Related Kcontrols
366
+=============================
367
+Details
368
+-------
369
+A set of kcontrols can be utilized by applications to help select the proper sound
370
+devices to enable USB audio offloading. SoC USB exposes the get_offload_dev()
371
+callback that designs can use to ensure that the proper indices are returned to the
372
+application.
373
+
374
+Implementation
375
+--------------
376
+
377
+**Example:**
378
+
379
+ **Sound Cards**:
380
+
381
+    ::
382
+
383
+     0 [SM8250MTPWCD938]: sm8250 - SM8250-MTP-WCD9380-WSA8810-VA-D
384
+                        SM8250-MTP-WCD9380-WSA8810-VA-DMIC
385
+     1 [Seri ]: USB-Audio - Plantronics Blackwire 3225 Seri
386
+                        Plantronics Plantronics Blackwire
387
+                        3225 Seri at usb-xhci-hcd.1.auto-1.1,
388
+                        full sp
389
+     2 [C320M ]: USB-Audio - Plantronics C320-M
390
+ Plantronics Plantronics C320-M at usb-xhci-hcd.1.auto-1.2, full speed
391
+
392
+ **PCM Devices**:
393
+
394
+    ::
395
+
396
+     card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 0: MultiMedia1 (*) []
397
+     Subdevices: 1/1
398
+     Subdevice #0: subdevice #0
399
+     card 0: SM8250MTPWCD938 [SM8250-MTP-WCD9380-WSA8810-VA-D], device 1: MultiMedia2 (*) []
400
+     Subdevices: 1/1
401
+     Subdevice #0: subdevice #0
402
+     card 1: Seri [Plantronics Blackwire 3225 Seri], device 0: USB Audio [USB Audio]
403
+     Subdevices: 1/1
404
+     Subdevice #0: subdevice #0
405
+     card 2: C320M [Plantronics C320-M], device 0: USB Audio [USB Audio]
406
+     Subdevices: 1/1
407
+     Subdevice #0: subdevice #0
408
+
409
+ **USB Sound Card** - card#1:
410
+
411
+    ::
412
+
413
+     USB Offload Playback Card Route PCM#0 -1 (range -1->32)
414
+     USB Offload Playback PCM Route PCM#0 -1 (range -1->255)
415
+
416
+ **USB Sound Card** - card#2:
417
+
418
+    ::
419
+
420
+     USB Offload Playback Card Route PCM#0 0 (range -1->32)
421
+     USB Offload Playback PCM Route PCM#0 1 (range -1->255)
422
+
423
+The above example shows a scenario where the system has one ASoC platform card
424
+(card#0) and two USB sound devices connected (card#1 and card#2). When reading
425
+the available kcontrols for each USB audio device, the following kcontrols lists
426
+the mapped offload card and pcm device indexes for the specific USB device:
427
+
428
+    ``USB Offload Playback Card Route PCM#*``
429
+
430
+    ``USB Offload Playback PCM Route PCM#*``
431
+
432
+The kcontrol is indexed, because a USB audio device could potentially have
433
+several PCM devices. The above kcontrols are defined as:
434
+
435
+ - ``USB Offload Playback Card Route PCM#`` **(R)**: Returns the ASoC platform sound
436
+ card index for a mapped offload path. The output **"0"** (card index) signifies
437
+ that there is an available offload path for the USB SND device through card#0.
438
+ If **"-1"** is seen, then no offload path is available for the USB SND device.
439
+ This kcontrol exists for each USB audio device that exists in the system, and
440
+ its expected to derive the current status of offload based on the output value
441
+ for the kcontrol along with the PCM route kcontrol.
442
+
443
+ - ``USB Offload Playback PCM Route PCM#`` **(R)**: Returns the ASoC platform sound
444
+ PCM device index for a mapped offload path. The output **"1"** (PCM device index)
445
+ signifies that there is an available offload path for the USB SND device through
446
+ PCM device#0. If **"-1"** is seen, then no offload path is available for the USB\
447
+ SND device. This kcontrol exists for each USB audio device that exists in the
448
+ system, and its expected to derive the current status of offload based on the
449
+ output value for this kcontrol, in addition to the card route kcontrol.
450
+
451
+USB Offload Playback Route Kcontrol
452
+-----------------------------------
453
+In order to allow for vendor specific implementations on audio offloading device
454
+selection, the SoC USB layer exposes the following:
455
+
456
+.. code-block:: rst
457
+
458
+    int (*update_offload_route_info)(struct snd_soc_component *component,
459
+                     int card, int pcm, int direction,
460
+                     enum snd_soc_usb_kctl path,
461
+                     long *route)
462
+..
463
+
464
+These are specific for the **USB Offload Playback Card Route PCM#** and **USB
465
+Offload PCM Route PCM#** kcontrols.
466
+
467
+When users issue get calls to the kcontrol, the registered SoC USB callbacks will
468
+execute the registered function calls to the DPCM BE DAI link.
469
+
470
+**Callback Registration:**
471
+
472
+.. code-block:: rst
473
+
474
+    static int q6usb_component_probe(struct snd_soc_component *component)
475
+    {
476
+    ...
477
+    usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
478
+    if (IS_ERR(usb))
479
+        return -ENOMEM;
480
+
481
+    usb->connection_status_cb = q6usb_alsa_connection_cb;
482
+    usb->update_offload_route_info = q6usb_get_offload_dev;
483
+
484
+    ret = snd_soc_usb_add_port(usb);
485
+..
486
+
487
+Existing USB Sound Kcontrol
488
+---------------------------
489
+With the introduction of USB offload support, the above USB offload kcontrol
490
+will be added to the pre existing list of kcontrols identified by the USB sound
491
+framework. These kcontrols are still the main controls that are used to
492
+modify characteristics pertaining to the USB audio device.
493
+
494
+    ::
495
+
496
+     Number of controls: 9
497
+     ctl type num name value
498
+     0 INT 2 Capture Channel Map 0, 0 (range 0->36)
499
+     1 INT 2 Playback Channel Map 0, 0 (range 0->36)
500
+     2 BOOL 1 Headset Capture Switch On
501
+     3 INT 1 Headset Capture Volume 10 (range 0->13)
502
+     4 BOOL 1 Sidetone Playback Switch On
503
+     5 INT 1 Sidetone Playback Volume 4096 (range 0->8192)
504
+     6 BOOL 1 Headset Playback Switch On
505
+     7 INT 2 Headset Playback Volume 20, 20 (range 0->24)
506
+     8 INT 1 USB Offload Playback Card Route PCM#0 0 (range -1->32)
507
+     9 INT 1 USB Offload Playback PCM Route PCM#0 1 (range -1->255)
508
+
509
+Since USB audio device controls are handled over the USB control endpoint, use the
510
+existing mechanisms present in the USB mixer to set parameters, such as volume.
diff view generated by jsdifflib
1
Q6DSP supports handling of USB playback audio data if USB audio offloading
1
Q6DSP supports handling of USB playback audio data if USB audio offloading
2
is enabled. Add a new definition for the USB_RX AFE port, which is
2
is enabled. Add a new definition for the USB_RX AFE port, which is
3
referenced when the AFE port is started.
3
referenced when the AFE port is started.
4
4
5
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
5
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
7
---
8
include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 1 +
8
include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h | 1 +
9
1 file changed, 1 insertion(+)
9
1 file changed, 1 insertion(+)
10
10
11
diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
11
diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
12
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
13
--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
13
--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
14
+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
14
+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
15
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@
16
#define DISPLAY_PORT_RX_5    133
16
#define DISPLAY_PORT_RX_5    133
17
#define DISPLAY_PORT_RX_6    134
17
#define DISPLAY_PORT_RX_6    134
18
#define DISPLAY_PORT_RX_7    135
18
#define DISPLAY_PORT_RX_7    135
19
+#define USB_RX            136
19
+#define USB_RX            136
20
20
21
#define LPASS_CLK_ID_PRI_MI2S_IBIT    1
21
#define LPASS_CLK_ID_PRI_MI2S_IBIT    1
22
#define LPASS_CLK_ID_PRI_MI2S_EBIT    2
22
#define LPASS_CLK_ID_PRI_MI2S_EBIT    2
diff view generated by jsdifflib
...
...
30
+ platform {
30
+ platform {
31
+ sound-dai = <&q6routing>;
31
+ sound-dai = <&q6routing>;
32
+ };
32
+ };
33
+ };
33
+ };
34
};
34
};
35
36
- |
diff view generated by jsdifflib
1
The QC ADSP is able to support USB playback endpoints, so that the main
1
The QC ADSP is able to support USB playback endpoints, so that the main
2
application processor can be placed into lower CPU power modes. This adds
2
application processor can be placed into lower CPU power modes. This adds
3
the required AFE port configurations and port start command to start an
3
the required AFE port configurations and port start command to start an
4
audio session.
4
audio session.
5
5
6
Specifically, the QC ADSP can support all potential endpoints that are
6
Specifically, the QC ADSP can support all potential endpoints that are
7
exposed by the audio data interface. This includes, feedback endpoints
7
exposed by the audio data interface. This includes isochronous data
8
(both implicit and explicit) as well as the isochronous (data) endpoints.
8
endpoints, in either synchronous mode or asynchronous mode. In the latter
9
The size of audio samples sent per USB frame (microframe) will be adjusted
9
case both implicit or explicit feedback endpoints are supported. The size
10
based on information received on the feedback endpoint.
10
of audio samples sent per USB frame (microframe) will be adjusted based on
11
information received on the feedback endpoint.
12
13
Some pre-requisites are needed before issuing the AFE port start command,
14
such as setting the USB AFE dev_token. This carries information about the
15
available USB SND cards and PCM devices that have been discovered on the
16
USB bus. The dev_token field is used by the audio DSP to notify the USB
17
offload driver of which card and PCM index to enable playback on.
11
18
12
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
19
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
13
---
20
---
14
sound/soc/qcom/qdsp6/q6afe-dai.c | 56 +++++++
21
sound/soc/qcom/qdsp6/q6afe-dai.c | 60 +++++++
15
sound/soc/qcom/qdsp6/q6afe.c | 183 ++++++++++++++++++++++-
22
sound/soc/qcom/qdsp6/q6afe.c | 192 ++++++++++++++++++++++-
16
sound/soc/qcom/qdsp6/q6afe.h | 35 ++++-
23
sound/soc/qcom/qdsp6/q6afe.h | 36 ++++-
17
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 23 +++
24
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c | 23 +++
18
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h | 1 +
25
sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h | 1 +
19
sound/soc/qcom/qdsp6/q6routing.c | 9 ++
26
sound/soc/qcom/qdsp6/q6routing.c | 32 +++-
20
6 files changed, 305 insertions(+), 2 deletions(-)
27
6 files changed, 341 insertions(+), 3 deletions(-)
21
28
22
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
29
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
23
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
24
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
31
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
25
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
32
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
26
@@ -XXX,XX +XXX,XX @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
33
@@ -XXX,XX +XXX,XX @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
27
    return 0;
34
    return 0;
28
}
35
}
29
36
30
+static int q6usb_hw_params(struct snd_pcm_substream *substream,
37
+static int q6afe_usb_hw_params(struct snd_pcm_substream *substream,
31
+             struct snd_pcm_hw_params *params,
38
+             struct snd_pcm_hw_params *params,
32
+             struct snd_soc_dai *dai)
39
+             struct snd_soc_dai *dai)
33
+{
40
+{
34
+    struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
41
+    struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
35
+    int channels = params_channels(params);
42
+    int channels = params_channels(params);
36
+    int rate = params_rate(params);
43
+    int rate = params_rate(params);
37
+    struct q6afe_usb_cfg *usb = &dai_data->port_config[dai->id].usb_audio;
44
+    struct q6afe_usb_cfg *usb = &dai_data->port_config[dai->id].usb_audio;
...
...
40
+    usb->num_channels = channels;
47
+    usb->num_channels = channels;
41
+
48
+
42
+    switch (params_format(params)) {
49
+    switch (params_format(params)) {
43
+    case SNDRV_PCM_FORMAT_U16_LE:
50
+    case SNDRV_PCM_FORMAT_U16_LE:
44
+    case SNDRV_PCM_FORMAT_S16_LE:
51
+    case SNDRV_PCM_FORMAT_S16_LE:
45
+    case SNDRV_PCM_FORMAT_SPECIAL:
46
+        usb->bit_width = 16;
52
+        usb->bit_width = 16;
47
+        break;
53
+        break;
48
+    case SNDRV_PCM_FORMAT_S24_LE:
54
+    case SNDRV_PCM_FORMAT_S24_LE:
49
+    case SNDRV_PCM_FORMAT_S24_3LE:
55
+    case SNDRV_PCM_FORMAT_S24_3LE:
50
+        usb->bit_width = 24;
56
+        usb->bit_width = 24;
...
...
87
static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
93
static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
88
@@ -XXX,XX +XXX,XX @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
94
@@ -XXX,XX +XXX,XX @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
89
    return 0;
95
    return 0;
90
}
96
}
91
97
92
+static const struct snd_soc_dai_ops q6usb_ops = {
98
+static const struct snd_soc_dai_ops q6afe_usb_ops = {
93
+    .probe        = msm_dai_q6_dai_probe,
99
+    .probe        = msm_dai_q6_dai_probe,
94
+    .prepare    = q6afe_dai_prepare,
100
+    .prepare    = q6afe_dai_prepare,
95
+    .hw_params    = q6usb_hw_params,
101
+    .hw_params    = q6afe_usb_hw_params,
102
+    /*
103
+     * Shutdown callback required to stop the USB AFE port, which is enabled
104
+     * by the prepare() stage. This stops the audio traffic on the USB AFE
105
+     * port on the Q6DSP.
106
+     */
96
+    .shutdown    = q6afe_dai_shutdown,
107
+    .shutdown    = q6afe_dai_shutdown,
97
+    /*
108
+    /*
98
+     * Startup callback not needed, as AFE port start command passes the PCM
109
+     * Startup callback not needed, as AFE port start command passes the PCM
99
+     * parameters within the AFE command, which is provided by the PCM core
110
+     * parameters within the AFE command, which is provided by the PCM core
100
+     * during the prepare() stage.
111
+     * during the prepare() stage.
...
...
115
static const struct snd_soc_component_driver q6afe_dai_component = {
126
static const struct snd_soc_component_driver q6afe_dai_component = {
116
@@ -XXX,XX +XXX,XX @@ static int q6afe_dai_dev_probe(struct platform_device *pdev)
127
@@ -XXX,XX +XXX,XX @@ static int q6afe_dai_dev_probe(struct platform_device *pdev)
117
    cfg.q6i2s_ops = &q6i2s_ops;
128
    cfg.q6i2s_ops = &q6i2s_ops;
118
    cfg.q6tdm_ops = &q6tdm_ops;
129
    cfg.q6tdm_ops = &q6tdm_ops;
119
    cfg.q6dma_ops = &q6dma_ops;
130
    cfg.q6dma_ops = &q6dma_ops;
120
+    cfg.q6usb_ops = &q6usb_ops;
131
+    cfg.q6usb_ops = &q6afe_usb_ops;
121
    dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
132
    dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
122
133
123
    return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais);
134
    return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais);
124
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
135
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
125
index XXXXXXX..XXXXXXX 100644
136
index XXXXXXX..XXXXXXX 100644
...
...
229
+struct afe_param_id_usb_audio_dev_lpcm_fmt {
240
+struct afe_param_id_usb_audio_dev_lpcm_fmt {
230
+    u32    cfg_minor_version;
241
+    u32    cfg_minor_version;
231
+    u32    endian;
242
+    u32    endian;
232
+} __packed;
243
+} __packed;
233
+
244
+
234
+/**
235
+ * struct afe_param_id_usb_audio_dev_latency_mode
236
+ * @cfg_minor_version: Minor version used for tracking USB audio device
237
+ * configuration.
238
+ * Supported values:
239
+ * AFE_API_MINOR_VERSION_USB_AUDIO_LATENCY_MODE
240
+ * @mode: latency mode for the USB audio device
241
+ **/
242
+struct afe_param_id_usb_audio_dev_latency_mode {
243
+    u32    minor_version;
244
+    u32    mode;
245
+} __packed;
246
+
247
+#define AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL 0x000102B7
245
+#define AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL 0x000102B7
248
+
246
+
249
+/**
247
+/**
250
+ * struct afe_param_id_usb_audio_svc_interval
248
+ * struct afe_param_id_usb_audio_svc_interval
251
+ * @cfg_minor_version: Minor version used for tracking USB audio device
249
+ * @cfg_minor_version: Minor version used for tracking USB audio device
...
...
279
static void q6afe_port_free(struct kref *ref)
277
static void q6afe_port_free(struct kref *ref)
280
@@ -XXX,XX +XXX,XX @@ void q6afe_tdm_port_prepare(struct q6afe_port *port,
278
@@ -XXX,XX +XXX,XX @@ void q6afe_tdm_port_prepare(struct q6afe_port *port,
281
}
279
}
282
EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare);
280
EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare);
283
281
284
+static int afe_port_send_usb_dev_param(struct q6afe_port *port, struct q6afe_usb_cfg *cfg)
282
+/**
283
+ * afe_port_send_usb_dev_param() - Send USB dev token
284
+ *
285
+ * @port: Instance of afe port
286
+ * @cardidx: USB SND card index to reference
287
+ * @pcmidx: USB SND PCM device index to reference
288
+ *
289
+ * The USB dev token carries information about which USB SND card instance and
290
+ * PCM device to execute the offload on. This information is carried through
291
+ * to the stream enable QMI request, which is handled by the offload class
292
+ * driver. The information is parsed to determine which USB device to query
293
+ * the required resources for.
294
+ */
295
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx)
296
+{
297
+    struct afe_param_id_usb_audio_dev_params usb_dev;
298
+    int ret;
299
+
300
+    memset(&usb_dev, 0, sizeof(usb_dev));
301
+
302
+    usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
303
+    usb_dev.dev_token = (cardidx << 16) | (pcmidx << 8);
304
+    ret = q6afe_port_set_param_v2(port, &usb_dev,
305
+                 AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS,
306
+                 AFE_MODULE_AUDIO_DEV_INTERFACE,
307
+                 sizeof(usb_dev));
308
+    if (ret)
309
+        dev_err(port->afe->dev, "%s: AFE device param cmd failed %d\n",
310
+            __func__, ret);
311
+
312
+    return ret;
313
+}
314
+EXPORT_SYMBOL_GPL(afe_port_send_usb_dev_param);
315
+
316
+static int afe_port_send_usb_params(struct q6afe_port *port, struct q6afe_usb_cfg *cfg)
285
+{
317
+{
286
+    union afe_port_config *pcfg = &port->port_cfg;
318
+    union afe_port_config *pcfg = &port->port_cfg;
287
+    struct afe_param_id_usb_audio_dev_params usb_dev;
288
+    struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt;
319
+    struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt;
289
+    struct afe_param_id_usb_audio_svc_interval svc_int;
320
+    struct afe_param_id_usb_audio_svc_interval svc_int;
290
+    int ret = 0;
321
+    int ret;
291
+
322
+
292
+    if (!pcfg) {
323
+    if (!pcfg) {
293
+        dev_err(port->afe->dev, "%s: Error, no configuration data\n", __func__);
324
+        dev_err(port->afe->dev, "%s: Error, no configuration data\n", __func__);
294
+        ret = -EINVAL;
325
+        ret = -EINVAL;
295
+        goto exit;
326
+        goto exit;
296
+    }
327
+    }
297
+
328
+
298
+    memset(&usb_dev, 0, sizeof(usb_dev));
299
+    memset(&lpcm_fmt, 0, sizeof(lpcm_fmt));
329
+    memset(&lpcm_fmt, 0, sizeof(lpcm_fmt));
300
+    memset(&svc_int, 0, sizeof(svc_int));
330
+    memset(&svc_int, 0, sizeof(svc_int));
301
+
302
+    usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
303
+    ret = q6afe_port_set_param_v2(port, &usb_dev,
304
+                 AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS,
305
+                 AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(usb_dev));
306
+    if (ret) {
307
+        dev_err(port->afe->dev, "%s: AFE device param cmd failed %d\n",
308
+            __func__, ret);
309
+        goto exit;
310
+    }
311
+
331
+
312
+    lpcm_fmt.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
332
+    lpcm_fmt.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
313
+    lpcm_fmt.endian = pcfg->usb_cfg.endian;
333
+    lpcm_fmt.endian = pcfg->usb_cfg.endian;
314
+    ret = q6afe_port_set_param_v2(port, &lpcm_fmt,
334
+    ret = q6afe_port_set_param_v2(port, &lpcm_fmt,
315
+                 AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT,
335
+                 AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT,
...
...
339
+ * @port: Instance of afe port
359
+ * @port: Instance of afe port
340
+ * @cfg: USB configuration for the afe port
360
+ * @cfg: USB configuration for the afe port
341
+ *
361
+ *
342
+ */
362
+ */
343
+void q6afe_usb_port_prepare(struct q6afe_port *port,
363
+void q6afe_usb_port_prepare(struct q6afe_port *port,
344
+             struct q6afe_usb_cfg *cfg)
364
+             struct q6afe_usb_cfg *cfg)
345
+{
365
+{
346
+    union afe_port_config *pcfg = &port->port_cfg;
366
+    union afe_port_config *pcfg = &port->port_cfg;
347
+
367
+
348
+    pcfg->usb_cfg.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
368
+    pcfg->usb_cfg.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
349
+    pcfg->usb_cfg.sample_rate = cfg->sample_rate;
369
+    pcfg->usb_cfg.sample_rate = cfg->sample_rate;
350
+    pcfg->usb_cfg.num_channels = cfg->num_channels;
370
+    pcfg->usb_cfg.num_channels = cfg->num_channels;
351
+    pcfg->usb_cfg.bit_width = cfg->bit_width;
371
+    pcfg->usb_cfg.bit_width = cfg->bit_width;
352
+
372
+
353
+    afe_port_send_usb_dev_param(port, cfg);
373
+    afe_port_send_usb_params(port, cfg);
354
+}
374
+}
355
+EXPORT_SYMBOL_GPL(q6afe_usb_port_prepare);
375
+EXPORT_SYMBOL_GPL(q6afe_usb_port_prepare);
356
+
376
+
357
/**
377
/**
358
* q6afe_hdmi_port_prepare() - Prepare hdmi afe port.
378
* q6afe_hdmi_port_prepare() - Prepare hdmi afe port.
...
...
372
diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h
392
diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h
373
index XXXXXXX..XXXXXXX 100644
393
index XXXXXXX..XXXXXXX 100644
374
--- a/sound/soc/qcom/qdsp6/q6afe.h
394
--- a/sound/soc/qcom/qdsp6/q6afe.h
375
+++ b/sound/soc/qcom/qdsp6/q6afe.h
395
+++ b/sound/soc/qcom/qdsp6/q6afe.h
376
@@ -XXX,XX +XXX,XX @@
396
@@ -XXX,XX +XXX,XX @@
377
397
#ifndef __Q6AFE_H__
378
#include <dt-bindings/sound/qcom,q6afe.h>
398
#define __Q6AFE_H__
379
399
380
-#define AFE_PORT_MAX        129
400
-#define AFE_PORT_MAX        129
381
+#define AFE_PORT_MAX        137
401
+#define AFE_PORT_MAX        137
382
402
383
#define MSM_AFE_PORT_TYPE_RX 0
403
#define MSM_AFE_PORT_TYPE_RX 0
...
...
430
@@ -XXX,XX +XXX,XX @@ int q6afe_port_start(struct q6afe_port *port);
450
@@ -XXX,XX +XXX,XX @@ int q6afe_port_start(struct q6afe_port *port);
431
int q6afe_port_stop(struct q6afe_port *port);
451
int q6afe_port_stop(struct q6afe_port *port);
432
void q6afe_port_put(struct q6afe_port *port);
452
void q6afe_port_put(struct q6afe_port *port);
433
int q6afe_get_port_id(int index);
453
int q6afe_get_port_id(int index);
434
+void q6afe_usb_port_prepare(struct q6afe_port *port,
454
+void q6afe_usb_port_prepare(struct q6afe_port *port,
435
+             struct q6afe_usb_cfg *cfg);
455
+             struct q6afe_usb_cfg *cfg);
436
void q6afe_hdmi_port_prepare(struct q6afe_port *port,
456
void q6afe_hdmi_port_prepare(struct q6afe_port *port,
437
             struct q6afe_hdmi_cfg *cfg);
457
             struct q6afe_hdmi_cfg *cfg);
438
void q6afe_slim_port_prepare(struct q6afe_port *port,
458
void q6afe_slim_port_prepare(struct q6afe_port *port,
459
@@ -XXX,XX +XXX,XX @@ void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg);
460
void q6afe_cdc_dma_port_prepare(struct q6afe_port *port,
461
                struct q6afe_cdc_dma_cfg *cfg);
462
463
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx);
464
int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id,
465
             int clk_src, int clk_root,
466
             unsigned int freq, int dir);
439
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
467
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
440
index XXXXXXX..XXXXXXX 100644
468
index XXXXXXX..XXXXXXX 100644
441
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
469
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
442
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
470
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
443
@@ -XXX,XX +XXX,XX @@
471
@@ -XXX,XX +XXX,XX @@
...
...
491
struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
519
struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
492
diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
520
diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
493
index XXXXXXX..XXXXXXX 100644
521
index XXXXXXX..XXXXXXX 100644
494
--- a/sound/soc/qcom/qdsp6/q6routing.c
522
--- a/sound/soc/qcom/qdsp6/q6routing.c
495
+++ b/sound/soc/qcom/qdsp6/q6routing.c
523
+++ b/sound/soc/qcom/qdsp6/q6routing.c
524
@@ -XXX,XX +XXX,XX @@ static struct session_data *get_session_from_id(struct msm_routing_data *data,
525
526
    return NULL;
527
}
528
+
529
+static bool is_usb_routing_enabled(struct msm_routing_data *data)
530
+{
531
+    int i;
532
+
533
+    /*
534
+     * Loop through current sessions to see if there are active routes
535
+     * to the USB_RX backend DAI. The USB offload routing is designed
536
+     * similarly to the non offload path. If there are multiple PCM
537
+     * devices associated with the ASoC platform card, only one active
538
+     * path can be routed to the USB offloaded endpoint.
539
+     */
540
+    for (i = 0; i < MAX_SESSIONS; i++) {
541
+        if (data->sessions[i].port_id == USB_RX)
542
+            return true;
543
+    }
544
+
545
+    return false;
546
+}
547
+
548
/**
549
* q6routing_stream_close() - Deregister a stream
550
*
551
@@ -XXX,XX +XXX,XX @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
552
    struct session_data *session = &data->sessions[session_id];
553
554
    if (ucontrol->value.integer.value[0]) {
555
-        if (session->port_id == be_id)
556
+        if (session->port_id == be_id ||
557
+         (be_id == USB_RX && is_usb_routing_enabled(data)))
558
            return 0;
559
560
        session->port_id = be_id;
496
@@ -XXX,XX +XXX,XX @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
561
@@ -XXX,XX +XXX,XX @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
497
    return 1;
562
    return 1;
498
}
563
}
499
564
500
+static const struct snd_kcontrol_new usb_mixer_controls[] = {
565
+static const struct snd_kcontrol_new usb_mixer_controls[] = {
...
...
diff view generated by jsdifflib
...
...
3
the USB bus is suspended, this routine would require more time to complete,
3
the USB bus is suspended, this routine would require more time to complete,
4
as resuming the USB bus has some overhead associated with it. Increase the
4
as resuming the USB bus has some overhead associated with it. Increase the
5
timeout to 3s to allow for sufficient time for the USB QMI stream enable
5
timeout to 3s to allow for sufficient time for the USB QMI stream enable
6
handshake to complete.
6
handshake to complete.
7
7
8
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
8
Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
9
Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
9
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
10
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
10
---
11
---
11
sound/soc/qcom/qdsp6/q6afe.c | 2 +-
12
sound/soc/qcom/qdsp6/q6afe.c | 2 +-
12
1 file changed, 1 insertion(+), 1 deletion(-)
13
1 file changed, 1 insertion(+), 1 deletion(-)
...
...
diff view generated by jsdifflib
1
Create a USB BE component that will register a new USB port to the ASoC USB
1
Create a USB BE component that will register a new USB port to the ASoC USB
2
framework. This will handle determination on if the requested audio
2
framework. This will handle determination on if the requested audio
3
profile is supported by the USB device currently selected.
3
profile is supported by the USB device currently selected.
4
4
5
Check for if the PCM format is supported during the hw_params callback. If
6
the profile is not supported then the userspace ALSA entity will receive an
7
error, and can take further action.
8
5
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
9
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
---
10
---
7
include/sound/q6usboffload.h | 20 ++++
11
include/sound/q6usboffload.h | 20 +++
8
sound/soc/qcom/Kconfig | 4 +
12
sound/soc/qcom/Kconfig | 10 ++
9
sound/soc/qcom/qdsp6/Makefile | 1 +
13
sound/soc/qcom/qdsp6/Makefile | 1 +
10
sound/soc/qcom/qdsp6/q6usb.c | 200 ++++++++++++++++++++++++++++++++++
14
sound/soc/qcom/qdsp6/q6usb.c | 246 ++++++++++++++++++++++++++++++++++
11
4 files changed, 225 insertions(+)
15
4 files changed, 277 insertions(+)
12
create mode 100644 include/sound/q6usboffload.h
16
create mode 100644 include/sound/q6usboffload.h
13
create mode 100644 sound/soc/qcom/qdsp6/q6usb.c
17
create mode 100644 sound/soc/qcom/qdsp6/q6usb.c
14
18
15
diff --git a/include/sound/q6usboffload.h b/include/sound/q6usboffload.h
19
diff --git a/include/sound/q6usboffload.h b/include/sound/q6usboffload.h
16
new file mode 100644
20
new file mode 100644
17
index XXXXXXX..XXXXXXX
21
index XXXXXXX..XXXXXXX
18
--- /dev/null
22
--- /dev/null
19
+++ b/include/sound/q6usboffload.h
23
+++ b/include/sound/q6usboffload.h
20
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@
21
+/* SPDX-License-Identifier: GPL-2.0
25
+/* SPDX-License-Identifier: GPL-2.0
22
+ *
26
+ *
23
+ * linux/sound/q6usboffload.h -- QDSP6 USB offload
27
+ * sound/q6usboffload.h -- QDSP6 USB offload
24
+ *
28
+ *
25
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
29
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
26
+ */
30
+ */
27
+
31
+
28
+/**
32
+/**
29
+ * struct q6usb_offload
33
+ * struct q6usb_offload - USB backend DAI link offload parameters
30
+ * @dev - dev handle to usb be
34
+ * @dev: dev handle to usb be
31
+ * @sid - streamID for iommu
35
+ * @domain: allocated iommu domain
32
+ * @intr_num - usb interrupter number
36
+ * @sid: streamID for iommu
33
+ * @domain - allocated iommu domain
37
+ * @intr_num: usb interrupter number
34
+ **/
38
+ **/
35
+struct q6usb_offload {
39
+struct q6usb_offload {
36
+    struct device *dev;
40
+    struct device *dev;
41
+    struct iommu_domain *domain;
37
+    long long sid;
42
+    long long sid;
38
+    u16 intr_num;
43
+    u16 intr_num;
39
+    struct iommu_domain *domain;
40
+};
44
+};
41
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
45
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
42
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
43
--- a/sound/soc/qcom/Kconfig
47
--- a/sound/soc/qcom/Kconfig
44
+++ b/sound/soc/qcom/Kconfig
48
+++ b/sound/soc/qcom/Kconfig
45
@@ -XXX,XX +XXX,XX @@ config SND_SOC_QDSP6_APM
49
@@ -XXX,XX +XXX,XX @@ config SND_SOC_QDSP6_PRM
46
config SND_SOC_QDSP6_PRM_LPASS_CLOCKS
47
    tristate
48
49
+config SND_SOC_QDSP6_USB
50
+    tristate
51
+
52
config SND_SOC_QDSP6_PRM
53
    tristate
50
    tristate
54
    select SND_SOC_QDSP6_PRM_LPASS_CLOCKS
51
    select SND_SOC_QDSP6_PRM_LPASS_CLOCKS
55
@@ -XXX,XX +XXX,XX @@ config SND_SOC_QDSP6
52
56
    select SND_SOC_TOPOLOGY
53
+config SND_SOC_QDSP6_USB
57
    select SND_SOC_QDSP6_APM
54
+ tristate "SoC ALSA USB offloading backing for QDSP6"
58
    select SND_SOC_QDSP6_PRM
55
+ depends on SND_SOC_USB
59
+    select SND_SOC_QDSP6_USB
56
+ help
60
    help
57
+ Adds support for USB offloading for QDSP6 ASoC
61
     To add support for MSM QDSP6 Soc Audio.
58
+ based platform sound cards. This will enable the
62
     This will enable sound soc platform specific
59
+ Q6USB DPCM backend DAI link, which will interact
60
+ with the SoC USB framework to initialize a session
61
+ with active USB SND devices.
62
+
63
config SND_SOC_QDSP6
64
    tristate "SoC ALSA audio driver for QDSP6"
65
    depends on QCOM_APR
63
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
66
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
64
index XXXXXXX..XXXXXXX 100644
67
index XXXXXXX..XXXXXXX 100644
65
--- a/sound/soc/qcom/qdsp6/Makefile
68
--- a/sound/soc/qcom/qdsp6/Makefile
66
+++ b/sound/soc/qcom/qdsp6/Makefile
69
+++ b/sound/soc/qcom/qdsp6/Makefile
67
@@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o
70
@@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o
...
...
75
--- /dev/null
78
--- /dev/null
76
+++ b/sound/soc/qcom/qdsp6/q6usb.c
79
+++ b/sound/soc/qcom/qdsp6/q6usb.c
77
@@ -XXX,XX +XXX,XX @@
80
@@ -XXX,XX +XXX,XX @@
78
+// SPDX-License-Identifier: GPL-2.0
81
+// SPDX-License-Identifier: GPL-2.0
79
+/*
82
+/*
80
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
83
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
81
+ */
84
+ */
82
+
85
+
86
+#include <linux/device.h>
87
+#include <linux/dma-mapping.h>
88
+#include <linux/dma-map-ops.h>
83
+#include <linux/err.h>
89
+#include <linux/err.h>
84
+#include <linux/init.h>
90
+#include <linux/init.h>
91
+#include <linux/iommu.h>
85
+#include <linux/module.h>
92
+#include <linux/module.h>
86
+#include <linux/device.h>
87
+#include <linux/platform_device.h>
93
+#include <linux/platform_device.h>
88
+#include <linux/slab.h>
94
+#include <linux/slab.h>
89
+#include <linux/iommu.h>
95
+
90
+#include <linux/dma-mapping.h>
96
+#include <sound/asound.h>
91
+#include <linux/dma-map-ops.h>
92
+
93
+#include <sound/pcm.h>
97
+#include <sound/pcm.h>
98
+#include <sound/pcm_params.h>
99
+#include <sound/q6usboffload.h>
94
+#include <sound/soc.h>
100
+#include <sound/soc.h>
95
+#include <sound/soc-usb.h>
101
+#include <sound/soc-usb.h>
96
+#include <sound/pcm_params.h>
102
+
97
+#include <sound/asound.h>
103
+#include <dt-bindings/sound/qcom,q6afe.h>
98
+#include <sound/q6usboffload.h>
104
+
99
+
105
+#include "q6afe.h"
100
+#include "q6dsp-lpass-ports.h"
106
+#include "q6dsp-lpass-ports.h"
101
+#include "q6afe.h"
107
+
102
+
108
+#define Q6_USB_SID_MASK    0xF
103
+#define SID_MASK    0xF
104
+
109
+
105
+struct q6usb_port_data {
110
+struct q6usb_port_data {
106
+    struct q6afe_usb_cfg usb_cfg;
111
+    struct q6afe_usb_cfg usb_cfg;
107
+    struct snd_soc_usb *usb;
112
+    struct snd_soc_usb *usb;
108
+    struct q6usb_offload priv;
113
+    struct q6usb_offload priv;
109
+    int active_idx;
114
+
115
+    /* Protects against operations between SOC USB and ASoC */
116
+    struct mutex mutex;
117
+    struct list_head devices;
110
+};
118
+};
111
+
119
+
112
+static const struct snd_soc_dapm_widget q6usb_dai_widgets[] = {
120
+static const struct snd_soc_dapm_widget q6usb_dai_widgets[] = {
113
+    SND_SOC_DAPM_HP("USB_RX_BE", NULL),
121
+    SND_SOC_DAPM_HP("USB_RX_BE", NULL),
114
+};
122
+};
...
...
119
+
127
+
120
+static int q6usb_hw_params(struct snd_pcm_substream *substream,
128
+static int q6usb_hw_params(struct snd_pcm_substream *substream,
121
+             struct snd_pcm_hw_params *params,
129
+             struct snd_pcm_hw_params *params,
122
+             struct snd_soc_dai *dai)
130
+             struct snd_soc_dai *dai)
123
+{
131
+{
124
+    return 0;
132
+    struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
133
+    struct snd_soc_pcm_runtime *rtd = substream->private_data;
134
+    struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
135
+    int direction = substream->stream;
136
+    struct q6afe_port *q6usb_afe;
137
+    struct snd_soc_usb_device *sdev;
138
+    int ret = -EINVAL;
139
+
140
+    mutex_lock(&data->mutex);
141
+
142
+    /* No active chip index */
143
+    if (list_empty(&data->devices))
144
+        goto out;
145
+
146
+    sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list);
147
+
148
+    ret = snd_soc_usb_find_supported_format(sdev->chip_idx, params, direction);
149
+    if (ret < 0)
150
+        goto out;
151
+
152
+    q6usb_afe = q6afe_port_get_from_id(cpu_dai->dev, USB_RX);
153
+    if (IS_ERR(q6usb_afe))
154
+        goto out;
155
+
156
+    /* Notify audio DSP about the devices being offloaded */
157
+    ret = afe_port_send_usb_dev_param(q6usb_afe, sdev->card_idx,
158
+                     sdev->ppcm_idx[sdev->num_playback - 1]);
159
+
160
+out:
161
+    mutex_unlock(&data->mutex);
162
+
163
+    return ret;
125
+}
164
+}
126
+
165
+
127
+static const struct snd_soc_dai_ops q6usb_ops = {
166
+static const struct snd_soc_dai_ops q6usb_ops = {
128
+    .hw_params = q6usb_hw_params,
167
+    .hw_params = q6usb_hw_params,
129
+};
168
+};
...
...
151
+        .ops = &q6usb_ops,
190
+        .ops = &q6usb_ops,
152
+    },
191
+    },
153
+};
192
+};
154
+
193
+
155
+static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
194
+static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
156
+                    const struct of_phandle_args *args,
195
+                     const struct of_phandle_args *args,
157
+                    const char **dai_name)
196
+                     const char **dai_name)
158
+{
197
+{
159
+    int id = args->args[0];
198
+    int id = args->args[0];
160
+    int ret = -EINVAL;
199
+    int ret = -EINVAL;
161
+    int i;
200
+    int i;
162
+
201
+
...
...
170
+
209
+
171
+    return ret;
210
+    return ret;
172
+}
211
+}
173
+
212
+
174
+static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
213
+static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
175
+            struct snd_soc_usb_device *sdev, bool connected)
214
+                 struct snd_soc_usb_device *sdev, bool connected)
176
+{
215
+{
177
+    struct q6usb_port_data *data;
216
+    struct q6usb_port_data *data;
178
+
217
+
179
+    if (!usb->component)
218
+    if (!usb->component)
180
+        return -ENODEV;
219
+        return -ENODEV;
181
+
220
+
182
+    data = dev_get_drvdata(usb->component->dev);
221
+    data = dev_get_drvdata(usb->component->dev);
183
+
222
+
223
+    mutex_lock(&data->mutex);
184
+    if (connected) {
224
+    if (connected) {
185
+        /* We only track the latest USB headset plugged in */
225
+        /* Selects the latest USB headset plugged in for offloading */
186
+        data->active_idx = sdev->card_idx;
226
+        list_add_tail(&sdev->list, &data->devices);
227
+    } else {
228
+        list_del(&sdev->list);
187
+    }
229
+    }
230
+    mutex_unlock(&data->mutex);
188
+
231
+
189
+    return 0;
232
+    return 0;
190
+}
233
+}
191
+
234
+
192
+static int q6usb_component_probe(struct snd_soc_component *component)
235
+static int q6usb_component_probe(struct snd_soc_component *component)
193
+{
236
+{
194
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
237
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
195
+
238
+    struct snd_soc_usb *usb;
196
+    data->usb = snd_soc_usb_add_port(component->dev, &data->priv, q6usb_alsa_connection_cb);
239
+
197
+    if (IS_ERR(data->usb)) {
240
+    usb = snd_soc_usb_allocate_port(component, &data->priv);
198
+        dev_err(component->dev, "failed to add usb port\n");
241
+    if (IS_ERR(usb))
199
+        return -ENODEV;
242
+        return -ENOMEM;
200
+    }
243
+
201
+
244
+    usb->connection_status_cb = q6usb_alsa_connection_cb;
202
+    data->usb->component = component;
245
+
246
+    snd_soc_usb_add_port(usb);
247
+    data->usb = usb;
203
+
248
+
204
+    return 0;
249
+    return 0;
205
+}
250
+}
206
+
251
+
207
+static void q6usb_component_remove(struct snd_soc_component *component)
252
+static void q6usb_component_remove(struct snd_soc_component *component)
208
+{
253
+{
209
+    snd_soc_usb_remove_port(component->dev);
254
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
255
+
256
+    snd_soc_usb_remove_port(data->usb);
257
+    snd_soc_usb_free_port(data->usb);
210
+}
258
+}
211
+
259
+
212
+static const struct snd_soc_component_driver q6usb_dai_component = {
260
+static const struct snd_soc_component_driver q6usb_dai_component = {
213
+    .probe = q6usb_component_probe,
261
+    .probe = q6usb_component_probe,
214
+    .remove = q6usb_component_remove,
262
+    .remove = q6usb_component_remove,
...
...
231
+    data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
279
+    data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
232
+    if (!data)
280
+    if (!data)
233
+        return -ENOMEM;
281
+        return -ENOMEM;
234
+
282
+
235
+    ret = of_property_read_u16(node, "qcom,usb-audio-intr-idx",
283
+    ret = of_property_read_u16(node, "qcom,usb-audio-intr-idx",
236
+                &data->priv.intr_num);
284
+                 &data->priv.intr_num);
237
+    if (ret) {
285
+    if (ret) {
238
+        dev_err(&pdev->dev, "failed to read intr idx.\n");
286
+        dev_err(&pdev->dev, "failed to read intr idx.\n");
239
+        return ret;
287
+        return ret;
240
+    }
288
+    }
241
+
289
+
242
+    ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
290
+    ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
243
+    if (ret < 0)
291
+    if (ret < 0)
244
+        data->priv.sid = -1;
292
+        data->priv.sid = -1;
245
+    else
293
+    else
246
+        data->priv.sid = args.args[0] & SID_MASK;
294
+        data->priv.sid = args.args[0] & Q6_USB_SID_MASK;
247
+
295
+
248
+    data->priv.domain = iommu_get_domain_for_dev(&pdev->dev);
296
+    data->priv.domain = iommu_get_domain_for_dev(&pdev->dev);
249
+
297
+
250
+    data->priv.dev = dev;
298
+    data->priv.dev = dev;
299
+    INIT_LIST_HEAD(&data->devices);
251
+    dev_set_drvdata(dev, data);
300
+    dev_set_drvdata(dev, data);
252
+
301
+
253
+    return devm_snd_soc_register_component(dev, &q6usb_dai_component,
302
+    return devm_snd_soc_register_component(dev, &q6usb_dai_component,
254
+                    q6usb_be_dais, ARRAY_SIZE(q6usb_be_dais));
303
+                    q6usb_be_dais, ARRAY_SIZE(q6usb_be_dais));
255
+}
304
+}
...
...
diff view generated by jsdifflib
Deleted patch
1
Add the definition for how many interrupts the XHCI host controller should
2
allocate. XHCI can potentially support up to 1024 interrupters, which
3
implementations may want to limit.
4
1
5
Reviewed-by: Rob Herring <robh@kernel.org>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
8
Documentation/devicetree/bindings/usb/usb-xhci.yaml | 6 ++++++
9
1 file changed, 6 insertions(+)
10
11
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.yaml b/Documentation/devicetree/bindings/usb/usb-xhci.yaml
12
index XXXXXXX..XXXXXXX 100644
13
--- a/Documentation/devicetree/bindings/usb/usb-xhci.yaml
14
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.yaml
15
@@ -XXX,XX +XXX,XX @@ properties:
16
description: Interrupt moderation interval
17
default: 5000
18
19
+ num-hc-interrupters:
20
+ description: Maximum number of interrupters to allocate
21
+ $ref: /schemas/types.yaml#/definitions/uint16
22
+ minimum: 1
23
+ maximum: 1024
24
+
25
additionalProperties: true
26
27
examples:
diff view generated by jsdifflib
Deleted patch
1
Ensure that the number of XHCI secondary interrupters defined for a DWC3
2
based implementation is limited to 8. XHCI in general can potentially
3
support up to 1024 interrupters.
4
1
5
Reviewed-by: Rob Herring <robh@kernel.org>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
8
Documentation/devicetree/bindings/usb/snps,dwc3.yaml | 4 ++++
9
1 file changed, 4 insertions(+)
10
11
diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
12
index XXXXXXX..XXXXXXX 100644
13
--- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
14
+++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml
15
@@ -XXX,XX +XXX,XX @@ properties:
16
items:
17
enum: [1, 4, 8, 16, 32, 64, 128, 256]
18
19
+ num-hc-interrupters:
20
+ maximum: 8
21
+ default: 1
22
+
23
port:
24
$ref: /schemas/graph.yaml#/properties/port
25
description:
diff view generated by jsdifflib
...
...
4
Applications can further identify specific offloading information through
4
Applications can further identify specific offloading information through
5
other SND kcontrols.
5
other SND kcontrols.
6
6
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
---
8
---
9
sound/soc/qcom/qdsp6/q6usb.c | 14 ++++++++++++++
9
sound/soc/qcom/Kconfig | 5 +++
10
1 file changed, 14 insertions(+)
10
sound/soc/qcom/Makefile | 2 ++
11
sound/soc/qcom/qdsp6/q6usb.c | 41 ++++++++++++++++++++++
12
sound/soc/qcom/sm8250.c | 24 ++++++++++++-
13
sound/soc/qcom/usb_offload_utils.c | 56 ++++++++++++++++++++++++++++++
14
sound/soc/qcom/usb_offload_utils.h | 30 ++++++++++++++++
15
6 files changed, 157 insertions(+), 1 deletion(-)
16
create mode 100644 sound/soc/qcom/usb_offload_utils.c
17
create mode 100644 sound/soc/qcom/usb_offload_utils.h
11
18
19
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
20
index XXXXXXX..XXXXXXX 100644
21
--- a/sound/soc/qcom/Kconfig
22
+++ b/sound/soc/qcom/Kconfig
23
@@ -XXX,XX +XXX,XX @@ config SND_SOC_QDSP6_PRM
24
    tristate
25
    select SND_SOC_QDSP6_PRM_LPASS_CLOCKS
26
27
+config SND_SOC_QCOM_OFFLOAD_UTILS
28
+    tristate
29
+
30
config SND_SOC_QDSP6_USB
31
tristate "SoC ALSA USB offloading backing for QDSP6"
32
depends on SND_SOC_USB
33
+ select SND_SOC_QCOM_OFFLOAD_UTILS
34
+
35
help
36
Adds support for USB offloading for QDSP6 ASoC
37
based platform sound cards. This will enable the
38
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
39
index XXXXXXX..XXXXXXX 100644
40
--- a/sound/soc/qcom/Makefile
41
+++ b/sound/soc/qcom/Makefile
42
@@ -XXX,XX +XXX,XX @@ snd-soc-sc8280xp-y := sc8280xp.o
43
snd-soc-qcom-common-y := common.o
44
snd-soc-qcom-sdw-y := sdw.o
45
snd-soc-x1e80100-y := x1e80100.o
46
+snd-soc-qcom-offload-utils-objs := usb_offload_utils.o
47
48
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
49
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
50
@@ -XXX,XX +XXX,XX @@ obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
51
obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
52
obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o
53
obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o
54
+obj-$(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) += snd-soc-qcom-offload-utils.o
55
56
#DSP lib
57
obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/
12
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
58
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
13
index XXXXXXX..XXXXXXX 100644
59
index XXXXXXX..XXXXXXX 100644
14
--- a/sound/soc/qcom/qdsp6/q6usb.c
60
--- a/sound/soc/qcom/qdsp6/q6usb.c
15
+++ b/sound/soc/qcom/qdsp6/q6usb.c
61
+++ b/sound/soc/qcom/qdsp6/q6usb.c
16
@@ -XXX,XX +XXX,XX @@
62
@@ -XXX,XX +XXX,XX @@
63
#include <linux/slab.h>
64
65
#include <sound/asound.h>
66
+#include <sound/jack.h>
67
#include <sound/pcm.h>
17
#include <sound/pcm_params.h>
68
#include <sound/pcm_params.h>
18
#include <sound/asound.h>
19
#include <sound/q6usboffload.h>
69
#include <sound/q6usboffload.h>
20
+#include <sound/jack.h>
70
@@ -XXX,XX +XXX,XX @@
21
22
#include "q6dsp-lpass-ports.h"
23
#include "q6afe.h"
24
@@ -XXX,XX +XXX,XX @@ struct q6usb_status {
25
struct q6usb_port_data {
71
struct q6usb_port_data {
26
    struct q6afe_usb_cfg usb_cfg;
72
    struct q6afe_usb_cfg usb_cfg;
27
    struct snd_soc_usb *usb;
73
    struct snd_soc_usb *usb;
28
+    struct snd_soc_jack hs_jack;
74
+    struct snd_soc_jack *hs_jack;
29
    struct q6usb_offload priv;
75
    struct q6usb_offload priv;
30
    struct mutex mutex;
76
31
    unsigned long available_card_slot;
77
    /* Protects against operations between SOC USB and ASoC */
32
@@ -XXX,XX +XXX,XX @@ static const struct snd_kcontrol_new q6usb_offload_control = {
33
/* Build a mixer control for a UAC connector control (jack-detect) */
34
static void q6usb_connector_control_init(struct snd_soc_component *component)
35
{
36
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
37
    int ret;
38
39
    ret = snd_ctl_add(component->card->snd_card,
40
@@ -XXX,XX +XXX,XX @@ static void q6usb_connector_control_init(struct snd_soc_component *component)
41
                snd_ctl_new1(&q6usb_offload_dev_ctrl, component));
42
    if (ret < 0)
43
        return;
44
+
45
+    ret = snd_soc_card_jack_new(component->card, "USB offload",
46
+                    SND_JACK_HEADSET, &data->hs_jack);
47
+    if (ret)
48
+        return;
49
}
50
51
static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
52
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
78
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
53
79
54
    mutex_lock(&data->mutex);
80
    mutex_lock(&data->mutex);
55
    if (connected) {
81
    if (connected) {
56
+        if (!data->available_card_slot)
82
+        if (data->hs_jack)
57
+            snd_jack_report(data->hs_jack.jack, 1);
83
+            snd_jack_report(data->hs_jack->jack, SND_JACK_USB);
58
+
84
+
59
        /*
85
        /* Selects the latest USB headset plugged in for offloading */
60
         * Update the latest USB headset plugged in, if session is
86
        list_add_tail(&sdev->list, &data->devices);
61
         * idle.
87
    } else {
62
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
88
        list_del(&sdev->list);
63
        clear_bit(sdev->card_idx, &data->available_card_slot);
89
+
64
        data->status[sdev->card_idx].num_pcm = 0;
90
+        if (data->hs_jack)
65
        data->status[sdev->card_idx].chip_index = 0;
91
+            snd_jack_report(data->hs_jack->jack, 0);
66
+
67
+        if (!data->available_card_slot)
68
+            snd_jack_report(data->hs_jack.jack, 0);
69
    }
92
    }
70
    mutex_unlock(&data->mutex);
93
    mutex_unlock(&data->mutex);
94
95
    return 0;
96
}
97
98
+static void q6usb_component_disable_jack(struct q6usb_port_data *data)
99
+{
100
+    /* Offload jack has already been disabled */
101
+    if (!data->hs_jack)
102
+        return;
103
+
104
+    snd_jack_report(data->hs_jack->jack, 0);
105
+    data->hs_jack = NULL;
106
+}
107
+
108
+static void q6usb_component_enable_jack(struct q6usb_port_data *data,
109
+                    struct snd_soc_jack *jack)
110
+{
111
+    snd_jack_report(jack->jack, !list_empty(&data->devices) ? SND_JACK_USB : 0);
112
+    data->hs_jack = jack;
113
+}
114
+
115
+static int q6usb_component_set_jack(struct snd_soc_component *component,
116
+                 struct snd_soc_jack *jack, void *priv)
117
+{
118
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
119
+
120
+    mutex_lock(&data->mutex);
121
+    if (jack)
122
+        q6usb_component_enable_jack(data, jack);
123
+    else
124
+        q6usb_component_disable_jack(data);
125
+    mutex_unlock(&data->mutex);
126
+
127
+    return 0;
128
+}
129
+
130
static int q6usb_component_probe(struct snd_soc_component *component)
131
{
132
    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
133
@@ -XXX,XX +XXX,XX @@ static void q6usb_component_remove(struct snd_soc_component *component)
134
135
static const struct snd_soc_component_driver q6usb_dai_component = {
136
    .probe = q6usb_component_probe,
137
+    .set_jack = q6usb_component_set_jack,
138
    .remove = q6usb_component_remove,
139
    .name = "q6usb-dai-component",
140
    .dapm_widgets = q6usb_dai_widgets,
141
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
142
index XXXXXXX..XXXXXXX 100644
143
--- a/sound/soc/qcom/sm8250.c
144
+++ b/sound/soc/qcom/sm8250.c
145
@@ -XXX,XX +XXX,XX @@
146
#include <linux/input-event-codes.h>
147
#include "qdsp6/q6afe.h"
148
#include "common.h"
149
+#include "usb_offload_utils.h"
150
#include "sdw.h"
151
152
#define DRIVER_NAME        "sm8250"
153
@@ -XXX,XX +XXX,XX @@ struct sm8250_snd_data {
154
    struct snd_soc_card *card;
155
    struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
156
    struct snd_soc_jack jack;
157
+    struct snd_soc_jack usb_offload_jack;
158
+    bool usb_offload_jack_setup;
159
    bool jack_setup;
160
};
161
162
static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
163
{
164
    struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
165
+    struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
166
+    int ret;
167
+
168
+    if (cpu_dai->id == USB_RX)
169
+        ret = qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack,
170
+                         &data->usb_offload_jack_setup);
171
+    else
172
+        ret = qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
173
+    return ret;
174
+}
175
+
176
+static void sm8250_snd_exit(struct snd_soc_pcm_runtime *rtd)
177
+{
178
+    struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
179
+    struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
180
+
181
+    if (cpu_dai->id == USB_RX)
182
+        qcom_snd_usb_offload_jack_remove(rtd,
183
+                         &data->usb_offload_jack_setup);
184
185
-    return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
186
}
187
188
static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
189
@@ -XXX,XX +XXX,XX @@ static void sm8250_add_be_ops(struct snd_soc_card *card)
190
    for_each_card_prelinks(card, i, link) {
191
        if (link->no_pcm == 1) {
192
            link->init = sm8250_snd_init;
193
+            link->exit = sm8250_snd_exit;
194
            link->be_hw_params_fixup = sm8250_be_hw_params_fixup;
195
            link->ops = &sm8250_be_ops;
196
        }
197
diff --git a/sound/soc/qcom/usb_offload_utils.c b/sound/soc/qcom/usb_offload_utils.c
198
new file mode 100644
199
index XXXXXXX..XXXXXXX
200
--- /dev/null
201
+++ b/sound/soc/qcom/usb_offload_utils.c
202
@@ -XXX,XX +XXX,XX @@
203
+// SPDX-License-Identifier: GPL-2.0
204
+/*
205
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
206
+ */
207
+#include <dt-bindings/sound/qcom,q6afe.h>
208
+#include <linux/module.h>
209
+#include <sound/jack.h>
210
+#include <sound/soc-usb.h>
211
+
212
+#include "usb_offload_utils.h"
213
+
214
+int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
215
+                 struct snd_soc_jack *jack, bool *jack_setup)
216
+{
217
+    struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
218
+    struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
219
+    int ret = 0;
220
+
221
+    if (cpu_dai->id != USB_RX)
222
+        return -EINVAL;
223
+
224
+    if (!*jack_setup) {
225
+        ret = snd_soc_usb_setup_offload_jack(codec_dai->component, jack);
226
+        if (ret)
227
+            return ret;
228
+    }
229
+
230
+    *jack_setup = true;
231
+
232
+    return 0;
233
+}
234
+EXPORT_SYMBOL_GPL(qcom_snd_usb_offload_jack_setup);
235
+
236
+int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
237
+                 bool *jack_setup)
238
+{
239
+    struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
240
+    struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
241
+    int ret = 0;
242
+
243
+    if (cpu_dai->id != USB_RX)
244
+        return -EINVAL;
245
+
246
+    if (*jack_setup) {
247
+        ret = snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
248
+        if (ret)
249
+            return ret;
250
+    }
251
+
252
+    *jack_setup = false;
253
+
254
+    return 0;
255
+}
256
+EXPORT_SYMBOL_GPL(qcom_snd_usb_offload_jack_remove);
257
+MODULE_DESCRIPTION("ASoC Q6 USB offload controls");
258
+MODULE_LICENSE("GPL");
259
diff --git a/sound/soc/qcom/usb_offload_utils.h b/sound/soc/qcom/usb_offload_utils.h
260
new file mode 100644
261
index XXXXXXX..XXXXXXX
262
--- /dev/null
263
+++ b/sound/soc/qcom/usb_offload_utils.h
264
@@ -XXX,XX +XXX,XX @@
265
+/* SPDX-License-Identifier: GPL-2.0
266
+ *
267
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
268
+ */
269
+#ifndef __QCOM_SND_USB_OFFLOAD_UTILS_H__
270
+#define __QCOM_SND_USB_OFFLOAD_UTILS_H__
271
+
272
+#include <sound/soc.h>
273
+
274
+#if IS_ENABLED(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS)
275
+int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
276
+                 struct snd_soc_jack *jack, bool *jack_setup);
277
+
278
+int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
279
+                 bool *jack_setup);
280
+#else
281
+static inline int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
282
+                         struct snd_soc_jack *jack,
283
+                         bool *jack_setup)
284
+{
285
+    return -ENODEV;
286
+}
287
+
288
+static inline int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
289
+                         bool *jack_setup)
290
+{
291
+    return -ENODEV;
292
+}
293
+#endif /* IS_ENABLED(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) */
294
+#endif /* __QCOM_SND_USB_OFFLOAD_UTILS_H__ */
diff view generated by jsdifflib
1
Add a kcontrol to the platform sound card to fetch the current offload
1
The USB SND path may need to know how the USB offload path is routed, so
2
status. This can allow for userspace to ensure/check which USB SND
2
that applications can open the proper sound card and PCM device. The
3
resources are actually busy versus having to attempt opening the USB SND
3
implementation for the QC ASoC design has a "USB Mixer" kcontrol for each
4
devices, which will result in an error if offloading is active.
4
possible FE (Q6ASM) DAI, which can be utilized to know which front end link
5
is enabled.
6
7
When an application/userspace queries for the mapped offload devices, the
8
logic will lookup the USB mixer status though the following path:
9
10
MultiMedia* <-> MM_DL* <-> USB Mixer*
11
12
The "USB Mixer" is a DAPM widget, and the q6routing entity will set the
13
DAPM connect status accordingly if the USB mixer is enabled. If enabled,
14
the Q6USB backend link can fetch the PCM device number from the FE DAI
15
link (Multimedia*). With respects to the card number, that is
16
straightforward, as the ASoC components have direct references to the ASoC
17
platform sound card.
18
19
An example output can be shown below:
20
21
Number of controls: 9
22
name value
23
Capture Channel Map 0, 0 (range 0->36)
24
Playback Channel Map 0, 0 (range 0->36)
25
Headset Capture Switch On
26
Headset Capture Volume 1 (range 0->4)
27
Sidetone Playback Switch On
28
Sidetone Playback Volume 4096 (range 0->8192)
29
Headset Playback Switch On
30
Headset Playback Volume 20, 20 (range 0->24)
31
USB Offload Playback Route PCM#0 0, 1 (range -1->255)
32
33
The "USB Offload Playback Route PCM#*" kcontrol will signify the
34
corresponding card and pcm device it is offload to. (card#0 pcm - device#1)
35
If the USB SND device supports multiple audio interfaces, then it will
36
contain several PCM streams, hence in those situations, it is expected
37
that there will be multiple playback route kcontrols created.
5
38
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
39
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
40
---
8
sound/soc/qcom/qdsp6/q6usb.c | 104 ++++++++++++++++++++++++++++++++++-
41
sound/soc/qcom/qdsp6/q6usb.c | 98 ++++++++++++++++++++++++++++++++++++
9
1 file changed, 101 insertions(+), 3 deletions(-)
42
1 file changed, 98 insertions(+)
10
43
11
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
44
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
12
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
13
--- a/sound/soc/qcom/qdsp6/q6usb.c
46
--- a/sound/soc/qcom/qdsp6/q6usb.c
14
+++ b/sound/soc/qcom/qdsp6/q6usb.c
47
+++ b/sound/soc/qcom/qdsp6/q6usb.c
15
@@ -XXX,XX +XXX,XX @@ struct q6usb_status {
48
@@ -XXX,XX +XXX,XX @@ static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *compone
16
    unsigned int num_pcm;
49
    return ret;
17
    unsigned int chip_index;
50
}
18
    unsigned int pcm_index;
51
19
+    bool prepared;
52
+static int q6usb_get_pcm_id_from_widget(struct snd_soc_dapm_widget *w)
20
+    bool running;
21
};
22
23
struct q6usb_port_data {
24
@@ -XXX,XX +XXX,XX @@ static const struct snd_soc_dapm_route q6usb_dapm_routes[] = {
25
    {"USB Playback", NULL, "USB_RX_BE"},
26
};
27
28
+static int q6usb_find_running(struct q6usb_port_data *data)
29
+{
53
+{
30
+    int i;
54
+    struct snd_soc_pcm_runtime *rtd;
55
+    struct snd_soc_dai *dai;
31
+
56
+
32
+    for (i = 0; i < SNDRV_CARDS; i++) {
57
+    for_each_card_rtds(w->dapm->card, rtd) {
33
+        if (data->status[i].running)
58
+        dai = snd_soc_rtd_to_cpu(rtd, 0);
34
+            return i;
59
+        /*
60
+         * Only look for playback widget. RTD number carries the assigned
61
+         * PCM index.
62
+         */
63
+        if (dai->stream[0].widget == w)
64
+            return rtd->id;
35
+    }
65
+    }
36
+    return -ENODEV;
66
+
67
+    return -1;
37
+}
68
+}
38
+
69
+
39
static int q6usb_hw_params(struct snd_pcm_substream *substream,
70
+static int q6usb_usb_mixer_enabled(struct snd_soc_dapm_widget *w)
40
             struct snd_pcm_hw_params *params,
41
             struct snd_soc_dai *dai)
42
@@ -XXX,XX +XXX,XX @@ static int q6usb_hw_params(struct snd_pcm_substream *substream,
43
        goto out;
44
45
    data->status[data->sel_card_idx].pcm_index = data->sel_pcm_idx;
46
+    data->status[data->sel_card_idx].prepared = true;
47
out:
48
    mutex_unlock(&data->mutex);
49
50
    return ret;
51
}
52
53
+static int q6usb_prepare(struct snd_pcm_substream *substream,
54
+        struct snd_soc_dai *dai)
55
+{
71
+{
56
+    struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
72
+    struct snd_soc_dapm_path *p;
57
+
73
+
58
+    mutex_lock(&data->mutex);
74
+    /* Checks to ensure USB path is enabled/connected */
59
+    data->status[data->sel_card_idx].running = true;
75
+    snd_soc_dapm_widget_for_each_sink_path(w, p)
60
+    mutex_unlock(&data->mutex);
76
+        if (!strcmp(p->sink->name, "USB Mixer") && p->connect)
77
+            return 1;
61
+
78
+
62
+    return 0;
79
+    return 0;
63
+}
80
+}
64
+
81
+
65
+static void q6usb_shutdown(struct snd_pcm_substream *substream,
82
+static int q6usb_get_pcm_id(struct snd_soc_component *component)
66
+                struct snd_soc_dai *dai)
67
+{
83
+{
68
+    struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
84
+    struct snd_soc_dapm_widget *w;
85
+    struct snd_soc_dapm_path *p;
86
+    int pidx;
87
+
88
+    /*
89
+     * Traverse widgets to find corresponding FE widget. The DAI links are
90
+     * built like the following:
91
+     * MultiMedia* <-> MM_DL* <-> USB Mixer*
92
+     */
93
+    for_each_card_widgets(component->card, w) {
94
+        if (!strncmp(w->name, "MultiMedia", 10)) {
95
+            /*
96
+             * Look up all paths associated with the FE widget to see if
97
+             * the USB BE is enabled. The sink widget is responsible to
98
+             * link with the USB mixers.
99
+             */
100
+            snd_soc_dapm_widget_for_each_sink_path(w, p) {
101
+                if (q6usb_usb_mixer_enabled(p->sink)) {
102
+                    pidx = q6usb_get_pcm_id_from_widget(w);
103
+                    return pidx;
104
+                }
105
+            }
106
+        }
107
+    }
108
+
109
+    return -1;
110
+}
111
+
112
+static int q6usb_update_offload_route(struct snd_soc_component *component, int card,
113
+                 int pcm, int direction, enum snd_soc_usb_kctl path,
114
+                 long *route)
115
+{
116
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
117
+    struct snd_soc_usb_device *sdev;
118
+    int ret = 0;
119
+    int idx = -1;
69
+
120
+
70
+    mutex_lock(&data->mutex);
121
+    mutex_lock(&data->mutex);
71
+    data->status[data->sel_card_idx].running = false;
122
+
72
+    data->status[data->sel_card_idx].prepared = false;
123
+    if (list_empty(&data->devices) ||
124
+     direction == SNDRV_PCM_STREAM_CAPTURE) {
125
+        ret = -ENODEV;
126
+        goto out;
127
+    }
128
+
129
+    sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list);
130
+
131
+    /*
132
+     * Will always look for last PCM device discovered/probed as the
133
+     * active offload index.
134
+     */
135
+    if (card == sdev->card_idx &&
136
+     pcm == sdev->ppcm_idx[sdev->num_playback - 1]) {
137
+        idx = path == SND_SOC_USB_KCTL_CARD_ROUTE ?
138
+                component->card->snd_card->number :
139
+                q6usb_get_pcm_id(component);
140
+    }
141
+
142
+out:
143
+    route[0] = idx;
73
+    mutex_unlock(&data->mutex);
144
+    mutex_unlock(&data->mutex);
145
+
146
+    return ret;
74
+}
147
+}
75
+
148
+
76
static const struct snd_soc_dai_ops q6usb_ops = {
149
static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
77
    .hw_params = q6usb_hw_params,
150
                 struct snd_soc_usb_device *sdev, bool connected)
78
+    .prepare = q6usb_prepare,
79
+    .shutdown = q6usb_shutdown,
80
};
81
82
static struct snd_soc_dai_driver q6usb_be_dais[] = {
83
@@ -XXX,XX +XXX,XX @@ static int q6usb_put_offload_dev(struct snd_kcontrol *kcontrol,
84
    int pcmidx;
85
    int cardidx;
86
87
+    mutex_lock(&data->mutex);
88
+
89
+    /* Don't allow changes to the offloading devices if session is busy */
90
+    if (data->sel_card_idx >= 0 && data->status[data->sel_card_idx].prepared)
91
+        goto out;
92
+
93
    cardidx = ucontrol->value.integer.value[0];
94
    pcmidx = ucontrol->value.integer.value[1];
95
96
-    mutex_lock(&data->mutex);
97
    if ((cardidx >= 0 && test_bit(cardidx, &data->available_card_slot))) {
98
        data->sel_card_idx = cardidx;
99
        changed = 1;
100
@@ -XXX,XX +XXX,XX @@ static int q6usb_put_offload_dev(struct snd_kcontrol *kcontrol,
101
        data->idx_valid = true;
102
        changed = 1;
103
    }
104
+
105
+out:
106
    mutex_unlock(&data->mutex);
107
108
    return changed;
109
@@ -XXX,XX +XXX,XX @@ static const struct snd_kcontrol_new q6usb_offload_dev_ctrl = {
110
    .put = q6usb_put_offload_dev,
111
};
112
113
+static int q6usb_mixer_get_offload_status(struct snd_kcontrol *kcontrol,
114
+                 struct snd_ctl_elem_value *ucontrol)
115
+{
116
+    struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
117
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
118
+    int running;
119
+    int card_idx;
120
+    int pcm_idx;
121
+
122
+    running = q6usb_find_running(data);
123
+    if (running < 0) {
124
+        card_idx = -1;
125
+        pcm_idx = -1;
126
+    } else {
127
+        card_idx = running;
128
+        pcm_idx = data->status[running].pcm_index;
129
+    }
130
+
131
+    ucontrol->value.integer.value[0] = card_idx;
132
+    ucontrol->value.integer.value[1] = pcm_idx;
133
+    return 0;
134
+}
135
+
136
+static int q6usb_offload_ctl_info(struct snd_kcontrol *kcontrol,
137
+             struct snd_ctl_elem_info *uinfo)
138
+{
139
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
140
+    uinfo->count = 2;
141
+    uinfo->value.integer.min = 0;
142
+    uinfo->value.integer.max = SNDRV_CARDS;
143
+
144
+    return 0;
145
+}
146
+
147
+static const struct snd_kcontrol_new q6usb_offload_control = {
148
+    .iface = SNDRV_CTL_ELEM_IFACE_CARD,
149
+    .access = SNDRV_CTL_ELEM_ACCESS_READ,
150
+    .name = "Q6USB offload status",
151
+    .info = q6usb_offload_ctl_info,
152
+    .get = q6usb_mixer_get_offload_status,
153
+    .put = NULL,
154
+};
155
+
156
/* Build a mixer control for a UAC connector control (jack-detect) */
157
static void q6usb_connector_control_init(struct snd_soc_component *component)
158
{
151
{
159
    int ret;
152
@@ -XXX,XX +XXX,XX @@ static int q6usb_component_probe(struct snd_soc_component *component)
160
153
        return -ENOMEM;
161
+    ret = snd_ctl_add(component->card->snd_card,
154
162
+                snd_ctl_new1(&q6usb_offload_control, component));
155
    usb->connection_status_cb = q6usb_alsa_connection_cb;
163
+    if (ret < 0)
156
+    usb->update_offload_route_info = q6usb_update_offload_route;
164
+        return;
157
165
+
158
    snd_soc_usb_add_port(usb);
166
    ret = snd_ctl_add(component->card->snd_card,
159
    data->usb = usb;
167
                snd_ctl_new1(&q6usb_offload_dev_ctrl, component));
168
    if (ret < 0)
169
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
170
171
    mutex_lock(&data->mutex);
172
    if (connected) {
173
-        /* We only track the latest USB headset plugged in */
174
-        if (!data->idx_valid || data->sel_card_idx < 0)
175
+        /*
176
+         * Update the latest USB headset plugged in, if session is
177
+         * idle.
178
+         */
179
+        if ((!data->idx_valid || data->sel_card_idx < 0) &&
180
+            !data->status[data->sel_card_idx].prepared)
181
            data->sel_card_idx = sdev->card_idx;
182
183
        set_bit(sdev->card_idx, &data->available_card_slot);
diff view generated by jsdifflib
...
...
3
definitions, so the QMI interface driver is able to route the QMI packet
3
definitions, so the QMI interface driver is able to route the QMI packet
4
received to the USB audio offload driver.
4
received to the USB audio offload driver.
5
5
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
---
7
---
8
sound/usb/qcom/usb_audio_qmi_v01.c | 892 +++++++++++++++++++++++++++++
8
sound/usb/qcom/usb_audio_qmi_v01.c | 863 +++++++++++++++++++++++++++++
9
sound/usb/qcom/usb_audio_qmi_v01.h | 162 ++++++
9
sound/usb/qcom/usb_audio_qmi_v01.h | 164 ++++++
10
2 files changed, 1054 insertions(+)
10
2 files changed, 1027 insertions(+)
11
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.c
11
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.c
12
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.h
12
create mode 100644 sound/usb/qcom/usb_audio_qmi_v01.h
13
13
14
diff --git a/sound/usb/qcom/usb_audio_qmi_v01.c b/sound/usb/qcom/usb_audio_qmi_v01.c
14
diff --git a/sound/usb/qcom/usb_audio_qmi_v01.c b/sound/usb/qcom/usb_audio_qmi_v01.c
15
new file mode 100644
15
new file mode 100644
16
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
17
--- /dev/null
17
--- /dev/null
18
+++ b/sound/usb/qcom/usb_audio_qmi_v01.c
18
+++ b/sound/usb/qcom/usb_audio_qmi_v01.c
19
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
20
+// SPDX-License-Identifier: GPL-2.0
20
+// SPDX-License-Identifier: GPL-2.0
21
+/*
21
+/*
22
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
22
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
23
+ */
23
+ */
24
+
24
+
25
+#include <linux/soc/qcom/qmi.h>
25
+#include <linux/soc/qcom/qmi.h>
26
+
26
+
27
+#include "usb_audio_qmi_v01.h"
27
+#include "usb_audio_qmi_v01.h"
28
+
28
+
29
+static struct qmi_elem_info mem_info_v01_ei[] = {
29
+static const struct qmi_elem_info mem_info_v01_ei[] = {
30
+    {
30
+    {
31
+        .data_type    = QMI_UNSIGNED_8_BYTE,
31
+        .data_type    = QMI_UNSIGNED_8_BYTE,
32
+        .elem_len    = 1,
32
+        .elem_len    = 1,
33
+        .elem_size    = sizeof(u64),
33
+        .elem_size    = sizeof(u64),
34
+        .array_type    = NO_ARRAY,
34
+        .array_type    = NO_ARRAY,
...
...
56
+        .array_type    = NO_ARRAY,
56
+        .array_type    = NO_ARRAY,
57
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
57
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
58
+    },
58
+    },
59
+};
59
+};
60
+
60
+
61
+static struct qmi_elem_info apps_mem_info_v01_ei[] = {
61
+static const struct qmi_elem_info apps_mem_info_v01_ei[] = {
62
+    {
62
+    {
63
+        .data_type    = QMI_STRUCT,
63
+        .data_type    = QMI_STRUCT,
64
+        .elem_len    = 1,
64
+        .elem_len    = 1,
65
+        .elem_size    = sizeof(struct mem_info_v01),
65
+        .elem_size    = sizeof(struct mem_info_v01),
66
+        .array_type    = NO_ARRAY,
66
+        .array_type    = NO_ARRAY,
...
...
109
+        .array_type    = NO_ARRAY,
109
+        .array_type    = NO_ARRAY,
110
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
110
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
111
+    },
111
+    },
112
+};
112
+};
113
+
113
+
114
+static struct qmi_elem_info usb_endpoint_descriptor_v01_ei[] = {
114
+static const struct qmi_elem_info usb_endpoint_descriptor_v01_ei[] = {
115
+    {
115
+    {
116
+        .data_type    = QMI_UNSIGNED_1_BYTE,
116
+        .data_type    = QMI_UNSIGNED_1_BYTE,
117
+        .elem_len    = 1,
117
+        .elem_len    = 1,
118
+        .elem_size    = sizeof(u8),
118
+        .elem_size    = sizeof(u8),
119
+        .array_type    = NO_ARRAY,
119
+        .array_type    = NO_ARRAY,
...
...
189
+        .array_type    = NO_ARRAY,
189
+        .array_type    = NO_ARRAY,
190
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
190
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
191
+    },
191
+    },
192
+};
192
+};
193
+
193
+
194
+static struct qmi_elem_info usb_interface_descriptor_v01_ei[] = {
194
+static const struct qmi_elem_info usb_interface_descriptor_v01_ei[] = {
195
+    {
195
+    {
196
+        .data_type    = QMI_UNSIGNED_1_BYTE,
196
+        .data_type    = QMI_UNSIGNED_1_BYTE,
197
+        .elem_len    = 1,
197
+        .elem_len    = 1,
198
+        .elem_size    = sizeof(u8),
198
+        .elem_size    = sizeof(u8),
199
+        .array_type    = NO_ARRAY,
199
+        .array_type    = NO_ARRAY,
...
...
278
+        .array_type    = NO_ARRAY,
278
+        .array_type    = NO_ARRAY,
279
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
279
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
280
+    },
280
+    },
281
+};
281
+};
282
+
282
+
283
+struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
283
+const struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[] = {
284
+    {
284
+    {
285
+        .data_type    = QMI_UNSIGNED_1_BYTE,
285
+        .data_type    = QMI_UNSIGNED_1_BYTE,
286
+        .elem_len    = 1,
286
+        .elem_len    = 1,
287
+        .elem_size    = sizeof(u8),
287
+        .elem_size    = sizeof(u8),
288
+        .array_type    = NO_ARRAY,
288
+        .array_type    = NO_ARRAY,
...
...
394
+        .array_type    = NO_ARRAY,
394
+        .array_type    = NO_ARRAY,
395
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
395
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
396
+    },
396
+    },
397
+};
397
+};
398
+
398
+
399
+struct qmi_elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
399
+const struct qmi_elem_info qmi_uaudio_stream_resp_msg_v01_ei[] = {
400
+    {
400
+    {
401
+        .data_type    = QMI_STRUCT,
401
+        .data_type    = QMI_STRUCT,
402
+        .elem_len    = 1,
402
+        .elem_len    = 1,
403
+        .elem_size    = sizeof(struct qmi_response_type_v01),
403
+        .elem_size    = sizeof(struct qmi_response_type_v01),
404
+        .array_type    = NO_ARRAY,
404
+        .array_type    = NO_ARRAY,
405
+        .tlv_type    = 0x02,
405
+        .tlv_type    = 0x02,
406
+        .offset        = offsetof(
406
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
407
+                    struct qmi_uaudio_stream_resp_msg_v01,
408
+                    resp),
407
+                    resp),
409
+        .ei_array    = qmi_response_type_v01_ei,
408
+        .ei_array    = qmi_response_type_v01_ei,
410
+    },
409
+    },
411
+    {
410
+    {
412
+        .data_type    = QMI_OPT_FLAG,
411
+        .data_type    = QMI_OPT_FLAG,
413
+        .elem_len    = 1,
412
+        .elem_len    = 1,
414
+        .elem_size    = sizeof(u8),
413
+        .elem_size    = sizeof(u8),
415
+        .array_type    = NO_ARRAY,
414
+        .array_type    = NO_ARRAY,
416
+        .tlv_type    = 0x10,
415
+        .tlv_type    = 0x10,
417
+        .offset        = offsetof(
416
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
418
+                    struct qmi_uaudio_stream_resp_msg_v01,
419
+                    status_valid),
417
+                    status_valid),
420
+    },
418
+    },
421
+    {
419
+    {
422
+        .data_type    = QMI_SIGNED_4_BYTE_ENUM,
420
+        .data_type    = QMI_SIGNED_4_BYTE_ENUM,
423
+        .elem_len    = 1,
421
+        .elem_len    = 1,
424
+        .elem_size    = sizeof(enum usb_qmi_audio_stream_status_enum_v01),
422
+        .elem_size    = sizeof(enum usb_qmi_audio_stream_status_enum_v01),
425
+        .array_type    = NO_ARRAY,
423
+        .array_type    = NO_ARRAY,
426
+        .tlv_type    = 0x10,
424
+        .tlv_type    = 0x10,
427
+        .offset        = offsetof(
425
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
428
+                    struct qmi_uaudio_stream_resp_msg_v01,
429
+                    status),
426
+                    status),
430
+    },
427
+    },
431
+    {
428
+    {
432
+        .data_type    = QMI_OPT_FLAG,
429
+        .data_type    = QMI_OPT_FLAG,
433
+        .elem_len    = 1,
430
+        .elem_len    = 1,
434
+        .elem_size    = sizeof(u8),
431
+        .elem_size    = sizeof(u8),
435
+        .array_type    = NO_ARRAY,
432
+        .array_type    = NO_ARRAY,
436
+        .tlv_type    = 0x11,
433
+        .tlv_type    = 0x11,
437
+        .offset        = offsetof(
434
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
438
+                    struct qmi_uaudio_stream_resp_msg_v01,
439
+                    internal_status_valid),
435
+                    internal_status_valid),
440
+    },
436
+    },
441
+    {
437
+    {
442
+        .data_type    = QMI_UNSIGNED_4_BYTE,
438
+        .data_type    = QMI_UNSIGNED_4_BYTE,
443
+        .elem_len    = 1,
439
+        .elem_len    = 1,
444
+        .elem_size    = sizeof(u32),
440
+        .elem_size    = sizeof(u32),
445
+        .array_type    = NO_ARRAY,
441
+        .array_type    = NO_ARRAY,
446
+        .tlv_type    = 0x11,
442
+        .tlv_type    = 0x11,
447
+        .offset        = offsetof(
443
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
448
+                    struct qmi_uaudio_stream_resp_msg_v01,
449
+                    internal_status),
444
+                    internal_status),
450
+    },
445
+    },
451
+    {
446
+    {
452
+        .data_type    = QMI_OPT_FLAG,
447
+        .data_type    = QMI_OPT_FLAG,
453
+        .elem_len    = 1,
448
+        .elem_len    = 1,
454
+        .elem_size    = sizeof(u8),
449
+        .elem_size    = sizeof(u8),
455
+        .array_type    = NO_ARRAY,
450
+        .array_type    = NO_ARRAY,
456
+        .tlv_type    = 0x12,
451
+        .tlv_type    = 0x12,
457
+        .offset        = offsetof(
452
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
458
+                    struct qmi_uaudio_stream_resp_msg_v01,
459
+                    slot_id_valid),
453
+                    slot_id_valid),
460
+    },
454
+    },
461
+    {
455
+    {
462
+        .data_type    = QMI_UNSIGNED_4_BYTE,
456
+        .data_type    = QMI_UNSIGNED_4_BYTE,
463
+        .elem_len    = 1,
457
+        .elem_len    = 1,
464
+        .elem_size    = sizeof(u32),
458
+        .elem_size    = sizeof(u32),
465
+        .array_type    = NO_ARRAY,
459
+        .array_type    = NO_ARRAY,
466
+        .tlv_type    = 0x12,
460
+        .tlv_type    = 0x12,
467
+        .offset        = offsetof(
461
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
468
+                    struct qmi_uaudio_stream_resp_msg_v01,
469
+                    slot_id),
462
+                    slot_id),
470
+    },
463
+    },
471
+    {
464
+    {
472
+        .data_type    = QMI_OPT_FLAG,
465
+        .data_type    = QMI_OPT_FLAG,
473
+        .elem_len    = 1,
466
+        .elem_len    = 1,
474
+        .elem_size    = sizeof(u8),
467
+        .elem_size    = sizeof(u8),
475
+        .array_type    = NO_ARRAY,
468
+        .array_type    = NO_ARRAY,
476
+        .tlv_type    = 0x13,
469
+        .tlv_type    = 0x13,
477
+        .offset        = offsetof(
470
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
478
+                    struct qmi_uaudio_stream_resp_msg_v01,
479
+                    usb_token_valid),
471
+                    usb_token_valid),
480
+    },
472
+    },
481
+    {
473
+    {
482
+        .data_type    = QMI_UNSIGNED_4_BYTE,
474
+        .data_type    = QMI_UNSIGNED_4_BYTE,
483
+        .elem_len    = 1,
475
+        .elem_len    = 1,
484
+        .elem_size    = sizeof(u32),
476
+        .elem_size    = sizeof(u32),
485
+        .array_type    = NO_ARRAY,
477
+        .array_type    = NO_ARRAY,
486
+        .tlv_type    = 0x13,
478
+        .tlv_type    = 0x13,
487
+        .offset        = offsetof(
479
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
488
+                    struct qmi_uaudio_stream_resp_msg_v01,
489
+                    usb_token),
480
+                    usb_token),
490
+    },
481
+    },
491
+    {
482
+    {
492
+        .data_type    = QMI_OPT_FLAG,
483
+        .data_type    = QMI_OPT_FLAG,
493
+        .elem_len    = 1,
484
+        .elem_len    = 1,
494
+        .elem_size    = sizeof(u8),
485
+        .elem_size    = sizeof(u8),
495
+        .array_type    = NO_ARRAY,
486
+        .array_type    = NO_ARRAY,
496
+        .tlv_type    = 0x14,
487
+        .tlv_type    = 0x14,
497
+        .offset        = offsetof(
488
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
498
+                    struct qmi_uaudio_stream_resp_msg_v01,
499
+                    std_as_opr_intf_desc_valid),
489
+                    std_as_opr_intf_desc_valid),
500
+    },
490
+    },
501
+    {
491
+    {
502
+        .data_type    = QMI_STRUCT,
492
+        .data_type    = QMI_STRUCT,
503
+        .elem_len    = 1,
493
+        .elem_len    = 1,
504
+        .elem_size    = sizeof(struct usb_interface_descriptor_v01),
494
+        .elem_size    = sizeof(struct usb_interface_descriptor_v01),
505
+        .array_type    = NO_ARRAY,
495
+        .array_type    = NO_ARRAY,
506
+        .tlv_type    = 0x14,
496
+        .tlv_type    = 0x14,
507
+        .offset        = offsetof(
497
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
508
+                    struct qmi_uaudio_stream_resp_msg_v01,
509
+                    std_as_opr_intf_desc),
498
+                    std_as_opr_intf_desc),
510
+        .ei_array    = usb_interface_descriptor_v01_ei,
499
+        .ei_array    = usb_interface_descriptor_v01_ei,
511
+    },
500
+    },
512
+    {
501
+    {
513
+        .data_type    = QMI_OPT_FLAG,
502
+        .data_type    = QMI_OPT_FLAG,
514
+        .elem_len    = 1,
503
+        .elem_len    = 1,
515
+        .elem_size    = sizeof(u8),
504
+        .elem_size    = sizeof(u8),
516
+        .array_type    = NO_ARRAY,
505
+        .array_type    = NO_ARRAY,
517
+        .tlv_type    = 0x15,
506
+        .tlv_type    = 0x15,
518
+        .offset        = offsetof(
507
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
519
+                    struct qmi_uaudio_stream_resp_msg_v01,
520
+                    std_as_data_ep_desc_valid),
508
+                    std_as_data_ep_desc_valid),
521
+    },
509
+    },
522
+    {
510
+    {
523
+        .data_type    = QMI_STRUCT,
511
+        .data_type    = QMI_STRUCT,
524
+        .elem_len    = 1,
512
+        .elem_len    = 1,
525
+        .elem_size    = sizeof(struct usb_endpoint_descriptor_v01),
513
+        .elem_size    = sizeof(struct usb_endpoint_descriptor_v01),
526
+        .array_type    = NO_ARRAY,
514
+        .array_type    = NO_ARRAY,
527
+        .tlv_type    = 0x15,
515
+        .tlv_type    = 0x15,
528
+        .offset        = offsetof(
516
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
529
+                    struct qmi_uaudio_stream_resp_msg_v01,
530
+                    std_as_data_ep_desc),
517
+                    std_as_data_ep_desc),
531
+        .ei_array    = usb_endpoint_descriptor_v01_ei,
518
+        .ei_array    = usb_endpoint_descriptor_v01_ei,
532
+    },
519
+    },
533
+    {
520
+    {
534
+        .data_type    = QMI_OPT_FLAG,
521
+        .data_type    = QMI_OPT_FLAG,
535
+        .elem_len    = 1,
522
+        .elem_len    = 1,
536
+        .elem_size    = sizeof(u8),
523
+        .elem_size    = sizeof(u8),
537
+        .array_type    = NO_ARRAY,
524
+        .array_type    = NO_ARRAY,
538
+        .tlv_type    = 0x16,
525
+        .tlv_type    = 0x16,
539
+        .offset        = offsetof(
526
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
540
+                    struct qmi_uaudio_stream_resp_msg_v01,
541
+                    std_as_sync_ep_desc_valid),
527
+                    std_as_sync_ep_desc_valid),
542
+    },
528
+    },
543
+    {
529
+    {
544
+        .data_type    = QMI_STRUCT,
530
+        .data_type    = QMI_STRUCT,
545
+        .elem_len    = 1,
531
+        .elem_len    = 1,
546
+        .elem_size    = sizeof(struct usb_endpoint_descriptor_v01),
532
+        .elem_size    = sizeof(struct usb_endpoint_descriptor_v01),
547
+        .array_type    = NO_ARRAY,
533
+        .array_type    = NO_ARRAY,
548
+        .tlv_type    = 0x16,
534
+        .tlv_type    = 0x16,
549
+        .offset        = offsetof(
535
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
550
+                    struct qmi_uaudio_stream_resp_msg_v01,
551
+                    std_as_sync_ep_desc),
536
+                    std_as_sync_ep_desc),
552
+        .ei_array    = usb_endpoint_descriptor_v01_ei,
537
+        .ei_array    = usb_endpoint_descriptor_v01_ei,
553
+    },
538
+    },
554
+    {
539
+    {
555
+        .data_type    = QMI_OPT_FLAG,
540
+        .data_type    = QMI_OPT_FLAG,
556
+        .elem_len    = 1,
541
+        .elem_len    = 1,
557
+        .elem_size    = sizeof(u8),
542
+        .elem_size    = sizeof(u8),
558
+        .array_type    = NO_ARRAY,
543
+        .array_type    = NO_ARRAY,
559
+        .tlv_type    = 0x17,
544
+        .tlv_type    = 0x17,
560
+        .offset        = offsetof(
545
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
561
+                    struct qmi_uaudio_stream_resp_msg_v01,
562
+                    usb_audio_spec_revision_valid),
546
+                    usb_audio_spec_revision_valid),
563
+    },
547
+    },
564
+    {
548
+    {
565
+        .data_type    = QMI_UNSIGNED_2_BYTE,
549
+        .data_type    = QMI_UNSIGNED_2_BYTE,
566
+        .elem_len    = 1,
550
+        .elem_len    = 1,
567
+        .elem_size    = sizeof(u16),
551
+        .elem_size    = sizeof(u16),
568
+        .array_type    = NO_ARRAY,
552
+        .array_type    = NO_ARRAY,
569
+        .tlv_type    = 0x17,
553
+        .tlv_type    = 0x17,
570
+        .offset        = offsetof(
554
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
571
+                    struct qmi_uaudio_stream_resp_msg_v01,
572
+                    usb_audio_spec_revision),
555
+                    usb_audio_spec_revision),
573
+    },
556
+    },
574
+    {
557
+    {
575
+        .data_type    = QMI_OPT_FLAG,
558
+        .data_type    = QMI_OPT_FLAG,
576
+        .elem_len    = 1,
559
+        .elem_len    = 1,
577
+        .elem_size    = sizeof(u8),
560
+        .elem_size    = sizeof(u8),
578
+        .array_type    = NO_ARRAY,
561
+        .array_type    = NO_ARRAY,
579
+        .tlv_type    = 0x18,
562
+        .tlv_type    = 0x18,
580
+        .offset        = offsetof(
563
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
581
+                    struct qmi_uaudio_stream_resp_msg_v01,
582
+                    data_path_delay_valid),
564
+                    data_path_delay_valid),
583
+    },
565
+    },
584
+    {
566
+    {
585
+        .data_type    = QMI_UNSIGNED_1_BYTE,
567
+        .data_type    = QMI_UNSIGNED_1_BYTE,
586
+        .elem_len    = 1,
568
+        .elem_len    = 1,
587
+        .elem_size    = sizeof(u8),
569
+        .elem_size    = sizeof(u8),
588
+        .array_type    = NO_ARRAY,
570
+        .array_type    = NO_ARRAY,
589
+        .tlv_type    = 0x18,
571
+        .tlv_type    = 0x18,
590
+        .offset        = offsetof(
572
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
591
+                    struct qmi_uaudio_stream_resp_msg_v01,
592
+                    data_path_delay),
573
+                    data_path_delay),
593
+    },
574
+    },
594
+    {
575
+    {
595
+        .data_type    = QMI_OPT_FLAG,
576
+        .data_type    = QMI_OPT_FLAG,
596
+        .elem_len    = 1,
577
+        .elem_len    = 1,
597
+        .elem_size    = sizeof(u8),
578
+        .elem_size    = sizeof(u8),
598
+        .array_type    = NO_ARRAY,
579
+        .array_type    = NO_ARRAY,
599
+        .tlv_type    = 0x19,
580
+        .tlv_type    = 0x19,
600
+        .offset        = offsetof(
581
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
601
+                    struct qmi_uaudio_stream_resp_msg_v01,
602
+                    usb_audio_subslot_size_valid),
582
+                    usb_audio_subslot_size_valid),
603
+    },
583
+    },
604
+    {
584
+    {
605
+        .data_type    = QMI_UNSIGNED_1_BYTE,
585
+        .data_type    = QMI_UNSIGNED_1_BYTE,
606
+        .elem_len    = 1,
586
+        .elem_len    = 1,
607
+        .elem_size    = sizeof(u8),
587
+        .elem_size    = sizeof(u8),
608
+        .array_type    = NO_ARRAY,
588
+        .array_type    = NO_ARRAY,
609
+        .tlv_type    = 0x19,
589
+        .tlv_type    = 0x19,
610
+        .offset        = offsetof(
590
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
611
+                    struct qmi_uaudio_stream_resp_msg_v01,
612
+                    usb_audio_subslot_size),
591
+                    usb_audio_subslot_size),
613
+    },
592
+    },
614
+    {
593
+    {
615
+        .data_type    = QMI_OPT_FLAG,
594
+        .data_type    = QMI_OPT_FLAG,
616
+        .elem_len    = 1,
595
+        .elem_len    = 1,
617
+        .elem_size    = sizeof(u8),
596
+        .elem_size    = sizeof(u8),
618
+        .array_type    = NO_ARRAY,
597
+        .array_type    = NO_ARRAY,
619
+        .tlv_type    = 0x1A,
598
+        .tlv_type    = 0x1A,
620
+        .offset        = offsetof(
599
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
621
+                    struct qmi_uaudio_stream_resp_msg_v01,
622
+                    xhci_mem_info_valid),
600
+                    xhci_mem_info_valid),
623
+    },
601
+    },
624
+    {
602
+    {
625
+        .data_type    = QMI_STRUCT,
603
+        .data_type    = QMI_STRUCT,
626
+        .elem_len    = 1,
604
+        .elem_len    = 1,
627
+        .elem_size    = sizeof(struct apps_mem_info_v01),
605
+        .elem_size    = sizeof(struct apps_mem_info_v01),
628
+        .array_type    = NO_ARRAY,
606
+        .array_type    = NO_ARRAY,
629
+        .tlv_type    = 0x1A,
607
+        .tlv_type    = 0x1A,
630
+        .offset        = offsetof(
608
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
631
+                    struct qmi_uaudio_stream_resp_msg_v01,
632
+                    xhci_mem_info),
609
+                    xhci_mem_info),
633
+        .ei_array    = apps_mem_info_v01_ei,
610
+        .ei_array    = apps_mem_info_v01_ei,
634
+    },
611
+    },
635
+    {
612
+    {
636
+        .data_type    = QMI_OPT_FLAG,
613
+        .data_type    = QMI_OPT_FLAG,
637
+        .elem_len    = 1,
614
+        .elem_len    = 1,
638
+        .elem_size    = sizeof(u8),
615
+        .elem_size    = sizeof(u8),
639
+        .array_type    = NO_ARRAY,
616
+        .array_type    = NO_ARRAY,
640
+        .tlv_type    = 0x1B,
617
+        .tlv_type    = 0x1B,
641
+        .offset        = offsetof(
618
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
642
+                    struct qmi_uaudio_stream_resp_msg_v01,
643
+                    interrupter_num_valid),
619
+                    interrupter_num_valid),
644
+    },
620
+    },
645
+    {
621
+    {
646
+        .data_type    = QMI_UNSIGNED_1_BYTE,
622
+        .data_type    = QMI_UNSIGNED_1_BYTE,
647
+        .elem_len    = 1,
623
+        .elem_len    = 1,
648
+        .elem_size    = sizeof(u8),
624
+        .elem_size    = sizeof(u8),
649
+        .array_type    = NO_ARRAY,
625
+        .array_type    = NO_ARRAY,
650
+        .tlv_type    = 0x1B,
626
+        .tlv_type    = 0x1B,
651
+        .offset        = offsetof(
627
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
652
+                    struct qmi_uaudio_stream_resp_msg_v01,
653
+                    interrupter_num),
628
+                    interrupter_num),
654
+    },
629
+    },
655
+    {
630
+    {
656
+        .data_type    = QMI_OPT_FLAG,
631
+        .data_type    = QMI_OPT_FLAG,
657
+        .elem_len    = 1,
632
+        .elem_len    = 1,
658
+        .elem_size    = sizeof(u8),
633
+        .elem_size    = sizeof(u8),
659
+        .array_type    = NO_ARRAY,
634
+        .array_type    = NO_ARRAY,
660
+        .tlv_type    = 0x1C,
635
+        .tlv_type    = 0x1C,
661
+        .offset        = offsetof(
636
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
662
+                    struct qmi_uaudio_stream_resp_msg_v01,
663
+                    speed_info_valid),
637
+                    speed_info_valid),
664
+    },
638
+    },
665
+    {
639
+    {
666
+        .data_type    = QMI_SIGNED_4_BYTE_ENUM,
640
+        .data_type    = QMI_SIGNED_4_BYTE_ENUM,
667
+        .elem_len    = 1,
641
+        .elem_len    = 1,
668
+        .elem_size    = sizeof(enum usb_qmi_audio_device_speed_enum_v01),
642
+        .elem_size    = sizeof(enum usb_qmi_audio_device_speed_enum_v01),
669
+        .array_type    = NO_ARRAY,
643
+        .array_type    = NO_ARRAY,
670
+        .tlv_type    = 0x1C,
644
+        .tlv_type    = 0x1C,
671
+        .offset        = offsetof(
645
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
672
+                    struct qmi_uaudio_stream_resp_msg_v01,
673
+                    speed_info),
646
+                    speed_info),
674
+    },
647
+    },
675
+    {
648
+    {
676
+        .data_type    = QMI_OPT_FLAG,
649
+        .data_type    = QMI_OPT_FLAG,
677
+        .elem_len    = 1,
650
+        .elem_len    = 1,
678
+        .elem_size    = sizeof(u8),
651
+        .elem_size    = sizeof(u8),
679
+        .array_type    = NO_ARRAY,
652
+        .array_type    = NO_ARRAY,
680
+        .tlv_type    = 0x1D,
653
+        .tlv_type    = 0x1D,
681
+        .offset        = offsetof(
654
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
682
+                    struct qmi_uaudio_stream_resp_msg_v01,
683
+                    controller_num_valid),
655
+                    controller_num_valid),
684
+    },
656
+    },
685
+    {
657
+    {
686
+        .data_type    = QMI_UNSIGNED_1_BYTE,
658
+        .data_type    = QMI_UNSIGNED_1_BYTE,
687
+        .elem_len    = 1,
659
+        .elem_len    = 1,
688
+        .elem_size    = sizeof(u8),
660
+        .elem_size    = sizeof(u8),
689
+        .array_type    = NO_ARRAY,
661
+        .array_type    = NO_ARRAY,
690
+        .tlv_type    = 0x1D,
662
+        .tlv_type    = 0x1D,
691
+        .offset        = offsetof(
663
+        .offset        = offsetof(struct qmi_uaudio_stream_resp_msg_v01,
692
+                    struct qmi_uaudio_stream_resp_msg_v01,
693
+                    controller_num),
664
+                    controller_num),
694
+    },
665
+    },
695
+    {
666
+    {
696
+        .data_type    = QMI_EOTI,
667
+        .data_type    = QMI_EOTI,
697
+        .array_type    = NO_ARRAY,
668
+        .array_type    = NO_ARRAY,
698
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
669
+        .tlv_type    = QMI_COMMON_TLV_TYPE,
699
+    },
670
+    },
700
+};
671
+};
701
+
672
+
702
+struct qmi_elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
673
+const struct qmi_elem_info qmi_uaudio_stream_ind_msg_v01_ei[] = {
703
+    {
674
+    {
704
+        .data_type    = QMI_SIGNED_4_BYTE_ENUM,
675
+        .data_type    = QMI_SIGNED_4_BYTE_ENUM,
705
+        .elem_len    = 1,
676
+        .elem_len    = 1,
706
+        .elem_size    = sizeof(
677
+        .elem_size    = sizeof(
707
+                enum usb_qmi_audio_device_indication_enum_v01),
678
+                enum usb_qmi_audio_device_indication_enum_v01),
...
...
915
--- /dev/null
886
--- /dev/null
916
+++ b/sound/usb/qcom/usb_audio_qmi_v01.h
887
+++ b/sound/usb/qcom/usb_audio_qmi_v01.h
917
@@ -XXX,XX +XXX,XX @@
888
@@ -XXX,XX +XXX,XX @@
918
+/* SPDX-License-Identifier: GPL-2.0
889
+/* SPDX-License-Identifier: GPL-2.0
919
+ *
890
+ *
920
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
891
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
921
+ */
892
+ */
922
+
893
+
923
+#ifndef USB_QMI_V01_H
894
+#ifndef USB_QMI_V01_H
924
+#define USB_QMI_V01_H
895
+#define USB_QMI_V01_H
925
+
896
+
926
+#define UAUDIO_STREAM_SERVICE_ID_V01 0x41D
897
+#define UAUDIO_STREAM_SERVICE_ID_V01 0x41D
927
+#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01
898
+#define UAUDIO_STREAM_SERVICE_VERS_V01 0x01
928
+
899
+
929
+#define QMI_UAUDIO_STREAM_RESP_V01 0x0001
900
+#define QMI_UAUDIO_STREAM_RESP_V01 0x0001
930
+#define QMI_UAUDIO_STREAM_REQ_V01 0x0001
901
+#define QMI_UAUDIO_STREAM_REQ_V01 0x0001
931
+#define QMI_UAUDIO_STREAM_IND_V01 0x0001
902
+#define QMI_UAUDIO_STREAM_IND_V01 0x0001
932
+
933
+
903
+
934
+struct mem_info_v01 {
904
+struct mem_info_v01 {
935
+    u64 va;
905
+    u64 va;
936
+    u64 pa;
906
+    u64 pa;
937
+    u32 size;
907
+    u32 size;
...
...
1010
+    u8 xfer_buff_size_valid;
980
+    u8 xfer_buff_size_valid;
1011
+    u32 xfer_buff_size;
981
+    u32 xfer_buff_size;
1012
+    u8 service_interval_valid;
982
+    u8 service_interval_valid;
1013
+    u32 service_interval;
983
+    u32 service_interval;
1014
+};
984
+};
985
+
1015
+#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46
986
+#define QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN 46
1016
+extern struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[];
987
+extern const struct qmi_elem_info qmi_uaudio_stream_req_msg_v01_ei[];
1017
+
988
+
1018
+struct qmi_uaudio_stream_resp_msg_v01 {
989
+struct qmi_uaudio_stream_resp_msg_v01 {
1019
+    struct qmi_response_type_v01 resp;
990
+    struct qmi_response_type_v01 resp;
1020
+    u8 status_valid;
991
+    u8 status_valid;
1021
+    enum usb_qmi_audio_stream_status_enum_v01 status;
992
+    enum usb_qmi_audio_stream_status_enum_v01 status;
...
...
1044
+    u8 speed_info_valid;
1015
+    u8 speed_info_valid;
1045
+    enum usb_qmi_audio_device_speed_enum_v01 speed_info;
1016
+    enum usb_qmi_audio_device_speed_enum_v01 speed_info;
1046
+    u8 controller_num_valid;
1017
+    u8 controller_num_valid;
1047
+    u8 controller_num;
1018
+    u8 controller_num;
1048
+};
1019
+};
1020
+
1049
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 202
1021
+#define QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN 202
1050
+extern struct qmi_elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
1022
+extern const struct qmi_elem_info qmi_uaudio_stream_resp_msg_v01_ei[];
1051
+
1023
+
1052
+struct qmi_uaudio_stream_ind_msg_v01 {
1024
+struct qmi_uaudio_stream_ind_msg_v01 {
1053
+    enum usb_qmi_audio_device_indication_enum_v01 dev_event;
1025
+    enum usb_qmi_audio_device_indication_enum_v01 dev_event;
1054
+    u32 slot_id;
1026
+    u32 slot_id;
1055
+    u8 usb_token_valid;
1027
+    u8 usb_token_valid;
...
...
1071
+    u8 interrupter_num_valid;
1043
+    u8 interrupter_num_valid;
1072
+    u8 interrupter_num;
1044
+    u8 interrupter_num;
1073
+    u8 controller_num_valid;
1045
+    u8 controller_num_valid;
1074
+    u8 controller_num;
1046
+    u8 controller_num;
1075
+};
1047
+};
1048
+
1076
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 181
1049
+#define QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN 181
1077
+extern struct qmi_elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
1050
+extern const struct qmi_elem_info qmi_uaudio_stream_ind_msg_v01_ei[];
1078
+
1051
+
1079
+#endif
1052
+#endif
diff view generated by jsdifflib
...
...
23
4. The QC audio offload driver will fetch the required resources, and pass
23
4. The QC audio offload driver will fetch the required resources, and pass
24
this information as part of the QMI response to the STREAM enable command.
24
this information as part of the QMI response to the STREAM enable command.
25
5. Once the QMI response is received the audio DSP will start queuing data
25
5. Once the QMI response is received the audio DSP will start queuing data
26
on the USB bus.
26
on the USB bus.
27
27
28
As part of step#2, the audio DSP is aware of the USB SND card and pcm
29
device index that is being selected, and is communicated as part of the QMI
30
request received by QC audio offload. These indices will be used to handle
31
the stream enable QMI request.
32
28
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
33
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
29
---
34
---
30
include/sound/soc-usb.h | 2 +-
35
sound/usb/Kconfig | 14 +
31
sound/usb/Kconfig | 15 +
32
sound/usb/Makefile | 2 +-
36
sound/usb/Makefile | 2 +-
33
sound/usb/qcom/Makefile | 2 +
37
sound/usb/qcom/Makefile | 2 +
34
sound/usb/qcom/qc_audio_offload.c | 1843 +++++++++++++++++++++++++++++
38
sound/usb/qcom/qc_audio_offload.c | 1988 +++++++++++++++++++++++++++++
35
5 files changed, 1862 insertions(+), 2 deletions(-)
39
4 files changed, 2005 insertions(+), 1 deletion(-)
36
create mode 100644 sound/usb/qcom/Makefile
40
create mode 100644 sound/usb/qcom/Makefile
37
create mode 100644 sound/usb/qcom/qc_audio_offload.c
41
create mode 100644 sound/usb/qcom/qc_audio_offload.c
38
42
39
diff --git a/include/sound/soc-usb.h b/include/sound/soc-usb.h
40
index XXXXXXX..XXXXXXX 100644
41
--- a/include/sound/soc-usb.h
42
+++ b/include/sound/soc-usb.h
43
@@ -XXX,XX +XXX,XX @@ struct snd_soc_usb {
44
45
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev);
46
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev);
47
-void *snd_soc_usb_get_priv_data(struct device *usbdev);
48
+void *snd_soc_usb_find_priv_data(struct device *usbdev);
49
50
struct snd_soc_usb *snd_soc_usb_add_port(struct device *dev, void *priv,
51
            int (*connection_cb)(struct snd_soc_usb *usb,
52
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
43
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
53
index XXXXXXX..XXXXXXX 100644
44
index XXXXXXX..XXXXXXX 100644
54
--- a/sound/usb/Kconfig
45
--- a/sound/usb/Kconfig
55
+++ b/sound/usb/Kconfig
46
+++ b/sound/usb/Kconfig
56
@@ -XXX,XX +XXX,XX @@ config SND_BCD2000
47
@@ -XXX,XX +XXX,XX @@ config SND_BCD2000
57
     To compile this driver as a module, choose M here: the module
48
     To compile this driver as a module, choose M here: the module
58
     will be called snd-bcd2000.
49
     will be called snd-bcd2000.
59
50
60
+config SND_USB_AUDIO_QMI
51
+config SND_USB_AUDIO_QMI
61
+    tristate "Qualcomm Audio Offload driver"
52
+    tristate "Qualcomm Audio Offload driver"
62
+    depends on QCOM_QMI_HELPERS && SND_USB_AUDIO && USB_XHCI_SIDEBAND
53
+    depends on QCOM_QMI_HELPERS && SND_USB_AUDIO && USB_XHCI_SIDEBAND && SND_SOC_USB
63
+    select SND_PCM
64
+    help
54
+    help
65
+     Say Y here to enable the Qualcomm USB audio offloading feature.
55
+     Say Y here to enable the Qualcomm USB audio offloading feature.
66
+
56
+
67
+     This module sets up the required QMI stream enable/disable
57
+     This module sets up the required QMI stream enable/disable
68
+     responses to requests generated by the audio DSP. It passes the
58
+     responses to requests generated by the audio DSP. It passes the
...
...
90
new file mode 100644
80
new file mode 100644
91
index XXXXXXX..XXXXXXX
81
index XXXXXXX..XXXXXXX
92
--- /dev/null
82
--- /dev/null
93
+++ b/sound/usb/qcom/Makefile
83
+++ b/sound/usb/qcom/Makefile
94
@@ -XXX,XX +XXX,XX @@
84
@@ -XXX,XX +XXX,XX @@
95
+snd-usb-audio-qmi-objs := usb_audio_qmi_v01.o qc_audio_offload.o
85
+snd-usb-audio-qmi-y := usb_audio_qmi_v01.o qc_audio_offload.o
96
+obj-$(CONFIG_SND_USB_AUDIO_QMI) += snd-usb-audio-qmi.o
86
+obj-$(CONFIG_SND_USB_AUDIO_QMI) += snd-usb-audio-qmi.o
97
\ No newline at end of file
98
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
87
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
99
new file mode 100644
88
new file mode 100644
100
index XXXXXXX..XXXXXXX
89
index XXXXXXX..XXXXXXX
101
--- /dev/null
90
--- /dev/null
102
+++ b/sound/usb/qcom/qc_audio_offload.c
91
+++ b/sound/usb/qcom/qc_audio_offload.c
103
@@ -XXX,XX +XXX,XX @@
92
@@ -XXX,XX +XXX,XX @@
104
+// SPDX-License-Identifier: GPL-2.0
93
+// SPDX-License-Identifier: GPL-2.0
105
+/*
94
+/*
106
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
95
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
107
+ */
96
+ */
108
+
97
+
109
+#include <linux/ctype.h>
98
+#include <linux/ctype.h>
99
+#include <linux/dma-mapping.h>
100
+#include <linux/dma-map-ops.h>
101
+#include <linux/init.h>
102
+#include <linux/iommu.h>
103
+#include <linux/module.h>
110
+#include <linux/moduleparam.h>
104
+#include <linux/moduleparam.h>
111
+#include <linux/module.h>
105
+#include <linux/soc/qcom/qmi.h>
112
+#include <linux/usb.h>
106
+#include <linux/usb.h>
113
+#include <linux/init.h>
114
+#include <linux/usb/hcd.h>
115
+#include <linux/usb/xhci-sideband.h>
116
+#include <linux/usb/quirks.h>
117
+#include <linux/usb/audio.h>
107
+#include <linux/usb/audio.h>
118
+#include <linux/usb/audio-v2.h>
108
+#include <linux/usb/audio-v2.h>
119
+#include <linux/usb/audio-v3.h>
109
+#include <linux/usb/audio-v3.h>
120
+#include <linux/soc/qcom/qmi.h>
110
+#include <linux/usb/hcd.h>
121
+#include <linux/iommu.h>
111
+#include <linux/usb/quirks.h>
122
+#include <linux/dma-mapping.h>
112
+#include <linux/usb/xhci-sideband.h>
123
+#include <linux/dma-map-ops.h>
124
+#include <sound/q6usboffload.h>
125
+
113
+
126
+#include <sound/control.h>
114
+#include <sound/control.h>
127
+#include <sound/core.h>
115
+#include <sound/core.h>
128
+#include <sound/info.h>
116
+#include <sound/info.h>
117
+#include <sound/initval.h>
129
+#include <sound/pcm.h>
118
+#include <sound/pcm.h>
130
+#include <sound/pcm_params.h>
119
+#include <sound/pcm_params.h>
131
+#include <sound/initval.h>
120
+#include <sound/q6usboffload.h>
132
+
133
+#include <sound/soc.h>
121
+#include <sound/soc.h>
134
+#include <sound/soc-usb.h>
122
+#include <sound/soc-usb.h>
123
+
135
+#include "../usbaudio.h"
124
+#include "../usbaudio.h"
136
+#include "../card.h"
125
+#include "../card.h"
137
+#include "../endpoint.h"
126
+#include "../endpoint.h"
127
+#include "../format.h"
138
+#include "../helper.h"
128
+#include "../helper.h"
139
+#include "../pcm.h"
129
+#include "../pcm.h"
140
+#include "../format.h"
141
+#include "../power.h"
130
+#include "../power.h"
131
+
142
+#include "usb_audio_qmi_v01.h"
132
+#include "usb_audio_qmi_v01.h"
143
+
133
+
144
+/* Stream disable request timeout during USB device disconnect */
134
+/* Stream disable request timeout during USB device disconnect */
145
+#define DEV_RELEASE_WAIT_TIMEOUT 10000 /* in ms */
135
+#define DEV_RELEASE_WAIT_TIMEOUT 10000 /* in ms */
146
+
136
+
...
...
154
+#define QMI_STREAM_REQ_DIRECTION 0xff
144
+#define QMI_STREAM_REQ_DIRECTION 0xff
155
+
145
+
156
+/* iommu resource parameters and management */
146
+/* iommu resource parameters and management */
157
+#define PREPEND_SID_TO_IOVA(iova, sid) ((u64)(((u64)(iova)) | \
147
+#define PREPEND_SID_TO_IOVA(iova, sid) ((u64)(((u64)(iova)) | \
158
+                    (((u64)sid) << 32)))
148
+                    (((u64)sid) << 32)))
149
+#define IOVA_MASK(iova) (((u64)(iova)) & 0xFFFFFFFF)
159
+#define IOVA_BASE 0x1000
150
+#define IOVA_BASE 0x1000
160
+#define IOVA_XFER_RING_BASE (IOVA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
151
+#define IOVA_XFER_RING_BASE (IOVA_BASE + PAGE_SIZE * (SNDRV_CARDS + 1))
161
+#define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32)
152
+#define IOVA_XFER_BUF_BASE (IOVA_XFER_RING_BASE + PAGE_SIZE * SNDRV_CARDS * 32)
162
+#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
153
+#define IOVA_XFER_RING_MAX (IOVA_XFER_BUF_BASE - PAGE_SIZE)
163
+#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
154
+#define IOVA_XFER_BUF_MAX (0xfffff000 - PAGE_SIZE)
...
...
170
+    size_t size;
161
+    size_t size;
171
+    bool in_use;
162
+    bool in_use;
172
+};
163
+};
173
+
164
+
174
+struct intf_info {
165
+struct intf_info {
166
+    /* IOMMU ring/buffer mapping information */
175
+    unsigned long data_xfer_ring_va;
167
+    unsigned long data_xfer_ring_va;
176
+    size_t data_xfer_ring_size;
168
+    size_t data_xfer_ring_size;
177
+    unsigned long sync_xfer_ring_va;
169
+    unsigned long sync_xfer_ring_va;
178
+    size_t sync_xfer_ring_size;
170
+    size_t sync_xfer_ring_size;
179
+    unsigned long xfer_buf_va;
171
+    unsigned long xfer_buf_va;
180
+    size_t xfer_buf_size;
172
+    size_t xfer_buf_size;
181
+    phys_addr_t xfer_buf_pa;
173
+    phys_addr_t xfer_buf_pa;
174
+    u8 *xfer_buf;
175
+
176
+    /* USB endpoint information */
182
+    unsigned int data_ep_pipe;
177
+    unsigned int data_ep_pipe;
183
+    unsigned int sync_ep_pipe;
178
+    unsigned int sync_ep_pipe;
184
+    u8 *xfer_buf;
179
+    unsigned int data_ep_idx;
180
+    unsigned int sync_ep_idx;
181
+
185
+    u8 intf_num;
182
+    u8 intf_num;
186
+    u8 pcm_card_num;
183
+    u8 pcm_card_num;
187
+    u8 pcm_dev_num;
184
+    u8 pcm_dev_num;
188
+    u8 direction;
185
+    u8 direction;
189
+    bool in_use;
186
+    bool in_use;
190
+};
187
+};
191
+
188
+
192
+struct uaudio_qmi_dev {
189
+struct uaudio_qmi_dev {
193
+    struct device *dev;
190
+    struct device *dev;
194
+    u32 sid;
191
+    struct q6usb_offload *data;
195
+    u32 intr_num;
196
+    struct xhci_ring *sec_ring;
197
+    struct iommu_domain *domain;
198
+
192
+
199
+    /* list to keep track of available iova */
193
+    /* list to keep track of available iova */
200
+    struct list_head xfer_ring_list;
194
+    struct list_head xfer_ring_list;
201
+    size_t xfer_ring_iova_size;
195
+    size_t xfer_ring_iova_size;
202
+    unsigned long curr_xfer_ring_iova;
196
+    unsigned long curr_xfer_ring_iova;
...
...
208
+    unsigned long card_slot;
202
+    unsigned long card_slot;
209
+    /* indicate event ring mapped or not */
203
+    /* indicate event ring mapped or not */
210
+    bool er_mapped;
204
+    bool er_mapped;
211
+    /* reference count to number of possible consumers */
205
+    /* reference count to number of possible consumers */
212
+    atomic_t qdev_in_use;
206
+    atomic_t qdev_in_use;
213
+    /* idx to last udev card number plugged in */
214
+    unsigned int last_card_num;
215
+};
207
+};
216
+
208
+
217
+struct uaudio_dev {
209
+struct uaudio_dev {
218
+    struct usb_device *udev;
210
+    struct usb_device *udev;
219
+    /* audio control interface */
211
+    /* audio control interface */
...
...
240
+static struct uaudio_qmi_svc *uaudio_svc;
232
+static struct uaudio_qmi_svc *uaudio_svc;
241
+static DEFINE_MUTEX(qdev_mutex);
233
+static DEFINE_MUTEX(qdev_mutex);
242
+
234
+
243
+struct uaudio_qmi_svc {
235
+struct uaudio_qmi_svc {
244
+    struct qmi_handle *uaudio_svc_hdl;
236
+    struct qmi_handle *uaudio_svc_hdl;
245
+    struct work_struct qmi_disconnect_work;
246
+    struct workqueue_struct *uaudio_wq;
247
+    struct sockaddr_qrtr client_sq;
237
+    struct sockaddr_qrtr client_sq;
248
+    bool client_connected;
238
+    bool client_connected;
249
+};
239
+};
250
+
240
+
251
+enum mem_type {
241
+enum mem_type {
...
...
274
+    USB_QMI_PCM_FORMAT_S32_BE,
264
+    USB_QMI_PCM_FORMAT_S32_BE,
275
+    USB_QMI_PCM_FORMAT_U32_LE,
265
+    USB_QMI_PCM_FORMAT_U32_LE,
276
+    USB_QMI_PCM_FORMAT_U32_BE,
266
+    USB_QMI_PCM_FORMAT_U32_BE,
277
+};
267
+};
278
+
268
+
269
+static int usb_qmi_get_pcm_num(struct snd_usb_audio *chip, int direction)
270
+{
271
+    struct snd_usb_substream *subs = NULL;
272
+    struct snd_usb_stream *as;
273
+    int count = 0;
274
+
275
+    list_for_each_entry(as, &chip->pcm_list, list) {
276
+        subs = &as->substream[direction];
277
+        if (subs->ep_num)
278
+            count++;
279
+    }
280
+
281
+    return count;
282
+}
283
+
279
+static enum usb_qmi_audio_device_speed_enum_v01
284
+static enum usb_qmi_audio_device_speed_enum_v01
280
+get_speed_info(enum usb_device_speed udev_speed)
285
+get_speed_info(enum usb_device_speed udev_speed)
281
+{
286
+{
282
+    switch (udev_speed) {
287
+    switch (udev_speed) {
283
+    case USB_SPEED_LOW:
288
+    case USB_SPEED_LOW:
...
...
294
+        return USB_QMI_DEVICE_SPEED_INVALID_V01;
299
+        return USB_QMI_DEVICE_SPEED_INVALID_V01;
295
+    }
300
+    }
296
+}
301
+}
297
+
302
+
298
+static struct snd_usb_substream *find_substream(unsigned int card_num,
303
+static struct snd_usb_substream *find_substream(unsigned int card_num,
299
+    unsigned int pcm_idx, unsigned int direction)
304
+                        unsigned int pcm_idx,
300
+{
305
+                        unsigned int direction)
301
+    struct snd_usb_stream *as;
306
+{
302
+    struct snd_usb_substream *subs = NULL;
307
+    struct snd_usb_substream *subs = NULL;
303
+    struct snd_usb_audio *chip;
308
+    struct snd_usb_audio *chip;
309
+    struct snd_usb_stream *as;
304
+
310
+
305
+    chip = uadev[card_num].chip;
311
+    chip = uadev[card_num].chip;
306
+    if (!chip || atomic_read(&chip->shutdown))
312
+    if (!chip || atomic_read(&chip->shutdown))
307
+        goto done;
313
+        goto done;
308
+
314
+
...
...
336
+
342
+
337
+    for (i = 0; i < uadev[card_num].num_intf; i++) {
343
+    for (i = 0; i < uadev[card_num].num_intf; i++) {
338
+        if (enable && !uadev[card_num].info[i].in_use)
344
+        if (enable && !uadev[card_num].info[i].in_use)
339
+            return i;
345
+            return i;
340
+        else if (!enable &&
346
+        else if (!enable &&
341
+                uadev[card_num].info[i].intf_num == intf_num)
347
+             uadev[card_num].info[i].intf_num == intf_num)
342
+            return i;
348
+            return i;
343
+    }
349
+    }
344
+
350
+
345
+    return -EINVAL;
351
+    return -EINVAL;
346
+}
352
+}
347
+
353
+
348
+static int get_data_interval_from_si(struct snd_usb_substream *subs,
354
+static int get_data_interval_from_si(struct snd_usb_substream *subs,
349
+    u32 service_interval)
355
+                 u32 service_interval)
350
+{
356
+{
351
+    unsigned int bus_intval, bus_intval_mult, binterval;
357
+    unsigned int bus_intval_mult;
358
+    unsigned int bus_intval;
359
+    unsigned int binterval;
352
+
360
+
353
+    if (subs->dev->speed >= USB_SPEED_HIGH)
361
+    if (subs->dev->speed >= USB_SPEED_HIGH)
354
+        bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE;
362
+        bus_intval = BUS_INTERVAL_HIGHSPEED_AND_ABOVE;
355
+    else
363
+    else
356
+        bus_intval = BUS_INTERVAL_FULL_SPEED;
364
+        bus_intval = BUS_INTERVAL_FULL_SPEED;
...
...
420
+         */
428
+         */
421
+        return SNDRV_PCM_FORMAT_S8;
429
+        return SNDRV_PCM_FORMAT_S8;
422
+    }
430
+    }
423
+}
431
+}
424
+
432
+
433
+/*
434
+ * Sends QMI disconnect indication message, assumes chip->mutex and qdev_mutex
435
+ * lock held by caller.
436
+ */
437
+static int uaudio_send_disconnect_ind(struct snd_usb_audio *chip)
438
+{
439
+    struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0};
440
+    struct uaudio_qmi_svc *svc = uaudio_svc;
441
+    struct uaudio_dev *dev;
442
+    int ret = 0;
443
+
444
+    dev = &uadev[chip->card->number];
445
+
446
+    if (atomic_read(&dev->in_use)) {
447
+        mutex_unlock(&chip->mutex);
448
+        mutex_unlock(&qdev_mutex);
449
+        dev_dbg(uaudio_qdev->data->dev, "sending qmi indication suspend\n");
450
+        disconnect_ind.dev_event = USB_QMI_DEV_DISCONNECT_V01;
451
+        disconnect_ind.slot_id = dev->udev->slot_id;
452
+        disconnect_ind.controller_num = dev->usb_core_id;
453
+        disconnect_ind.controller_num_valid = 1;
454
+        ret = qmi_send_indication(svc->uaudio_svc_hdl, &svc->client_sq,
455
+                     QMI_UAUDIO_STREAM_IND_V01,
456
+                     QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN,
457
+                     qmi_uaudio_stream_ind_msg_v01_ei,
458
+                     &disconnect_ind);
459
+        if (ret < 0)
460
+            dev_err(uaudio_qdev->data->dev,
461
+                "qmi send failed with err: %d\n", ret);
462
+
463
+        ret = wait_event_interruptible_timeout(dev->disconnect_wq,
464
+                !atomic_read(&dev->in_use),
465
+                msecs_to_jiffies(DEV_RELEASE_WAIT_TIMEOUT));
466
+        if (!ret) {
467
+            dev_err(uaudio_qdev->data->dev,
468
+                "timeout while waiting for dev_release\n");
469
+            atomic_set(&dev->in_use, 0);
470
+        } else if (ret < 0) {
471
+            dev_err(uaudio_qdev->data->dev,
472
+                "failed with ret %d\n", ret);
473
+                atomic_set(&dev->in_use, 0);
474
+        }
475
+        mutex_lock(&qdev_mutex);
476
+        mutex_lock(&chip->mutex);
477
+    }
478
+
479
+    return ret;
480
+}
481
+
425
+/* Offloading IOMMU management */
482
+/* Offloading IOMMU management */
426
+static unsigned long uaudio_get_iova(unsigned long *curr_iova,
483
+static unsigned long uaudio_get_iova(unsigned long *curr_iova,
427
+    size_t *curr_iova_size, struct list_head *head, size_t size)
484
+                 size_t *curr_iova_size,
485
+                 struct list_head *head, size_t size)
428
+{
486
+{
429
+    struct iova_info *info, *new_info = NULL;
487
+    struct iova_info *info, *new_info = NULL;
430
+    struct list_head *curr_head;
488
+    struct list_head *curr_head;
489
+    size_t tmp_size = size;
431
+    unsigned long va = 0;
490
+    unsigned long va = 0;
432
+    size_t tmp_size = size;
491
+
433
+    bool found = false;
492
+    if (size % PAGE_SIZE)
434
+
435
+    if (size % PAGE_SIZE) {
436
+        dev_dbg(uaudio_qdev->dev, "size %zu is not page size multiple\n",
437
+            size);
438
+        goto done;
493
+        goto done;
439
+    }
494
+
440
+
495
+    if (size > *curr_iova_size)
441
+    if (size > *curr_iova_size) {
442
+        dev_dbg(uaudio_qdev->dev, "size %zu > curr size %zu\n",
443
+            size, *curr_iova_size);
444
+        goto done;
496
+        goto done;
445
+    }
497
+
446
+    if (*curr_iova_size == 0) {
498
+    if (*curr_iova_size == 0)
447
+        dev_dbg(uaudio_qdev->dev, "iova mapping is full\n");
448
+        goto done;
499
+        goto done;
449
+    }
450
+
500
+
451
+    list_for_each_entry(info, head, list) {
501
+    list_for_each_entry(info, head, list) {
452
+        /* exact size iova_info */
502
+        /* exact size iova_info */
453
+        if (!info->in_use && info->size == size) {
503
+        if (!info->in_use && info->size == size) {
454
+            info->in_use = true;
504
+            info->in_use = true;
455
+            va = info->start_iova;
505
+            va = info->start_iova;
456
+            *curr_iova_size -= size;
506
+            *curr_iova_size -= size;
457
+            found = true;
458
+            dev_dbg(uaudio_qdev->dev, "exact size: %zu found\n", size);
459
+            goto done;
507
+            goto done;
460
+        } else if (!info->in_use && tmp_size >= info->size) {
508
+        } else if (!info->in_use && tmp_size >= info->size) {
461
+            if (!new_info)
509
+            if (!new_info)
462
+                new_info = info;
510
+                new_info = info;
463
+            dev_dbg(uaudio_qdev->dev, "partial size: %zu found\n",
464
+                    info->size);
465
+            tmp_size -= info->size;
511
+            tmp_size -= info->size;
466
+            if (tmp_size)
512
+            if (tmp_size)
467
+                continue;
513
+                continue;
468
+
514
+
469
+            va = new_info->start_iova;
515
+            va = new_info->start_iova;
...
...
473
+                        iova_info, list);
519
+                        iova_info, list);
474
+                new_info->in_use = true;
520
+                new_info->in_use = true;
475
+            }
521
+            }
476
+            info->in_use = true;
522
+            info->in_use = true;
477
+            *curr_iova_size -= size;
523
+            *curr_iova_size -= size;
478
+            found = true;
479
+            goto done;
524
+            goto done;
480
+        } else {
525
+        } else {
481
+            /* iova region in use */
526
+            /* iova region in use */
482
+            new_info = NULL;
527
+            new_info = NULL;
483
+            tmp_size = size;
528
+            tmp_size = size;
484
+        }
529
+        }
485
+    }
530
+    }
486
+
531
+
487
+    info = kzalloc(sizeof(struct iova_info), GFP_KERNEL);
532
+    info = kzalloc(sizeof(*info), GFP_KERNEL);
488
+    if (!info) {
533
+    if (!info) {
489
+        va = 0;
534
+        va = 0;
490
+        goto done;
535
+        goto done;
491
+    }
536
+    }
492
+
537
+
493
+    va = info->start_iova = *curr_iova;
538
+    va = *curr_iova;
539
+    info->start_iova = *curr_iova;
494
+    info->size = size;
540
+    info->size = size;
495
+    info->in_use = true;
541
+    info->in_use = true;
496
+    *curr_iova += size;
542
+    *curr_iova += size;
497
+    *curr_iova_size -= size;
543
+    *curr_iova_size -= size;
498
+    found = true;
499
+    list_add_tail(&info->list, head);
544
+    list_add_tail(&info->list, head);
500
+
545
+
501
+done:
546
+done:
502
+    if (!found)
503
+        dev_err(uaudio_qdev->dev, "unable to find %zu size iova\n",
504
+            size);
505
+    else
506
+        dev_dbg(uaudio_qdev->dev,
507
+            "va:0x%08lx curr_iova:0x%08lx curr_iova_size:%zu\n",
508
+            va, *curr_iova, *curr_iova_size);
509
+
510
+    return va;
547
+    return va;
511
+}
548
+}
512
+
549
+
513
+static void uaudio_put_iova(unsigned long va, size_t size, struct list_head
550
+static void uaudio_put_iova(unsigned long va, size_t size, struct list_head
514
+    *head, size_t *curr_iova_size)
551
+    *head, size_t *curr_iova_size)
...
...
517
+    size_t tmp_size = size;
554
+    size_t tmp_size = size;
518
+    bool found = false;
555
+    bool found = false;
519
+
556
+
520
+    list_for_each_entry(info, head, list) {
557
+    list_for_each_entry(info, head, list) {
521
+        if (info->start_iova == va) {
558
+        if (info->start_iova == va) {
522
+            if (!info->in_use) {
559
+            if (!info->in_use)
523
+                dev_err(uaudio_qdev->dev, "va %lu is not in use\n",
524
+                    va);
525
+                return;
560
+                return;
526
+            }
561
+
527
+            found = true;
562
+            found = true;
528
+            info->in_use = false;
563
+            info->in_use = false;
529
+            if (info->size == size)
564
+            if (info->size == size)
530
+                goto done;
565
+                goto done;
531
+        }
566
+        }
...
...
536
+            if (!tmp_size)
571
+            if (!tmp_size)
537
+                goto done;
572
+                goto done;
538
+        }
573
+        }
539
+    }
574
+    }
540
+
575
+
541
+    if (!found) {
576
+    if (!found)
542
+        dev_err(uaudio_qdev->dev, "unable to find the va %lu\n", va);
543
+        return;
577
+        return;
544
+    }
578
+
545
+done:
579
+done:
546
+    *curr_iova_size += size;
580
+    *curr_iova_size += size;
547
+    dev_dbg(uaudio_qdev->dev, "curr_iova_size %zu\n", *curr_iova_size);
548
+}
581
+}
549
+
582
+
550
+/**
583
+/**
551
+ * uaudio_iommu_unmap() - unmaps iommu memory for adsp
584
+ * uaudio_iommu_unmap() - unmaps iommu memory for adsp
552
+ * @mtype: ring type
585
+ * @mtype: ring type
...
...
556
+ *
589
+ *
557
+ * Unmaps the memory region that was previously assigned to the adsp.
590
+ * Unmaps the memory region that was previously assigned to the adsp.
558
+ *
591
+ *
559
+ */
592
+ */
560
+static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
593
+static void uaudio_iommu_unmap(enum mem_type mtype, unsigned long va,
561
+    size_t iova_size, size_t mapped_iova_size)
594
+             size_t iova_size, size_t mapped_iova_size)
562
+{
595
+{
563
+    size_t umap_size;
596
+    size_t umap_size;
564
+    bool unmap = true;
597
+    bool unmap = true;
565
+
598
+
566
+    if (!va || !iova_size)
599
+    if (!va || !iova_size)
...
...
574
+            unmap = false;
607
+            unmap = false;
575
+        break;
608
+        break;
576
+
609
+
577
+    case MEM_XFER_RING:
610
+    case MEM_XFER_RING:
578
+        uaudio_put_iova(va, iova_size, &uaudio_qdev->xfer_ring_list,
611
+        uaudio_put_iova(va, iova_size, &uaudio_qdev->xfer_ring_list,
579
+        &uaudio_qdev->xfer_ring_iova_size);
612
+                &uaudio_qdev->xfer_ring_iova_size);
580
+        break;
613
+        break;
581
+    case MEM_XFER_BUF:
614
+    case MEM_XFER_BUF:
582
+        uaudio_put_iova(va, iova_size, &uaudio_qdev->xfer_buf_list,
615
+        uaudio_put_iova(va, iova_size, &uaudio_qdev->xfer_buf_list,
583
+        &uaudio_qdev->xfer_buf_iova_size);
616
+                &uaudio_qdev->xfer_buf_iova_size);
584
+        break;
617
+        break;
585
+    default:
618
+    default:
586
+        dev_err(uaudio_qdev->dev, "unknown mem type %d\n", mtype);
587
+        unmap = false;
619
+        unmap = false;
588
+    }
620
+    }
589
+
621
+
590
+    if (!unmap || !mapped_iova_size)
622
+    if (!unmap || !mapped_iova_size)
591
+        return;
623
+        return;
592
+
624
+
593
+    dev_dbg(uaudio_qdev->dev, "type %d: unmap iova 0x%08lx size %zu\n",
625
+    umap_size = iommu_unmap(uaudio_qdev->data->domain, va, mapped_iova_size);
594
+        mtype, va, mapped_iova_size);
595
+
596
+    umap_size = iommu_unmap(uaudio_qdev->domain, va, mapped_iova_size);
597
+    if (umap_size != mapped_iova_size)
626
+    if (umap_size != mapped_iova_size)
598
+        dev_err(uaudio_qdev->dev,
627
+        dev_err(uaudio_qdev->data->dev,
599
+            "unmapped size %zu for iova 0x%08lx of mapped size %zu\n",
628
+            "unmapped size %zu for iova 0x%08lx of mapped size %zu\n",
600
+            umap_size, va, mapped_iova_size);
629
+            umap_size, va, mapped_iova_size);
601
+}
630
+}
602
+
631
+
603
+/**
632
+/**
...
...
612
+ * used by the adsp. This will be mapped to the domain, which is created by
641
+ * used by the adsp. This will be mapped to the domain, which is created by
613
+ * the ASoC USB backend driver.
642
+ * the ASoC USB backend driver.
614
+ *
643
+ *
615
+ */
644
+ */
616
+static unsigned long uaudio_iommu_map(enum mem_type mtype, bool dma_coherent,
645
+static unsigned long uaudio_iommu_map(enum mem_type mtype, bool dma_coherent,
617
+        phys_addr_t pa, size_t size, struct sg_table *sgt)
646
+                 phys_addr_t pa, size_t size,
618
+{
647
+                 struct sg_table *sgt)
619
+    unsigned long va_sg, va = 0;
648
+{
649
+    struct scatterlist *sg;
650
+    unsigned long va = 0;
651
+    size_t total_len = 0;
652
+    unsigned long va_sg;
653
+    phys_addr_t pa_sg;
620
+    bool map = true;
654
+    bool map = true;
621
+    int i, ret;
655
+    size_t sg_len;
622
+    size_t sg_len, total_len = 0;
656
+    int prot;
623
+    struct scatterlist *sg;
657
+    int ret;
624
+    phys_addr_t pa_sg;
658
+    int i;
625
+    int prot = IOMMU_READ | IOMMU_WRITE;
659
+
660
+    prot = IOMMU_READ | IOMMU_WRITE;
626
+
661
+
627
+    if (dma_coherent)
662
+    if (dma_coherent)
628
+        prot |= IOMMU_CACHE;
663
+        prot |= IOMMU_CACHE;
629
+
664
+
630
+    switch (mtype) {
665
+    switch (mtype) {
...
...
634
+        if (uaudio_qdev->er_mapped)
669
+        if (uaudio_qdev->er_mapped)
635
+            map = false;
670
+            map = false;
636
+        break;
671
+        break;
637
+    case MEM_XFER_RING:
672
+    case MEM_XFER_RING:
638
+        va = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova,
673
+        va = uaudio_get_iova(&uaudio_qdev->curr_xfer_ring_iova,
639
+        &uaudio_qdev->xfer_ring_iova_size, &uaudio_qdev->xfer_ring_list,
674
+                 &uaudio_qdev->xfer_ring_iova_size,
640
+        size);
675
+                 &uaudio_qdev->xfer_ring_list, size);
641
+        break;
676
+        break;
642
+    case MEM_XFER_BUF:
677
+    case MEM_XFER_BUF:
643
+        va = uaudio_get_iova(&uaudio_qdev->curr_xfer_buf_iova,
678
+        va = uaudio_get_iova(&uaudio_qdev->curr_xfer_buf_iova,
644
+        &uaudio_qdev->xfer_buf_iova_size, &uaudio_qdev->xfer_buf_list,
679
+                 &uaudio_qdev->xfer_buf_iova_size,
645
+        size);
680
+                 &uaudio_qdev->xfer_buf_list, size);
646
+        break;
681
+        break;
647
+    default:
682
+    default:
648
+        dev_err(uaudio_qdev->dev, "unknown mem type %d\n", mtype);
683
+        dev_err(uaudio_qdev->data->dev, "unknown mem type %d\n", mtype);
649
+    }
684
+    }
650
+
685
+
651
+    if (!va || !map)
686
+    if (!va || !map)
652
+        goto done;
687
+        goto done;
653
+
688
+
...
...
656
+
691
+
657
+    va_sg = va;
692
+    va_sg = va;
658
+    for_each_sg(sgt->sgl, sg, sgt->nents, i) {
693
+    for_each_sg(sgt->sgl, sg, sgt->nents, i) {
659
+        sg_len = PAGE_ALIGN(sg->offset + sg->length);
694
+        sg_len = PAGE_ALIGN(sg->offset + sg->length);
660
+        pa_sg = page_to_phys(sg_page(sg));
695
+        pa_sg = page_to_phys(sg_page(sg));
661
+        ret = iommu_map(uaudio_qdev->domain, va_sg, pa_sg, sg_len,
696
+        ret = iommu_map(uaudio_qdev->data->domain, va_sg, pa_sg, sg_len,
662
+                prot, GFP_KERNEL);
697
+                prot, GFP_KERNEL);
663
+        if (ret) {
698
+        if (ret) {
664
+            dev_err(uaudio_qdev->dev, "mapping failed ret%d\n", ret);
665
+            dev_err(uaudio_qdev->dev,
666
+                "type:%d, pa:%pa iova:0x%08lx sg_len:%zu\n",
667
+                mtype, &pa_sg, va_sg, sg_len);
668
+            uaudio_iommu_unmap(MEM_XFER_BUF, va, size, total_len);
699
+            uaudio_iommu_unmap(MEM_XFER_BUF, va, size, total_len);
669
+            va = 0;
700
+            va = 0;
670
+            goto done;
701
+            goto done;
671
+        }
702
+        }
672
+        dev_dbg(uaudio_qdev->dev,
703
+
673
+            "type:%d map pa:%pa to iova:0x%08lx len:%zu offset:%u\n",
674
+            mtype, &pa_sg, va_sg, sg_len, sg->offset);
675
+        va_sg += sg_len;
704
+        va_sg += sg_len;
676
+        total_len += sg_len;
705
+        total_len += sg_len;
677
+    }
706
+    }
678
+
707
+
679
+    if (size != total_len) {
708
+    if (size != total_len) {
680
+        dev_err(uaudio_qdev->dev, "iova size %zu != mapped iova size %zu\n",
681
+            size, total_len);
682
+        uaudio_iommu_unmap(MEM_XFER_BUF, va, size, total_len);
709
+        uaudio_iommu_unmap(MEM_XFER_BUF, va, size, total_len);
683
+        va = 0;
710
+        va = 0;
684
+    }
711
+    }
685
+    return va;
712
+    return va;
686
+
713
+
687
+skip_sgt_map:
714
+skip_sgt_map:
688
+    dev_dbg(uaudio_qdev->dev, "type:%d map pa:%pa to iova:0x%08lx size:%zu\n",
715
+    iommu_map(uaudio_qdev->data->domain, va, pa, size, prot, GFP_KERNEL);
689
+        mtype, &pa, va, size);
716
+
690
+
691
+    ret = iommu_map(uaudio_qdev->domain, va, pa, size, prot, GFP_KERNEL);
692
+    if (ret)
693
+        dev_err(uaudio_qdev->dev,
694
+            "failed to map pa:%pa iova:0x%lx type:%d ret:%d\n",
695
+            &pa, va, mtype, ret);
696
+done:
717
+done:
697
+    return va;
718
+    return va;
698
+}
719
+}
699
+
720
+
700
+/* looks up alias, if any, for controller DT node and returns the index */
721
+/* looks up alias, if any, for controller DT node and returns the index */
...
...
714
+ * Cleans up the transfer ring related resources which are assigned per
735
+ * Cleans up the transfer ring related resources which are assigned per
715
+ * endpoint from XHCI. This is invoked when the USB endpoints are no
736
+ * endpoint from XHCI. This is invoked when the USB endpoints are no
716
+ * longer in use by the adsp.
737
+ * longer in use by the adsp.
717
+ *
738
+ *
718
+ */
739
+ */
719
+static void uaudio_dev_intf_cleanup(struct usb_device *udev,
740
+static void uaudio_dev_intf_cleanup(struct usb_device *udev, struct intf_info *info)
720
+    struct intf_info *info)
721
+{
741
+{
722
+    uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
742
+    uaudio_iommu_unmap(MEM_XFER_RING, info->data_xfer_ring_va,
723
+        info->data_xfer_ring_size, info->data_xfer_ring_size);
743
+             info->data_xfer_ring_size, info->data_xfer_ring_size);
724
+    info->data_xfer_ring_va = 0;
744
+    info->data_xfer_ring_va = 0;
725
+    info->data_xfer_ring_size = 0;
745
+    info->data_xfer_ring_size = 0;
726
+
746
+
727
+    uaudio_iommu_unmap(MEM_XFER_RING, info->sync_xfer_ring_va,
747
+    uaudio_iommu_unmap(MEM_XFER_RING, info->sync_xfer_ring_va,
728
+        info->sync_xfer_ring_size, info->sync_xfer_ring_size);
748
+             info->sync_xfer_ring_size, info->sync_xfer_ring_size);
729
+    info->sync_xfer_ring_va = 0;
749
+    info->sync_xfer_ring_va = 0;
730
+    info->sync_xfer_ring_size = 0;
750
+    info->sync_xfer_ring_size = 0;
731
+
751
+
732
+    uaudio_iommu_unmap(MEM_XFER_BUF, info->xfer_buf_va,
752
+    uaudio_iommu_unmap(MEM_XFER_BUF, info->xfer_buf_va, info->xfer_buf_size,
733
+        info->xfer_buf_size, info->xfer_buf_size);
753
+             info->xfer_buf_size);
734
+    info->xfer_buf_va = 0;
754
+    info->xfer_buf_va = 0;
735
+
755
+
736
+    usb_free_coherent(udev, info->xfer_buf_size,
756
+    usb_free_coherent(udev, info->xfer_buf_size, info->xfer_buf,
737
+        info->xfer_buf, info->xfer_buf_pa);
757
+             info->xfer_buf_pa);
738
+    info->xfer_buf_size = 0;
758
+    info->xfer_buf_size = 0;
739
+    info->xfer_buf = NULL;
759
+    info->xfer_buf = NULL;
740
+    info->xfer_buf_pa = 0;
760
+    info->xfer_buf_pa = 0;
741
+
761
+
742
+    info->in_use = false;
762
+    info->in_use = false;
...
...
755
+{
775
+{
756
+    clear_bit(dev->chip->card->number, &uaudio_qdev->card_slot);
776
+    clear_bit(dev->chip->card->number, &uaudio_qdev->card_slot);
757
+    /* all audio devices are disconnected */
777
+    /* all audio devices are disconnected */
758
+    if (!uaudio_qdev->card_slot) {
778
+    if (!uaudio_qdev->card_slot) {
759
+        uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
779
+        uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE,
760
+            PAGE_SIZE);
780
+                 PAGE_SIZE);
761
+        xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb);
781
+        xhci_sideband_remove_interrupter(uadev[dev->chip->card->number].sb);
762
+    }
782
+    }
763
+}
783
+}
764
+
784
+
765
+static void uaudio_dev_cleanup(struct uaudio_dev *dev)
785
+static void uaudio_dev_cleanup(struct uaudio_dev *dev)
...
...
772
+    /* free xfer buffer and unmap xfer ring and buf per interface */
792
+    /* free xfer buffer and unmap xfer ring and buf per interface */
773
+    for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
793
+    for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
774
+        if (!dev->info[if_idx].in_use)
794
+        if (!dev->info[if_idx].in_use)
775
+            continue;
795
+            continue;
776
+        uaudio_dev_intf_cleanup(dev->udev, &dev->info[if_idx]);
796
+        uaudio_dev_intf_cleanup(dev->udev, &dev->info[if_idx]);
777
+        dev_dbg(uaudio_qdev->dev, "release resources: intf# %d card# %d\n",
797
+        dev_dbg(uaudio_qdev->data->dev,
798
+            "release resources: intf# %d card# %d\n",
778
+            dev->info[if_idx].intf_num, dev->chip->card->number);
799
+            dev->info[if_idx].intf_num, dev->chip->card->number);
779
+    }
800
+    }
780
+
801
+
781
+    dev->num_intf = 0;
802
+    dev->num_intf = 0;
782
+
803
+
...
...
802
+    snd_usb_hw_free(subs);
823
+    snd_usb_hw_free(subs);
803
+    snd_usb_autosuspend(chip);
824
+    snd_usb_autosuspend(chip);
804
+}
825
+}
805
+
826
+
806
+/* QMI service disconnect handlers */
827
+/* QMI service disconnect handlers */
807
+static void qmi_disconnect_work(struct work_struct *w)
828
+static void qmi_stop_session(void)
808
+{
829
+{
830
+    struct snd_usb_substream *subs;
831
+    struct usb_host_endpoint *ep;
832
+    struct snd_usb_audio *chip;
809
+    struct intf_info *info;
833
+    struct intf_info *info;
810
+    int idx, if_idx;
834
+    int pcm_card_num;
811
+    struct snd_usb_substream *subs;
835
+    int if_idx;
812
+    struct snd_usb_audio *chip;
836
+    int idx;
813
+
837
+
814
+    mutex_lock(&qdev_mutex);
838
+    mutex_lock(&qdev_mutex);
815
+    /* find all active intf for set alt 0 and cleanup usb audio dev */
839
+    /* find all active intf for set alt 0 and cleanup usb audio dev */
816
+    for (idx = 0; idx < SNDRV_CARDS; idx++) {
840
+    for (idx = 0; idx < SNDRV_CARDS; idx++) {
817
+        if (!atomic_read(&uadev[idx].in_use))
841
+        if (!atomic_read(&uadev[idx].in_use))
...
...
820
+        chip = uadev[idx].chip;
844
+        chip = uadev[idx].chip;
821
+        for (if_idx = 0; if_idx < uadev[idx].num_intf; if_idx++) {
845
+        for (if_idx = 0; if_idx < uadev[idx].num_intf; if_idx++) {
822
+            if (!uadev[idx].info || !uadev[idx].info[if_idx].in_use)
846
+            if (!uadev[idx].info || !uadev[idx].info[if_idx].in_use)
823
+                continue;
847
+                continue;
824
+            info = &uadev[idx].info[if_idx];
848
+            info = &uadev[idx].info[if_idx];
825
+            subs = find_substream(info->pcm_card_num,
849
+            pcm_card_num = info->pcm_card_num;
826
+                        info->pcm_dev_num,
850
+            subs = find_substream(pcm_card_num, info->pcm_dev_num,
827
+                        info->direction);
851
+                     info->direction);
828
+            if (!subs || !chip || atomic_read(&chip->shutdown)) {
852
+            if (!subs || !chip || atomic_read(&chip->shutdown)) {
829
+                dev_err(&subs->dev->dev,
853
+                dev_err(&subs->dev->dev,
830
+                    "no sub for c#%u dev#%u dir%u\n",
854
+                    "no sub for c#%u dev#%u dir%u\n",
831
+                    info->pcm_card_num,
855
+                    info->pcm_card_num,
832
+                    info->pcm_dev_num,
856
+                    info->pcm_dev_num,
833
+                    info->direction);
857
+                    info->direction);
834
+                continue;
858
+                continue;
835
+            }
859
+            }
860
+            /* Release XHCI endpoints */
861
+            if (info->data_ep_pipe)
862
+                ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
863
+                         info->data_ep_pipe);
864
+            xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
865
+
866
+            if (info->sync_ep_pipe)
867
+                ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
868
+                         info->sync_ep_pipe);
869
+            xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
870
+
836
+            disable_audio_stream(subs);
871
+            disable_audio_stream(subs);
837
+        }
872
+        }
838
+        atomic_set(&uadev[idx].in_use, 0);
873
+        atomic_set(&uadev[idx].in_use, 0);
839
+        mutex_lock(&chip->mutex);
874
+        mutex_lock(&chip->mutex);
840
+        uaudio_dev_cleanup(&uadev[idx]);
875
+        uaudio_dev_cleanup(&uadev[idx]);
841
+        mutex_unlock(&chip->mutex);
876
+        mutex_unlock(&chip->mutex);
842
+    }
877
+    }
843
+    mutex_unlock(&qdev_mutex);
878
+    mutex_unlock(&qdev_mutex);
844
+}
879
+}
845
+
880
+
846
+/**
881
+/**
882
+ * uaudio_sideband_notifier() - xHCI sideband event handler
883
+ * @intf: USB interface handle
884
+ * @evt: xHCI sideband event type
885
+ *
886
+ * This callback is executed when the xHCI sideband encounters a sequence
887
+ * that requires the sideband clients to take action. An example, is when
888
+ * xHCI frees the transfer ring, so the client has to ensure that the
889
+ * offload path is halted.
890
+ *
891
+ */
892
+static int uaudio_sideband_notifier(struct usb_interface *intf,
893
+                 struct xhci_sideband_event *evt)
894
+{
895
+    struct snd_usb_audio *chip;
896
+    struct uaudio_dev *dev;
897
+    int if_idx;
898
+
899
+    if (!intf || !evt)
900
+        return 0;
901
+
902
+    chip = usb_get_intfdata(intf);
903
+
904
+    mutex_lock(&qdev_mutex);
905
+    mutex_lock(&chip->mutex);
906
+
907
+    dev = &uadev[chip->card->number];
908
+
909
+    if (evt->type == XHCI_SIDEBAND_XFER_RING_FREE) {
910
+        unsigned int *ep = (unsigned int *) evt->evt_data;
911
+
912
+        for (if_idx = 0; if_idx < dev->num_intf; if_idx++) {
913
+            if (dev->info[if_idx].data_ep_idx == *ep ||
914
+             dev->info[if_idx].sync_ep_idx == *ep)
915
+                uaudio_send_disconnect_ind(chip);
916
+        }
917
+    }
918
+
919
+    mutex_unlock(&qdev_mutex);
920
+    mutex_unlock(&chip->mutex);
921
+
922
+    return 0;
923
+}
924
+
925
+/**
847
+ * qmi_bye_cb() - qmi bye message callback
926
+ * qmi_bye_cb() - qmi bye message callback
848
+ * @handle: QMI handle
927
+ * @handle: QMI handle
849
+ * @node: id of the dying node
928
+ * @node: id of the dying node
850
+ *
929
+ *
851
+ * This callback is invoked when the QMI bye control message is received
930
+ * This callback is invoked when the QMI bye control message is received
...
...
860
+
939
+
861
+    if (svc->uaudio_svc_hdl != handle)
940
+    if (svc->uaudio_svc_hdl != handle)
862
+        return;
941
+        return;
863
+
942
+
864
+    if (svc->client_connected && svc->client_sq.sq_node == node) {
943
+    if (svc->client_connected && svc->client_sq.sq_node == node) {
865
+        queue_work(svc->uaudio_wq, &svc->qmi_disconnect_work);
944
+        qmi_stop_session();
945
+
946
+        /* clear QMI client parameters to block further QMI messages */
866
+        svc->client_sq.sq_node = 0;
947
+        svc->client_sq.sq_node = 0;
867
+        svc->client_sq.sq_port = 0;
948
+        svc->client_sq.sq_port = 0;
868
+        svc->client_sq.sq_family = 0;
949
+        svc->client_sq.sq_family = 0;
869
+        svc->client_connected = false;
950
+        svc->client_connected = false;
870
+    }
951
+    }
...
...
884
+static void qmi_svc_disconnect_cb(struct qmi_handle *handle,
965
+static void qmi_svc_disconnect_cb(struct qmi_handle *handle,
885
+                 unsigned int node, unsigned int port)
966
+                 unsigned int node, unsigned int port)
886
+{
967
+{
887
+    struct uaudio_qmi_svc *svc;
968
+    struct uaudio_qmi_svc *svc;
888
+
969
+
889
+    if (uaudio_svc == NULL)
970
+    if (!uaudio_svc)
890
+        return;
971
+        return;
891
+
972
+
892
+    svc = uaudio_svc;
973
+    svc = uaudio_svc;
893
+    if (svc->uaudio_svc_hdl != handle)
974
+    if (svc->uaudio_svc_hdl != handle)
894
+        return;
975
+        return;
895
+
976
+
896
+    if (svc->client_connected && svc->client_sq.sq_node == node &&
977
+    if (svc->client_connected && svc->client_sq.sq_node == node &&
897
+            svc->client_sq.sq_port == port) {
978
+     svc->client_sq.sq_port == port) {
898
+        queue_work(svc->uaudio_wq, &svc->qmi_disconnect_work);
979
+        qmi_stop_session();
980
+
981
+        /* clear QMI client parameters to block further QMI messages */
899
+        svc->client_sq.sq_node = 0;
982
+        svc->client_sq.sq_node = 0;
900
+        svc->client_sq.sq_port = 0;
983
+        svc->client_sq.sq_port = 0;
901
+        svc->client_sq.sq_family = 0;
984
+        svc->client_sq.sq_family = 0;
902
+        svc->client_connected = false;
985
+        svc->client_connected = false;
903
+    }
986
+    }
...
...
932
+ * until the audio stream is stopped. Will issue the USB set interface control
1015
+ * until the audio stream is stopped. Will issue the USB set interface control
933
+ * message to enable the data interface.
1016
+ * message to enable the data interface.
934
+ *
1017
+ *
935
+ */
1018
+ */
936
+static int enable_audio_stream(struct snd_usb_substream *subs,
1019
+static int enable_audio_stream(struct snd_usb_substream *subs,
937
+                snd_pcm_format_t pcm_format,
1020
+             snd_pcm_format_t pcm_format,
938
+                unsigned int channels, unsigned int cur_rate,
1021
+             unsigned int channels, unsigned int cur_rate,
939
+                int datainterval)
1022
+             int datainterval)
940
+{
1023
+{
941
+    struct snd_usb_audio *chip = subs->stream->chip;
942
+    struct snd_pcm_hw_params params;
1024
+    struct snd_pcm_hw_params params;
1025
+    struct snd_usb_audio *chip;
1026
+    struct snd_interval *i;
943
+    struct snd_mask *m;
1027
+    struct snd_mask *m;
944
+    struct snd_interval *i;
945
+    int ret;
1028
+    int ret;
1029
+
1030
+    chip = subs->stream->chip;
946
+
1031
+
947
+    _snd_pcm_hw_params_any(&params);
1032
+    _snd_pcm_hw_params_any(&params);
948
+
1033
+
949
+    m = hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT);
1034
+    m = hw_param_mask(&params, SNDRV_PCM_HW_PARAM_FORMAT);
950
+    snd_mask_leave(m, pcm_format);
1035
+    snd_mask_leave(m, pcm_format);
951
+
1036
+
952
+    i = hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS);
1037
+    i = hw_param_interval(&params, SNDRV_PCM_HW_PARAM_CHANNELS);
953
+    snd_interval_setinteger(i);
1038
+    snd_interval_setinteger(i);
954
+    i->min = i->max = channels;
1039
+    i->min = channels;
1040
+    i->max = channels;
955
+
1041
+
956
+    i = hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE);
1042
+    i = hw_param_interval(&params, SNDRV_PCM_HW_PARAM_RATE);
957
+    snd_interval_setinteger(i);
1043
+    snd_interval_setinteger(i);
958
+    i->min = i->max = cur_rate;
1044
+    i->min = cur_rate;
1045
+    i->max = cur_rate;
959
+
1046
+
960
+    pm_runtime_barrier(&chip->intf[0]->dev);
1047
+    pm_runtime_barrier(&chip->intf[0]->dev);
961
+    snd_usb_autoresume(chip);
1048
+    snd_usb_autoresume(chip);
962
+
1049
+
963
+    ret = snd_usb_hw_params(subs, &params);
1050
+    ret = snd_usb_hw_params(subs, &params);
...
...
979
+        if (ret < 0)
1066
+        if (ret < 0)
980
+            goto unlock;
1067
+            goto unlock;
981
+
1068
+
982
+        snd_usb_unlock_shutdown(chip);
1069
+        snd_usb_unlock_shutdown(chip);
983
+
1070
+
984
+        dev_dbg(uaudio_qdev->dev,
1071
+        dev_dbg(uaudio_qdev->data->dev,
985
+            "selected %s iface:%d altsetting:%d datainterval:%dus\n",
1072
+            "selected %s iface:%d altsetting:%d datainterval:%dus\n",
986
+            subs->direction ? "capture" : "playback",
1073
+            subs->direction ? "capture" : "playback",
987
+            subs->cur_audiofmt->iface, subs->cur_audiofmt->altsetting,
1074
+            subs->cur_audiofmt->iface, subs->cur_audiofmt->altsetting,
988
+            (1 << subs->cur_audiofmt->datainterval) *
1075
+            (1 << subs->cur_audiofmt->datainterval) *
989
+            (subs->dev->speed >= USB_SPEED_HIGH ?
1076
+            (subs->dev->speed >= USB_SPEED_HIGH ?
...
...
1013
+
1100
+
1014
+    return NULL;
1101
+    return NULL;
1015
+}
1102
+}
1016
+
1103
+
1017
+/**
1104
+/**
1018
+ * prepare_qmi_response() - prepare stream enable response
1105
+ * uaudio_transfer_buffer_setup() - fetch and populate xfer buffer params
1019
+ * @subs: usb substream
1106
+ * @subs: usb substream
1020
+ * @req_msg: QMI request message
1107
+ * @xfer_buf: xfer buf to be allocated
1108
+ * @xfer_buf_len: size of allocation
1109
+ * @mem_info: QMI response info
1110
+ *
1111
+ * Allocates and maps the transfer buffers that will be utilized by the
1112
+ * audio DSP. Will populate the information in the QMI response that is
1113
+ * sent back to the stream enable request.
1114
+ *
1115
+ */
1116
+static int uaudio_transfer_buffer_setup(struct snd_usb_substream *subs,
1117
+                    u8 *xfer_buf, u32 xfer_buf_len,
1118
+                    struct mem_info_v01 *mem_info)
1119
+{
1120
+    struct sg_table xfer_buf_sgt;
1121
+    phys_addr_t xfer_buf_pa;
1122
+    u32 len = xfer_buf_len;
1123
+    bool dma_coherent;
1124
+    unsigned long va;
1125
+    u32 remainder;
1126
+    u32 mult;
1127
+    int ret;
1128
+
1129
+    dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
1130
+
1131
+    /* xfer buffer, multiple of 4K only */
1132
+    if (!len)
1133
+        len = PAGE_SIZE;
1134
+
1135
+    mult = len / PAGE_SIZE;
1136
+    remainder = len % PAGE_SIZE;
1137
+    len = mult * PAGE_SIZE;
1138
+    len += remainder ? PAGE_SIZE : 0;
1139
+
1140
+    if (len > MAX_XFER_BUFF_LEN) {
1141
+        dev_err(uaudio_qdev->data->dev,
1142
+            "req buf len %d > max buf len %lu, setting %lu\n",
1143
+            len, MAX_XFER_BUFF_LEN, MAX_XFER_BUFF_LEN);
1144
+        len = MAX_XFER_BUFF_LEN;
1145
+    }
1146
+
1147
+    xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_pa);
1148
+    if (!xfer_buf)
1149
+        return -ENOMEM;
1150
+
1151
+    dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf,
1152
+            xfer_buf_pa, len);
1153
+    va = uaudio_iommu_map(MEM_XFER_BUF, dma_coherent, xfer_buf_pa, len,
1154
+             &xfer_buf_sgt);
1155
+    if (!va) {
1156
+        ret = -ENOMEM;
1157
+        goto unmap_sync;
1158
+    }
1159
+
1160
+    mem_info->pa = xfer_buf_pa;
1161
+    mem_info->size = len;
1162
+    mem_info->va = PREPEND_SID_TO_IOVA(va, uaudio_qdev->data->sid);
1163
+    sg_free_table(&xfer_buf_sgt);
1164
+
1165
+    return 0;
1166
+
1167
+unmap_sync:
1168
+    usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa);
1169
+
1170
+    return ret;
1171
+}
1172
+
1173
+/**
1174
+ * uaudio_endpoint_setup() - fetch and populate endpoint params
1175
+ * @subs: usb substream
1176
+ * @endpoint: usb endpoint to add
1177
+ * @card_num: uadev index
1178
+ * @mem_info: QMI response info
1179
+ * @ep_desc: QMI ep desc response field
1180
+ *
1181
+ * Initialize the USB endpoint being used for a particular USB
1182
+ * stream. Will request XHCI sec intr to reserve the EP for
1183
+ * offloading as well as populating the QMI response with the
1184
+ * transfer ring parameters.
1185
+ *
1186
+ */
1187
+static phys_addr_t
1188
+uaudio_endpoint_setup(struct snd_usb_substream *subs,
1189
+         struct snd_usb_endpoint *endpoint, int card_num,
1190
+         struct mem_info_v01 *mem_info,
1191
+         struct usb_endpoint_descriptor_v01 *ep_desc)
1192
+{
1193
+    struct usb_host_endpoint *ep;
1194
+    phys_addr_t tr_pa = 0;
1195
+    struct sg_table *sgt;
1196
+    bool dma_coherent;
1197
+    unsigned long va;
1198
+    struct page *pg;
1199
+    int ret = -ENODEV;
1200
+
1201
+    dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
1202
+
1203
+    ep = usb_pipe_endpoint(subs->dev, endpoint->pipe);
1204
+    if (!ep) {
1205
+        dev_err(uaudio_qdev->data->dev, "data ep # %d context is null\n",
1206
+            subs->data_endpoint->ep_num);
1207
+        goto exit;
1208
+    }
1209
+
1210
+    memcpy(ep_desc, &ep->desc, sizeof(ep->desc));
1211
+
1212
+    ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep);
1213
+    if (ret < 0) {
1214
+        dev_err(&subs->dev->dev,
1215
+            "failed to add data ep to sec intr\n");
1216
+        ret = -ENODEV;
1217
+        goto exit;
1218
+    }
1219
+
1220
+    sgt = xhci_sideband_get_endpoint_buffer(uadev[card_num].sb, ep);
1221
+    if (!sgt) {
1222
+        dev_err(&subs->dev->dev,
1223
+            "failed to get data ep ring address\n");
1224
+        ret = -ENODEV;
1225
+        goto remove_ep;
1226
+    }
1227
+
1228
+    pg = sg_page(sgt->sgl);
1229
+    tr_pa = page_to_phys(pg);
1230
+    mem_info->pa = sg_dma_address(sgt->sgl);
1231
+    sg_free_table(sgt);
1232
+
1233
+    /* data transfer ring */
1234
+    va = uaudio_iommu_map(MEM_XFER_RING, dma_coherent, tr_pa,
1235
+             PAGE_SIZE, NULL);
1236
+    if (!va) {
1237
+        ret = -ENOMEM;
1238
+        goto clear_pa;
1239
+    }
1240
+
1241
+    mem_info->va = PREPEND_SID_TO_IOVA(va, uaudio_qdev->data->sid);
1242
+    mem_info->size = PAGE_SIZE;
1243
+
1244
+    return 0;
1245
+
1246
+clear_pa:
1247
+    mem_info->pa = 0;
1248
+remove_ep:
1249
+    xhci_sideband_remove_endpoint(uadev[card_num].sb, ep);
1250
+exit:
1251
+    return ret;
1252
+}
1253
+
1254
+/**
1255
+ * uaudio_event_ring_setup() - fetch and populate event ring params
1256
+ * @subs: usb substream
1257
+ * @card_num: uadev index
1258
+ * @mem_info: QMI response info
1259
+ *
1260
+ * Register secondary interrupter to XHCI and fetch the event buffer info
1261
+ * and populate the information into the QMI response.
1262
+ *
1263
+ */
1264
+static int uaudio_event_ring_setup(struct snd_usb_substream *subs,
1265
+                 int card_num, struct mem_info_v01 *mem_info)
1266
+{
1267
+    struct sg_table *sgt;
1268
+    phys_addr_t er_pa;
1269
+    bool dma_coherent;
1270
+    unsigned long va;
1271
+    struct page *pg;
1272
+    int ret;
1273
+
1274
+    dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
1275
+    er_pa = 0;
1276
+
1277
+    /* event ring */
1278
+    ret = xhci_sideband_create_interrupter(uadev[card_num].sb, 1, false,
1279
+                     0, uaudio_qdev->data->intr_num);
1280
+    if (ret < 0) {
1281
+        dev_err(&subs->dev->dev, "failed to fetch interrupter\n");
1282
+        goto exit;
1283
+    }
1284
+
1285
+    sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb);
1286
+    if (!sgt) {
1287
+        dev_err(&subs->dev->dev,
1288
+            "failed to get event ring address\n");
1289
+        ret = -ENODEV;
1290
+        goto remove_interrupter;
1291
+    }
1292
+
1293
+    pg = sg_page(sgt->sgl);
1294
+    er_pa = page_to_phys(pg);
1295
+    mem_info->pa = sg_dma_address(sgt->sgl);
1296
+    sg_free_table(sgt);
1297
+
1298
+    va = uaudio_iommu_map(MEM_EVENT_RING, dma_coherent, er_pa,
1299
+             PAGE_SIZE, NULL);
1300
+    if (!va) {
1301
+        ret = -ENOMEM;
1302
+        goto clear_pa;
1303
+    }
1304
+
1305
+    mem_info->va = PREPEND_SID_TO_IOVA(va, uaudio_qdev->data->sid);
1306
+    mem_info->size = PAGE_SIZE;
1307
+
1308
+    return 0;
1309
+
1310
+clear_pa:
1311
+    mem_info->pa = 0;
1312
+remove_interrupter:
1313
+    xhci_sideband_remove_interrupter(uadev[card_num].sb);
1314
+exit:
1315
+    return ret;
1316
+}
1317
+
1318
+/**
1319
+ * uaudio_populate_uac_desc() - parse UAC parameters and populate QMI resp
1320
+ * @subs: usb substream
1021
+ * @resp: QMI response buffer
1321
+ * @resp: QMI response buffer
1022
+ * @info_idx: usb interface array index
1322
+ *
1023
+ *
1323
+ * Parses information specified within UAC descriptors which explain the
1024
+ * Prepares the QMI response for a USB QMI stream enable request. Will parse
1324
+ * sample parameters that the device expects. This information is populated
1025
+ * out the parameters within the stream enable request, in order to match
1325
+ * to the QMI response sent back to the audio DSP.
1026
+ * requested audio profile to the ones exposed by the USB device connected.
1326
+ *
1027
+ *
1327
+ */
1028
+ * In addition, will fetch the XHCI transfer resources needed for the handoff to
1328
+static int uaudio_populate_uac_desc(struct snd_usb_substream *subs,
1029
+ * happen. This includes, transfer ring and buffer addresses and secondary event
1329
+                 struct qmi_uaudio_stream_resp_msg_v01 *resp)
1030
+ * ring address. These parameters will be communicated as part of the USB QMI
1330
+{
1031
+ * stream enable response.
1331
+    struct usb_interface_descriptor *altsd;
1032
+ *
1332
+    struct usb_host_interface *alts;
1033
+ */
1034
+static int prepare_qmi_response(struct snd_usb_substream *subs,
1035
+        struct qmi_uaudio_stream_req_msg_v01 *req_msg,
1036
+        struct qmi_uaudio_stream_resp_msg_v01 *resp, int info_idx)
1037
+{
1038
+    struct usb_interface *iface;
1333
+    struct usb_interface *iface;
1039
+    struct usb_host_interface *alts;
1334
+    int protocol;
1040
+    struct usb_interface_descriptor *altsd;
1041
+    struct usb_interface_assoc_descriptor *assoc;
1042
+    struct usb_host_endpoint *ep;
1043
+    struct uac_format_type_i_continuous_descriptor *fmt;
1044
+    struct uac_format_type_i_discrete_descriptor *fmt_v1;
1045
+    struct uac_format_type_i_ext_descriptor *fmt_v2;
1046
+    struct uac1_as_header_descriptor *as;
1047
+    struct q6usb_offload *data;
1048
+    int ret;
1049
+    int protocol, card_num, pcm_dev_num;
1050
+    void *hdr_ptr;
1051
+    u8 *xfer_buf;
1052
+    unsigned int data_ep_pipe = 0, sync_ep_pipe = 0;
1053
+    u32 len, mult, remainder, xfer_buf_len;
1054
+    unsigned long va, tr_data_va = 0, tr_sync_va = 0;
1055
+    phys_addr_t xhci_pa, xfer_buf_pa, tr_data_pa = 0, tr_sync_pa = 0;
1056
+    struct sg_table *sgt;
1057
+    struct sg_table xfer_buf_sgt;
1058
+    struct page *pg;
1059
+    bool dma_coherent;
1060
+
1335
+
1061
+    iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
1336
+    iface = usb_ifnum_to_if(subs->dev, subs->cur_audiofmt->iface);
1062
+    if (!iface) {
1337
+    if (!iface) {
1063
+        dev_err(uaudio_qdev->dev, "interface # %d does not exist\n",
1338
+        dev_err(&subs->dev->dev, "interface # %d does not exist\n",
1064
+            subs->cur_audiofmt->iface);
1339
+            subs->cur_audiofmt->iface);
1065
+        ret = -ENODEV;
1340
+        return -ENODEV;
1066
+        goto err;
1341
+    }
1067
+    }
1068
+
1069
+    assoc = iface->intf_assoc;
1070
+    pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
1071
+    xfer_buf_len = req_msg->xfer_buff_size;
1072
+    card_num = uaudio_qdev->last_card_num;
1073
+
1342
+
1074
+    alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
1343
+    alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
1075
+    altsd = get_iface_desc(alts);
1344
+    altsd = get_iface_desc(alts);
1076
+    protocol = altsd->bInterfaceProtocol;
1345
+    protocol = altsd->bInterfaceProtocol;
1077
+
1346
+
1078
+    /* get format type */
1079
+    if (protocol != UAC_VERSION_3) {
1080
+        fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
1081
+                UAC_FORMAT_TYPE);
1082
+        if (!fmt) {
1083
+            dev_err(uaudio_qdev->dev,
1084
+                "%u:%d : no UAC_FORMAT_TYPE desc\n",
1085
+                subs->cur_audiofmt->iface,
1086
+                subs->cur_audiofmt->altset_idx);
1087
+            ret = -ENODEV;
1088
+            goto err;
1089
+        }
1090
+    }
1091
+
1092
+    if (!uadev[card_num].ctrl_intf) {
1093
+        dev_err(uaudio_qdev->dev, "audio ctrl intf info not cached\n");
1094
+        ret = -ENODEV;
1095
+        goto err;
1096
+    }
1097
+
1098
+    if (protocol != UAC_VERSION_3) {
1099
+        hdr_ptr = snd_usb_find_csint_desc(uadev[card_num].ctrl_intf->extra,
1100
+                uadev[card_num].ctrl_intf->extralen, NULL,
1101
+                UAC_HEADER);
1102
+        if (!hdr_ptr) {
1103
+            dev_err(uaudio_qdev->dev, "no UAC_HEADER desc\n");
1104
+            ret = -ENODEV;
1105
+            goto err;
1106
+        }
1107
+    }
1108
+
1109
+    if (protocol == UAC_VERSION_1) {
1347
+    if (protocol == UAC_VERSION_1) {
1110
+        struct uac1_ac_header_descriptor *uac1_hdr = hdr_ptr;
1348
+        struct uac1_as_header_descriptor *as;
1111
+
1349
+
1112
+        as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
1350
+        as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL,
1113
+            UAC_AS_GENERAL);
1351
+                     UAC_AS_GENERAL);
1114
+        if (!as) {
1352
+        if (!as) {
1115
+            dev_err(uaudio_qdev->dev,
1353
+            dev_err(&subs->dev->dev,
1116
+                "%u:%d : no UAC_AS_GENERAL desc\n",
1354
+                "%u:%d : no UAC_AS_GENERAL desc\n",
1117
+                subs->cur_audiofmt->iface,
1355
+                subs->cur_audiofmt->iface,
1118
+                subs->cur_audiofmt->altset_idx);
1356
+                subs->cur_audiofmt->altset_idx);
1119
+            ret = -ENODEV;
1357
+            return -ENODEV;
1120
+            goto err;
1121
+        }
1358
+        }
1359
+
1122
+        resp->data_path_delay = as->bDelay;
1360
+        resp->data_path_delay = as->bDelay;
1123
+        resp->data_path_delay_valid = 1;
1361
+        resp->data_path_delay_valid = 1;
1124
+        fmt_v1 = (struct uac_format_type_i_discrete_descriptor *)fmt;
1362
+
1125
+        resp->usb_audio_subslot_size = fmt_v1->bSubframeSize;
1363
+        resp->usb_audio_subslot_size = subs->cur_audiofmt->fmt_sz;
1126
+        resp->usb_audio_subslot_size_valid = 1;
1364
+        resp->usb_audio_subslot_size_valid = 1;
1127
+
1365
+
1128
+        resp->usb_audio_spec_revision = le16_to_cpu(uac1_hdr->bcdADC);
1366
+        resp->usb_audio_spec_revision = le16_to_cpu((__force __le16)0x0100);
1129
+        resp->usb_audio_spec_revision_valid = 1;
1367
+        resp->usb_audio_spec_revision_valid = 1;
1130
+    } else if (protocol == UAC_VERSION_2) {
1368
+    } else if (protocol == UAC_VERSION_2) {
1131
+        struct uac2_ac_header_descriptor *uac2_hdr = hdr_ptr;
1369
+        resp->usb_audio_subslot_size = subs->cur_audiofmt->fmt_sz;
1132
+
1133
+        fmt_v2 = (struct uac_format_type_i_ext_descriptor *)fmt;
1134
+        resp->usb_audio_subslot_size = fmt_v2->bSubslotSize;
1135
+        resp->usb_audio_subslot_size_valid = 1;
1370
+        resp->usb_audio_subslot_size_valid = 1;
1136
+
1371
+
1137
+        resp->usb_audio_spec_revision = le16_to_cpu(uac2_hdr->bcdADC);
1372
+        resp->usb_audio_spec_revision = le16_to_cpu((__force __le16)0x0200);
1138
+        resp->usb_audio_spec_revision_valid = 1;
1373
+        resp->usb_audio_spec_revision_valid = 1;
1139
+    } else if (protocol == UAC_VERSION_3) {
1374
+    } else if (protocol == UAC_VERSION_3) {
1140
+        if (assoc->bFunctionSubClass ==
1375
+        if (iface->intf_assoc->bFunctionSubClass ==
1141
+                    UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0) {
1376
+                    UAC3_FUNCTION_SUBCLASS_FULL_ADC_3_0) {
1142
+            dev_err(uaudio_qdev->dev, "full adc is not supported\n");
1377
+            dev_err(&subs->dev->dev,
1143
+            ret = -EINVAL;
1378
+                "full adc is not supported\n");
1379
+            return -EINVAL;
1144
+        }
1380
+        }
1145
+
1381
+
1146
+        switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
1382
+        switch (le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize)) {
1147
+        case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
1383
+        case UAC3_BADD_EP_MAXPSIZE_SYNC_MONO_16:
1148
+        case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
1384
+        case UAC3_BADD_EP_MAXPSIZE_SYNC_STEREO_16:
...
...
1159
+            resp->usb_audio_subslot_size = 0x3;
1395
+            resp->usb_audio_subslot_size = 0x3;
1160
+            break;
1396
+            break;
1161
+        }
1397
+        }
1162
+
1398
+
1163
+        default:
1399
+        default:
1164
+            dev_err(uaudio_qdev->dev,
1400
+            dev_err(&subs->dev->dev,
1165
+                "%d: %u: Invalid wMaxPacketSize\n",
1401
+                "%d: %u: Invalid wMaxPacketSize\n",
1166
+                subs->cur_audiofmt->iface,
1402
+                subs->cur_audiofmt->iface,
1167
+                subs->cur_audiofmt->altset_idx);
1403
+                subs->cur_audiofmt->altset_idx);
1168
+            ret = -EINVAL;
1404
+            return -EINVAL;
1169
+            goto err;
1170
+        }
1405
+        }
1171
+        resp->usb_audio_subslot_size_valid = 1;
1406
+        resp->usb_audio_subslot_size_valid = 1;
1172
+    } else {
1407
+    } else {
1173
+        dev_err(uaudio_qdev->dev, "unknown protocol version %x\n",
1408
+        dev_err(&subs->dev->dev, "unknown protocol version %x\n",
1174
+            protocol);
1409
+            protocol);
1410
+        return -ENODEV;
1411
+    }
1412
+
1413
+    memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
1414
+
1415
+    return 0;
1416
+}
1417
+
1418
+/**
1419
+ * prepare_qmi_response() - prepare stream enable response
1420
+ * @subs: usb substream
1421
+ * @req_msg: QMI request message
1422
+ * @resp: QMI response buffer
1423
+ * @info_idx: usb interface array index
1424
+ *
1425
+ * Prepares the QMI response for a USB QMI stream enable request. Will parse
1426
+ * out the parameters within the stream enable request, in order to match
1427
+ * requested audio profile to the ones exposed by the USB device connected.
1428
+ *
1429
+ * In addition, will fetch the XHCI transfer resources needed for the handoff to
1430
+ * happen. This includes, transfer ring and buffer addresses and secondary event
1431
+ * ring address. These parameters will be communicated as part of the USB QMI
1432
+ * stream enable response.
1433
+ *
1434
+ */
1435
+static int prepare_qmi_response(struct snd_usb_substream *subs,
1436
+                struct qmi_uaudio_stream_req_msg_v01 *req_msg,
1437
+                struct qmi_uaudio_stream_resp_msg_v01 *resp,
1438
+                int info_idx)
1439
+{
1440
+    struct q6usb_offload *data;
1441
+    int pcm_dev_num;
1442
+    int card_num;
1443
+    u8 *xfer_buf = NULL;
1444
+    int ret;
1445
+
1446
+    pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
1447
+    card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
1448
+
1449
+    if (!uadev[card_num].ctrl_intf) {
1450
+        dev_err(&subs->dev->dev, "audio ctrl intf info not cached\n");
1175
+        ret = -ENODEV;
1451
+        ret = -ENODEV;
1176
+        goto err;
1452
+        goto err;
1177
+    }
1453
+    }
1178
+
1454
+
1455
+    ret = uaudio_populate_uac_desc(subs, resp);
1456
+    if (ret < 0)
1457
+        goto err;
1458
+
1179
+    resp->slot_id = subs->dev->slot_id;
1459
+    resp->slot_id = subs->dev->slot_id;
1180
+    resp->slot_id_valid = 1;
1460
+    resp->slot_id_valid = 1;
1181
+
1461
+
1182
+    memcpy(&resp->std_as_opr_intf_desc, &alts->desc, sizeof(alts->desc));
1183
+    resp->std_as_opr_intf_desc_valid = 1;
1184
+
1185
+    ep = usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe);
1186
+    if (!ep) {
1187
+        dev_err(uaudio_qdev->dev, "data ep # %d context is null\n",
1188
+            subs->data_endpoint->ep_num);
1189
+        ret = -ENODEV;
1190
+        goto err;
1191
+    }
1192
+    data_ep_pipe = subs->data_endpoint->pipe;
1193
+    memcpy(&resp->std_as_data_ep_desc, &ep->desc, sizeof(ep->desc));
1194
+    resp->std_as_data_ep_desc_valid = 1;
1195
+
1196
+    ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep);
1197
+    if (ret < 0) {
1198
+        dev_err(uaudio_qdev->dev, "failed to add data ep to sideband\n");
1199
+        ret = -ENODEV;
1200
+        goto err;
1201
+    }
1202
+
1203
+    sgt = xhci_sideband_get_endpoint_buffer(uadev[card_num].sb, ep);
1204
+    if (!sgt) {
1205
+        dev_err(uaudio_qdev->dev, "failed to get data ep ring address\n");
1206
+        ret = -ENODEV;
1207
+        goto drop_data_ep;
1208
+    }
1209
+
1210
+    pg = sg_page(sgt->sgl);
1211
+    tr_data_pa = page_to_phys(pg);
1212
+    resp->xhci_mem_info.tr_data.pa = sg_dma_address(sgt->sgl);
1213
+    sg_free_table(sgt);
1214
+
1215
+    if (subs->sync_endpoint) {
1216
+        ep = usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe);
1217
+        if (!ep) {
1218
+            dev_err(uaudio_qdev->dev, "implicit fb on data ep\n");
1219
+            goto skip_sync_ep;
1220
+        }
1221
+        sync_ep_pipe = subs->sync_endpoint->pipe;
1222
+        memcpy(&resp->std_as_sync_ep_desc, &ep->desc, sizeof(ep->desc));
1223
+        resp->std_as_sync_ep_desc_valid = 1;
1224
+
1225
+        ret = xhci_sideband_add_endpoint(uadev[card_num].sb, ep);
1226
+        if (ret < 0) {
1227
+            dev_err(uaudio_qdev->dev,
1228
+                "failed to add sync ep to sideband\n");
1229
+            ret = -ENODEV;
1230
+            goto drop_data_ep;
1231
+        }
1232
+
1233
+        sgt = xhci_sideband_get_endpoint_buffer(uadev[card_num].sb, ep);
1234
+        if (!sgt) {
1235
+            dev_err(uaudio_qdev->dev, "failed to get sync ep ring address\n");
1236
+            ret = -ENODEV;
1237
+            goto drop_sync_ep;
1238
+        }
1239
+
1240
+        pg = sg_page(sgt->sgl);
1241
+        tr_sync_pa = page_to_phys(pg);
1242
+        resp->xhci_mem_info.tr_sync.pa = sg_dma_address(sgt->sgl);
1243
+        sg_free_table(sgt);
1244
+    }
1245
+
1246
+skip_sync_ep:
1247
+    data = snd_soc_usb_find_priv_data(usb_get_usb_backend(subs->dev));
1462
+    data = snd_soc_usb_find_priv_data(usb_get_usb_backend(subs->dev));
1248
+    if (!data)
1463
+    if (!data)
1249
+        goto drop_sync_ep;
1464
+        goto err;
1250
+
1465
+
1251
+    uaudio_qdev->domain = data->domain;
1466
+    uaudio_qdev->data = data;
1252
+    uaudio_qdev->sid = data->sid;
1467
+
1253
+    uaudio_qdev->intr_num = data->intr_num;
1468
+    resp->std_as_opr_intf_desc_valid = 1;
1254
+    uaudio_qdev->dev = data->dev;
1469
+    ret = uaudio_endpoint_setup(subs, subs->data_endpoint, card_num,
1470
+                 &resp->xhci_mem_info.tr_data,
1471
+                 &resp->std_as_data_ep_desc);
1472
+    if (ret < 0)
1473
+        goto err;
1474
+
1475
+    resp->std_as_data_ep_desc_valid = 1;
1476
+
1477
+    if (subs->sync_endpoint) {
1478
+        ret = uaudio_endpoint_setup(subs, subs->sync_endpoint, card_num,
1479
+                     &resp->xhci_mem_info.tr_sync,
1480
+                     &resp->std_as_sync_ep_desc);
1481
+        if (ret < 0)
1482
+            goto drop_data_ep;
1483
+
1484
+        resp->std_as_sync_ep_desc_valid = 1;
1485
+    }
1255
+
1486
+
1256
+    resp->interrupter_num_valid = 1;
1487
+    resp->interrupter_num_valid = 1;
1257
+    resp->controller_num_valid = 0;
1488
+    resp->controller_num_valid = 0;
1258
+    ret = usb_get_controller_id(subs->dev);
1489
+    ret = usb_get_controller_id(subs->dev);
1259
+    if (ret >= 0) {
1490
+    if (ret >= 0) {
1260
+        resp->controller_num = ret;
1491
+        resp->controller_num = ret;
1261
+        resp->controller_num_valid = 1;
1492
+        resp->controller_num_valid = 1;
1262
+    }
1493
+    }
1263
+    /* map xhci data structures PA memory to iova */
1264
+    dma_coherent = dev_is_dma_coherent(subs->dev->bus->sysdev);
1265
+
1494
+
1266
+    /* event ring */
1495
+    /* event ring */
1267
+    ret = xhci_sideband_create_interrupter(uadev[card_num].sb, uaudio_qdev->intr_num);
1496
+    ret = uaudio_event_ring_setup(subs, card_num,
1268
+    if (ret < 0) {
1497
+                 &resp->xhci_mem_info.evt_ring);
1269
+        dev_err(uaudio_qdev->dev, "failed to fetch interrupter\n");
1498
+    if (ret < 0)
1270
+        ret = -ENODEV;
1271
+        goto drop_sync_ep;
1499
+        goto drop_sync_ep;
1272
+    }
1500
+
1273
+
1501
+    uaudio_qdev->er_mapped = true;
1274
+    sgt = xhci_sideband_get_event_buffer(uadev[card_num].sb);
1275
+    if (!sgt) {
1276
+        dev_err(uaudio_qdev->dev, "failed to get event ring address\n");
1277
+        ret = -ENODEV;
1278
+        goto free_sec_ring;
1279
+    }
1280
+
1281
+    xhci_pa = page_to_phys(sg_page(sgt->sgl));
1282
+    resp->xhci_mem_info.evt_ring.pa = sg_dma_address(sgt->sgl);
1283
+    sg_free_table(sgt);
1284
+    if (!xhci_pa) {
1285
+        dev_err(uaudio_qdev->dev,
1286
+            "failed to get sec event ring address\n");
1287
+        ret = -ENODEV;
1288
+        goto free_sec_ring;
1289
+    }
1290
+
1291
+    resp->interrupter_num = xhci_sideband_interrupter_id(uadev[card_num].sb);
1502
+    resp->interrupter_num = xhci_sideband_interrupter_id(uadev[card_num].sb);
1292
+
1293
+    va = uaudio_iommu_map(MEM_EVENT_RING, dma_coherent, xhci_pa, PAGE_SIZE,
1294
+            NULL);
1295
+    if (!va) {
1296
+        ret = -ENOMEM;
1297
+        goto free_sec_ring;
1298
+    }
1299
+
1300
+    resp->xhci_mem_info.evt_ring.va = PREPEND_SID_TO_IOVA(va,
1301
+                        uaudio_qdev->sid);
1302
+    resp->xhci_mem_info.evt_ring.size = PAGE_SIZE;
1303
+    uaudio_qdev->er_mapped = true;
1304
+
1503
+
1305
+    resp->speed_info = get_speed_info(subs->dev->speed);
1504
+    resp->speed_info = get_speed_info(subs->dev->speed);
1306
+    if (resp->speed_info == USB_QMI_DEVICE_SPEED_INVALID_V01) {
1505
+    if (resp->speed_info == USB_QMI_DEVICE_SPEED_INVALID_V01) {
1307
+        ret = -ENODEV;
1506
+        ret = -ENODEV;
1308
+        goto unmap_er;
1507
+        goto free_sec_ring;
1309
+    }
1508
+    }
1310
+
1509
+
1311
+    resp->speed_info_valid = 1;
1510
+    resp->speed_info_valid = 1;
1312
+
1511
+
1313
+    /* data transfer ring */
1512
+    ret = uaudio_transfer_buffer_setup(subs, xfer_buf, req_msg->xfer_buff_size,
1314
+    va = uaudio_iommu_map(MEM_XFER_RING, dma_coherent, tr_data_pa,
1513
+                     &resp->xhci_mem_info.xfer_buff);
1315
+            PAGE_SIZE, NULL);
1514
+    if (ret < 0) {
1316
+    if (!va) {
1317
+        ret = -ENOMEM;
1515
+        ret = -ENOMEM;
1318
+        goto unmap_er;
1516
+        goto free_sec_ring;
1319
+    }
1517
+    }
1320
+
1321
+    tr_data_va = va;
1322
+    resp->xhci_mem_info.tr_data.va = PREPEND_SID_TO_IOVA(va,
1323
+                        uaudio_qdev->sid);
1324
+    resp->xhci_mem_info.tr_data.size = PAGE_SIZE;
1325
+
1326
+    /* sync transfer ring */
1327
+    if (!resp->xhci_mem_info.tr_sync.pa)
1328
+        goto skip_sync;
1329
+
1330
+    xhci_pa = resp->xhci_mem_info.tr_sync.pa;
1331
+    va = uaudio_iommu_map(MEM_XFER_RING, dma_coherent, tr_sync_pa,
1332
+            PAGE_SIZE, NULL);
1333
+    if (!va) {
1334
+        ret = -ENOMEM;
1335
+        goto unmap_data;
1336
+    }
1337
+
1338
+    tr_sync_va = va;
1339
+    resp->xhci_mem_info.tr_sync.va = PREPEND_SID_TO_IOVA(va,
1340
+                        uaudio_qdev->sid);
1341
+    resp->xhci_mem_info.tr_sync.size = PAGE_SIZE;
1342
+
1343
+skip_sync:
1344
+    /* xfer buffer, multiple of 4K only */
1345
+    if (!xfer_buf_len)
1346
+        xfer_buf_len = PAGE_SIZE;
1347
+
1348
+    mult = xfer_buf_len / PAGE_SIZE;
1349
+    remainder = xfer_buf_len % PAGE_SIZE;
1350
+    len = mult * PAGE_SIZE;
1351
+    len += remainder ? PAGE_SIZE : 0;
1352
+
1353
+    if (len > MAX_XFER_BUFF_LEN) {
1354
+        dev_err(uaudio_qdev->dev,
1355
+            "req buf len %d > max buf len %lu, setting %lu\n",
1356
+            len, MAX_XFER_BUFF_LEN, MAX_XFER_BUFF_LEN);
1357
+        len = MAX_XFER_BUFF_LEN;
1358
+    }
1359
+
1360
+    xfer_buf = usb_alloc_coherent(subs->dev, len, GFP_KERNEL, &xfer_buf_pa);
1361
+    if (!xfer_buf) {
1362
+        ret = -ENOMEM;
1363
+        goto unmap_sync;
1364
+    }
1365
+
1366
+    dma_get_sgtable(subs->dev->bus->sysdev, &xfer_buf_sgt, xfer_buf, xfer_buf_pa,
1367
+            len);
1368
+    va = uaudio_iommu_map(MEM_XFER_BUF, dma_coherent, xfer_buf_pa, len,
1369
+            &xfer_buf_sgt);
1370
+    if (!va) {
1371
+        ret = -ENOMEM;
1372
+        goto unmap_sync;
1373
+    }
1374
+
1375
+    resp->xhci_mem_info.xfer_buff.pa = xfer_buf_pa;
1376
+    resp->xhci_mem_info.xfer_buff.size = len;
1377
+
1378
+    resp->xhci_mem_info.xfer_buff.va = PREPEND_SID_TO_IOVA(va,
1379
+                        uaudio_qdev->sid);
1380
+
1518
+
1381
+    resp->xhci_mem_info_valid = 1;
1519
+    resp->xhci_mem_info_valid = 1;
1382
+
1383
+    sg_free_table(&xfer_buf_sgt);
1384
+
1520
+
1385
+    if (!atomic_read(&uadev[card_num].in_use)) {
1521
+    if (!atomic_read(&uadev[card_num].in_use)) {
1386
+        kref_init(&uadev[card_num].kref);
1522
+        kref_init(&uadev[card_num].kref);
1387
+        init_waitqueue_head(&uadev[card_num].disconnect_wq);
1523
+        init_waitqueue_head(&uadev[card_num].disconnect_wq);
1388
+        uadev[card_num].num_intf =
1524
+        uadev[card_num].num_intf =
1389
+            subs->dev->config->desc.bNumInterfaces;
1525
+            subs->dev->config->desc.bNumInterfaces;
1390
+        uadev[card_num].info = kcalloc(uadev[card_num].num_intf,
1526
+        uadev[card_num].info = kcalloc(uadev[card_num].num_intf,
1391
+            sizeof(struct intf_info), GFP_KERNEL);
1527
+                     sizeof(struct intf_info),
1528
+                     GFP_KERNEL);
1392
+        if (!uadev[card_num].info) {
1529
+        if (!uadev[card_num].info) {
1393
+            ret = -ENOMEM;
1530
+            ret = -ENOMEM;
1394
+            goto unmap_sync;
1531
+            goto unmap_er;
1395
+        }
1532
+        }
1396
+        uadev[card_num].udev = subs->dev;
1533
+        uadev[card_num].udev = subs->dev;
1397
+        atomic_set(&uadev[card_num].in_use, 1);
1534
+        atomic_set(&uadev[card_num].in_use, 1);
1398
+    } else {
1535
+    } else {
1399
+        kref_get(&uadev[card_num].kref);
1536
+        kref_get(&uadev[card_num].kref);
1400
+    }
1537
+    }
1401
+
1538
+
1402
+    uadev[card_num].usb_core_id = resp->controller_num;
1539
+    uadev[card_num].usb_core_id = resp->controller_num;
1403
+
1540
+
1404
+    /* cache intf specific info to use it for unmap and free xfer buf */
1541
+    /* cache intf specific info to use it for unmap and free xfer buf */
1405
+    uadev[card_num].info[info_idx].data_xfer_ring_va = tr_data_va;
1542
+    uadev[card_num].info[info_idx].data_xfer_ring_va =
1543
+                    IOVA_MASK(resp->xhci_mem_info.tr_data.va);
1406
+    uadev[card_num].info[info_idx].data_xfer_ring_size = PAGE_SIZE;
1544
+    uadev[card_num].info[info_idx].data_xfer_ring_size = PAGE_SIZE;
1407
+    uadev[card_num].info[info_idx].sync_xfer_ring_va = tr_sync_va;
1545
+    uadev[card_num].info[info_idx].sync_xfer_ring_va =
1546
+                    IOVA_MASK(resp->xhci_mem_info.tr_sync.va);
1408
+    uadev[card_num].info[info_idx].sync_xfer_ring_size = PAGE_SIZE;
1547
+    uadev[card_num].info[info_idx].sync_xfer_ring_size = PAGE_SIZE;
1409
+    uadev[card_num].info[info_idx].xfer_buf_va = va;
1548
+    uadev[card_num].info[info_idx].xfer_buf_va =
1410
+    uadev[card_num].info[info_idx].xfer_buf_pa = xfer_buf_pa;
1549
+                    IOVA_MASK(resp->xhci_mem_info.xfer_buff.va);
1411
+    uadev[card_num].info[info_idx].xfer_buf_size = len;
1550
+    uadev[card_num].info[info_idx].xfer_buf_pa =
1412
+    uadev[card_num].info[info_idx].data_ep_pipe = data_ep_pipe;
1551
+                    resp->xhci_mem_info.xfer_buff.pa;
1413
+    uadev[card_num].info[info_idx].sync_ep_pipe = sync_ep_pipe;
1552
+    uadev[card_num].info[info_idx].xfer_buf_size =
1553
+                    resp->xhci_mem_info.xfer_buff.size;
1554
+    uadev[card_num].info[info_idx].data_ep_pipe = subs->data_endpoint ?
1555
+                        subs->data_endpoint->pipe : 0;
1556
+    uadev[card_num].info[info_idx].sync_ep_pipe = subs->sync_endpoint ?
1557
+                        subs->sync_endpoint->pipe : 0;
1558
+    uadev[card_num].info[info_idx].data_ep_idx = subs->data_endpoint ?
1559
+                        subs->data_endpoint->ep_num : 0;
1560
+    uadev[card_num].info[info_idx].sync_ep_idx = subs->sync_endpoint ?
1561
+                        subs->sync_endpoint->ep_num : 0;
1414
+    uadev[card_num].info[info_idx].xfer_buf = xfer_buf;
1562
+    uadev[card_num].info[info_idx].xfer_buf = xfer_buf;
1415
+    uadev[card_num].info[info_idx].pcm_card_num = card_num;
1563
+    uadev[card_num].info[info_idx].pcm_card_num = card_num;
1416
+    uadev[card_num].info[info_idx].pcm_dev_num = pcm_dev_num;
1564
+    uadev[card_num].info[info_idx].pcm_dev_num = pcm_dev_num;
1417
+    uadev[card_num].info[info_idx].direction = subs->direction;
1565
+    uadev[card_num].info[info_idx].direction = subs->direction;
1418
+    uadev[card_num].info[info_idx].intf_num = subs->cur_audiofmt->iface;
1566
+    uadev[card_num].info[info_idx].intf_num = subs->cur_audiofmt->iface;
1419
+    uadev[card_num].info[info_idx].in_use = true;
1567
+    uadev[card_num].info[info_idx].in_use = true;
1420
+
1568
+
1421
+    set_bit(card_num, &uaudio_qdev->card_slot);
1569
+    set_bit(card_num, &uaudio_qdev->card_slot);
1422
+
1570
+
1423
+    return 0;
1571
+    return 0;
1424
+
1572
+
1425
+unmap_sync:
1426
+    usb_free_coherent(subs->dev, len, xfer_buf, xfer_buf_pa);
1427
+    uaudio_iommu_unmap(MEM_XFER_RING, tr_sync_va, PAGE_SIZE, PAGE_SIZE);
1428
+unmap_data:
1429
+    uaudio_iommu_unmap(MEM_XFER_RING, tr_data_va, PAGE_SIZE, PAGE_SIZE);
1430
+unmap_er:
1573
+unmap_er:
1431
+    uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE);
1574
+    uaudio_iommu_unmap(MEM_EVENT_RING, IOVA_BASE, PAGE_SIZE, PAGE_SIZE);
1432
+free_sec_ring:
1575
+free_sec_ring:
1433
+    xhci_sideband_remove_interrupter(uadev[card_num].sb);
1576
+    xhci_sideband_remove_interrupter(uadev[card_num].sb);
1434
+drop_sync_ep:
1577
+drop_sync_ep:
1435
+    if (subs->sync_endpoint)
1578
+    if (subs->sync_endpoint) {
1579
+        uaudio_iommu_unmap(MEM_XFER_RING,
1580
+                 IOVA_MASK(resp->xhci_mem_info.tr_sync.va),
1581
+                 PAGE_SIZE, PAGE_SIZE);
1436
+        xhci_sideband_remove_endpoint(uadev[card_num].sb,
1582
+        xhci_sideband_remove_endpoint(uadev[card_num].sb,
1437
+            usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe));
1583
+            usb_pipe_endpoint(subs->dev, subs->sync_endpoint->pipe));
1584
+    }
1438
+drop_data_ep:
1585
+drop_data_ep:
1586
+    uaudio_iommu_unmap(MEM_XFER_RING, IOVA_MASK(resp->xhci_mem_info.tr_data.va),
1587
+             PAGE_SIZE, PAGE_SIZE);
1439
+    xhci_sideband_remove_endpoint(uadev[card_num].sb,
1588
+    xhci_sideband_remove_endpoint(uadev[card_num].sb,
1440
+            usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe));
1589
+            usb_pipe_endpoint(subs->dev, subs->data_endpoint->pipe));
1441
+
1590
+
1442
+err:
1591
+err:
1443
+    return ret;
1592
+    return ret;
...
...
1453
+ * Main handler for the QMI stream enable/disable requests. This executes the
1602
+ * Main handler for the QMI stream enable/disable requests. This executes the
1454
+ * corresponding enable/disable stream apis, respectively.
1603
+ * corresponding enable/disable stream apis, respectively.
1455
+ *
1604
+ *
1456
+ */
1605
+ */
1457
+static void handle_uaudio_stream_req(struct qmi_handle *handle,
1606
+static void handle_uaudio_stream_req(struct qmi_handle *handle,
1458
+            struct sockaddr_qrtr *sq,
1607
+                 struct sockaddr_qrtr *sq,
1459
+            struct qmi_txn *txn,
1608
+                 struct qmi_txn *txn,
1460
+            const void *decoded_msg)
1609
+                 const void *decoded_msg)
1461
+{
1610
+{
1462
+    struct qmi_uaudio_stream_req_msg_v01 *req_msg;
1611
+    struct qmi_uaudio_stream_req_msg_v01 *req_msg;
1463
+    struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0};
1612
+    struct qmi_uaudio_stream_resp_msg_v01 resp = {{0}, 0};
1613
+    struct uaudio_qmi_svc *svc = uaudio_svc;
1614
+    struct snd_usb_audio *chip = NULL;
1464
+    struct snd_usb_substream *subs;
1615
+    struct snd_usb_substream *subs;
1465
+    struct snd_usb_audio *chip = NULL;
1616
+    struct usb_host_endpoint *ep;
1466
+    struct uaudio_qmi_svc *svc = uaudio_svc;
1617
+    int datainterval = -EINVAL;
1618
+    int info_idx = -EINVAL;
1467
+    struct intf_info *info;
1619
+    struct intf_info *info;
1468
+    struct usb_host_endpoint *ep;
1620
+    u8 pcm_card_num;
1469
+    u8 pcm_card_num, pcm_dev_num, direction;
1621
+    u8 pcm_dev_num;
1470
+    int info_idx = -EINVAL, datainterval = -EINVAL, ret = 0;
1622
+    u8 direction;
1623
+    int ret = 0;
1471
+
1624
+
1472
+    if (!svc->client_connected) {
1625
+    if (!svc->client_connected) {
1473
+        svc->client_sq = *sq;
1626
+        svc->client_sq = *sq;
1474
+        svc->client_connected = true;
1627
+        svc->client_connected = true;
1475
+    }
1628
+    }
...
...
1487
+        goto response;
1640
+        goto response;
1488
+    }
1641
+    }
1489
+
1642
+
1490
+    direction = (req_msg->usb_token & QMI_STREAM_REQ_DIRECTION);
1643
+    direction = (req_msg->usb_token & QMI_STREAM_REQ_DIRECTION);
1491
+    pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
1644
+    pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
1492
+    pcm_card_num = req_msg->enable ? uaudio_qdev->last_card_num :
1645
+    pcm_card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
1493
+                ffs(uaudio_qdev->card_slot) - 1;
1494
+    if (pcm_card_num >= SNDRV_CARDS) {
1646
+    if (pcm_card_num >= SNDRV_CARDS) {
1495
+        ret = -EINVAL;
1647
+        ret = -EINVAL;
1496
+        goto response;
1648
+        goto response;
1497
+    }
1649
+    }
1498
+
1650
+
...
...
1508
+        goto response;
1660
+        goto response;
1509
+    }
1661
+    }
1510
+
1662
+
1511
+    info_idx = info_idx_from_ifnum(pcm_card_num, subs->cur_audiofmt ?
1663
+    info_idx = info_idx_from_ifnum(pcm_card_num, subs->cur_audiofmt ?
1512
+            subs->cur_audiofmt->iface : -1, req_msg->enable);
1664
+            subs->cur_audiofmt->iface : -1, req_msg->enable);
1513
+    if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm
1665
+    if (atomic_read(&chip->shutdown) || !subs->stream || !subs->stream->pcm ||
1514
+            || !subs->stream->chip) {
1666
+     !subs->stream->chip) {
1515
+        ret = -ENODEV;
1667
+        ret = -ENODEV;
1516
+        goto response;
1668
+        goto response;
1517
+    }
1669
+    }
1518
+
1670
+
1519
+    if (req_msg->enable) {
1671
+    if (req_msg->enable) {
...
...
1534
+
1686
+
1535
+    uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
1687
+    uadev[pcm_card_num].ctrl_intf = chip->ctrl_intf;
1536
+
1688
+
1537
+    if (req_msg->enable) {
1689
+    if (req_msg->enable) {
1538
+        ret = enable_audio_stream(subs,
1690
+        ret = enable_audio_stream(subs,
1539
+                map_pcm_format(req_msg->audio_format),
1691
+                     map_pcm_format(req_msg->audio_format),
1540
+                req_msg->number_of_ch, req_msg->bit_rate,
1692
+                     req_msg->number_of_ch, req_msg->bit_rate,
1541
+                datainterval);
1693
+                     datainterval);
1542
+
1694
+
1543
+        if (!ret)
1695
+        if (!ret)
1544
+            ret = prepare_qmi_response(subs, req_msg, &resp,
1696
+            ret = prepare_qmi_response(subs, req_msg, &resp,
1545
+                    info_idx);
1697
+                         info_idx);
1546
+    } else {
1698
+    } else {
1547
+        info = &uadev[pcm_card_num].info[info_idx];
1699
+        info = &uadev[pcm_card_num].info[info_idx];
1548
+        if (info->data_ep_pipe) {
1700
+        if (info->data_ep_pipe) {
1549
+            ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
1701
+            ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
1550
+                        info->data_ep_pipe);
1702
+                     info->data_ep_pipe);
1551
+            if (ep)
1703
+            if (ep) {
1552
+                xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
1704
+                xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
1553
+                        ep);
1705
+                             ep);
1554
+            xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
1706
+                xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
1707
+                             ep);
1708
+            }
1709
+
1555
+            info->data_ep_pipe = 0;
1710
+            info->data_ep_pipe = 0;
1556
+        }
1711
+        }
1557
+
1712
+
1558
+        if (info->sync_ep_pipe) {
1713
+        if (info->sync_ep_pipe) {
1559
+            ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
1714
+            ep = usb_pipe_endpoint(uadev[pcm_card_num].udev,
1560
+                        info->sync_ep_pipe);
1715
+                     info->sync_ep_pipe);
1561
+            if (ep)
1716
+            if (ep) {
1562
+                xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
1717
+                xhci_sideband_stop_endpoint(uadev[pcm_card_num].sb,
1563
+                        ep);
1718
+                             ep);
1564
+            xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb, ep);
1719
+                xhci_sideband_remove_endpoint(uadev[pcm_card_num].sb,
1720
+                             ep);
1721
+            }
1722
+
1565
+            info->sync_ep_pipe = 0;
1723
+            info->sync_ep_pipe = 0;
1566
+        }
1724
+        }
1567
+
1725
+
1568
+        disable_audio_stream(subs);
1726
+        disable_audio_stream(subs);
1569
+    }
1727
+    }
1570
+
1728
+
1571
+response:
1729
+response:
1572
+    if (!req_msg->enable && ret != -EINVAL && ret != -ENODEV) {
1730
+    if (!req_msg->enable && ret != -EINVAL && ret != -ENODEV) {
1573
+        mutex_lock(&chip->mutex);
1731
+        mutex_lock(&chip->mutex);
1574
+        if (info_idx >= 0) {
1732
+        if (info_idx >= 0) {
1575
+            info = &uadev[pcm_card_num].info[info_idx];
1733
+            info = &uadev[pcm_card_num].info[info_idx];
1576
+            uaudio_dev_intf_cleanup(
1734
+            uaudio_dev_intf_cleanup(uadev[pcm_card_num].udev,
1577
+                    uadev[pcm_card_num].udev,
1735
+                        info);
1578
+                    info);
1579
+        }
1736
+        }
1580
+        if (atomic_read(&uadev[pcm_card_num].in_use))
1737
+        if (atomic_read(&uadev[pcm_card_num].in_use))
1581
+            kref_put(&uadev[pcm_card_num].kref,
1738
+            kref_put(&uadev[pcm_card_num].kref,
1582
+                    uaudio_dev_release);
1739
+                 uaudio_dev_release);
1583
+        mutex_unlock(&chip->mutex);
1740
+        mutex_unlock(&chip->mutex);
1584
+    }
1741
+    }
1585
+    mutex_unlock(&qdev_mutex);
1742
+    mutex_unlock(&qdev_mutex);
1586
+
1743
+
1587
+    resp.usb_token = req_msg->usb_token;
1744
+    resp.usb_token = req_msg->usb_token;
1588
+    resp.usb_token_valid = 1;
1745
+    resp.usb_token_valid = 1;
1589
+    resp.internal_status = ret;
1746
+    resp.internal_status = ret;
1590
+    resp.internal_status_valid = 1;
1747
+    resp.internal_status_valid = 1;
1591
+    resp.status = ret ? USB_QMI_STREAM_REQ_FAILURE_V01 : ret;
1748
+    resp.status = ret ? USB_QMI_STREAM_REQ_FAILURE_V01 : ret;
1592
+    resp.status_valid = 1;
1749
+    resp.status_valid = 1;
1593
+    ret = qmi_send_response(svc->uaudio_svc_hdl, sq, txn,
1750
+    ret = qmi_send_response(svc->uaudio_svc_hdl, sq, txn,
1594
+            QMI_UAUDIO_STREAM_RESP_V01,
1751
+                QMI_UAUDIO_STREAM_RESP_V01,
1595
+            QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN,
1752
+                QMI_UAUDIO_STREAM_RESP_MSG_V01_MAX_MSG_LEN,
1596
+            qmi_uaudio_stream_resp_msg_v01_ei, &resp);
1753
+                qmi_uaudio_stream_resp_msg_v01_ei, &resp);
1597
+}
1754
+}
1598
+
1755
+
1599
+static struct qmi_msg_handler uaudio_stream_req_handlers = {
1756
+static struct qmi_msg_handler uaudio_stream_req_handlers = {
1600
+    .type = QMI_REQUEST,
1757
+    .type = QMI_REQUEST,
1601
+    .msg_id = QMI_UAUDIO_STREAM_REQ_V01,
1758
+    .msg_id = QMI_UAUDIO_STREAM_REQ_V01,
...
...
1610
+ * Initializes the USB qdev, which is used to carry information pertaining to
1767
+ * Initializes the USB qdev, which is used to carry information pertaining to
1611
+ * the offloading resources. This device is freed only when there are no longer
1768
+ * the offloading resources. This device is freed only when there are no longer
1612
+ * any offloading candidates. (i.e, when all audio devices are disconnected)
1769
+ * any offloading candidates. (i.e, when all audio devices are disconnected)
1613
+ *
1770
+ *
1614
+ */
1771
+ */
1615
+static int qc_usb_audio_offload_init_qmi_dev(struct usb_device *udev)
1772
+static int qc_usb_audio_offload_init_qmi_dev(void)
1616
+{
1773
+{
1617
+    uaudio_qdev = kzalloc(sizeof(struct uaudio_qmi_dev),
1774
+    uaudio_qdev = kzalloc(sizeof(*uaudio_qdev), GFP_KERNEL);
1618
+        GFP_KERNEL);
1619
+    if (!uaudio_qdev)
1775
+    if (!uaudio_qdev)
1620
+        return -ENOMEM;
1776
+        return -ENOMEM;
1621
+
1777
+
1622
+    /* initialize xfer ring and xfer buf iova list */
1778
+    /* initialize xfer ring and xfer buf iova list */
1623
+    INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list);
1779
+    INIT_LIST_HEAD(&uaudio_qdev->xfer_ring_list);
...
...
1631
+        IOVA_XFER_BUF_MAX - IOVA_XFER_BUF_BASE;
1787
+        IOVA_XFER_BUF_MAX - IOVA_XFER_BUF_BASE;
1632
+
1788
+
1633
+    return 0;
1789
+    return 0;
1634
+}
1790
+}
1635
+
1791
+
1792
+/* Populates ppcm_idx array with supported PCM indexes */
1793
+static int qc_usb_audio_offload_fill_avail_pcms(struct snd_usb_audio *chip,
1794
+                        struct snd_soc_usb_device *sdev)
1795
+{
1796
+    struct snd_usb_stream *as;
1797
+    struct snd_usb_substream *subs;
1798
+    int idx = 0;
1799
+
1800
+    list_for_each_entry(as, &chip->pcm_list, list) {
1801
+        subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK];
1802
+        if (subs->ep_num) {
1803
+            sdev->ppcm_idx[idx] = as->pcm->device;
1804
+            idx++;
1805
+        }
1806
+        /*
1807
+         * Break if the current index exceeds the number of possible
1808
+         * playback streams counted from the UAC descriptors.
1809
+         */
1810
+        if (idx >= sdev->num_playback)
1811
+            break;
1812
+    }
1813
+
1814
+    return -1;
1815
+}
1816
+
1636
+/**
1817
+/**
1637
+ * qc_usb_audio_offload_probe() - platform op connect handler
1818
+ * qc_usb_audio_offload_probe() - platform op connect handler
1638
+ * @chip: USB SND device
1819
+ * @chip: USB SND device
1639
+ *
1820
+ *
1640
+ * Platform connect handler when a USB SND device is detected. Will
1821
+ * Platform connect handler when a USB SND device is detected. Will
1641
+ * notify SOC USB about the connection to enable the USB ASoC backend
1822
+ * notify SOC USB about the connection to enable the USB ASoC backend
1642
+ * and populate internal USB chip array.
1823
+ * and populate internal USB chip array.
1643
+ *
1824
+ *
1644
+ */
1825
+ */
1645
+static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
1826
+static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
1646
+{
1827
+{
1828
+    struct usb_interface *intf = chip->intf[chip->num_interfaces - 1];
1829
+    struct usb_interface_descriptor *altsd;
1647
+    struct usb_device *udev = chip->dev;
1830
+    struct usb_device *udev = chip->dev;
1831
+    struct usb_host_interface *alts;
1832
+    struct snd_soc_usb_device *sdev;
1648
+    struct xhci_sideband *sb;
1833
+    struct xhci_sideband *sb;
1649
+    struct snd_soc_usb_device *sdev;
1650
+
1834
+
1651
+    /*
1835
+    /*
1652
+     * If there is no priv_data, the connected device is on a USB bus
1836
+     * If there is no priv_data, or no playback paths, the connected
1653
+     * that doesn't support offloading. Avoid populating entries for
1837
+     * device doesn't support offloading. Avoid populating entries for
1654
+     * this device.
1838
+     * this device.
1655
+     */
1839
+     */
1656
+    if (!snd_soc_usb_find_priv_data(usb_get_usb_backend(udev)))
1840
+    if (!snd_soc_usb_find_priv_data(usb_get_usb_backend(udev)) ||
1841
+     !usb_qmi_get_pcm_num(chip, 0))
1657
+        return;
1842
+        return;
1658
+
1843
+
1844
+    mutex_lock(&qdev_mutex);
1659
+    mutex_lock(&chip->mutex);
1845
+    mutex_lock(&chip->mutex);
1660
+    if (!uadev[chip->card->number].chip) {
1846
+    if (!uadev[chip->card->number].chip) {
1661
+        sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
1847
+        sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
1662
+        if (!sdev)
1848
+        if (!sdev)
1663
+            goto exit;
1849
+            goto exit;
1664
+
1850
+
1665
+        sb = xhci_sideband_register(udev);
1851
+        sb = xhci_sideband_register(intf, XHCI_SIDEBAND_VENDOR,
1852
+                     uaudio_sideband_notifier);
1666
+        if (!sb)
1853
+        if (!sb)
1667
+            goto free_sdev;
1854
+            goto free_sdev;
1668
+    } else {
1855
+    } else {
1669
+        sb = uadev[chip->card->number].sb;
1856
+        sb = uadev[chip->card->number].sb;
1670
+        sdev = uadev[chip->card->number].sdev;
1857
+        sdev = uadev[chip->card->number].sdev;
1671
+    }
1858
+    }
1672
+
1859
+
1673
+    mutex_lock(&qdev_mutex);
1674
+    if (!uaudio_qdev)
1860
+    if (!uaudio_qdev)
1675
+        qc_usb_audio_offload_init_qmi_dev(udev);
1861
+        qc_usb_audio_offload_init_qmi_dev();
1676
+
1862
+
1677
+    atomic_inc(&uaudio_qdev->qdev_in_use);
1863
+    atomic_inc(&uaudio_qdev->qdev_in_use);
1678
+    mutex_unlock(&qdev_mutex);
1679
+
1864
+
1680
+    uadev[chip->card->number].sb = sb;
1865
+    uadev[chip->card->number].sb = sb;
1681
+    uadev[chip->card->number].chip = chip;
1866
+    uadev[chip->card->number].chip = chip;
1682
+
1683
+    sdev->card_idx = chip->card->number;
1684
+    sdev->chip_idx = chip->index;
1685
+    uadev[chip->card->number].sdev = sdev;
1867
+    uadev[chip->card->number].sdev = sdev;
1686
+
1868
+
1687
+    uaudio_qdev->last_card_num = chip->card->number;
1869
+    alts = &intf->altsetting[0];
1688
+    snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
1870
+    altsd = get_iface_desc(alts);
1871
+
1872
+    /* Wait until all PCM devices are populated before notifying soc-usb */
1873
+    if (altsd->bInterfaceNumber == chip->last_iface) {
1874
+        sdev->num_playback = usb_qmi_get_pcm_num(chip, 0);
1875
+
1876
+        /*
1877
+         * Allocate playback pcm index array based on number of possible
1878
+         * playback paths within the UAC descriptors.
1879
+         */
1880
+        sdev->ppcm_idx = kcalloc(sdev->num_playback, sizeof(unsigned int),
1881
+                     GFP_KERNEL);
1882
+        if (!sdev->ppcm_idx)
1883
+            goto unreg_xhci;
1884
+
1885
+        qc_usb_audio_offload_fill_avail_pcms(chip, sdev);
1886
+        sdev->card_idx = chip->card->number;
1887
+        sdev->chip_idx = chip->index;
1888
+
1889
+        snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
1890
+    }
1891
+
1689
+    mutex_unlock(&chip->mutex);
1892
+    mutex_unlock(&chip->mutex);
1893
+    mutex_unlock(&qdev_mutex);
1690
+
1894
+
1691
+    return;
1895
+    return;
1692
+
1896
+
1897
+unreg_xhci:
1898
+    xhci_sideband_unregister(sb);
1899
+    uadev[chip->card->number].sb = NULL;
1693
+free_sdev:
1900
+free_sdev:
1694
+    kfree(sdev);
1901
+    kfree(sdev);
1902
+    uadev[chip->card->number].sdev = NULL;
1903
+    uadev[chip->card->number].chip = NULL;
1695
+exit:
1904
+exit:
1696
+    mutex_unlock(&chip->mutex);
1905
+    mutex_unlock(&chip->mutex);
1906
+    mutex_unlock(&qdev_mutex);
1697
+}
1907
+}
1698
+
1908
+
1699
+/**
1909
+/**
1700
+ * qc_usb_audio_cleanup_qmi_dev() - release qmi device
1910
+ * qc_usb_audio_cleanup_qmi_dev() - release qmi device
1701
+ *
1911
+ *
...
...
1717
+ * halted by issuing a QMI disconnect indication packet to the adsp.
1927
+ * halted by issuing a QMI disconnect indication packet to the adsp.
1718
+ *
1928
+ *
1719
+ */
1929
+ */
1720
+static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
1930
+static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
1721
+{
1931
+{
1722
+    struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0};
1723
+    struct uaudio_qmi_svc *svc = uaudio_svc;
1724
+    struct uaudio_dev *dev;
1932
+    struct uaudio_dev *dev;
1725
+    int card_num;
1933
+    int card_num;
1726
+    int ret;
1727
+
1934
+
1728
+    if (!chip)
1935
+    if (!chip)
1729
+        return;
1936
+        return;
1730
+
1937
+
1731
+    card_num = chip->card->number;
1938
+    card_num = chip->card->number;
...
...
1741
+        mutex_unlock(&qdev_mutex);
1948
+        mutex_unlock(&qdev_mutex);
1742
+        mutex_unlock(&chip->mutex);
1949
+        mutex_unlock(&chip->mutex);
1743
+        return;
1950
+        return;
1744
+    }
1951
+    }
1745
+
1952
+
1746
+    /* clean up */
1953
+    /* cleaned up already */
1747
+    if (!dev->udev)
1954
+    if (!dev->udev)
1748
+        goto done;
1955
+        goto done;
1749
+
1956
+
1750
+    if (atomic_read(&dev->in_use)) {
1957
+    uaudio_send_disconnect_ind(chip);
1751
+        mutex_unlock(&chip->mutex);
1752
+        mutex_unlock(&qdev_mutex);
1753
+        dev_dbg(uaudio_qdev->dev, "sending qmi indication disconnect\n");
1754
+        disconnect_ind.dev_event = USB_QMI_DEV_DISCONNECT_V01;
1755
+        disconnect_ind.slot_id = dev->udev->slot_id;
1756
+        disconnect_ind.controller_num = dev->usb_core_id;
1757
+        disconnect_ind.controller_num_valid = 1;
1758
+        ret = qmi_send_indication(svc->uaudio_svc_hdl, &svc->client_sq,
1759
+                QMI_UAUDIO_STREAM_IND_V01,
1760
+                QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN,
1761
+                qmi_uaudio_stream_ind_msg_v01_ei,
1762
+                &disconnect_ind);
1763
+        if (ret < 0)
1764
+            dev_err(uaudio_qdev->dev,
1765
+                "qmi send failed with err: %d\n", ret);
1766
+
1767
+        ret = wait_event_interruptible_timeout(dev->disconnect_wq,
1768
+                !atomic_read(&dev->in_use),
1769
+                msecs_to_jiffies(DEV_RELEASE_WAIT_TIMEOUT));
1770
+        if (!ret) {
1771
+            dev_err(uaudio_qdev->dev,
1772
+                "timeout while waiting for dev_release\n");
1773
+            atomic_set(&dev->in_use, 0);
1774
+        } else if (ret < 0) {
1775
+            dev_err(uaudio_qdev->dev, "failed with ret %d\n", ret);
1776
+            atomic_set(&dev->in_use, 0);
1777
+        }
1778
+        mutex_lock(&qdev_mutex);
1779
+        mutex_lock(&chip->mutex);
1780
+    }
1781
+
1782
+    uaudio_dev_cleanup(dev);
1958
+    uaudio_dev_cleanup(dev);
1783
+done:
1959
+done:
1784
+    snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev);
1785
+
1786
+    /*
1960
+    /*
1787
+     * If num_interfaces == 1, the last USB SND interface is being removed.
1961
+     * If num_interfaces == 1, the last USB SND interface is being removed.
1788
+     * This is to accommodate for devices w/ multiple UAC functions.
1962
+     * This is to accommodate for devices w/ multiple UAC functions.
1789
+     */
1963
+     */
1790
+    if (chip->num_interfaces == 1) {
1964
+    if (chip->num_interfaces == 1) {
1965
+        snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev);
1791
+        xhci_sideband_unregister(dev->sb);
1966
+        xhci_sideband_unregister(dev->sb);
1792
+        dev->chip = NULL;
1967
+        dev->chip = NULL;
1968
+        kfree(dev->sdev->ppcm_idx);
1793
+        kfree(dev->sdev);
1969
+        kfree(dev->sdev);
1794
+        dev->sdev = NULL;
1970
+        dev->sdev = NULL;
1795
+    }
1971
+    }
1796
+    mutex_unlock(&chip->mutex);
1972
+    mutex_unlock(&chip->mutex);
1797
+
1973
+
1798
+    atomic_dec(&uaudio_qdev->qdev_in_use);
1974
+    atomic_dec(&uaudio_qdev->qdev_in_use);
1799
+    if (!atomic_read(&uaudio_qdev->qdev_in_use)) {
1975
+    if (!atomic_read(&uaudio_qdev->qdev_in_use))
1800
+        snd_soc_usb_disconnect(usb_get_usb_backend(udev));
1801
+        qc_usb_audio_cleanup_qmi_dev();
1976
+        qc_usb_audio_cleanup_qmi_dev();
1802
+    }
1977
+
1803
+    mutex_unlock(&qdev_mutex);
1978
+    mutex_unlock(&qdev_mutex);
1804
+}
1979
+}
1805
+
1980
+
1806
+/**
1981
+/**
1807
+ * qc_usb_audio_offload_suspend() - USB offload PM suspend handler
1982
+ * qc_usb_audio_offload_suspend() - USB offload PM suspend handler
...
...
1810
+ *
1985
+ *
1811
+ * PM suspend handler to ensure that the USB offloading driver is able to stop
1986
+ * PM suspend handler to ensure that the USB offloading driver is able to stop
1812
+ * any pending traffic, so that the bus can be suspended.
1987
+ * any pending traffic, so that the bus can be suspended.
1813
+ *
1988
+ *
1814
+ */
1989
+ */
1815
+static void qc_usb_audio_offload_suspend(struct usb_interface *intf, pm_message_t message)
1990
+static void qc_usb_audio_offload_suspend(struct usb_interface *intf,
1991
+                     pm_message_t message)
1816
+{
1992
+{
1817
+    struct snd_usb_audio *chip = usb_get_intfdata(intf);
1993
+    struct snd_usb_audio *chip = usb_get_intfdata(intf);
1818
+    struct qmi_uaudio_stream_ind_msg_v01 disconnect_ind = {0};
1819
+    struct uaudio_qmi_svc *svc = uaudio_svc;
1820
+    struct uaudio_dev *dev;
1821
+    int card_num;
1994
+    int card_num;
1822
+    int ret;
1823
+
1995
+
1824
+    if (!chip)
1996
+    if (!chip)
1825
+        return;
1997
+        return;
1826
+
1998
+
1827
+    card_num = chip->card->number;
1999
+    card_num = chip->card->number;
1828
+    if (card_num >= SNDRV_CARDS)
2000
+    if (card_num >= SNDRV_CARDS)
1829
+        return;
2001
+        return;
1830
+
2002
+
1831
+
2003
+    mutex_lock(&qdev_mutex);
1832
+    mutex_lock(&chip->mutex);
2004
+    mutex_lock(&chip->mutex);
1833
+    dev = &uadev[card_num];
2005
+
1834
+
2006
+    uaudio_send_disconnect_ind(chip);
1835
+    if (atomic_read(&dev->in_use)) {
2007
+
1836
+        mutex_unlock(&chip->mutex);
2008
+    mutex_unlock(&qdev_mutex);
1837
+        dev_dbg(uaudio_qdev->dev, "sending qmi indication suspend\n");
1838
+        disconnect_ind.dev_event = USB_QMI_DEV_DISCONNECT_V01;
1839
+        disconnect_ind.slot_id = dev->udev->slot_id;
1840
+        disconnect_ind.controller_num = dev->usb_core_id;
1841
+        disconnect_ind.controller_num_valid = 1;
1842
+        ret = qmi_send_indication(svc->uaudio_svc_hdl, &svc->client_sq,
1843
+                QMI_UAUDIO_STREAM_IND_V01,
1844
+                QMI_UAUDIO_STREAM_IND_MSG_V01_MAX_MSG_LEN,
1845
+                qmi_uaudio_stream_ind_msg_v01_ei,
1846
+                &disconnect_ind);
1847
+        if (ret < 0)
1848
+            dev_err(uaudio_qdev->dev,
1849
+                "qmi send failed with err: %d\n", ret);
1850
+
1851
+        ret = wait_event_interruptible_timeout(dev->disconnect_wq,
1852
+                !atomic_read(&dev->in_use),
1853
+                msecs_to_jiffies(DEV_RELEASE_WAIT_TIMEOUT));
1854
+        if (!ret) {
1855
+            dev_err(uaudio_qdev->dev,
1856
+                "timeout while waiting for dev_release\n");
1857
+            atomic_set(&dev->in_use, 0);
1858
+        } else if (ret < 0) {
1859
+            dev_err(uaudio_qdev->dev, "failed with ret %d\n", ret);
1860
+            atomic_set(&dev->in_use, 0);
1861
+        }
1862
+        mutex_lock(&chip->mutex);
1863
+    }
1864
+    mutex_unlock(&chip->mutex);
2009
+    mutex_unlock(&chip->mutex);
1865
+}
2010
+}
1866
+
2011
+
1867
+static struct snd_usb_platform_ops offload_ops = {
2012
+static struct snd_usb_platform_ops offload_ops = {
1868
+    .connect_cb = qc_usb_audio_offload_probe,
2013
+    .connect_cb = qc_usb_audio_offload_probe,
...
...
1873
+static int __init qc_usb_audio_offload_init(void)
2018
+static int __init qc_usb_audio_offload_init(void)
1874
+{
2019
+{
1875
+    struct uaudio_qmi_svc *svc;
2020
+    struct uaudio_qmi_svc *svc;
1876
+    int ret;
2021
+    int ret;
1877
+
2022
+
1878
+    svc = kzalloc(sizeof(struct uaudio_qmi_svc), GFP_KERNEL);
2023
+    svc = kzalloc(sizeof(*svc), GFP_KERNEL);
1879
+    if (!svc)
2024
+    if (!svc)
1880
+        return -ENOMEM;
2025
+        return -ENOMEM;
1881
+
2026
+
1882
+    svc->uaudio_wq = create_singlethread_workqueue("uaudio_svc");
2027
+    svc->uaudio_svc_hdl = kzalloc(sizeof(*svc->uaudio_svc_hdl), GFP_KERNEL);
1883
+    if (!svc->uaudio_wq) {
2028
+    if (!svc->uaudio_svc_hdl) {
1884
+        ret = -ENOMEM;
2029
+        ret = -ENOMEM;
1885
+        goto free_svc;
2030
+        goto free_svc;
1886
+    }
2031
+    }
1887
+
2032
+
1888
+    svc->uaudio_svc_hdl = kzalloc(sizeof(struct qmi_handle), GFP_KERNEL);
1889
+    if (!svc->uaudio_svc_hdl) {
1890
+        ret = -ENOMEM;
1891
+        goto free_wq;
1892
+    }
1893
+
1894
+    ret = qmi_handle_init(svc->uaudio_svc_hdl,
2033
+    ret = qmi_handle_init(svc->uaudio_svc_hdl,
1895
+                QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN,
2034
+             QMI_UAUDIO_STREAM_REQ_MSG_V01_MAX_MSG_LEN,
1896
+                &uaudio_svc_ops_options,
2035
+             &uaudio_svc_ops_options,
1897
+                &uaudio_stream_req_handlers);
2036
+             &uaudio_stream_req_handlers);
1898
+    ret = qmi_add_server(svc->uaudio_svc_hdl, UAUDIO_STREAM_SERVICE_ID_V01,
2037
+    ret = qmi_add_server(svc->uaudio_svc_hdl, UAUDIO_STREAM_SERVICE_ID_V01,
1899
+                    UAUDIO_STREAM_SERVICE_VERS_V01, 0);
2038
+             UAUDIO_STREAM_SERVICE_VERS_V01, 0);
1900
+
2039
+
1901
+    INIT_WORK(&svc->qmi_disconnect_work, qmi_disconnect_work);
1902
+    uaudio_svc = svc;
2040
+    uaudio_svc = svc;
1903
+
2041
+
1904
+    ret = snd_usb_register_platform_ops(&offload_ops);
2042
+    ret = snd_usb_register_platform_ops(&offload_ops);
1905
+    if (ret < 0)
2043
+    if (ret < 0)
1906
+        goto release_qmi;
2044
+        goto release_qmi;
1907
+
2045
+
1908
+    return 0;
2046
+    return 0;
1909
+
2047
+
1910
+release_qmi:
2048
+release_qmi:
1911
+    qmi_handle_release(svc->uaudio_svc_hdl);
2049
+    qmi_handle_release(svc->uaudio_svc_hdl);
1912
+free_wq:
1913
+    destroy_workqueue(svc->uaudio_wq);
1914
+free_svc:
2050
+free_svc:
1915
+    kfree(svc);
2051
+    kfree(svc);
1916
+
2052
+
1917
+    return ret;
2053
+    return ret;
1918
+}
2054
+}
...
...
1931
+    snd_usb_unregister_platform_ops();
2067
+    snd_usb_unregister_platform_ops();
1932
+    for (idx = 0; idx < SNDRV_CARDS; idx++)
2068
+    for (idx = 0; idx < SNDRV_CARDS; idx++)
1933
+        qc_usb_audio_offload_disconnect(uadev[idx].chip);
2069
+        qc_usb_audio_offload_disconnect(uadev[idx].chip);
1934
+
2070
+
1935
+    qmi_handle_release(svc->uaudio_svc_hdl);
2071
+    qmi_handle_release(svc->uaudio_svc_hdl);
1936
+    flush_workqueue(svc->uaudio_wq);
1937
+    destroy_workqueue(svc->uaudio_wq);
1938
+    kfree(svc);
2072
+    kfree(svc);
1939
+    uaudio_svc = NULL;
2073
+    uaudio_svc = NULL;
1940
+}
2074
+}
1941
+
2075
+
1942
+module_init(qc_usb_audio_offload_init);
2076
+module_init(qc_usb_audio_offload_init);
1943
+module_exit(qc_usb_audio_offload_exit);
2077
+module_exit(qc_usb_audio_offload_exit);
1944
+
2078
+
1945
+MODULE_DESCRIPTION("QC USB Audio Offloading");
2079
+MODULE_DESCRIPTION("QC USB Audio Offloading");
1946
+MODULE_LICENSE("GPL");
2080
+MODULE_LICENSE("GPL");
diff view generated by jsdifflib
1
Utilize the card and PCM index coming from the USB QMI stream request.
1
Add proper checks and updates to the USB substream once receiving a USB QMI
2
This field follows what is set by the ASoC USB backend, and could
2
stream enable request. If the substream is already in use from the non
3
potentially carry information about a specific device selected through the
3
offload path, reject the stream enable request. In addition, update the
4
ASoC USB backend. The backend also has information about the last USB
4
USB substream opened parameter when enabling the offload path, so the
5
sound device plugged in, so it can choose to select the last device plugged
5
non offload path can be blocked.
6
in, accordingly.
7
6
8
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
9
---
8
---
10
sound/usb/qcom/qc_audio_offload.c | 8 ++------
9
sound/usb/qcom/qc_audio_offload.c | 15 ++++++++++++++-
11
1 file changed, 2 insertions(+), 6 deletions(-)
10
1 file changed, 14 insertions(+), 1 deletion(-)
12
11
13
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
12
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
14
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
15
--- a/sound/usb/qcom/qc_audio_offload.c
14
--- a/sound/usb/qcom/qc_audio_offload.c
16
+++ b/sound/usb/qcom/qc_audio_offload.c
15
+++ b/sound/usb/qcom/qc_audio_offload.c
17
@@ -XXX,XX +XXX,XX @@ struct uaudio_qmi_dev {
18
    bool er_mapped;
19
    /* reference count to number of possible consumers */
20
    atomic_t qdev_in_use;
21
-    /* idx to last udev card number plugged in */
22
-    unsigned int last_card_num;
23
};
24
25
struct uaudio_dev {
26
@@ -XXX,XX +XXX,XX @@ static int prepare_qmi_response(struct snd_usb_substream *subs,
27
    assoc = iface->intf_assoc;
28
    pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
29
    xfer_buf_len = req_msg->xfer_buff_size;
30
-    card_num = uaudio_qdev->last_card_num;
31
+    card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
32
33
    alts = &iface->altsetting[subs->cur_audiofmt->altset_idx];
34
    altsd = get_iface_desc(alts);
35
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
16
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
36
37
    direction = (req_msg->usb_token & QMI_STREAM_REQ_DIRECTION);
38
    pcm_dev_num = (req_msg->usb_token & QMI_STREAM_REQ_DEV_NUM_MASK) >> 8;
39
-    pcm_card_num = req_msg->enable ? uaudio_qdev->last_card_num :
40
-                ffs(uaudio_qdev->card_slot) - 1;
41
+    pcm_card_num = (req_msg->usb_token & QMI_STREAM_REQ_CARD_NUM_MASK) >> 16;
42
    if (pcm_card_num >= SNDRV_CARDS) {
43
        ret = -EINVAL;
44
        goto response;
17
        goto response;
45
@@ -XXX,XX +XXX,XX @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
18
    }
46
    sdev->num_capture = usb_qmi_get_pcm_num(chip, 1);
19
47
    uadev[chip->card->number].sdev = sdev;
20
+    mutex_lock(&chip->mutex);
48
21
    if (req_msg->enable) {
49
-    uaudio_qdev->last_card_num = chip->card->number;
22
-        if (info_idx < 0 || chip->system_suspend) {
50
    snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
23
+        if (info_idx < 0 || chip->system_suspend || subs->opened) {
51
    mutex_unlock(&chip->mutex);
24
            ret = -EBUSY;
25
+            mutex_unlock(&chip->mutex);
26
+
27
            goto response;
28
        }
29
+        subs->opened = 1;
30
    }
31
+    mutex_unlock(&chip->mutex);
32
33
    if (req_msg->service_interval_valid) {
34
        ret = get_data_interval_from_si(subs,
35
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
36
        if (!ret)
37
            ret = prepare_qmi_response(subs, req_msg, &resp,
38
                         info_idx);
39
+        if (ret < 0) {
40
+            mutex_lock(&chip->mutex);
41
+            subs->opened = 0;
42
+            mutex_unlock(&chip->mutex);
43
+        }
44
    } else {
45
        info = &uadev[pcm_card_num].info[info_idx];
46
        if (info->data_ep_pipe) {
47
@@ -XXX,XX +XXX,XX @@ static void handle_uaudio_stream_req(struct qmi_handle *handle,
48
        }
49
50
        disable_audio_stream(subs);
51
+        mutex_lock(&chip->mutex);
52
+        subs->opened = 0;
53
+        mutex_unlock(&chip->mutex);
54
    }
55
56
response:
diff view generated by jsdifflib
1
Expose a kcontrol on the platform sound card, which will allow for
1
In order to allow userspace/applications know about USB offloading status,
2
userspace to determine which USB card number and PCM device to offload.
2
expose a sound kcontrol that fetches information about which sound card
3
This allows for userspace to potentially tag an alternate path for a
3
and PCM index the USB device is mapped to for supporting offloading. In
4
specific USB SND card and PCM device. Previously, control was absent, and
4
the USB audio offloading framework, the ASoC BE DAI link is the entity
5
the offload path would be enabled on the last USB SND device which was
5
responsible for registering to the SOC USB layer.
6
connected. This logic will continue to be applicable if no mixer input is
6
7
received for specific device selection.
7
It is expected for the USB SND offloading driver to add the kcontrol to the
8
8
sound card associated with the USB audio device. An example output would
9
An example to configure the offload device using tinymix:
9
look like:
10
tinymix -D 0 set 'Q6USB offload SND device select' 1 0
10
11
11
tinymix -D 1 get 'USB Offload Playback Route PCM#0'
12
The above will set the Q6AFE device token to choose offload on card#1 and
12
-1, -1 (range -1->255)
13
pcm#0. Device selection is made possible by setting the Q6AFE device
13
14
token. The audio DSP utilizes this parameter, and will pass this field
14
This example signifies that there is no mapped ASoC path available for the
15
back to the USB offload driver within the QMI stream requests.
15
USB SND device.
16
17
tinymix -D 1 get 'USB Offload Playback Route PCM#0'
18
0, 0 (range -1->255)
19
20
This example signifies that the offload path is available over ASoC sound
21
card index#0 and PCM device#0.
22
23
The USB offload kcontrol will be added in addition to the existing
24
kcontrols identified by the USB SND mixer. The kcontrols used to modify
25
the USB audio device specific parameters are still valid and expected to be
26
used. These parameters are not mirrored to the ASoC subsystem.
16
27
17
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
28
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
18
---
29
---
19
sound/soc/qcom/qdsp6/q6usb.c | 125 ++++++++++++++++++++++++++++++++++-
30
sound/usb/Kconfig | 10 ++
20
1 file changed, 122 insertions(+), 3 deletions(-)
31
sound/usb/qcom/Makefile | 4 +
21
32
sound/usb/qcom/mixer_usb_offload.c | 158 +++++++++++++++++++++++++++++
22
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
33
sound/usb/qcom/mixer_usb_offload.h | 17 ++++
34
sound/usb/qcom/qc_audio_offload.c | 2 +
35
5 files changed, 191 insertions(+)
36
create mode 100644 sound/usb/qcom/mixer_usb_offload.c
37
create mode 100644 sound/usb/qcom/mixer_usb_offload.h
38
39
diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig
23
index XXXXXXX..XXXXXXX 100644
40
index XXXXXXX..XXXXXXX 100644
24
--- a/sound/soc/qcom/qdsp6/q6usb.c
41
--- a/sound/usb/Kconfig
25
+++ b/sound/soc/qcom/qdsp6/q6usb.c
42
+++ b/sound/usb/Kconfig
26
@@ -XXX,XX +XXX,XX @@
43
@@ -XXX,XX +XXX,XX @@ config SND_BCD2000
27
#include <linux/dma-map-ops.h>
44
     To compile this driver as a module, choose M here: the module
28
45
     will be called snd-bcd2000.
29
#include <sound/pcm.h>
46
47
+config SND_USB_QC_OFFLOAD_MIXER
48
+    tristate "Qualcomm USB Audio Offload mixer control"
49
+    help
50
+     Say Y to enable the Qualcomm USB audio offloading mixer controls.
51
+     This exposes an USB offload capable kcontrol to signal to
52
+     applications about which platform sound card can support USB
53
+     audio offload. The returning values specify the mapped ASoC card
54
+     and PCM device the USB audio device is associated to.
55
+
56
config SND_USB_AUDIO_QMI
57
    tristate "Qualcomm Audio Offload driver"
58
    depends on QCOM_QMI_HELPERS && SND_USB_AUDIO && USB_XHCI_SIDEBAND && SND_SOC_USB
59
+    select SND_USB_OFFLOAD_MIXER
60
    help
61
     Say Y here to enable the Qualcomm USB audio offloading feature.
62
63
diff --git a/sound/usb/qcom/Makefile b/sound/usb/qcom/Makefile
64
index XXXXXXX..XXXXXXX 100644
65
--- a/sound/usb/qcom/Makefile
66
+++ b/sound/usb/qcom/Makefile
67
@@ -XXX,XX +XXX,XX @@
68
snd-usb-audio-qmi-y := usb_audio_qmi_v01.o qc_audio_offload.o
69
obj-$(CONFIG_SND_USB_AUDIO_QMI) += snd-usb-audio-qmi.o
70
+
71
+ifneq ($(CONFIG_SND_USB_QC_OFFLOAD_MIXER),)
72
+snd-usb-audio-qmi-y += mixer_usb_offload.o
73
+endif
74
diff --git a/sound/usb/qcom/mixer_usb_offload.c b/sound/usb/qcom/mixer_usb_offload.c
75
new file mode 100644
76
index XXXXXXX..XXXXXXX
77
--- /dev/null
78
+++ b/sound/usb/qcom/mixer_usb_offload.c
79
@@ -XXX,XX +XXX,XX @@
80
+// SPDX-License-Identifier: GPL-2.0
81
+/*
82
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
83
+ */
84
+
85
+#include <linux/usb.h>
86
+
87
+#include <sound/core.h>
30
+#include <sound/control.h>
88
+#include <sound/control.h>
31
#include <sound/soc.h>
89
+#include <sound/soc-usb.h>
32
#include <sound/soc-usb.h>
90
+
33
#include <sound/pcm_params.h>
91
+#include "../usbaudio.h"
34
@@ -XXX,XX +XXX,XX @@ struct q6usb_port_data {
92
+#include "../card.h"
35
    struct q6afe_usb_cfg usb_cfg;
93
+#include "../helper.h"
36
    struct snd_soc_usb *usb;
94
+#include "../mixer.h"
37
    struct q6usb_offload priv;
95
+
38
+    struct mutex mutex;
96
+#include "mixer_usb_offload.h"
39
    unsigned long available_card_slot;
97
+
40
    struct q6usb_status status[SNDRV_CARDS];
98
+#define PCM_IDX(n) ((n) & 0xffff)
41
-    int active_idx;
99
+#define CARD_IDX(n) ((n) >> 16)
42
+    bool idx_valid;
100
+
43
+    int sel_card_idx;
101
+static int
44
+    int sel_pcm_idx;
102
+snd_usb_offload_card_route_get(struct snd_kcontrol *kcontrol,
45
};
103
+             struct snd_ctl_elem_value *ucontrol)
46
104
+{
47
static const struct snd_soc_dapm_widget q6usb_dai_widgets[] = {
105
+    struct device *sysdev = snd_kcontrol_chip(kcontrol);
48
@@ -XXX,XX +XXX,XX @@ static int q6usb_hw_params(struct snd_pcm_substream *substream,
49
             struct snd_soc_dai *dai)
50
{
51
    struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
52
+    struct snd_soc_pcm_runtime *rtd = substream->private_data;
53
+    struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0);
54
+    struct q6afe_port *q6usb_afe;
55
    int direction = substream->stream;
56
+    int chip_idx;
57
+    int ret;
106
+    int ret;
58
+
107
+
59
+    mutex_lock(&data->mutex);
108
+    ret = snd_soc_usb_update_offload_route(sysdev,
60
+    chip_idx = data->status[data->sel_card_idx].chip_index;
109
+                     CARD_IDX(kcontrol->private_value),
61
+
110
+                     PCM_IDX(kcontrol->private_value),
62
+    ret = snd_soc_usb_find_format(chip_idx, params, direction);
111
+                     SNDRV_PCM_STREAM_PLAYBACK,
63
+    if (ret < 0)
112
+                     SND_SOC_USB_KCTL_CARD_ROUTE,
64
+        goto out;
113
+                     ucontrol->value.integer.value);
65
+
114
+    if (ret < 0) {
66
+    q6usb_afe = q6afe_port_get_from_id(cpu_dai->dev, USB_RX);
115
+        ucontrol->value.integer.value[0] = -1;
67
+    if (IS_ERR(q6usb_afe))
116
+        ucontrol->value.integer.value[1] = -1;
68
+        goto out;
69
70
-    return snd_soc_usb_find_format(data->active_idx, params, direction);
71
+    ret = afe_port_send_usb_dev_param(q6usb_afe, data->sel_card_idx,
72
+                        data->sel_pcm_idx);
73
+    if (ret < 0)
74
+        goto out;
75
+
76
+    data->status[data->sel_card_idx].pcm_index = data->sel_pcm_idx;
77
+out:
78
+    mutex_unlock(&data->mutex);
79
+
80
+    return ret;
81
}
82
83
static const struct snd_soc_dai_ops q6usb_ops = {
84
@@ -XXX,XX +XXX,XX @@ static struct snd_soc_dai_driver q6usb_be_dais[] = {
85
    },
86
};
87
88
+static int q6usb_get_offload_dev(struct snd_kcontrol *kcontrol,
89
+                 struct snd_ctl_elem_value *ucontrol)
90
+{
91
+    struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
92
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
93
+    int pcm_idx;
94
+    int card_idx;
95
+
96
+    mutex_lock(&data->mutex);
97
+    if (!data->idx_valid) {
98
+        card_idx = -1;
99
+        pcm_idx = -1;
100
+    } else {
101
+        card_idx = data->sel_card_idx;
102
+        pcm_idx = data->sel_pcm_idx;
103
+    }
117
+    }
104
+
118
+
105
+    ucontrol->value.integer.value[0] = card_idx;
119
+    return 0;
106
+    ucontrol->value.integer.value[1] = pcm_idx;
120
+}
107
+    mutex_unlock(&data->mutex);
121
+
108
+
122
+static int snd_usb_offload_card_route_info(struct snd_kcontrol *kcontrol,
109
+    return 0;
123
+                     struct snd_ctl_elem_info *uinfo)
110
+}
111
+
112
+static int q6usb_put_offload_dev(struct snd_kcontrol *kcontrol,
113
+             struct snd_ctl_elem_value *ucontrol)
114
+{
115
+    struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
116
+    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
117
+    int changed = 0;
118
+    int pcmidx;
119
+    int cardidx;
120
+
121
+    cardidx = ucontrol->value.integer.value[0];
122
+    pcmidx = ucontrol->value.integer.value[1];
123
+
124
+    mutex_lock(&data->mutex);
125
+    if ((cardidx >= 0 && test_bit(cardidx, &data->available_card_slot))) {
126
+        data->sel_card_idx = cardidx;
127
+        changed = 1;
128
+    }
129
+
130
+    if ((pcmidx >= 0 && pcmidx < data->status[cardidx].num_pcm)) {
131
+        data->sel_pcm_idx = pcmidx;
132
+        data->idx_valid = true;
133
+        changed = 1;
134
+    }
135
+    mutex_unlock(&data->mutex);
136
+
137
+    return changed;
138
+}
139
+
140
+static int q6usb_offload_dev_info(struct snd_kcontrol *kcontrol,
141
+             struct snd_ctl_elem_info *uinfo)
142
+{
124
+{
143
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
125
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
144
+    uinfo->count = 2;
126
+    uinfo->count = 1;
145
+    uinfo->value.integer.min = -1;
127
+    uinfo->value.integer.min = -1;
146
+    uinfo->value.integer.max = SNDRV_CARDS;
128
+    uinfo->value.integer.max = SNDRV_CARDS;
147
+
129
+
148
+    return 0;
130
+    return 0;
149
+}
131
+}
150
+
132
+
151
+static const struct snd_kcontrol_new q6usb_offload_dev_ctrl = {
133
+static struct snd_kcontrol_new snd_usb_offload_mapped_card_ctl = {
152
+    .iface = SNDRV_CTL_ELEM_IFACE_CARD,
134
+    .iface = SNDRV_CTL_ELEM_IFACE_CARD,
153
+    .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
135
+    .access = SNDRV_CTL_ELEM_ACCESS_READ,
154
+    .name = "Q6USB offload SND device select",
136
+    .info = snd_usb_offload_card_route_info,
155
+    .info = q6usb_offload_dev_info,
137
+    .get = snd_usb_offload_card_route_get,
156
+    .get = q6usb_get_offload_dev,
157
+    .put = q6usb_put_offload_dev,
158
+};
138
+};
159
+
139
+
160
+/* Build a mixer control for a UAC connector control (jack-detect) */
140
+static int
161
+static void q6usb_connector_control_init(struct snd_soc_component *component)
141
+snd_usb_offload_pcm_route_get(struct snd_kcontrol *kcontrol,
162
+{
142
+             struct snd_ctl_elem_value *ucontrol)
143
+{
144
+    struct device *sysdev = snd_kcontrol_chip(kcontrol);
163
+    int ret;
145
+    int ret;
164
+
146
+
165
+    ret = snd_ctl_add(component->card->snd_card,
147
+    ret = snd_soc_usb_update_offload_route(sysdev,
166
+                snd_ctl_new1(&q6usb_offload_dev_ctrl, component));
148
+                     CARD_IDX(kcontrol->private_value),
167
+    if (ret < 0)
149
+                     PCM_IDX(kcontrol->private_value),
168
+        return;
150
+                     SNDRV_PCM_STREAM_PLAYBACK,
169
+}
151
+                     SND_SOC_USB_KCTL_PCM_ROUTE,
170
+
152
+                     ucontrol->value.integer.value);
171
static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
153
+    if (ret < 0) {
172
                    const struct of_phandle_args *args,
154
+        ucontrol->value.integer.value[0] = -1;
173
                    const char **dai_name)
155
+        ucontrol->value.integer.value[1] = -1;
174
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
156
+    }
175
157
+
176
    data = dev_get_drvdata(usb->component->dev);
158
+    return 0;
177
159
+}
178
+    mutex_lock(&data->mutex);
160
+
179
    if (connected) {
161
+static int snd_usb_offload_pcm_route_info(struct snd_kcontrol *kcontrol,
180
        /* We only track the latest USB headset plugged in */
162
+                     struct snd_ctl_elem_info *uinfo)
181
-        data->active_idx = sdev->card_idx;
163
+{
182
+        if (!data->idx_valid || data->sel_card_idx < 0)
164
+    uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
183
+            data->sel_card_idx = sdev->card_idx;
165
+    uinfo->count = 1;
184
166
+    uinfo->value.integer.min = -1;
185
        set_bit(sdev->card_idx, &data->available_card_slot);
167
+    /* Arbitrary max value, as there is no 'limit' on number of PCM devices */
186
        data->status[sdev->card_idx].num_pcm = sdev->num_playback;
168
+    uinfo->value.integer.max = 0xff;
187
@@ -XXX,XX +XXX,XX @@ static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
169
+
188
        data->status[sdev->card_idx].num_pcm = 0;
170
+    return 0;
189
        data->status[sdev->card_idx].chip_index = 0;
171
+}
172
+
173
+static struct snd_kcontrol_new snd_usb_offload_mapped_pcm_ctl = {
174
+    .iface = SNDRV_CTL_ELEM_IFACE_CARD,
175
+    .access = SNDRV_CTL_ELEM_ACCESS_READ,
176
+    .info = snd_usb_offload_pcm_route_info,
177
+    .get = snd_usb_offload_pcm_route_get,
178
+};
179
+
180
+/**
181
+ * snd_usb_offload_create_ctl() - Add USB offload bounded mixer
182
+ * @chip: USB SND chip device
183
+ *
184
+ * Creates a sound control for a USB audio device, so that applications can
185
+ * query for if there is an available USB audio offload path, and which
186
+ * card is managing it.
187
+ */
188
+int snd_usb_offload_create_ctl(struct snd_usb_audio *chip)
189
+{
190
+    struct usb_device *udev = chip->dev;
191
+    struct snd_kcontrol_new *chip_kctl;
192
+    struct snd_usb_substream *subs;
193
+    struct snd_usb_stream *as;
194
+    char ctl_name[48];
195
+    int ret;
196
+
197
+    list_for_each_entry(as, &chip->pcm_list, list) {
198
+        subs = &as->substream[SNDRV_PCM_STREAM_PLAYBACK];
199
+        if (!subs->ep_num || as->pcm_index > 0xff)
200
+            continue;
201
+
202
+        chip_kctl = &snd_usb_offload_mapped_card_ctl;
203
+        chip_kctl->count = 1;
204
+        /*
205
+         * Store the associated USB SND card number and PCM index for
206
+         * the kctl.
207
+         */
208
+        chip_kctl->private_value = as->pcm_index |
209
+                     chip->card->number << 16;
210
+        sprintf(ctl_name, "USB Offload Playback Card Route PCM#%d",
211
+            as->pcm_index);
212
+        chip_kctl->name = ctl_name;
213
+        ret = snd_ctl_add(chip->card, snd_ctl_new1(chip_kctl,
214
+                             udev->bus->sysdev));
215
+        if (ret < 0)
216
+            break;
217
+
218
+        chip_kctl = &snd_usb_offload_mapped_pcm_ctl;
219
+        chip_kctl->count = 1;
220
+        /*
221
+         * Store the associated USB SND card number and PCM index for
222
+         * the kctl.
223
+         */
224
+        chip_kctl->private_value = as->pcm_index |
225
+                     chip->card->number << 16;
226
+        sprintf(ctl_name, "USB Offload Playback PCM Route PCM#%d",
227
+            as->pcm_index);
228
+        chip_kctl->name = ctl_name;
229
+        ret = snd_ctl_add(chip->card, snd_ctl_new1(chip_kctl,
230
+                             udev->bus->sysdev));
231
+        if (ret < 0)
232
+            break;
233
+    }
234
+
235
+    return ret;
236
+}
237
+EXPORT_SYMBOL_GPL(snd_usb_offload_create_ctl);
238
diff --git a/sound/usb/qcom/mixer_usb_offload.h b/sound/usb/qcom/mixer_usb_offload.h
239
new file mode 100644
240
index XXXXXXX..XXXXXXX
241
--- /dev/null
242
+++ b/sound/usb/qcom/mixer_usb_offload.h
243
@@ -XXX,XX +XXX,XX @@
244
+/* SPDX-License-Identifier: GPL-2.0
245
+ *
246
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
247
+ */
248
+
249
+#ifndef __USB_OFFLOAD_MIXER_H
250
+#define __USB_OFFLOAD_MIXER_H
251
+
252
+#if IS_ENABLED(CONFIG_SND_USB_QC_OFFLOAD_MIXER)
253
+int snd_usb_offload_create_ctl(struct snd_usb_audio *chip);
254
+#else
255
+static inline int snd_usb_offload_create_ctl(struct snd_usb_audio *chip)
256
+{
257
+    return 0;
258
+}
259
+#endif
260
+#endif /* __USB_OFFLOAD_MIXER_H */
261
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
262
index XXXXXXX..XXXXXXX 100644
263
--- a/sound/usb/qcom/qc_audio_offload.c
264
+++ b/sound/usb/qcom/qc_audio_offload.c
265
@@ -XXX,XX +XXX,XX @@
266
#include "../pcm.h"
267
#include "../power.h"
268
269
+#include "mixer_usb_offload.h"
270
#include "usb_audio_qmi_v01.h"
271
272
/* Stream disable request timeout during USB device disconnect */
273
@@ -XXX,XX +XXX,XX @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
274
        sdev->card_idx = chip->card->number;
275
        sdev->chip_idx = chip->index;
276
277
+        snd_usb_offload_create_ctl(chip);
278
        snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
190
    }
279
    }
191
+    mutex_unlock(&data->mutex);
192
193
    return 0;
194
}
195
@@ -XXX,XX +XXX,XX @@ static int q6usb_component_probe(struct snd_soc_component *component)
196
{
197
    struct q6usb_port_data *data = dev_get_drvdata(component->dev);
198
199
+    q6usb_connector_control_init(component);
200
+
201
    data->usb = snd_soc_usb_add_port(component->dev, &data->priv, q6usb_alsa_connection_cb);
202
    if (IS_ERR(data->usb)) {
203
        dev_err(component->dev, "failed to add usb port\n");
204
@@ -XXX,XX +XXX,XX @@ static int q6usb_dai_dev_probe(struct platform_device *pdev)
205
206
    data->priv.domain = iommu_get_domain_for_dev(&pdev->dev);
207
208
+    mutex_init(&data->mutex);
209
+
210
    data->priv.dev = dev;
211
    dev_set_drvdata(dev, data);
diff view generated by jsdifflib
1
Currently, only the index to the USB SND card array is passed to the USB
1
If the vendor USB offload class driver is not ready/initialized before USB
2
backend. Pass through more information, specifically the USB SND card
2
SND discovers attached devices, utilize snd_usb_rediscover_devices() to
3
number and the number of PCM devices available. This allows for the DPCM
3
find all currently attached devices, so that the ASoC entities are notified
4
backend to determine what USB resources are available during situations,
4
on available USB audio devices.
5
such as USB audio offloading.
6
5
7
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
6
Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
8
---
7
---
9
sound/usb/qcom/qc_audio_offload.c | 21 ++++++++++++++++++---
8
sound/usb/qcom/qc_audio_offload.c | 2 ++
10
1 file changed, 18 insertions(+), 3 deletions(-)
9
1 file changed, 2 insertions(+)
11
10
12
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
11
diff --git a/sound/usb/qcom/qc_audio_offload.c b/sound/usb/qcom/qc_audio_offload.c
13
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
14
--- a/sound/usb/qcom/qc_audio_offload.c
13
--- a/sound/usb/qcom/qc_audio_offload.c
15
+++ b/sound/usb/qcom/qc_audio_offload.c
14
+++ b/sound/usb/qcom/qc_audio_offload.c
16
@@ -XXX,XX +XXX,XX @@ enum usb_qmi_audio_format {
15
@@ -XXX,XX +XXX,XX @@ static int __init qc_usb_audio_offload_init(void)
17
    USB_QMI_PCM_FORMAT_U32_BE,
16
    if (ret < 0)
18
};
17
        goto release_qmi;
19
18
20
+static int usb_qmi_get_pcm_num(struct snd_usb_audio *chip, int direction)
19
+    snd_usb_rediscover_devices();
21
+{
22
+    struct snd_usb_substream *subs = NULL;
23
+    struct snd_usb_stream *as;
24
+    int count = 0;
25
+
20
+
26
+    list_for_each_entry(as, &chip->pcm_list, list) {
21
    return 0;
27
+        subs = &as->substream[direction];
22
28
+        if (subs->ep_num)
23
release_qmi:
29
+            count++;
30
+    }
31
+
32
+    return count;
33
+}
34
+
35
static enum usb_qmi_audio_device_speed_enum_v01
36
get_speed_info(enum usb_device_speed udev_speed)
37
{
38
@@ -XXX,XX +XXX,XX @@ static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
39
40
    sdev->card_idx = chip->card->number;
41
    sdev->chip_idx = chip->index;
42
+    sdev->num_playback = usb_qmi_get_pcm_num(chip, 0);
43
+    sdev->num_capture = usb_qmi_get_pcm_num(chip, 1);
44
    uadev[chip->card->number].sdev = sdev;
45
46
    uaudio_qdev->last_card_num = chip->card->number;
47
@@ -XXX,XX +XXX,XX @@ static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
48
    mutex_unlock(&chip->mutex);
49
50
    atomic_dec(&uaudio_qdev->qdev_in_use);
51
-    if (!atomic_read(&uaudio_qdev->qdev_in_use)) {
52
-        snd_soc_usb_disconnect(usb_get_usb_backend(udev));
53
+    if (!atomic_read(&uaudio_qdev->qdev_in_use))
54
        qc_usb_audio_cleanup_qmi_dev();
55
-    }
56
    mutex_unlock(&qdev_mutex);
57
}
diff view generated by jsdifflib