434 lines · c
1// SPDX-License-Identifier: GPL-2.02//3// Socionext UniPhier AIO Compress Audio driver.4//5// Copyright (c) 2017-2018 Socionext Inc.6 7#include <linux/bitfield.h>8#include <linux/circ_buf.h>9#include <linux/dma-mapping.h>10#include <linux/errno.h>11#include <linux/kernel.h>12#include <linux/module.h>13#include <sound/core.h>14#include <sound/pcm.h>15#include <sound/soc.h>16 17#include "aio.h"18 19static int uniphier_aio_compr_prepare(struct snd_soc_component *component,20 struct snd_compr_stream *cstream);21static int uniphier_aio_compr_hw_free(struct snd_soc_component *component,22 struct snd_compr_stream *cstream);23 24static int uniphier_aio_comprdma_new(struct snd_soc_pcm_runtime *rtd)25{26 struct snd_compr *compr = rtd->compr;27 struct device *dev = compr->card->dev;28 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));29 struct uniphier_aio_sub *sub = &aio->sub[compr->direction];30 size_t size = AUD_RING_SIZE;31 int dma_dir = DMA_FROM_DEVICE, ret;32 33 ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));34 if (ret)35 return ret;36 37 sub->compr_area = kzalloc(size, GFP_KERNEL);38 if (!sub->compr_area)39 return -ENOMEM;40 41 if (sub->swm->dir == PORT_DIR_OUTPUT)42 dma_dir = DMA_TO_DEVICE;43 44 sub->compr_addr = dma_map_single(dev, sub->compr_area, size, dma_dir);45 if (dma_mapping_error(dev, sub->compr_addr)) {46 kfree(sub->compr_area);47 sub->compr_area = NULL;48 49 return -ENOMEM;50 }51 52 sub->compr_bytes = size;53 54 return 0;55}56 57static int uniphier_aio_comprdma_free(struct snd_soc_pcm_runtime *rtd)58{59 struct snd_compr *compr = rtd->compr;60 struct device *dev = compr->card->dev;61 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));62 struct uniphier_aio_sub *sub = &aio->sub[compr->direction];63 int dma_dir = DMA_FROM_DEVICE;64 65 if (sub->swm->dir == PORT_DIR_OUTPUT)66 dma_dir = DMA_TO_DEVICE;67 68 dma_unmap_single(dev, sub->compr_addr, sub->compr_bytes, dma_dir);69 kfree(sub->compr_area);70 sub->compr_area = NULL;71 72 return 0;73}74 75static int uniphier_aio_compr_open(struct snd_soc_component *component,76 struct snd_compr_stream *cstream)77{78 struct snd_soc_pcm_runtime *rtd = cstream->private_data;79 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));80 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];81 int ret;82 83 if (sub->cstream)84 return -EBUSY;85 86 sub->cstream = cstream;87 sub->pass_through = 1;88 sub->use_mmap = false;89 90 ret = uniphier_aio_comprdma_new(rtd);91 if (ret)92 return ret;93 94 ret = aio_init(sub);95 if (ret)96 return ret;97 98 return 0;99}100 101static int uniphier_aio_compr_free(struct snd_soc_component *component,102 struct snd_compr_stream *cstream)103{104 struct snd_soc_pcm_runtime *rtd = cstream->private_data;105 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));106 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];107 int ret;108 109 ret = uniphier_aio_compr_hw_free(component, cstream);110 if (ret)111 return ret;112 ret = uniphier_aio_comprdma_free(rtd);113 if (ret)114 return ret;115 116 sub->cstream = NULL;117 118 return 0;119}120 121static int uniphier_aio_compr_get_params(struct snd_soc_component *component,122 struct snd_compr_stream *cstream,123 struct snd_codec *params)124{125 struct snd_soc_pcm_runtime *rtd = cstream->private_data;126 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));127 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];128 129 *params = sub->cparams.codec;130 131 return 0;132}133 134static int uniphier_aio_compr_set_params(struct snd_soc_component *component,135 struct snd_compr_stream *cstream,136 struct snd_compr_params *params)137{138 struct snd_soc_pcm_runtime *rtd = cstream->private_data;139 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));140 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];141 struct device *dev = &aio->chip->pdev->dev;142 143 if (params->codec.id != SND_AUDIOCODEC_IEC61937) {144 dev_err(dev, "Codec ID is not supported(%d)\n",145 params->codec.id);146 return -EINVAL;147 }148 if (params->codec.profile != SND_AUDIOPROFILE_IEC61937_SPDIF) {149 dev_err(dev, "Codec profile is not supported(%d)\n",150 params->codec.profile);151 return -EINVAL;152 }153 154 /* IEC frame type will be changed after received valid data */155 sub->iec_pc = IEC61937_PC_AAC;156 157 sub->cparams = *params;158 sub->setting = 1;159 160 aio_port_reset(sub);161 aio_src_reset(sub);162 163 return uniphier_aio_compr_prepare(component, cstream);164}165 166static int uniphier_aio_compr_hw_free(struct snd_soc_component *component,167 struct snd_compr_stream *cstream)168{169 struct snd_soc_pcm_runtime *rtd = cstream->private_data;170 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));171 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];172 173 sub->setting = 0;174 175 return 0;176}177 178static int uniphier_aio_compr_prepare(struct snd_soc_component *component,179 struct snd_compr_stream *cstream)180{181 struct snd_soc_pcm_runtime *rtd = cstream->private_data;182 struct snd_compr_runtime *runtime = cstream->runtime;183 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));184 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];185 int bytes = runtime->fragment_size;186 unsigned long flags;187 int ret;188 189 ret = aiodma_ch_set_param(sub);190 if (ret)191 return ret;192 193 spin_lock_irqsave(&sub->lock, flags);194 ret = aiodma_rb_set_buffer(sub, sub->compr_addr,195 sub->compr_addr + sub->compr_bytes,196 bytes);197 spin_unlock_irqrestore(&sub->lock, flags);198 if (ret)199 return ret;200 201 ret = aio_port_set_param(sub, sub->pass_through, &sub->params);202 if (ret)203 return ret;204 ret = aio_oport_set_stream_type(sub, sub->iec_pc);205 if (ret)206 return ret;207 aio_port_set_enable(sub, 1);208 209 ret = aio_if_set_param(sub, sub->pass_through);210 if (ret)211 return ret;212 213 return 0;214}215 216static int uniphier_aio_compr_trigger(struct snd_soc_component *component,217 struct snd_compr_stream *cstream,218 int cmd)219{220 struct snd_soc_pcm_runtime *rtd = cstream->private_data;221 struct snd_compr_runtime *runtime = cstream->runtime;222 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));223 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];224 struct device *dev = &aio->chip->pdev->dev;225 int bytes = runtime->fragment_size, ret = 0;226 unsigned long flags;227 228 spin_lock_irqsave(&sub->lock, flags);229 switch (cmd) {230 case SNDRV_PCM_TRIGGER_START:231 aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);232 aiodma_ch_set_enable(sub, 1);233 sub->running = 1;234 235 break;236 case SNDRV_PCM_TRIGGER_STOP:237 sub->running = 0;238 aiodma_ch_set_enable(sub, 0);239 240 break;241 default:242 dev_warn(dev, "Unknown trigger(%d)\n", cmd);243 ret = -EINVAL;244 }245 spin_unlock_irqrestore(&sub->lock, flags);246 247 return ret;248}249 250static int uniphier_aio_compr_pointer(struct snd_soc_component *component,251 struct snd_compr_stream *cstream,252 struct snd_compr_tstamp *tstamp)253{254 struct snd_soc_pcm_runtime *rtd = cstream->private_data;255 struct snd_compr_runtime *runtime = cstream->runtime;256 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));257 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];258 int bytes = runtime->fragment_size;259 unsigned long flags;260 u32 pos;261 262 spin_lock_irqsave(&sub->lock, flags);263 264 aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);265 266 if (sub->swm->dir == PORT_DIR_OUTPUT) {267 pos = sub->rd_offs;268 /* Size of AIO output format is double of IEC61937 */269 tstamp->copied_total = sub->rd_total / 2;270 } else {271 pos = sub->wr_offs;272 tstamp->copied_total = sub->rd_total;273 }274 tstamp->byte_offset = pos;275 276 spin_unlock_irqrestore(&sub->lock, flags);277 278 return 0;279}280 281static int aio_compr_send_to_hw(struct uniphier_aio_sub *sub,282 char __user *buf, size_t dstsize)283{284 u32 __user *srcbuf = (u32 __user *)buf;285 u32 *dstbuf = (u32 *)(sub->compr_area + sub->wr_offs);286 int src = 0, dst = 0, ret;287 u32 frm, frm_a, frm_b;288 289 while (dstsize > 0) {290 ret = get_user(frm, srcbuf + src);291 if (ret)292 return ret;293 src++;294 295 frm_a = frm & 0xffff;296 frm_b = (frm >> 16) & 0xffff;297 298 if (frm == IEC61937_HEADER_SIGN) {299 frm_a |= 0x01000000;300 301 /* Next data is Pc and Pd */302 sub->iec_header = true;303 } else {304 u16 pc = be16_to_cpu((__be16)frm_a);305 306 if (sub->iec_header && sub->iec_pc != pc) {307 /* Force overwrite IEC frame type */308 sub->iec_pc = pc;309 ret = aio_oport_set_stream_type(sub, pc);310 if (ret)311 return ret;312 }313 sub->iec_header = false;314 }315 dstbuf[dst++] = frm_a;316 dstbuf[dst++] = frm_b;317 318 dstsize -= sizeof(u32) * 2;319 }320 321 return 0;322}323 324static int uniphier_aio_compr_copy(struct snd_soc_component *component,325 struct snd_compr_stream *cstream,326 char __user *buf, size_t count)327{328 struct snd_soc_pcm_runtime *rtd = cstream->private_data;329 struct snd_compr_runtime *runtime = cstream->runtime;330 struct device *carddev = rtd->compr->card->dev;331 struct uniphier_aio *aio = uniphier_priv(snd_soc_rtd_to_cpu(rtd, 0));332 struct uniphier_aio_sub *sub = &aio->sub[cstream->direction];333 size_t cnt = min_t(size_t, count, aio_rb_space_to_end(sub) / 2);334 int bytes = runtime->fragment_size;335 unsigned long flags;336 size_t s;337 int ret;338 339 if (cnt < sizeof(u32))340 return 0;341 342 if (sub->swm->dir == PORT_DIR_OUTPUT) {343 dma_addr_t dmapos = sub->compr_addr + sub->wr_offs;344 345 /* Size of AIO output format is double of IEC61937 */346 s = cnt * 2;347 348 dma_sync_single_for_cpu(carddev, dmapos, s, DMA_TO_DEVICE);349 ret = aio_compr_send_to_hw(sub, buf, s);350 dma_sync_single_for_device(carddev, dmapos, s, DMA_TO_DEVICE);351 } else {352 dma_addr_t dmapos = sub->compr_addr + sub->rd_offs;353 354 s = cnt;355 356 dma_sync_single_for_cpu(carddev, dmapos, s, DMA_FROM_DEVICE);357 ret = copy_to_user(buf, sub->compr_area + sub->rd_offs, s);358 dma_sync_single_for_device(carddev, dmapos, s, DMA_FROM_DEVICE);359 }360 if (ret)361 return -EFAULT;362 363 spin_lock_irqsave(&sub->lock, flags);364 365 sub->threshold = 2 * bytes;366 aiodma_rb_set_threshold(sub, sub->compr_bytes, 2 * bytes);367 368 if (sub->swm->dir == PORT_DIR_OUTPUT) {369 sub->wr_offs += s;370 if (sub->wr_offs >= sub->compr_bytes)371 sub->wr_offs -= sub->compr_bytes;372 } else {373 sub->rd_offs += s;374 if (sub->rd_offs >= sub->compr_bytes)375 sub->rd_offs -= sub->compr_bytes;376 }377 aiodma_rb_sync(sub, sub->compr_addr, sub->compr_bytes, bytes);378 379 spin_unlock_irqrestore(&sub->lock, flags);380 381 return cnt;382}383 384static int uniphier_aio_compr_get_caps(struct snd_soc_component *component,385 struct snd_compr_stream *cstream,386 struct snd_compr_caps *caps)387{388 caps->num_codecs = 1;389 caps->min_fragment_size = AUD_MIN_FRAGMENT_SIZE;390 caps->max_fragment_size = AUD_MAX_FRAGMENT_SIZE;391 caps->min_fragments = AUD_MIN_FRAGMENT;392 caps->max_fragments = AUD_MAX_FRAGMENT;393 caps->codecs[0] = SND_AUDIOCODEC_IEC61937;394 395 return 0;396}397 398static const struct snd_compr_codec_caps caps_iec = {399 .num_descriptors = 1,400 .descriptor[0].max_ch = 8,401 .descriptor[0].num_sample_rates = 0,402 .descriptor[0].num_bitrates = 0,403 .descriptor[0].profiles = SND_AUDIOPROFILE_IEC61937_SPDIF,404 .descriptor[0].modes = SND_AUDIOMODE_IEC_AC3 |405 SND_AUDIOMODE_IEC_MPEG1 |406 SND_AUDIOMODE_IEC_MP3 |407 SND_AUDIOMODE_IEC_DTS,408 .descriptor[0].formats = 0,409};410 411static int uniphier_aio_compr_get_codec_caps(struct snd_soc_component *component,412 struct snd_compr_stream *stream,413 struct snd_compr_codec_caps *codec)414{415 if (codec->codec == SND_AUDIOCODEC_IEC61937)416 *codec = caps_iec;417 else418 return -EINVAL;419 420 return 0;421}422 423const struct snd_compress_ops uniphier_aio_compress_ops = {424 .open = uniphier_aio_compr_open,425 .free = uniphier_aio_compr_free,426 .get_params = uniphier_aio_compr_get_params,427 .set_params = uniphier_aio_compr_set_params,428 .trigger = uniphier_aio_compr_trigger,429 .pointer = uniphier_aio_compr_pointer,430 .copy = uniphier_aio_compr_copy,431 .get_caps = uniphier_aio_compr_get_caps,432 .get_codec_caps = uniphier_aio_compr_get_codec_caps,433};434