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 void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, 110 struct hdac_ext_stream *stream, 111 bool decouple) 112 { 113 struct hdac_stream *hstream = &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 stream->decoupled = decouple; 125 } 126 EXPORT_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 * @stream: HD-audio ext core stream object to initialize 132 * @decouple: flag to decouple 133 */ 134 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, 135 struct hdac_ext_stream *stream, bool decouple) 136 { 137 spin_lock_irq(&bus->reg_lock); 138 snd_hdac_ext_stream_decouple_locked(bus, stream, decouple); 139 spin_unlock_irq(&bus->reg_lock); 140 } 141 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); 142 143 /** 144 * snd_hdac_ext_link_stream_start - start a stream 145 * @stream: HD-audio ext core stream to start 146 */ 147 void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) 148 { 149 snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 150 AZX_PPLCCTL_RUN, AZX_PPLCCTL_RUN); 151 } 152 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); 153 154 /** 155 * snd_hdac_ext_link_stream_clear - stop a stream DMA 156 * @stream: HD-audio ext core stream to stop 157 */ 158 void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) 159 { 160 snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); 161 } 162 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); 163 164 /** 165 * snd_hdac_ext_link_stream_reset - reset a stream 166 * @stream: HD-audio ext core stream to reset 167 */ 168 void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) 169 { 170 unsigned char val; 171 int timeout; 172 173 snd_hdac_ext_link_stream_clear(stream); 174 175 snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 176 AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); 177 udelay(3); 178 timeout = 50; 179 do { 180 val = readl(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, 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(stream->pplc_addr + AZX_REG_PPLCCTL) & AZX_PPLCCTL_STRST; 194 if (!val) 195 break; 196 udelay(3); 197 } while (--timeout); 198 199 } 200 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_reset); 201 202 /** 203 * snd_hdac_ext_link_stream_setup - set up the SD for streaming 204 * @stream: HD-audio ext core stream to set up 205 * @fmt: stream format 206 */ 207 int snd_hdac_ext_link_stream_setup(struct hdac_ext_stream *stream, int fmt) 208 { 209 struct hdac_stream *hstream = &stream->hstream; 210 unsigned int val; 211 212 /* make sure the run bit is zero for SD */ 213 snd_hdac_ext_link_stream_clear(stream); 214 /* program the stream_tag */ 215 val = readl(stream->pplc_addr + AZX_REG_PPLCCTL); 216 val = (val & ~AZX_PPLCCTL_STRM_MASK) | 217 (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); 218 writel(val, stream->pplc_addr + AZX_REG_PPLCCTL); 219 220 /* program the stream format */ 221 writew(fmt, stream->pplc_addr + AZX_REG_PPLCFMT); 222 223 return 0; 224 } 225 EXPORT_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 */ 232 void 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 } 237 EXPORT_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 */ 244 void 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 } 249 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_clear_stream_id); 250 251 static struct hdac_ext_stream * 252 hdac_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 *stream = 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(stream, &bus->stream_list, list) { 265 struct hdac_ext_stream *hstream = container_of(stream, 266 struct hdac_ext_stream, 267 hstream); 268 if (stream->direction != substream->stream) 269 continue; 270 271 /* check if decoupled stream and not in use is available */ 272 if (hstream->decoupled && !hstream->link_locked) { 273 res = hstream; 274 break; 275 } 276 277 if (!hstream->link_locked) { 278 snd_hdac_ext_stream_decouple_locked(bus, hstream, true); 279 res = hstream; 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 291 static struct hdac_ext_stream * 292 hdac_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 *stream = 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(stream, &bus->stream_list, list) { 305 struct hdac_ext_stream *hstream = container_of(stream, 306 struct hdac_ext_stream, 307 hstream); 308 if (stream->direction != substream->stream) 309 continue; 310 311 if (!stream->opened) { 312 if (!hstream->decoupled) 313 snd_hdac_ext_stream_decouple_locked(bus, hstream, true); 314 res = hstream; 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 */ 345 struct 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 *hstream = NULL; 350 struct hdac_stream *stream = NULL; 351 352 switch (type) { 353 case HDAC_EXT_STREAM_TYPE_COUPLED: 354 stream = snd_hdac_stream_assign(bus, substream); 355 if (stream) 356 hstream = container_of(stream, 357 struct hdac_ext_stream, hstream); 358 return hstream; 359 360 case HDAC_EXT_STREAM_TYPE_HOST: 361 return hdac_ext_host_stream_assign(bus, substream); 362 363 case HDAC_EXT_STREAM_TYPE_LINK: 364 return hdac_ext_link_stream_assign(bus, substream); 365 366 default: 367 return NULL; 368 } 369 } 370 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); 371 372 /** 373 * snd_hdac_ext_stream_release - release the assigned stream 374 * @stream: HD-audio ext core stream to release 375 * @type: type of stream (coupled, host or link stream) 376 * 377 * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). 378 */ 379 void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) 380 { 381 struct hdac_bus *bus = stream->hstream.bus; 382 383 switch (type) { 384 case HDAC_EXT_STREAM_TYPE_COUPLED: 385 snd_hdac_stream_release(&stream->hstream); 386 break; 387 388 case HDAC_EXT_STREAM_TYPE_HOST: 389 spin_lock_irq(&bus->reg_lock); 390 if (stream->decoupled && !stream->link_locked) 391 snd_hdac_ext_stream_decouple_locked(bus, stream, false); 392 spin_unlock_irq(&bus->reg_lock); 393 snd_hdac_stream_release(&stream->hstream); 394 break; 395 396 case HDAC_EXT_STREAM_TYPE_LINK: 397 spin_lock_irq(&bus->reg_lock); 398 if (stream->decoupled && !stream->hstream.opened) 399 snd_hdac_ext_stream_decouple_locked(bus, stream, false); 400 stream->link_locked = 0; 401 stream->link_substream = NULL; 402 spin_unlock_irq(&bus->reg_lock); 403 break; 404 405 default: 406 dev_dbg(bus->dev, "Invalid type %d\n", type); 407 } 408 409 } 410 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); 411 412 /** 413 * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream 414 * @bus: HD-audio core bus 415 * @enable: flag to enable/disable SPIB 416 * @index: stream index for which SPIB need to be enabled 417 */ 418 void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, 419 bool enable, int index) 420 { 421 u32 mask = 0; 422 423 if (!bus->spbcap) { 424 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 425 return; 426 } 427 428 mask |= (1 << index); 429 430 if (enable) 431 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask); 432 else 433 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); 434 } 435 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); 436 437 /** 438 * snd_hdac_ext_stream_set_spib - sets the spib value of a stream 439 * @bus: HD-audio core bus 440 * @stream: hdac_ext_stream 441 * @value: spib value to set 442 */ 443 int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, 444 struct hdac_ext_stream *stream, u32 value) 445 { 446 447 if (!bus->spbcap) { 448 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 449 return -EINVAL; 450 } 451 452 writel(value, stream->spib_addr); 453 454 return 0; 455 } 456 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); 457 458 /** 459 * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream 460 * @bus: HD-audio core bus 461 * @stream: hdac_ext_stream 462 * 463 * Return maxfifo for the stream 464 */ 465 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, 466 struct hdac_ext_stream *stream) 467 { 468 469 if (!bus->spbcap) { 470 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 471 return -EINVAL; 472 } 473 474 return readl(stream->fifo_addr); 475 } 476 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); 477 478 479 /** 480 * snd_hdac_ext_stop_streams - stop all stream if running 481 * @bus: HD-audio core bus 482 */ 483 void snd_hdac_ext_stop_streams(struct hdac_bus *bus) 484 { 485 struct hdac_stream *stream; 486 487 if (bus->chip_init) { 488 list_for_each_entry(stream, &bus->stream_list, list) 489 snd_hdac_stream_stop(stream); 490 snd_hdac_bus_stop_chip(bus); 491 } 492 } 493 EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); 494 495 /** 496 * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream 497 * @bus: HD-audio core bus 498 * @enable: flag to enable/disable DRSM 499 * @index: stream index for which DRSM need to be enabled 500 */ 501 void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, 502 bool enable, int index) 503 { 504 u32 mask = 0; 505 506 if (!bus->drsmcap) { 507 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 508 return; 509 } 510 511 mask |= (1 << index); 512 513 if (enable) 514 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask); 515 else 516 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); 517 } 518 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); 519 520 /** 521 * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream 522 * @bus: HD-audio core bus 523 * @stream: hdac_ext_stream 524 * @value: dpib value to set 525 */ 526 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, 527 struct hdac_ext_stream *stream, u32 value) 528 { 529 530 if (!bus->drsmcap) { 531 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 532 return -EINVAL; 533 } 534 535 writel(value, stream->dpibr_addr); 536 537 return 0; 538 } 539 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); 540 541 /** 542 * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream 543 * @stream: hdac_ext_stream 544 * @value: lpib value to set 545 */ 546 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) 547 { 548 snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); 549 550 return 0; 551 } 552 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 553