hda-mlink.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
  2. //
  3. // This file is provided under a dual BSD/GPLv2 license. When using or
  4. // redistributing this file, you may do so under either license.
  5. //
  6. // Copyright(c) 2022 Intel Corporation
  7. //
  8. /*
  9. * Management of HDaudio multi-link (capabilities, power, coupling)
  10. */
  11. #include <sound/hdaudio_ext.h>
  12. #include <sound/hda_register.h>
  13. #include <sound/hda-mlink.h>
  14. #include <linux/bitfield.h>
  15. #include <linux/module.h>
  16. #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_MLINK)
  17. /* worst-case number of sublinks is used for sublink refcount array allocation only */
  18. #define HDAML_MAX_SUBLINKS (AZX_ML_LCTL_CPA_SHIFT - AZX_ML_LCTL_SPA_SHIFT)
  19. /**
  20. * struct hdac_ext2_link - HDAudio extended+alternate link
  21. *
  22. * @hext_link: hdac_ext_link
  23. * @alt: flag set for alternate extended links
  24. * @intc: boolean for interrupt capable
  25. * @ofls: boolean for offload support
  26. * @lss: boolean for link synchronization capabilities
  27. * @slcount: sublink count
  28. * @elid: extended link ID (AZX_REG_ML_LEPTR_ID_ defines)
  29. * @elver: extended link version
  30. * @leptr: extended link pointer
  31. * @eml_lock: mutual exclusion to access shared registers e.g. CPA/SPA bits
  32. * in LCTL register
  33. * @sublink_ref_count: array of refcounts, required to power-manage sublinks independently
  34. * @base_ptr: pointer to shim/ip/shim_vs space
  35. * @instance_offset: offset between each of @slcount instances managed by link
  36. * @shim_offset: offset to SHIM register base
  37. * @ip_offset: offset to IP register base
  38. * @shim_vs_offset: offset to vendor-specific (VS) SHIM base
  39. */
  40. struct hdac_ext2_link {
  41. struct hdac_ext_link hext_link;
  42. /* read directly from LCAP register */
  43. bool alt;
  44. bool intc;
  45. bool ofls;
  46. bool lss;
  47. int slcount;
  48. int elid;
  49. int elver;
  50. u32 leptr;
  51. struct mutex eml_lock; /* prevent concurrent access to e.g. CPA/SPA */
  52. int sublink_ref_count[HDAML_MAX_SUBLINKS];
  53. /* internal values computed from LCAP contents */
  54. void __iomem *base_ptr;
  55. u32 instance_offset;
  56. u32 shim_offset;
  57. u32 ip_offset;
  58. u32 shim_vs_offset;
  59. };
  60. #define hdac_ext_link_to_ext2(h) container_of(h, struct hdac_ext2_link, hext_link)
  61. #define AZX_REG_SDW_INSTANCE_OFFSET 0x8000
  62. #define AZX_REG_SDW_SHIM_OFFSET 0x0
  63. #define AZX_REG_SDW_IP_OFFSET 0x100
  64. #define AZX_REG_SDW_VS_SHIM_OFFSET 0x6000
  65. #define AZX_REG_SDW_SHIM_PCMSyCM(y) (0x16 + 0x4 * (y))
  66. /* only one instance supported */
  67. #define AZX_REG_INTEL_DMIC_SHIM_OFFSET 0x0
  68. #define AZX_REG_INTEL_DMIC_IP_OFFSET 0x100
  69. #define AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET 0x6000
  70. #define AZX_REG_INTEL_SSP_INSTANCE_OFFSET 0x1000
  71. #define AZX_REG_INTEL_SSP_SHIM_OFFSET 0x0
  72. #define AZX_REG_INTEL_SSP_IP_OFFSET 0x100
  73. #define AZX_REG_INTEL_SSP_VS_SHIM_OFFSET 0xC00
  74. /* only one instance supported */
  75. #define AZX_REG_INTEL_UAOL_SHIM_OFFSET 0x0
  76. #define AZX_REG_INTEL_UAOL_IP_OFFSET 0x100
  77. #define AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET 0xC00
  78. /* HDAML section - this part follows sequences in the hardware specification,
  79. * including naming conventions and the use of the hdaml_ prefix.
  80. * The code is intentionally minimal with limited dependencies on frameworks or
  81. * helpers. Locking and scanning lists is handled at a higher level
  82. */
  83. static int hdaml_lnk_enum(struct device *dev, struct hdac_ext2_link *h2link,
  84. void __iomem *remap_addr, void __iomem *ml_addr, int link_idx)
  85. {
  86. struct hdac_ext_link *hlink = &h2link->hext_link;
  87. u32 base_offset;
  88. hlink->lcaps = readl(ml_addr + AZX_REG_ML_LCAP);
  89. h2link->alt = FIELD_GET(AZX_ML_HDA_LCAP_ALT, hlink->lcaps);
  90. /* handle alternate extensions */
  91. if (!h2link->alt) {
  92. h2link->slcount = 1;
  93. /*
  94. * LSDIID is initialized by hardware for HDaudio link,
  95. * it needs to be setup by software for alternate links
  96. */
  97. hlink->lsdiid = readw(ml_addr + AZX_REG_ML_LSDIID);
  98. dev_dbg(dev, "Link %d: HDAudio - lsdiid=%d\n",
  99. link_idx, hlink->lsdiid);
  100. return 0;
  101. }
  102. h2link->intc = FIELD_GET(AZX_ML_HDA_LCAP_INTC, hlink->lcaps);
  103. h2link->ofls = FIELD_GET(AZX_ML_HDA_LCAP_OFLS, hlink->lcaps);
  104. h2link->lss = FIELD_GET(AZX_ML_HDA_LCAP_LSS, hlink->lcaps);
  105. /* read slcount (increment due to zero-based hardware representation */
  106. h2link->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
  107. dev_dbg(dev, "Link %d: HDAudio extended - sublink count %d\n",
  108. link_idx, h2link->slcount);
  109. /* find IP ID and offsets */
  110. h2link->leptr = readl(ml_addr + AZX_REG_ML_LEPTR);
  111. h2link->elid = FIELD_GET(AZX_REG_ML_LEPTR_ID, h2link->leptr);
  112. base_offset = FIELD_GET(AZX_REG_ML_LEPTR_PTR, h2link->leptr);
  113. h2link->base_ptr = remap_addr + base_offset;
  114. switch (h2link->elid) {
  115. case AZX_REG_ML_LEPTR_ID_SDW:
  116. h2link->instance_offset = AZX_REG_SDW_INSTANCE_OFFSET;
  117. h2link->shim_offset = AZX_REG_SDW_SHIM_OFFSET;
  118. h2link->ip_offset = AZX_REG_SDW_IP_OFFSET;
  119. h2link->shim_vs_offset = AZX_REG_SDW_VS_SHIM_OFFSET;
  120. dev_dbg(dev, "Link %d: HDAudio extended - SoundWire alternate link, leptr.ptr %#x\n",
  121. link_idx, base_offset);
  122. break;
  123. case AZX_REG_ML_LEPTR_ID_INTEL_DMIC:
  124. h2link->shim_offset = AZX_REG_INTEL_DMIC_SHIM_OFFSET;
  125. h2link->ip_offset = AZX_REG_INTEL_DMIC_IP_OFFSET;
  126. h2link->shim_vs_offset = AZX_REG_INTEL_DMIC_VS_SHIM_OFFSET;
  127. dev_dbg(dev, "Link %d: HDAudio extended - INTEL DMIC alternate link, leptr.ptr %#x\n",
  128. link_idx, base_offset);
  129. break;
  130. case AZX_REG_ML_LEPTR_ID_INTEL_SSP:
  131. h2link->instance_offset = AZX_REG_INTEL_SSP_INSTANCE_OFFSET;
  132. h2link->shim_offset = AZX_REG_INTEL_SSP_SHIM_OFFSET;
  133. h2link->ip_offset = AZX_REG_INTEL_SSP_IP_OFFSET;
  134. h2link->shim_vs_offset = AZX_REG_INTEL_SSP_VS_SHIM_OFFSET;
  135. dev_dbg(dev, "Link %d: HDAudio extended - INTEL SSP alternate link, leptr.ptr %#x\n",
  136. link_idx, base_offset);
  137. break;
  138. case AZX_REG_ML_LEPTR_ID_INTEL_UAOL:
  139. h2link->shim_offset = AZX_REG_INTEL_UAOL_SHIM_OFFSET;
  140. h2link->ip_offset = AZX_REG_INTEL_UAOL_IP_OFFSET;
  141. h2link->shim_vs_offset = AZX_REG_INTEL_UAOL_VS_SHIM_OFFSET;
  142. dev_dbg(dev, "Link %d: HDAudio extended - INTEL UAOL alternate link, leptr.ptr %#x\n",
  143. link_idx, base_offset);
  144. break;
  145. default:
  146. dev_err(dev, "Link %d: HDAudio extended - Unsupported alternate link, leptr.id=%#02x value\n",
  147. link_idx, h2link->elid);
  148. return -EINVAL;
  149. }
  150. return 0;
  151. }
  152. /*
  153. * Hardware recommendations are to wait ~10us before checking any hardware transition
  154. * reported by bits changing status.
  155. * This value does not need to be super-precise, a slack of 5us is perfectly acceptable.
  156. * The worst-case is about 1ms before reporting an issue
  157. */
  158. #define HDAML_POLL_DELAY_MIN_US 10
  159. #define HDAML_POLL_DELAY_SLACK_US 5
  160. #define HDAML_POLL_DELAY_RETRY 100
  161. static int check_sublink_power(u32 __iomem *lctl, int sublink, bool enabled)
  162. {
  163. int mask = BIT(sublink) << AZX_ML_LCTL_CPA_SHIFT;
  164. int retry = HDAML_POLL_DELAY_RETRY;
  165. u32 val;
  166. usleep_range(HDAML_POLL_DELAY_MIN_US,
  167. HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
  168. do {
  169. val = readl(lctl);
  170. if (enabled) {
  171. if (val & mask)
  172. return 0;
  173. } else {
  174. if (!(val & mask))
  175. return 0;
  176. }
  177. usleep_range(HDAML_POLL_DELAY_MIN_US,
  178. HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
  179. } while (--retry);
  180. return -EIO;
  181. }
  182. static int hdaml_link_init(u32 __iomem *lctl, int sublink)
  183. {
  184. u32 val;
  185. u32 mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
  186. val = readl(lctl);
  187. val |= mask;
  188. writel(val, lctl);
  189. return check_sublink_power(lctl, sublink, true);
  190. }
  191. static int hdaml_link_shutdown(u32 __iomem *lctl, int sublink)
  192. {
  193. u32 val;
  194. u32 mask;
  195. val = readl(lctl);
  196. mask = BIT(sublink) << AZX_ML_LCTL_SPA_SHIFT;
  197. val &= ~mask;
  198. writel(val, lctl);
  199. return check_sublink_power(lctl, sublink, false);
  200. }
  201. static void hdaml_link_enable_interrupt(u32 __iomem *lctl, bool enable)
  202. {
  203. u32 val;
  204. val = readl(lctl);
  205. if (enable)
  206. val |= AZX_ML_LCTL_INTEN;
  207. else
  208. val &= ~AZX_ML_LCTL_INTEN;
  209. writel(val, lctl);
  210. }
  211. static bool hdaml_link_check_interrupt(u32 __iomem *lctl)
  212. {
  213. u32 val;
  214. val = readl(lctl);
  215. return val & AZX_ML_LCTL_INTSTS;
  216. }
  217. static int hdaml_wait_bit(void __iomem *base, int offset, u32 mask, u32 target)
  218. {
  219. int timeout = HDAML_POLL_DELAY_RETRY;
  220. u32 reg_read;
  221. do {
  222. reg_read = readl(base + offset);
  223. if ((reg_read & mask) == target)
  224. return 0;
  225. timeout--;
  226. usleep_range(HDAML_POLL_DELAY_MIN_US,
  227. HDAML_POLL_DELAY_MIN_US + HDAML_POLL_DELAY_SLACK_US);
  228. } while (timeout != 0);
  229. return -EAGAIN;
  230. }
  231. static void hdaml_link_set_syncprd(u32 __iomem *lsync, u32 syncprd)
  232. {
  233. u32 val;
  234. val = readl(lsync);
  235. val &= ~AZX_REG_ML_LSYNC_SYNCPRD;
  236. val |= (syncprd & AZX_REG_ML_LSYNC_SYNCPRD);
  237. /*
  238. * set SYNCPU but do not wait. The bit is cleared by hardware when
  239. * the link becomes active.
  240. */
  241. val |= AZX_REG_ML_LSYNC_SYNCPU;
  242. writel(val, lsync);
  243. }
  244. static int hdaml_link_wait_syncpu(u32 __iomem *lsync)
  245. {
  246. return hdaml_wait_bit(lsync, 0, AZX_REG_ML_LSYNC_SYNCPU, 0);
  247. }
  248. static void hdaml_link_sync_arm(u32 __iomem *lsync, int sublink)
  249. {
  250. u32 val;
  251. val = readl(lsync);
  252. val |= (AZX_REG_ML_LSYNC_CMDSYNC << sublink);
  253. writel(val, lsync);
  254. }
  255. static void hdaml_link_sync_go(u32 __iomem *lsync)
  256. {
  257. u32 val;
  258. val = readl(lsync);
  259. val |= AZX_REG_ML_LSYNC_SYNCGO;
  260. writel(val, lsync);
  261. }
  262. static bool hdaml_link_check_cmdsync(u32 __iomem *lsync, u32 cmdsync_mask)
  263. {
  264. u32 val;
  265. val = readl(lsync);
  266. return !!(val & cmdsync_mask);
  267. }
  268. static u16 hdaml_link_get_lsdiid(u16 __iomem *lsdiid)
  269. {
  270. return readw(lsdiid);
  271. }
  272. static void hdaml_link_set_lsdiid(u16 __iomem *lsdiid, int dev_num)
  273. {
  274. u16 val;
  275. val = readw(lsdiid);
  276. val |= BIT(dev_num);
  277. writew(val, lsdiid);
  278. }
  279. static void hdaml_shim_map_stream_ch(u16 __iomem *pcmsycm, int lchan, int hchan,
  280. int stream_id, int dir)
  281. {
  282. u16 val;
  283. val = readw(pcmsycm);
  284. u16p_replace_bits(&val, lchan, GENMASK(3, 0));
  285. u16p_replace_bits(&val, hchan, GENMASK(7, 4));
  286. u16p_replace_bits(&val, stream_id, GENMASK(13, 8));
  287. u16p_replace_bits(&val, dir, BIT(15));
  288. writew(val, pcmsycm);
  289. }
  290. static void hdaml_lctl_offload_enable(u32 __iomem *lctl, bool enable)
  291. {
  292. u32 val = readl(lctl);
  293. if (enable)
  294. val |= AZX_ML_LCTL_OFLEN;
  295. else
  296. val &= ~AZX_ML_LCTL_OFLEN;
  297. writel(val, lctl);
  298. }
  299. /* END HDAML section */
  300. static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
  301. {
  302. struct hdac_ext2_link *h2link;
  303. struct hdac_ext_link *hlink;
  304. int ret;
  305. h2link = kzalloc(sizeof(*h2link), GFP_KERNEL);
  306. if (!h2link)
  307. return -ENOMEM;
  308. /* basic initialization */
  309. hlink = &h2link->hext_link;
  310. hlink->index = index;
  311. hlink->bus = bus;
  312. hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
  313. ret = hdaml_lnk_enum(bus->dev, h2link, bus->remap_addr, hlink->ml_addr, index);
  314. if (ret < 0) {
  315. kfree(h2link);
  316. return ret;
  317. }
  318. mutex_init(&h2link->eml_lock);
  319. list_add_tail(&hlink->list, &bus->hlink_list);
  320. /*
  321. * HDaudio regular links are powered-on by default, the
  322. * refcount needs to be initialized.
  323. */
  324. if (!h2link->alt)
  325. hlink->ref_count = 1;
  326. return 0;
  327. }
  328. int hda_bus_ml_init(struct hdac_bus *bus)
  329. {
  330. u32 link_count;
  331. int ret;
  332. int i;
  333. if (!bus->mlcap)
  334. return 0;
  335. link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
  336. dev_dbg(bus->dev, "HDAudio Multi-Link count: %d\n", link_count);
  337. for (i = 0; i < link_count; i++) {
  338. ret = hda_ml_alloc_h2link(bus, i);
  339. if (ret < 0) {
  340. hda_bus_ml_free(bus);
  341. return ret;
  342. }
  343. }
  344. return 0;
  345. }
  346. EXPORT_SYMBOL_NS(hda_bus_ml_init, SND_SOC_SOF_HDA_MLINK);
  347. void hda_bus_ml_free(struct hdac_bus *bus)
  348. {
  349. struct hdac_ext_link *hlink, *_h;
  350. struct hdac_ext2_link *h2link;
  351. if (!bus->mlcap)
  352. return;
  353. list_for_each_entry_safe(hlink, _h, &bus->hlink_list, list) {
  354. list_del(&hlink->list);
  355. h2link = hdac_ext_link_to_ext2(hlink);
  356. mutex_destroy(&h2link->eml_lock);
  357. kfree(h2link);
  358. }
  359. }
  360. EXPORT_SYMBOL_NS(hda_bus_ml_free, SND_SOC_SOF_HDA_MLINK);
  361. static struct hdac_ext2_link *
  362. find_ext2_link(struct hdac_bus *bus, bool alt, int elid)
  363. {
  364. struct hdac_ext_link *hlink;
  365. list_for_each_entry(hlink, &bus->hlink_list, list) {
  366. struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
  367. if (h2link->alt == alt && h2link->elid == elid)
  368. return h2link;
  369. }
  370. return NULL;
  371. }
  372. int hdac_bus_eml_get_count(struct hdac_bus *bus, bool alt, int elid)
  373. {
  374. struct hdac_ext2_link *h2link;
  375. h2link = find_ext2_link(bus, alt, elid);
  376. if (!h2link)
  377. return 0;
  378. return h2link->slcount;
  379. }
  380. EXPORT_SYMBOL_NS(hdac_bus_eml_get_count, SND_SOC_SOF_HDA_MLINK);
  381. void hdac_bus_eml_enable_interrupt(struct hdac_bus *bus, bool alt, int elid, bool enable)
  382. {
  383. struct hdac_ext2_link *h2link;
  384. struct hdac_ext_link *hlink;
  385. h2link = find_ext2_link(bus, alt, elid);
  386. if (!h2link)
  387. return;
  388. if (!h2link->intc)
  389. return;
  390. hlink = &h2link->hext_link;
  391. mutex_lock(&h2link->eml_lock);
  392. hdaml_link_enable_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
  393. mutex_unlock(&h2link->eml_lock);
  394. }
  395. EXPORT_SYMBOL_NS(hdac_bus_eml_enable_interrupt, SND_SOC_SOF_HDA_MLINK);
  396. bool hdac_bus_eml_check_interrupt(struct hdac_bus *bus, bool alt, int elid)
  397. {
  398. struct hdac_ext2_link *h2link;
  399. struct hdac_ext_link *hlink;
  400. h2link = find_ext2_link(bus, alt, elid);
  401. if (!h2link)
  402. return false;
  403. if (!h2link->intc)
  404. return false;
  405. hlink = &h2link->hext_link;
  406. return hdaml_link_check_interrupt(hlink->ml_addr + AZX_REG_ML_LCTL);
  407. }
  408. EXPORT_SYMBOL_NS(hdac_bus_eml_check_interrupt, SND_SOC_SOF_HDA_MLINK);
  409. int hdac_bus_eml_set_syncprd_unlocked(struct hdac_bus *bus, bool alt, int elid, u32 syncprd)
  410. {
  411. struct hdac_ext2_link *h2link;
  412. struct hdac_ext_link *hlink;
  413. h2link = find_ext2_link(bus, alt, elid);
  414. if (!h2link)
  415. return 0;
  416. if (!h2link->lss)
  417. return 0;
  418. hlink = &h2link->hext_link;
  419. hdaml_link_set_syncprd(hlink->ml_addr + AZX_REG_ML_LSYNC, syncprd);
  420. return 0;
  421. }
  422. EXPORT_SYMBOL_NS(hdac_bus_eml_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
  423. int hdac_bus_eml_sdw_set_syncprd_unlocked(struct hdac_bus *bus, u32 syncprd)
  424. {
  425. return hdac_bus_eml_set_syncprd_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, syncprd);
  426. }
  427. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_syncprd_unlocked, SND_SOC_SOF_HDA_MLINK);
  428. int hdac_bus_eml_wait_syncpu_unlocked(struct hdac_bus *bus, bool alt, int elid)
  429. {
  430. struct hdac_ext2_link *h2link;
  431. struct hdac_ext_link *hlink;
  432. h2link = find_ext2_link(bus, alt, elid);
  433. if (!h2link)
  434. return 0;
  435. if (!h2link->lss)
  436. return 0;
  437. hlink = &h2link->hext_link;
  438. return hdaml_link_wait_syncpu(hlink->ml_addr + AZX_REG_ML_LSYNC);
  439. }
  440. EXPORT_SYMBOL_NS(hdac_bus_eml_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
  441. int hdac_bus_eml_sdw_wait_syncpu_unlocked(struct hdac_bus *bus)
  442. {
  443. return hdac_bus_eml_wait_syncpu_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  444. }
  445. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_wait_syncpu_unlocked, SND_SOC_SOF_HDA_MLINK);
  446. void hdac_bus_eml_sync_arm_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
  447. {
  448. struct hdac_ext2_link *h2link;
  449. struct hdac_ext_link *hlink;
  450. h2link = find_ext2_link(bus, alt, elid);
  451. if (!h2link)
  452. return;
  453. if (!h2link->lss)
  454. return;
  455. hlink = &h2link->hext_link;
  456. hdaml_link_sync_arm(hlink->ml_addr + AZX_REG_ML_LSYNC, sublink);
  457. }
  458. EXPORT_SYMBOL_NS(hdac_bus_eml_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
  459. void hdac_bus_eml_sdw_sync_arm_unlocked(struct hdac_bus *bus, int sublink)
  460. {
  461. hdac_bus_eml_sync_arm_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
  462. }
  463. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_arm_unlocked, SND_SOC_SOF_HDA_MLINK);
  464. int hdac_bus_eml_sync_go_unlocked(struct hdac_bus *bus, bool alt, int elid)
  465. {
  466. struct hdac_ext2_link *h2link;
  467. struct hdac_ext_link *hlink;
  468. h2link = find_ext2_link(bus, alt, elid);
  469. if (!h2link)
  470. return 0;
  471. if (!h2link->lss)
  472. return 0;
  473. hlink = &h2link->hext_link;
  474. hdaml_link_sync_go(hlink->ml_addr + AZX_REG_ML_LSYNC);
  475. return 0;
  476. }
  477. EXPORT_SYMBOL_NS(hdac_bus_eml_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
  478. int hdac_bus_eml_sdw_sync_go_unlocked(struct hdac_bus *bus)
  479. {
  480. return hdac_bus_eml_sync_go_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  481. }
  482. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_sync_go_unlocked, SND_SOC_SOF_HDA_MLINK);
  483. bool hdac_bus_eml_check_cmdsync_unlocked(struct hdac_bus *bus, bool alt, int elid)
  484. {
  485. struct hdac_ext2_link *h2link;
  486. struct hdac_ext_link *hlink;
  487. u32 cmdsync_mask;
  488. h2link = find_ext2_link(bus, alt, elid);
  489. if (!h2link)
  490. return 0;
  491. if (!h2link->lss)
  492. return 0;
  493. hlink = &h2link->hext_link;
  494. cmdsync_mask = GENMASK(AZX_REG_ML_LSYNC_CMDSYNC_SHIFT + h2link->slcount - 1,
  495. AZX_REG_ML_LSYNC_CMDSYNC_SHIFT);
  496. return hdaml_link_check_cmdsync(hlink->ml_addr + AZX_REG_ML_LSYNC,
  497. cmdsync_mask);
  498. }
  499. EXPORT_SYMBOL_NS(hdac_bus_eml_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
  500. bool hdac_bus_eml_sdw_check_cmdsync_unlocked(struct hdac_bus *bus)
  501. {
  502. return hdac_bus_eml_check_cmdsync_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  503. }
  504. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_check_cmdsync_unlocked, SND_SOC_SOF_HDA_MLINK);
  505. static int hdac_bus_eml_power_up_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
  506. bool eml_lock)
  507. {
  508. struct hdac_ext2_link *h2link;
  509. struct hdac_ext_link *hlink;
  510. int ret = 0;
  511. h2link = find_ext2_link(bus, alt, elid);
  512. if (!h2link)
  513. return -ENODEV;
  514. if (sublink >= h2link->slcount)
  515. return -EINVAL;
  516. hlink = &h2link->hext_link;
  517. if (eml_lock)
  518. mutex_lock(&h2link->eml_lock);
  519. if (!alt) {
  520. if (++hlink->ref_count > 1)
  521. goto skip_init;
  522. } else {
  523. if (++h2link->sublink_ref_count[sublink] > 1)
  524. goto skip_init;
  525. }
  526. ret = hdaml_link_init(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
  527. skip_init:
  528. if (eml_lock)
  529. mutex_unlock(&h2link->eml_lock);
  530. return ret;
  531. }
  532. int hdac_bus_eml_power_up(struct hdac_bus *bus, bool alt, int elid, int sublink)
  533. {
  534. return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, true);
  535. }
  536. EXPORT_SYMBOL_NS(hdac_bus_eml_power_up, SND_SOC_SOF_HDA_MLINK);
  537. int hdac_bus_eml_power_up_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
  538. {
  539. return hdac_bus_eml_power_up_base(bus, alt, elid, sublink, false);
  540. }
  541. EXPORT_SYMBOL_NS(hdac_bus_eml_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
  542. static int hdac_bus_eml_power_down_base(struct hdac_bus *bus, bool alt, int elid, int sublink,
  543. bool eml_lock)
  544. {
  545. struct hdac_ext2_link *h2link;
  546. struct hdac_ext_link *hlink;
  547. int ret = 0;
  548. h2link = find_ext2_link(bus, alt, elid);
  549. if (!h2link)
  550. return -ENODEV;
  551. if (sublink >= h2link->slcount)
  552. return -EINVAL;
  553. hlink = &h2link->hext_link;
  554. if (eml_lock)
  555. mutex_lock(&h2link->eml_lock);
  556. if (!alt) {
  557. if (--hlink->ref_count > 0)
  558. goto skip_shutdown;
  559. } else {
  560. if (--h2link->sublink_ref_count[sublink] > 0)
  561. goto skip_shutdown;
  562. }
  563. ret = hdaml_link_shutdown(hlink->ml_addr + AZX_REG_ML_LCTL, sublink);
  564. skip_shutdown:
  565. if (eml_lock)
  566. mutex_unlock(&h2link->eml_lock);
  567. return ret;
  568. }
  569. int hdac_bus_eml_power_down(struct hdac_bus *bus, bool alt, int elid, int sublink)
  570. {
  571. return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, true);
  572. }
  573. EXPORT_SYMBOL_NS(hdac_bus_eml_power_down, SND_SOC_SOF_HDA_MLINK);
  574. int hdac_bus_eml_power_down_unlocked(struct hdac_bus *bus, bool alt, int elid, int sublink)
  575. {
  576. return hdac_bus_eml_power_down_base(bus, alt, elid, sublink, false);
  577. }
  578. EXPORT_SYMBOL_NS(hdac_bus_eml_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
  579. int hdac_bus_eml_sdw_power_up_unlocked(struct hdac_bus *bus, int sublink)
  580. {
  581. return hdac_bus_eml_power_up_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
  582. }
  583. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_up_unlocked, SND_SOC_SOF_HDA_MLINK);
  584. int hdac_bus_eml_sdw_power_down_unlocked(struct hdac_bus *bus, int sublink)
  585. {
  586. return hdac_bus_eml_power_down_unlocked(bus, true, AZX_REG_ML_LEPTR_ID_SDW, sublink);
  587. }
  588. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_power_down_unlocked, SND_SOC_SOF_HDA_MLINK);
  589. int hdac_bus_eml_sdw_get_lsdiid_unlocked(struct hdac_bus *bus, int sublink, u16 *lsdiid)
  590. {
  591. struct hdac_ext2_link *h2link;
  592. struct hdac_ext_link *hlink;
  593. h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  594. if (!h2link)
  595. return -ENODEV;
  596. hlink = &h2link->hext_link;
  597. *lsdiid = hdaml_link_get_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink));
  598. return 0;
  599. } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_lsdiid_unlocked, SND_SOC_SOF_HDA_MLINK);
  600. int hdac_bus_eml_sdw_set_lsdiid(struct hdac_bus *bus, int sublink, int dev_num)
  601. {
  602. struct hdac_ext2_link *h2link;
  603. struct hdac_ext_link *hlink;
  604. h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  605. if (!h2link)
  606. return -ENODEV;
  607. hlink = &h2link->hext_link;
  608. mutex_lock(&h2link->eml_lock);
  609. hdaml_link_set_lsdiid(hlink->ml_addr + AZX_REG_ML_LSDIID_OFFSET(sublink), dev_num);
  610. mutex_unlock(&h2link->eml_lock);
  611. return 0;
  612. } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_set_lsdiid, SND_SOC_SOF_HDA_MLINK);
  613. /*
  614. * the 'y' parameter comes from the PCMSyCM hardware register naming. 'y' refers to the
  615. * PDI index, i.e. the FIFO used for RX or TX
  616. */
  617. int hdac_bus_eml_sdw_map_stream_ch(struct hdac_bus *bus, int sublink, int y,
  618. int channel_mask, int stream_id, int dir)
  619. {
  620. struct hdac_ext2_link *h2link;
  621. u16 __iomem *pcmsycm;
  622. int hchan;
  623. int lchan;
  624. u16 val;
  625. h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  626. if (!h2link)
  627. return -ENODEV;
  628. pcmsycm = h2link->base_ptr + h2link->shim_offset +
  629. h2link->instance_offset * sublink +
  630. AZX_REG_SDW_SHIM_PCMSyCM(y);
  631. if (channel_mask) {
  632. hchan = __fls(channel_mask);
  633. lchan = __ffs(channel_mask);
  634. } else {
  635. hchan = 0;
  636. lchan = 0;
  637. }
  638. mutex_lock(&h2link->eml_lock);
  639. hdaml_shim_map_stream_ch(pcmsycm, lchan, hchan,
  640. stream_id, dir);
  641. mutex_unlock(&h2link->eml_lock);
  642. val = readw(pcmsycm);
  643. dev_dbg(bus->dev, "sublink %d channel_mask %#x stream_id %d dir %d pcmscm %#x\n",
  644. sublink, channel_mask, stream_id, dir, val);
  645. return 0;
  646. } EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_map_stream_ch, SND_SOC_SOF_HDA_MLINK);
  647. void hda_bus_ml_put_all(struct hdac_bus *bus)
  648. {
  649. struct hdac_ext_link *hlink;
  650. list_for_each_entry(hlink, &bus->hlink_list, list) {
  651. struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
  652. if (!h2link->alt)
  653. snd_hdac_ext_bus_link_put(bus, hlink);
  654. }
  655. }
  656. EXPORT_SYMBOL_NS(hda_bus_ml_put_all, SND_SOC_SOF_HDA_MLINK);
  657. void hda_bus_ml_reset_losidv(struct hdac_bus *bus)
  658. {
  659. struct hdac_ext_link *hlink;
  660. /* Reset stream-to-link mapping */
  661. list_for_each_entry(hlink, &bus->hlink_list, list)
  662. writel(0, hlink->ml_addr + AZX_REG_ML_LOSIDV);
  663. }
  664. EXPORT_SYMBOL_NS(hda_bus_ml_reset_losidv, SND_SOC_SOF_HDA_MLINK);
  665. int hda_bus_ml_resume(struct hdac_bus *bus)
  666. {
  667. struct hdac_ext_link *hlink;
  668. int ret;
  669. /* power up links that were active before suspend */
  670. list_for_each_entry(hlink, &bus->hlink_list, list) {
  671. struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
  672. if (!h2link->alt && hlink->ref_count) {
  673. ret = snd_hdac_ext_bus_link_power_up(hlink);
  674. if (ret < 0)
  675. return ret;
  676. }
  677. }
  678. return 0;
  679. }
  680. EXPORT_SYMBOL_NS(hda_bus_ml_resume, SND_SOC_SOF_HDA_MLINK);
  681. int hda_bus_ml_suspend(struct hdac_bus *bus)
  682. {
  683. struct hdac_ext_link *hlink;
  684. int ret;
  685. list_for_each_entry(hlink, &bus->hlink_list, list) {
  686. struct hdac_ext2_link *h2link = hdac_ext_link_to_ext2(hlink);
  687. if (!h2link->alt) {
  688. ret = snd_hdac_ext_bus_link_power_down(hlink);
  689. if (ret < 0)
  690. return ret;
  691. }
  692. }
  693. return 0;
  694. }
  695. EXPORT_SYMBOL_NS(hda_bus_ml_suspend, SND_SOC_SOF_HDA_MLINK);
  696. struct mutex *hdac_bus_eml_get_mutex(struct hdac_bus *bus, bool alt, int elid)
  697. {
  698. struct hdac_ext2_link *h2link;
  699. h2link = find_ext2_link(bus, alt, elid);
  700. if (!h2link)
  701. return NULL;
  702. return &h2link->eml_lock;
  703. }
  704. EXPORT_SYMBOL_NS(hdac_bus_eml_get_mutex, SND_SOC_SOF_HDA_MLINK);
  705. struct hdac_ext_link *hdac_bus_eml_ssp_get_hlink(struct hdac_bus *bus)
  706. {
  707. struct hdac_ext2_link *h2link;
  708. h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
  709. if (!h2link)
  710. return NULL;
  711. return &h2link->hext_link;
  712. }
  713. EXPORT_SYMBOL_NS(hdac_bus_eml_ssp_get_hlink, SND_SOC_SOF_HDA_MLINK);
  714. struct hdac_ext_link *hdac_bus_eml_dmic_get_hlink(struct hdac_bus *bus)
  715. {
  716. struct hdac_ext2_link *h2link;
  717. h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
  718. if (!h2link)
  719. return NULL;
  720. return &h2link->hext_link;
  721. }
  722. EXPORT_SYMBOL_NS(hdac_bus_eml_dmic_get_hlink, SND_SOC_SOF_HDA_MLINK);
  723. struct hdac_ext_link *hdac_bus_eml_sdw_get_hlink(struct hdac_bus *bus)
  724. {
  725. struct hdac_ext2_link *h2link;
  726. h2link = find_ext2_link(bus, true, AZX_REG_ML_LEPTR_ID_SDW);
  727. if (!h2link)
  728. return NULL;
  729. return &h2link->hext_link;
  730. }
  731. EXPORT_SYMBOL_NS(hdac_bus_eml_sdw_get_hlink, SND_SOC_SOF_HDA_MLINK);
  732. int hdac_bus_eml_enable_offload(struct hdac_bus *bus, bool alt, int elid, bool enable)
  733. {
  734. struct hdac_ext2_link *h2link;
  735. struct hdac_ext_link *hlink;
  736. h2link = find_ext2_link(bus, alt, elid);
  737. if (!h2link)
  738. return -ENODEV;
  739. if (!h2link->ofls)
  740. return 0;
  741. hlink = &h2link->hext_link;
  742. mutex_lock(&h2link->eml_lock);
  743. hdaml_lctl_offload_enable(hlink->ml_addr + AZX_REG_ML_LCTL, enable);
  744. mutex_unlock(&h2link->eml_lock);
  745. return 0;
  746. }
  747. EXPORT_SYMBOL_NS(hdac_bus_eml_enable_offload, SND_SOC_SOF_HDA_MLINK);
  748. #endif
  749. MODULE_LICENSE("Dual BSD/GPL");
  750. MODULE_DESCRIPTION("SOF support for HDaudio multi-link");