xref: /linux/sound/hda/core/ext/stream.c (revision 177bf8620cf4ed290ee170a6c5966adc0924b336)
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