162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  ALSA PCM device for the
462306a36Sopenharmony_ci *  ALSA interface to cobalt PCM capture streams
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci *  Copyright 2014-2015 Cisco Systems, Inc. and/or its affiliates.
762306a36Sopenharmony_ci *  All rights reserved.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/init.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/delay.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <media/v4l2-device.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <sound/core.h>
1762306a36Sopenharmony_ci#include <sound/pcm.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include "cobalt-driver.h"
2062306a36Sopenharmony_ci#include "cobalt-alsa.h"
2162306a36Sopenharmony_ci#include "cobalt-alsa-pcm.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic unsigned int pcm_debug;
2462306a36Sopenharmony_cimodule_param(pcm_debug, int, 0644);
2562306a36Sopenharmony_ciMODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define dprintk(fmt, arg...) \
2862306a36Sopenharmony_ci	do { \
2962306a36Sopenharmony_ci		if (pcm_debug) \
3062306a36Sopenharmony_ci			pr_info("cobalt-alsa-pcm %s: " fmt, __func__, ##arg); \
3162306a36Sopenharmony_ci	} while (0)
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cobalt_hdmi_capture = {
3462306a36Sopenharmony_ci	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
3562306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP           |
3662306a36Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED    |
3762306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID,
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	.rates = SNDRV_PCM_RATE_48000,
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	.rate_min = 48000,
4462306a36Sopenharmony_ci	.rate_max = 48000,
4562306a36Sopenharmony_ci	.channels_min = 1,
4662306a36Sopenharmony_ci	.channels_max = 8,
4762306a36Sopenharmony_ci	.buffer_bytes_max = 4 * 240 * 8 * 4,	/* 5 ms of data */
4862306a36Sopenharmony_ci	.period_bytes_min = 1920,		/* 1 sample = 8 * 4 bytes */
4962306a36Sopenharmony_ci	.period_bytes_max = 240 * 8 * 4,	/* 5 ms of 8 channel data */
5062306a36Sopenharmony_ci	.periods_min = 1,
5162306a36Sopenharmony_ci	.periods_max = 4,
5262306a36Sopenharmony_ci};
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_cobalt_playback = {
5562306a36Sopenharmony_ci	.info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
5662306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP           |
5762306a36Sopenharmony_ci		SNDRV_PCM_INFO_INTERLEAVED    |
5862306a36Sopenharmony_ci		SNDRV_PCM_INFO_MMAP_VALID,
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE,
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	.rates = SNDRV_PCM_RATE_48000,
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	.rate_min = 48000,
6562306a36Sopenharmony_ci	.rate_max = 48000,
6662306a36Sopenharmony_ci	.channels_min = 1,
6762306a36Sopenharmony_ci	.channels_max = 8,
6862306a36Sopenharmony_ci	.buffer_bytes_max = 4 * 240 * 8 * 4,	/* 5 ms of data */
6962306a36Sopenharmony_ci	.period_bytes_min = 1920,		/* 1 sample = 8 * 4 bytes */
7062306a36Sopenharmony_ci	.period_bytes_max = 240 * 8 * 4,	/* 5 ms of 8 channel data */
7162306a36Sopenharmony_ci	.periods_min = 1,
7262306a36Sopenharmony_ci	.periods_max = 4,
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic void sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
7862306a36Sopenharmony_ci	unsigned idx = 0;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	while (len >= (is_s32 ? 4 : 2)) {
8162306a36Sopenharmony_ci		unsigned offset = map[idx] * 4;
8262306a36Sopenharmony_ci		u32 val = src[offset + 1] + (src[offset + 2] << 8) +
8362306a36Sopenharmony_ci			 (src[offset + 3] << 16);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci		if (is_s32) {
8662306a36Sopenharmony_ci			*dst++ = 0;
8762306a36Sopenharmony_ci			*dst++ = val & 0xff;
8862306a36Sopenharmony_ci		}
8962306a36Sopenharmony_ci		*dst++ = (val >> 8) & 0xff;
9062306a36Sopenharmony_ci		*dst++ = (val >> 16) & 0xff;
9162306a36Sopenharmony_ci		len -= is_s32 ? 4 : 2;
9262306a36Sopenharmony_ci		idx++;
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic void cobalt_alsa_announce_pcm_data(struct snd_cobalt_card *cobsc,
9762306a36Sopenharmony_ci					u8 *pcm_data,
9862306a36Sopenharmony_ci					size_t skip,
9962306a36Sopenharmony_ci					size_t samples)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
10262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime;
10362306a36Sopenharmony_ci	unsigned long flags;
10462306a36Sopenharmony_ci	unsigned int oldptr;
10562306a36Sopenharmony_ci	unsigned int stride;
10662306a36Sopenharmony_ci	int length = samples;
10762306a36Sopenharmony_ci	int period_elapsed = 0;
10862306a36Sopenharmony_ci	bool is_s32;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	dprintk("cobalt alsa announce ptr=%p data=%p num_bytes=%zd\n", cobsc,
11162306a36Sopenharmony_ci		pcm_data, samples);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	substream = cobsc->capture_pcm_substream;
11462306a36Sopenharmony_ci	if (substream == NULL) {
11562306a36Sopenharmony_ci		dprintk("substream was NULL\n");
11662306a36Sopenharmony_ci		return;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	runtime = substream->runtime;
12062306a36Sopenharmony_ci	if (runtime == NULL) {
12162306a36Sopenharmony_ci		dprintk("runtime was NULL\n");
12262306a36Sopenharmony_ci		return;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	stride = runtime->frame_bits >> 3;
12762306a36Sopenharmony_ci	if (stride == 0) {
12862306a36Sopenharmony_ci		dprintk("stride is zero\n");
12962306a36Sopenharmony_ci		return;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (length == 0) {
13362306a36Sopenharmony_ci		dprintk("%s: length was zero\n", __func__);
13462306a36Sopenharmony_ci		return;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	if (runtime->dma_area == NULL) {
13862306a36Sopenharmony_ci		dprintk("dma area was NULL - ignoring\n");
13962306a36Sopenharmony_ci		return;
14062306a36Sopenharmony_ci	}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	oldptr = cobsc->hwptr_done_capture;
14362306a36Sopenharmony_ci	if (oldptr + length >= runtime->buffer_size) {
14462306a36Sopenharmony_ci		unsigned int cnt = runtime->buffer_size - oldptr;
14562306a36Sopenharmony_ci		unsigned i;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		for (i = 0; i < cnt; i++)
14862306a36Sopenharmony_ci			sample_cpy(runtime->dma_area + (oldptr + i) * stride,
14962306a36Sopenharmony_ci					pcm_data + i * skip,
15062306a36Sopenharmony_ci					stride, is_s32);
15162306a36Sopenharmony_ci		for (i = cnt; i < length; i++)
15262306a36Sopenharmony_ci			sample_cpy(runtime->dma_area + (i - cnt) * stride,
15362306a36Sopenharmony_ci					pcm_data + i * skip, stride, is_s32);
15462306a36Sopenharmony_ci	} else {
15562306a36Sopenharmony_ci		unsigned i;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci		for (i = 0; i < length; i++)
15862306a36Sopenharmony_ci			sample_cpy(runtime->dma_area + (oldptr + i) * stride,
15962306a36Sopenharmony_ci					pcm_data + i * skip,
16062306a36Sopenharmony_ci					stride, is_s32);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	snd_pcm_stream_lock_irqsave(substream, flags);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	cobsc->hwptr_done_capture += length;
16562306a36Sopenharmony_ci	if (cobsc->hwptr_done_capture >=
16662306a36Sopenharmony_ci	    runtime->buffer_size)
16762306a36Sopenharmony_ci		cobsc->hwptr_done_capture -=
16862306a36Sopenharmony_ci			runtime->buffer_size;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	cobsc->capture_transfer_done += length;
17162306a36Sopenharmony_ci	if (cobsc->capture_transfer_done >=
17262306a36Sopenharmony_ci	    runtime->period_size) {
17362306a36Sopenharmony_ci		cobsc->capture_transfer_done -=
17462306a36Sopenharmony_ci			runtime->period_size;
17562306a36Sopenharmony_ci		period_elapsed = 1;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	snd_pcm_stream_unlock_irqrestore(substream, flags);
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	if (period_elapsed)
18162306a36Sopenharmony_ci		snd_pcm_period_elapsed(substream);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic int alsa_fnc(struct vb2_buffer *vb, void *priv)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	struct cobalt_stream *s = priv;
18762306a36Sopenharmony_ci	unsigned char *p = vb2_plane_vaddr(vb, 0);
18862306a36Sopenharmony_ci	int i;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (pcm_debug) {
19162306a36Sopenharmony_ci		pr_info("alsa: ");
19262306a36Sopenharmony_ci		for (i = 0; i < 8 * 4; i++) {
19362306a36Sopenharmony_ci			if (!(i & 3))
19462306a36Sopenharmony_ci				pr_cont(" ");
19562306a36Sopenharmony_ci			pr_cont("%02x", p[i]);
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci		pr_cont("\n");
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	cobalt_alsa_announce_pcm_data(s->alsa,
20062306a36Sopenharmony_ci			vb2_plane_vaddr(vb, 0),
20162306a36Sopenharmony_ci			8 * 4,
20262306a36Sopenharmony_ci			vb2_get_plane_payload(vb, 0) / (8 * 4));
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int snd_cobalt_pcm_capture_open(struct snd_pcm_substream *substream)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
20962306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
21062306a36Sopenharmony_ci	struct cobalt_stream *s = cobsc->s;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	runtime->hw = snd_cobalt_hdmi_capture;
21362306a36Sopenharmony_ci	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
21462306a36Sopenharmony_ci	cobsc->capture_pcm_substream = substream;
21562306a36Sopenharmony_ci	runtime->private_data = s;
21662306a36Sopenharmony_ci	cobsc->alsa_record_cnt++;
21762306a36Sopenharmony_ci	if (cobsc->alsa_record_cnt == 1) {
21862306a36Sopenharmony_ci		int rc;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		rc = vb2_thread_start(&s->q, alsa_fnc, s, s->vdev.name);
22162306a36Sopenharmony_ci		if (rc) {
22262306a36Sopenharmony_ci			cobsc->alsa_record_cnt--;
22362306a36Sopenharmony_ci			return rc;
22462306a36Sopenharmony_ci		}
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci	return 0;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic int snd_cobalt_pcm_capture_close(struct snd_pcm_substream *substream)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
23262306a36Sopenharmony_ci	struct cobalt_stream *s = cobsc->s;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	cobsc->alsa_record_cnt--;
23562306a36Sopenharmony_ci	if (cobsc->alsa_record_cnt == 0)
23662306a36Sopenharmony_ci		vb2_thread_stop(&s->q);
23762306a36Sopenharmony_ci	return 0;
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int snd_cobalt_pcm_prepare(struct snd_pcm_substream *substream)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	cobsc->hwptr_done_capture = 0;
24562306a36Sopenharmony_ci	cobsc->capture_transfer_done = 0;
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	return 0;
24862306a36Sopenharmony_ci}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_cistatic int snd_cobalt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	switch (cmd) {
25362306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
25462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
25562306a36Sopenharmony_ci		return 0;
25662306a36Sopenharmony_ci	default:
25762306a36Sopenharmony_ci		return -EINVAL;
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci	return 0;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic
26362306a36Sopenharmony_cisnd_pcm_uframes_t snd_cobalt_pcm_pointer(struct snd_pcm_substream *substream)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	snd_pcm_uframes_t hwptr_done;
26662306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	hwptr_done = cobsc->hwptr_done_capture;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return hwptr_done;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void pb_sample_cpy(u8 *dst, const u8 *src, u32 len, bool is_s32)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	static const unsigned map[8] = { 0, 1, 5, 4, 2, 3, 6, 7 };
27662306a36Sopenharmony_ci	unsigned idx = 0;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	while (len >= (is_s32 ? 4 : 2)) {
27962306a36Sopenharmony_ci		unsigned offset = map[idx] * 4;
28062306a36Sopenharmony_ci		u8 *out = dst + offset;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		*out++ = 0;
28362306a36Sopenharmony_ci		if (is_s32) {
28462306a36Sopenharmony_ci			src++;
28562306a36Sopenharmony_ci			*out++ = *src++;
28662306a36Sopenharmony_ci		} else {
28762306a36Sopenharmony_ci			*out++ = 0;
28862306a36Sopenharmony_ci		}
28962306a36Sopenharmony_ci		*out++ = *src++;
29062306a36Sopenharmony_ci		*out = *src++;
29162306a36Sopenharmony_ci		len -= is_s32 ? 4 : 2;
29262306a36Sopenharmony_ci		idx++;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic void cobalt_alsa_pb_pcm_data(struct snd_cobalt_card *cobsc,
29762306a36Sopenharmony_ci					u8 *pcm_data,
29862306a36Sopenharmony_ci					size_t skip,
29962306a36Sopenharmony_ci					size_t samples)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct snd_pcm_substream *substream;
30262306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime;
30362306a36Sopenharmony_ci	unsigned long flags;
30462306a36Sopenharmony_ci	unsigned int pos;
30562306a36Sopenharmony_ci	unsigned int stride;
30662306a36Sopenharmony_ci	bool is_s32;
30762306a36Sopenharmony_ci	unsigned i;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	dprintk("cobalt alsa pb ptr=%p data=%p samples=%zd\n", cobsc,
31062306a36Sopenharmony_ci		pcm_data, samples);
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	substream = cobsc->playback_pcm_substream;
31362306a36Sopenharmony_ci	if (substream == NULL) {
31462306a36Sopenharmony_ci		dprintk("substream was NULL\n");
31562306a36Sopenharmony_ci		return;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	runtime = substream->runtime;
31962306a36Sopenharmony_ci	if (runtime == NULL) {
32062306a36Sopenharmony_ci		dprintk("runtime was NULL\n");
32162306a36Sopenharmony_ci		return;
32262306a36Sopenharmony_ci	}
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	is_s32 = runtime->format == SNDRV_PCM_FORMAT_S32_LE;
32562306a36Sopenharmony_ci	stride = runtime->frame_bits >> 3;
32662306a36Sopenharmony_ci	if (stride == 0) {
32762306a36Sopenharmony_ci		dprintk("stride is zero\n");
32862306a36Sopenharmony_ci		return;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (samples == 0) {
33262306a36Sopenharmony_ci		dprintk("%s: samples was zero\n", __func__);
33362306a36Sopenharmony_ci		return;
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (runtime->dma_area == NULL) {
33762306a36Sopenharmony_ci		dprintk("dma area was NULL - ignoring\n");
33862306a36Sopenharmony_ci		return;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	pos = cobsc->pb_pos % cobsc->pb_size;
34262306a36Sopenharmony_ci	for (i = 0; i < cobsc->pb_count / (8 * 4); i++)
34362306a36Sopenharmony_ci		pb_sample_cpy(pcm_data + i * skip,
34462306a36Sopenharmony_ci				runtime->dma_area + pos + i * stride,
34562306a36Sopenharmony_ci				stride, is_s32);
34662306a36Sopenharmony_ci	snd_pcm_stream_lock_irqsave(substream, flags);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	cobsc->pb_pos += i * stride;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	snd_pcm_stream_unlock_irqrestore(substream, flags);
35162306a36Sopenharmony_ci	if (cobsc->pb_pos % cobsc->pb_count == 0)
35262306a36Sopenharmony_ci		snd_pcm_period_elapsed(substream);
35362306a36Sopenharmony_ci}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_cistatic int alsa_pb_fnc(struct vb2_buffer *vb, void *priv)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	struct cobalt_stream *s = priv;
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (s->alsa->alsa_pb_channel)
36062306a36Sopenharmony_ci		cobalt_alsa_pb_pcm_data(s->alsa,
36162306a36Sopenharmony_ci				vb2_plane_vaddr(vb, 0),
36262306a36Sopenharmony_ci				8 * 4,
36362306a36Sopenharmony_ci				vb2_get_plane_payload(vb, 0) / (8 * 4));
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cistatic int snd_cobalt_pcm_playback_open(struct snd_pcm_substream *substream)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
37062306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = substream->runtime;
37162306a36Sopenharmony_ci	struct cobalt_stream *s = cobsc->s;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	runtime->hw = snd_cobalt_playback;
37462306a36Sopenharmony_ci	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
37562306a36Sopenharmony_ci	cobsc->playback_pcm_substream = substream;
37662306a36Sopenharmony_ci	runtime->private_data = s;
37762306a36Sopenharmony_ci	cobsc->alsa_playback_cnt++;
37862306a36Sopenharmony_ci	if (cobsc->alsa_playback_cnt == 1) {
37962306a36Sopenharmony_ci		int rc;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci		rc = vb2_thread_start(&s->q, alsa_pb_fnc, s, s->vdev.name);
38262306a36Sopenharmony_ci		if (rc) {
38362306a36Sopenharmony_ci			cobsc->alsa_playback_cnt--;
38462306a36Sopenharmony_ci			return rc;
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	return 0;
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int snd_cobalt_pcm_playback_close(struct snd_pcm_substream *substream)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
39462306a36Sopenharmony_ci	struct cobalt_stream *s = cobsc->s;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	cobsc->alsa_playback_cnt--;
39762306a36Sopenharmony_ci	if (cobsc->alsa_playback_cnt == 0)
39862306a36Sopenharmony_ci		vb2_thread_stop(&s->q);
39962306a36Sopenharmony_ci	return 0;
40062306a36Sopenharmony_ci}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_cistatic int snd_cobalt_pcm_pb_prepare(struct snd_pcm_substream *substream)
40362306a36Sopenharmony_ci{
40462306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	cobsc->pb_size = snd_pcm_lib_buffer_bytes(substream);
40762306a36Sopenharmony_ci	cobsc->pb_count = snd_pcm_lib_period_bytes(substream);
40862306a36Sopenharmony_ci	cobsc->pb_pos = 0;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	return 0;
41162306a36Sopenharmony_ci}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int snd_cobalt_pcm_pb_trigger(struct snd_pcm_substream *substream,
41462306a36Sopenharmony_ci				     int cmd)
41562306a36Sopenharmony_ci{
41662306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	switch (cmd) {
41962306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
42062306a36Sopenharmony_ci		if (cobsc->alsa_pb_channel)
42162306a36Sopenharmony_ci			return -EBUSY;
42262306a36Sopenharmony_ci		cobsc->alsa_pb_channel = true;
42362306a36Sopenharmony_ci		return 0;
42462306a36Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
42562306a36Sopenharmony_ci		cobsc->alsa_pb_channel = false;
42662306a36Sopenharmony_ci		return 0;
42762306a36Sopenharmony_ci	default:
42862306a36Sopenharmony_ci		return -EINVAL;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic
43362306a36Sopenharmony_cisnd_pcm_uframes_t snd_cobalt_pcm_pb_pointer(struct snd_pcm_substream *substream)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct snd_cobalt_card *cobsc = snd_pcm_substream_chip(substream);
43662306a36Sopenharmony_ci	size_t ptr;
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	ptr = cobsc->pb_pos;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	return bytes_to_frames(substream->runtime, ptr) %
44162306a36Sopenharmony_ci	       substream->runtime->buffer_size;
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cobalt_pcm_capture_ops = {
44562306a36Sopenharmony_ci	.open		= snd_cobalt_pcm_capture_open,
44662306a36Sopenharmony_ci	.close		= snd_cobalt_pcm_capture_close,
44762306a36Sopenharmony_ci	.prepare	= snd_cobalt_pcm_prepare,
44862306a36Sopenharmony_ci	.trigger	= snd_cobalt_pcm_trigger,
44962306a36Sopenharmony_ci	.pointer	= snd_cobalt_pcm_pointer,
45062306a36Sopenharmony_ci};
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_cobalt_pcm_playback_ops = {
45362306a36Sopenharmony_ci	.open		= snd_cobalt_pcm_playback_open,
45462306a36Sopenharmony_ci	.close		= snd_cobalt_pcm_playback_close,
45562306a36Sopenharmony_ci	.prepare	= snd_cobalt_pcm_pb_prepare,
45662306a36Sopenharmony_ci	.trigger	= snd_cobalt_pcm_pb_trigger,
45762306a36Sopenharmony_ci	.pointer	= snd_cobalt_pcm_pb_pointer,
45862306a36Sopenharmony_ci};
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ciint snd_cobalt_pcm_create(struct snd_cobalt_card *cobsc)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct snd_pcm *sp;
46362306a36Sopenharmony_ci	struct snd_card *sc = cobsc->sc;
46462306a36Sopenharmony_ci	struct cobalt_stream *s = cobsc->s;
46562306a36Sopenharmony_ci	struct cobalt *cobalt = s->cobalt;
46662306a36Sopenharmony_ci	int ret;
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	s->q.gfp_flags |= __GFP_ZERO;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (!s->is_output) {
47162306a36Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
47262306a36Sopenharmony_ci			COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
47362306a36Sopenharmony_ci			0);
47462306a36Sopenharmony_ci		mdelay(2);
47562306a36Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
47662306a36Sopenharmony_ci			COBALT_SYS_CTRL_AUDIO_IPP_RESETN_BIT(s->video_channel),
47762306a36Sopenharmony_ci			1);
47862306a36Sopenharmony_ci		mdelay(1);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci		ret = snd_pcm_new(sc, "Cobalt PCM-In HDMI",
48162306a36Sopenharmony_ci			0, /* PCM device 0, the only one for this card */
48262306a36Sopenharmony_ci			0, /* 0 playback substreams */
48362306a36Sopenharmony_ci			1, /* 1 capture substream */
48462306a36Sopenharmony_ci			&sp);
48562306a36Sopenharmony_ci		if (ret) {
48662306a36Sopenharmony_ci			cobalt_err("snd_cobalt_pcm_create() failed for input with err %d\n",
48762306a36Sopenharmony_ci				   ret);
48862306a36Sopenharmony_ci			goto err_exit;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE,
49262306a36Sopenharmony_ci				&snd_cobalt_pcm_capture_ops);
49362306a36Sopenharmony_ci		snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
49462306a36Sopenharmony_ci					       NULL, 0, 0);
49562306a36Sopenharmony_ci		sp->info_flags = 0;
49662306a36Sopenharmony_ci		sp->private_data = cobsc;
49762306a36Sopenharmony_ci		strscpy(sp->name, "cobalt", sizeof(sp->name));
49862306a36Sopenharmony_ci	} else {
49962306a36Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
50062306a36Sopenharmony_ci			COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 0);
50162306a36Sopenharmony_ci		mdelay(2);
50262306a36Sopenharmony_ci		cobalt_s_bit_sysctrl(cobalt,
50362306a36Sopenharmony_ci			COBALT_SYS_CTRL_AUDIO_OPP_RESETN_BIT, 1);
50462306a36Sopenharmony_ci		mdelay(1);
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		ret = snd_pcm_new(sc, "Cobalt PCM-Out HDMI",
50762306a36Sopenharmony_ci			0, /* PCM device 0, the only one for this card */
50862306a36Sopenharmony_ci			1, /* 0 playback substreams */
50962306a36Sopenharmony_ci			0, /* 1 capture substream */
51062306a36Sopenharmony_ci			&sp);
51162306a36Sopenharmony_ci		if (ret) {
51262306a36Sopenharmony_ci			cobalt_err("snd_cobalt_pcm_create() failed for output with err %d\n",
51362306a36Sopenharmony_ci				   ret);
51462306a36Sopenharmony_ci			goto err_exit;
51562306a36Sopenharmony_ci		}
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_PLAYBACK,
51862306a36Sopenharmony_ci				&snd_cobalt_pcm_playback_ops);
51962306a36Sopenharmony_ci		snd_pcm_set_managed_buffer_all(sp, SNDRV_DMA_TYPE_VMALLOC,
52062306a36Sopenharmony_ci					       NULL, 0, 0);
52162306a36Sopenharmony_ci		sp->info_flags = 0;
52262306a36Sopenharmony_ci		sp->private_data = cobsc;
52362306a36Sopenharmony_ci		strscpy(sp->name, "cobalt", sizeof(sp->name));
52462306a36Sopenharmony_ci	}
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_cierr_exit:
52962306a36Sopenharmony_ci	return ret;
53062306a36Sopenharmony_ci}
531