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