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 * @stream: hdac_ext_stream 534 * @value: lpib value to set 535 */ 536 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) 537 { 538 snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); 539 540 return 0; 541 } 542 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 543