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. struct lpass_pcm_data {
  26. int dma_ch;
  27. int i2s_port;
  28. };
  29. #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024)
  30. #define LPASS_PLATFORM_PERIODS 2
  31. static struct snd_pcm_hardware lpass_platform_pcm_hardware = {
  32. .info = SNDRV_PCM_INFO_MMAP |
  33. SNDRV_PCM_INFO_MMAP_VALID |
  34. SNDRV_PCM_INFO_INTERLEAVED |
  35. SNDRV_PCM_INFO_PAUSE |
  36. SNDRV_PCM_INFO_RESUME,
  37. .formats = SNDRV_PCM_FMTBIT_S16 |
  38. SNDRV_PCM_FMTBIT_S24 |
  39. SNDRV_PCM_FMTBIT_S32,
  40. .rates = SNDRV_PCM_RATE_8000_192000,
  41. .rate_min = 8000,
  42. .rate_max = 192000,
  43. .channels_min = 1,
  44. .channels_max = 8,
  45. .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE,
  46. .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE /
  47. LPASS_PLATFORM_PERIODS,
  48. .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE /
  49. LPASS_PLATFORM_PERIODS,
  50. .periods_min = LPASS_PLATFORM_PERIODS,
  51. .periods_max = LPASS_PLATFORM_PERIODS,
  52. .fifo_size = 0,
  53. };
  54. static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
  55. {
  56. struct snd_pcm_runtime *runtime = substream->runtime;
  57. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  58. struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
  59. struct lpass_data *drvdata =
  60. snd_soc_platform_get_drvdata(soc_runtime->platform);
  61. struct lpass_variant *v = drvdata->variant;
  62. int ret, dma_ch, dir = substream->stream;
  63. struct lpass_pcm_data *data;
  64. data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
  65. if (!data)
  66. return -ENOMEM;
  67. data->i2s_port = cpu_dai->driver->id;
  68. runtime->private_data = data;
  69. dma_ch = 0;
  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. "%s() error writing to rdmactl reg: %d\n",
  82. __func__, ret);
  83. return ret;
  84. }
  85. data->dma_ch = dma_ch;
  86. snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
  87. runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
  88. ret = snd_pcm_hw_constraint_integer(runtime,
  89. SNDRV_PCM_HW_PARAM_PERIODS);
  90. if (ret < 0) {
  91. dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n",
  92. __func__, ret);
  93. return -EINVAL;
  94. }
  95. snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  96. return 0;
  97. }
  98. static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
  99. {
  100. struct snd_pcm_runtime *runtime = substream->runtime;
  101. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  102. struct lpass_data *drvdata =
  103. snd_soc_platform_get_drvdata(soc_runtime->platform);
  104. struct lpass_variant *v = drvdata->variant;
  105. struct lpass_pcm_data *data;
  106. data = runtime->private_data;
  107. v = drvdata->variant;
  108. drvdata->substream[data->dma_ch] = NULL;
  109. if (v->free_dma_channel)
  110. v->free_dma_channel(drvdata, data->dma_ch);
  111. return 0;
  112. }
  113. static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
  114. struct snd_pcm_hw_params *params)
  115. {
  116. struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
  117. struct lpass_data *drvdata =
  118. snd_soc_platform_get_drvdata(soc_runtime->platform);
  119. struct snd_pcm_runtime *rt = substream->runtime;
  120. struct lpass_pcm_data *pcm_data = rt->private_data;
  121. struct lpass_variant *v = drvdata->variant;
  122. snd_pcm_format_t format = params_format(params);
  123. unsigned int channels = params_channels(params);
  124. unsigned int regval;
  125. int ch, dir = substream->stream;
  126. int bitwidth;
  127. int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
  128. ch = pcm_data->dma_ch;
  129. bitwidth = snd_pcm_format_width(format);
  130. if (bitwidth < 0) {
  131. dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n",
  132. __func__, bitwidth);
  133. return bitwidth;
  134. }
  135. regval = LPAIF_DMACTL_BURSTEN_INCR4 |
  136. LPAIF_DMACTL_AUDINTF(dma_port) |
  137. LPAIF_DMACTL_FIFOWM_8;
  138. switch (bitwidth) {
  139. case 16:
  140. switch (channels) {
  141. case 1:
  142. case 2:
  143. regval |= LPAIF_DMACTL_WPSCNT_ONE;
  144. break;
  145. case 4:
  146. regval |= LPAIF_DMACTL_WPSCNT_TWO;
  147. break;
  148. case 6:
  149. regval |= LPAIF_DMACTL_WPSCNT_THREE;
  150. break;
  151. case 8:
  152. regval |= LPAIF_DMACTL_WPSCNT_FOUR;
  153. break;
  154. default:
  155. dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
  156. __func__, bitwidth, channels);
  157. return -EINVAL;
  158. }
  159. break;
  160. case 24:
  161. case 32:
  162. switch (channels) {
  163. case 1:
  164. regval |= LPAIF_DMACTL_WPSCNT_ONE;
  165. break;
  166. case 2:
  167. regval |= LPAIF_DMACTL_WPSCNT_TWO;
  168. break;
  169. case 4:
  170. regval |= LPAIF_DMACTL_WPSCNT_FOUR;
  171. break;
  172. case 6:
  173. regval |= LPAIF_DMACTL_WPSCNT_SIX;
  174. break;
  175. case 8:
  176. regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
  177. break;
  178. default:
  179. dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
  180. __func__, bitwidth, channels);
  181. return -EINVAL;
  182. }
  183. break;
  184. default:
  185. dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
  186. __func__, 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, "%s() error writing to rdmactl reg: %d\n",
  193. __func__, 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 lpass_data *drvdata =
  202. snd_soc_platform_get_drvdata(soc_runtime->platform);
  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, "%s() error writing to rdmactl reg: %d\n",
  212. __func__, 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 lpass_data *drvdata =
  220. snd_soc_platform_get_drvdata(soc_runtime->platform);
  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, "%s() error writing to rdmabase reg: %d\n",
  231. __func__, 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, "%s() error writing to rdmabuff reg: %d\n",
  239. __func__, 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, "%s() error writing to rdmaper reg: %d\n",
  247. __func__, 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, "%s() error writing to rdmactl reg: %d\n",
  255. __func__, 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 lpass_data *drvdata =
  265. snd_soc_platform_get_drvdata(soc_runtime->platform);
  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, "%s() error writing to irqclear reg: %d\n",
  281. __func__, 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, "%s() error writing to irqen reg: %d\n",
  290. __func__, 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, "%s() error writing to rdmactl reg: %d\n",
  299. __func__, 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, "%s() error writing to rdmactl reg: %d\n",
  312. __func__, 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, "%s() error writing to irqen reg: %d\n",
  320. __func__, 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 lpass_data *drvdata =
  332. snd_soc_platform_get_drvdata(soc_runtime->platform);
  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, "%s() error reading from rdmabase reg: %d\n",
  343. __func__, 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, "%s() error reading from rdmacurr reg: %d\n",
  350. __func__, 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, "%s() error writing to irqclear reg: %d\n",
  389. __func__, 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, "%s() error writing to irqclear reg: %d\n",
  401. __func__, rv);
  402. return IRQ_NONE;
  403. }
  404. dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__);
  405. snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
  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, "%s() error writing to irqclear reg: %d\n",
  414. __func__, rv);
  415. return IRQ_NONE;
  416. }
  417. dev_err(soc_runtime->dev, "%s() bus access error\n", __func__);
  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("%s() error reading from irqstat reg: %d\n",
  433. __func__, rv);
  434. return IRQ_NONE;
  435. }
  436. /* Handle per channel interrupts */
  437. for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
  438. if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
  439. rv = lpass_dma_interrupt_handler(
  440. drvdata->substream[chan],
  441. drvdata, chan, irqs);
  442. if (rv != IRQ_HANDLED)
  443. return rv;
  444. }
  445. }
  446. return IRQ_HANDLED;
  447. }
  448. static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
  449. {
  450. struct snd_pcm *pcm = soc_runtime->pcm;
  451. struct snd_pcm_substream *psubstream, *csubstream;
  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. soc_runtime->platform->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. soc_runtime->platform->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 struct snd_soc_platform_driver lpass_platform_driver = {
  492. .pcm_new = lpass_platform_pcm_new,
  493. .pcm_free = lpass_platform_pcm_free,
  494. .ops = &lpass_platform_pcm_ops,
  495. };
  496. int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
  497. {
  498. struct lpass_data *drvdata = platform_get_drvdata(pdev);
  499. struct lpass_variant *v = drvdata->variant;
  500. int ret;
  501. drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
  502. if (drvdata->lpaif_irq < 0) {
  503. dev_err(&pdev->dev, "%s() error getting irq handle: %d\n",
  504. __func__, drvdata->lpaif_irq);
  505. return -ENODEV;
  506. }
  507. /* ensure audio hardware is disabled */
  508. ret = regmap_write(drvdata->lpaif_map,
  509. LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
  510. if (ret) {
  511. dev_err(&pdev->dev, "%s() error writing to irqen reg: %d\n",
  512. __func__, 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, "%s() irq request failed: %d\n",
  520. __func__, ret);
  521. return ret;
  522. }
  523. return devm_snd_soc_register_platform(&pdev->dev,
  524. &lpass_platform_driver);
  525. }
  526. EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
  527. MODULE_DESCRIPTION("QTi LPASS Platform Driver");
  528. MODULE_LICENSE("GPL v2");