mxc-ssi.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. /*
  2. * mxc-ssi.c -- SSI driver for Freescale IMX
  3. *
  4. * Copyright 2006 Wolfson Microelectronics PLC.
  5. * Author: Liam Girdwood
  6. * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
  7. *
  8. * Based on mxc-alsa-mc13783 (C) 2006 Freescale.
  9. *
  10. * This program is free software; you can redistribute it and/or modify it
  11. * under the terms of the GNU General Public License as published by the
  12. * Free Software Foundation; either version 2 of the License, or (at your
  13. * option) any later version.
  14. *
  15. * TODO:
  16. * Need to rework SSI register defs when new defs go into mainline.
  17. * Add support for TDM and FIFO 1.
  18. * Add support for i.mx3x DMA interface.
  19. *
  20. */
  21. #include <linux/module.h>
  22. #include <linux/init.h>
  23. #include <linux/platform_device.h>
  24. #include <linux/slab.h>
  25. #include <linux/dma-mapping.h>
  26. #include <linux/clk.h>
  27. #include <sound/core.h>
  28. #include <sound/pcm.h>
  29. #include <sound/pcm_params.h>
  30. #include <sound/soc.h>
  31. #include <mach/dma-mx1-mx2.h>
  32. #include <asm/mach-types.h>
  33. #include "mxc-ssi.h"
  34. #include "mx1_mx2-pcm.h"
  35. #define SSI1_PORT 0
  36. #define SSI2_PORT 1
  37. static int ssi_active[2] = {0, 0};
  38. /* DMA information for mx1_mx2 platforms */
  39. static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out0 = {
  40. .name = "SSI1 PCM Stereo out 0",
  41. .transfer_type = DMA_MODE_WRITE,
  42. .per_address = SSI1_BASE_ADDR + STX0,
  43. .event_id = DMA_REQ_SSI1_TX0,
  44. .watermark_level = TXFIFO_WATERMARK,
  45. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  46. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  47. };
  48. static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_out1 = {
  49. .name = "SSI1 PCM Stereo out 1",
  50. .transfer_type = DMA_MODE_WRITE,
  51. .per_address = SSI1_BASE_ADDR + STX1,
  52. .event_id = DMA_REQ_SSI1_TX1,
  53. .watermark_level = TXFIFO_WATERMARK,
  54. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  55. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  56. };
  57. static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in0 = {
  58. .name = "SSI1 PCM Stereo in 0",
  59. .transfer_type = DMA_MODE_READ,
  60. .per_address = SSI1_BASE_ADDR + SRX0,
  61. .event_id = DMA_REQ_SSI1_RX0,
  62. .watermark_level = RXFIFO_WATERMARK,
  63. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  64. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  65. };
  66. static struct mx1_mx2_pcm_dma_params imx_ssi1_pcm_stereo_in1 = {
  67. .name = "SSI1 PCM Stereo in 1",
  68. .transfer_type = DMA_MODE_READ,
  69. .per_address = SSI1_BASE_ADDR + SRX1,
  70. .event_id = DMA_REQ_SSI1_RX1,
  71. .watermark_level = RXFIFO_WATERMARK,
  72. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  73. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  74. };
  75. static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out0 = {
  76. .name = "SSI2 PCM Stereo out 0",
  77. .transfer_type = DMA_MODE_WRITE,
  78. .per_address = SSI2_BASE_ADDR + STX0,
  79. .event_id = DMA_REQ_SSI2_TX0,
  80. .watermark_level = TXFIFO_WATERMARK,
  81. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  82. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  83. };
  84. static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_out1 = {
  85. .name = "SSI2 PCM Stereo out 1",
  86. .transfer_type = DMA_MODE_WRITE,
  87. .per_address = SSI2_BASE_ADDR + STX1,
  88. .event_id = DMA_REQ_SSI2_TX1,
  89. .watermark_level = TXFIFO_WATERMARK,
  90. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  91. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  92. };
  93. static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in0 = {
  94. .name = "SSI2 PCM Stereo in 0",
  95. .transfer_type = DMA_MODE_READ,
  96. .per_address = SSI2_BASE_ADDR + SRX0,
  97. .event_id = DMA_REQ_SSI2_RX0,
  98. .watermark_level = RXFIFO_WATERMARK,
  99. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  100. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  101. };
  102. static struct mx1_mx2_pcm_dma_params imx_ssi2_pcm_stereo_in1 = {
  103. .name = "SSI2 PCM Stereo in 1",
  104. .transfer_type = DMA_MODE_READ,
  105. .per_address = SSI2_BASE_ADDR + SRX1,
  106. .event_id = DMA_REQ_SSI2_RX1,
  107. .watermark_level = RXFIFO_WATERMARK,
  108. .per_config = IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO,
  109. .mem_config = IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR,
  110. };
  111. static struct clk *ssi_clk0, *ssi_clk1;
  112. int get_ssi_clk(int ssi, struct device *dev)
  113. {
  114. switch (ssi) {
  115. case 0:
  116. ssi_clk0 = clk_get(dev, "ssi1");
  117. if (IS_ERR(ssi_clk0))
  118. return PTR_ERR(ssi_clk0);
  119. return 0;
  120. case 1:
  121. ssi_clk1 = clk_get(dev, "ssi2");
  122. if (IS_ERR(ssi_clk1))
  123. return PTR_ERR(ssi_clk1);
  124. return 0;
  125. default:
  126. return -EINVAL;
  127. }
  128. }
  129. EXPORT_SYMBOL(get_ssi_clk);
  130. void put_ssi_clk(int ssi)
  131. {
  132. switch (ssi) {
  133. case 0:
  134. clk_put(ssi_clk0);
  135. ssi_clk0 = NULL;
  136. break;
  137. case 1:
  138. clk_put(ssi_clk1);
  139. ssi_clk1 = NULL;
  140. break;
  141. }
  142. }
  143. EXPORT_SYMBOL(put_ssi_clk);
  144. /*
  145. * SSI system clock configuration.
  146. * Should only be called when port is inactive (i.e. SSIEN = 0).
  147. */
  148. static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
  149. int clk_id, unsigned int freq, int dir)
  150. {
  151. u32 scr;
  152. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  153. scr = SSI1_SCR;
  154. pr_debug("%s: SCR for SSI1 is %x\n", __func__, scr);
  155. } else {
  156. scr = SSI2_SCR;
  157. pr_debug("%s: SCR for SSI2 is %x\n", __func__, scr);
  158. }
  159. if (scr & SSI_SCR_SSIEN) {
  160. printk(KERN_WARNING "Warning ssi already enabled\n");
  161. return 0;
  162. }
  163. switch (clk_id) {
  164. case IMX_SSP_SYS_CLK:
  165. if (dir == SND_SOC_CLOCK_OUT) {
  166. scr |= SSI_SCR_SYS_CLK_EN;
  167. pr_debug("%s: clk of is output\n", __func__);
  168. } else {
  169. scr &= ~SSI_SCR_SYS_CLK_EN;
  170. pr_debug("%s: clk of is input\n", __func__);
  171. }
  172. break;
  173. default:
  174. return -EINVAL;
  175. }
  176. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  177. pr_debug("%s: writeback of SSI1_SCR\n", __func__);
  178. SSI1_SCR = scr;
  179. } else {
  180. pr_debug("%s: writeback of SSI2_SCR\n", __func__);
  181. SSI2_SCR = scr;
  182. }
  183. return 0;
  184. }
  185. /*
  186. * SSI Clock dividers
  187. * Should only be called when port is inactive (i.e. SSIEN = 0).
  188. */
  189. static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
  190. int div_id, int div)
  191. {
  192. u32 stccr, srccr;
  193. pr_debug("%s\n", __func__);
  194. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  195. if (SSI1_SCR & SSI_SCR_SSIEN)
  196. return 0;
  197. srccr = SSI1_STCCR;
  198. stccr = SSI1_STCCR;
  199. } else {
  200. if (SSI2_SCR & SSI_SCR_SSIEN)
  201. return 0;
  202. srccr = SSI2_STCCR;
  203. stccr = SSI2_STCCR;
  204. }
  205. switch (div_id) {
  206. case IMX_SSI_TX_DIV_2:
  207. stccr &= ~SSI_STCCR_DIV2;
  208. stccr |= div;
  209. break;
  210. case IMX_SSI_TX_DIV_PSR:
  211. stccr &= ~SSI_STCCR_PSR;
  212. stccr |= div;
  213. break;
  214. case IMX_SSI_TX_DIV_PM:
  215. stccr &= ~0xff;
  216. stccr |= SSI_STCCR_PM(div);
  217. break;
  218. case IMX_SSI_RX_DIV_2:
  219. stccr &= ~SSI_STCCR_DIV2;
  220. stccr |= div;
  221. break;
  222. case IMX_SSI_RX_DIV_PSR:
  223. stccr &= ~SSI_STCCR_PSR;
  224. stccr |= div;
  225. break;
  226. case IMX_SSI_RX_DIV_PM:
  227. stccr &= ~0xff;
  228. stccr |= SSI_STCCR_PM(div);
  229. break;
  230. default:
  231. return -EINVAL;
  232. }
  233. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  234. SSI1_STCCR = stccr;
  235. SSI1_SRCCR = srccr;
  236. } else {
  237. SSI2_STCCR = stccr;
  238. SSI2_SRCCR = srccr;
  239. }
  240. return 0;
  241. }
  242. /*
  243. * SSI Network Mode or TDM slots configuration.
  244. * Should only be called when port is inactive (i.e. SSIEN = 0).
  245. */
  246. static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai,
  247. unsigned int mask, int slots)
  248. {
  249. u32 stmsk, srmsk, stccr;
  250. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  251. if (SSI1_SCR & SSI_SCR_SSIEN) {
  252. printk(KERN_WARNING "Warning ssi already enabled\n");
  253. return 0;
  254. }
  255. stccr = SSI1_STCCR;
  256. } else {
  257. if (SSI2_SCR & SSI_SCR_SSIEN) {
  258. printk(KERN_WARNING "Warning ssi already enabled\n");
  259. return 0;
  260. }
  261. stccr = SSI2_STCCR;
  262. }
  263. stmsk = srmsk = mask;
  264. stccr &= ~SSI_STCCR_DC_MASK;
  265. stccr |= SSI_STCCR_DC(slots - 1);
  266. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  267. SSI1_STMSK = stmsk;
  268. SSI1_SRMSK = srmsk;
  269. SSI1_SRCCR = SSI1_STCCR = stccr;
  270. } else {
  271. SSI2_STMSK = stmsk;
  272. SSI2_SRMSK = srmsk;
  273. SSI2_SRCCR = SSI2_STCCR = stccr;
  274. }
  275. return 0;
  276. }
  277. /*
  278. * SSI DAI format configuration.
  279. * Should only be called when port is inactive (i.e. SSIEN = 0).
  280. * Note: We don't use the I2S modes but instead manually configure the
  281. * SSI for I2S.
  282. */
  283. static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai,
  284. unsigned int fmt)
  285. {
  286. u32 stcr = 0, srcr = 0, scr;
  287. /*
  288. * This is done to avoid this function to modify
  289. * previous set values in stcr
  290. */
  291. stcr = SSI1_STCR;
  292. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  293. scr = SSI1_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
  294. else
  295. scr = SSI2_SCR & ~(SSI_SCR_SYN | SSI_SCR_NET);
  296. if (scr & SSI_SCR_SSIEN) {
  297. printk(KERN_WARNING "Warning ssi already enabled\n");
  298. return 0;
  299. }
  300. /* DAI mode */
  301. switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
  302. case SND_SOC_DAIFMT_I2S:
  303. /* data on rising edge of bclk, frame low 1clk before data */
  304. stcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0;
  305. srcr |= SSI_SRCR_RFSI | SSI_SRCR_REFS | SSI_SRCR_RXBIT0;
  306. break;
  307. case SND_SOC_DAIFMT_LEFT_J:
  308. /* data on rising edge of bclk, frame high with data */
  309. stcr |= SSI_STCR_TXBIT0;
  310. srcr |= SSI_SRCR_RXBIT0;
  311. break;
  312. case SND_SOC_DAIFMT_DSP_B:
  313. /* data on rising edge of bclk, frame high with data */
  314. stcr |= SSI_STCR_TFSL;
  315. srcr |= SSI_SRCR_RFSL;
  316. break;
  317. case SND_SOC_DAIFMT_DSP_A:
  318. /* data on rising edge of bclk, frame high 1clk before data */
  319. stcr |= SSI_STCR_TFSL | SSI_STCR_TEFS;
  320. srcr |= SSI_SRCR_RFSL | SSI_SRCR_REFS;
  321. break;
  322. }
  323. /* DAI clock inversion */
  324. switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
  325. case SND_SOC_DAIFMT_IB_IF:
  326. stcr |= SSI_STCR_TFSI;
  327. stcr &= ~SSI_STCR_TSCKP;
  328. srcr |= SSI_SRCR_RFSI;
  329. srcr &= ~SSI_SRCR_RSCKP;
  330. break;
  331. case SND_SOC_DAIFMT_IB_NF:
  332. stcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI);
  333. srcr &= ~(SSI_SRCR_RSCKP | SSI_SRCR_RFSI);
  334. break;
  335. case SND_SOC_DAIFMT_NB_IF:
  336. stcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP;
  337. srcr |= SSI_SRCR_RFSI | SSI_SRCR_RSCKP;
  338. break;
  339. case SND_SOC_DAIFMT_NB_NF:
  340. stcr &= ~SSI_STCR_TFSI;
  341. stcr |= SSI_STCR_TSCKP;
  342. srcr &= ~SSI_SRCR_RFSI;
  343. srcr |= SSI_SRCR_RSCKP;
  344. break;
  345. }
  346. /* DAI clock master masks */
  347. switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
  348. case SND_SOC_DAIFMT_CBS_CFS:
  349. stcr |= SSI_STCR_TFDIR | SSI_STCR_TXDIR;
  350. srcr |= SSI_SRCR_RFDIR | SSI_SRCR_RXDIR;
  351. break;
  352. case SND_SOC_DAIFMT_CBM_CFS:
  353. stcr |= SSI_STCR_TFDIR;
  354. srcr |= SSI_SRCR_RFDIR;
  355. break;
  356. case SND_SOC_DAIFMT_CBS_CFM:
  357. stcr |= SSI_STCR_TXDIR;
  358. srcr |= SSI_SRCR_RXDIR;
  359. break;
  360. }
  361. /* sync */
  362. if (!(fmt & SND_SOC_DAIFMT_ASYNC))
  363. scr |= SSI_SCR_SYN;
  364. /* tdm - only for stereo atm */
  365. if (fmt & SND_SOC_DAIFMT_TDM)
  366. scr |= SSI_SCR_NET;
  367. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  368. SSI1_STCR = stcr;
  369. SSI1_SRCR = srcr;
  370. SSI1_SCR = scr;
  371. } else {
  372. SSI2_STCR = stcr;
  373. SSI2_SRCR = srcr;
  374. SSI2_SCR = scr;
  375. }
  376. return 0;
  377. }
  378. static int imx_ssi_startup(struct snd_pcm_substream *substream,
  379. struct snd_soc_dai *dai)
  380. {
  381. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  382. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  383. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  384. /* set up TX DMA params */
  385. switch (cpu_dai->id) {
  386. case IMX_DAI_SSI0:
  387. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
  388. break;
  389. case IMX_DAI_SSI1:
  390. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
  391. break;
  392. case IMX_DAI_SSI2:
  393. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
  394. break;
  395. case IMX_DAI_SSI3:
  396. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
  397. }
  398. pr_debug("%s: (playback)\n", __func__);
  399. } else {
  400. /* set up RX DMA params */
  401. switch (cpu_dai->id) {
  402. case IMX_DAI_SSI0:
  403. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
  404. break;
  405. case IMX_DAI_SSI1:
  406. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
  407. break;
  408. case IMX_DAI_SSI2:
  409. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
  410. break;
  411. case IMX_DAI_SSI3:
  412. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
  413. }
  414. pr_debug("%s: (capture)\n", __func__);
  415. }
  416. /*
  417. * we cant really change any SSI values after SSI is enabled
  418. * need to fix in software for max flexibility - lrg
  419. */
  420. if (cpu_dai->active) {
  421. printk(KERN_WARNING "Warning ssi already enabled\n");
  422. return 0;
  423. }
  424. /* reset the SSI port - Sect 45.4.4 */
  425. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  426. if (!ssi_clk0)
  427. return -EINVAL;
  428. if (ssi_active[SSI1_PORT]++) {
  429. pr_debug("%s: exit before reset\n", __func__);
  430. return 0;
  431. }
  432. /* SSI1 Reset */
  433. SSI1_SCR = 0;
  434. SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
  435. SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
  436. SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
  437. SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
  438. } else {
  439. if (!ssi_clk1)
  440. return -EINVAL;
  441. if (ssi_active[SSI2_PORT]++) {
  442. pr_debug("%s: exit before reset\n", __func__);
  443. return 0;
  444. }
  445. /* SSI2 Reset */
  446. SSI2_SCR = 0;
  447. SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
  448. SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
  449. SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
  450. SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
  451. }
  452. return 0;
  453. }
  454. int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
  455. struct snd_pcm_hw_params *params)
  456. {
  457. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  458. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  459. u32 stccr, stcr, sier;
  460. pr_debug("%s\n", __func__);
  461. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  462. stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
  463. stcr = SSI1_STCR;
  464. sier = SSI1_SIER;
  465. } else {
  466. stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
  467. stcr = SSI2_STCR;
  468. sier = SSI2_SIER;
  469. }
  470. /* DAI data (word) size */
  471. switch (params_format(params)) {
  472. case SNDRV_PCM_FORMAT_S16_LE:
  473. stccr |= SSI_STCCR_WL(16);
  474. break;
  475. case SNDRV_PCM_FORMAT_S20_3LE:
  476. stccr |= SSI_STCCR_WL(20);
  477. break;
  478. case SNDRV_PCM_FORMAT_S24_LE:
  479. stccr |= SSI_STCCR_WL(24);
  480. break;
  481. }
  482. /* enable interrupts */
  483. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  484. stcr |= SSI_STCR_TFEN0;
  485. else
  486. stcr |= SSI_STCR_TFEN1;
  487. sier |= SSI_SIER_TDMAE;
  488. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  489. SSI1_STCR = stcr;
  490. SSI1_STCCR = stccr;
  491. SSI1_SIER = sier;
  492. } else {
  493. SSI2_STCR = stcr;
  494. SSI2_STCCR = stccr;
  495. SSI2_SIER = sier;
  496. }
  497. return 0;
  498. }
  499. int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
  500. struct snd_pcm_hw_params *params)
  501. {
  502. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  503. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  504. u32 srccr, srcr, sier;
  505. pr_debug("%s\n", __func__);
  506. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  507. srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
  508. srcr = SSI1_SRCR;
  509. sier = SSI1_SIER;
  510. } else {
  511. srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
  512. srcr = SSI2_SRCR;
  513. sier = SSI2_SIER;
  514. }
  515. /* DAI data (word) size */
  516. switch (params_format(params)) {
  517. case SNDRV_PCM_FORMAT_S16_LE:
  518. srccr |= SSI_SRCCR_WL(16);
  519. break;
  520. case SNDRV_PCM_FORMAT_S20_3LE:
  521. srccr |= SSI_SRCCR_WL(20);
  522. break;
  523. case SNDRV_PCM_FORMAT_S24_LE:
  524. srccr |= SSI_SRCCR_WL(24);
  525. break;
  526. }
  527. /* enable interrupts */
  528. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  529. srcr |= SSI_SRCR_RFEN0;
  530. else
  531. srcr |= SSI_SRCR_RFEN1;
  532. sier |= SSI_SIER_RDMAE;
  533. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  534. SSI1_SRCR = srcr;
  535. SSI1_SRCCR = srccr;
  536. SSI1_SIER = sier;
  537. } else {
  538. SSI2_SRCR = srcr;
  539. SSI2_SRCCR = srccr;
  540. SSI2_SIER = sier;
  541. }
  542. return 0;
  543. }
  544. /*
  545. * Should only be called when port is inactive (i.e. SSIEN = 0),
  546. * although can be called multiple times by upper layers.
  547. */
  548. int imx_ssi_hw_params(struct snd_pcm_substream *substream,
  549. struct snd_pcm_hw_params *params,
  550. struct snd_soc_dai *dai)
  551. {
  552. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  553. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  554. int ret;
  555. /* cant change any parameters when SSI is running */
  556. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  557. if (SSI1_SCR & SSI_SCR_SSIEN) {
  558. printk(KERN_WARNING "Warning ssi already enabled\n");
  559. return 0;
  560. }
  561. } else {
  562. if (SSI2_SCR & SSI_SCR_SSIEN) {
  563. printk(KERN_WARNING "Warning ssi already enabled\n");
  564. return 0;
  565. }
  566. }
  567. /*
  568. * Configure both tx and rx params with the same settings. This is
  569. * really a harware restriction because SSI must be disabled until
  570. * we can change those values. If there is an active audio stream in
  571. * one direction, enabling the other direction with different
  572. * settings would mean disturbing the running one.
  573. */
  574. ret = imx_ssi_hw_tx_params(substream, params);
  575. if (ret < 0)
  576. return ret;
  577. return imx_ssi_hw_rx_params(substream, params);
  578. }
  579. int imx_ssi_prepare(struct snd_pcm_substream *substream,
  580. struct snd_soc_dai *dai)
  581. {
  582. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  583. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  584. int ret;
  585. pr_debug("%s\n", __func__);
  586. /* Enable clks here to follow SSI recommended init sequence */
  587. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  588. ret = clk_enable(ssi_clk0);
  589. if (ret < 0)
  590. printk(KERN_ERR "Unable to enable ssi_clk0\n");
  591. } else {
  592. ret = clk_enable(ssi_clk1);
  593. if (ret < 0)
  594. printk(KERN_ERR "Unable to enable ssi_clk1\n");
  595. }
  596. return 0;
  597. }
  598. static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
  599. struct snd_soc_dai *dai)
  600. {
  601. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  602. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  603. u32 scr;
  604. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  605. scr = SSI1_SCR;
  606. else
  607. scr = SSI2_SCR;
  608. switch (cmd) {
  609. case SNDRV_PCM_TRIGGER_START:
  610. case SNDRV_PCM_TRIGGER_RESUME:
  611. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  612. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  613. scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
  614. else
  615. scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
  616. break;
  617. case SNDRV_PCM_TRIGGER_SUSPEND:
  618. case SNDRV_PCM_TRIGGER_STOP:
  619. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  620. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  621. scr &= ~SSI_SCR_TE;
  622. else
  623. scr &= ~SSI_SCR_RE;
  624. break;
  625. default:
  626. return -EINVAL;
  627. }
  628. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  629. SSI1_SCR = scr;
  630. else
  631. SSI2_SCR = scr;
  632. return 0;
  633. }
  634. static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
  635. struct snd_soc_dai *dai)
  636. {
  637. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  638. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  639. /* shutdown SSI if neither Tx or Rx is active */
  640. if (!cpu_dai->active) {
  641. if (cpu_dai->id == IMX_DAI_SSI0 ||
  642. cpu_dai->id == IMX_DAI_SSI2) {
  643. if (--ssi_active[SSI1_PORT] > 1)
  644. return;
  645. SSI1_SCR = 0;
  646. clk_disable(ssi_clk0);
  647. } else {
  648. if (--ssi_active[SSI2_PORT])
  649. return;
  650. SSI2_SCR = 0;
  651. clk_disable(ssi_clk1);
  652. }
  653. }
  654. }
  655. #ifdef CONFIG_PM
  656. static int imx_ssi_suspend(struct platform_device *dev,
  657. struct snd_soc_dai *dai)
  658. {
  659. return 0;
  660. }
  661. static int imx_ssi_resume(struct platform_device *pdev,
  662. struct snd_soc_dai *dai)
  663. {
  664. return 0;
  665. }
  666. #else
  667. #define imx_ssi_suspend NULL
  668. #define imx_ssi_resume NULL
  669. #endif
  670. #define IMX_SSI_RATES \
  671. (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
  672. SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
  673. SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
  674. SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
  675. SNDRV_PCM_RATE_96000)
  676. #define IMX_SSI_BITS \
  677. (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
  678. SNDRV_PCM_FMTBIT_S24_LE)
  679. static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
  680. .startup = imx_ssi_startup,
  681. .shutdown = imx_ssi_shutdown,
  682. .trigger = imx_ssi_trigger,
  683. .prepare = imx_ssi_prepare,
  684. .hw_params = imx_ssi_hw_params,
  685. .set_sysclk = imx_ssi_set_dai_sysclk,
  686. .set_clkdiv = imx_ssi_set_dai_clkdiv,
  687. .set_fmt = imx_ssi_set_dai_fmt,
  688. .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
  689. };
  690. struct snd_soc_dai imx_ssi_pcm_dai[] = {
  691. {
  692. .name = "imx-i2s-1-0",
  693. .id = IMX_DAI_SSI0,
  694. .suspend = imx_ssi_suspend,
  695. .resume = imx_ssi_resume,
  696. .playback = {
  697. .channels_min = 1,
  698. .channels_max = 2,
  699. .formats = IMX_SSI_BITS,
  700. .rates = IMX_SSI_RATES,},
  701. .capture = {
  702. .channels_min = 1,
  703. .channels_max = 2,
  704. .formats = IMX_SSI_BITS,
  705. .rates = IMX_SSI_RATES,},
  706. .ops = &imx_ssi_pcm_dai_ops,
  707. },
  708. {
  709. .name = "imx-i2s-2-0",
  710. .id = IMX_DAI_SSI1,
  711. .playback = {
  712. .channels_min = 1,
  713. .channels_max = 2,
  714. .formats = IMX_SSI_BITS,
  715. .rates = IMX_SSI_RATES,},
  716. .capture = {
  717. .channels_min = 1,
  718. .channels_max = 2,
  719. .formats = IMX_SSI_BITS,
  720. .rates = IMX_SSI_RATES,},
  721. .ops = &imx_ssi_pcm_dai_ops,
  722. },
  723. {
  724. .name = "imx-i2s-1-1",
  725. .id = IMX_DAI_SSI2,
  726. .suspend = imx_ssi_suspend,
  727. .resume = imx_ssi_resume,
  728. .playback = {
  729. .channels_min = 1,
  730. .channels_max = 2,
  731. .formats = IMX_SSI_BITS,
  732. .rates = IMX_SSI_RATES,},
  733. .capture = {
  734. .channels_min = 1,
  735. .channels_max = 2,
  736. .formats = IMX_SSI_BITS,
  737. .rates = IMX_SSI_RATES,},
  738. .ops = &imx_ssi_pcm_dai_ops,
  739. },
  740. {
  741. .name = "imx-i2s-2-1",
  742. .id = IMX_DAI_SSI3,
  743. .playback = {
  744. .channels_min = 1,
  745. .channels_max = 2,
  746. .formats = IMX_SSI_BITS,
  747. .rates = IMX_SSI_RATES,},
  748. .capture = {
  749. .channels_min = 1,
  750. .channels_max = 2,
  751. .formats = IMX_SSI_BITS,
  752. .rates = IMX_SSI_RATES,},
  753. .ops = &imx_ssi_pcm_dai_ops,
  754. },
  755. };
  756. EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
  757. static int __init imx_ssi_init(void)
  758. {
  759. return snd_soc_register_dais(imx_ssi_pcm_dai,
  760. ARRAY_SIZE(imx_ssi_pcm_dai));
  761. }
  762. static void __exit imx_ssi_exit(void)
  763. {
  764. snd_soc_unregister_dais(imx_ssi_pcm_dai,
  765. ARRAY_SIZE(imx_ssi_pcm_dai));
  766. }
  767. module_init(imx_ssi_init);
  768. module_exit(imx_ssi_exit);
  769. MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
  770. MODULE_DESCRIPTION("i.MX ASoC I2S driver");
  771. MODULE_LICENSE("GPL");