cobalt-alsa-pcm.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * ALSA PCM device for the
  4. * ALSA interface to cobalt PCM capture streams
  5. *
  6. * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
  7. * All rights reserved.
  8. */
  9. #include <linux/init.h>
  10. #include <linux/kernel.h>
  11. #include <linux/vmalloc.h>
  12. #include <linux/delay.h>
  13. #include <media/v4l2-device.h>
  14. #include <sound/core.h>
  15. #include <sound/pcm.h>
  16. #include "cobalt-driver.h"
  17. #include "cobalt-alsa.h"
  18. #include "cobalt-alsa-pcm.h"
  19. static unsigned int pcm_debug;
  20. module_param(pcm_debug, int, 0644);
  21. MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
  22. #define dprintk(fmt, arg...) \
  23. do { \
  24. if (pcm_debug) \
  25. pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
  26. } while (0)
  27. static const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
  28. .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
  29. SNDRV_PCM_INFO_MMAP |
  30. SNDRV_PCM_INFO_INTERLEAVED |
  31. SNDRV_PCM_INFO_MMAP_VALID,
  32. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
  33. .rates = SNDRV_PCM_RATE_48000,
  34. .rate_min = 48000,
  35. .rate_max = 48000,
  36. .channels_min = 1,
  37. .channels_max = 8,
  38. .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */
  39. .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */
  40. .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */
  41. .periods_min = 1,
  42. .periods_max = 4,
  43. };
  44. static const struct snd_pcm_hardware snd_cobalt_playback = {
  45. .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
  46. SNDRV_PCM_INFO_MMAP |
  47. SNDRV_PCM_INFO_INTERLEAVED |
  48. SNDRV_PCM_INFO_MMAP_VALID,
  49. .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
  50. .rates = SNDRV_PCM_RATE_48000,
  51. .rate_min = 48000,
  52. .rate_max = 48000,
  53. .channels_min = 1,
  54. .channels_max = 8,
  55. .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */
  56. .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */
  57. .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */
  58. .periods_min = 1,
  59. .periods_max = 4,
  60. };
  61. static void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
  62. {
  63. static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
  64. unsigned idx = 0;
  65. while (len >= (is_s32 ? 4 : 2)) {
  66. unsigned offset = map[idx] * 4;
  67. u32 val = src[offset + 1] + (src[offset + 2] << 8) +
  68. (src[offset + 3] << 16);
  69. if (is_s32) {
  70. *dst++ = 0;
  71. *dst++ = val & 0xff;
  72. }
  73. *dst++ = (val >> 8) & 0xff;
  74. *dst++ = (val >> 16) & 0xff;
  75. len -= is_s32 ? 4 : 2;
  76. idx++;
  77. }
  78. }
  79. static void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
  80. u8 *pcm_data,
  81. size_t skip,
  82. size_t samples)
  83. {
  84. struct snd_pcm_substream *substream;
  85. struct snd_pcm_runtime *runtime;
  86. unsigned long flags;
  87. unsigned int oldptr;
  88. unsigned int stride;
  89. int length = samples;
  90. int period_elapsed = 0;
  91. bool is_s32;
  92. dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
  93. pcm_data, samples);
  94. substream = cobsc->capture_pcm_substream;
  95. if (substream == NULL) {
  96. dprintk("substream was NULL\n");
  97. return;
  98. }
  99. runtime = substream->runtime;
  100. if (runtime == NULL) {
  101. dprintk("runtime was NULL\n");
  102. return;
  103. }
  104. is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
  105. stride = runtime->frame_bits >> 3;
  106. if (stride == 0) {
  107. dprintk("stride is zero\n");
  108. return;
  109. }
  110. if (length == 0) {
  111. dprintk("%s: length was zero\n", __func__);
  112. return;
  113. }
  114. if (runtime->dma_area == NULL) {
  115. dprintk("dma area was NULL - ignoring\n");
  116. return;
  117. }
  118. oldptr = cobsc->hwptr_done_capture;
  119. if (oldptr + length >= runtime->buffer_size) {
  120. unsigned int cnt = runtime->buffer_size - oldptr;
  121. unsigned i;
  122. for (i = 0; i < cnt; i++)
  123. sample_cpy(runtime->dma_area + (oldptr + i) * stride,
  124. pcm_data + i * skip,
  125. stride, is_s32);
  126. for (i = cnt; i < length; i++)
  127. sample_cpy(runtime->dma_area + (i - cnt) * stride,
  128. pcm_data + i * skip, stride, is_s32);
  129. } else {
  130. unsigned i;
  131. for (i = 0; i < length; i++)
  132. sample_cpy(runtime->dma_area + (oldptr + i) * stride,
  133. pcm_data + i * skip,
  134. stride, is_s32);
  135. }
  136. snd_pcm_stream_lock_irqsave(substream, flags);
  137. cobsc->hwptr_done_capture += length;
  138. if (cobsc->hwptr_done_capture >=
  139. runtime->buffer_size)
  140. cobsc->hwptr_done_capture -=
  141. runtime->buffer_size;
  142. cobsc->capture_transfer_done += length;
  143. if (cobsc->capture_transfer_done >=
  144. runtime->period_size) {
  145. cobsc->capture_transfer_done -=
  146. runtime->period_size;
  147. period_elapsed = 1;
  148. }
  149. snd_pcm_stream_unlock_irqrestore(substream, flags);
  150. if (period_elapsed)
  151. snd_pcm_period_elapsed(substream);
  152. }
  153. static int alsa_fnc(struct vb2_buffer *vb, void *priv)
  154. {
  155. struct cobalt_stream *s = priv;
  156. unsigned char *p = vb2_plane_vaddr(vb, 0);
  157. int i;
  158. if (pcm_debug) {
  159. pr_info("alsa: ");
  160. for (i = 0; i < 8 * 4; i++) {
  161. if (!(i & 3))
  162. pr_cont(" ");
  163. pr_cont("%02x", p[i]);
  164. }
  165. pr_cont("\n");
  166. }
  167. cobalt_alsa_announce_pcm_data(s->alsa,
  168. vb2_plane_vaddr(vb, 0),
  169. 8 * 4,
  170. vb2_get_plane_payload(vb, 0) / (8 * 4));
  171. return 0;
  172. }
  173. static int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
  174. {
  175. struct snd_pcm_runtime *runtime = substream->runtime;
  176. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  177. struct cobalt_stream *s = cobsc->s;
  178. runtime->hw = snd_cobalt_hdmi_capture;
  179. snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
  180. cobsc->capture_pcm_substream = substream;
  181. runtime->private_data = s;
  182. cobsc->alsa_record_cnt++;
  183. if (cobsc->alsa_record_cnt == 1) {
  184. int rc;
  185. rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
  186. if (rc) {
  187. cobsc->alsa_record_cnt--;
  188. return rc;
  189. }
  190. }
  191. return 0;
  192. }
  193. static int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
  194. {
  195. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  196. struct cobalt_stream *s = cobsc->s;
  197. cobsc->alsa_record_cnt--;
  198. if (cobsc->alsa_record_cnt == 0)
  199. vb2_thread_stop(&s->q);
  200. return 0;
  201. }
  202. static int snd_cobalt_pcm_ioctl(struct snd_pcm_substream *substream,
  203. unsigned int cmd, void *arg)
  204. {
  205. return snd_pcm_lib_ioctl(substream, cmd, arg);
  206. }
  207. static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
  208. size_t size)
  209. {
  210. struct snd_pcm_runtime *runtime = subs->runtime;
  211. dprintk("Allocating vbuffer\n");
  212. if (runtime->dma_area) {
  213. if (runtime->dma_bytes > size)
  214. return 0;
  215. vfree(runtime->dma_area);
  216. }
  217. runtime->dma_area = vmalloc(size);
  218. if (!runtime->dma_area)
  219. return -ENOMEM;
  220. runtime->dma_bytes = size;
  221. return 0;
  222. }
  223. static int snd_cobalt_pcm_hw_params(struct snd_pcm_substream *substream,
  224. struct snd_pcm_hw_params *params)
  225. {
  226. dprintk("%s called\n", __func__);
  227. return snd_pcm_alloc_vmalloc_buffer(substream,
  228. params_buffer_bytes(params));
  229. }
  230. static int snd_cobalt_pcm_hw_free(struct snd_pcm_substream *substream)
  231. {
  232. if (substream->runtime->dma_area) {
  233. dprintk("freeing pcm capture region\n");
  234. vfree(substream->runtime->dma_area);
  235. substream->runtime->dma_area = NULL;
  236. }
  237. return 0;
  238. }
  239. static int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
  240. {
  241. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  242. cobsc->hwptr_done_capture = 0;
  243. cobsc->capture_transfer_done = 0;
  244. return 0;
  245. }
  246. static int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
  247. {
  248. switch (cmd) {
  249. case SNDRV_PCM_TRIGGER_START:
  250. case SNDRV_PCM_TRIGGER_STOP:
  251. return 0;
  252. default:
  253. return -EINVAL;
  254. }
  255. return 0;
  256. }
  257. static
  258. snd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
  259. {
  260. snd_pcm_uframes_t hwptr_done;
  261. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  262. hwptr_done = cobsc->hwptr_done_capture;
  263. return hwptr_done;
  264. }
  265. static void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
  266. {
  267. static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
  268. unsigned idx = 0;
  269. while (len >= (is_s32 ? 4 : 2)) {
  270. unsigned offset = map[idx] * 4;
  271. u8 *out = dst + offset;
  272. *out++ = 0;
  273. if (is_s32) {
  274. src++;
  275. *out++ = *src++;
  276. } else {
  277. *out++ = 0;
  278. }
  279. *out++ = *src++;
  280. *out = *src++;
  281. len -= is_s32 ? 4 : 2;
  282. idx++;
  283. }
  284. }
  285. static void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
  286. u8 *pcm_data,
  287. size_t skip,
  288. size_t samples)
  289. {
  290. struct snd_pcm_substream *substream;
  291. struct snd_pcm_runtime *runtime;
  292. unsigned long flags;
  293. unsigned int pos;
  294. unsigned int stride;
  295. bool is_s32;
  296. unsigned i;
  297. dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
  298. pcm_data, samples);
  299. substream = cobsc->playback_pcm_substream;
  300. if (substream == NULL) {
  301. dprintk("substream was NULL\n");
  302. return;
  303. }
  304. runtime = substream->runtime;
  305. if (runtime == NULL) {
  306. dprintk("runtime was NULL\n");
  307. return;
  308. }
  309. is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
  310. stride = runtime->frame_bits >> 3;
  311. if (stride == 0) {
  312. dprintk("stride is zero\n");
  313. return;
  314. }
  315. if (samples == 0) {
  316. dprintk("%s: samples was zero\n", __func__);
  317. return;
  318. }
  319. if (runtime->dma_area == NULL) {
  320. dprintk("dma area was NULL - ignoring\n");
  321. return;
  322. }
  323. pos = cobsc->pb_pos % cobsc->pb_size;
  324. for (i = 0; i < cobsc->pb_count / (8 * 4); i++)
  325. pb_sample_cpy(pcm_data + i * skip,
  326. runtime->dma_area + pos + i * stride,
  327. stride, is_s32);
  328. snd_pcm_stream_lock_irqsave(substream, flags);
  329. cobsc->pb_pos += i * stride;
  330. snd_pcm_stream_unlock_irqrestore(substream, flags);
  331. if (cobsc->pb_pos % cobsc->pb_count == 0)
  332. snd_pcm_period_elapsed(substream);
  333. }
  334. static int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
  335. {
  336. struct cobalt_stream *s = priv;
  337. if (s->alsa->alsa_pb_channel)
  338. cobalt_alsa_pb_pcm_data(s->alsa,
  339. vb2_plane_vaddr(vb, 0),
  340. 8 * 4,
  341. vb2_get_plane_payload(vb, 0) / (8 * 4));
  342. return 0;
  343. }
  344. static int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
  345. {
  346. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  347. struct snd_pcm_runtime *runtime = substream->runtime;
  348. struct cobalt_stream *s = cobsc->s;
  349. runtime->hw = snd_cobalt_playback;
  350. snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
  351. cobsc->playback_pcm_substream = substream;
  352. runtime->private_data = s;
  353. cobsc->alsa_playback_cnt++;
  354. if (cobsc->alsa_playback_cnt == 1) {
  355. int rc;
  356. rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
  357. if (rc) {
  358. cobsc->alsa_playback_cnt--;
  359. return rc;
  360. }
  361. }
  362. return 0;
  363. }
  364. static int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
  365. {
  366. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  367. struct cobalt_stream *s = cobsc->s;
  368. cobsc->alsa_playback_cnt--;
  369. if (cobsc->alsa_playback_cnt == 0)
  370. vb2_thread_stop(&s->q);
  371. return 0;
  372. }
  373. static int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
  374. {
  375. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  376. cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream);
  377. cobsc->pb_count = snd_pcm_lib_period_bytes(substream);
  378. cobsc->pb_pos = 0;
  379. return 0;
  380. }
  381. static int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
  382. int cmd)
  383. {
  384. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  385. switch (cmd) {
  386. case SNDRV_PCM_TRIGGER_START:
  387. if (cobsc->alsa_pb_channel)
  388. return -EBUSY;
  389. cobsc->alsa_pb_channel = true;
  390. return 0;
  391. case SNDRV_PCM_TRIGGER_STOP:
  392. cobsc->alsa_pb_channel = false;
  393. return 0;
  394. default:
  395. return -EINVAL;
  396. }
  397. }
  398. static
  399. snd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
  400. {
  401. struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
  402. size_t ptr;
  403. ptr = cobsc->pb_pos;
  404. return bytes_to_frames(substream->runtime, ptr) %
  405. substream->runtime->buffer_size;
  406. }
  407. static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
  408. unsigned long offset)
  409. {
  410. void *pageptr = subs->runtime->dma_area + offset;
  411. return vmalloc_to_page(pageptr);
  412. }
  413. static const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = {
  414. .open = snd_cobalt_pcm_capture_open,
  415. .close = snd_cobalt_pcm_capture_close,
  416. .ioctl = snd_cobalt_pcm_ioctl,
  417. .hw_params = snd_cobalt_pcm_hw_params,
  418. .hw_free = snd_cobalt_pcm_hw_free,
  419. .prepare = snd_cobalt_pcm_prepare,
  420. .trigger = snd_cobalt_pcm_trigger,
  421. .pointer = snd_cobalt_pcm_pointer,
  422. .page = snd_pcm_get_vmalloc_page,
  423. };
  424. static const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = {
  425. .open = snd_cobalt_pcm_playback_open,
  426. .close = snd_cobalt_pcm_playback_close,
  427. .ioctl = snd_cobalt_pcm_ioctl,
  428. .hw_params = snd_cobalt_pcm_hw_params,
  429. .hw_free = snd_cobalt_pcm_hw_free,
  430. .prepare = snd_cobalt_pcm_pb_prepare,
  431. .trigger = snd_cobalt_pcm_pb_trigger,
  432. .pointer = snd_cobalt_pcm_pb_pointer,
  433. .page = snd_pcm_get_vmalloc_page,
  434. };
  435. int snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
  436. {
  437. struct snd_pcm *sp;
  438. struct snd_card *sc = cobsc->sc;
  439. struct cobalt_stream *s = cobsc->s;
  440. struct cobalt *cobalt = s->cobalt;
  441. int ret;
  442. s->q.gfp_flags |= __GFP_ZERO;
  443. if (!s->is_output) {
  444. cobalt_s_bit_sysctrl(cobalt,
  445. COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
  446. 0);
  447. mdelay(2);
  448. cobalt_s_bit_sysctrl(cobalt,
  449. COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
  450. 1);
  451. mdelay(1);
  452. ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
  453. 0, /* PCM device 0, the only one for this card */
  454. 0, /* 0 playback substreams */
  455. 1, /* 1 capture substream */
  456. &sp);
  457. if (ret) {
  458. cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
  459. ret);
  460. goto err_exit;
  461. }
  462. snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
  463. &snd_cobalt_pcm_capture_ops);
  464. sp->info_flags = 0;
  465. sp->private_data = cobsc;
  466. strlcpy(sp->name, "cobalt", sizeof(sp->name));
  467. } else {
  468. cobalt_s_bit_sysctrl(cobalt,
  469. COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
  470. mdelay(2);
  471. cobalt_s_bit_sysctrl(cobalt,
  472. COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1);
  473. mdelay(1);
  474. ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
  475. 0, /* PCM device 0, the only one for this card */
  476. 1, /* 0 playback substreams */
  477. 0, /* 1 capture substream */
  478. &sp);
  479. if (ret) {
  480. cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
  481. ret);
  482. goto err_exit;
  483. }
  484. snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
  485. &snd_cobalt_pcm_playback_ops);
  486. sp->info_flags = 0;
  487. sp->private_data = cobsc;
  488. strlcpy(sp->name, "cobalt", sizeof(sp->name));
  489. }
  490. return 0;
  491. err_exit:
  492. return ret;
  493. }