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, 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 list_for_each_entry(stream, &bus->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(bus, hstream, true); 278 res = hstream; 279 break; 280 } 281 } 282 if (res) { 283 spin_lock_irq(&bus->reg_lock); 284 res->link_locked = 1; 285 res->link_substream = substream; 286 spin_unlock_irq(&bus->reg_lock); 287 } 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 list_for_each_entry(stream, &bus->stream_list, list) { 304 struct hdac_ext_stream *hstream = container_of(stream, 305 struct hdac_ext_stream, 306 hstream); 307 if (stream->direction != substream->stream) 308 continue; 309 310 if (!stream->opened) { 311 if (!hstream->decoupled) 312 snd_hdac_ext_stream_decouple(bus, hstream, true); 313 res = hstream; 314 break; 315 } 316 } 317 if (res) { 318 spin_lock_irq(&bus->reg_lock); 319 res->hstream.opened = 1; 320 res->hstream.running = 0; 321 res->hstream.substream = substream; 322 spin_unlock_irq(&bus->reg_lock); 323 } 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 if (stream->decoupled && !stream->link_locked) 390 snd_hdac_ext_stream_decouple(bus, stream, false); 391 snd_hdac_stream_release(&stream->hstream); 392 break; 393 394 case HDAC_EXT_STREAM_TYPE_LINK: 395 if (stream->decoupled && !stream->hstream.opened) 396 snd_hdac_ext_stream_decouple(bus, stream, false); 397 spin_lock_irq(&bus->reg_lock); 398 stream->link_locked = 0; 399 stream->link_substream = NULL; 400 spin_unlock_irq(&bus->reg_lock); 401 break; 402 403 default: 404 dev_dbg(bus->dev, "Invalid type %d\n", type); 405 } 406 407 } 408 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); 409 410 /** 411 * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream 412 * @bus: HD-audio core bus 413 * @enable: flag to enable/disable SPIB 414 * @index: stream index for which SPIB need to be enabled 415 */ 416 void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, 417 bool enable, int index) 418 { 419 u32 mask = 0; 420 421 if (!bus->spbcap) { 422 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 423 return; 424 } 425 426 mask |= (1 << index); 427 428 if (enable) 429 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask); 430 else 431 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); 432 } 433 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); 434 435 /** 436 * snd_hdac_ext_stream_set_spib - sets the spib value of a stream 437 * @bus: HD-audio core bus 438 * @stream: hdac_ext_stream 439 * @value: spib value to set 440 */ 441 int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, 442 struct hdac_ext_stream *stream, u32 value) 443 { 444 445 if (!bus->spbcap) { 446 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 447 return -EINVAL; 448 } 449 450 writel(value, stream->spib_addr); 451 452 return 0; 453 } 454 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); 455 456 /** 457 * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream 458 * @bus: HD-audio core bus 459 * @stream: hdac_ext_stream 460 * 461 * Return maxfifo for the stream 462 */ 463 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, 464 struct hdac_ext_stream *stream) 465 { 466 467 if (!bus->spbcap) { 468 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 469 return -EINVAL; 470 } 471 472 return readl(stream->fifo_addr); 473 } 474 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); 475 476 477 /** 478 * snd_hdac_ext_stop_streams - stop all stream if running 479 * @bus: HD-audio core bus 480 */ 481 void snd_hdac_ext_stop_streams(struct hdac_bus *bus) 482 { 483 struct hdac_stream *stream; 484 485 if (bus->chip_init) { 486 list_for_each_entry(stream, &bus->stream_list, list) 487 snd_hdac_stream_stop(stream); 488 snd_hdac_bus_stop_chip(bus); 489 } 490 } 491 EXPORT_SYMBOL_GPL(snd_hdac_ext_stop_streams); 492 493 /** 494 * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream 495 * @bus: HD-audio core bus 496 * @enable: flag to enable/disable DRSM 497 * @index: stream index for which DRSM need to be enabled 498 */ 499 void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, 500 bool enable, int index) 501 { 502 u32 mask = 0; 503 504 if (!bus->drsmcap) { 505 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 506 return; 507 } 508 509 mask |= (1 << index); 510 511 if (enable) 512 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask); 513 else 514 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); 515 } 516 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); 517 518 /** 519 * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream 520 * @bus: HD-audio core bus 521 * @stream: hdac_ext_stream 522 * @value: dpib value to set 523 */ 524 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, 525 struct hdac_ext_stream *stream, u32 value) 526 { 527 528 if (!bus->drsmcap) { 529 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 530 return -EINVAL; 531 } 532 533 writel(value, stream->dpibr_addr); 534 535 return 0; 536 } 537 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); 538 539 /** 540 * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream 541 * @bus: HD-audio core bus 542 * @stream: hdac_ext_stream 543 * @value: lpib value to set 544 */ 545 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *stream, u32 value) 546 { 547 snd_hdac_stream_writel(&stream->hstream, SD_LPIB, value); 548 549 return 0; 550 } 551 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 552