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