18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ALSA PCM device for the 48c2ecf20Sopenharmony_ci * ALSA interface to cobalt PCM capture streams 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates. 78c2ecf20Sopenharmony_ci * All rights reserved. 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/delay.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <media/v4l2-device.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <sound/core.h> 178c2ecf20Sopenharmony_ci#include <sound/pcm.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include "cobalt-driver.h" 208c2ecf20Sopenharmony_ci#include "cobalt-alsa.h" 218c2ecf20Sopenharmony_ci#include "cobalt-alsa-pcm.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic unsigned int pcm_debug; 248c2ecf20Sopenharmony_cimodule_param(pcm_debug, int, 0644); 258c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define dprintk(fmt, arg...) \ 288c2ecf20Sopenharmony_ci do { \ 298c2ecf20Sopenharmony_ci if (pcm_debug) \ 308c2ecf20Sopenharmony_ci pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \ 318c2ecf20Sopenharmony_ci } while (0) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cobalt_hdmi_capture = { 348c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 358c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 368c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 378c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci .rate_min = 48000, 448c2ecf20Sopenharmony_ci .rate_max = 48000, 458c2ecf20Sopenharmony_ci .channels_min = 1, 468c2ecf20Sopenharmony_ci .channels_max = 8, 478c2ecf20Sopenharmony_ci .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */ 488c2ecf20Sopenharmony_ci .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */ 498c2ecf20Sopenharmony_ci .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */ 508c2ecf20Sopenharmony_ci .periods_min = 1, 518c2ecf20Sopenharmony_ci .periods_max = 4, 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic const struct snd_pcm_hardware snd_cobalt_playback = { 558c2ecf20Sopenharmony_ci .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | 568c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP | 578c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_INTERLEAVED | 588c2ecf20Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID, 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci .rates = SNDRV_PCM_RATE_48000, 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci .rate_min = 48000, 658c2ecf20Sopenharmony_ci .rate_max = 48000, 668c2ecf20Sopenharmony_ci .channels_min = 1, 678c2ecf20Sopenharmony_ci .channels_max = 8, 688c2ecf20Sopenharmony_ci .buffer_bytes_max = 4 * 240 * 8 * 4, /* 5 ms of data */ 698c2ecf20Sopenharmony_ci .period_bytes_min = 1920, /* 1 sample = 8 * 4 bytes */ 708c2ecf20Sopenharmony_ci .period_bytes_max = 240 * 8 * 4, /* 5 ms of 8 channel data */ 718c2ecf20Sopenharmony_ci .periods_min = 1, 728c2ecf20Sopenharmony_ci .periods_max = 4, 738c2ecf20Sopenharmony_ci}; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32) 768c2ecf20Sopenharmony_ci{ 778c2ecf20Sopenharmony_ci static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 }; 788c2ecf20Sopenharmony_ci unsigned idx = 0; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci while (len >= (is_s32 ? 4 : 2)) { 818c2ecf20Sopenharmony_ci unsigned offset = map[idx] * 4; 828c2ecf20Sopenharmony_ci u32 val = src[offset + 1] + (src[offset + 2] << 8) + 838c2ecf20Sopenharmony_ci (src[offset + 3] << 16); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (is_s32) { 868c2ecf20Sopenharmony_ci *dst++ = 0; 878c2ecf20Sopenharmony_ci *dst++ = val & 0xff; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci *dst++ = (val >> 8) & 0xff; 908c2ecf20Sopenharmony_ci *dst++ = (val >> 16) & 0xff; 918c2ecf20Sopenharmony_ci len -= is_s32 ? 4 : 2; 928c2ecf20Sopenharmony_ci idx++; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc, 978c2ecf20Sopenharmony_ci u8 *pcm_data, 988c2ecf20Sopenharmony_ci size_t skip, 998c2ecf20Sopenharmony_ci size_t samples) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 1028c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 1038c2ecf20Sopenharmony_ci unsigned long flags; 1048c2ecf20Sopenharmony_ci unsigned int oldptr; 1058c2ecf20Sopenharmony_ci unsigned int stride; 1068c2ecf20Sopenharmony_ci int length = samples; 1078c2ecf20Sopenharmony_ci int period_elapsed = 0; 1088c2ecf20Sopenharmony_ci bool is_s32; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc, 1118c2ecf20Sopenharmony_ci pcm_data, samples); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci substream = cobsc->capture_pcm_substream; 1148c2ecf20Sopenharmony_ci if (substream == NULL) { 1158c2ecf20Sopenharmony_ci dprintk("substream was NULL\n"); 1168c2ecf20Sopenharmony_ci return; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci runtime = substream->runtime; 1208c2ecf20Sopenharmony_ci if (runtime == NULL) { 1218c2ecf20Sopenharmony_ci dprintk("runtime was NULL\n"); 1228c2ecf20Sopenharmony_ci return; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci stride = runtime->frame_bits >> 3; 1278c2ecf20Sopenharmony_ci if (stride == 0) { 1288c2ecf20Sopenharmony_ci dprintk("stride is zero\n"); 1298c2ecf20Sopenharmony_ci return; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci if (length == 0) { 1338c2ecf20Sopenharmony_ci dprintk("%s: length was zero\n", __func__); 1348c2ecf20Sopenharmony_ci return; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (runtime->dma_area == NULL) { 1388c2ecf20Sopenharmony_ci dprintk("dma area was NULL - ignoring\n"); 1398c2ecf20Sopenharmony_ci return; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci oldptr = cobsc->hwptr_done_capture; 1438c2ecf20Sopenharmony_ci if (oldptr + length >= runtime->buffer_size) { 1448c2ecf20Sopenharmony_ci unsigned int cnt = runtime->buffer_size - oldptr; 1458c2ecf20Sopenharmony_ci unsigned i; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (i = 0; i < cnt; i++) 1488c2ecf20Sopenharmony_ci sample_cpy(runtime->dma_area + (oldptr + i) * stride, 1498c2ecf20Sopenharmony_ci pcm_data + i * skip, 1508c2ecf20Sopenharmony_ci stride, is_s32); 1518c2ecf20Sopenharmony_ci for (i = cnt; i < length; i++) 1528c2ecf20Sopenharmony_ci sample_cpy(runtime->dma_area + (i - cnt) * stride, 1538c2ecf20Sopenharmony_ci pcm_data + i * skip, stride, is_s32); 1548c2ecf20Sopenharmony_ci } else { 1558c2ecf20Sopenharmony_ci unsigned i; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci for (i = 0; i < length; i++) 1588c2ecf20Sopenharmony_ci sample_cpy(runtime->dma_area + (oldptr + i) * stride, 1598c2ecf20Sopenharmony_ci pcm_data + i * skip, 1608c2ecf20Sopenharmony_ci stride, is_s32); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irqsave(substream, flags); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci cobsc->hwptr_done_capture += length; 1658c2ecf20Sopenharmony_ci if (cobsc->hwptr_done_capture >= 1668c2ecf20Sopenharmony_ci runtime->buffer_size) 1678c2ecf20Sopenharmony_ci cobsc->hwptr_done_capture -= 1688c2ecf20Sopenharmony_ci runtime->buffer_size; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci cobsc->capture_transfer_done += length; 1718c2ecf20Sopenharmony_ci if (cobsc->capture_transfer_done >= 1728c2ecf20Sopenharmony_ci runtime->period_size) { 1738c2ecf20Sopenharmony_ci cobsc->capture_transfer_done -= 1748c2ecf20Sopenharmony_ci runtime->period_size; 1758c2ecf20Sopenharmony_ci period_elapsed = 1; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(substream, flags); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (period_elapsed) 1818c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int alsa_fnc(struct vb2_buffer *vb, void *priv) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci struct cobalt_stream *s = priv; 1878c2ecf20Sopenharmony_ci unsigned char *p = vb2_plane_vaddr(vb, 0); 1888c2ecf20Sopenharmony_ci int i; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (pcm_debug) { 1918c2ecf20Sopenharmony_ci pr_info("alsa: "); 1928c2ecf20Sopenharmony_ci for (i = 0; i < 8 * 4; i++) { 1938c2ecf20Sopenharmony_ci if (!(i & 3)) 1948c2ecf20Sopenharmony_ci pr_cont(" "); 1958c2ecf20Sopenharmony_ci pr_cont("%02x", p[i]); 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci pr_cont("\n"); 1988c2ecf20Sopenharmony_ci } 1998c2ecf20Sopenharmony_ci cobalt_alsa_announce_pcm_data(s->alsa, 2008c2ecf20Sopenharmony_ci vb2_plane_vaddr(vb, 0), 2018c2ecf20Sopenharmony_ci 8 * 4, 2028c2ecf20Sopenharmony_ci vb2_get_plane_payload(vb, 0) / (8 * 4)); 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 2098c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 2108c2ecf20Sopenharmony_ci struct cobalt_stream *s = cobsc->s; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci runtime->hw = snd_cobalt_hdmi_capture; 2138c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 2148c2ecf20Sopenharmony_ci cobsc->capture_pcm_substream = substream; 2158c2ecf20Sopenharmony_ci runtime->private_data = s; 2168c2ecf20Sopenharmony_ci cobsc->alsa_record_cnt++; 2178c2ecf20Sopenharmony_ci if (cobsc->alsa_record_cnt == 1) { 2188c2ecf20Sopenharmony_ci int rc; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name); 2218c2ecf20Sopenharmony_ci if (rc) { 2228c2ecf20Sopenharmony_ci cobsc->alsa_record_cnt--; 2238c2ecf20Sopenharmony_ci return rc; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci return 0; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 2328c2ecf20Sopenharmony_ci struct cobalt_stream *s = cobsc->s; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci cobsc->alsa_record_cnt--; 2358c2ecf20Sopenharmony_ci if (cobsc->alsa_record_cnt == 0) 2368c2ecf20Sopenharmony_ci vb2_thread_stop(&s->q); 2378c2ecf20Sopenharmony_ci return 0; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci cobsc->hwptr_done_capture = 0; 2458c2ecf20Sopenharmony_ci cobsc->capture_transfer_done = 0; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return 0; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci switch (cmd) { 2538c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 2548c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 2558c2ecf20Sopenharmony_ci return 0; 2568c2ecf20Sopenharmony_ci default: 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic 2638c2ecf20Sopenharmony_cisnd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci snd_pcm_uframes_t hwptr_done; 2668c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci hwptr_done = cobsc->hwptr_done_capture; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci return hwptr_done; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 }; 2768c2ecf20Sopenharmony_ci unsigned idx = 0; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci while (len >= (is_s32 ? 4 : 2)) { 2798c2ecf20Sopenharmony_ci unsigned offset = map[idx] * 4; 2808c2ecf20Sopenharmony_ci u8 *out = dst + offset; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci *out++ = 0; 2838c2ecf20Sopenharmony_ci if (is_s32) { 2848c2ecf20Sopenharmony_ci src++; 2858c2ecf20Sopenharmony_ci *out++ = *src++; 2868c2ecf20Sopenharmony_ci } else { 2878c2ecf20Sopenharmony_ci *out++ = 0; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci *out++ = *src++; 2908c2ecf20Sopenharmony_ci *out = *src++; 2918c2ecf20Sopenharmony_ci len -= is_s32 ? 4 : 2; 2928c2ecf20Sopenharmony_ci idx++; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci} 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_cistatic void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc, 2978c2ecf20Sopenharmony_ci u8 *pcm_data, 2988c2ecf20Sopenharmony_ci size_t skip, 2998c2ecf20Sopenharmony_ci size_t samples) 3008c2ecf20Sopenharmony_ci{ 3018c2ecf20Sopenharmony_ci struct snd_pcm_substream *substream; 3028c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime; 3038c2ecf20Sopenharmony_ci unsigned long flags; 3048c2ecf20Sopenharmony_ci unsigned int pos; 3058c2ecf20Sopenharmony_ci unsigned int stride; 3068c2ecf20Sopenharmony_ci bool is_s32; 3078c2ecf20Sopenharmony_ci unsigned i; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc, 3108c2ecf20Sopenharmony_ci pcm_data, samples); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci substream = cobsc->playback_pcm_substream; 3138c2ecf20Sopenharmony_ci if (substream == NULL) { 3148c2ecf20Sopenharmony_ci dprintk("substream was NULL\n"); 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci runtime = substream->runtime; 3198c2ecf20Sopenharmony_ci if (runtime == NULL) { 3208c2ecf20Sopenharmony_ci dprintk("runtime was NULL\n"); 3218c2ecf20Sopenharmony_ci return; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE; 3258c2ecf20Sopenharmony_ci stride = runtime->frame_bits >> 3; 3268c2ecf20Sopenharmony_ci if (stride == 0) { 3278c2ecf20Sopenharmony_ci dprintk("stride is zero\n"); 3288c2ecf20Sopenharmony_ci return; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci if (samples == 0) { 3328c2ecf20Sopenharmony_ci dprintk("%s: samples was zero\n", __func__); 3338c2ecf20Sopenharmony_ci return; 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci if (runtime->dma_area == NULL) { 3378c2ecf20Sopenharmony_ci dprintk("dma area was NULL - ignoring\n"); 3388c2ecf20Sopenharmony_ci return; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci pos = cobsc->pb_pos % cobsc->pb_size; 3428c2ecf20Sopenharmony_ci for (i = 0; i < cobsc->pb_count / (8 * 4); i++) 3438c2ecf20Sopenharmony_ci pb_sample_cpy(pcm_data + i * skip, 3448c2ecf20Sopenharmony_ci runtime->dma_area + pos + i * stride, 3458c2ecf20Sopenharmony_ci stride, is_s32); 3468c2ecf20Sopenharmony_ci snd_pcm_stream_lock_irqsave(substream, flags); 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci cobsc->pb_pos += i * stride; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci snd_pcm_stream_unlock_irqrestore(substream, flags); 3518c2ecf20Sopenharmony_ci if (cobsc->pb_pos % cobsc->pb_count == 0) 3528c2ecf20Sopenharmony_ci snd_pcm_period_elapsed(substream); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_cistatic int alsa_pb_fnc(struct vb2_buffer *vb, void *priv) 3568c2ecf20Sopenharmony_ci{ 3578c2ecf20Sopenharmony_ci struct cobalt_stream *s = priv; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (s->alsa->alsa_pb_channel) 3608c2ecf20Sopenharmony_ci cobalt_alsa_pb_pcm_data(s->alsa, 3618c2ecf20Sopenharmony_ci vb2_plane_vaddr(vb, 0), 3628c2ecf20Sopenharmony_ci 8 * 4, 3638c2ecf20Sopenharmony_ci vb2_get_plane_payload(vb, 0) / (8 * 4)); 3648c2ecf20Sopenharmony_ci return 0; 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 3708c2ecf20Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 3718c2ecf20Sopenharmony_ci struct cobalt_stream *s = cobsc->s; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci runtime->hw = snd_cobalt_playback; 3748c2ecf20Sopenharmony_ci snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 3758c2ecf20Sopenharmony_ci cobsc->playback_pcm_substream = substream; 3768c2ecf20Sopenharmony_ci runtime->private_data = s; 3778c2ecf20Sopenharmony_ci cobsc->alsa_playback_cnt++; 3788c2ecf20Sopenharmony_ci if (cobsc->alsa_playback_cnt == 1) { 3798c2ecf20Sopenharmony_ci int rc; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name); 3828c2ecf20Sopenharmony_ci if (rc) { 3838c2ecf20Sopenharmony_ci cobsc->alsa_playback_cnt--; 3848c2ecf20Sopenharmony_ci return rc; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci return 0; 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 3948c2ecf20Sopenharmony_ci struct cobalt_stream *s = cobsc->s; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci cobsc->alsa_playback_cnt--; 3978c2ecf20Sopenharmony_ci if (cobsc->alsa_playback_cnt == 0) 3988c2ecf20Sopenharmony_ci vb2_thread_stop(&s->q); 3998c2ecf20Sopenharmony_ci return 0; 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream); 4078c2ecf20Sopenharmony_ci cobsc->pb_count = snd_pcm_lib_period_bytes(substream); 4088c2ecf20Sopenharmony_ci cobsc->pb_pos = 0; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci return 0; 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_cistatic int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream, 4148c2ecf20Sopenharmony_ci int cmd) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci switch (cmd) { 4198c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 4208c2ecf20Sopenharmony_ci if (cobsc->alsa_pb_channel) 4218c2ecf20Sopenharmony_ci return -EBUSY; 4228c2ecf20Sopenharmony_ci cobsc->alsa_pb_channel = true; 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 4258c2ecf20Sopenharmony_ci cobsc->alsa_pb_channel = false; 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci default: 4288c2ecf20Sopenharmony_ci return -EINVAL; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci} 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_cistatic 4338c2ecf20Sopenharmony_cisnd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream) 4348c2ecf20Sopenharmony_ci{ 4358c2ecf20Sopenharmony_ci struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream); 4368c2ecf20Sopenharmony_ci size_t ptr; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci ptr = cobsc->pb_pos; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return bytes_to_frames(substream->runtime, ptr) % 4418c2ecf20Sopenharmony_ci substream->runtime->buffer_size; 4428c2ecf20Sopenharmony_ci} 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = { 4458c2ecf20Sopenharmony_ci .open = snd_cobalt_pcm_capture_open, 4468c2ecf20Sopenharmony_ci .close = snd_cobalt_pcm_capture_close, 4478c2ecf20Sopenharmony_ci .prepare = snd_cobalt_pcm_prepare, 4488c2ecf20Sopenharmony_ci .trigger = snd_cobalt_pcm_trigger, 4498c2ecf20Sopenharmony_ci .pointer = snd_cobalt_pcm_pointer, 4508c2ecf20Sopenharmony_ci}; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cistatic const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = { 4538c2ecf20Sopenharmony_ci .open = snd_cobalt_pcm_playback_open, 4548c2ecf20Sopenharmony_ci .close = snd_cobalt_pcm_playback_close, 4558c2ecf20Sopenharmony_ci .prepare = snd_cobalt_pcm_pb_prepare, 4568c2ecf20Sopenharmony_ci .trigger = snd_cobalt_pcm_pb_trigger, 4578c2ecf20Sopenharmony_ci .pointer = snd_cobalt_pcm_pb_pointer, 4588c2ecf20Sopenharmony_ci}; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ciint snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci struct snd_pcm *sp; 4638c2ecf20Sopenharmony_ci struct snd_card *sc = cobsc->sc; 4648c2ecf20Sopenharmony_ci struct cobalt_stream *s = cobsc->s; 4658c2ecf20Sopenharmony_ci struct cobalt *cobalt = s->cobalt; 4668c2ecf20Sopenharmony_ci int ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci s->q.gfp_flags |= __GFP_ZERO; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (!s->is_output) { 4718c2ecf20Sopenharmony_ci cobalt_s_bit_sysctrl(cobalt, 4728c2ecf20Sopenharmony_ci COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel), 4738c2ecf20Sopenharmony_ci 0); 4748c2ecf20Sopenharmony_ci mdelay(2); 4758c2ecf20Sopenharmony_ci cobalt_s_bit_sysctrl(cobalt, 4768c2ecf20Sopenharmony_ci COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel), 4778c2ecf20Sopenharmony_ci 1); 4788c2ecf20Sopenharmony_ci mdelay(1); 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI", 4818c2ecf20Sopenharmony_ci 0, /* PCM device 0, the only one for this card */ 4828c2ecf20Sopenharmony_ci 0, /* 0 playback substreams */ 4838c2ecf20Sopenharmony_ci 1, /* 1 capture substream */ 4848c2ecf20Sopenharmony_ci &sp); 4858c2ecf20Sopenharmony_ci if (ret) { 4868c2ecf20Sopenharmony_ci cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n", 4878c2ecf20Sopenharmony_ci ret); 4888c2ecf20Sopenharmony_ci goto err_exit; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, 4928c2ecf20Sopenharmony_ci &snd_cobalt_pcm_capture_ops); 4938c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, 4948c2ecf20Sopenharmony_ci NULL, 0, 0); 4958c2ecf20Sopenharmony_ci sp->info_flags = 0; 4968c2ecf20Sopenharmony_ci sp->private_data = cobsc; 4978c2ecf20Sopenharmony_ci strscpy(sp->name, "cobalt", sizeof(sp->name)); 4988c2ecf20Sopenharmony_ci } else { 4998c2ecf20Sopenharmony_ci cobalt_s_bit_sysctrl(cobalt, 5008c2ecf20Sopenharmony_ci COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0); 5018c2ecf20Sopenharmony_ci mdelay(2); 5028c2ecf20Sopenharmony_ci cobalt_s_bit_sysctrl(cobalt, 5038c2ecf20Sopenharmony_ci COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1); 5048c2ecf20Sopenharmony_ci mdelay(1); 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI", 5078c2ecf20Sopenharmony_ci 0, /* PCM device 0, the only one for this card */ 5088c2ecf20Sopenharmony_ci 1, /* 0 playback substreams */ 5098c2ecf20Sopenharmony_ci 0, /* 1 capture substream */ 5108c2ecf20Sopenharmony_ci &sp); 5118c2ecf20Sopenharmony_ci if (ret) { 5128c2ecf20Sopenharmony_ci cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n", 5138c2ecf20Sopenharmony_ci ret); 5148c2ecf20Sopenharmony_ci goto err_exit; 5158c2ecf20Sopenharmony_ci } 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK, 5188c2ecf20Sopenharmony_ci &snd_cobalt_pcm_playback_ops); 5198c2ecf20Sopenharmony_ci snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC, 5208c2ecf20Sopenharmony_ci NULL, 0, 0); 5218c2ecf20Sopenharmony_ci sp->info_flags = 0; 5228c2ecf20Sopenharmony_ci sp->private_data = cobsc; 5238c2ecf20Sopenharmony_ci strscpy(sp->name, "cobalt", sizeof(sp->name)); 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci return 0; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_cierr_exit: 5298c2ecf20Sopenharmony_ci return ret; 5308c2ecf20Sopenharmony_ci} 531