xref: /openbmc/linux/sound/hda/ext/hdac_ext_stream.c (revision 9cfc5c90)
1 /*
2  *  hdac-ext-stream.c - HD-audio extended stream operations.
3  *
4  *  Copyright (C) 2015 Intel Corp
5  *  Author: Jeeja KP <jeeja.kp@intel.com>
6  *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; version 2 of the License.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  General Public License for more details.
16  *
17  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18  */
19 
20 #include <linux/delay.h>
21 #include <linux/slab.h>
22 #include <sound/pcm.h>
23 #include <sound/hda_register.h>
24 #include <sound/hdaudio_ext.h>
25 
26 /**
27  * snd_hdac_ext_stream_init - initialize each stream (aka device)
28  * @ebus: HD-audio ext core bus
29  * @stream: HD-audio ext core stream object to initialize
30  * @idx: stream index number
31  * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE)
32  * @tag: the tag id to assign
33  *
34  * initialize the stream, if ppcap is enabled then init those and then
35  * invoke hdac stream initialization routine
36  */
37 void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
38 				struct hdac_ext_stream *stream,
39 				int idx, int direction, int tag)
40 {
41 	struct hdac_bus *bus = &ebus->bus;
42 
43 	if (ebus->ppcap) {
44 		stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE +
45 				AZX_PPHC_INTERVAL * idx;
46 
47 		stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE +
48 				AZX_PPLC_MULTI * ebus->num_streams +
49 				AZX_PPLC_INTERVAL * idx;
50 	}
51 
52 	if (ebus->spbcap) {
53 		stream->spib_addr = ebus->spbcap + AZX_SPB_BASE +
54 					AZX_SPB_INTERVAL * idx +
55 					AZX_SPB_SPIB;
56 
57 		stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE +
58 					AZX_SPB_INTERVAL * idx +
59 					AZX_SPB_MAXFIFO;
60 	}
61 
62 	stream->decoupled = false;
63 	snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag);
64 }
65 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init);
66 
67 /**
68  * snd_hdac_ext_stream_init_all - create and initialize the stream objects
69  *   for an extended hda bus
70  * @ebus: HD-audio ext core bus
71  * @start_idx: start index for streams
72  * @num_stream: number of streams to initialize
73  * @dir: direction of streams
74  */
75 int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx,
76 		int num_stream, int dir)
77 {
78 	int stream_tag = 0;
79 	int i, tag, idx = start_idx;
80 
81 	for (i = 0; i < num_stream; i++) {
82 		struct hdac_ext_stream *stream =
83 				kzalloc(sizeof(*stream), GFP_KERNEL);
84 		if (!stream)
85 			return -ENOMEM;
86 		tag = ++stream_tag;
87 		snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag);
88 		idx++;
89 	}
90 
91 	return 0;
92 
93 }
94 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all);
95 
96 /**
97  * snd_hdac_stream_free_all - free hdac extended stream objects
98  *
99  * @ebus: HD-audio ext core bus
100  */
101 void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus)
102 {
103 	struct hdac_stream *s;
104 	struct hdac_ext_stream *stream;
105 	struct hdac_bus *bus = ebus_to_hbus(ebus);
106 
107 	while (!list_empty(&bus->stream_list)) {
108 		s = list_first_entry(&bus->stream_list, struct hdac_stream, list);
109 		stream = stream_to_hdac_ext_stream(s);
110 		list_del(&s->list);
111 		kfree(stream);
112 	}
113 }
114 EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all);
115 
116 /**
117  * snd_hdac_ext_stream_decouple - decouple the hdac stream
118  * @ebus: HD-audio ext core bus
119  * @stream: HD-audio ext core stream object to initialize
120  * @decouple: flag to decouple
121  */
122 void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
123 				struct hdac_ext_stream *stream, bool decouple)
124 {
125 	struct hdac_stream *hstream = &stream->hstream;
126 	struct hdac_bus *bus = &ebus->bus;
127 
128 	spin_lock_irq(&bus->reg_lock);
129 	if (decouple)
130 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0,
131 				AZX_PPCTL_PROCEN(hstream->index));
132 	else
133 		snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL,
134 					AZX_PPCTL_PROCEN(hstream->index), 0);
135 	stream->decoupled = decouple;
136 	spin_unlock_irq(&bus->reg_lock);
137 }
138 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple);
139 
140 /**
141  * snd_hdac_ext_linkstream_start - start a stream
142  * @stream: HD-audio ext core stream to start
143  */
144 void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream)
145 {
146 	snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_RUN);
147 }
148 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start);
149 
150 /**
151  * snd_hdac_ext_link_stream_clear - stop a stream DMA
152  * @stream: HD-audio ext core stream to stop
153  */
154 void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream)
155 {
156 	snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0);
157 }
158 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear);
159 
160 /**
161  * snd_hdac_ext_link_stream_reset - reset a stream
162  * @stream: HD-audio ext core stream to reset
163  */
164 void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream)
165 {
166 	unsigned char val;
167 	int timeout;
168 
169 	snd_hdac_ext_link_stream_clear(stream);
170 
171 	snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_STRST);
172 	udelay(3);
173 	timeout = 50;
174 	do {
175 		val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) &
176 				AZX_PPLCCTL_STRST;
177 		if (val)
178 			break;
179 		udelay(3);
180 	} while (--timeout);
181 	val &= ~AZX_PPLCCTL_STRST;
182 	writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
183 	udelay(3);
184 
185 	timeout = 50;
186 	/* waiting for hardware to report that the stream is out of reset */
187 	do {
188 		val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST;
189 		if (!val)
190 			break;
191 		udelay(3);
192 	} while (--timeout);
193 
194 }
195 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset);
196 
197 /**
198  * snd_hdac_ext_link_stream_setup -  set up the SD for streaming
199  * @stream: HD-audio ext core stream to set up
200  * @fmt: stream format
201  */
202 int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt)
203 {
204 	struct hdac_stream *hstream = &stream->hstream;
205 	unsigned int val;
206 
207 	/* make sure the run bit is zero for SD */
208 	snd_hdac_ext_link_stream_clear(stream);
209 	/* program the stream_tag */
210 	val = readl(stream->pplc_addr + AZX_REG_PPLCCTL);
211 	val = (val & ~AZX_PPLCCTL_STRM_MASK) |
212 		(hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT);
213 	writel(val, stream->pplc_addr + AZX_REG_PPLCCTL);
214 
215 	/* program the stream format */
216 	writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT);
217 
218 	return 0;
219 }
220 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
221 
222 /**
223  * snd_hdac_ext_link_set_stream_id - maps stream id to link output
224  * @link: HD-audio ext link to set up
225  * @stream: stream id
226  */
227 void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
228 				 int stream)
229 {
230 	snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
231 }
232 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
233 
234 /**
235  * snd_hdac_ext_link_clear_stream_id - maps stream id to link output
236  * @link: HD-audio ext link to set up
237  * @stream: stream id
238  */
239 void snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link,
240 				 int stream)
241 {
242 	snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, 0, (1 << stream));
243 }
244 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id);
245 
246 static struct hdac_ext_stream *
247 hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
248 				struct snd_pcm_substream *substream)
249 {
250 	struct hdac_ext_stream *res = NULL;
251 	struct hdac_stream *stream = NULL;
252 	struct hdac_bus *hbus = &ebus->bus;
253 
254 	if (!ebus->ppcap) {
255 		dev_err(hbus->dev, "stream type not supported\n");
256 		return NULL;
257 	}
258 
259 	list_for_each_entry(stream, &hbus->stream_list, list) {
260 		struct hdac_ext_stream *hstream = container_of(stream,
261 						struct hdac_ext_stream,
262 						hstream);
263 		if (stream->direction != substream->stream)
264 			continue;
265 
266 		/* check if decoupled stream and not in use is available */
267 		if (hstream->decoupled && !hstream->link_locked) {
268 			res = hstream;
269 			break;
270 		}
271 
272 		if (!hstream->link_locked) {
273 			snd_hdac_ext_stream_decouple(ebus, hstream, true);
274 			res = hstream;
275 			break;
276 		}
277 	}
278 	if (res) {
279 		spin_lock_irq(&hbus->reg_lock);
280 		res->link_locked = 1;
281 		res->link_substream = substream;
282 		spin_unlock_irq(&hbus->reg_lock);
283 	}
284 	return res;
285 }
286 
287 static struct hdac_ext_stream *
288 hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
289 				struct snd_pcm_substream *substream)
290 {
291 	struct hdac_ext_stream *res = NULL;
292 	struct hdac_stream *stream = NULL;
293 	struct hdac_bus *hbus = &ebus->bus;
294 
295 	if (!ebus->ppcap) {
296 		dev_err(hbus->dev, "stream type not supported\n");
297 		return NULL;
298 	}
299 
300 	list_for_each_entry(stream, &hbus->stream_list, list) {
301 		struct hdac_ext_stream *hstream = container_of(stream,
302 						struct hdac_ext_stream,
303 						hstream);
304 		if (stream->direction != substream->stream)
305 			continue;
306 
307 		if (!stream->opened) {
308 			if (!hstream->decoupled)
309 				snd_hdac_ext_stream_decouple(ebus, hstream, true);
310 			res = hstream;
311 			break;
312 		}
313 	}
314 	if (res) {
315 		spin_lock_irq(&hbus->reg_lock);
316 		res->hstream.opened = 1;
317 		res->hstream.running = 0;
318 		res->hstream.substream = substream;
319 		spin_unlock_irq(&hbus->reg_lock);
320 	}
321 
322 	return res;
323 }
324 
325 /**
326  * snd_hdac_ext_stream_assign - assign a stream for the PCM
327  * @ebus: HD-audio ext core bus
328  * @substream: PCM substream to assign
329  * @type: type of stream (coupled, host or link stream)
330  *
331  * This assigns the stream based on the type (coupled/host/link), for the
332  * given PCM substream, assigns it and returns the stream object
333  *
334  * coupled: Looks for an unused stream
335  * host: Looks for an unused decoupled host stream
336  * link: Looks for an unused decoupled link stream
337  *
338  * If no stream is free, returns NULL. The function tries to keep using
339  * the same stream object when it's used beforehand.  when a stream is
340  * decoupled, it becomes a host stream and link stream.
341  */
342 struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus,
343 					   struct snd_pcm_substream *substream,
344 					   int type)
345 {
346 	struct hdac_ext_stream *hstream = NULL;
347 	struct hdac_stream *stream = NULL;
348 	struct hdac_bus *hbus = &ebus->bus;
349 
350 	switch (type) {
351 	case HDAC_EXT_STREAM_TYPE_COUPLED:
352 		stream = snd_hdac_stream_assign(hbus, substream);
353 		if (stream)
354 			hstream = container_of(stream,
355 					struct hdac_ext_stream, hstream);
356 		return hstream;
357 
358 	case HDAC_EXT_STREAM_TYPE_HOST:
359 		return hdac_ext_host_stream_assign(ebus, substream);
360 
361 	case HDAC_EXT_STREAM_TYPE_LINK:
362 		return hdac_ext_link_stream_assign(ebus, substream);
363 
364 	default:
365 		return NULL;
366 	}
367 }
368 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign);
369 
370 /**
371  * snd_hdac_ext_stream_release - release the assigned stream
372  * @stream: HD-audio ext core stream to release
373  * @type: type of stream (coupled, host or link stream)
374  *
375  * Release the stream that has been assigned by snd_hdac_ext_stream_assign().
376  */
377 void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
378 {
379 	struct hdac_bus *bus = stream->hstream.bus;
380 	struct hdac_ext_bus *ebus = hbus_to_ebus(bus);
381 
382 	switch (type) {
383 	case HDAC_EXT_STREAM_TYPE_COUPLED:
384 		snd_hdac_stream_release(&stream->hstream);
385 		break;
386 
387 	case HDAC_EXT_STREAM_TYPE_HOST:
388 		if (stream->decoupled && !stream->link_locked)
389 			snd_hdac_ext_stream_decouple(ebus, stream, false);
390 		snd_hdac_stream_release(&stream->hstream);
391 		break;
392 
393 	case HDAC_EXT_STREAM_TYPE_LINK:
394 		if (stream->decoupled && !stream->hstream.opened)
395 			snd_hdac_ext_stream_decouple(ebus, stream, false);
396 		spin_lock_irq(&bus->reg_lock);
397 		stream->link_locked = 0;
398 		stream->link_substream = NULL;
399 		spin_unlock_irq(&bus->reg_lock);
400 		break;
401 
402 	default:
403 		dev_dbg(bus->dev, "Invalid type %d\n", type);
404 	}
405 
406 }
407 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release);
408 
409 /**
410  * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream
411  * @ebus: HD-audio ext core bus
412  * @enable: flag to enable/disable SPIB
413  * @index: stream index for which SPIB need to be enabled
414  */
415 void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
416 				 bool enable, int index)
417 {
418 	u32 mask = 0;
419 	u32 register_mask = 0;
420 	struct hdac_bus *bus = &ebus->bus;
421 
422 	if (!ebus->spbcap) {
423 		dev_err(bus->dev, "Address of SPB capability is NULL");
424 		return;
425 	}
426 
427 	mask |= (1 << index);
428 
429 	register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL);
430 
431 	mask |= register_mask;
432 
433 	if (enable)
434 		snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
435 	else
436 		snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
437 }
438 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
439 
440 /**
441  * snd_hdac_ext_stream_set_spib - sets the spib value of a stream
442  * @ebus: HD-audio ext core bus
443  * @stream: hdac_ext_stream
444  * @value: spib value to set
445  */
446 int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
447 				 struct hdac_ext_stream *stream, u32 value)
448 {
449 	struct hdac_bus *bus = &ebus->bus;
450 
451 	if (!ebus->spbcap) {
452 		dev_err(bus->dev, "Address of SPB capability is NULL");
453 		return -EINVAL;
454 	}
455 
456 	writel(value, stream->spib_addr);
457 
458 	return 0;
459 }
460 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib);
461 
462 /**
463  * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream
464  * @ebus: HD-audio ext core bus
465  * @stream: hdac_ext_stream
466  *
467  * Return maxfifo for the stream
468  */
469 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
470 				 struct hdac_ext_stream *stream)
471 {
472 	struct hdac_bus *bus = &ebus->bus;
473 
474 	if (!ebus->spbcap) {
475 		dev_err(bus->dev, "Address of SPB capability is NULL");
476 		return -EINVAL;
477 	}
478 
479 	return readl(stream->fifo_addr);
480 }
481 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo);
482 
483 
484 /**
485  * snd_hdac_ext_stop_streams - stop all stream if running
486  * @ebus: HD-audio ext core bus
487  */
488 void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus)
489 {
490 	struct hdac_bus *bus = ebus_to_hbus(ebus);
491 	struct hdac_stream *stream;
492 
493 	if (bus->chip_init) {
494 		list_for_each_entry(stream, &bus->stream_list, list)
495 			snd_hdac_stream_stop(stream);
496 		snd_hdac_bus_stop_chip(bus);
497 	}
498 }
499 EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams);
500