162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * hdac-ext-stream.c - HD-audio extended stream operations. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Intel Corp 662306a36Sopenharmony_ci * Author: Jeeja KP <jeeja.kp@intel.com> 762306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/delay.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <sound/pcm.h> 1562306a36Sopenharmony_ci#include <sound/hda_register.h> 1662306a36Sopenharmony_ci#include <sound/hdaudio_ext.h> 1762306a36Sopenharmony_ci#include <sound/compress_driver.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * snd_hdac_ext_stream_init - initialize each stream (aka device) 2162306a36Sopenharmony_ci * @bus: HD-audio core bus 2262306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream object to initialize 2362306a36Sopenharmony_ci * @idx: stream index number 2462306a36Sopenharmony_ci * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) 2562306a36Sopenharmony_ci * @tag: the tag id to assign 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * initialize the stream, if ppcap is enabled then init those and then 2862306a36Sopenharmony_ci * invoke hdac stream initialization routine 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic void snd_hdac_ext_stream_init(struct hdac_bus *bus, 3162306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, 3262306a36Sopenharmony_ci int idx, int direction, int tag) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci if (bus->ppcap) { 3562306a36Sopenharmony_ci hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + 3662306a36Sopenharmony_ci AZX_PPHC_INTERVAL * idx; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + 3962306a36Sopenharmony_ci AZX_PPLC_MULTI * bus->num_streams + 4062306a36Sopenharmony_ci AZX_PPLC_INTERVAL * idx; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci hext_stream->decoupled = false; 4462306a36Sopenharmony_ci snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag); 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * snd_hdac_ext_stream_init_all - create and initialize the stream objects 4962306a36Sopenharmony_ci * for an extended hda bus 5062306a36Sopenharmony_ci * @bus: HD-audio core bus 5162306a36Sopenharmony_ci * @start_idx: start index for streams 5262306a36Sopenharmony_ci * @num_stream: number of streams to initialize 5362306a36Sopenharmony_ci * @dir: direction of streams 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ciint snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, 5662306a36Sopenharmony_ci int num_stream, int dir) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci int stream_tag = 0; 5962306a36Sopenharmony_ci int i, tag, idx = start_idx; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci for (i = 0; i < num_stream; i++) { 6262306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = 6362306a36Sopenharmony_ci kzalloc(sizeof(*hext_stream), GFP_KERNEL); 6462306a36Sopenharmony_ci if (!hext_stream) 6562306a36Sopenharmony_ci return -ENOMEM; 6662306a36Sopenharmony_ci tag = ++stream_tag; 6762306a36Sopenharmony_ci snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); 6862306a36Sopenharmony_ci idx++; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci return 0; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * snd_hdac_ext_stream_free_all - free hdac extended stream objects 7862306a36Sopenharmony_ci * 7962306a36Sopenharmony_ci * @bus: HD-audio core bus 8062306a36Sopenharmony_ci */ 8162306a36Sopenharmony_civoid snd_hdac_ext_stream_free_all(struct hdac_bus *bus) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct hdac_stream *s, *_s; 8462306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci list_for_each_entry_safe(s, _s, &bus->stream_list, list) { 8762306a36Sopenharmony_ci hext_stream = stream_to_hdac_ext_stream(s); 8862306a36Sopenharmony_ci snd_hdac_ext_stream_decouple(bus, hext_stream, false); 8962306a36Sopenharmony_ci list_del(&s->list); 9062306a36Sopenharmony_ci kfree(hext_stream); 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci} 9362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_civoid snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, 9662306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, 9762306a36Sopenharmony_ci bool decouple) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 10062306a36Sopenharmony_ci u32 val; 10162306a36Sopenharmony_ci int mask = AZX_PPCTL_PROCEN(hstream->index); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (decouple && !val) 10662306a36Sopenharmony_ci snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask); 10762306a36Sopenharmony_ci else if (!decouple && val) 10862306a36Sopenharmony_ci snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci hext_stream->decoupled = decouple; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/** 11562306a36Sopenharmony_ci * snd_hdac_ext_stream_decouple - decouple the hdac stream 11662306a36Sopenharmony_ci * @bus: HD-audio core bus 11762306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream object to initialize 11862306a36Sopenharmony_ci * @decouple: flag to decouple 11962306a36Sopenharmony_ci */ 12062306a36Sopenharmony_civoid snd_hdac_ext_stream_decouple(struct hdac_bus *bus, 12162306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream, bool decouple) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 12462306a36Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple); 12562306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/** 13062306a36Sopenharmony_ci * snd_hdac_ext_stream_start - start a stream 13162306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream to start 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_civoid snd_hdac_ext_stream_start(struct hdac_ext_stream *hext_stream) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, 13662306a36Sopenharmony_ci AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_start); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci/** 14162306a36Sopenharmony_ci * snd_hdac_ext_stream_clear - stop a stream DMA 14262306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream to stop 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_civoid snd_hdac_ext_stream_clear(struct hdac_ext_stream *hext_stream) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_clear); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci/** 15162306a36Sopenharmony_ci * snd_hdac_ext_stream_reset - reset a stream 15262306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream to reset 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_civoid snd_hdac_ext_stream_reset(struct hdac_ext_stream *hext_stream) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci unsigned char val; 15762306a36Sopenharmony_ci int timeout; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci snd_hdac_ext_stream_clear(hext_stream); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, 16262306a36Sopenharmony_ci AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); 16362306a36Sopenharmony_ci udelay(3); 16462306a36Sopenharmony_ci timeout = 50; 16562306a36Sopenharmony_ci do { 16662306a36Sopenharmony_ci val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & 16762306a36Sopenharmony_ci AZX_PPLCCTL_STRST; 16862306a36Sopenharmony_ci if (val) 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci udelay(3); 17162306a36Sopenharmony_ci } while (--timeout); 17262306a36Sopenharmony_ci val &= ~AZX_PPLCCTL_STRST; 17362306a36Sopenharmony_ci writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); 17462306a36Sopenharmony_ci udelay(3); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci timeout = 50; 17762306a36Sopenharmony_ci /* waiting for hardware to report that the stream is out of reset */ 17862306a36Sopenharmony_ci do { 17962306a36Sopenharmony_ci val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; 18062306a36Sopenharmony_ci if (!val) 18162306a36Sopenharmony_ci break; 18262306a36Sopenharmony_ci udelay(3); 18362306a36Sopenharmony_ci } while (--timeout); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_reset); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/** 18962306a36Sopenharmony_ci * snd_hdac_ext_stream_setup - set up the SD for streaming 19062306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream to set up 19162306a36Sopenharmony_ci * @fmt: stream format 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_ciint snd_hdac_ext_stream_setup(struct hdac_ext_stream *hext_stream, int fmt) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct hdac_stream *hstream = &hext_stream->hstream; 19662306a36Sopenharmony_ci unsigned int val; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* make sure the run bit is zero for SD */ 19962306a36Sopenharmony_ci snd_hdac_ext_stream_clear(hext_stream); 20062306a36Sopenharmony_ci /* program the stream_tag */ 20162306a36Sopenharmony_ci val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL); 20262306a36Sopenharmony_ci val = (val & ~AZX_PPLCCTL_STRM_MASK) | 20362306a36Sopenharmony_ci (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); 20462306a36Sopenharmony_ci writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci /* program the stream format */ 20762306a36Sopenharmony_ci writew(fmt, hext_stream->pplc_addr + AZX_REG_PPLCFMT); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_setup); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic struct hdac_ext_stream * 21462306a36Sopenharmony_cihdac_ext_link_dma_stream_assign(struct hdac_bus *bus, 21562306a36Sopenharmony_ci struct snd_pcm_substream *substream) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci struct hdac_ext_stream *res = NULL; 21862306a36Sopenharmony_ci struct hdac_stream *hstream = NULL; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (!bus->ppcap) { 22162306a36Sopenharmony_ci dev_err(bus->dev, "stream type not supported\n"); 22262306a36Sopenharmony_ci return NULL; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 22662306a36Sopenharmony_ci list_for_each_entry(hstream, &bus->stream_list, list) { 22762306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = container_of(hstream, 22862306a36Sopenharmony_ci struct hdac_ext_stream, 22962306a36Sopenharmony_ci hstream); 23062306a36Sopenharmony_ci if (hstream->direction != substream->stream) 23162306a36Sopenharmony_ci continue; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* check if link stream is available */ 23462306a36Sopenharmony_ci if (!hext_stream->link_locked) { 23562306a36Sopenharmony_ci res = hext_stream; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci if (res) { 24162306a36Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, res, true); 24262306a36Sopenharmony_ci res->link_locked = 1; 24362306a36Sopenharmony_ci res->link_substream = substream; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 24662306a36Sopenharmony_ci return res; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic struct hdac_ext_stream * 25062306a36Sopenharmony_cihdac_ext_host_dma_stream_assign(struct hdac_bus *bus, 25162306a36Sopenharmony_ci struct snd_pcm_substream *substream) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct hdac_ext_stream *res = NULL; 25462306a36Sopenharmony_ci struct hdac_stream *hstream = NULL; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (!bus->ppcap) { 25762306a36Sopenharmony_ci dev_err(bus->dev, "stream type not supported\n"); 25862306a36Sopenharmony_ci return NULL; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 26262306a36Sopenharmony_ci list_for_each_entry(hstream, &bus->stream_list, list) { 26362306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = container_of(hstream, 26462306a36Sopenharmony_ci struct hdac_ext_stream, 26562306a36Sopenharmony_ci hstream); 26662306a36Sopenharmony_ci if (hstream->direction != substream->stream) 26762306a36Sopenharmony_ci continue; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (!hstream->opened) { 27062306a36Sopenharmony_ci res = hext_stream; 27162306a36Sopenharmony_ci break; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci if (res) { 27562306a36Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, res, true); 27662306a36Sopenharmony_ci res->hstream.opened = 1; 27762306a36Sopenharmony_ci res->hstream.running = 0; 27862306a36Sopenharmony_ci res->hstream.substream = substream; 27962306a36Sopenharmony_ci } 28062306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return res; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci/** 28662306a36Sopenharmony_ci * snd_hdac_ext_stream_assign - assign a stream for the PCM 28762306a36Sopenharmony_ci * @bus: HD-audio core bus 28862306a36Sopenharmony_ci * @substream: PCM substream to assign 28962306a36Sopenharmony_ci * @type: type of stream (coupled, host or link stream) 29062306a36Sopenharmony_ci * 29162306a36Sopenharmony_ci * This assigns the stream based on the type (coupled/host/link), for the 29262306a36Sopenharmony_ci * given PCM substream, assigns it and returns the stream object 29362306a36Sopenharmony_ci * 29462306a36Sopenharmony_ci * coupled: Looks for an unused stream 29562306a36Sopenharmony_ci * host: Looks for an unused decoupled host stream 29662306a36Sopenharmony_ci * link: Looks for an unused decoupled link stream 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * If no stream is free, returns NULL. The function tries to keep using 29962306a36Sopenharmony_ci * the same stream object when it's used beforehand. when a stream is 30062306a36Sopenharmony_ci * decoupled, it becomes a host stream and link stream. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistruct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, 30362306a36Sopenharmony_ci struct snd_pcm_substream *substream, 30462306a36Sopenharmony_ci int type) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = NULL; 30762306a36Sopenharmony_ci struct hdac_stream *hstream = NULL; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci switch (type) { 31062306a36Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_COUPLED: 31162306a36Sopenharmony_ci hstream = snd_hdac_stream_assign(bus, substream); 31262306a36Sopenharmony_ci if (hstream) 31362306a36Sopenharmony_ci hext_stream = container_of(hstream, 31462306a36Sopenharmony_ci struct hdac_ext_stream, 31562306a36Sopenharmony_ci hstream); 31662306a36Sopenharmony_ci return hext_stream; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_HOST: 31962306a36Sopenharmony_ci return hdac_ext_host_dma_stream_assign(bus, substream); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_LINK: 32262306a36Sopenharmony_ci return hdac_ext_link_dma_stream_assign(bus, substream); 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci default: 32562306a36Sopenharmony_ci return NULL; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci/** 33162306a36Sopenharmony_ci * snd_hdac_ext_stream_release - release the assigned stream 33262306a36Sopenharmony_ci * @hext_stream: HD-audio ext core stream to release 33362306a36Sopenharmony_ci * @type: type of stream (coupled, host or link stream) 33462306a36Sopenharmony_ci * 33562306a36Sopenharmony_ci * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_civoid snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct hdac_bus *bus = hext_stream->hstream.bus; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci switch (type) { 34262306a36Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_COUPLED: 34362306a36Sopenharmony_ci snd_hdac_stream_release(&hext_stream->hstream); 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_HOST: 34762306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 34862306a36Sopenharmony_ci /* couple link only if not in use */ 34962306a36Sopenharmony_ci if (!hext_stream->link_locked) 35062306a36Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 35162306a36Sopenharmony_ci snd_hdac_stream_release_locked(&hext_stream->hstream); 35262306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 35362306a36Sopenharmony_ci break; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_LINK: 35662306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 35762306a36Sopenharmony_ci /* couple host only if not in use */ 35862306a36Sopenharmony_ci if (!hext_stream->hstream.opened) 35962306a36Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 36062306a36Sopenharmony_ci hext_stream->link_locked = 0; 36162306a36Sopenharmony_ci hext_stream->link_substream = NULL; 36262306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 36362306a36Sopenharmony_ci break; 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci default: 36662306a36Sopenharmony_ci dev_dbg(bus->dev, "Invalid type %d\n", type); 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/** 37362306a36Sopenharmony_ci * snd_hdac_ext_cstream_assign - assign a host stream for compress 37462306a36Sopenharmony_ci * @bus: HD-audio core bus 37562306a36Sopenharmony_ci * @cstream: Compress stream to assign 37662306a36Sopenharmony_ci * 37762306a36Sopenharmony_ci * Assign an unused host stream for the given compress stream. 37862306a36Sopenharmony_ci * If no stream is free, NULL is returned. Stream is decoupled 37962306a36Sopenharmony_ci * before assignment. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistruct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus, 38262306a36Sopenharmony_ci struct snd_compr_stream *cstream) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct hdac_ext_stream *res = NULL; 38562306a36Sopenharmony_ci struct hdac_stream *hstream; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 38862306a36Sopenharmony_ci list_for_each_entry(hstream, &bus->stream_list, list) { 38962306a36Sopenharmony_ci struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (hstream->direction != cstream->direction) 39262306a36Sopenharmony_ci continue; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (!hstream->opened) { 39562306a36Sopenharmony_ci res = hext_stream; 39662306a36Sopenharmony_ci break; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci if (res) { 40162306a36Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, res, true); 40262306a36Sopenharmony_ci res->hstream.opened = 1; 40362306a36Sopenharmony_ci res->hstream.running = 0; 40462306a36Sopenharmony_ci res->hstream.cstream = cstream; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return res; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign); 411