2
0

0069-ASoC-starfive-Add-JH7110-TDM-driver.patch 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. From 9d90936b0f69d891001a11d3f6c0c3728d8b6d85 Mon Sep 17 00:00:00 2001
  2. From: Walker Chen <[email protected]>
  3. Date: Fri, 26 May 2023 22:54:01 +0800
  4. Subject: [PATCH 069/122] ASoC: starfive: Add JH7110 TDM driver
  5. Add tdm driver support for the StarFive JH7110 SoC.
  6. Signed-off-by: Walker Chen <[email protected]>
  7. ---
  8. sound/soc/Kconfig | 1 +
  9. sound/soc/Makefile | 1 +
  10. sound/soc/starfive/Kconfig | 15 +
  11. sound/soc/starfive/Makefile | 2 +
  12. sound/soc/starfive/jh7110_tdm.c | 679 ++++++++++++++++++++++++++++++++
  13. 5 files changed, 698 insertions(+)
  14. create mode 100644 sound/soc/starfive/Kconfig
  15. create mode 100644 sound/soc/starfive/Makefile
  16. create mode 100644 sound/soc/starfive/jh7110_tdm.c
  17. --- a/sound/soc/Kconfig
  18. +++ b/sound/soc/Kconfig
  19. @@ -91,6 +91,7 @@ source "sound/soc/sh/Kconfig"
  20. source "sound/soc/sof/Kconfig"
  21. source "sound/soc/spear/Kconfig"
  22. source "sound/soc/sprd/Kconfig"
  23. +source "sound/soc/starfive/Kconfig"
  24. source "sound/soc/sti/Kconfig"
  25. source "sound/soc/stm/Kconfig"
  26. source "sound/soc/sunxi/Kconfig"
  27. --- a/sound/soc/Makefile
  28. +++ b/sound/soc/Makefile
  29. @@ -59,6 +59,7 @@ obj-$(CONFIG_SND_SOC) += sh/
  30. obj-$(CONFIG_SND_SOC) += sof/
  31. obj-$(CONFIG_SND_SOC) += spear/
  32. obj-$(CONFIG_SND_SOC) += sprd/
  33. +obj-$(CONFIG_SND_SOC) += starfive/
  34. obj-$(CONFIG_SND_SOC) += sti/
  35. obj-$(CONFIG_SND_SOC) += stm/
  36. obj-$(CONFIG_SND_SOC) += sunxi/
  37. --- /dev/null
  38. +++ b/sound/soc/starfive/Kconfig
  39. @@ -0,0 +1,15 @@
  40. +# SPDX-License-Identifier: GPL-2.0-only
  41. +config SND_SOC_STARFIVE
  42. + tristate "Audio support for StarFive SoC"
  43. + depends on COMPILE_TEST || ARCH_STARFIVE
  44. + help
  45. + Say Y or M if you want to add support for codecs attached to
  46. + the Starfive SoCs' Audio interfaces. You will also need to
  47. + select the audio interfaces to support below.
  48. +
  49. +config SND_SOC_JH7110_TDM
  50. + tristate "JH7110 TDM device driver"
  51. + depends on HAVE_CLK && SND_SOC_STARFIVE
  52. + select SND_SOC_GENERIC_DMAENGINE_PCM
  53. + help
  54. + Say Y or M if you want to add support for StarFive TDM driver.
  55. --- /dev/null
  56. +++ b/sound/soc/starfive/Makefile
  57. @@ -0,0 +1,2 @@
  58. +# StarFive Platform Support
  59. +obj-$(CONFIG_SND_SOC_JH7110_TDM) += jh7110_tdm.o
  60. --- /dev/null
  61. +++ b/sound/soc/starfive/jh7110_tdm.c
  62. @@ -0,0 +1,679 @@
  63. +// SPDX-License-Identifier: GPL-2.0
  64. +/*
  65. + * jh7110_tdm.c -- StarFive JH7110 TDM driver
  66. + *
  67. + * Copyright (C) 2023 StarFive Technology Co., Ltd.
  68. + *
  69. + * Author: Walker Chen <[email protected]>
  70. + */
  71. +
  72. +#include <linux/clk.h>
  73. +#include <linux/device.h>
  74. +#include <linux/dmaengine.h>
  75. +#include <linux/module.h>
  76. +#include <linux/of_irq.h>
  77. +#include <linux/of_platform.h>
  78. +#include <linux/pm_runtime.h>
  79. +#include <linux/regmap.h>
  80. +#include <linux/reset.h>
  81. +#include <linux/types.h>
  82. +#include <sound/dmaengine_pcm.h>
  83. +#include <sound/initval.h>
  84. +#include <sound/pcm.h>
  85. +#include <sound/pcm_params.h>
  86. +#include <sound/soc.h>
  87. +#include <sound/soc-dai.h>
  88. +
  89. +#define TDM_PCMGBCR 0x00
  90. + #define PCMGBCR_MASK 0x1e
  91. + #define PCMGBCR_ENABLE BIT(0)
  92. + #define PCMGBCR_TRITXEN BIT(4)
  93. + #define CLKPOL_BIT 5
  94. + #define TRITXEN_BIT 4
  95. + #define ELM_BIT 3
  96. + #define SYNCM_BIT 2
  97. + #define MS_BIT 1
  98. +#define TDM_PCMTXCR 0x04
  99. + #define PCMTXCR_TXEN BIT(0)
  100. + #define IFL_BIT 11
  101. + #define WL_BIT 8
  102. + #define SSCALE_BIT 4
  103. + #define SL_BIT 2
  104. + #define LRJ_BIT 1
  105. +#define TDM_PCMRXCR 0x08
  106. + #define PCMRXCR_RXEN BIT(0)
  107. + #define PCMRXCR_RXSL_MASK 0xc
  108. + #define PCMRXCR_RXSL_16BIT 0x4
  109. + #define PCMRXCR_RXSL_32BIT 0x8
  110. + #define PCMRXCR_SCALE_MASK 0xf0
  111. + #define PCMRXCR_SCALE_1CH 0x10
  112. +#define TDM_PCMDIV 0x0c
  113. +
  114. +#define JH7110_TDM_FIFO 0x170c0000
  115. +#define JH7110_TDM_FIFO_DEPTH 32
  116. +
  117. +enum TDM_MASTER_SLAVE_MODE {
  118. + TDM_AS_MASTER = 0,
  119. + TDM_AS_SLAVE,
  120. +};
  121. +
  122. +enum TDM_CLKPOL {
  123. + /* tx raising and rx falling */
  124. + TDM_TX_RASING_RX_FALLING = 0,
  125. + /* tx falling and rx raising */
  126. + TDM_TX_FALLING_RX_RASING,
  127. +};
  128. +
  129. +enum TDM_ELM {
  130. + /* only work while SYNCM=0 */
  131. + TDM_ELM_LATE = 0,
  132. + TDM_ELM_EARLY,
  133. +};
  134. +
  135. +enum TDM_SYNCM {
  136. + /* short frame sync */
  137. + TDM_SYNCM_SHORT = 0,
  138. + /* long frame sync */
  139. + TDM_SYNCM_LONG,
  140. +};
  141. +
  142. +enum TDM_IFL {
  143. + /* FIFO to send or received : half-1/2, Quarter-1/4 */
  144. + TDM_FIFO_HALF = 0,
  145. + TDM_FIFO_QUARTER,
  146. +};
  147. +
  148. +enum TDM_WL {
  149. + /* send or received word length */
  150. + TDM_8BIT_WORD_LEN = 0,
  151. + TDM_16BIT_WORD_LEN,
  152. + TDM_20BIT_WORD_LEN,
  153. + TDM_24BIT_WORD_LEN,
  154. + TDM_32BIT_WORD_LEN,
  155. +};
  156. +
  157. +enum TDM_SL {
  158. + /* send or received slot length */
  159. + TDM_8BIT_SLOT_LEN = 0,
  160. + TDM_16BIT_SLOT_LEN,
  161. + TDM_32BIT_SLOT_LEN,
  162. +};
  163. +
  164. +enum TDM_LRJ {
  165. + /* left-justify or right-justify */
  166. + TDM_RIGHT_JUSTIFY = 0,
  167. + TDM_LEFT_JUSTIFT,
  168. +};
  169. +
  170. +struct tdm_chan_cfg {
  171. + enum TDM_IFL ifl;
  172. + enum TDM_WL wl;
  173. + unsigned char sscale;
  174. + enum TDM_SL sl;
  175. + enum TDM_LRJ lrj;
  176. + unsigned char enable;
  177. +};
  178. +
  179. +struct jh7110_tdm_dev {
  180. + void __iomem *tdm_base;
  181. + struct device *dev;
  182. + struct clk_bulk_data clks[6];
  183. + struct reset_control *resets;
  184. +
  185. + enum TDM_CLKPOL clkpolity;
  186. + enum TDM_ELM elm;
  187. + enum TDM_SYNCM syncm;
  188. + enum TDM_MASTER_SLAVE_MODE ms_mode;
  189. +
  190. + struct tdm_chan_cfg tx;
  191. + struct tdm_chan_cfg rx;
  192. +
  193. + u16 syncdiv;
  194. + u32 samplerate;
  195. + u32 pcmclk;
  196. +
  197. + /* data related to DMA transfers b/w tdm and DMAC */
  198. + struct snd_dmaengine_dai_dma_data play_dma_data;
  199. + struct snd_dmaengine_dai_dma_data capture_dma_data;
  200. + u32 saved_pcmgbcr;
  201. + u32 saved_pcmtxcr;
  202. + u32 saved_pcmrxcr;
  203. + u32 saved_pcmdiv;
  204. +};
  205. +
  206. +static inline u32 jh7110_tdm_readl(struct jh7110_tdm_dev *tdm, u16 reg)
  207. +{
  208. + return readl_relaxed(tdm->tdm_base + reg);
  209. +}
  210. +
  211. +static inline void jh7110_tdm_writel(struct jh7110_tdm_dev *tdm, u16 reg, u32 val)
  212. +{
  213. + writel_relaxed(val, tdm->tdm_base + reg);
  214. +}
  215. +
  216. +static void jh7110_tdm_save_context(struct jh7110_tdm_dev *tdm,
  217. + struct snd_pcm_substream *substream)
  218. +{
  219. + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  220. + tdm->saved_pcmtxcr = jh7110_tdm_readl(tdm, TDM_PCMTXCR);
  221. + else
  222. + tdm->saved_pcmrxcr = jh7110_tdm_readl(tdm, TDM_PCMRXCR);
  223. +}
  224. +
  225. +static void jh7110_tdm_start(struct jh7110_tdm_dev *tdm,
  226. + struct snd_pcm_substream *substream)
  227. +{
  228. + u32 data;
  229. +
  230. + data = jh7110_tdm_readl(tdm, TDM_PCMGBCR);
  231. + jh7110_tdm_writel(tdm, TDM_PCMGBCR, data | PCMGBCR_ENABLE);
  232. +
  233. + /* restore context */
  234. + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  235. + jh7110_tdm_writel(tdm, TDM_PCMTXCR, tdm->saved_pcmtxcr | PCMTXCR_TXEN);
  236. + else
  237. + jh7110_tdm_writel(tdm, TDM_PCMRXCR, tdm->saved_pcmrxcr | PCMRXCR_RXEN);
  238. +}
  239. +
  240. +static void jh7110_tdm_stop(struct jh7110_tdm_dev *tdm,
  241. + struct snd_pcm_substream *substream)
  242. +{
  243. + unsigned int val;
  244. +
  245. + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  246. + val = jh7110_tdm_readl(tdm, TDM_PCMTXCR);
  247. + val &= ~PCMTXCR_TXEN;
  248. + jh7110_tdm_writel(tdm, TDM_PCMTXCR, val);
  249. + } else {
  250. + val = jh7110_tdm_readl(tdm, TDM_PCMRXCR);
  251. + val &= ~PCMRXCR_RXEN;
  252. + jh7110_tdm_writel(tdm, TDM_PCMRXCR, val);
  253. + }
  254. +}
  255. +
  256. +static int jh7110_tdm_syncdiv(struct jh7110_tdm_dev *tdm)
  257. +{
  258. + u32 sl, sscale, syncdiv;
  259. +
  260. + if (tdm->rx.sl >= tdm->tx.sl)
  261. + sl = tdm->rx.sl;
  262. + else
  263. + sl = tdm->tx.sl;
  264. +
  265. + if (tdm->rx.sscale >= tdm->tx.sscale)
  266. + sscale = tdm->rx.sscale;
  267. + else
  268. + sscale = tdm->tx.sscale;
  269. +
  270. + syncdiv = tdm->pcmclk / tdm->samplerate - 1;
  271. +
  272. + if ((syncdiv + 1) < (sl * sscale)) {
  273. + dev_err(tdm->dev, "Failed to set syncdiv!\n");
  274. + return -EINVAL;
  275. + }
  276. +
  277. + if (tdm->syncm == TDM_SYNCM_LONG &&
  278. + (tdm->rx.sscale <= 1 || tdm->tx.sscale <= 1) &&
  279. + ((syncdiv + 1) <= sl)) {
  280. + dev_err(tdm->dev, "Wrong syncdiv! It must be (syncdiv+1) > max[tx.sl, rx.sl]\n");
  281. + return -EINVAL;
  282. + }
  283. +
  284. + jh7110_tdm_writel(tdm, TDM_PCMDIV, syncdiv);
  285. + return 0;
  286. +}
  287. +
  288. +static int jh7110_tdm_config(struct jh7110_tdm_dev *tdm,
  289. + struct snd_pcm_substream *substream)
  290. +{
  291. + u32 datarx, datatx;
  292. + int ret;
  293. +
  294. + ret = jh7110_tdm_syncdiv(tdm);
  295. + if (ret)
  296. + return ret;
  297. +
  298. + datarx = (tdm->rx.ifl << IFL_BIT) |
  299. + (tdm->rx.wl << WL_BIT) |
  300. + (tdm->rx.sscale << SSCALE_BIT) |
  301. + (tdm->rx.sl << SL_BIT) |
  302. + (tdm->rx.lrj << LRJ_BIT);
  303. +
  304. + datatx = (tdm->tx.ifl << IFL_BIT) |
  305. + (tdm->tx.wl << WL_BIT) |
  306. + (tdm->tx.sscale << SSCALE_BIT) |
  307. + (tdm->tx.sl << SL_BIT) |
  308. + (tdm->tx.lrj << LRJ_BIT);
  309. +
  310. + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  311. + jh7110_tdm_writel(tdm, TDM_PCMTXCR, datatx);
  312. + else
  313. + jh7110_tdm_writel(tdm, TDM_PCMRXCR, datarx);
  314. +
  315. + return 0;
  316. +}
  317. +
  318. +static void jh7110_tdm_clk_disable(struct jh7110_tdm_dev *tdm)
  319. +{
  320. + clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks);
  321. +}
  322. +
  323. +static int jh7110_tdm_clk_enable(struct jh7110_tdm_dev *tdm)
  324. +{
  325. + int ret;
  326. +
  327. + ret = clk_bulk_prepare_enable(ARRAY_SIZE(tdm->clks), tdm->clks);
  328. + if (ret) {
  329. + dev_err(tdm->dev, "Failed to enable tdm clocks\n");
  330. + return ret;
  331. + }
  332. +
  333. + ret = reset_control_deassert(tdm->resets);
  334. + if (ret) {
  335. + dev_err(tdm->dev, "Failed to deassert tdm resets\n");
  336. + goto dis_tdm_clk;
  337. + }
  338. +
  339. + /* select tdm_ext clock as the clock source for tdm */
  340. + ret = clk_set_parent(tdm->clks[5].clk, tdm->clks[4].clk);
  341. + if (ret) {
  342. + dev_err(tdm->dev, "Can't set extern clock source for clk_tdm\n");
  343. + goto dis_tdm_clk;
  344. + }
  345. +
  346. + return 0;
  347. +
  348. +dis_tdm_clk:
  349. + clk_bulk_disable_unprepare(ARRAY_SIZE(tdm->clks), tdm->clks);
  350. +
  351. + return ret;
  352. +}
  353. +
  354. +static int jh7110_tdm_runtime_suspend(struct device *dev)
  355. +{
  356. + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
  357. +
  358. + jh7110_tdm_clk_disable(tdm);
  359. + return 0;
  360. +}
  361. +
  362. +static int jh7110_tdm_runtime_resume(struct device *dev)
  363. +{
  364. + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
  365. +
  366. + return jh7110_tdm_clk_enable(tdm);
  367. +}
  368. +
  369. +static int jh7110_tdm_system_suspend(struct device *dev)
  370. +{
  371. + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
  372. +
  373. + /* save context */
  374. + tdm->saved_pcmgbcr = jh7110_tdm_readl(tdm, TDM_PCMGBCR);
  375. + tdm->saved_pcmdiv = jh7110_tdm_readl(tdm, TDM_PCMDIV);
  376. +
  377. + return pm_runtime_force_suspend(dev);
  378. +}
  379. +
  380. +static int jh7110_tdm_system_resume(struct device *dev)
  381. +{
  382. + struct jh7110_tdm_dev *tdm = dev_get_drvdata(dev);
  383. +
  384. + /* restore context */
  385. + jh7110_tdm_writel(tdm, TDM_PCMGBCR, tdm->saved_pcmgbcr);
  386. + jh7110_tdm_writel(tdm, TDM_PCMDIV, tdm->saved_pcmdiv);
  387. +
  388. + return pm_runtime_force_resume(dev);
  389. +}
  390. +
  391. +static const struct snd_soc_component_driver jh7110_tdm_component = {
  392. + .name = "jh7110-tdm",
  393. +};
  394. +
  395. +static int jh7110_tdm_startup(struct snd_pcm_substream *substream,
  396. + struct snd_soc_dai *cpu_dai)
  397. +{
  398. + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
  399. + struct snd_soc_dai_link *dai_link = rtd->dai_link;
  400. +
  401. + dai_link->stop_dma_first = 1;
  402. +
  403. + return 0;
  404. +}
  405. +
  406. +static int jh7110_tdm_hw_params(struct snd_pcm_substream *substream,
  407. + struct snd_pcm_hw_params *params,
  408. + struct snd_soc_dai *dai)
  409. +{
  410. + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai);
  411. + int chan_wl, chan_sl, chan_nr;
  412. + unsigned int data_width;
  413. + unsigned int dma_bus_width;
  414. + struct snd_dmaengine_dai_dma_data *dma_data = NULL;
  415. + int ret;
  416. +
  417. + data_width = params_width(params);
  418. +
  419. + tdm->samplerate = params_rate(params);
  420. + tdm->pcmclk = params_channels(params) * tdm->samplerate * data_width;
  421. +
  422. + switch (params_format(params)) {
  423. + case SNDRV_PCM_FORMAT_S16_LE:
  424. + chan_wl = TDM_16BIT_WORD_LEN;
  425. + chan_sl = TDM_16BIT_SLOT_LEN;
  426. + dma_bus_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
  427. + break;
  428. +
  429. + case SNDRV_PCM_FORMAT_S32_LE:
  430. + chan_wl = TDM_32BIT_WORD_LEN;
  431. + chan_sl = TDM_32BIT_SLOT_LEN;
  432. + dma_bus_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
  433. + break;
  434. +
  435. + default:
  436. + dev_err(tdm->dev, "tdm: unsupported PCM fmt");
  437. + return -EINVAL;
  438. + }
  439. +
  440. + chan_nr = params_channels(params);
  441. + switch (chan_nr) {
  442. + case 1:
  443. + case 2:
  444. + case 4:
  445. + case 6:
  446. + case 8:
  447. + break;
  448. + default:
  449. + dev_err(tdm->dev, "channel not supported\n");
  450. + return -EINVAL;
  451. + }
  452. +
  453. + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  454. + tdm->tx.wl = chan_wl;
  455. + tdm->tx.sl = chan_sl;
  456. + tdm->tx.sscale = chan_nr;
  457. + tdm->play_dma_data.addr_width = dma_bus_width;
  458. + dma_data = &tdm->play_dma_data;
  459. + } else {
  460. + tdm->rx.wl = chan_wl;
  461. + tdm->rx.sl = chan_sl;
  462. + tdm->rx.sscale = chan_nr;
  463. + tdm->capture_dma_data.addr_width = dma_bus_width;
  464. + dma_data = &tdm->capture_dma_data;
  465. + }
  466. +
  467. + snd_soc_dai_set_dma_data(dai, substream, dma_data);
  468. +
  469. + ret = jh7110_tdm_config(tdm, substream);
  470. + if (ret)
  471. + return ret;
  472. +
  473. + jh7110_tdm_save_context(tdm, substream);
  474. + return 0;
  475. +}
  476. +
  477. +static int jh7110_tdm_trigger(struct snd_pcm_substream *substream,
  478. + int cmd, struct snd_soc_dai *dai)
  479. +{
  480. + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai);
  481. + int ret = 0;
  482. +
  483. + switch (cmd) {
  484. + case SNDRV_PCM_TRIGGER_START:
  485. + case SNDRV_PCM_TRIGGER_RESUME:
  486. + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  487. + jh7110_tdm_start(tdm, substream);
  488. + break;
  489. +
  490. + case SNDRV_PCM_TRIGGER_STOP:
  491. + case SNDRV_PCM_TRIGGER_SUSPEND:
  492. + case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  493. + jh7110_tdm_stop(tdm, substream);
  494. + break;
  495. + default:
  496. + ret = -EINVAL;
  497. + break;
  498. + }
  499. +
  500. + return ret;
  501. +}
  502. +
  503. +static int jh7110_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai,
  504. + unsigned int fmt)
  505. +{
  506. + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(cpu_dai);
  507. + unsigned int gbcr;
  508. +
  509. + /* set master/slave audio interface */
  510. + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
  511. + case SND_SOC_DAIFMT_BP_FP:
  512. + /* cpu is master */
  513. + tdm->ms_mode = TDM_AS_MASTER;
  514. + break;
  515. + case SND_SOC_DAIFMT_BC_FC:
  516. + /* codec is master */
  517. + tdm->ms_mode = TDM_AS_SLAVE;
  518. + break;
  519. + case SND_SOC_DAIFMT_BC_FP:
  520. + case SND_SOC_DAIFMT_BP_FC:
  521. + return -EINVAL;
  522. + default:
  523. + dev_dbg(tdm->dev, "dwc : Invalid clock provider format\n");
  524. + return -EINVAL;
  525. + }
  526. +
  527. + gbcr = (tdm->clkpolity << CLKPOL_BIT) |
  528. + (tdm->elm << ELM_BIT) |
  529. + (tdm->syncm << SYNCM_BIT) |
  530. + (tdm->ms_mode << MS_BIT);
  531. + jh7110_tdm_writel(tdm, TDM_PCMGBCR, gbcr);
  532. +
  533. + return 0;
  534. +}
  535. +
  536. +static const struct snd_soc_dai_ops jh7110_tdm_dai_ops = {
  537. + .startup = jh7110_tdm_startup,
  538. + .hw_params = jh7110_tdm_hw_params,
  539. + .trigger = jh7110_tdm_trigger,
  540. + .set_fmt = jh7110_tdm_set_dai_fmt,
  541. +};
  542. +
  543. +static int jh7110_tdm_dai_probe(struct snd_soc_dai *dai)
  544. +{
  545. + struct jh7110_tdm_dev *tdm = snd_soc_dai_get_drvdata(dai);
  546. +
  547. + snd_soc_dai_init_dma_data(dai, &tdm->play_dma_data, &tdm->capture_dma_data);
  548. + snd_soc_dai_set_drvdata(dai, tdm);
  549. + return 0;
  550. +}
  551. +
  552. +#define JH7110_TDM_RATES SNDRV_PCM_RATE_8000_48000
  553. +
  554. +#define JH7110_TDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
  555. + SNDRV_PCM_FMTBIT_S32_LE)
  556. +
  557. +static struct snd_soc_dai_driver jh7110_tdm_dai = {
  558. + .name = "sf_tdm",
  559. + .id = 0,
  560. + .playback = {
  561. + .stream_name = "Playback",
  562. + .channels_min = 1,
  563. + .channels_max = 8,
  564. + .rates = JH7110_TDM_RATES,
  565. + .formats = JH7110_TDM_FORMATS,
  566. + },
  567. + .capture = {
  568. + .stream_name = "Capture",
  569. + .channels_min = 1,
  570. + .channels_max = 8,
  571. + .rates = JH7110_TDM_RATES,
  572. + .formats = JH7110_TDM_FORMATS,
  573. + },
  574. + .ops = &jh7110_tdm_dai_ops,
  575. + .probe = jh7110_tdm_dai_probe,
  576. + .symmetric_rate = 1,
  577. +};
  578. +
  579. +static const struct snd_pcm_hardware jh7110_pcm_hardware = {
  580. + .info = (SNDRV_PCM_INFO_MMAP |
  581. + SNDRV_PCM_INFO_MMAP_VALID |
  582. + SNDRV_PCM_INFO_PAUSE |
  583. + SNDRV_PCM_INFO_RESUME |
  584. + SNDRV_PCM_INFO_INTERLEAVED |
  585. + SNDRV_PCM_INFO_BLOCK_TRANSFER),
  586. + .buffer_bytes_max = 192512,
  587. + .period_bytes_min = 4096,
  588. + .period_bytes_max = 32768,
  589. + .periods_min = 1,
  590. + .periods_max = 48,
  591. + .fifo_size = 16,
  592. +};
  593. +
  594. +static const struct snd_dmaengine_pcm_config jh7110_dmaengine_pcm_config = {
  595. + .pcm_hardware = &jh7110_pcm_hardware,
  596. + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
  597. + .prealloc_buffer_size = 192512,
  598. +};
  599. +
  600. +static void jh7110_tdm_init_params(struct jh7110_tdm_dev *tdm)
  601. +{
  602. + tdm->clkpolity = TDM_TX_RASING_RX_FALLING;
  603. + tdm->elm = TDM_ELM_LATE;
  604. + tdm->syncm = TDM_SYNCM_SHORT;
  605. +
  606. + tdm->rx.ifl = TDM_FIFO_HALF;
  607. + tdm->tx.ifl = TDM_FIFO_HALF;
  608. + tdm->rx.wl = TDM_16BIT_WORD_LEN;
  609. + tdm->tx.wl = TDM_16BIT_WORD_LEN;
  610. + tdm->rx.sscale = 2;
  611. + tdm->tx.sscale = 2;
  612. + tdm->rx.lrj = TDM_LEFT_JUSTIFT;
  613. + tdm->tx.lrj = TDM_LEFT_JUSTIFT;
  614. +
  615. + tdm->play_dma_data.addr = JH7110_TDM_FIFO;
  616. + tdm->play_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
  617. + tdm->play_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2;
  618. + tdm->play_dma_data.maxburst = 16;
  619. +
  620. + tdm->capture_dma_data.addr = JH7110_TDM_FIFO;
  621. + tdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
  622. + tdm->capture_dma_data.fifo_size = JH7110_TDM_FIFO_DEPTH / 2;
  623. + tdm->capture_dma_data.maxburst = 8;
  624. +}
  625. +
  626. +static int jh7110_tdm_clk_reset_get(struct platform_device *pdev,
  627. + struct jh7110_tdm_dev *tdm)
  628. +{
  629. + int ret;
  630. +
  631. + tdm->clks[0].id = "mclk_inner";
  632. + tdm->clks[1].id = "tdm_ahb";
  633. + tdm->clks[2].id = "tdm_apb";
  634. + tdm->clks[3].id = "tdm_internal";
  635. + tdm->clks[4].id = "tdm_ext";
  636. + tdm->clks[5].id = "tdm";
  637. +
  638. + ret = devm_clk_bulk_get(&pdev->dev, ARRAY_SIZE(tdm->clks), tdm->clks);
  639. + if (ret) {
  640. + dev_err(&pdev->dev, "Failed to get tdm clocks\n");
  641. + return ret;
  642. + }
  643. +
  644. + tdm->resets = devm_reset_control_array_get_exclusive(&pdev->dev);
  645. + if (IS_ERR_OR_NULL(tdm->resets)) {
  646. + ret = PTR_ERR(tdm->resets);
  647. + dev_err(&pdev->dev, "Failed to get tdm resets");
  648. + return ret;
  649. + }
  650. +
  651. + return 0;
  652. +}
  653. +
  654. +static int jh7110_tdm_probe(struct platform_device *pdev)
  655. +{
  656. + struct jh7110_tdm_dev *tdm;
  657. + int ret;
  658. +
  659. + tdm = devm_kzalloc(&pdev->dev, sizeof(*tdm), GFP_KERNEL);
  660. + if (!tdm)
  661. + return -ENOMEM;
  662. +
  663. + tdm->tdm_base = devm_platform_ioremap_resource(pdev, 0);
  664. + if (IS_ERR(tdm->tdm_base))
  665. + return PTR_ERR(tdm->tdm_base);
  666. +
  667. + tdm->dev = &pdev->dev;
  668. +
  669. + ret = jh7110_tdm_clk_reset_get(pdev, tdm);
  670. + if (ret) {
  671. + dev_err(&pdev->dev, "Failed to enable audio-tdm clock\n");
  672. + return ret;
  673. + }
  674. +
  675. + jh7110_tdm_init_params(tdm);
  676. +
  677. + dev_set_drvdata(&pdev->dev, tdm);
  678. + ret = devm_snd_soc_register_component(&pdev->dev, &jh7110_tdm_component,
  679. + &jh7110_tdm_dai, 1);
  680. + if (ret) {
  681. + dev_err(&pdev->dev, "Failed to register dai\n");
  682. + return ret;
  683. + }
  684. +
  685. + ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
  686. + &jh7110_dmaengine_pcm_config,
  687. + SND_DMAENGINE_PCM_FLAG_COMPAT);
  688. + if (ret) {
  689. + dev_err(&pdev->dev, "Could not register pcm: %d\n", ret);
  690. + return ret;
  691. + }
  692. +
  693. + pm_runtime_enable(&pdev->dev);
  694. + if (!pm_runtime_enabled(&pdev->dev)) {
  695. + ret = jh7110_tdm_runtime_resume(&pdev->dev);
  696. + if (ret)
  697. + goto err_pm_disable;
  698. + }
  699. +
  700. + return 0;
  701. +
  702. +err_pm_disable:
  703. + pm_runtime_disable(&pdev->dev);
  704. +
  705. + return ret;
  706. +}
  707. +
  708. +static int jh7110_tdm_dev_remove(struct platform_device *pdev)
  709. +{
  710. + pm_runtime_disable(&pdev->dev);
  711. + return 0;
  712. +}
  713. +
  714. +static const struct of_device_id jh7110_tdm_of_match[] = {
  715. + { .compatible = "starfive,jh7110-tdm", },
  716. + {}
  717. +};
  718. +
  719. +MODULE_DEVICE_TABLE(of, jh7110_tdm_of_match);
  720. +
  721. +static const struct dev_pm_ops jh7110_tdm_pm_ops = {
  722. + RUNTIME_PM_OPS(jh7110_tdm_runtime_suspend,
  723. + jh7110_tdm_runtime_resume, NULL)
  724. + SYSTEM_SLEEP_PM_OPS(jh7110_tdm_system_suspend,
  725. + jh7110_tdm_system_resume)
  726. +};
  727. +
  728. +static struct platform_driver jh7110_tdm_driver = {
  729. + .driver = {
  730. + .name = "jh7110-tdm",
  731. + .of_match_table = jh7110_tdm_of_match,
  732. + .pm = pm_ptr(&jh7110_tdm_pm_ops),
  733. + },
  734. + .probe = jh7110_tdm_probe,
  735. + .remove = jh7110_tdm_dev_remove,
  736. +};
  737. +module_platform_driver(jh7110_tdm_driver);
  738. +
  739. +MODULE_DESCRIPTION("StarFive JH7110 TDM ASoC Driver");
  740. +MODULE_AUTHOR("Walker Chen <[email protected]>");
  741. +MODULE_LICENSE("GPL");