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