hdac_ext_stream.c 14 KB

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