lpass-platform.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. /*
  2. * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License version 2 and
  6. * only version 2 as published by the Free Software Foundation.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU General Public License for more details.
  12. *
  13. * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
  14. */
  15. #include <linux/dma-mapping.h>
  16. #include <linux/export.h>
  17. #include <linux/kernel.h>
  18. #include <linux/module.h>
  19. #include <linux/platform_device.h>
  20. #include <sound/pcm_params.h>
  21. #include <linux/regmap.h>
  22. #include <sound/soc.h>
  23. #include "lpass-lpaif-reg.h"
  24. #include "lpass.h"
  25. #define DRV_NAME "lpass-platform"
  26. struct lpass_pcm_data {
  27. int dma_ch;
  28. int i2s_port;
  29. };
  30. #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
  31. #define LPASS_PLATFORM_PERIODS 2
  32. static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
  33. .info = SNDRV_PCM_INFO_MMAP |
  34. SNDRV_PCM_INFO_MMAP_VALID |
  35. SNDRV_PCM_INFO_INTERLEAVED |
  36. SNDRV_PCM_INFO_PAUSE |
  37. SNDRV_PCM_INFO_RESUME,
  38. .formats = SNDRV_PCM_FMTBIT_S16 |
  39. SNDRV_PCM_FMTBIT_S24 |
  40. SNDRV_PCM_FMTBIT_S32,
  41. .rates = SNDRV_PCM_RATE_8000_192000,
  42. .rate_min = 8000,
  43. .rate_max = 192000,
  44. .channels_min = 1,
  45. .channels_max = 8,
  46. .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE,
  47. .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE /
  48. LPASS_PLATFORM_PERIODS,
  49. .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE /
  50. LPASS_PLATFORM_PERIODS,
  51. .periods_min = LPASS_PLATFORM_PERIODS,
  52. .periods_max = LPASS_PLATFORM_PERIODS,
  53. .fifo_size = 0,
  54. };
  55. static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
  56. {
  57. struct snd_pcm_runtime *runtime = substream->runtime;
  58. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  59. struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
  60. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  61. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  62. struct lpass_variant *v = drvdata->variant;
  63. int ret, dma_ch, dir = substream->stream;
  64. struct lpass_pcm_data *data;
  65. data = kzalloc(sizeof(*data), GFP_KERNEL);
  66. if (!data)
  67. return -ENOMEM;
  68. data->i2s_port = cpu_dai->driver->id;
  69. runtime->private_data = data;
  70. if (v->alloc_dma_channel)
  71. dma_ch = v->alloc_dma_channel(drvdata, dir);
  72. else
  73. dma_ch = 0;
  74. if (dma_ch < 0) {
  75. kfree(data);
  76. return dma_ch;
  77. }
  78. drvdata->substream[dma_ch] = substream;
  79. ret = regmap_write(drvdata->lpaif_map,
  80. LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
  81. if (ret) {
  82. dev_err(soc_runtime->dev,
  83. "error writing to rdmactl reg: %d\n", ret);
  84. return ret;
  85. }
  86. data->dma_ch = dma_ch;
  87. snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
  88. runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
  89. ret = snd_pcm_hw_constraint_integer(runtime,
  90. SNDRV_PCM_HW_PARAM_PERIODS);
  91. if (ret < 0) {
  92. kfree(data);
  93. dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
  94. ret);
  95. return -EINVAL;
  96. }
  97. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  98. return 0;
  99. }
  100. static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
  101. {
  102. struct snd_pcm_runtime *runtime = substream->runtime;
  103. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  104. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  105. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  106. struct lpass_variant *v = drvdata->variant;
  107. struct lpass_pcm_data *data;
  108. data = runtime->private_data;
  109. drvdata->substream[data->dma_ch] = NULL;
  110. if (v->free_dma_channel)
  111. v->free_dma_channel(drvdata, data->dma_ch);
  112. kfree(data);
  113. return 0;
  114. }
  115. static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
  116. struct snd_pcm_hw_params *params)
  117. {
  118. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  119. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  120. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  121. struct snd_pcm_runtime *rt = substream->runtime;
  122. struct lpass_pcm_data *pcm_data = rt->private_data;
  123. struct lpass_variant *v = drvdata->variant;
  124. snd_pcm_format_t format = params_format(params);
  125. unsigned int channels = params_channels(params);
  126. unsigned int regval;
  127. int ch, dir = substream->stream;
  128. int bitwidth;
  129. int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
  130. ch = pcm_data->dma_ch;
  131. bitwidth = snd_pcm_format_width(format);
  132. if (bitwidth < 0) {
  133. dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
  134. bitwidth);
  135. return bitwidth;
  136. }
  137. regval = LPAIF_DMACTL_BURSTEN_INCR4 |
  138. LPAIF_DMACTL_AUDINTF(dma_port) |
  139. LPAIF_DMACTL_FIFOWM_8;
  140. switch (bitwidth) {
  141. case 16:
  142. switch (channels) {
  143. case 1:
  144. case 2:
  145. regval |= LPAIF_DMACTL_WPSCNT_ONE;
  146. break;
  147. case 4:
  148. regval |= LPAIF_DMACTL_WPSCNT_TWO;
  149. break;
  150. case 6:
  151. regval |= LPAIF_DMACTL_WPSCNT_THREE;
  152. break;
  153. case 8:
  154. regval |= LPAIF_DMACTL_WPSCNT_FOUR;
  155. break;
  156. default:
  157. dev_err(soc_runtime->dev,
  158. "invalid PCM config given: bw=%d, ch=%u\n",
  159. bitwidth, channels);
  160. return -EINVAL;
  161. }
  162. break;
  163. case 24:
  164. case 32:
  165. switch (channels) {
  166. case 1:
  167. regval |= LPAIF_DMACTL_WPSCNT_ONE;
  168. break;
  169. case 2:
  170. regval |= LPAIF_DMACTL_WPSCNT_TWO;
  171. break;
  172. case 4:
  173. regval |= LPAIF_DMACTL_WPSCNT_FOUR;
  174. break;
  175. case 6:
  176. regval |= LPAIF_DMACTL_WPSCNT_SIX;
  177. break;
  178. case 8:
  179. regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
  180. break;
  181. default:
  182. dev_err(soc_runtime->dev,
  183. "invalid PCM config given: bw=%d, ch=%u\n",
  184. bitwidth, channels);
  185. return -EINVAL;
  186. }
  187. break;
  188. default:
  189. dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
  190. bitwidth, channels);
  191. return -EINVAL;
  192. }
  193. ret = regmap_write(drvdata->lpaif_map,
  194. LPAIF_DMACTL_REG(v, ch, dir), regval);
  195. if (ret) {
  196. dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
  197. ret);
  198. return ret;
  199. }
  200. return 0;
  201. }
  202. static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
  203. {
  204. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  205. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  206. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  207. struct snd_pcm_runtime *rt = substream->runtime;
  208. struct lpass_pcm_data *pcm_data = rt->private_data;
  209. struct lpass_variant *v = drvdata->variant;
  210. unsigned int reg;
  211. int ret;
  212. reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);
  213. ret = regmap_write(drvdata->lpaif_map, reg, 0);
  214. if (ret)
  215. dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
  216. ret);
  217. return ret;
  218. }
  219. static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
  220. {
  221. struct snd_pcm_runtime *runtime = substream->runtime;
  222. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  223. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  224. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  225. struct snd_pcm_runtime *rt = substream->runtime;
  226. struct lpass_pcm_data *pcm_data = rt->private_data;
  227. struct lpass_variant *v = drvdata->variant;
  228. int ret, ch, dir = substream->stream;
  229. ch = pcm_data->dma_ch;
  230. ret = regmap_write(drvdata->lpaif_map,
  231. LPAIF_DMABASE_REG(v, ch, dir),
  232. runtime->dma_addr);
  233. if (ret) {
  234. dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
  235. ret);
  236. return ret;
  237. }
  238. ret = regmap_write(drvdata->lpaif_map,
  239. LPAIF_DMABUFF_REG(v, ch, dir),
  240. (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
  241. if (ret) {
  242. dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
  243. ret);
  244. return ret;
  245. }
  246. ret = regmap_write(drvdata->lpaif_map,
  247. LPAIF_DMAPER_REG(v, ch, dir),
  248. (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
  249. if (ret) {
  250. dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
  251. ret);
  252. return ret;
  253. }
  254. ret = regmap_update_bits(drvdata->lpaif_map,
  255. LPAIF_DMACTL_REG(v, ch, dir),
  256. LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
  257. if (ret) {
  258. dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
  259. ret);
  260. return ret;
  261. }
  262. return 0;
  263. }
  264. static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
  265. int cmd)
  266. {
  267. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  268. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  269. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  270. struct snd_pcm_runtime *rt = substream->runtime;
  271. struct lpass_pcm_data *pcm_data = rt->private_data;
  272. struct lpass_variant *v = drvdata->variant;
  273. int ret, ch, dir = substream->stream;
  274. ch = pcm_data->dma_ch;
  275. switch (cmd) {
  276. case SNDRV_PCM_TRIGGER_START:
  277. case SNDRV_PCM_TRIGGER_RESUME:
  278. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  279. /* clear status before enabling interrupts */
  280. ret = regmap_write(drvdata->lpaif_map,
  281. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  282. LPAIF_IRQ_ALL(ch));
  283. if (ret) {
  284. dev_err(soc_runtime->dev,
  285. "error writing to irqclear reg: %d\n", ret);
  286. return ret;
  287. }
  288. ret = regmap_update_bits(drvdata->lpaif_map,
  289. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
  290. LPAIF_IRQ_ALL(ch),
  291. LPAIF_IRQ_ALL(ch));
  292. if (ret) {
  293. dev_err(soc_runtime->dev,
  294. "error writing to irqen reg: %d\n", ret);
  295. return ret;
  296. }
  297. ret = regmap_update_bits(drvdata->lpaif_map,
  298. LPAIF_DMACTL_REG(v, ch, dir),
  299. LPAIF_DMACTL_ENABLE_MASK,
  300. LPAIF_DMACTL_ENABLE_ON);
  301. if (ret) {
  302. dev_err(soc_runtime->dev,
  303. "error writing to rdmactl reg: %d\n", ret);
  304. return ret;
  305. }
  306. break;
  307. case SNDRV_PCM_TRIGGER_STOP:
  308. case SNDRV_PCM_TRIGGER_SUSPEND:
  309. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  310. ret = regmap_update_bits(drvdata->lpaif_map,
  311. LPAIF_DMACTL_REG(v, ch, dir),
  312. LPAIF_DMACTL_ENABLE_MASK,
  313. LPAIF_DMACTL_ENABLE_OFF);
  314. if (ret) {
  315. dev_err(soc_runtime->dev,
  316. "error writing to rdmactl reg: %d\n", ret);
  317. return ret;
  318. }
  319. ret = regmap_update_bits(drvdata->lpaif_map,
  320. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
  321. LPAIF_IRQ_ALL(ch), 0);
  322. if (ret) {
  323. dev_err(soc_runtime->dev,
  324. "error writing to irqen reg: %d\n", ret);
  325. return ret;
  326. }
  327. break;
  328. }
  329. return 0;
  330. }
  331. static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
  332. struct snd_pcm_substream *substream)
  333. {
  334. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  335. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  336. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  337. struct snd_pcm_runtime *rt = substream->runtime;
  338. struct lpass_pcm_data *pcm_data = rt->private_data;
  339. struct lpass_variant *v = drvdata->variant;
  340. unsigned int base_addr, curr_addr;
  341. int ret, ch, dir = substream->stream;
  342. ch = pcm_data->dma_ch;
  343. ret = regmap_read(drvdata->lpaif_map,
  344. LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
  345. if (ret) {
  346. dev_err(soc_runtime->dev,
  347. "error reading from rdmabase reg: %d\n", ret);
  348. return ret;
  349. }
  350. ret = regmap_read(drvdata->lpaif_map,
  351. LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
  352. if (ret) {
  353. dev_err(soc_runtime->dev,
  354. "error reading from rdmacurr reg: %d\n", ret);
  355. return ret;
  356. }
  357. return bytes_to_frames(substream->runtime, curr_addr - base_addr);
  358. }
  359. static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
  360. struct vm_area_struct *vma)
  361. {
  362. struct snd_pcm_runtime *runtime = substream->runtime;
  363. return dma_mmap_coherent(substream->pcm->card->dev, vma,
  364. runtime->dma_area, runtime->dma_addr,
  365. runtime->dma_bytes);
  366. }
  367. static const struct snd_pcm_ops lpass_platform_pcm_ops = {
  368. .open = lpass_platform_pcmops_open,
  369. .close = lpass_platform_pcmops_close,
  370. .ioctl = snd_pcm_lib_ioctl,
  371. .hw_params = lpass_platform_pcmops_hw_params,
  372. .hw_free = lpass_platform_pcmops_hw_free,
  373. .prepare = lpass_platform_pcmops_prepare,
  374. .trigger = lpass_platform_pcmops_trigger,
  375. .pointer = lpass_platform_pcmops_pointer,
  376. .mmap = lpass_platform_pcmops_mmap,
  377. };
  378. static irqreturn_t lpass_dma_interrupt_handler(
  379. struct snd_pcm_substream *substream,
  380. struct lpass_data *drvdata,
  381. int chan, u32 interrupts)
  382. {
  383. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  384. struct lpass_variant *v = drvdata->variant;
  385. irqreturn_t ret = IRQ_NONE;
  386. int rv;
  387. if (interrupts & LPAIF_IRQ_PER(chan)) {
  388. rv = regmap_write(drvdata->lpaif_map,
  389. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  390. LPAIF_IRQ_PER(chan));
  391. if (rv) {
  392. dev_err(soc_runtime->dev,
  393. "error writing to irqclear reg: %d\n", rv);
  394. return IRQ_NONE;
  395. }
  396. snd_pcm_period_elapsed(substream);
  397. ret = IRQ_HANDLED;
  398. }
  399. if (interrupts & LPAIF_IRQ_XRUN(chan)) {
  400. rv = regmap_write(drvdata->lpaif_map,
  401. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  402. LPAIF_IRQ_XRUN(chan));
  403. if (rv) {
  404. dev_err(soc_runtime->dev,
  405. "error writing to irqclear reg: %d\n", rv);
  406. return IRQ_NONE;
  407. }
  408. dev_warn(soc_runtime->dev, "xrun warning\n");
  409. snd_pcm_stop_xrun(substream);
  410. ret = IRQ_HANDLED;
  411. }
  412. if (interrupts & LPAIF_IRQ_ERR(chan)) {
  413. rv = regmap_write(drvdata->lpaif_map,
  414. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  415. LPAIF_IRQ_ERR(chan));
  416. if (rv) {
  417. dev_err(soc_runtime->dev,
  418. "error writing to irqclear reg: %d\n", rv);
  419. return IRQ_NONE;
  420. }
  421. dev_err(soc_runtime->dev, "bus access error\n");
  422. snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
  423. ret = IRQ_HANDLED;
  424. }
  425. return ret;
  426. }
  427. static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
  428. {
  429. struct lpass_data *drvdata = data;
  430. struct lpass_variant *v = drvdata->variant;
  431. unsigned int irqs;
  432. int rv, chan;
  433. rv = regmap_read(drvdata->lpaif_map,
  434. LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
  435. if (rv) {
  436. pr_err("error reading from irqstat reg: %d\n", rv);
  437. return IRQ_NONE;
  438. }
  439. /* Handle per channel interrupts */
  440. for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
  441. if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
  442. rv = lpass_dma_interrupt_handler(
  443. drvdata->substream[chan],
  444. drvdata, chan, irqs);
  445. if (rv != IRQ_HANDLED)
  446. return rv;
  447. }
  448. }
  449. return IRQ_HANDLED;
  450. }
  451. static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
  452. {
  453. struct snd_pcm *pcm = soc_runtime->pcm;
  454. struct snd_pcm_substream *psubstream, *csubstream;
  455. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  456. int ret = -EINVAL;
  457. size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
  458. psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
  459. if (psubstream) {
  460. ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
  461. component->dev,
  462. size, &psubstream->dma_buffer);
  463. if (ret) {
  464. dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
  465. return ret;
  466. }
  467. }
  468. csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
  469. if (csubstream) {
  470. ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
  471. component->dev,
  472. size, &csubstream->dma_buffer);
  473. if (ret) {
  474. dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
  475. if (psubstream)
  476. snd_dma_free_pages(&psubstream->dma_buffer);
  477. return ret;
  478. }
  479. }
  480. return 0;
  481. }
  482. static void lpass_platform_pcm_free(struct snd_pcm *pcm)
  483. {
  484. struct snd_pcm_substream *substream;
  485. int i;
  486. for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
  487. substream = pcm->streams[i].substream;
  488. if (substream) {
  489. snd_dma_free_pages(&substream->dma_buffer);
  490. substream->dma_buffer.area = NULL;
  491. substream->dma_buffer.addr = 0;
  492. }
  493. }
  494. }
  495. static const struct snd_soc_component_driver lpass_component_driver = {
  496. .name = DRV_NAME,
  497. .pcm_new = lpass_platform_pcm_new,
  498. .pcm_free = lpass_platform_pcm_free,
  499. .ops = &lpass_platform_pcm_ops,
  500. };
  501. int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
  502. {
  503. struct lpass_data *drvdata = platform_get_drvdata(pdev);
  504. struct lpass_variant *v = drvdata->variant;
  505. int ret;
  506. drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
  507. if (drvdata->lpaif_irq < 0) {
  508. dev_err(&pdev->dev, "error getting irq handle: %d\n",
  509. drvdata->lpaif_irq);
  510. return -ENODEV;
  511. }
  512. /* ensure audio hardware is disabled */
  513. ret = regmap_write(drvdata->lpaif_map,
  514. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
  515. if (ret) {
  516. dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
  517. return ret;
  518. }
  519. ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
  520. lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
  521. "lpass-irq-lpaif", drvdata);
  522. if (ret) {
  523. dev_err(&pdev->dev, "irq request failed: %d\n", ret);
  524. return ret;
  525. }
  526. return devm_snd_soc_register_component(&pdev->dev,
  527. &lpass_component_driver, NULL, 0);
  528. }
  529. EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
  530. MODULE_DESCRIPTION("QTi LPASS Platform Driver");
  531. MODULE_LICENSE("GPL v2");