1 /* 2 * hdac-ext-stream.c - HD-audio extended stream operations. 3 * 4 * Copyright (C) 2015 Intel Corp 5 * Author: Jeeja KP <jeeja.kp@intel.com> 6 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; version 2 of the License. 11 * 12 * This program is distributed in the hope that it will be useful, but 13 * WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * General Public License for more details. 16 * 17 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 18 */ 19 20 #include <linux/delay.h> 21 #include <linux/slab.h> 22 #include <sound/pcm.h> 23 #include <sound/hda_register.h> 24 #include <sound/hdaudio_ext.h> 25 26 /** 27 * snd_hdac_ext_stream_init - initialize each stream (aka device) 28 * @ebus: HD-audio ext core bus 29 * @stream: HD-audio ext core stream object to initialize 30 * @idx: stream index number 31 * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) 32 * @tag: the tag id to assign 33 * 34 * initialize the stream, if ppcap is enabled then init those and then 35 * invoke hdac stream initialization routine 36 */ 37 void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus, 38 struct hdac_ext_stream *stream, 39 int idx, int direction, int tag) 40 { 41 struct hdac_bus *bus = &ebus->bus; 42 43 if (ebus->ppcap) { 44 stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE + 45 AZX_PPHC_INTERVAL * idx; 46 47 stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE + 48 AZX_PPLC_MULTI * ebus->num_streams + 49 AZX_PPLC_INTERVAL * idx; 50 } 51 52 if (ebus->spbcap) { 53 stream->spib_addr = ebus->spbcap + AZX_SPB_BASE + 54 AZX_SPB_INTERVAL * idx + 55 AZX_SPB_SPIB; 56 57 stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE + 58 AZX_SPB_INTERVAL * idx + 59 AZX_SPB_MAXFIFO; 60 } 61 62 if (ebus->drsmcap) 63 stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE + 64 AZX_DRSM_INTERVAL * idx; 65 66 stream->decoupled = false; 67 snd_hdac_stream_init(bus, &stream->hstream, idx, direction, tag); 68 } 69 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); 70 71 /** 72 * snd_hdac_ext_stream_init_all - create and initialize the stream objects 73 * for an extended hda bus 74 * @ebus: HD-audio ext core bus 75 * @start_idx: start index for streams 76 * @num_stream: number of streams to initialize 77 * @dir: direction of streams 78 */ 79 int snd_hdac_ext_stream_init_all(struct hdac_ext_bus *ebus, int start_idx, 80 int num_stream, int dir) 81 { 82 int stream_tag = 0; 83 int i, tag, idx = start_idx; 84 85 for (i = 0; i < num_stream; i++) { 86 struct hdac_ext_stream *stream = 87 kzalloc(sizeof(*stream), GFP_KERNEL); 88 if (!stream) 89 return -ENOMEM; 90 tag = ++stream_tag; 91 snd_hdac_ext_stream_init(ebus, stream, idx, dir, tag); 92 idx++; 93 } 94 95 return 0; 96 97 } 98 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); 99 100 /** 101 * snd_hdac_stream_free_all - free hdac extended stream objects 102 * 103 * @ebus: HD-audio ext core bus 104 */ 105 void snd_hdac_stream_free_all(struct hdac_ext_bus *ebus) 106 { 107 struct hdac_stream *s; 108 struct hdac_ext_stream *stream; 109 struct hdac_bus *bus = ebus_to_hbus(ebus); 110 111 while (!list_empty(&bus->stream_list)) { 112 s = list_first_entry(&bus->stream_list, struct hdac_stream, list); 113 stream = stream_to_hdac_ext_stream(s); 114 snd_hdac_ext_stream_decouple(ebus, stream, false); 115 list_del(&s->list); 116 kfree(stream); 117 } 118 } 119 EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); 120 121 /** 122 * snd_hdac_ext_stream_decouple - decouple the hdac stream 123 * @ebus: HD-audio ext core bus 124 * @stream: HD-audio ext core stream object to initialize 125 * @decouple: flag to decouple 126 */ 127 void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus, 128 struct hdac_ext_stream *stream, bool decouple) 129 { 130 struct hdac_stream *hstream = &stream->hstream; 131 struct hdac_bus *bus = &ebus->bus; 132 133 spin_lock_irq(&bus->reg_lock); 134 if (decouple) 135 snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, 136 AZX_PPCTL_PROCEN(hstream->index)); 137 else 138 snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 139 AZX_PPCTL_PROCEN(hstream->index), 0); 140 stream->decoupled = decouple; 141 spin_unlock_irq(&bus->reg_lock); 142 } 143 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); 144 145 /** 146 * snd_hdac_ext_linkstream_start - start a stream 147 * @stream: HD-audio ext core stream to start 148 */ 149 void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *stream) 150 { 151 snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, AZX_PPLCCTL_RUN); 152 } 153 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_start); 154 155 /** 156 * snd_hdac_ext_link_stream_clear - stop a stream DMA 157 * @stream: HD-audio ext core stream to stop 158 */ 159 void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *stream) 160 { 161 snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, AZX_PPLCCTL_RUN, 0); 162 } 163 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_clear); 164 165 /** 166 * snd_hdac_ext_link_stream_reset - reset a stream 167 * @stream: HD-audio ext core stream to reset 168 */ 169 void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *stream) 170 { 171 unsigned char val; 172 int timeout; 173 174 snd_hdac_ext_link_stream_clear(stream); 175 176 snd_hdac_updatel(stream->pplc_addr, AZX_REG_PPLCCTL, 0, 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, 0, (1 << stream)); 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_ext_bus *ebus, 253 struct snd_pcm_substream *substream) 254 { 255 struct hdac_ext_stream *res = NULL; 256 struct hdac_stream *stream = NULL; 257 struct hdac_bus *hbus = &ebus->bus; 258 259 if (!ebus->ppcap) { 260 dev_err(hbus->dev, "stream type not supported\n"); 261 return NULL; 262 } 263 264 list_for_each_entry(stream, &hbus->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(ebus, hstream, true); 279 res = hstream; 280 break; 281 } 282 } 283 if (res) { 284 spin_lock_irq(&hbus->reg_lock); 285 res->link_locked = 1; 286 res->link_substream = substream; 287 spin_unlock_irq(&hbus->reg_lock); 288 } 289 return res; 290 } 291 292 static struct hdac_ext_stream * 293 hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus, 294 struct snd_pcm_substream *substream) 295 { 296 struct hdac_ext_stream *res = NULL; 297 struct hdac_stream *stream = NULL; 298 struct hdac_bus *hbus = &ebus->bus; 299 300 if (!ebus->ppcap) { 301 dev_err(hbus->dev, "stream type not supported\n"); 302 return NULL; 303 } 304 305 list_for_each_entry(stream, &hbus->stream_list, list) { 306 struct hdac_ext_stream *hstream = container_of(stream, 307 struct hdac_ext_stream, 308 hstream); 309 if (stream->direction != substream->stream) 310 continue; 311 312 if (!stream->opened) { 313 if (!hstream->decoupled) 314 snd_hdac_ext_stream_decouple(ebus, hstream, true); 315 res = hstream; 316 break; 317 } 318 } 319 if (res) { 320 spin_lock_irq(&hbus->reg_lock); 321 res->hstream.opened = 1; 322 res->hstream.running = 0; 323 res->hstream.substream = substream; 324 spin_unlock_irq(&hbus->reg_lock); 325 } 326 327 return res; 328 } 329 330 /** 331 * snd_hdac_ext_stream_assign - assign a stream for the PCM 332 * @ebus: HD-audio ext core bus 333 * @substream: PCM substream to assign 334 * @type: type of stream (coupled, host or link stream) 335 * 336 * This assigns the stream based on the type (coupled/host/link), for the 337 * given PCM substream, assigns it and returns the stream object 338 * 339 * coupled: Looks for an unused stream 340 * host: Looks for an unused decoupled host stream 341 * link: Looks for an unused decoupled link stream 342 * 343 * If no stream is free, returns NULL. The function tries to keep using 344 * the same stream object when it's used beforehand. when a stream is 345 * decoupled, it becomes a host stream and link stream. 346 */ 347 struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_ext_bus *ebus, 348 struct snd_pcm_substream *substream, 349 int type) 350 { 351 struct hdac_ext_stream *hstream = NULL; 352 struct hdac_stream *stream = NULL; 353 struct hdac_bus *hbus = &ebus->bus; 354 355 switch (type) { 356 case HDAC_EXT_STREAM_TYPE_COUPLED: 357 stream = snd_hdac_stream_assign(hbus, substream); 358 if (stream) 359 hstream = container_of(stream, 360 struct hdac_ext_stream, hstream); 361 return hstream; 362 363 case HDAC_EXT_STREAM_TYPE_HOST: 364 return hdac_ext_host_stream_assign(ebus, substream); 365 366 case HDAC_EXT_STREAM_TYPE_LINK: 367 return hdac_ext_link_stream_assign(ebus, substream); 368 369 default: 370 return NULL; 371 } 372 } 373 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); 374 375 /** 376 * snd_hdac_ext_stream_release - release the assigned stream 377 * @stream: HD-audio ext core stream to release 378 * @type: type of stream (coupled, host or link stream) 379 * 380 * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). 381 */ 382 void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type) 383 { 384 struct hdac_bus *bus = stream->hstream.bus; 385 struct hdac_ext_bus *ebus = hbus_to_ebus(bus); 386 387 switch (type) { 388 case HDAC_EXT_STREAM_TYPE_COUPLED: 389 snd_hdac_stream_release(&stream->hstream); 390 break; 391 392 case HDAC_EXT_STREAM_TYPE_HOST: 393 if (stream->decoupled && !stream->link_locked) 394 snd_hdac_ext_stream_decouple(ebus, stream, false); 395 snd_hdac_stream_release(&stream->hstream); 396 break; 397 398 case HDAC_EXT_STREAM_TYPE_LINK: 399 if (stream->decoupled && !stream->hstream.opened) 400 snd_hdac_ext_stream_decouple(ebus, stream, false); 401 spin_lock_irq(&bus->reg_lock); 402 stream->link_locked = 0; 403 stream->link_substream = NULL; 404 spin_unlock_irq(&bus->reg_lock); 405 break; 406 407 default: 408 dev_dbg(bus->dev, "Invalid type %d\n", type); 409 } 410 411 } 412 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); 413 414 /** 415 * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream 416 * @ebus: HD-audio ext core bus 417 * @enable: flag to enable/disable SPIB 418 * @index: stream index for which SPIB need to be enabled 419 */ 420 void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus, 421 bool enable, int index) 422 { 423 u32 mask = 0; 424 u32 register_mask = 0; 425 struct hdac_bus *bus = &ebus->bus; 426 427 if (!ebus->spbcap) { 428 dev_err(bus->dev, "Address of SPB capability is NULL"); 429 return; 430 } 431 432 mask |= (1 << index); 433 434 register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL); 435 436 mask |= register_mask; 437 438 if (enable) 439 snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask); 440 else 441 snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); 442 } 443 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); 444 445 /** 446 * snd_hdac_ext_stream_set_spib - sets the spib value of a stream 447 * @ebus: HD-audio ext core bus 448 * @stream: hdac_ext_stream 449 * @value: spib value to set 450 */ 451 int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus, 452 struct hdac_ext_stream *stream, u32 value) 453 { 454 struct hdac_bus *bus = &ebus->bus; 455 456 if (!ebus->spbcap) { 457 dev_err(bus->dev, "Address of SPB capability is NULL"); 458 return -EINVAL; 459 } 460 461 writel(value, stream->spib_addr); 462 463 return 0; 464 } 465 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); 466 467 /** 468 * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream 469 * @ebus: HD-audio ext core bus 470 * @stream: hdac_ext_stream 471 * 472 * Return maxfifo for the stream 473 */ 474 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus, 475 struct hdac_ext_stream *stream) 476 { 477 struct hdac_bus *bus = &ebus->bus; 478 479 if (!ebus->spbcap) { 480 dev_err(bus->dev, "Address of SPB capability is NULL"); 481 return -EINVAL; 482 } 483 484 return readl(stream->fifo_addr); 485 } 486 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); 487 488 489 /** 490 * snd_hdac_ext_stop_streams - stop all stream if running 491 * @ebus: HD-audio ext core bus 492 */ 493 void snd_hdac_ext_stop_streams(struct hdac_ext_bus *ebus) 494 { 495 struct hdac_bus *bus = ebus_to_hbus(ebus); 496 struct hdac_stream *stream; 497 498 if (bus->chip_init) { 499 list_for_each_entry(stream, &bus->stream_list, list) 500 snd_hdac_stream_stop(stream); 501 snd_hdac_bus_stop_chip(bus); 502 } 503 } 504 EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); 505 506 /** 507 * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream 508 * @ebus: HD-audio ext core bus 509 * @enable: flag to enable/disable DRSM 510 * @index: stream index for which DRSM need to be enabled 511 */ 512 void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus, 513 bool enable, int index) 514 { 515 u32 mask = 0; 516 u32 register_mask = 0; 517 struct hdac_bus *bus = &ebus->bus; 518 519 if (!ebus->drsmcap) { 520 dev_err(bus->dev, "Address of DRSM capability is NULL"); 521 return; 522 } 523 524 mask |= (1 << index); 525 526 register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL); 527 528 mask |= register_mask; 529 530 if (enable) 531 snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask); 532 else 533 snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); 534 } 535 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); 536 537 /** 538 * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream 539 * @ebus: HD-audio ext core bus 540 * @stream: hdac_ext_stream 541 * @value: dpib value to set 542 */ 543 int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus, 544 struct hdac_ext_stream *stream, u32 value) 545 { 546 struct hdac_bus *bus = &ebus->bus; 547 548 if (!ebus->drsmcap) { 549 dev_err(bus->dev, "Address of DRSM capability is NULL"); 550 return -EINVAL; 551 } 552 553 writel(value, stream->dpibr_addr); 554 555 return 0; 556 } 557 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); 558 559 /** 560 * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream 561 * @ebus: HD-audio ext core bus 562 * @stream: hdac_ext_stream 563 * @value: lpib value to set 564 */ 565 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) 566 { 567 snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); 568 569 return 0; 570 } 571 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 572