1*b2660d1eSTakashi Iwai // SPDX-License-Identifier: GPL-2.0-only
2*b2660d1eSTakashi Iwai /*
3*b2660d1eSTakashi Iwai * hdac-ext-stream.c - HD-audio extended stream operations.
4*b2660d1eSTakashi Iwai *
5*b2660d1eSTakashi Iwai * Copyright (C) 2015 Intel Corp
6*b2660d1eSTakashi Iwai * Author: Jeeja KP <jeeja.kp@intel.com>
7*b2660d1eSTakashi Iwai * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8*b2660d1eSTakashi Iwai *
9*b2660d1eSTakashi Iwai * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10*b2660d1eSTakashi Iwai */
11*b2660d1eSTakashi Iwai
12*b2660d1eSTakashi Iwai #include <linux/delay.h>
13*b2660d1eSTakashi Iwai #include <linux/pci.h>
14*b2660d1eSTakashi Iwai #include <linux/pci_ids.h>
15*b2660d1eSTakashi Iwai #include <linux/slab.h>
16*b2660d1eSTakashi Iwai #include <sound/pcm.h>
17*b2660d1eSTakashi Iwai #include <sound/hda_register.h>
18*b2660d1eSTakashi Iwai #include <sound/hdaudio_ext.h>
19*b2660d1eSTakashi Iwai #include <sound/compress_driver.h>
20*b2660d1eSTakashi Iwai
21*b2660d1eSTakashi Iwai /**
22*b2660d1eSTakashi Iwai * snd_hdac_ext_host_stream_setup - Setup a HOST stream.
23*b2660d1eSTakashi Iwai * @hext_stream: HDAudio stream to set up.
24*b2660d1eSTakashi Iwai * @code_loading: Whether the stream is for PCM or code-loading.
25*b2660d1eSTakashi Iwai *
26*b2660d1eSTakashi Iwai * Return: Zero on success or negative error code.
27*b2660d1eSTakashi Iwai */
snd_hdac_ext_host_stream_setup(struct hdac_ext_stream * hext_stream,bool code_loading)28*b2660d1eSTakashi Iwai int snd_hdac_ext_host_stream_setup(struct hdac_ext_stream *hext_stream, bool code_loading)
29*b2660d1eSTakashi Iwai {
30*b2660d1eSTakashi Iwai return hext_stream->host_setup(hdac_stream(hext_stream), code_loading);
31*b2660d1eSTakashi Iwai }
32*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_host_stream_setup);
33*b2660d1eSTakashi Iwai
34*b2660d1eSTakashi Iwai /**
35*b2660d1eSTakashi Iwai * snd_hdac_apl_host_stream_setup - Setup a HOST stream following procedure
36*b2660d1eSTakashi Iwai * recommended for ApolloLake devices.
37*b2660d1eSTakashi Iwai * @hstream: HDAudio stream to set up.
38*b2660d1eSTakashi Iwai * @code_loading: Whether the stream is for PCM or code-loading.
39*b2660d1eSTakashi Iwai *
40*b2660d1eSTakashi Iwai * Return: Zero on success or negative error code.
41*b2660d1eSTakashi Iwai */
snd_hdac_apl_host_stream_setup(struct hdac_stream * hstream,bool code_loading)42*b2660d1eSTakashi Iwai static int snd_hdac_apl_host_stream_setup(struct hdac_stream *hstream, bool code_loading)
43*b2660d1eSTakashi Iwai {
44*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
45*b2660d1eSTakashi Iwai int ret;
46*b2660d1eSTakashi Iwai
47*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, false);
48*b2660d1eSTakashi Iwai ret = snd_hdac_stream_setup(hstream, code_loading);
49*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple(hstream->bus, hext_stream, true);
50*b2660d1eSTakashi Iwai
51*b2660d1eSTakashi Iwai return ret;
52*b2660d1eSTakashi Iwai }
53*b2660d1eSTakashi Iwai
54*b2660d1eSTakashi Iwai /**
55*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_init - initialize each stream (aka device)
56*b2660d1eSTakashi Iwai * @bus: HD-audio core bus
57*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream object to initialize
58*b2660d1eSTakashi Iwai * @idx: stream index number
59*b2660d1eSTakashi Iwai * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
60*b2660d1eSTakashi Iwai * @tag: the tag id to assign
61*b2660d1eSTakashi Iwai *
62*b2660d1eSTakashi Iwai * initialize the stream, if ppcap is enabled then init those and then
63*b2660d1eSTakashi Iwai * invoke hdac stream initialization routine
64*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_init(struct hdac_bus * bus,struct hdac_ext_stream * hext_stream,int idx,int direction,int tag)65*b2660d1eSTakashi Iwai static void snd_hdac_ext_stream_init(struct hdac_bus *bus,
66*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream,
67*b2660d1eSTakashi Iwai int idx, int direction, int tag)
68*b2660d1eSTakashi Iwai {
69*b2660d1eSTakashi Iwai if (bus->ppcap) {
70*b2660d1eSTakashi Iwai hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
71*b2660d1eSTakashi Iwai AZX_PPHC_INTERVAL * idx;
72*b2660d1eSTakashi Iwai
73*b2660d1eSTakashi Iwai hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
74*b2660d1eSTakashi Iwai AZX_PPLC_MULTI * bus->num_streams +
75*b2660d1eSTakashi Iwai AZX_PPLC_INTERVAL * idx;
76*b2660d1eSTakashi Iwai }
77*b2660d1eSTakashi Iwai
78*b2660d1eSTakashi Iwai hext_stream->decoupled = false;
79*b2660d1eSTakashi Iwai snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag);
80*b2660d1eSTakashi Iwai }
81*b2660d1eSTakashi Iwai
82*b2660d1eSTakashi Iwai /**
83*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_init_all - create and initialize the stream objects
84*b2660d1eSTakashi Iwai * for an extended hda bus
85*b2660d1eSTakashi Iwai * @bus: HD-audio core bus
86*b2660d1eSTakashi Iwai * @start_idx: start index for streams
87*b2660d1eSTakashi Iwai * @num_stream: number of streams to initialize
88*b2660d1eSTakashi Iwai * @dir: direction of streams
89*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_init_all(struct hdac_bus * bus,int start_idx,int num_stream,int dir)90*b2660d1eSTakashi Iwai int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx,
91*b2660d1eSTakashi Iwai int num_stream, int dir)
92*b2660d1eSTakashi Iwai {
93*b2660d1eSTakashi Iwai struct pci_dev *pci = to_pci_dev(bus->dev);
94*b2660d1eSTakashi Iwai int (*setup_op)(struct hdac_stream *, bool);
95*b2660d1eSTakashi Iwai int stream_tag = 0;
96*b2660d1eSTakashi Iwai int i, tag, idx = start_idx;
97*b2660d1eSTakashi Iwai
98*b2660d1eSTakashi Iwai if (pci->device == PCI_DEVICE_ID_INTEL_HDA_APL)
99*b2660d1eSTakashi Iwai setup_op = snd_hdac_apl_host_stream_setup;
100*b2660d1eSTakashi Iwai else
101*b2660d1eSTakashi Iwai setup_op = snd_hdac_stream_setup;
102*b2660d1eSTakashi Iwai
103*b2660d1eSTakashi Iwai for (i = 0; i < num_stream; i++) {
104*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream =
105*b2660d1eSTakashi Iwai kzalloc(sizeof(*hext_stream), GFP_KERNEL);
106*b2660d1eSTakashi Iwai if (!hext_stream)
107*b2660d1eSTakashi Iwai return -ENOMEM;
108*b2660d1eSTakashi Iwai tag = ++stream_tag;
109*b2660d1eSTakashi Iwai snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag);
110*b2660d1eSTakashi Iwai idx++;
111*b2660d1eSTakashi Iwai hext_stream->host_setup = setup_op;
112*b2660d1eSTakashi Iwai }
113*b2660d1eSTakashi Iwai
114*b2660d1eSTakashi Iwai return 0;
115*b2660d1eSTakashi Iwai
116*b2660d1eSTakashi Iwai }
117*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
118*b2660d1eSTakashi Iwai
119*b2660d1eSTakashi Iwai /**
120*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_free_all - free hdac extended stream objects
121*b2660d1eSTakashi Iwai *
122*b2660d1eSTakashi Iwai * @bus: HD-audio core bus
123*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_free_all(struct hdac_bus * bus)124*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_free_all(struct hdac_bus *bus)
125*b2660d1eSTakashi Iwai {
126*b2660d1eSTakashi Iwai struct hdac_stream *s, *_s;
127*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream;
128*b2660d1eSTakashi Iwai
129*b2660d1eSTakashi Iwai list_for_each_entry_safe(s, _s, &bus->stream_list, list) {
130*b2660d1eSTakashi Iwai hext_stream = stream_to_hdac_ext_stream(s);
131*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple(bus, hext_stream, false);
132*b2660d1eSTakashi Iwai list_del(&s->list);
133*b2660d1eSTakashi Iwai kfree(hext_stream);
134*b2660d1eSTakashi Iwai }
135*b2660d1eSTakashi Iwai }
136*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all);
137*b2660d1eSTakashi Iwai
snd_hdac_ext_stream_decouple_locked(struct hdac_bus * bus,struct hdac_ext_stream * hext_stream,bool decouple)138*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus,
139*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream,
140*b2660d1eSTakashi Iwai bool decouple)
141*b2660d1eSTakashi Iwai {
142*b2660d1eSTakashi Iwai struct hdac_stream *hstream = &hext_stream->hstream;
143*b2660d1eSTakashi Iwai u32 val;
144*b2660d1eSTakashi Iwai int mask = AZX_PPCTL_PROCEN(hstream->index);
145*b2660d1eSTakashi Iwai
146*b2660d1eSTakashi Iwai val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask;
147*b2660d1eSTakashi Iwai
148*b2660d1eSTakashi Iwai if (decouple && !val)
149*b2660d1eSTakashi Iwai snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask);
150*b2660d1eSTakashi Iwai else if (!decouple && val)
151*b2660d1eSTakashi Iwai snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0);
152*b2660d1eSTakashi Iwai
153*b2660d1eSTakashi Iwai hext_stream->decoupled = decouple;
154*b2660d1eSTakashi Iwai }
155*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked);
156*b2660d1eSTakashi Iwai
157*b2660d1eSTakashi Iwai /**
158*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_decouple - decouple the hdac stream
159*b2660d1eSTakashi Iwai * @bus: HD-audio core bus
160*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream object to initialize
161*b2660d1eSTakashi Iwai * @decouple: flag to decouple
162*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_decouple(struct hdac_bus * bus,struct hdac_ext_stream * hext_stream,bool decouple)163*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_decouple(struct hdac_bus *bus,
164*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream, bool decouple)
165*b2660d1eSTakashi Iwai {
166*b2660d1eSTakashi Iwai spin_lock_irq(&bus->reg_lock);
167*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple);
168*b2660d1eSTakashi Iwai spin_unlock_irq(&bus->reg_lock);
169*b2660d1eSTakashi Iwai }
170*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
171*b2660d1eSTakashi Iwai
172*b2660d1eSTakashi Iwai /**
173*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_start - start a stream
174*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream to start
175*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_start(struct hdac_ext_stream * hext_stream)176*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_start(struct hdac_ext_stream *hext_stream)
177*b2660d1eSTakashi Iwai {
178*b2660d1eSTakashi Iwai snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
179*b2660d1eSTakashi Iwai AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN);
180*b2660d1eSTakashi Iwai }
181*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_start);
182*b2660d1eSTakashi Iwai
183*b2660d1eSTakashi Iwai /**
184*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_clear - stop a stream DMA
185*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream to stop
186*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_clear(struct hdac_ext_stream * hext_stream)187*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_clear(struct hdac_ext_stream *hext_stream)
188*b2660d1eSTakashi Iwai {
189*b2660d1eSTakashi Iwai snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
190*b2660d1eSTakashi Iwai }
191*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_clear);
192*b2660d1eSTakashi Iwai
193*b2660d1eSTakashi Iwai /**
194*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_reset - reset a stream
195*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream to reset
196*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_reset(struct hdac_ext_stream * hext_stream)197*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream)
198*b2660d1eSTakashi Iwai {
199*b2660d1eSTakashi Iwai unsigned char val;
200*b2660d1eSTakashi Iwai int timeout;
201*b2660d1eSTakashi Iwai
202*b2660d1eSTakashi Iwai snd_hdac_ext_stream_clear(hext_stream);
203*b2660d1eSTakashi Iwai
204*b2660d1eSTakashi Iwai snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL,
205*b2660d1eSTakashi Iwai AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST);
206*b2660d1eSTakashi Iwai udelay(3);
207*b2660d1eSTakashi Iwai timeout = 50;
208*b2660d1eSTakashi Iwai do {
209*b2660d1eSTakashi Iwai val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) &
210*b2660d1eSTakashi Iwai AZX_PPLCCTL_STRST;
211*b2660d1eSTakashi Iwai if (val)
212*b2660d1eSTakashi Iwai break;
213*b2660d1eSTakashi Iwai udelay(3);
214*b2660d1eSTakashi Iwai } while (--timeout);
215*b2660d1eSTakashi Iwai val &= ~AZX_PPLCCTL_STRST;
216*b2660d1eSTakashi Iwai writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
217*b2660d1eSTakashi Iwai udelay(3);
218*b2660d1eSTakashi Iwai
219*b2660d1eSTakashi Iwai timeout = 50;
220*b2660d1eSTakashi Iwai /* waiting for hardware to report that the stream is out of reset */
221*b2660d1eSTakashi Iwai do {
222*b2660d1eSTakashi Iwai val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
223*b2660d1eSTakashi Iwai if (!val)
224*b2660d1eSTakashi Iwai break;
225*b2660d1eSTakashi Iwai udelay(3);
226*b2660d1eSTakashi Iwai } while (--timeout);
227*b2660d1eSTakashi Iwai
228*b2660d1eSTakashi Iwai }
229*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_reset);
230*b2660d1eSTakashi Iwai
231*b2660d1eSTakashi Iwai /**
232*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_setup - set up the SD for streaming
233*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream to set up
234*b2660d1eSTakashi Iwai * @fmt: stream format
235*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_setup(struct hdac_ext_stream * hext_stream,int fmt)236*b2660d1eSTakashi Iwai int snd_hdac_ext_stream_setup(struct hdac_ext_stream *hext_stream, int fmt)
237*b2660d1eSTakashi Iwai {
238*b2660d1eSTakashi Iwai struct hdac_stream *hstream = &hext_stream->hstream;
239*b2660d1eSTakashi Iwai unsigned int val;
240*b2660d1eSTakashi Iwai
241*b2660d1eSTakashi Iwai /* make sure the run bit is zero for SD */
242*b2660d1eSTakashi Iwai snd_hdac_ext_stream_clear(hext_stream);
243*b2660d1eSTakashi Iwai /* program the stream_tag */
244*b2660d1eSTakashi Iwai val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL);
245*b2660d1eSTakashi Iwai val = (val & ~AZX_PPLCCTL_STRM_MASK) |
246*b2660d1eSTakashi Iwai (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
247*b2660d1eSTakashi Iwai writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL);
248*b2660d1eSTakashi Iwai
249*b2660d1eSTakashi Iwai /* program the stream format */
250*b2660d1eSTakashi Iwai writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT);
251*b2660d1eSTakashi Iwai
252*b2660d1eSTakashi Iwai return 0;
253*b2660d1eSTakashi Iwai }
254*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_setup);
255*b2660d1eSTakashi Iwai
256*b2660d1eSTakashi Iwai static struct hdac_ext_stream *
hdac_ext_link_dma_stream_assign(struct hdac_bus * bus,struct snd_pcm_substream * substream)257*b2660d1eSTakashi Iwai hdac_ext_link_dma_stream_assign(struct hdac_bus *bus,
258*b2660d1eSTakashi Iwai struct snd_pcm_substream *substream)
259*b2660d1eSTakashi Iwai {
260*b2660d1eSTakashi Iwai struct hdac_ext_stream *res = NULL;
261*b2660d1eSTakashi Iwai struct hdac_stream *hstream = NULL;
262*b2660d1eSTakashi Iwai
263*b2660d1eSTakashi Iwai if (!bus->ppcap) {
264*b2660d1eSTakashi Iwai dev_err(bus->dev, "stream type not supported\n");
265*b2660d1eSTakashi Iwai return NULL;
266*b2660d1eSTakashi Iwai }
267*b2660d1eSTakashi Iwai
268*b2660d1eSTakashi Iwai spin_lock_irq(&bus->reg_lock);
269*b2660d1eSTakashi Iwai list_for_each_entry(hstream, &bus->stream_list, list) {
270*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream = container_of(hstream,
271*b2660d1eSTakashi Iwai struct hdac_ext_stream,
272*b2660d1eSTakashi Iwai hstream);
273*b2660d1eSTakashi Iwai if (hstream->direction != substream->stream)
274*b2660d1eSTakashi Iwai continue;
275*b2660d1eSTakashi Iwai
276*b2660d1eSTakashi Iwai /* check if link stream is available */
277*b2660d1eSTakashi Iwai if (!hext_stream->link_locked) {
278*b2660d1eSTakashi Iwai res = hext_stream;
279*b2660d1eSTakashi Iwai break;
280*b2660d1eSTakashi Iwai }
281*b2660d1eSTakashi Iwai
282*b2660d1eSTakashi Iwai }
283*b2660d1eSTakashi Iwai if (res) {
284*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple_locked(bus, res, true);
285*b2660d1eSTakashi Iwai res->link_locked = 1;
286*b2660d1eSTakashi Iwai res->link_substream = substream;
287*b2660d1eSTakashi Iwai }
288*b2660d1eSTakashi Iwai spin_unlock_irq(&bus->reg_lock);
289*b2660d1eSTakashi Iwai return res;
290*b2660d1eSTakashi Iwai }
291*b2660d1eSTakashi Iwai
292*b2660d1eSTakashi Iwai static struct hdac_ext_stream *
hdac_ext_host_dma_stream_assign(struct hdac_bus * bus,struct snd_pcm_substream * substream)293*b2660d1eSTakashi Iwai hdac_ext_host_dma_stream_assign(struct hdac_bus *bus,
294*b2660d1eSTakashi Iwai struct snd_pcm_substream *substream)
295*b2660d1eSTakashi Iwai {
296*b2660d1eSTakashi Iwai struct hdac_ext_stream *res = NULL;
297*b2660d1eSTakashi Iwai struct hdac_stream *hstream = NULL;
298*b2660d1eSTakashi Iwai
299*b2660d1eSTakashi Iwai if (!bus->ppcap) {
300*b2660d1eSTakashi Iwai dev_err(bus->dev, "stream type not supported\n");
301*b2660d1eSTakashi Iwai return NULL;
302*b2660d1eSTakashi Iwai }
303*b2660d1eSTakashi Iwai
304*b2660d1eSTakashi Iwai spin_lock_irq(&bus->reg_lock);
305*b2660d1eSTakashi Iwai list_for_each_entry(hstream, &bus->stream_list, list) {
306*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream = container_of(hstream,
307*b2660d1eSTakashi Iwai struct hdac_ext_stream,
308*b2660d1eSTakashi Iwai hstream);
309*b2660d1eSTakashi Iwai if (hstream->direction != substream->stream)
310*b2660d1eSTakashi Iwai continue;
311*b2660d1eSTakashi Iwai
312*b2660d1eSTakashi Iwai if (!hstream->opened) {
313*b2660d1eSTakashi Iwai res = hext_stream;
314*b2660d1eSTakashi Iwai break;
315*b2660d1eSTakashi Iwai }
316*b2660d1eSTakashi Iwai }
317*b2660d1eSTakashi Iwai if (res) {
318*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple_locked(bus, res, true);
319*b2660d1eSTakashi Iwai res->hstream.opened = 1;
320*b2660d1eSTakashi Iwai res->hstream.running = 0;
321*b2660d1eSTakashi Iwai res->hstream.substream = substream;
322*b2660d1eSTakashi Iwai }
323*b2660d1eSTakashi Iwai spin_unlock_irq(&bus->reg_lock);
324*b2660d1eSTakashi Iwai
325*b2660d1eSTakashi Iwai return res;
326*b2660d1eSTakashi Iwai }
327*b2660d1eSTakashi Iwai
328*b2660d1eSTakashi Iwai /**
329*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_assign - assign a stream for the PCM
330*b2660d1eSTakashi Iwai * @bus: HD-audio core bus
331*b2660d1eSTakashi Iwai * @substream: PCM substream to assign
332*b2660d1eSTakashi Iwai * @type: type of stream (coupled, host or link stream)
333*b2660d1eSTakashi Iwai *
334*b2660d1eSTakashi Iwai * This assigns the stream based on the type (coupled/host/link), for the
335*b2660d1eSTakashi Iwai * given PCM substream, assigns it and returns the stream object
336*b2660d1eSTakashi Iwai *
337*b2660d1eSTakashi Iwai * coupled: Looks for an unused stream
338*b2660d1eSTakashi Iwai * host: Looks for an unused decoupled host stream
339*b2660d1eSTakashi Iwai * link: Looks for an unused decoupled link stream
340*b2660d1eSTakashi Iwai *
341*b2660d1eSTakashi Iwai * If no stream is free, returns NULL. The function tries to keep using
342*b2660d1eSTakashi Iwai * the same stream object when it's used beforehand. when a stream is
343*b2660d1eSTakashi Iwai * decoupled, it becomes a host stream and link stream.
344*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_assign(struct hdac_bus * bus,struct snd_pcm_substream * substream,int type)345*b2660d1eSTakashi Iwai struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus,
346*b2660d1eSTakashi Iwai struct snd_pcm_substream *substream,
347*b2660d1eSTakashi Iwai int type)
348*b2660d1eSTakashi Iwai {
349*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream = NULL;
350*b2660d1eSTakashi Iwai struct hdac_stream *hstream = NULL;
351*b2660d1eSTakashi Iwai
352*b2660d1eSTakashi Iwai switch (type) {
353*b2660d1eSTakashi Iwai case HDAC_EXT_STREAM_TYPE_COUPLED:
354*b2660d1eSTakashi Iwai hstream = snd_hdac_stream_assign(bus, substream);
355*b2660d1eSTakashi Iwai if (hstream)
356*b2660d1eSTakashi Iwai hext_stream = container_of(hstream,
357*b2660d1eSTakashi Iwai struct hdac_ext_stream,
358*b2660d1eSTakashi Iwai hstream);
359*b2660d1eSTakashi Iwai return hext_stream;
360*b2660d1eSTakashi Iwai
361*b2660d1eSTakashi Iwai case HDAC_EXT_STREAM_TYPE_HOST:
362*b2660d1eSTakashi Iwai return hdac_ext_host_dma_stream_assign(bus, substream);
363*b2660d1eSTakashi Iwai
364*b2660d1eSTakashi Iwai case HDAC_EXT_STREAM_TYPE_LINK:
365*b2660d1eSTakashi Iwai return hdac_ext_link_dma_stream_assign(bus, substream);
366*b2660d1eSTakashi Iwai
367*b2660d1eSTakashi Iwai default:
368*b2660d1eSTakashi Iwai return NULL;
369*b2660d1eSTakashi Iwai }
370*b2660d1eSTakashi Iwai }
371*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
372*b2660d1eSTakashi Iwai
373*b2660d1eSTakashi Iwai /**
374*b2660d1eSTakashi Iwai * snd_hdac_ext_stream_release - release the assigned stream
375*b2660d1eSTakashi Iwai * @hext_stream: HD-audio ext core stream to release
376*b2660d1eSTakashi Iwai * @type: type of stream (coupled, host or link stream)
377*b2660d1eSTakashi Iwai *
378*b2660d1eSTakashi Iwai * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
379*b2660d1eSTakashi Iwai */
snd_hdac_ext_stream_release(struct hdac_ext_stream * hext_stream,int type)380*b2660d1eSTakashi Iwai void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type)
381*b2660d1eSTakashi Iwai {
382*b2660d1eSTakashi Iwai struct hdac_bus *bus = hext_stream->hstream.bus;
383*b2660d1eSTakashi Iwai
384*b2660d1eSTakashi Iwai switch (type) {
385*b2660d1eSTakashi Iwai case HDAC_EXT_STREAM_TYPE_COUPLED:
386*b2660d1eSTakashi Iwai snd_hdac_stream_release(&hext_stream->hstream);
387*b2660d1eSTakashi Iwai break;
388*b2660d1eSTakashi Iwai
389*b2660d1eSTakashi Iwai case HDAC_EXT_STREAM_TYPE_HOST:
390*b2660d1eSTakashi Iwai spin_lock_irq(&bus->reg_lock);
391*b2660d1eSTakashi Iwai /* couple link only if not in use */
392*b2660d1eSTakashi Iwai if (!hext_stream->link_locked)
393*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
394*b2660d1eSTakashi Iwai snd_hdac_stream_release_locked(&hext_stream->hstream);
395*b2660d1eSTakashi Iwai spin_unlock_irq(&bus->reg_lock);
396*b2660d1eSTakashi Iwai break;
397*b2660d1eSTakashi Iwai
398*b2660d1eSTakashi Iwai case HDAC_EXT_STREAM_TYPE_LINK:
399*b2660d1eSTakashi Iwai spin_lock_irq(&bus->reg_lock);
400*b2660d1eSTakashi Iwai /* couple host only if not in use */
401*b2660d1eSTakashi Iwai if (!hext_stream->hstream.opened)
402*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false);
403*b2660d1eSTakashi Iwai hext_stream->link_locked = 0;
404*b2660d1eSTakashi Iwai hext_stream->link_substream = NULL;
405*b2660d1eSTakashi Iwai spin_unlock_irq(&bus->reg_lock);
406*b2660d1eSTakashi Iwai break;
407*b2660d1eSTakashi Iwai
408*b2660d1eSTakashi Iwai default:
409*b2660d1eSTakashi Iwai dev_dbg(bus->dev, "Invalid type %d\n", type);
410*b2660d1eSTakashi Iwai }
411*b2660d1eSTakashi Iwai
412*b2660d1eSTakashi Iwai }
413*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
414*b2660d1eSTakashi Iwai
415*b2660d1eSTakashi Iwai /**
416*b2660d1eSTakashi Iwai * snd_hdac_ext_cstream_assign - assign a host stream for compress
417*b2660d1eSTakashi Iwai * @bus: HD-audio core bus
418*b2660d1eSTakashi Iwai * @cstream: Compress stream to assign
419*b2660d1eSTakashi Iwai *
420*b2660d1eSTakashi Iwai * Assign an unused host stream for the given compress stream.
421*b2660d1eSTakashi Iwai * If no stream is free, NULL is returned. Stream is decoupled
422*b2660d1eSTakashi Iwai * before assignment.
423*b2660d1eSTakashi Iwai */
snd_hdac_ext_cstream_assign(struct hdac_bus * bus,struct snd_compr_stream * cstream)424*b2660d1eSTakashi Iwai struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus,
425*b2660d1eSTakashi Iwai struct snd_compr_stream *cstream)
426*b2660d1eSTakashi Iwai {
427*b2660d1eSTakashi Iwai struct hdac_ext_stream *res = NULL;
428*b2660d1eSTakashi Iwai struct hdac_stream *hstream;
429*b2660d1eSTakashi Iwai
430*b2660d1eSTakashi Iwai spin_lock_irq(&bus->reg_lock);
431*b2660d1eSTakashi Iwai list_for_each_entry(hstream, &bus->stream_list, list) {
432*b2660d1eSTakashi Iwai struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream);
433*b2660d1eSTakashi Iwai
434*b2660d1eSTakashi Iwai if (hstream->direction != cstream->direction)
435*b2660d1eSTakashi Iwai continue;
436*b2660d1eSTakashi Iwai
437*b2660d1eSTakashi Iwai if (!hstream->opened) {
438*b2660d1eSTakashi Iwai res = hext_stream;
439*b2660d1eSTakashi Iwai break;
440*b2660d1eSTakashi Iwai }
441*b2660d1eSTakashi Iwai }
442*b2660d1eSTakashi Iwai
443*b2660d1eSTakashi Iwai if (res) {
444*b2660d1eSTakashi Iwai snd_hdac_ext_stream_decouple_locked(bus, res, true);
445*b2660d1eSTakashi Iwai res->hstream.opened = 1;
446*b2660d1eSTakashi Iwai res->hstream.running = 0;
447*b2660d1eSTakashi Iwai res->hstream.cstream = cstream;
448*b2660d1eSTakashi Iwai }
449*b2660d1eSTakashi Iwai spin_unlock_irq(&bus->reg_lock);
450*b2660d1eSTakashi Iwai
451*b2660d1eSTakashi Iwai return res;
452*b2660d1eSTakashi Iwai }
453*b2660d1eSTakashi Iwai EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign);
454