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