/kernel/2.6.32_froyo_photon_nightly/sound/soc/imx/mxc-ssi.c

http://photon-android.googlecode.com/ · C · 860 lines · 688 code · 95 blank · 77 comment · 137 complexity · 1a647d354a39158a68f8112ed84e5f28 MD5 · raw file

  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. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  362. SSI1_STCR = stcr;
  363. SSI1_SRCR = srcr;
  364. SSI1_SCR = scr;
  365. } else {
  366. SSI2_STCR = stcr;
  367. SSI2_SRCR = srcr;
  368. SSI2_SCR = scr;
  369. }
  370. return 0;
  371. }
  372. static int imx_ssi_startup(struct snd_pcm_substream *substream,
  373. struct snd_soc_dai *dai)
  374. {
  375. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  376. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  377. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  378. /* set up TX DMA params */
  379. switch (cpu_dai->id) {
  380. case IMX_DAI_SSI0:
  381. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out0;
  382. break;
  383. case IMX_DAI_SSI1:
  384. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_out1;
  385. break;
  386. case IMX_DAI_SSI2:
  387. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out0;
  388. break;
  389. case IMX_DAI_SSI3:
  390. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_out1;
  391. }
  392. pr_debug("%s: (playback)\n", __func__);
  393. } else {
  394. /* set up RX DMA params */
  395. switch (cpu_dai->id) {
  396. case IMX_DAI_SSI0:
  397. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in0;
  398. break;
  399. case IMX_DAI_SSI1:
  400. cpu_dai->dma_data = &imx_ssi1_pcm_stereo_in1;
  401. break;
  402. case IMX_DAI_SSI2:
  403. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in0;
  404. break;
  405. case IMX_DAI_SSI3:
  406. cpu_dai->dma_data = &imx_ssi2_pcm_stereo_in1;
  407. }
  408. pr_debug("%s: (capture)\n", __func__);
  409. }
  410. /*
  411. * we cant really change any SSI values after SSI is enabled
  412. * need to fix in software for max flexibility - lrg
  413. */
  414. if (cpu_dai->active) {
  415. printk(KERN_WARNING "Warning ssi already enabled\n");
  416. return 0;
  417. }
  418. /* reset the SSI port - Sect 45.4.4 */
  419. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  420. if (!ssi_clk0)
  421. return -EINVAL;
  422. if (ssi_active[SSI1_PORT]++) {
  423. pr_debug("%s: exit before reset\n", __func__);
  424. return 0;
  425. }
  426. /* SSI1 Reset */
  427. SSI1_SCR = 0;
  428. SSI1_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
  429. SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
  430. SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
  431. SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
  432. } else {
  433. if (!ssi_clk1)
  434. return -EINVAL;
  435. if (ssi_active[SSI2_PORT]++) {
  436. pr_debug("%s: exit before reset\n", __func__);
  437. return 0;
  438. }
  439. /* SSI2 Reset */
  440. SSI2_SCR = 0;
  441. SSI2_SFCSR = SSI_SFCSR_RFWM1(RXFIFO_WATERMARK) |
  442. SSI_SFCSR_RFWM0(RXFIFO_WATERMARK) |
  443. SSI_SFCSR_TFWM1(TXFIFO_WATERMARK) |
  444. SSI_SFCSR_TFWM0(TXFIFO_WATERMARK);
  445. }
  446. return 0;
  447. }
  448. int imx_ssi_hw_tx_params(struct snd_pcm_substream *substream,
  449. struct snd_pcm_hw_params *params)
  450. {
  451. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  452. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  453. u32 stccr, stcr, sier;
  454. pr_debug("%s\n", __func__);
  455. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  456. stccr = SSI1_STCCR & ~SSI_STCCR_WL_MASK;
  457. stcr = SSI1_STCR;
  458. sier = SSI1_SIER;
  459. } else {
  460. stccr = SSI2_STCCR & ~SSI_STCCR_WL_MASK;
  461. stcr = SSI2_STCR;
  462. sier = SSI2_SIER;
  463. }
  464. /* DAI data (word) size */
  465. switch (params_format(params)) {
  466. case SNDRV_PCM_FORMAT_S16_LE:
  467. stccr |= SSI_STCCR_WL(16);
  468. break;
  469. case SNDRV_PCM_FORMAT_S20_3LE:
  470. stccr |= SSI_STCCR_WL(20);
  471. break;
  472. case SNDRV_PCM_FORMAT_S24_LE:
  473. stccr |= SSI_STCCR_WL(24);
  474. break;
  475. }
  476. /* enable interrupts */
  477. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  478. stcr |= SSI_STCR_TFEN0;
  479. else
  480. stcr |= SSI_STCR_TFEN1;
  481. sier |= SSI_SIER_TDMAE;
  482. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  483. SSI1_STCR = stcr;
  484. SSI1_STCCR = stccr;
  485. SSI1_SIER = sier;
  486. } else {
  487. SSI2_STCR = stcr;
  488. SSI2_STCCR = stccr;
  489. SSI2_SIER = sier;
  490. }
  491. return 0;
  492. }
  493. int imx_ssi_hw_rx_params(struct snd_pcm_substream *substream,
  494. struct snd_pcm_hw_params *params)
  495. {
  496. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  497. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  498. u32 srccr, srcr, sier;
  499. pr_debug("%s\n", __func__);
  500. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  501. srccr = SSI1_SRCCR & ~SSI_SRCCR_WL_MASK;
  502. srcr = SSI1_SRCR;
  503. sier = SSI1_SIER;
  504. } else {
  505. srccr = SSI2_SRCCR & ~SSI_SRCCR_WL_MASK;
  506. srcr = SSI2_SRCR;
  507. sier = SSI2_SIER;
  508. }
  509. /* DAI data (word) size */
  510. switch (params_format(params)) {
  511. case SNDRV_PCM_FORMAT_S16_LE:
  512. srccr |= SSI_SRCCR_WL(16);
  513. break;
  514. case SNDRV_PCM_FORMAT_S20_3LE:
  515. srccr |= SSI_SRCCR_WL(20);
  516. break;
  517. case SNDRV_PCM_FORMAT_S24_LE:
  518. srccr |= SSI_SRCCR_WL(24);
  519. break;
  520. }
  521. /* enable interrupts */
  522. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  523. srcr |= SSI_SRCR_RFEN0;
  524. else
  525. srcr |= SSI_SRCR_RFEN1;
  526. sier |= SSI_SIER_RDMAE;
  527. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  528. SSI1_SRCR = srcr;
  529. SSI1_SRCCR = srccr;
  530. SSI1_SIER = sier;
  531. } else {
  532. SSI2_SRCR = srcr;
  533. SSI2_SRCCR = srccr;
  534. SSI2_SIER = sier;
  535. }
  536. return 0;
  537. }
  538. /*
  539. * Should only be called when port is inactive (i.e. SSIEN = 0),
  540. * although can be called multiple times by upper layers.
  541. */
  542. int imx_ssi_hw_params(struct snd_pcm_substream *substream,
  543. struct snd_pcm_hw_params *params,
  544. struct snd_soc_dai *dai)
  545. {
  546. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  547. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  548. int ret;
  549. /* cant change any parameters when SSI is running */
  550. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  551. if (SSI1_SCR & SSI_SCR_SSIEN) {
  552. printk(KERN_WARNING "Warning ssi already enabled\n");
  553. return 0;
  554. }
  555. } else {
  556. if (SSI2_SCR & SSI_SCR_SSIEN) {
  557. printk(KERN_WARNING "Warning ssi already enabled\n");
  558. return 0;
  559. }
  560. }
  561. /*
  562. * Configure both tx and rx params with the same settings. This is
  563. * really a harware restriction because SSI must be disabled until
  564. * we can change those values. If there is an active audio stream in
  565. * one direction, enabling the other direction with different
  566. * settings would mean disturbing the running one.
  567. */
  568. ret = imx_ssi_hw_tx_params(substream, params);
  569. if (ret < 0)
  570. return ret;
  571. return imx_ssi_hw_rx_params(substream, params);
  572. }
  573. int imx_ssi_prepare(struct snd_pcm_substream *substream,
  574. struct snd_soc_dai *dai)
  575. {
  576. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  577. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  578. int ret;
  579. pr_debug("%s\n", __func__);
  580. /* Enable clks here to follow SSI recommended init sequence */
  581. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2) {
  582. ret = clk_enable(ssi_clk0);
  583. if (ret < 0)
  584. printk(KERN_ERR "Unable to enable ssi_clk0\n");
  585. } else {
  586. ret = clk_enable(ssi_clk1);
  587. if (ret < 0)
  588. printk(KERN_ERR "Unable to enable ssi_clk1\n");
  589. }
  590. return 0;
  591. }
  592. static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
  593. struct snd_soc_dai *dai)
  594. {
  595. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  596. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  597. u32 scr;
  598. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  599. scr = SSI1_SCR;
  600. else
  601. scr = SSI2_SCR;
  602. switch (cmd) {
  603. case SNDRV_PCM_TRIGGER_START:
  604. case SNDRV_PCM_TRIGGER_RESUME:
  605. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  606. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  607. scr |= SSI_SCR_TE | SSI_SCR_SSIEN;
  608. else
  609. scr |= SSI_SCR_RE | SSI_SCR_SSIEN;
  610. break;
  611. case SNDRV_PCM_TRIGGER_SUSPEND:
  612. case SNDRV_PCM_TRIGGER_STOP:
  613. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  614. if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  615. scr &= ~SSI_SCR_TE;
  616. else
  617. scr &= ~SSI_SCR_RE;
  618. break;
  619. default:
  620. return -EINVAL;
  621. }
  622. if (cpu_dai->id == IMX_DAI_SSI0 || cpu_dai->id == IMX_DAI_SSI2)
  623. SSI1_SCR = scr;
  624. else
  625. SSI2_SCR = scr;
  626. return 0;
  627. }
  628. static void imx_ssi_shutdown(struct snd_pcm_substream *substream,
  629. struct snd_soc_dai *dai)
  630. {
  631. struct snd_soc_pcm_runtime *rtd = substream->private_data;
  632. struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
  633. /* shutdown SSI if neither Tx or Rx is active */
  634. if (!cpu_dai->active) {
  635. if (cpu_dai->id == IMX_DAI_SSI0 ||
  636. cpu_dai->id == IMX_DAI_SSI2) {
  637. if (--ssi_active[SSI1_PORT] > 1)
  638. return;
  639. SSI1_SCR = 0;
  640. clk_disable(ssi_clk0);
  641. } else {
  642. if (--ssi_active[SSI2_PORT])
  643. return;
  644. SSI2_SCR = 0;
  645. clk_disable(ssi_clk1);
  646. }
  647. }
  648. }
  649. #ifdef CONFIG_PM
  650. static int imx_ssi_suspend(struct platform_device *dev,
  651. struct snd_soc_dai *dai)
  652. {
  653. return 0;
  654. }
  655. static int imx_ssi_resume(struct platform_device *pdev,
  656. struct snd_soc_dai *dai)
  657. {
  658. return 0;
  659. }
  660. #else
  661. #define imx_ssi_suspend NULL
  662. #define imx_ssi_resume NULL
  663. #endif
  664. #define IMX_SSI_RATES \
  665. (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 | \
  666. SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
  667. SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
  668. SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
  669. SNDRV_PCM_RATE_96000)
  670. #define IMX_SSI_BITS \
  671. (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
  672. SNDRV_PCM_FMTBIT_S24_LE)
  673. static struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = {
  674. .startup = imx_ssi_startup,
  675. .shutdown = imx_ssi_shutdown,
  676. .trigger = imx_ssi_trigger,
  677. .prepare = imx_ssi_prepare,
  678. .hw_params = imx_ssi_hw_params,
  679. .set_sysclk = imx_ssi_set_dai_sysclk,
  680. .set_clkdiv = imx_ssi_set_dai_clkdiv,
  681. .set_fmt = imx_ssi_set_dai_fmt,
  682. .set_tdm_slot = imx_ssi_set_dai_tdm_slot,
  683. };
  684. struct snd_soc_dai imx_ssi_pcm_dai[] = {
  685. {
  686. .name = "imx-i2s-1-0",
  687. .id = IMX_DAI_SSI0,
  688. .suspend = imx_ssi_suspend,
  689. .resume = imx_ssi_resume,
  690. .playback = {
  691. .channels_min = 1,
  692. .channels_max = 2,
  693. .formats = IMX_SSI_BITS,
  694. .rates = IMX_SSI_RATES,},
  695. .capture = {
  696. .channels_min = 1,
  697. .channels_max = 2,
  698. .formats = IMX_SSI_BITS,
  699. .rates = IMX_SSI_RATES,},
  700. .ops = &imx_ssi_pcm_dai_ops,
  701. },
  702. {
  703. .name = "imx-i2s-2-0",
  704. .id = IMX_DAI_SSI1,
  705. .playback = {
  706. .channels_min = 1,
  707. .channels_max = 2,
  708. .formats = IMX_SSI_BITS,
  709. .rates = IMX_SSI_RATES,},
  710. .capture = {
  711. .channels_min = 1,
  712. .channels_max = 2,
  713. .formats = IMX_SSI_BITS,
  714. .rates = IMX_SSI_RATES,},
  715. .ops = &imx_ssi_pcm_dai_ops,
  716. },
  717. {
  718. .name = "imx-i2s-1-1",
  719. .id = IMX_DAI_SSI2,
  720. .suspend = imx_ssi_suspend,
  721. .resume = imx_ssi_resume,
  722. .playback = {
  723. .channels_min = 1,
  724. .channels_max = 2,
  725. .formats = IMX_SSI_BITS,
  726. .rates = IMX_SSI_RATES,},
  727. .capture = {
  728. .channels_min = 1,
  729. .channels_max = 2,
  730. .formats = IMX_SSI_BITS,
  731. .rates = IMX_SSI_RATES,},
  732. .ops = &imx_ssi_pcm_dai_ops,
  733. },
  734. {
  735. .name = "imx-i2s-2-1",
  736. .id = IMX_DAI_SSI3,
  737. .playback = {
  738. .channels_min = 1,
  739. .channels_max = 2,
  740. .formats = IMX_SSI_BITS,
  741. .rates = IMX_SSI_RATES,},
  742. .capture = {
  743. .channels_min = 1,
  744. .channels_max = 2,
  745. .formats = IMX_SSI_BITS,
  746. .rates = IMX_SSI_RATES,},
  747. .ops = &imx_ssi_pcm_dai_ops,
  748. },
  749. };
  750. EXPORT_SYMBOL_GPL(imx_ssi_pcm_dai);
  751. static int __init imx_ssi_init(void)
  752. {
  753. return snd_soc_register_dais(imx_ssi_pcm_dai,
  754. ARRAY_SIZE(imx_ssi_pcm_dai));
  755. }
  756. static void __exit imx_ssi_exit(void)
  757. {
  758. snd_soc_unregister_dais(imx_ssi_pcm_dai,
  759. ARRAY_SIZE(imx_ssi_pcm_dai));
  760. }
  761. module_init(imx_ssi_init);
  762. module_exit(imx_ssi_exit);
  763. MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com");
  764. MODULE_DESCRIPTION("i.MX ASoC I2S driver");
  765. MODULE_LICENSE("GPL");