lpass-dma.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /* Copyright (c) 2010-2011, The Linux Foundation. All rights reserved.
  2. *
  3. * This program is free software; you can redistribute it and/or modify
  4. * it under the terms of the GNU General Public License version 2 and
  5. * only version 2 as published by the Free Software Foundation.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. */
  12. #include <linux/module.h>
  13. #include <linux/platform_device.h>
  14. #include <linux/dma-mapping.h>
  15. #include <linux/debugfs.h>
  16. #include <linux/delay.h>
  17. #include <linux/uaccess.h>
  18. #include <linux/irq.h>
  19. #include <linux/interrupt.h>
  20. #include <linux/spinlock.h>
  21. #include <linux/slab.h>
  22. #include <linux/msm_audio.h>
  23. #include <linux/clk.h>
  24. #include <sound/core.h>
  25. #include <sound/pcm.h>
  26. #include <sound/soc.h>
  27. #include <mach/msm_iomap-8x60.h>
  28. #include <mach/audio_dma_msm8k.h>
  29. #include <sound/dai.h>
  30. #include "lpass-pcm.h"
  31. struct dai_baseinfo {
  32. void __iomem *base;
  33. };
  34. static struct dai_baseinfo dai_info;
  35. struct dai_drv {
  36. u8 *buffer;
  37. u32 buffer_phys;
  38. int channels;
  39. irqreturn_t (*callback) (int intrsrc, void *private_data);
  40. void *private_data;
  41. int in_use;
  42. u32 buffer_len;
  43. u32 period_len;
  44. u32 master_mode;
  45. };
  46. static struct dai_drv *dai[MAX_CHANNELS];
  47. static spinlock_t dai_lock;
  48. static int dai_find_dma_channel(uint32_t intrsrc)
  49. {
  50. int i, dma_channel = 0;
  51. pr_debug("%s\n", __func__);
  52. for (i = 0; i <= 27; i += 3) {
  53. if (intrsrc & (1 << i)) {
  54. dma_channel = i / 3;
  55. break;
  56. }
  57. }
  58. return dma_channel;
  59. }
  60. void register_dma_irq_handler(int dma_ch,
  61. irqreturn_t (*callback) (int intrsrc, void *private_data),
  62. void *private_data)
  63. {
  64. pr_debug("%s\n", __func__);
  65. dai[dma_ch]->callback = callback;
  66. dai[dma_ch]->private_data = private_data;
  67. }
  68. void unregister_dma_irq_handler(int dma_ch)
  69. {
  70. pr_debug("%s\n", __func__);
  71. dai[dma_ch]->callback = NULL;
  72. dai[dma_ch]->private_data = NULL;
  73. }
  74. static irqreturn_t dai_irq_handler(int irq, void *data)
  75. {
  76. unsigned long flag;
  77. uint32_t intrsrc;
  78. uint32_t dma_ch = 0;
  79. irqreturn_t ret = IRQ_HANDLED;
  80. pr_debug("%s\n", __func__);
  81. spin_lock_irqsave(&dai_lock, flag);
  82. intrsrc = readl(dai_info.base + LPAIF_IRQ_STAT(0));
  83. writel(intrsrc, dai_info.base + LPAIF_IRQ_CLEAR(0));
  84. mb();
  85. while (intrsrc) {
  86. dma_ch = dai_find_dma_channel(intrsrc);
  87. if (!dai[dma_ch]->callback)
  88. goto handled;
  89. if (!dai[dma_ch]->private_data)
  90. goto handled;
  91. ret = dai[dma_ch]->callback(intrsrc,
  92. dai[dma_ch]->private_data);
  93. intrsrc &= ~(0x7 << (dma_ch * 3));
  94. }
  95. handled:
  96. spin_unlock_irqrestore(&dai_lock, flag);
  97. return ret;
  98. }
  99. void dai_print_state(uint32_t dma_ch)
  100. {
  101. int i = 0;
  102. unsigned long *ptrmem = (unsigned long *)dai_info.base;
  103. for (i = 0; i < 4; i++, ++ptrmem)
  104. pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
  105. (unsigned int)*ptrmem);
  106. ptrmem = (unsigned long *)(dai_info.base
  107. + DMA_CH_CTL_BASE + DMA_CH_INDEX(dma_ch));
  108. for (i = 0; i < 10; i++, ++ptrmem)
  109. pr_debug("[0x%08x]=0x%08x\n", (unsigned int)ptrmem,
  110. (unsigned int) *ptrmem);
  111. }
  112. static int dai_enable_irq(uint32_t dma_ch)
  113. {
  114. int ret;
  115. pr_debug("%s\n", __func__);
  116. ret = request_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, dai_irq_handler,
  117. IRQF_TRIGGER_RISING | IRQF_SHARED, "msm-i2s",
  118. (void *) (dma_ch+1));
  119. if (ret < 0) {
  120. pr_debug("Request Irq Failed err = %d\n", ret);
  121. return ret;
  122. }
  123. return ret;
  124. }
  125. static void dai_config_dma(uint32_t dma_ch)
  126. {
  127. pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
  128. writel(dai[dma_ch]->buffer_phys,
  129. dai_info.base + LPAIF_DMA_BASE(dma_ch));
  130. writel(((dai[dma_ch]->buffer_len >> 2) - 1),
  131. dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch));
  132. writel(((dai[dma_ch]->period_len >> 2) - 1),
  133. dai_info.base + LPAIF_DMA_PER_LEN(dma_ch));
  134. mb();
  135. }
  136. static void dai_enable_codec(uint32_t dma_ch, int codec)
  137. {
  138. uint32_t intrVal;
  139. uint32_t i2sctl;
  140. pr_debug("%s\n", __func__);
  141. intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
  142. intrVal = intrVal | (7 << (dma_ch * 3));
  143. writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
  144. if (codec == DAI_SPKR) {
  145. writel(0x0813, dai_info.base + LPAIF_DMA_CTL(dma_ch));
  146. i2sctl = 0x4400;
  147. i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
  148. writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_SPKR));
  149. } else if (codec == DAI_MIC) {
  150. writel(0x81b, dai_info.base + LPAIF_DMA_CTL(dma_ch));
  151. i2sctl = 0x0110;
  152. i2sctl |= (dai[dma_ch]->master_mode ? WS_SRC_INT : WS_SRC_EXT);
  153. writel(i2sctl, dai_info.base + LPAIF_I2S_CTL_OFFSET(DAI_MIC));
  154. }
  155. }
  156. static void dai_disable_codec(uint32_t dma_ch, int codec)
  157. {
  158. uint32_t intrVal = 0;
  159. uint32_t intrVal1 = 0;
  160. unsigned long flag = 0x0;
  161. pr_debug("%s\n", __func__);
  162. spin_lock_irqsave(&dai_lock, flag);
  163. intrVal1 = readl(dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
  164. if (codec == DAI_SPKR)
  165. intrVal1 = intrVal1 & ~(1 << 14);
  166. else if (codec == DAI_MIC)
  167. intrVal1 = intrVal1 & ~(1 << 8);
  168. writel(intrVal1, dai_info.base + LPAIF_I2S_CTL_OFFSET(codec));
  169. intrVal = 0x0;
  170. writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
  171. spin_unlock_irqrestore(&dai_lock, flag);
  172. }
  173. int dai_open(uint32_t dma_ch)
  174. {
  175. pr_debug("%s\n", __func__);
  176. if (!dai_info.base) {
  177. pr_debug("%s failed as no msm-dai device\n", __func__);
  178. return -ENODEV;
  179. }
  180. if (dma_ch >= MAX_CHANNELS) {
  181. pr_debug("%s over max channesl %d\n", __func__, dma_ch);
  182. return -ENODEV;
  183. }
  184. return 0;
  185. }
  186. void dai_close(uint32_t dma_ch)
  187. {
  188. pr_debug("%s\n", __func__);
  189. if ((dma_ch >= 0) && (dma_ch < 5))
  190. dai_disable_codec(dma_ch, DAI_SPKR);
  191. else
  192. dai_disable_codec(dma_ch, DAI_MIC);
  193. free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
  194. }
  195. void dai_set_master_mode(uint32_t dma_ch, int mode)
  196. {
  197. if (dma_ch < MAX_CHANNELS)
  198. dai[dma_ch]->master_mode = mode;
  199. else
  200. pr_err("%s: invalid dma channel\n", __func__);
  201. }
  202. int dai_set_params(uint32_t dma_ch, struct dai_dma_params *params)
  203. {
  204. pr_debug("%s\n", __func__);
  205. dai[dma_ch]->buffer = params->buffer;
  206. dai[dma_ch]->buffer_phys = params->src_start;
  207. dai[dma_ch]->channels = params->channels;
  208. dai[dma_ch]->buffer_len = params->buffer_size;
  209. dai[dma_ch]->period_len = params->period_size;
  210. mb();
  211. dai_config_dma(dma_ch);
  212. return dma_ch;
  213. }
  214. int dai_start(uint32_t dma_ch)
  215. {
  216. unsigned long flag = 0x0;
  217. spin_lock_irqsave(&dai_lock, flag);
  218. dai_enable_irq(dma_ch);
  219. if ((dma_ch >= 0) && (dma_ch < 5))
  220. dai_enable_codec(dma_ch, DAI_SPKR);
  221. else
  222. dai_enable_codec(dma_ch, DAI_MIC);
  223. spin_unlock_irqrestore(&dai_lock, flag);
  224. dai_print_state(dma_ch);
  225. return 0;
  226. }
  227. #define HDMI_BURST_INCR4 (1 << 11)
  228. #define HDMI_WPSCNT (1 << 8)
  229. #define HDMI_AUDIO_INTF (5 << 4)
  230. #define HDMI_FIFO_WATER_MARK (7 << 1)
  231. #define HDMI_ENABLE (1)
  232. int dai_start_hdmi(uint32_t dma_ch)
  233. {
  234. unsigned long flag = 0x0;
  235. uint32_t val;
  236. pr_debug("%s dma_ch = %u\n", __func__, dma_ch);
  237. spin_lock_irqsave(&dai_lock, flag);
  238. dai_enable_irq(dma_ch);
  239. if ((dma_ch >= 0) && (dma_ch < 5)) {
  240. val = readl(dai_info.base + LPAIF_IRQ_EN(0));
  241. val = val | (7 << (dma_ch * 3));
  242. writel(val, dai_info.base + LPAIF_IRQ_EN(0));
  243. mb();
  244. val = (HDMI_BURST_INCR4 | HDMI_WPSCNT | HDMI_AUDIO_INTF |
  245. HDMI_FIFO_WATER_MARK | HDMI_ENABLE);
  246. writel(val, dai_info.base + LPAIF_DMA_CTL(dma_ch));
  247. }
  248. spin_unlock_irqrestore(&dai_lock, flag);
  249. mb();
  250. dai_print_state(dma_ch);
  251. return 0;
  252. }
  253. int wait_for_dma_cnt_stop(uint32_t dma_ch)
  254. {
  255. uint32_t dma_per_cnt_reg_val, dma_per_cnt, prev_dma_per_cnt;
  256. uint32_t i;
  257. pr_info("%s dma_ch %u\n", __func__, dma_ch);
  258. dma_per_cnt_reg_val = readl_relaxed(dai_info.base +
  259. LPAIF_DMA_PER_CNT(dma_ch));
  260. dma_per_cnt =
  261. ((LPAIF_DMA_PER_CNT_PER_CNT_MASK & dma_per_cnt_reg_val) >>
  262. LPAIF_DMA_PER_CNT_PER_CNT_SHIFT) -
  263. ((LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK & dma_per_cnt_reg_val) >>
  264. LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT);
  265. prev_dma_per_cnt = dma_per_cnt;
  266. i = 1;
  267. pr_info("%s: i = %u dma_per_cnt_reg_val 0x%08x , dma_per_cnt %u\n",
  268. __func__, i, dma_per_cnt_reg_val, dma_per_cnt);
  269. while (i <= 50) {
  270. msleep(50);
  271. dma_per_cnt_reg_val = readl_relaxed(dai_info.base +
  272. LPAIF_DMA_PER_CNT(dma_ch));
  273. dma_per_cnt =
  274. ((LPAIF_DMA_PER_CNT_PER_CNT_MASK & dma_per_cnt_reg_val) >>
  275. LPAIF_DMA_PER_CNT_PER_CNT_SHIFT) -
  276. ((LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK & dma_per_cnt_reg_val) >>
  277. LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT);
  278. i++;
  279. pr_info("%s: i = %u dma_per_cnt_reg_val 0x%08x , dma_per_cnt %u\n",
  280. __func__, i, dma_per_cnt_reg_val, dma_per_cnt);
  281. if (prev_dma_per_cnt == dma_per_cnt)
  282. break;
  283. prev_dma_per_cnt = dma_per_cnt;
  284. }
  285. return 0;
  286. }
  287. void dai_stop_hdmi(uint32_t dma_ch)
  288. {
  289. unsigned long flag = 0x0;
  290. uint32_t intrVal;
  291. uint32_t int_mask = 0x00000007;
  292. pr_debug("%s dma_ch %u\n", __func__, dma_ch);
  293. spin_lock_irqsave(&dai_lock, flag);
  294. free_irq(LPASS_SCSS_AUDIO_IF_OUT0_IRQ, (void *) (dma_ch + 1));
  295. intrVal = 0x0;
  296. writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
  297. mb();
  298. intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
  299. int_mask = ((int_mask) << (dma_ch * 3));
  300. int_mask = ~int_mask;
  301. intrVal = intrVal & int_mask;
  302. writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
  303. mb();
  304. spin_unlock_irqrestore(&dai_lock, flag);
  305. }
  306. int dai_stop(uint32_t dma_ch)
  307. {
  308. pr_debug("%s\n", __func__);
  309. return 0;
  310. }
  311. uint32_t dai_get_dma_pos(uint32_t dma_ch)
  312. {
  313. uint32_t addr;
  314. pr_debug("%s\n", __func__);
  315. addr = readl(dai_info.base + LPAIF_DMA_CURR_ADDR(dma_ch));
  316. return addr;
  317. }
  318. static int __devinit dai_probe(struct platform_device *pdev)
  319. {
  320. int rc = 0;
  321. int i = 0;
  322. struct resource *src;
  323. src = platform_get_resource_byname(pdev, IORESOURCE_MEM, "msm-dai");
  324. if (!src) {
  325. rc = -ENODEV;
  326. pr_debug("%s Error rc=%d\n", __func__, rc);
  327. goto error;
  328. }
  329. for (i = 0; i <= MAX_CHANNELS; i++) {
  330. dai[i] = kzalloc(sizeof(struct dai_drv), GFP_KERNEL);
  331. if (!dai[0]) {
  332. pr_debug("Allocation failed for dma_channel = 0\n");
  333. return -ENODEV;
  334. }
  335. }
  336. dai_info.base = ioremap(src->start, (src->end - src->start) + 1);
  337. pr_debug("%s: msm-dai: 0x%08x\n", __func__,
  338. (unsigned int)dai_info.base);
  339. spin_lock_init(&dai_lock);
  340. error:
  341. return rc;
  342. }
  343. static int dai_remove(struct platform_device *pdev)
  344. {
  345. iounmap(dai_info.base);
  346. return 0;
  347. }
  348. static struct platform_driver dai_driver = {
  349. .probe = dai_probe,
  350. .remove = dai_remove,
  351. .driver = {
  352. .name = "msm-dai",
  353. .owner = THIS_MODULE
  354. },
  355. };
  356. static struct resource msm_lpa_resources[] = {
  357. {
  358. .start = MSM_LPA_PHYS,
  359. .end = MSM_LPA_END,
  360. .flags = IORESOURCE_MEM,
  361. .name = "msm-dai",
  362. },
  363. };
  364. static struct platform_device *codec_device;
  365. static int msm_dai_dev_register(const char *name)
  366. {
  367. int ret = 0;
  368. pr_debug("%s : called\n", __func__);
  369. codec_device = platform_device_alloc(name, -1);
  370. if (codec_device == NULL) {
  371. pr_debug("Failed to allocate %s\n", name);
  372. return -ENODEV;
  373. }
  374. platform_set_drvdata(codec_device, (void *)&dai_info);
  375. platform_device_add_resources(codec_device, &msm_lpa_resources[0],
  376. ARRAY_SIZE(msm_lpa_resources));
  377. ret = platform_device_add(codec_device);
  378. if (ret != 0) {
  379. pr_debug("Failed to register %s: %d\n", name, ret);
  380. platform_device_put(codec_device);
  381. }
  382. return ret;
  383. }
  384. static int __init dai_init(void)
  385. {
  386. if (msm_dai_dev_register("msm-dai")) {
  387. pr_notice("dai_init: msm-dai Failed");
  388. return -ENODEV;
  389. }
  390. return platform_driver_register(&dai_driver);
  391. }
  392. static void __exit dai_exit(void)
  393. {
  394. platform_driver_unregister(&dai_driver);
  395. platform_device_put(codec_device);
  396. }
  397. module_init(dai_init);
  398. module_exit(dai_exit);
  399. MODULE_DESCRIPTION("MSM I2S driver");
  400. MODULE_LICENSE("GPL v2");