cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

hdac_ext_stream.c (14634B)


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