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