root/sound/hda/ext/hdac_ext_stream.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_hdac_ext_stream_init
  2. snd_hdac_ext_stream_init_all
  3. snd_hdac_stream_free_all
  4. snd_hdac_ext_stream_decouple
  5. snd_hdac_ext_link_stream_start
  6. snd_hdac_ext_link_stream_clear
  7. snd_hdac_ext_link_stream_reset
  8. snd_hdac_ext_link_stream_setup
  9. snd_hdac_ext_link_set_stream_id
  10. snd_hdac_ext_link_clear_stream_id
  11. hdac_ext_link_stream_assign
  12. hdac_ext_host_stream_assign
  13. snd_hdac_ext_stream_assign
  14. snd_hdac_ext_stream_release
  15. snd_hdac_ext_stream_spbcap_enable
  16. snd_hdac_ext_stream_set_spib
  17. snd_hdac_ext_stream_get_spbmaxfifo
  18. snd_hdac_ext_stop_streams
  19. snd_hdac_ext_stream_drsm_enable
  20. snd_hdac_ext_stream_set_dpibr
  21. snd_hdac_ext_stream_set_lpib

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

/* [<][>][^][v][top][bottom][index][help] */