cobalt-alsa-pcm.c 14 KB

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