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 static 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 60 /** 61 * snd_hdac_ext_stream_init_all - create and initialize the stream objects 62 * for an extended hda bus 63 * @bus: HD-audio core bus 64 * @start_idx: start index for streams 65 * @num_stream: number of streams to initialize 66 * @dir: direction of streams 67 */ 68 int snd_hdac_ext_stream_init_all(struct hdac_bus *bus, int start_idx, 69 int num_stream, int dir) 70 { 71 int stream_tag = 0; 72 int i, tag, idx = start_idx; 73 74 for (i = 0; i < num_stream; i++) { 75 struct hdac_ext_stream *hext_stream = 76 kzalloc(sizeof(*hext_stream), GFP_KERNEL); 77 if (!hext_stream) 78 return -ENOMEM; 79 tag = ++stream_tag; 80 snd_hdac_ext_stream_init(bus, hext_stream, idx, dir, tag); 81 idx++; 82 } 83 84 return 0; 85 86 } 87 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_init_all); 88 89 /** 90 * snd_hdac_ext_stream_free_all - free hdac extended stream objects 91 * 92 * @bus: HD-audio core bus 93 */ 94 void snd_hdac_ext_stream_free_all(struct hdac_bus *bus) 95 { 96 struct hdac_stream *s, *_s; 97 struct hdac_ext_stream *hext_stream; 98 99 list_for_each_entry_safe(s, _s, &bus->stream_list, list) { 100 hext_stream = stream_to_hdac_ext_stream(s); 101 snd_hdac_ext_stream_decouple(bus, hext_stream, false); 102 list_del(&s->list); 103 kfree(hext_stream); 104 } 105 } 106 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_free_all); 107 108 void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, 109 struct hdac_ext_stream *hext_stream, 110 bool decouple) 111 { 112 struct hdac_stream *hstream = &hext_stream->hstream; 113 u32 val; 114 int mask = AZX_PPCTL_PROCEN(hstream->index); 115 116 val = readw(bus->ppcap + AZX_REG_PP_PPCTL) & mask; 117 118 if (decouple && !val) 119 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, mask); 120 else if (!decouple && val) 121 snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, mask, 0); 122 123 hext_stream->decoupled = decouple; 124 } 125 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple_locked); 126 127 /** 128 * snd_hdac_ext_stream_decouple - decouple the hdac stream 129 * @bus: HD-audio core bus 130 * @hext_stream: HD-audio ext core stream object to initialize 131 * @decouple: flag to decouple 132 */ 133 void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, 134 struct hdac_ext_stream *hext_stream, bool decouple) 135 { 136 spin_lock_irq(&bus->reg_lock); 137 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, decouple); 138 spin_unlock_irq(&bus->reg_lock); 139 } 140 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_decouple); 141 142 /** 143 * snd_hdac_ext_link_stream_start - start a stream 144 * @hext_stream: HD-audio ext core stream to start 145 */ 146 void snd_hdac_ext_link_stream_start(struct hdac_ext_stream *hext_stream) 147 { 148 snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, 149 AZX_PPLCCTL_RUN, 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 * @hext_stream: HD-audio ext core stream to stop 156 */ 157 void snd_hdac_ext_link_stream_clear(struct hdac_ext_stream *hext_stream) 158 { 159 snd_hdac_updatel(hext_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 * @hext_stream: HD-audio ext core stream to reset 166 */ 167 void snd_hdac_ext_link_stream_reset(struct hdac_ext_stream *hext_stream) 168 { 169 unsigned char val; 170 int timeout; 171 172 snd_hdac_ext_link_stream_clear(hext_stream); 173 174 snd_hdac_updatel(hext_stream->pplc_addr, AZX_REG_PPLCCTL, 175 AZX_PPLCCTL_STRST, AZX_PPLCCTL_STRST); 176 udelay(3); 177 timeout = 50; 178 do { 179 val = readl(hext_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, hext_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(hext_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 * @hext_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 *hext_stream, int fmt) 207 { 208 struct hdac_stream *hstream = &hext_stream->hstream; 209 unsigned int val; 210 211 /* make sure the run bit is zero for SD */ 212 snd_hdac_ext_link_stream_clear(hext_stream); 213 /* program the stream_tag */ 214 val = readl(hext_stream->pplc_addr + AZX_REG_PPLCCTL); 215 val = (val & ~AZX_PPLCCTL_STRM_MASK) | 216 (hstream->stream_tag << AZX_PPLCCTL_STRM_SHIFT); 217 writel(val, hext_stream->pplc_addr + AZX_REG_PPLCCTL); 218 219 /* program the stream format */ 220 writew(fmt, hext_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, (1 << stream), 0); 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_bus *bus, 252 struct snd_pcm_substream *substream) 253 { 254 struct hdac_ext_stream *res = NULL; 255 struct hdac_stream *hstream = NULL; 256 257 if (!bus->ppcap) { 258 dev_err(bus->dev, "stream type not supported\n"); 259 return NULL; 260 } 261 262 spin_lock_irq(&bus->reg_lock); 263 list_for_each_entry(hstream, &bus->stream_list, list) { 264 struct hdac_ext_stream *hext_stream = container_of(hstream, 265 struct hdac_ext_stream, 266 hstream); 267 if (hstream->direction != substream->stream) 268 continue; 269 270 /* check if link stream is available */ 271 if (!hext_stream->link_locked) { 272 res = hext_stream; 273 break; 274 } 275 276 } 277 if (res) { 278 snd_hdac_ext_stream_decouple_locked(bus, res, true); 279 res->link_locked = 1; 280 res->link_substream = substream; 281 } 282 spin_unlock_irq(&bus->reg_lock); 283 return res; 284 } 285 286 static struct hdac_ext_stream * 287 hdac_ext_host_stream_assign(struct hdac_bus *bus, 288 struct snd_pcm_substream *substream) 289 { 290 struct hdac_ext_stream *res = NULL; 291 struct hdac_stream *hstream = NULL; 292 293 if (!bus->ppcap) { 294 dev_err(bus->dev, "stream type not supported\n"); 295 return NULL; 296 } 297 298 spin_lock_irq(&bus->reg_lock); 299 list_for_each_entry(hstream, &bus->stream_list, list) { 300 struct hdac_ext_stream *hext_stream = container_of(hstream, 301 struct hdac_ext_stream, 302 hstream); 303 if (hstream->direction != substream->stream) 304 continue; 305 306 if (!hstream->opened) { 307 res = hext_stream; 308 break; 309 } 310 } 311 if (res) { 312 snd_hdac_ext_stream_decouple_locked(bus, res, true); 313 res->hstream.opened = 1; 314 res->hstream.running = 0; 315 res->hstream.substream = substream; 316 } 317 spin_unlock_irq(&bus->reg_lock); 318 319 return res; 320 } 321 322 /** 323 * snd_hdac_ext_stream_assign - assign a stream for the PCM 324 * @bus: HD-audio core bus 325 * @substream: PCM substream to assign 326 * @type: type of stream (coupled, host or link stream) 327 * 328 * This assigns the stream based on the type (coupled/host/link), for the 329 * given PCM substream, assigns it and returns the stream object 330 * 331 * coupled: Looks for an unused stream 332 * host: Looks for an unused decoupled host stream 333 * link: Looks for an unused decoupled link stream 334 * 335 * If no stream is free, returns NULL. The function tries to keep using 336 * the same stream object when it's used beforehand. when a stream is 337 * decoupled, it becomes a host stream and link stream. 338 */ 339 struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, 340 struct snd_pcm_substream *substream, 341 int type) 342 { 343 struct hdac_ext_stream *hext_stream = NULL; 344 struct hdac_stream *hstream = NULL; 345 346 switch (type) { 347 case HDAC_EXT_STREAM_TYPE_COUPLED: 348 hstream = snd_hdac_stream_assign(bus, substream); 349 if (hstream) 350 hext_stream = container_of(hstream, 351 struct hdac_ext_stream, 352 hstream); 353 return hext_stream; 354 355 case HDAC_EXT_STREAM_TYPE_HOST: 356 return hdac_ext_host_stream_assign(bus, substream); 357 358 case HDAC_EXT_STREAM_TYPE_LINK: 359 return hdac_ext_link_stream_assign(bus, substream); 360 361 default: 362 return NULL; 363 } 364 } 365 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_assign); 366 367 /** 368 * snd_hdac_ext_stream_release - release the assigned stream 369 * @hext_stream: HD-audio ext core stream to release 370 * @type: type of stream (coupled, host or link stream) 371 * 372 * Release the stream that has been assigned by snd_hdac_ext_stream_assign(). 373 */ 374 void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) 375 { 376 struct hdac_bus *bus = hext_stream->hstream.bus; 377 378 switch (type) { 379 case HDAC_EXT_STREAM_TYPE_COUPLED: 380 snd_hdac_stream_release(&hext_stream->hstream); 381 break; 382 383 case HDAC_EXT_STREAM_TYPE_HOST: 384 spin_lock_irq(&bus->reg_lock); 385 /* couple link only if not in use */ 386 if (!hext_stream->link_locked) 387 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 388 snd_hdac_stream_release_locked(&hext_stream->hstream); 389 spin_unlock_irq(&bus->reg_lock); 390 break; 391 392 case HDAC_EXT_STREAM_TYPE_LINK: 393 spin_lock_irq(&bus->reg_lock); 394 /* couple host only if not in use */ 395 if (!hext_stream->hstream.opened) 396 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 397 hext_stream->link_locked = 0; 398 hext_stream->link_substream = NULL; 399 spin_unlock_irq(&bus->reg_lock); 400 break; 401 402 default: 403 dev_dbg(bus->dev, "Invalid type %d\n", type); 404 } 405 406 } 407 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); 408 409 /** 410 * snd_hdac_ext_stream_spbcap_enable - enable SPIB for a stream 411 * @bus: HD-audio core bus 412 * @enable: flag to enable/disable SPIB 413 * @index: stream index for which SPIB need to be enabled 414 */ 415 void snd_hdac_ext_stream_spbcap_enable(struct hdac_bus *bus, 416 bool enable, int index) 417 { 418 u32 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 if (enable) 428 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, mask); 429 else 430 snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0); 431 } 432 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable); 433 434 /** 435 * snd_hdac_ext_stream_set_spib - sets the spib value of a stream 436 * @bus: HD-audio core bus 437 * @hext_stream: hdac_ext_stream 438 * @value: spib value to set 439 */ 440 int snd_hdac_ext_stream_set_spib(struct hdac_bus *bus, 441 struct hdac_ext_stream *hext_stream, u32 value) 442 { 443 444 if (!bus->spbcap) { 445 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 446 return -EINVAL; 447 } 448 449 writel(value, hext_stream->spib_addr); 450 451 return 0; 452 } 453 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_spib); 454 455 /** 456 * snd_hdac_ext_stream_get_spbmaxfifo - gets the spib value of a stream 457 * @bus: HD-audio core bus 458 * @hext_stream: hdac_ext_stream 459 * 460 * Return maxfifo for the stream 461 */ 462 int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_bus *bus, 463 struct hdac_ext_stream *hext_stream) 464 { 465 466 if (!bus->spbcap) { 467 dev_err(bus->dev, "Address of SPB capability is NULL\n"); 468 return -EINVAL; 469 } 470 471 return readl(hext_stream->fifo_addr); 472 } 473 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_get_spbmaxfifo); 474 475 /** 476 * snd_hdac_ext_stream_drsm_enable - enable DMA resume for a stream 477 * @bus: HD-audio core bus 478 * @enable: flag to enable/disable DRSM 479 * @index: stream index for which DRSM need to be enabled 480 */ 481 void snd_hdac_ext_stream_drsm_enable(struct hdac_bus *bus, 482 bool enable, int index) 483 { 484 u32 mask = 0; 485 486 if (!bus->drsmcap) { 487 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 488 return; 489 } 490 491 mask |= (1 << index); 492 493 if (enable) 494 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, mask); 495 else 496 snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0); 497 } 498 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable); 499 500 /** 501 * snd_hdac_ext_stream_set_dpibr - sets the dpibr value of a stream 502 * @bus: HD-audio core bus 503 * @hext_stream: hdac_ext_stream 504 * @value: dpib value to set 505 */ 506 int snd_hdac_ext_stream_set_dpibr(struct hdac_bus *bus, 507 struct hdac_ext_stream *hext_stream, u32 value) 508 { 509 510 if (!bus->drsmcap) { 511 dev_err(bus->dev, "Address of DRSM capability is NULL\n"); 512 return -EINVAL; 513 } 514 515 writel(value, hext_stream->dpibr_addr); 516 517 return 0; 518 } 519 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_dpibr); 520 521 /** 522 * snd_hdac_ext_stream_set_lpib - sets the lpib value of a stream 523 * @hext_stream: hdac_ext_stream 524 * @value: lpib value to set 525 */ 526 int snd_hdac_ext_stream_set_lpib(struct hdac_ext_stream *hext_stream, u32 value) 527 { 528 snd_hdac_stream_writel(&hext_stream->hstream, SD_LPIB, value); 529 530 return 0; 531 } 532 EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_set_lpib); 533