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