18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * hdac-ext-stream.c - HD-audio extended stream operations. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015 Intel Corp 68c2ecf20Sopenharmony_ci * Author: Jeeja KP <jeeja.kp@intel.com> 78c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <sound/pcm.h> 158c2ecf20Sopenharmony_ci#include <sound/hda_register.h> 168c2ecf20Sopenharmony_ci#include <sound/hdaudio_ext.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/** 198c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_init - initialize each stream (aka device) 208c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 218c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream object to initialize 228c2ecf20Sopenharmony_ci * @idx: stream index number 238c2ecf20Sopenharmony_ci * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) 248c2ecf20Sopenharmony_ci * @tag: the tag id to assign 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * initialize the stream, if ppcap is enabled then init those and then 278c2ecf20Sopenharmony_ci * invoke hdac stream initialization routine 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_civoid snd_hdac_ext_stream_init(struct hdac_bus *bus, 308c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, 318c2ecf20Sopenharmony_ci int idx, int direction, int tag) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci if (bus->ppcap) { 348c2ecf20Sopenharmony_ci stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + 358c2ecf20Sopenharmony_ci AZX_PPHC_INTERVAL * idx; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + 388c2ecf20Sopenharmony_ci AZX_PPLC_MULTI * bus->num_streams + 398c2ecf20Sopenharmony_ci AZX_PPLC_INTERVAL * idx; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci if (bus->spbcap) { 438c2ecf20Sopenharmony_ci stream->spib_addr = bus->spbcap + AZX_SPB_BASE + 448c2ecf20Sopenharmony_ci AZX_SPB_INTERVAL * idx + 458c2ecf20Sopenharmony_ci AZX_SPB_SPIB; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + 488c2ecf20Sopenharmony_ci AZX_SPB_INTERVAL * idx + 498c2ecf20Sopenharmony_ci AZX_SPB_MAXFIFO; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (bus->drsmcap) 538c2ecf20Sopenharmony_ci stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + 548c2ecf20Sopenharmony_ci AZX_DRSM_INTERVAL * idx; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci stream->decoupled = false; 578c2ecf20Sopenharmony_ci snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci/** 628c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_init_all - create and initialize the stream objects 638c2ecf20Sopenharmony_ci * for an extended hda bus 648c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 658c2ecf20Sopenharmony_ci * @start_idx: start index for streams 668c2ecf20Sopenharmony_ci * @num_stream: number of streams to initialize 678c2ecf20Sopenharmony_ci * @dir: direction of streams 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ciint snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, 708c2ecf20Sopenharmony_ci int num_stream, int dir) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int stream_tag = 0; 738c2ecf20Sopenharmony_ci int i, tag, idx = start_idx; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci for (i = 0; i < num_stream; i++) { 768c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream = 778c2ecf20Sopenharmony_ci kzalloc(sizeof(*stream), GFP_KERNEL); 788c2ecf20Sopenharmony_ci if (!stream) 798c2ecf20Sopenharmony_ci return -ENOMEM; 808c2ecf20Sopenharmony_ci tag = ++stream_tag; 818c2ecf20Sopenharmony_ci snd_hdac_ext_stream_init(bus, stream, idx, dir, tag); 828c2ecf20Sopenharmony_ci idx++; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/** 918c2ecf20Sopenharmony_ci * snd_hdac_stream_free_all - free hdac extended stream objects 928c2ecf20Sopenharmony_ci * 938c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 948c2ecf20Sopenharmony_ci */ 958c2ecf20Sopenharmony_civoid snd_hdac_stream_free_all(struct hdac_bus *bus) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct hdac_stream *s, *_s; 988c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci list_for_each_entry_safe(s, _s, &bus->stream_list, list) { 1018c2ecf20Sopenharmony_ci stream = stream_to_hdac_ext_stream(s); 1028c2ecf20Sopenharmony_ci snd_hdac_ext_stream_decouple(bus, stream, false); 1038c2ecf20Sopenharmony_ci list_del(&s->list); 1048c2ecf20Sopenharmony_ci kfree(stream); 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci} 1078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_civoid snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, 1108c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, 1118c2ecf20Sopenharmony_ci bool decouple) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 1148c2ecf20Sopenharmony_ci u32 val; 1158c2ecf20Sopenharmony_ci int mask = AZX_PPCTL_PROCEN(hstream->index); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (decouple && !val) 1208c2ecf20Sopenharmony_ci snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask); 1218c2ecf20Sopenharmony_ci else if (!decouple && val) 1228c2ecf20Sopenharmony_ci snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci stream->decoupled = decouple; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/** 1298c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_decouple - decouple the hdac stream 1308c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 1318c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream object to initialize 1328c2ecf20Sopenharmony_ci * @decouple: flag to decouple 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_civoid snd_hdac_ext_stream_decouple(struct hdac_bus *bus, 1358c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, bool decouple) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 1388c2ecf20Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, stream, decouple); 1398c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci/** 1448c2ecf20Sopenharmony_ci * snd_hdac_ext_linkstream_start - start a stream 1458c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream to start 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_civoid snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 1508c2ecf20Sopenharmony_ci AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci/** 1558c2ecf20Sopenharmony_ci * snd_hdac_ext_link_stream_clear - stop a stream DMA 1568c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream to stop 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_civoid snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci/** 1658c2ecf20Sopenharmony_ci * snd_hdac_ext_link_stream_reset - reset a stream 1668c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream to reset 1678c2ecf20Sopenharmony_ci */ 1688c2ecf20Sopenharmony_civoid snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci unsigned char val; 1718c2ecf20Sopenharmony_ci int timeout; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci snd_hdac_ext_link_stream_clear(stream); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 1768c2ecf20Sopenharmony_ci AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); 1778c2ecf20Sopenharmony_ci udelay(3); 1788c2ecf20Sopenharmony_ci timeout = 50; 1798c2ecf20Sopenharmony_ci do { 1808c2ecf20Sopenharmony_ci val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & 1818c2ecf20Sopenharmony_ci AZX_PPLCCTL_STRST; 1828c2ecf20Sopenharmony_ci if (val) 1838c2ecf20Sopenharmony_ci break; 1848c2ecf20Sopenharmony_ci udelay(3); 1858c2ecf20Sopenharmony_ci } while (--timeout); 1868c2ecf20Sopenharmony_ci val &= ~AZX_PPLCCTL_STRST; 1878c2ecf20Sopenharmony_ci writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); 1888c2ecf20Sopenharmony_ci udelay(3); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci timeout = 50; 1918c2ecf20Sopenharmony_ci /* waiting for hardware to report that the stream is out of reset */ 1928c2ecf20Sopenharmony_ci do { 1938c2ecf20Sopenharmony_ci val = readl(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; 1948c2ecf20Sopenharmony_ci if (!val) 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci udelay(3); 1978c2ecf20Sopenharmony_ci } while (--timeout); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * snd_hdac_ext_link_stream_setup - set up the SD for streaming 2048c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream to set up 2058c2ecf20Sopenharmony_ci * @fmt: stream format 2068c2ecf20Sopenharmony_ci */ 2078c2ecf20Sopenharmony_ciint snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct hdac_stream *hstream = &stream->hstream; 2108c2ecf20Sopenharmony_ci unsigned int val; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci /* make sure the run bit is zero for SD */ 2138c2ecf20Sopenharmony_ci snd_hdac_ext_link_stream_clear(stream); 2148c2ecf20Sopenharmony_ci /* program the stream_tag */ 2158c2ecf20Sopenharmony_ci val = readl(stream->pplc_addr + AZX_REG_PPLCCTL); 2168c2ecf20Sopenharmony_ci val = (val & ~AZX_PPLCCTL_STRM_MASK) | 2178c2ecf20Sopenharmony_ci (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); 2188c2ecf20Sopenharmony_ci writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci /* program the stream format */ 2218c2ecf20Sopenharmony_ci writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * snd_hdac_ext_link_set_stream_id - maps stream id to link output 2298c2ecf20Sopenharmony_ci * @link: HD-audio ext link to set up 2308c2ecf20Sopenharmony_ci * @stream: stream id 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_civoid snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link, 2338c2ecf20Sopenharmony_ci int stream) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/** 2408c2ecf20Sopenharmony_ci * snd_hdac_ext_link_clear_stream_id - maps stream id to link output 2418c2ecf20Sopenharmony_ci * @link: HD-audio ext link to set up 2428c2ecf20Sopenharmony_ci * @stream: stream id 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_civoid snd_hdac_ext_link_clear_stream_id(struct hdac_ext_link *link, 2458c2ecf20Sopenharmony_ci int stream) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0); 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct hdac_ext_stream * 2528c2ecf20Sopenharmony_cihdac_ext_link_stream_assign(struct hdac_bus *bus, 2538c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct hdac_ext_stream *res = NULL; 2568c2ecf20Sopenharmony_ci struct hdac_stream *stream = NULL; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (!bus->ppcap) { 2598c2ecf20Sopenharmony_ci dev_err(bus->dev, "stream type not supported\n"); 2608c2ecf20Sopenharmony_ci return NULL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 2648c2ecf20Sopenharmony_ci list_for_each_entry(stream, &bus->stream_list, list) { 2658c2ecf20Sopenharmony_ci struct hdac_ext_stream *hstream = container_of(stream, 2668c2ecf20Sopenharmony_ci struct hdac_ext_stream, 2678c2ecf20Sopenharmony_ci hstream); 2688c2ecf20Sopenharmony_ci if (stream->direction != substream->stream) 2698c2ecf20Sopenharmony_ci continue; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci /* check if decoupled stream and not in use is available */ 2728c2ecf20Sopenharmony_ci if (hstream->decoupled && !hstream->link_locked) { 2738c2ecf20Sopenharmony_ci res = hstream; 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (!hstream->link_locked) { 2788c2ecf20Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, hstream, true); 2798c2ecf20Sopenharmony_ci res = hstream; 2808c2ecf20Sopenharmony_ci break; 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci if (res) { 2848c2ecf20Sopenharmony_ci res->link_locked = 1; 2858c2ecf20Sopenharmony_ci res->link_substream = substream; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 2888c2ecf20Sopenharmony_ci return res; 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic struct hdac_ext_stream * 2928c2ecf20Sopenharmony_cihdac_ext_host_stream_assign(struct hdac_bus *bus, 2938c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct hdac_ext_stream *res = NULL; 2968c2ecf20Sopenharmony_ci struct hdac_stream *stream = NULL; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci if (!bus->ppcap) { 2998c2ecf20Sopenharmony_ci dev_err(bus->dev, "stream type not supported\n"); 3008c2ecf20Sopenharmony_ci return NULL; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 3048c2ecf20Sopenharmony_ci list_for_each_entry(stream, &bus->stream_list, list) { 3058c2ecf20Sopenharmony_ci struct hdac_ext_stream *hstream = container_of(stream, 3068c2ecf20Sopenharmony_ci struct hdac_ext_stream, 3078c2ecf20Sopenharmony_ci hstream); 3088c2ecf20Sopenharmony_ci if (stream->direction != substream->stream) 3098c2ecf20Sopenharmony_ci continue; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci if (!stream->opened) { 3128c2ecf20Sopenharmony_ci if (!hstream->decoupled) 3138c2ecf20Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, hstream, true); 3148c2ecf20Sopenharmony_ci res = hstream; 3158c2ecf20Sopenharmony_ci break; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci if (res) { 3198c2ecf20Sopenharmony_ci res->hstream.opened = 1; 3208c2ecf20Sopenharmony_ci res->hstream.running = 0; 3218c2ecf20Sopenharmony_ci res->hstream.substream = substream; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci return res; 3268c2ecf20Sopenharmony_ci} 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci/** 3298c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_assign - assign a stream for the PCM 3308c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 3318c2ecf20Sopenharmony_ci * @substream: PCM substream to assign 3328c2ecf20Sopenharmony_ci * @type: type of stream (coupled, host or link stream) 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * This assigns the stream based on the type (coupled/host/link), for the 3358c2ecf20Sopenharmony_ci * given PCM substream, assigns it and returns the stream object 3368c2ecf20Sopenharmony_ci * 3378c2ecf20Sopenharmony_ci * coupled: Looks for an unused stream 3388c2ecf20Sopenharmony_ci * host: Looks for an unused decoupled host stream 3398c2ecf20Sopenharmony_ci * link: Looks for an unused decoupled link stream 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * If no stream is free, returns NULL. The function tries to keep using 3428c2ecf20Sopenharmony_ci * the same stream object when it's used beforehand. when a stream is 3438c2ecf20Sopenharmony_ci * decoupled, it becomes a host stream and link stream. 3448c2ecf20Sopenharmony_ci */ 3458c2ecf20Sopenharmony_cistruct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, 3468c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream, 3478c2ecf20Sopenharmony_ci int type) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci struct hdac_ext_stream *hstream = NULL; 3508c2ecf20Sopenharmony_ci struct hdac_stream *stream = NULL; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci switch (type) { 3538c2ecf20Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_COUPLED: 3548c2ecf20Sopenharmony_ci stream = snd_hdac_stream_assign(bus, substream); 3558c2ecf20Sopenharmony_ci if (stream) 3568c2ecf20Sopenharmony_ci hstream = container_of(stream, 3578c2ecf20Sopenharmony_ci struct hdac_ext_stream, hstream); 3588c2ecf20Sopenharmony_ci return hstream; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_HOST: 3618c2ecf20Sopenharmony_ci return hdac_ext_host_stream_assign(bus, substream); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_LINK: 3648c2ecf20Sopenharmony_ci return hdac_ext_link_stream_assign(bus, substream); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci default: 3678c2ecf20Sopenharmony_ci return NULL; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci/** 3738c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_release - release the assigned stream 3748c2ecf20Sopenharmony_ci * @stream: HD-audio ext core stream to release 3758c2ecf20Sopenharmony_ci * @type: type of stream (coupled, host or link stream) 3768c2ecf20Sopenharmony_ci * 3778c2ecf20Sopenharmony_ci * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). 3788c2ecf20Sopenharmony_ci */ 3798c2ecf20Sopenharmony_civoid snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) 3808c2ecf20Sopenharmony_ci{ 3818c2ecf20Sopenharmony_ci struct hdac_bus *bus = stream->hstream.bus; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci switch (type) { 3848c2ecf20Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_COUPLED: 3858c2ecf20Sopenharmony_ci snd_hdac_stream_release(&stream->hstream); 3868c2ecf20Sopenharmony_ci break; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_HOST: 3898c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 3908c2ecf20Sopenharmony_ci if (stream->decoupled && !stream->link_locked) 3918c2ecf20Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, stream, false); 3928c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 3938c2ecf20Sopenharmony_ci snd_hdac_stream_release(&stream->hstream); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case HDAC_EXT_STREAM_TYPE_LINK: 3978c2ecf20Sopenharmony_ci spin_lock_irq(&bus->reg_lock); 3988c2ecf20Sopenharmony_ci if (stream->decoupled && !stream->hstream.opened) 3998c2ecf20Sopenharmony_ci snd_hdac_ext_stream_decouple_locked(bus, stream, false); 4008c2ecf20Sopenharmony_ci stream->link_locked = 0; 4018c2ecf20Sopenharmony_ci stream->link_substream = NULL; 4028c2ecf20Sopenharmony_ci spin_unlock_irq(&bus->reg_lock); 4038c2ecf20Sopenharmony_ci break; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci default: 4068c2ecf20Sopenharmony_ci dev_dbg(bus->dev, "Invalid type %d\n", type); 4078c2ecf20Sopenharmony_ci } 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci/** 4138c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream 4148c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 4158c2ecf20Sopenharmony_ci * @enable: flag to enable/disable SPIB 4168c2ecf20Sopenharmony_ci * @index: stream index for which SPIB need to be enabled 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_civoid snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, 4198c2ecf20Sopenharmony_ci bool enable, int index) 4208c2ecf20Sopenharmony_ci{ 4218c2ecf20Sopenharmony_ci u32 mask = 0; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (!bus->spbcap) { 4248c2ecf20Sopenharmony_ci dev_err(bus->dev, "Address of SPB capability is NULL\n"); 4258c2ecf20Sopenharmony_ci return; 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci mask |= (1 << index); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci if (enable) 4318c2ecf20Sopenharmony_ci snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask); 4328c2ecf20Sopenharmony_ci else 4338c2ecf20Sopenharmony_ci snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci/** 4388c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_set_spib - sets the spib value of a stream 4398c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 4408c2ecf20Sopenharmony_ci * @stream: hdac_ext_stream 4418c2ecf20Sopenharmony_ci * @value: spib value to set 4428c2ecf20Sopenharmony_ci */ 4438c2ecf20Sopenharmony_ciint snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, 4448c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, u32 value) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (!bus->spbcap) { 4488c2ecf20Sopenharmony_ci dev_err(bus->dev, "Address of SPB capability is NULL\n"); 4498c2ecf20Sopenharmony_ci return -EINVAL; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci writel(value, stream->spib_addr); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci return 0; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream 4608c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 4618c2ecf20Sopenharmony_ci * @stream: hdac_ext_stream 4628c2ecf20Sopenharmony_ci * 4638c2ecf20Sopenharmony_ci * Return maxfifo for the stream 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ciint snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, 4668c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci if (!bus->spbcap) { 4708c2ecf20Sopenharmony_ci dev_err(bus->dev, "Address of SPB capability is NULL\n"); 4718c2ecf20Sopenharmony_ci return -EINVAL; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci return readl(stream->fifo_addr); 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci/** 4798c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream 4808c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 4818c2ecf20Sopenharmony_ci * @enable: flag to enable/disable DRSM 4828c2ecf20Sopenharmony_ci * @index: stream index for which DRSM need to be enabled 4838c2ecf20Sopenharmony_ci */ 4848c2ecf20Sopenharmony_civoid snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, 4858c2ecf20Sopenharmony_ci bool enable, int index) 4868c2ecf20Sopenharmony_ci{ 4878c2ecf20Sopenharmony_ci u32 mask = 0; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (!bus->drsmcap) { 4908c2ecf20Sopenharmony_ci dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 4918c2ecf20Sopenharmony_ci return; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci mask |= (1 << index); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (enable) 4978c2ecf20Sopenharmony_ci snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask); 4988c2ecf20Sopenharmony_ci else 4998c2ecf20Sopenharmony_ci snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci/** 5048c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream 5058c2ecf20Sopenharmony_ci * @bus: HD-audio core bus 5068c2ecf20Sopenharmony_ci * @stream: hdac_ext_stream 5078c2ecf20Sopenharmony_ci * @value: dpib value to set 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_ciint snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, 5108c2ecf20Sopenharmony_ci struct hdac_ext_stream *stream, u32 value) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci if (!bus->drsmcap) { 5148c2ecf20Sopenharmony_ci dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 5158c2ecf20Sopenharmony_ci return -EINVAL; 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci writel(value, stream->dpibr_addr); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci return 0; 5218c2ecf20Sopenharmony_ci} 5228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci/** 5258c2ecf20Sopenharmony_ci * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream 5268c2ecf20Sopenharmony_ci * @stream: hdac_ext_stream 5278c2ecf20Sopenharmony_ci * @value: lpib value to set 5288c2ecf20Sopenharmony_ci */ 5298c2ecf20Sopenharmony_ciint snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return 0; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 536