18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// Socionext UniPhier AIO Compress Audio driver.
48c2ecf20Sopenharmony_ci//
58c2ecf20Sopenharmony_ci// Copyright (c) 2017-2018 Socionext Inc.
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/bitfield.h>
88c2ecf20Sopenharmony_ci#include <linux/circ_buf.h>
98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <sound/core.h>
148c2ecf20Sopenharmony_ci#include <sound/pcm.h>
158c2ecf20Sopenharmony_ci#include <sound/soc.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "aio.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_prepare(struct snd_soc_component *component,
208c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream);
218c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_hw_free(struct snd_soc_component *component,
228c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	struct snd_compr *compr = rtd->compr;
278c2ecf20Sopenharmony_ci	struct device *dev = compr->card->dev;
288c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
298c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
308c2ecf20Sopenharmony_ci	size_t size = AUD_RING_SIZE;
318c2ecf20Sopenharmony_ci	int dma_dir = DMA_FROM_DEVICE, ret;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
348c2ecf20Sopenharmony_ci	if (ret)
358c2ecf20Sopenharmony_ci		return ret;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	sub->compr_area = kzalloc(size, GFP_KERNEL);
388c2ecf20Sopenharmony_ci	if (!sub->compr_area)
398c2ecf20Sopenharmony_ci		return -ENOMEM;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	if (sub->swm->dir == PORT_DIR_OUTPUT)
428c2ecf20Sopenharmony_ci		dma_dir = DMA_TO_DEVICE;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	sub->compr_addr = dma_map_single(dev, sub->compr_area, size, dma_dir);
458c2ecf20Sopenharmony_ci	if (dma_mapping_error(dev, sub->compr_addr)) {
468c2ecf20Sopenharmony_ci		kfree(sub->compr_area);
478c2ecf20Sopenharmony_ci		sub->compr_area = NULL;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci		return -ENOMEM;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	sub->compr_bytes = size;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return 0;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistatic int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct snd_compr *compr = rtd->compr;
608c2ecf20Sopenharmony_ci	struct device *dev = compr->card->dev;
618c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
628c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[compr->direction];
638c2ecf20Sopenharmony_ci	int dma_dir = DMA_FROM_DEVICE;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (sub->swm->dir == PORT_DIR_OUTPUT)
668c2ecf20Sopenharmony_ci		dma_dir = DMA_TO_DEVICE;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	dma_unmap_single(dev, sub->compr_addr, sub->compr_bytes, dma_dir);
698c2ecf20Sopenharmony_ci	kfree(sub->compr_area);
708c2ecf20Sopenharmony_ci	sub->compr_area = NULL;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return 0;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_open(struct snd_soc_component *component,
768c2ecf20Sopenharmony_ci				   struct snd_compr_stream *cstream)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
798c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
808c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
818c2ecf20Sopenharmony_ci	int ret;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (sub->cstream)
848c2ecf20Sopenharmony_ci		return -EBUSY;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	sub->cstream = cstream;
878c2ecf20Sopenharmony_ci	sub->pass_through = 1;
888c2ecf20Sopenharmony_ci	sub->use_mmap = false;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	ret = uniphier_aio_comprdma_new(rtd);
918c2ecf20Sopenharmony_ci	if (ret)
928c2ecf20Sopenharmony_ci		return ret;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = aio_init(sub);
958c2ecf20Sopenharmony_ci	if (ret)
968c2ecf20Sopenharmony_ci		return ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return 0;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_free(struct snd_soc_component *component,
1028c2ecf20Sopenharmony_ci				   struct snd_compr_stream *cstream)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
1058c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1068c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
1078c2ecf20Sopenharmony_ci	int ret;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	ret = uniphier_aio_compr_hw_free(component, cstream);
1108c2ecf20Sopenharmony_ci	if (ret)
1118c2ecf20Sopenharmony_ci		return ret;
1128c2ecf20Sopenharmony_ci	ret = uniphier_aio_comprdma_free(rtd);
1138c2ecf20Sopenharmony_ci	if (ret)
1148c2ecf20Sopenharmony_ci		return ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	sub->cstream = NULL;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_get_params(struct snd_soc_component *component,
1228c2ecf20Sopenharmony_ci					 struct snd_compr_stream *cstream,
1238c2ecf20Sopenharmony_ci					 struct snd_codec *params)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
1268c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1278c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	*params = sub->cparams.codec;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	return 0;
1328c2ecf20Sopenharmony_ci}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_set_params(struct snd_soc_component *component,
1358c2ecf20Sopenharmony_ci					 struct snd_compr_stream *cstream,
1368c2ecf20Sopenharmony_ci					 struct snd_compr_params *params)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
1398c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1408c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
1418c2ecf20Sopenharmony_ci	struct device *dev = &aio->chip->pdev->dev;
1428c2ecf20Sopenharmony_ci	int ret;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (params->codec.id != SND_AUDIOCODEC_IEC61937) {
1458c2ecf20Sopenharmony_ci		dev_err(dev, "Codec ID is not supported(%d)\n",
1468c2ecf20Sopenharmony_ci			params->codec.id);
1478c2ecf20Sopenharmony_ci		return -EINVAL;
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci	if (params->codec.profile != SND_AUDIOPROFILE_IEC61937_SPDIF) {
1508c2ecf20Sopenharmony_ci		dev_err(dev, "Codec profile is not supported(%d)\n",
1518c2ecf20Sopenharmony_ci			params->codec.profile);
1528c2ecf20Sopenharmony_ci		return -EINVAL;
1538c2ecf20Sopenharmony_ci	}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* IEC frame type will be changed after received valid data */
1568c2ecf20Sopenharmony_ci	sub->iec_pc = IEC61937_PC_AAC;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	sub->cparams = *params;
1598c2ecf20Sopenharmony_ci	sub->setting = 1;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	aio_port_reset(sub);
1628c2ecf20Sopenharmony_ci	aio_src_reset(sub);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	ret = uniphier_aio_compr_prepare(component, cstream);
1658c2ecf20Sopenharmony_ci	if (ret)
1668c2ecf20Sopenharmony_ci		return ret;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	return 0;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_hw_free(struct snd_soc_component *component,
1728c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
1758c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1768c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	sub->setting = 0;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	return 0;
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_prepare(struct snd_soc_component *component,
1848c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
1878c2ecf20Sopenharmony_ci	struct snd_compr_runtime *runtime = cstream->runtime;
1888c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
1898c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
1908c2ecf20Sopenharmony_ci	int bytes = runtime->fragment_size;
1918c2ecf20Sopenharmony_ci	unsigned long flags;
1928c2ecf20Sopenharmony_ci	int ret;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	ret = aiodma_ch_set_param(sub);
1958c2ecf20Sopenharmony_ci	if (ret)
1968c2ecf20Sopenharmony_ci		return ret;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
1998c2ecf20Sopenharmony_ci	ret = aiodma_rb_set_buffer(sub, sub->compr_addr,
2008c2ecf20Sopenharmony_ci				   sub->compr_addr + sub->compr_bytes,
2018c2ecf20Sopenharmony_ci				   bytes);
2028c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
2038c2ecf20Sopenharmony_ci	if (ret)
2048c2ecf20Sopenharmony_ci		return ret;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	ret = aio_port_set_param(sub, sub->pass_through, &sub->params);
2078c2ecf20Sopenharmony_ci	if (ret)
2088c2ecf20Sopenharmony_ci		return ret;
2098c2ecf20Sopenharmony_ci	ret = aio_oport_set_stream_type(sub, sub->iec_pc);
2108c2ecf20Sopenharmony_ci	if (ret)
2118c2ecf20Sopenharmony_ci		return ret;
2128c2ecf20Sopenharmony_ci	aio_port_set_enable(sub, 1);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	ret = aio_if_set_param(sub, sub->pass_through);
2158c2ecf20Sopenharmony_ci	if (ret)
2168c2ecf20Sopenharmony_ci		return ret;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	return 0;
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_trigger(struct snd_soc_component *component,
2228c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream,
2238c2ecf20Sopenharmony_ci				      int cmd)
2248c2ecf20Sopenharmony_ci{
2258c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
2268c2ecf20Sopenharmony_ci	struct snd_compr_runtime *runtime = cstream->runtime;
2278c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
2288c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
2298c2ecf20Sopenharmony_ci	struct device *dev = &aio->chip->pdev->dev;
2308c2ecf20Sopenharmony_ci	int bytes = runtime->fragment_size, ret = 0;
2318c2ecf20Sopenharmony_ci	unsigned long flags;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
2348c2ecf20Sopenharmony_ci	switch (cmd) {
2358c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
2368c2ecf20Sopenharmony_ci		aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
2378c2ecf20Sopenharmony_ci		aiodma_ch_set_enable(sub, 1);
2388c2ecf20Sopenharmony_ci		sub->running = 1;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci		break;
2418c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
2428c2ecf20Sopenharmony_ci		sub->running = 0;
2438c2ecf20Sopenharmony_ci		aiodma_ch_set_enable(sub, 0);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		break;
2468c2ecf20Sopenharmony_ci	default:
2478c2ecf20Sopenharmony_ci		dev_warn(dev, "Unknown trigger(%d)\n", cmd);
2488c2ecf20Sopenharmony_ci		ret = -EINVAL;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return ret;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_pointer(struct snd_soc_component *component,
2568c2ecf20Sopenharmony_ci				      struct snd_compr_stream *cstream,
2578c2ecf20Sopenharmony_ci				      struct snd_compr_tstamp *tstamp)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
2608c2ecf20Sopenharmony_ci	struct snd_compr_runtime *runtime = cstream->runtime;
2618c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
2628c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
2638c2ecf20Sopenharmony_ci	int bytes = runtime->fragment_size;
2648c2ecf20Sopenharmony_ci	unsigned long flags;
2658c2ecf20Sopenharmony_ci	u32 pos;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (sub->swm->dir == PORT_DIR_OUTPUT) {
2728c2ecf20Sopenharmony_ci		pos = sub->rd_offs;
2738c2ecf20Sopenharmony_ci		/* Size of AIO output format is double of IEC61937 */
2748c2ecf20Sopenharmony_ci		tstamp->copied_total = sub->rd_total / 2;
2758c2ecf20Sopenharmony_ci	} else {
2768c2ecf20Sopenharmony_ci		pos = sub->wr_offs;
2778c2ecf20Sopenharmony_ci		tstamp->copied_total = sub->rd_total;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci	tstamp->byte_offset = pos;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	return 0;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cistatic int aio_compr_send_to_hw(struct uniphier_aio_sub *sub,
2878c2ecf20Sopenharmony_ci				char __user *buf, size_t dstsize)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	u32 __user *srcbuf = (u32 __user *)buf;
2908c2ecf20Sopenharmony_ci	u32 *dstbuf = (u32 *)(sub->compr_area + sub->wr_offs);
2918c2ecf20Sopenharmony_ci	int src = 0, dst = 0, ret;
2928c2ecf20Sopenharmony_ci	u32 frm, frm_a, frm_b;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	while (dstsize > 0) {
2958c2ecf20Sopenharmony_ci		ret = get_user(frm, srcbuf + src);
2968c2ecf20Sopenharmony_ci		if (ret)
2978c2ecf20Sopenharmony_ci			return ret;
2988c2ecf20Sopenharmony_ci		src++;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci		frm_a = frm & 0xffff;
3018c2ecf20Sopenharmony_ci		frm_b = (frm >> 16) & 0xffff;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci		if (frm == IEC61937_HEADER_SIGN) {
3048c2ecf20Sopenharmony_ci			frm_a |= 0x01000000;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci			/* Next data is Pc and Pd */
3078c2ecf20Sopenharmony_ci			sub->iec_header = true;
3088c2ecf20Sopenharmony_ci		} else {
3098c2ecf20Sopenharmony_ci			u16 pc = be16_to_cpu((__be16)frm_a);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci			if (sub->iec_header && sub->iec_pc != pc) {
3128c2ecf20Sopenharmony_ci				/* Force overwrite IEC frame type */
3138c2ecf20Sopenharmony_ci				sub->iec_pc = pc;
3148c2ecf20Sopenharmony_ci				ret = aio_oport_set_stream_type(sub, pc);
3158c2ecf20Sopenharmony_ci				if (ret)
3168c2ecf20Sopenharmony_ci					return ret;
3178c2ecf20Sopenharmony_ci			}
3188c2ecf20Sopenharmony_ci			sub->iec_header = false;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci		dstbuf[dst++] = frm_a;
3218c2ecf20Sopenharmony_ci		dstbuf[dst++] = frm_b;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		dstsize -= sizeof(u32) * 2;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	return 0;
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_copy(struct snd_soc_component *component,
3308c2ecf20Sopenharmony_ci				   struct snd_compr_stream *cstream,
3318c2ecf20Sopenharmony_ci				   char __user *buf, size_t count)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
3348c2ecf20Sopenharmony_ci	struct snd_compr_runtime *runtime = cstream->runtime;
3358c2ecf20Sopenharmony_ci	struct device *carddev = rtd->compr->card->dev;
3368c2ecf20Sopenharmony_ci	struct uniphier_aio *aio = uniphier_priv(asoc_rtd_to_cpu(rtd, 0));
3378c2ecf20Sopenharmony_ci	struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];
3388c2ecf20Sopenharmony_ci	size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2);
3398c2ecf20Sopenharmony_ci	int bytes = runtime->fragment_size;
3408c2ecf20Sopenharmony_ci	unsigned long flags;
3418c2ecf20Sopenharmony_ci	size_t s;
3428c2ecf20Sopenharmony_ci	int ret;
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	if (cnt < sizeof(u32))
3458c2ecf20Sopenharmony_ci		return 0;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (sub->swm->dir == PORT_DIR_OUTPUT) {
3488c2ecf20Sopenharmony_ci		dma_addr_t dmapos = sub->compr_addr + sub->wr_offs;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		/* Size of AIO output format is double of IEC61937 */
3518c2ecf20Sopenharmony_ci		s = cnt * 2;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci		dma_sync_single_for_cpu(carddev, dmapos, s, DMA_TO_DEVICE);
3548c2ecf20Sopenharmony_ci		ret = aio_compr_send_to_hw(sub, buf, s);
3558c2ecf20Sopenharmony_ci		dma_sync_single_for_device(carddev, dmapos, s, DMA_TO_DEVICE);
3568c2ecf20Sopenharmony_ci	} else {
3578c2ecf20Sopenharmony_ci		dma_addr_t dmapos = sub->compr_addr + sub->rd_offs;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci		s = cnt;
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci		dma_sync_single_for_cpu(carddev, dmapos, s, DMA_FROM_DEVICE);
3628c2ecf20Sopenharmony_ci		ret = copy_to_user(buf, sub->compr_area + sub->rd_offs, s);
3638c2ecf20Sopenharmony_ci		dma_sync_single_for_device(carddev, dmapos, s, DMA_FROM_DEVICE);
3648c2ecf20Sopenharmony_ci	}
3658c2ecf20Sopenharmony_ci	if (ret)
3668c2ecf20Sopenharmony_ci		return -EFAULT;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	spin_lock_irqsave(&sub->lock, flags);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	sub->threshold = 2 * bytes;
3718c2ecf20Sopenharmony_ci	aiodma_rb_set_threshold(sub, sub->compr_bytes, 2 * bytes);
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	if (sub->swm->dir == PORT_DIR_OUTPUT) {
3748c2ecf20Sopenharmony_ci		sub->wr_offs += s;
3758c2ecf20Sopenharmony_ci		if (sub->wr_offs >= sub->compr_bytes)
3768c2ecf20Sopenharmony_ci			sub->wr_offs -= sub->compr_bytes;
3778c2ecf20Sopenharmony_ci	} else {
3788c2ecf20Sopenharmony_ci		sub->rd_offs += s;
3798c2ecf20Sopenharmony_ci		if (sub->rd_offs >= sub->compr_bytes)
3808c2ecf20Sopenharmony_ci			sub->rd_offs -= sub->compr_bytes;
3818c2ecf20Sopenharmony_ci	}
3828c2ecf20Sopenharmony_ci	aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&sub->lock, flags);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return cnt;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_get_caps(struct snd_soc_component *component,
3908c2ecf20Sopenharmony_ci				       struct snd_compr_stream *cstream,
3918c2ecf20Sopenharmony_ci				       struct snd_compr_caps *caps)
3928c2ecf20Sopenharmony_ci{
3938c2ecf20Sopenharmony_ci	caps->num_codecs = 1;
3948c2ecf20Sopenharmony_ci	caps->min_fragment_size = AUD_MIN_FRAGMENT_SIZE;
3958c2ecf20Sopenharmony_ci	caps->max_fragment_size = AUD_MAX_FRAGMENT_SIZE;
3968c2ecf20Sopenharmony_ci	caps->min_fragments = AUD_MIN_FRAGMENT;
3978c2ecf20Sopenharmony_ci	caps->max_fragments = AUD_MAX_FRAGMENT;
3988c2ecf20Sopenharmony_ci	caps->codecs[0] = SND_AUDIOCODEC_IEC61937;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	return 0;
4018c2ecf20Sopenharmony_ci}
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cistatic const struct snd_compr_codec_caps caps_iec = {
4048c2ecf20Sopenharmony_ci	.num_descriptors = 1,
4058c2ecf20Sopenharmony_ci	.descriptor[0].max_ch = 8,
4068c2ecf20Sopenharmony_ci	.descriptor[0].num_sample_rates = 0,
4078c2ecf20Sopenharmony_ci	.descriptor[0].num_bitrates = 0,
4088c2ecf20Sopenharmony_ci	.descriptor[0].profiles = SND_AUDIOPROFILE_IEC61937_SPDIF,
4098c2ecf20Sopenharmony_ci	.descriptor[0].modes = SND_AUDIOMODE_IEC_AC3 |
4108c2ecf20Sopenharmony_ci				SND_AUDIOMODE_IEC_MPEG1 |
4118c2ecf20Sopenharmony_ci				SND_AUDIOMODE_IEC_MP3 |
4128c2ecf20Sopenharmony_ci				SND_AUDIOMODE_IEC_DTS,
4138c2ecf20Sopenharmony_ci	.descriptor[0].formats = 0,
4148c2ecf20Sopenharmony_ci};
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_cistatic int uniphier_aio_compr_get_codec_caps(struct snd_soc_component *component,
4178c2ecf20Sopenharmony_ci					     struct snd_compr_stream *stream,
4188c2ecf20Sopenharmony_ci					     struct snd_compr_codec_caps *codec)
4198c2ecf20Sopenharmony_ci{
4208c2ecf20Sopenharmony_ci	if (codec->codec == SND_AUDIOCODEC_IEC61937)
4218c2ecf20Sopenharmony_ci		*codec = caps_iec;
4228c2ecf20Sopenharmony_ci	else
4238c2ecf20Sopenharmony_ci		return -EINVAL;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ciconst struct snd_compress_ops uniphier_aio_compress_ops = {
4298c2ecf20Sopenharmony_ci	.open           = uniphier_aio_compr_open,
4308c2ecf20Sopenharmony_ci	.free           = uniphier_aio_compr_free,
4318c2ecf20Sopenharmony_ci	.get_params     = uniphier_aio_compr_get_params,
4328c2ecf20Sopenharmony_ci	.set_params     = uniphier_aio_compr_set_params,
4338c2ecf20Sopenharmony_ci	.trigger        = uniphier_aio_compr_trigger,
4348c2ecf20Sopenharmony_ci	.pointer        = uniphier_aio_compr_pointer,
4358c2ecf20Sopenharmony_ci	.copy           = uniphier_aio_compr_copy,
4368c2ecf20Sopenharmony_ci	.get_caps       = uniphier_aio_compr_get_caps,
4378c2ecf20Sopenharmony_ci	.get_codec_caps = uniphier_aio_compr_get_codec_caps,
4388c2ecf20Sopenharmony_ci};
439