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