lpass-platform.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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 = devm_kzalloc(soc_runtime->dev, 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. return dma_ch;
  76. drvdata->substream[dma_ch] = substream;
  77. ret = regmap_write(drvdata->lpaif_map,
  78. LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
  79. if (ret) {
  80. dev_err(soc_runtime->dev,
  81. "error writing to rdmactl reg: %d\n", ret);
  82. return ret;
  83. }
  84. data->dma_ch = dma_ch;
  85. snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
  86. runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
  87. ret = snd_pcm_hw_constraint_integer(runtime,
  88. SNDRV_PCM_HW_PARAM_PERIODS);
  89. if (ret < 0) {
  90. dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
  91. ret);
  92. return -EINVAL;
  93. }
  94. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  95. return 0;
  96. }
  97. static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
  98. {
  99. struct snd_pcm_runtime *runtime = substream->runtime;
  100. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  101. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  102. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  103. struct lpass_variant *v = drvdata->variant;
  104. struct lpass_pcm_data *data;
  105. data = runtime->private_data;
  106. drvdata->substream[data->dma_ch] = NULL;
  107. if (v->free_dma_channel)
  108. v->free_dma_channel(drvdata, data->dma_ch);
  109. return 0;
  110. }
  111. static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
  112. struct snd_pcm_hw_params *params)
  113. {
  114. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  115. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  116. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  117. struct snd_pcm_runtime *rt = substream->runtime;
  118. struct lpass_pcm_data *pcm_data = rt->private_data;
  119. struct lpass_variant *v = drvdata->variant;
  120. snd_pcm_format_t format = params_format(params);
  121. unsigned int channels = params_channels(params);
  122. unsigned int regval;
  123. int ch, dir = substream->stream;
  124. int bitwidth;
  125. int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
  126. ch = pcm_data->dma_ch;
  127. bitwidth = snd_pcm_format_width(format);
  128. if (bitwidth < 0) {
  129. dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
  130. bitwidth);
  131. return bitwidth;
  132. }
  133. regval = LPAIF_DMACTL_BURSTEN_INCR4 |
  134. LPAIF_DMACTL_AUDINTF(dma_port) |
  135. LPAIF_DMACTL_FIFOWM_8;
  136. switch (bitwidth) {
  137. case 16:
  138. switch (channels) {
  139. case 1:
  140. case 2:
  141. regval |= LPAIF_DMACTL_WPSCNT_ONE;
  142. break;
  143. case 4:
  144. regval |= LPAIF_DMACTL_WPSCNT_TWO;
  145. break;
  146. case 6:
  147. regval |= LPAIF_DMACTL_WPSCNT_THREE;
  148. break;
  149. case 8:
  150. regval |= LPAIF_DMACTL_WPSCNT_FOUR;
  151. break;
  152. default:
  153. dev_err(soc_runtime->dev,
  154. "invalid PCM config given: bw=%d, ch=%u\n",
  155. bitwidth, channels);
  156. return -EINVAL;
  157. }
  158. break;
  159. case 24:
  160. case 32:
  161. switch (channels) {
  162. case 1:
  163. regval |= LPAIF_DMACTL_WPSCNT_ONE;
  164. break;
  165. case 2:
  166. regval |= LPAIF_DMACTL_WPSCNT_TWO;
  167. break;
  168. case 4:
  169. regval |= LPAIF_DMACTL_WPSCNT_FOUR;
  170. break;
  171. case 6:
  172. regval |= LPAIF_DMACTL_WPSCNT_SIX;
  173. break;
  174. case 8:
  175. regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
  176. break;
  177. default:
  178. dev_err(soc_runtime->dev,
  179. "invalid PCM config given: bw=%d, ch=%u\n",
  180. bitwidth, channels);
  181. return -EINVAL;
  182. }
  183. break;
  184. default:
  185. dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
  186. bitwidth, channels);
  187. return -EINVAL;
  188. }
  189. ret = regmap_write(drvdata->lpaif_map,
  190. LPAIF_DMACTL_REG(v, ch, dir), regval);
  191. if (ret) {
  192. dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
  193. ret);
  194. return ret;
  195. }
  196. return 0;
  197. }
  198. static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
  199. {
  200. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  201. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  202. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  203. struct snd_pcm_runtime *rt = substream->runtime;
  204. struct lpass_pcm_data *pcm_data = rt->private_data;
  205. struct lpass_variant *v = drvdata->variant;
  206. unsigned int reg;
  207. int ret;
  208. reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);
  209. ret = regmap_write(drvdata->lpaif_map, reg, 0);
  210. if (ret)
  211. dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
  212. ret);
  213. return ret;
  214. }
  215. static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
  216. {
  217. struct snd_pcm_runtime *runtime = substream->runtime;
  218. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  219. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  220. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  221. struct snd_pcm_runtime *rt = substream->runtime;
  222. struct lpass_pcm_data *pcm_data = rt->private_data;
  223. struct lpass_variant *v = drvdata->variant;
  224. int ret, ch, dir = substream->stream;
  225. ch = pcm_data->dma_ch;
  226. ret = regmap_write(drvdata->lpaif_map,
  227. LPAIF_DMABASE_REG(v, ch, dir),
  228. runtime->dma_addr);
  229. if (ret) {
  230. dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
  231. ret);
  232. return ret;
  233. }
  234. ret = regmap_write(drvdata->lpaif_map,
  235. LPAIF_DMABUFF_REG(v, ch, dir),
  236. (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
  237. if (ret) {
  238. dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
  239. ret);
  240. return ret;
  241. }
  242. ret = regmap_write(drvdata->lpaif_map,
  243. LPAIF_DMAPER_REG(v, ch, dir),
  244. (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
  245. if (ret) {
  246. dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
  247. ret);
  248. return ret;
  249. }
  250. ret = regmap_update_bits(drvdata->lpaif_map,
  251. LPAIF_DMACTL_REG(v, ch, dir),
  252. LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
  253. if (ret) {
  254. dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
  255. ret);
  256. return ret;
  257. }
  258. return 0;
  259. }
  260. static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
  261. int cmd)
  262. {
  263. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  264. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  265. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  266. struct snd_pcm_runtime *rt = substream->runtime;
  267. struct lpass_pcm_data *pcm_data = rt->private_data;
  268. struct lpass_variant *v = drvdata->variant;
  269. int ret, ch, dir = substream->stream;
  270. ch = pcm_data->dma_ch;
  271. switch (cmd) {
  272. case SNDRV_PCM_TRIGGER_START:
  273. case SNDRV_PCM_TRIGGER_RESUME:
  274. case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
  275. /* clear status before enabling interrupts */
  276. ret = regmap_write(drvdata->lpaif_map,
  277. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  278. LPAIF_IRQ_ALL(ch));
  279. if (ret) {
  280. dev_err(soc_runtime->dev,
  281. "error writing to irqclear reg: %d\n", ret);
  282. return ret;
  283. }
  284. ret = regmap_update_bits(drvdata->lpaif_map,
  285. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
  286. LPAIF_IRQ_ALL(ch),
  287. LPAIF_IRQ_ALL(ch));
  288. if (ret) {
  289. dev_err(soc_runtime->dev,
  290. "error writing to irqen reg: %d\n", ret);
  291. return ret;
  292. }
  293. ret = regmap_update_bits(drvdata->lpaif_map,
  294. LPAIF_DMACTL_REG(v, ch, dir),
  295. LPAIF_DMACTL_ENABLE_MASK,
  296. LPAIF_DMACTL_ENABLE_ON);
  297. if (ret) {
  298. dev_err(soc_runtime->dev,
  299. "error writing to rdmactl reg: %d\n", ret);
  300. return ret;
  301. }
  302. break;
  303. case SNDRV_PCM_TRIGGER_STOP:
  304. case SNDRV_PCM_TRIGGER_SUSPEND:
  305. case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
  306. ret = regmap_update_bits(drvdata->lpaif_map,
  307. LPAIF_DMACTL_REG(v, ch, dir),
  308. LPAIF_DMACTL_ENABLE_MASK,
  309. LPAIF_DMACTL_ENABLE_OFF);
  310. if (ret) {
  311. dev_err(soc_runtime->dev,
  312. "error writing to rdmactl reg: %d\n", ret);
  313. return ret;
  314. }
  315. ret = regmap_update_bits(drvdata->lpaif_map,
  316. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
  317. LPAIF_IRQ_ALL(ch), 0);
  318. if (ret) {
  319. dev_err(soc_runtime->dev,
  320. "error writing to irqen reg: %d\n", ret);
  321. return ret;
  322. }
  323. break;
  324. }
  325. return 0;
  326. }
  327. static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
  328. struct snd_pcm_substream *substream)
  329. {
  330. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  331. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  332. struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
  333. struct snd_pcm_runtime *rt = substream->runtime;
  334. struct lpass_pcm_data *pcm_data = rt->private_data;
  335. struct lpass_variant *v = drvdata->variant;
  336. unsigned int base_addr, curr_addr;
  337. int ret, ch, dir = substream->stream;
  338. ch = pcm_data->dma_ch;
  339. ret = regmap_read(drvdata->lpaif_map,
  340. LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
  341. if (ret) {
  342. dev_err(soc_runtime->dev,
  343. "error reading from rdmabase reg: %d\n", ret);
  344. return ret;
  345. }
  346. ret = regmap_read(drvdata->lpaif_map,
  347. LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
  348. if (ret) {
  349. dev_err(soc_runtime->dev,
  350. "error reading from rdmacurr reg: %d\n", ret);
  351. return ret;
  352. }
  353. return bytes_to_frames(substream->runtime, curr_addr - base_addr);
  354. }
  355. static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
  356. struct vm_area_struct *vma)
  357. {
  358. struct snd_pcm_runtime *runtime = substream->runtime;
  359. return dma_mmap_coherent(substream->pcm->card->dev, vma,
  360. runtime->dma_area, runtime->dma_addr,
  361. runtime->dma_bytes);
  362. }
  363. static const struct snd_pcm_ops lpass_platform_pcm_ops = {
  364. .open = lpass_platform_pcmops_open,
  365. .close = lpass_platform_pcmops_close,
  366. .ioctl = snd_pcm_lib_ioctl,
  367. .hw_params = lpass_platform_pcmops_hw_params,
  368. .hw_free = lpass_platform_pcmops_hw_free,
  369. .prepare = lpass_platform_pcmops_prepare,
  370. .trigger = lpass_platform_pcmops_trigger,
  371. .pointer = lpass_platform_pcmops_pointer,
  372. .mmap = lpass_platform_pcmops_mmap,
  373. };
  374. static irqreturn_t lpass_dma_interrupt_handler(
  375. struct snd_pcm_substream *substream,
  376. struct lpass_data *drvdata,
  377. int chan, u32 interrupts)
  378. {
  379. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  380. struct lpass_variant *v = drvdata->variant;
  381. irqreturn_t ret = IRQ_NONE;
  382. int rv;
  383. if (interrupts & LPAIF_IRQ_PER(chan)) {
  384. rv = regmap_write(drvdata->lpaif_map,
  385. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  386. LPAIF_IRQ_PER(chan));
  387. if (rv) {
  388. dev_err(soc_runtime->dev,
  389. "error writing to irqclear reg: %d\n", rv);
  390. return IRQ_NONE;
  391. }
  392. snd_pcm_period_elapsed(substream);
  393. ret = IRQ_HANDLED;
  394. }
  395. if (interrupts & LPAIF_IRQ_XRUN(chan)) {
  396. rv = regmap_write(drvdata->lpaif_map,
  397. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  398. LPAIF_IRQ_XRUN(chan));
  399. if (rv) {
  400. dev_err(soc_runtime->dev,
  401. "error writing to irqclear reg: %d\n", rv);
  402. return IRQ_NONE;
  403. }
  404. dev_warn(soc_runtime->dev, "xrun warning\n");
  405. snd_pcm_stop_xrun(substream);
  406. ret = IRQ_HANDLED;
  407. }
  408. if (interrupts & LPAIF_IRQ_ERR(chan)) {
  409. rv = regmap_write(drvdata->lpaif_map,
  410. LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
  411. LPAIF_IRQ_ERR(chan));
  412. if (rv) {
  413. dev_err(soc_runtime->dev,
  414. "error writing to irqclear reg: %d\n", rv);
  415. return IRQ_NONE;
  416. }
  417. dev_err(soc_runtime->dev, "bus access error\n");
  418. snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
  419. ret = IRQ_HANDLED;
  420. }
  421. return ret;
  422. }
  423. static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
  424. {
  425. struct lpass_data *drvdata = data;
  426. struct lpass_variant *v = drvdata->variant;
  427. unsigned int irqs;
  428. int rv, chan;
  429. rv = regmap_read(drvdata->lpaif_map,
  430. LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
  431. if (rv) {
  432. pr_err("error reading from irqstat reg: %d\n", rv);
  433. return IRQ_NONE;
  434. }
  435. /* Handle per channel interrupts */
  436. for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
  437. if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
  438. rv = lpass_dma_interrupt_handler(
  439. drvdata->substream[chan],
  440. drvdata, chan, irqs);
  441. if (rv != IRQ_HANDLED)
  442. return rv;
  443. }
  444. }
  445. return IRQ_HANDLED;
  446. }
  447. static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
  448. {
  449. struct snd_pcm *pcm = soc_runtime->pcm;
  450. struct snd_pcm_substream *psubstream, *csubstream;
  451. struct snd_soc_component *component = snd_soc_rtdcom_lookup(soc_runtime, DRV_NAME);
  452. int ret = -EINVAL;
  453. size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
  454. psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
  455. if (psubstream) {
  456. ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
  457. component->dev,
  458. size, &psubstream->dma_buffer);
  459. if (ret) {
  460. dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
  461. return ret;
  462. }
  463. }
  464. csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
  465. if (csubstream) {
  466. ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
  467. component->dev,
  468. size, &csubstream->dma_buffer);
  469. if (ret) {
  470. dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
  471. if (psubstream)
  472. snd_dma_free_pages(&psubstream->dma_buffer);
  473. return ret;
  474. }
  475. }
  476. return 0;
  477. }
  478. static void lpass_platform_pcm_free(struct snd_pcm *pcm)
  479. {
  480. struct snd_pcm_substream *substream;
  481. int i;
  482. for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
  483. substream = pcm->streams[i].substream;
  484. if (substream) {
  485. snd_dma_free_pages(&substream->dma_buffer);
  486. substream->dma_buffer.area = NULL;
  487. substream->dma_buffer.addr = 0;
  488. }
  489. }
  490. }
  491. static const struct snd_soc_component_driver lpass_component_driver = {
  492. .name = DRV_NAME,
  493. .pcm_new = lpass_platform_pcm_new,
  494. .pcm_free = lpass_platform_pcm_free,
  495. .ops = &lpass_platform_pcm_ops,
  496. };
  497. int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
  498. {
  499. struct lpass_data *drvdata = platform_get_drvdata(pdev);
  500. struct lpass_variant *v = drvdata->variant;
  501. int ret;
  502. drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
  503. if (drvdata->lpaif_irq < 0) {
  504. dev_err(&pdev->dev, "error getting irq handle: %d\n",
  505. drvdata->lpaif_irq);
  506. return -ENODEV;
  507. }
  508. /* ensure audio hardware is disabled */
  509. ret = regmap_write(drvdata->lpaif_map,
  510. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
  511. if (ret) {
  512. dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
  513. return ret;
  514. }
  515. ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
  516. lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
  517. "lpass-irq-lpaif", drvdata);
  518. if (ret) {
  519. dev_err(&pdev->dev, "irq request failed: %d\n", ret);
  520. return ret;
  521. }
  522. return devm_snd_soc_register_component(&pdev->dev,
  523. &lpass_component_driver, NULL, 0);
  524. }
  525. EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
  526. MODULE_DESCRIPTION("QTi LPASS Platform Driver");
  527. MODULE_LICENSE("GPL v2");