lpass-platform.c 16 KB

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