1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * hdac-ext-stream.c - HD-audio extended stream operations. 4 * 5 * Copyright (C) 2015 Intel Corp 6 * Author: Jeeja KP <jeeja.kp@intel.com> 7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 * 9 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 10 */ 11 12 #include <linux/delay.h> 13 #include <linux/slab.h> 14 #include <sound/pcm.h> 15 #include <sound/hda_register.h> 16 #include <sound/hdaudio_ext.h> 17 18 /** 19 * snd_hdac_ext_stream_init - initialize each stream (aka device) 20 * @bus: HD-audio core bus 21 * @hext_stream: HD-audio ext core stream object to initialize 22 * @idx: stream index number 23 * @direction: stream direction (SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE) 24 * @tag: the tag id to assign 25 * 26 * initialize the stream, if ppcap is enabled then init those and then 27 * invoke hdac stream initialization routine 28 */ 29 void snd_hdac_ext_stream_init(struct hdac_bus *bus, 30 struct hdac_ext_stream *hext_stream, 31 int idx, int direction, int tag) 32 { 33 if (bus->ppcap) { 34 hext_stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE + 35 AZX_PPHC_INTERVAL * idx; 36 37 hext_stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE + 38 AZX_PPLC_MULTI * bus->num_streams + 39 AZX_PPLC_INTERVAL * idx; 40 } 41 42 if (bus->spbcap) { 43 hext_stream->spib_addr = bus->spbcap + AZX_SPB_BASE + 44 AZX_SPB_INTERVAL * idx + 45 AZX_SPB_SPIB; 46 47 hext_stream->fifo_addr = bus->spbcap + AZX_SPB_BASE + 48 AZX_SPB_INTERVAL * idx + 49 AZX_SPB_MAXFIFO; 50 } 51 52 if (bus->drsmcap) 53 hext_stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE + 54 AZX_DRSM_INTERVAL * idx; 55 56 hext_stream->decoupled = false; 57 snd_hdac_stream_init(bus, &hext_stream->hstream, idx, direction, tag); 58 } 59 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init); 60 61 /** 62 * snd_hdac_ext_stream_init_all - create and initialize the stream objects 63 * for an extended hda bus 64 * @bus: HD-audio core bus 65 * @start_idx: start index for streams 66 * @num_stream: number of streams to initialize 67 * @dir: direction of streams 68 */ 69 int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, 70 int num_stream, int dir) 71 { 72 int stream_tag = 0; 73 int i, tag, idx = start_idx; 74 75 for (i = 0; i < num_stream; i++) { 76 struct hdac_ext_stream *hext_stream = 77 kzalloc(sizeof(*hext_stream), GFP_KERNEL); 78 if (!hext_stream) 79 return -ENOMEM; 80 tag = ++stream_tag; 81 snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); 82 idx++; 83 } 84 85 return 0; 86 87 } 88 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); 89 90 /** 91 * snd_hdac_stream_free_all - free hdac extended stream objects 92 * 93 * @bus: HD-audio core bus 94 */ 95 void snd_hdac_stream_free_all(struct hdac_bus *bus) 96 { 97 struct hdac_stream *s, *_s; 98 struct hdac_ext_stream *hext_stream; 99 100 list_for_each_entry_safe(s, _s, &bus->stream_list, list) { 101 hext_stream = stream_to_hdac_ext_stream(s); 102 snd_hdac_ext_stream_decouple(bus, hext_stream, false); 103 list_del(&s->list); 104 kfree(hext_stream); 105 } 106 } 107 EXPORT_SYMBOL_GPL(snd_hdac_stream_free_all); 108 109 void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, 110 struct hdac_ext_stream *hext_stream, 111 bool decouple) 112 { 113 struct hdac_stream *hstream = &hext_stream->hstream; 114 u32 val; 115 int mask = AZX_PPCTL_PROCEN(hstream->index); 116 117 val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask; 118 119 if (decouple && !val) 120 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask); 121 else if (!decouple && val) 122 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); 123 124 hext_stream->decoupled = decouple; 125 } 126 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); 127 128 /** 129 * snd_hdac_ext_stream_decouple - decouple the hdac stream 130 * @bus: HD-audio core bus 131 * @hext_stream: HD-audio ext core stream object to initialize 132 * @decouple: flag to decouple 133 */ 134 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, 135 struct hdac_ext_stream *hext_stream, bool decouple) 136 { 137 spin_lock_irq(&bus->reg_lock); 138 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple); 139 spin_unlock_irq(&bus->reg_lock); 140 } 141 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); 142 143 /** 144 * snd_hdac_ext_link_stream_start - start a stream 145 * @hext_stream: HD-audio ext core stream to start 146 */ 147 void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream) 148 { 149 snd_hdac_updatel(hext_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 * @hext_stream: HD-audio ext core stream to stop 157 */ 158 void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream) 159 { 160 snd_hdac_updatel(hext_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 * @hext_stream: HD-audio ext core stream to reset 167 */ 168 void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream) 169 { 170 unsigned char val; 171 int timeout; 172 173 snd_hdac_ext_link_stream_clear(hext_stream); 174 175 snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, 176 AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); 177 udelay(3); 178 timeout = 50; 179 do { 180 val = readl(hext_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, hext_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(hext_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 * @hext_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 *hext_stream, int fmt) 208 { 209 struct hdac_stream *hstream = &hext_stream->hstream; 210 unsigned int val; 211 212 /* make sure the run bit is zero for SD */ 213 snd_hdac_ext_link_stream_clear(hext_stream); 214 /* program the stream_tag */ 215 val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL); 216 val = (val & ~AZX_PPLCCTL_STRM_MASK) | 217 (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); 218 writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); 219 220 /* program the stream format */ 221 writew(fmt, hext_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 *hstream = NULL; 257 258 if (!bus->ppcap) { 259 dev_err(bus->dev, "stream type not supported\n"); 260 return NULL; 261 } 262 263 spin_lock_irq(&bus->reg_lock); 264 list_for_each_entry(hstream, &bus->stream_list, list) { 265 struct hdac_ext_stream *hext_stream = container_of(hstream, 266 struct hdac_ext_stream, 267 hstream); 268 if (hstream->direction != substream->stream) 269 continue; 270 271 /* check if decoupled stream and not in use is available */ 272 if (hext_stream->decoupled && !hext_stream->link_locked) { 273 res = hext_stream; 274 break; 275 } 276 277 if (!hext_stream->link_locked) { 278 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); 279 res = hext_stream; 280 break; 281 } 282 } 283 if (res) { 284 res->link_locked = 1; 285 res->link_substream = substream; 286 } 287 spin_unlock_irq(&bus->reg_lock); 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 *hstream = NULL; 297 298 if (!bus->ppcap) { 299 dev_err(bus->dev, "stream type not supported\n"); 300 return NULL; 301 } 302 303 spin_lock_irq(&bus->reg_lock); 304 list_for_each_entry(hstream, &bus->stream_list, list) { 305 struct hdac_ext_stream *hext_stream = container_of(hstream, 306 struct hdac_ext_stream, 307 hstream); 308 if (hstream->direction != substream->stream) 309 continue; 310 311 if (!hstream->opened) { 312 if (!hext_stream->decoupled) 313 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, true); 314 res = hext_stream; 315 break; 316 } 317 } 318 if (res) { 319 res->hstream.opened = 1; 320 res->hstream.running = 0; 321 res->hstream.substream = substream; 322 } 323 spin_unlock_irq(&bus->reg_lock); 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 *hext_stream = NULL; 350 struct hdac_stream *hstream = NULL; 351 352 switch (type) { 353 case HDAC_EXT_STREAM_TYPE_COUPLED: 354 hstream = snd_hdac_stream_assign(bus, substream); 355 if (hstream) 356 hext_stream = container_of(hstream, 357 struct hdac_ext_stream, 358 hstream); 359 return hext_stream; 360 361 case HDAC_EXT_STREAM_TYPE_HOST: 362 return hdac_ext_host_stream_assign(bus, substream); 363 364 case HDAC_EXT_STREAM_TYPE_LINK: 365 return hdac_ext_link_stream_assign(bus, substream); 366 367 default: 368 return NULL; 369 } 370 } 371 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); 372 373 /** 374 * snd_hdac_ext_stream_release - release the assigned stream 375 * @hext_stream: HD-audio ext core stream to release 376 * @type: type of stream (coupled, host or link stream) 377 * 378 * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). 379 */ 380 void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) 381 { 382 struct hdac_bus *bus = hext_stream->hstream.bus; 383 384 switch (type) { 385 case HDAC_EXT_STREAM_TYPE_COUPLED: 386 snd_hdac_stream_release(&hext_stream->hstream); 387 break; 388 389 case HDAC_EXT_STREAM_TYPE_HOST: 390 spin_lock_irq(&bus->reg_lock); 391 if (hext_stream->decoupled && !hext_stream->link_locked) 392 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 393 spin_unlock_irq(&bus->reg_lock); 394 snd_hdac_stream_release(&hext_stream->hstream); 395 break; 396 397 case HDAC_EXT_STREAM_TYPE_LINK: 398 spin_lock_irq(&bus->reg_lock); 399 if (hext_stream->decoupled && !hext_stream->hstream.opened) 400 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 401 hext_stream->link_locked = 0; 402 hext_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 * @bus: HD-audio 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_bus *bus, 420 bool enable, int index) 421 { 422 u32 mask = 0; 423 424 if (!bus->spbcap) { 425 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 426 return; 427 } 428 429 mask |= (1 << index); 430 431 if (enable) 432 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 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 * @hext_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 *hext_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, hext_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 * @hext_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 *hext_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(hext_stream->fifo_addr); 476 } 477 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); 478 479 /** 480 * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream 481 * @bus: HD-audio core bus 482 * @enable: flag to enable/disable DRSM 483 * @index: stream index for which DRSM need to be enabled 484 */ 485 void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, 486 bool enable, int index) 487 { 488 u32 mask = 0; 489 490 if (!bus->drsmcap) { 491 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 492 return; 493 } 494 495 mask |= (1 << index); 496 497 if (enable) 498 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask); 499 else 500 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); 501 } 502 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); 503 504 /** 505 * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream 506 * @bus: HD-audio core bus 507 * @hext_stream: hdac_ext_stream 508 * @value: dpib value to set 509 */ 510 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, 511 struct hdac_ext_stream *hext_stream, u32 value) 512 { 513 514 if (!bus->drsmcap) { 515 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 516 return -EINVAL; 517 } 518 519 writel(value, hext_stream->dpibr_addr); 520 521 return 0; 522 } 523 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); 524 525 /** 526 * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream 527 * @hext_stream: hdac_ext_stream 528 * @value: lpib value to set 529 */ 530 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value) 531 { 532 snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value); 533 534 return 0; 535 } 536 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 537