// SPDX-License-Identifier: GPL-2.0-or-later /* * ALSA SoC - Samsung Abox DMA driver * * Copyright (c) 2019 Samsung Electronics Co. Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "abox_util.h" #include "abox_gic.h" #include "abox_cmpnt.h" #include "abox.h" #include "abox_dump.h" #include "abox_udma.h" #include "abox_dma.h" #include "abox_memlog.h" static const struct snd_pcm_hardware abox_dma_hardware = { .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID, .formats = ABOX_SAMPLE_FORMATS, .channels_min = 1, .channels_max = 8, .buffer_bytes_max = BUFFER_BYTES_MAX, .period_bytes_min = PERIOD_BYTES_MIN, .period_bytes_max = PERIOD_BYTES_MAX, .periods_min = BUFFER_BYTES_MAX / PERIOD_BYTES_MAX, .periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN, }; unsigned int abox_dma_iova(struct abox_dma_data *data) { unsigned int id = data->dai_drv->id; unsigned int ret; if (id >= ABOX_RDMA0 && id < ABOX_WDMA0) ret = IOVA_RDMA_BUFFER(id - ABOX_RDMA0); else if (id >= ABOX_WDMA0 && id < ABOX_WDMA0_DUAL) ret = IOVA_WDMA_BUFFER(id - ABOX_WDMA0); else if (id >= ABOX_WDMA0_DUAL && id < ABOX_DDMA0) ret = IOVA_DUAL_BUFFER(id - ABOX_WDMA0_DUAL); else if (id >= ABOX_DDMA0 && id < ABOX_UAIF0) ret = IOVA_DDMA_BUFFER(id - ABOX_DDMA0); else if (id >= ABOX_UDMA_RD0 && id < ABOX_UDMA_WR0) ret = IOVA_UDMA_RD_BUFFER(id - ABOX_UDMA_RD0); else if (id >= ABOX_UDMA_WR0 && id < ABOX_UDMA_WR0_DUAL) ret = IOVA_UDMA_WR_BUFFER(id - ABOX_UDMA_WR0); else if (id >= ABOX_UDMA_WR0_DUAL && id < ABOX_UDMA_WR_DBG0) ret = IOVA_UDMA_WR_DUAL_BUFFER(id - ABOX_UDMA_WR0_DUAL); else if (id >= ABOX_UDMA_WR_DBG0) ret = IOVA_UDMA_WR_DBG_BUFFER(id - ABOX_UDMA_WR_DBG0); else ret = 0; return ret; } bool abox_dma_is_sync_mode(struct abox_dma_data *data) { struct abox_data *abox_data = data->abox_data; int id = data->id; unsigned int val; if (id >= ARRAY_SIZE(abox_data->dev_rdma) || !abox_data->dev_rdma[id]) return false; data = dev_get_drvdata(abox_data->dev_rdma[id]); val = snd_soc_component_read(data->cmpnt, DMA_REG_CTRL0); return ((val & ABOX_DMA_SYNC_MODE_MASK) >> ABOX_DMA_SYNC_MODE_L); } bool abox_dma_is_opened(struct device *dev) { struct abox_dma_data *data = dev_get_drvdata(dev); abox_dbg(dev, "%s\n", __func__); return !!data->substream; } enum abox_irq abox_dma_get_irq(struct abox_dma_data *data, enum abox_dma_irq irq) { if (!data || !data->of_data || !data->of_data->get_irq) return -EINVAL; return data->of_data->get_irq(data, irq); } void abox_dma_acquire_irq(struct abox_dma_data *data, enum abox_dma_irq dma_irq) { struct device *dev_gic = data->abox_data->dev_gic; enum abox_irq irq; /* Acquire IRQ from the firmware */ irq = abox_dma_get_irq(data, dma_irq); if (irq < 0 || irq > IRQ_COUNT) return; abox_gic_target(dev_gic, irq, ABOX_GIC_AP); abox_gic_enable(dev_gic, irq, true); } void abox_dma_release_irq(struct abox_dma_data *data, enum abox_dma_irq dma_irq) { struct device *dev_gic = data->abox_data->dev_gic; enum abox_irq irq; /* Return back IRQ to the firmware */ irq = abox_dma_get_irq(data, dma_irq); if (irq < 0 || irq > IRQ_COUNT) return; abox_gic_enable(dev_gic, irq, false); abox_gic_target(dev_gic, irq, ABOX_GIC_CORE0); } static void abox_dma_acquire_irq_all(struct abox_dma_data *data) { int i; /* Acquire IRQ from the firmware */ for (i = DMA_IRQ_BUF_DONE; i < DMA_IRQ_COUNT; i++) abox_dma_acquire_irq(data, i); } static void abox_dma_release_irq_all(struct abox_dma_data *data) { int i; /* Return back IRQ to the firmware */ for (i = DMA_IRQ_BUF_DONE; i < DMA_IRQ_COUNT; i++) abox_dma_release_irq(data, i); } int abox_dma_register_irq(struct abox_dma_data *data, enum abox_dma_irq irq, irq_handler_t handler, void *dev_id) { struct device *dev_gic = data->abox_data->dev_gic; enum abox_irq _irq; _irq = abox_dma_get_irq(data, irq); if (_irq < 0 || _irq > IRQ_COUNT) return -EINVAL; return abox_gic_register_irq_handler(dev_gic, _irq, handler, dev_id); } void abox_dma_unregister_irq(struct abox_dma_data *data, enum abox_dma_irq irq) { struct device *dev_gic = data->abox_data->dev_gic; enum abox_irq _irq; _irq = abox_dma_get_irq(data, irq); if (_irq < 0 || _irq > IRQ_COUNT) return; abox_gic_unregister_irq_handler(dev_gic, _irq); } int abox_dma_mixer_control_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; int val; if (sign_bit) mask = (unsigned int)(BIT(sign_bit + 1) - 1); val = snd_soc_component_read(cmpnt, reg); val >>= shift; val &= mask; if (sign_bit) { /* use shift for sign extension */ val <<= 31 - sign_bit; val >>= 31 - sign_bit; } ucontrol->value.integer.value[0] = val; return 0; } int abox_dma_mixer_control_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol); struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = mc->reg; unsigned int shift = mc->shift; int max = mc->max; unsigned int sign_bit = mc->sign_bit; unsigned int mask = (1 << fls(max)) - 1; int err; unsigned int val, val_mask; if (sign_bit) mask = (unsigned int)(BIT(sign_bit + 1) - 1); val = (unsigned int)(ucontrol->value.integer.value[0] << shift); val_mask = mask << shift; err = snd_soc_component_update_bits(cmpnt, reg, val_mask, val); if (err < 0) return err; return 0; } static int abox_dma_progress(struct snd_soc_component *cmpnt) { unsigned int val; val = snd_soc_component_read(cmpnt, DMA_REG_STATUS); return !!(val & ABOX_DMA_PROGRESS_MASK); } void abox_dma_barrier(struct device *dev, struct abox_dma_data *data, int enable) { const int wait_ns = 10000000; /* 10ms */ u64 timeout = local_clock() + wait_ns; while (abox_dma_progress(data->cmpnt) != enable) { if (local_clock() <= timeout) { udelay(10); continue; } dev_warn_ratelimited(dev, "%s timeout\n", enable ? "enable" : "disable"); /* Disable DMA by force */ snd_soc_component_update_bits(data->cmpnt, DMA_REG_CTRL, ABOX_DMA_ENABLE_MASK, 1 << ABOX_DMA_ENABLE_L); snd_soc_component_update_bits(data->cmpnt, DMA_REG_CTRL, ABOX_DMA_ENABLE_MASK, 0 << ABOX_DMA_ENABLE_L); break; } } static irqreturn_t abox_dma_buf_done(int irq, void *dev_id) { struct device *dev = dev_id; struct abox_dma_data *data = dev_get_drvdata(dev); abox_dbg(dev, "%s(%d)\n", __func__, irq); snd_pcm_period_elapsed(data->substream); return IRQ_HANDLED; } static irqreturn_t abox_dma_fade_done(int irq, void *dev_id) { struct device *dev = dev_id; struct abox_dma_data *data = dev_get_drvdata(dev); abox_dbg(dev, "%s(%d)\n", __func__, irq); complete(&data->func_changed); return IRQ_HANDLED; } static irqreturn_t abox_dma_err(int irq, void *dev_id) { struct device *dev = dev_id; struct abox_dma_data *data = dev_get_drvdata(dev); struct snd_soc_component *cmpnt = data->cmpnt; unsigned int reg, val; abox_dbg(dev, "%s(%d)\n", __func__, irq); abox_err(dev, "Error\n"); for (reg = DMA_REG_CTRL0; reg <= DMA_REG_MAX; reg += 4) { val = snd_soc_component_read(cmpnt, reg); abox_err(dev, "%08x: %08x\n", reg, val); } return IRQ_HANDLED; } static const irq_handler_t abox_dma_irq_handlers[DMA_IRQ_COUNT] = { abox_dma_buf_done, abox_dma_fade_done, abox_dma_err, }; static int abox_dma_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct snd_soc_component *cmpnt = data->cmpnt; struct device *dev = data->dev; struct device *dev_abox = data->dev_abox; unsigned int iova = abox_dma_iova(data); unsigned int reg, mask, val; int ret; abox_dbg(dev, "%s\n", __func__); data->hw_params = *params; if (!rtd->dai_link->no_pcm) { ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); if (ret < 0) { abox_err(dev, "memory allocation fail: %d\n", ret); return ret; } else if (ret > 0) { abox_iommu_unmap(dev_abox, iova); ret = abox_iommu_map(dev_abox, iova, runtime->dma_addr, PAGE_ALIGN(runtime->dma_bytes), runtime->dma_area); if (ret < 0) { abox_err(dev, "memory mapping fail: %d\n", ret); snd_pcm_lib_free_pages(substream); return ret; } } } if (data->buf_type == BUFFER_TYPE_RAM) { if (data->ramb.bytes >= params_buffer_bytes(params)) iova = data->ramb.addr; } reg = DMA_REG_BUF_STR; mask = ABOX_DMA_BUF_STR_MASK; val = iova; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_BUF_END; mask = ABOX_DMA_BUF_END_MASK; val = iova + params_buffer_bytes(params); ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_BUF_OFFSET; mask = ABOX_DMA_BUF_OFFSET_MASK; val = params_period_bytes(params); ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_STR_POINT; mask = ABOX_DMA_STR_POINT_MASK; val = iova; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_CTRL; mask = ABOX_DMA_WIDTH_MASK; val = ((params_width(params) / 8) - 1) << ABOX_DMA_WIDTH_L; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_CTRL; mask = ABOX_DMA_PACKED_MASK; val = (params_physical_width(params) == 24) << ABOX_DMA_PACKED_L; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_CTRL; mask = ABOX_DMA_CHANNELS_MASK; val = (params_channels(params) - 1) << ABOX_DMA_CHANNELS_L; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; snd_soc_component_async_complete(cmpnt); if (!rtd->dai_link->no_pcm) abox_dma_acquire_irq_all(data); abox_info(dev, "%s:Total=%u PrdSz=%u(%u) #Prds=%u rate=%u, width=%d, channels=%u\n", snd_pcm_stream_str(substream), params_buffer_bytes(params), params_period_size(params), params_period_bytes(params), params_periods(params), params_rate(params), params_width(params), params_channels(params)); return 0; } static int abox_dma_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct device *dev = data->dev; abox_dbg(dev, "%s\n", __func__); if (rtd->dai_link->no_pcm) return 0; abox_dma_release_irq_all(data); return snd_pcm_lib_free_pages(substream); } static int abox_dma_prepare(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct device *dev = data->dev; abox_dbg(dev, "%s\n", __func__); /* set auto fade in before dma enable */ snd_soc_component_update_bits(data->cmpnt, DMA_REG_CTRL, ABOX_DMA_AUTO_FADE_IN_MASK, data->auto_fade_in ? ABOX_DMA_AUTO_FADE_IN_MASK : 0); return 0; } static int abox_dma_trigger(struct snd_soc_component *component, struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct snd_soc_component *cmpnt = data->cmpnt; struct device *dev = data->dev; unsigned int reg, mask, val; int ret; abox_info(dev, "%s(%d)\n", __func__, cmd); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: abox_dma_barrier(dev, data, 0); reg = DMA_REG_CTRL; mask = ABOX_DMA_ENABLE_MASK; val = snd_soc_component_read(cmpnt, reg); val = (val & ~mask) | ((1 << ABOX_DMA_ENABLE_L) & mask); ret = snd_soc_component_write(cmpnt, reg, val); break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* ToDo: wait for stable state. * Func shouldn't be changed during transition. */ abox_dma_barrier(dev, data, 1); reg = DMA_REG_CTRL; mask = ABOX_DMA_FUNC_MASK; val = snd_soc_component_read(cmpnt, reg); /* normal mode */ val = (val & ~mask) | ((0 << ABOX_DMA_FUNC_L) & mask); ret = snd_soc_component_write(cmpnt, reg, val); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: abox_dma_barrier(dev, data, 1); reg = DMA_REG_CTRL; mask = ABOX_DMA_ENABLE_MASK; val = snd_soc_component_read(cmpnt, reg); val = (val & ~mask) | ((0 << ABOX_DMA_ENABLE_L) & mask); ret = snd_soc_component_write(cmpnt, reg, val); break; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* ToDo: wait for stable state. * Func shouldn't be changed during transition. */ abox_dma_barrier(dev, data, 1); reg = DMA_REG_CTRL; mask = ABOX_DMA_FUNC_MASK; val = snd_soc_component_read(cmpnt, reg); /* pending mode */ val = (val & ~mask) | ((1 << ABOX_DMA_FUNC_L) & mask); ret = snd_soc_component_write(cmpnt, reg, val); break; default: ret = -EINVAL; break; } return ret; } static size_t abox_dma_read_pointer(struct abox_dma_data *data) { struct snd_soc_component *cmpnt = data->cmpnt; unsigned int status = 0; unsigned int buf_str, buf_end, buf_offset; size_t offset, count, period_bytes; ssize_t buffer_bytes; status = snd_soc_component_read(cmpnt, DMA_REG_STATUS); buf_str = snd_soc_component_read(cmpnt, DMA_REG_BUF_STR); buf_end = snd_soc_component_read(cmpnt, DMA_REG_BUF_END); buf_offset = snd_soc_component_read(cmpnt, DMA_REG_BUF_OFFSET); buffer_bytes = buf_end - buf_str; period_bytes = buf_offset; if (hweight_long(ABOX_DMA_BUF_OFFSET_CNT_MASK) > 8) offset = ((status & ABOX_DMA_BUF_OFFSET_CNT_MASK) >> ABOX_DMA_BUF_OFFSET_CNT_L) << 4; else offset = ((status & ABOX_DMA_BUF_OFFSET_CNT_MASK) >> ABOX_DMA_BUF_OFFSET_CNT_L) * period_bytes; if (period_bytes > ABOX_DMA_BUF_CNT_MASK + 1) count = 0; else count = (status & ABOX_DMA_BUF_CNT_MASK); while ((offset % period_bytes) && (buffer_bytes >= 0)) { buffer_bytes -= period_bytes; if ((buffer_bytes & offset) == offset) offset = buffer_bytes; } return offset + count; } static snd_pcm_uframes_t abox_dma_pointer(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct device *dev = data->dev; size_t pointer; if (abox_dma_progress(data->cmpnt)) pointer = abox_dma_read_pointer(data); else pointer = 0; abox_dbg(dev, "%s: pointer=%08zx\n", __func__, pointer); return bytes_to_frames(runtime, (ssize_t)pointer); } static int abox_dma_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct device *dev = data->dev; struct abox_data *abox_data = data->abox_data; abox_info(dev, "%s\n", __func__); abox_request_cpu_gear_dai(dev, abox_data, cpu_dai, abox_data->cpu_gear_min); if (substream->runtime) snd_soc_set_runtime_hwparams(substream, &abox_dma_hardware); data->substream = substream; return 0; } static int abox_dma_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct device *dev = data->dev; abox_info(dev, "%s\n", __func__); data->substream = NULL; return 0; } static int abox_dma_mmap(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(cpu_dai); struct device *dev = data->dev; abox_dbg(dev, "%s\n", __func__); return dma_mmap_wc(dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); } static int abox_dma_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct snd_pcm *pcm = rtd->pcm; struct snd_pcm_substream *substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ? pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream : pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; struct snd_dma_buffer *dmab = &substream->dma_buffer; struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(dai); struct device *dev = data->dev; struct device *dev_abox = data->dev_abox; int ret; abox_dbg(dev, "%s\n", __func__); if (dmab->bytes < BUFFER_BYTES_MIN) dmab->bytes = BUFFER_BYTES_MIN; snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, dev, dmab->bytes, dmab->bytes); ret = abox_iommu_map(dev_abox, abox_dma_iova(data), dmab->addr, dmab->bytes, dmab->area); if (ret < 0) snd_pcm_lib_preallocate_free_for_all(pcm); data->dmab = *dmab; return ret; } static void abox_dma_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = snd_pcm_chip(pcm); struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(dai); struct device *dev = data->dev; struct device *dev_abox = data->dev_abox; abox_dbg(dev, "%s\n", __func__); abox_iommu_unmap(dev_abox, abox_dma_iova(data)); snd_pcm_lib_preallocate_free_for_all(pcm); } static int abox_dma_probe(struct snd_soc_component *cmpnt) { struct device *dev = cmpnt->dev; struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); abox_dbg(dev, "%s\n", __func__); data->cmpnt = cmpnt; return 0; } static void abox_dma_remove(struct snd_soc_component *cmpnt) { struct device *dev = cmpnt->dev; abox_dbg(dev, "%s\n", __func__); } static unsigned int abox_dma_read(struct snd_soc_component *cmpnt, unsigned int base, unsigned int reg) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); struct abox_data *abox_data = data->abox_data; if (reg > DMA_REG_MAX) { abox_warn(cmpnt->dev, "invalid dma register:%#x\n", reg); dump_stack(); } return snd_soc_component_read(abox_data->cmpnt, base + reg); } static int abox_dma_write(struct snd_soc_component *cmpnt, unsigned int base, unsigned int reg, unsigned int val) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); struct abox_data *abox_data = data->abox_data; int ret; if (reg > DMA_REG_MAX) { abox_warn(cmpnt->dev, "invalid dma register:%#x\n", reg); dump_stack(); } ret = snd_soc_component_write(abox_data->cmpnt, base + reg, val); if (ret < 0) return ret; return 0; } const struct snd_soc_component_driver abox_dma = { .probe = abox_dma_probe, .remove = abox_dma_remove, .pcm_construct = abox_dma_pcm_new, .pcm_destruct = abox_dma_pcm_free, .open = abox_dma_open, .close = abox_dma_close, .hw_params = abox_dma_hw_params, .hw_free = abox_dma_hw_free, .prepare = abox_dma_prepare, .trigger = abox_dma_trigger, .pointer = abox_dma_pointer, .mmap = abox_dma_mmap, }; int abox_dma_set_dst_bit_width(struct device *dev, int width) { struct abox_dma_data *data = dev_get_drvdata(dev); struct snd_soc_component *cmpnt = data->cmpnt; unsigned int reg, mask, val; int ret; abox_dbg(dev, "%s(%d)\n", __func__, width); reg = DMA_REG_BIT_CTRL; mask = ABOX_DMA_DST_BIT_WIDTH_MASK; val = ((width / 8) - 1) << ABOX_DMA_DST_BIT_WIDTH_L; ret = snd_soc_component_update_bits(cmpnt, reg, mask, val); return ret; } int abox_dma_get_dst_bit_width(struct device *dev) { struct abox_dma_data *data = dev_get_drvdata(dev); struct snd_soc_component *cmpnt = data->cmpnt; unsigned int reg, val; int width; abox_dbg(dev, "%s\n", __func__); reg = DMA_REG_BIT_CTRL; val = snd_soc_component_read(cmpnt, reg); val &= ABOX_DMA_DST_BIT_WIDTH_MASK; val >>= ABOX_DMA_DST_BIT_WIDTH_L; width = (val + 1) * 8; return width; } int abox_dma_get_channels(struct device *dev) { struct abox_dma_data *data = dev_get_drvdata(dev); abox_dbg(dev, "%s\n", __func__); return params_channels(&data->hw_params); } static void hw_param_mask_set(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t param, unsigned int val) { struct snd_mask *mask = hw_param_mask(params, param); snd_mask_none(mask); if ((int)val >= 0) snd_mask_set(mask, val); } static void hw_param_interval_set(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t param, unsigned int val) { struct snd_interval *interval = hw_param_interval(params, param); snd_interval_none(interval); if ((int)val >= 0) { interval->empty = 0; interval->min = interval->max = val; interval->openmin = interval->openmax = 0; interval->integer = 1; } } int abox_dma_hw_params_fixup(struct device *dev, struct snd_pcm_hw_params *params) { struct abox_dma_data *data; const struct snd_pcm_hw_params *fix; const struct snd_mask *mask; const struct snd_interval *interval; snd_pcm_hw_param_t param; if (!dev) return -EINVAL; data = dev_get_drvdata(dev); fix = &data->hw_params; abox_dbg(dev, "%s:Total=%u PrdSz=%u(%u) #Prds=%u rate=%u, width=%d, channels=%u\n", __func__, params_buffer_bytes(fix), params_period_size(fix), params_period_bytes(fix), params_periods(fix), params_rate(fix), params_width(fix), params_channels(fix)); for (param = SNDRV_PCM_HW_PARAM_FIRST_MASK; param < SNDRV_PCM_HW_PARAM_LAST_MASK; param++) { mask = hw_param_mask_c(fix, param); if (!snd_mask_empty(mask)) snd_mask_copy(hw_param_mask(params, param), mask); } for (param = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL; param < SNDRV_PCM_HW_PARAM_LAST_INTERVAL; param++) { interval = hw_param_interval_c(fix, param); if (!snd_interval_empty(interval)) snd_interval_copy(hw_param_interval(params, param), interval); } abox_dbg(dev, "%s:Total=%u PrdSz=%u(%u) #Prds=%u rate=%u, width=%d, channels=%u\n", __func__, params_buffer_bytes(params), params_period_size(params), params_period_bytes(params), params_periods(params), params_rate(params), params_width(params), params_channels(params)); return 0; } void abox_dma_hw_params_set(struct device *dev, unsigned int rate, unsigned int width, unsigned int channels, unsigned int period_size, unsigned int periods, bool packed) { struct abox_dma_data *data = dev_get_drvdata(dev); struct snd_pcm_hw_params *params = &data->hw_params; unsigned int buffer_size, format, pwidth; abox_dbg(dev, "%s(%u, %u, %u, %u, %u, %d)\n", __func__, rate, width, channels, period_size, periods, packed); if (!rate) rate = params_rate(params); if (!width) width = params_width(params); switch (width) { case 8: format = SNDRV_PCM_FORMAT_S8; break; case 16: format = SNDRV_PCM_FORMAT_S16; break; case 24: if (packed) format = SNDRV_PCM_FORMAT_S24_3LE; else format = SNDRV_PCM_FORMAT_S24; break; case 32: format = SNDRV_PCM_FORMAT_S32; break; default: format = params_format(params); break; } pwidth = snd_pcm_format_physical_width(format); if (!channels) channels = params_channels(params); if (!period_size) period_size = params_period_size(params); if (!periods) periods = params_periods(params); buffer_size = period_size * periods; hw_param_mask_set(params, SNDRV_PCM_HW_PARAM_FORMAT, format); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS, pwidth); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_FRAME_BITS, pwidth * channels); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_CHANNELS, channels); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_RATE, rate); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, USEC_PER_SEC * period_size / rate); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, period_size); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, period_size * (pwidth / 8) * channels); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_PERIODS, periods); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_BUFFER_TIME, USEC_PER_SEC * buffer_size / rate); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, buffer_size); hw_param_interval_set(params, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, buffer_size * (pwidth / 8) * channels); } int abox_dma_hw_params_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct device *dev = cmpnt->dev; struct abox_dma_data *data = dev_get_drvdata(dev); const struct snd_pcm_hw_params *hw_params = &data->hw_params; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; enum abox_dma_param param = mc->reg; long value; abox_dbg(dev, "%s(%d)\n", __func__, param); switch (param) { case DMA_RATE: value = params_rate(hw_params); break; case DMA_WIDTH: value = params_width(hw_params); break; case DMA_CHANNEL: value = params_channels(hw_params); break; case DMA_PERIOD: value = params_period_size(hw_params); break; case DMA_PERIODS: value = params_periods(hw_params); break; case DMA_PACKED: value = (params_format(hw_params) == SNDRV_PCM_FORMAT_S24_3LE); break; default: return -EINVAL; } ucontrol->value.integer.value[0] = value; return 0; } int abox_dma_hw_params_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct device *dev = cmpnt->dev; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; enum abox_dma_param param = mc->reg; long value = ucontrol->value.integer.value[0]; abox_dbg(dev, "%s(%d, %ld)\n", __func__, param, value); if (value < mc->min || value > mc->max) return -EINVAL; switch (param) { case DMA_RATE: abox_dma_hw_params_set(dev, value, 0, 0, 0, 0, 0); break; case DMA_WIDTH: abox_dma_hw_params_set(dev, 0, value, 0, 0, 0, 0); break; case DMA_CHANNEL: abox_dma_hw_params_set(dev, 0, 0, value, 0, 0, 0); break; case DMA_PERIOD: abox_dma_hw_params_set(dev, 0, 0, 0, value, 0, 0); break; case DMA_PERIODS: abox_dma_hw_params_set(dev, 0, 0, 0, 0, value, 0); break; case DMA_PACKED: abox_dma_hw_params_set(dev, 0, 0, 0, 0, 0, !!value); break; default: return -EINVAL; } return 0; } int abox_dma_auto_fade_in_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct device *dev = cmpnt->dev; struct abox_dma_data *data = dev_get_drvdata(dev); abox_dbg(dev, "%s\n", __func__); ucontrol->value.integer.value[0] = data->auto_fade_in; return 0; } int abox_dma_auto_fade_in_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct device *dev = cmpnt->dev; struct abox_dma_data *data = dev_get_drvdata(dev); bool value = !!ucontrol->value.integer.value[0]; abox_dbg(dev, "%s(%d)\n", __func__, value); data->auto_fade_in = value; return 0; } static const char * const dma_func_texts[] = { "Normal", "Pending", "Mute", }; SOC_ENUM_SINGLE_DECL(abox_dma_func_enum, DMA_REG_CTRL, ABOX_DMA_FUNC_L, dma_func_texts); int abox_dma_func_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol); struct device *dev = cmpnt->dev; struct abox_dma_data *data = dev_get_drvdata(dev); unsigned int *item = ucontrol->value.enumerated.item; int ret; abox_dbg(dev, "%s(%d)\n", __func__, item[0]); if (abox_dma_progress(cmpnt)) { static const unsigned int VOL_MAX = 0x800000; unsigned int change = 1, rate, wait_ms; change = snd_soc_component_read(cmpnt, DMA_REG_VOL_CHANGE); rate = params_rate(&data->hw_params); wait_ms = DIV_ROUND_UP(MSEC_PER_SEC * VOL_MAX, change); wait_ms = DIV_ROUND_UP(wait_ms, rate); reinit_completion(&data->func_changed); ret = snd_soc_put_enum_double(kcontrol, ucontrol); if (ret > 0) { abox_info(dev, "func %d, wait %u ms\n", item[0], wait_ms); wait_for_completion_timeout(&data->func_changed, msecs_to_jiffies(wait_ms)); } } else { ret = snd_soc_put_enum_double(kcontrol, ucontrol); } return ret; } struct snd_soc_dai *abox_dma_get_dai(struct device *dev, enum abox_dma_dai type) { struct abox_dma_data *data = dev_get_drvdata(dev); struct snd_soc_component *cmpnt = data->cmpnt; struct snd_soc_dai *dai; abox_dbg(dev, "%s(%d)\n", __func__, type); list_for_each_entry(dai, &cmpnt->dai_list, list) { if (type-- == 0) return dai; } return ERR_PTR(-EINVAL); } int abox_dma_can_close(struct snd_soc_pcm_runtime *rtd, int stream) { int rdir = stream == SNDRV_PCM_STREAM_PLAYBACK ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; switch (rtd->dpcm[rdir].state) { case SND_SOC_DPCM_STATE_OPEN: case SND_SOC_DPCM_STATE_HW_PARAMS: case SND_SOC_DPCM_STATE_PREPARE: case SND_SOC_DPCM_STATE_START: case SND_SOC_DPCM_STATE_STOP: case SND_SOC_DPCM_STATE_PAUSED: case SND_SOC_DPCM_STATE_SUSPEND: case SND_SOC_DPCM_STATE_HW_FREE: return 0; default: return 1; } } int abox_dma_can_free(struct snd_soc_pcm_runtime *rtd, int stream) { int rdir = stream == SNDRV_PCM_STREAM_PLAYBACK ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; switch (rtd->dpcm[rdir].state) { case SND_SOC_DPCM_STATE_HW_PARAMS: case SND_SOC_DPCM_STATE_PREPARE: case SND_SOC_DPCM_STATE_START: case SND_SOC_DPCM_STATE_STOP: case SND_SOC_DPCM_STATE_PAUSED: case SND_SOC_DPCM_STATE_SUSPEND: return 0; default: return 1; } } int abox_dma_can_stop(struct snd_soc_pcm_runtime *rtd, int stream) { int rdir = stream == SNDRV_PCM_STREAM_PLAYBACK ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; switch (rtd->dpcm[rdir].state) { case SND_SOC_DPCM_STATE_PREPARE: case SND_SOC_DPCM_STATE_START: case SND_SOC_DPCM_STATE_PAUSED: case SND_SOC_DPCM_STATE_SUSPEND: return 0; default: return 1; } } int abox_dma_can_start(struct snd_soc_pcm_runtime *rtd, int stream) { int rdir = stream == SNDRV_PCM_STREAM_PLAYBACK ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; switch (rtd->dpcm[rdir].state) { case SND_SOC_DPCM_STATE_PREPARE: case SND_SOC_DPCM_STATE_START: return 0; default: return 1; } } int abox_dma_can_prepare(struct snd_soc_pcm_runtime *rtd, int stream) { return abox_dma_can_start(rtd, stream); } int abox_dma_can_params(struct snd_soc_pcm_runtime *rtd, int stream) { return abox_dma_can_free(rtd, stream); } int abox_dma_can_open(struct snd_soc_pcm_runtime *rtd, int stream) { return abox_dma_can_close(rtd, stream); } static unsigned int abox_dma_get_dst_format(struct abox_dma_data *data) { unsigned int width, channels; width = abox_dma_get_dst_bit_width(data->dev); channels = params_channels(&data->hw_params); return abox_get_format(width, channels); } static unsigned int abox_dma_get_format(struct abox_dma_data *data) { unsigned int width, channels; width = params_width(&data->hw_params); channels = params_channels(&data->hw_params); return abox_get_format(width, channels); } static int abox_dma_dump_set_format(struct abox_dma_data *data) { struct abox_data *abox_data = data->abox_data; enum abox_widget w; unsigned int format; int idx, ret; abox_dbg(data->dev, "%s\n", __func__); if (!data->of_data->get_src_widget) return 0; w = data->of_data->get_src_widget(data); if (w < 0) return w; switch (w) { case ABOX_WIDGET_SPUS_IN0 ... ABOX_WIDGET_SPUS_IN11: idx = w - ABOX_WIDGET_SPUS_IN0; format = abox_dma_get_dst_format(dev_get_drvdata( abox_data->dev_rdma[idx])); break; case ABOX_WIDGET_SPUS_ASRC0 ... ABOX_WIDGET_SPUS_ASRC7: idx = w - ABOX_WIDGET_SPUS_ASRC0; format = abox_cmpnt_asrc_get_dst_format(abox_data, SNDRV_PCM_STREAM_PLAYBACK, idx); break; case ABOX_WIDGET_SIFS0: format = abox_cmpnt_sif_get_dst_format(abox_data, SNDRV_PCM_STREAM_PLAYBACK, 0); break; case ABOX_WIDGET_NSRC0 ... ABOX_WIDGET_NSRC7: idx = w - ABOX_WIDGET_NSRC0; format = abox_cmpnt_sif_get_dst_format(abox_data, SNDRV_PCM_STREAM_CAPTURE, idx); break; case ABOX_WIDGET_SPUM_ASRC0 ... ABOX_WIDGET_SPUM_ASRC3: idx = w - ABOX_WIDGET_SPUM_ASRC0; format = abox_cmpnt_asrc_get_dst_format(abox_data, SNDRV_PCM_STREAM_CAPTURE, idx); break; case ABOX_WIDGET_UDMA_RD0 ... ABOX_WIDGET_UDMA_RD1: idx = w - ABOX_WIDGET_UDMA_RD0; format = abox_dma_get_format(dev_get_drvdata( abox_data->dev_udma_rd[idx])); break; case ABOX_WIDGET_UDMA_WR0 ... ABOX_WIDGET_UDMA_WR1: idx = w - ABOX_WIDGET_UDMA_WR0; format = abox_dma_get_format(dev_get_drvdata( abox_data->dev_udma_wr[idx])); break; default: return -EINVAL; } ret = snd_soc_component_update_bits(data->cmpnt, DMA_REG_CTRL, ABOX_DMA_FORMAT_MASK, format << ABOX_DMA_FORMAT_L); return ret; } static const unsigned int ABOX_DMA_DUMP_BUFFER_SIZE = SZ_512K; static const unsigned int ABOX_DMA_DUMP_OFFSET = SZ_4K; static int abox_dma_dump_set_buffer(struct abox_dma_data *data) { struct abox_dma_dump *dump = data->dump; struct device *dev = data->dev; struct device *dev_abox = data->abox_data->dev; struct snd_soc_component *cmpnt = data->cmpnt; unsigned int iova = abox_dma_iova(data); unsigned int reg, mask, val; int ret; abox_dbg(dev, "%s\n", __func__); if (dump->area) abox_err(dev, "memory leak suspected\n"); dump->bytes = ABOX_DMA_DUMP_BUFFER_SIZE; dump->area = dma_alloc_coherent(dev, dump->bytes, &dump->addr, GFP_KERNEL); if (!dump->area) return -ENOMEM; abox_iommu_unmap(dev_abox, abox_dma_iova(data)); ret = abox_iommu_map(dev_abox, abox_dma_iova(data), dump->addr, dump->bytes, dump->area); reg = DMA_REG_BUF_STR; mask = ABOX_DMA_BUF_STR_MASK; val = iova; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_BUF_END; mask = ABOX_DMA_BUF_END_MASK; val = iova + dump->bytes; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_BUF_OFFSET; mask = ABOX_DMA_BUF_OFFSET_MASK; val = ABOX_DMA_DUMP_OFFSET; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; reg = DMA_REG_STR_POINT; mask = ABOX_DMA_STR_POINT_MASK; val = iova; ret = snd_soc_component_update_bits_async(cmpnt, reg, mask, val); if (ret < 0) return ret; snd_soc_component_async_complete(cmpnt); return ret; } static void abox_dma_dump_free_buffer(struct abox_dma_data *data) { struct abox_dma_dump *dump = data->dump; struct device *dev = data->dev; struct device *dev_abox = data->abox_data->dev; struct snd_dma_buffer *dmab = &data->dmab; unsigned int size = ABOX_DMA_DUMP_BUFFER_SIZE; unsigned int iova = abox_dma_iova(data); abox_dbg(dev, "%s\n", __func__); if (dump->area) { abox_iommu_unmap(dev_abox, iova); dma_free_coherent(dev, size, dump->area, dump->addr); abox_iommu_map(dev_abox, iova, dmab->addr, dmab->bytes, dmab->area); dump->area = NULL; } } static bool abox_dma_dump_started(struct abox_dma_data *data) { unsigned int val; val = snd_soc_component_read(data->cmpnt, DMA_REG_CTRL); return (val & ABOX_DMA_ENABLE_MASK); } static int abox_dma_dump_stop(struct abox_dma_data *data) { abox_dbg(data->dev, "%s\n", __func__); snd_soc_component_update_bits(data->cmpnt, DMA_REG_CTRL, ABOX_DMA_ENABLE_MASK, 0 << ABOX_DMA_ENABLE_L); abox_dma_barrier(data->dev, data, 0); data->dump->updated = true; wake_up_interruptible(&data->dump->waitqueue); return 0; } static int abox_dma_dump_start(struct abox_dma_data *data) { int ret; abox_dbg(data->dev, "%s\n", __func__); /* restart if it has started already */ if (abox_dma_dump_started(data)) abox_dma_dump_stop(data); abox_dma_acquire_irq_all(data); ret = abox_dma_dump_set_format(data); if (ret < 0) return ret; data->dump->pointer = 0; ret = snd_soc_component_update_bits(data->cmpnt, DMA_REG_CTRL, ABOX_DMA_ENABLE_MASK, 1 << ABOX_DMA_ENABLE_L); if (ret < 0) return ret; return 0; } static int abox_dma_dump_file_notify(void *priv, bool enabled) { struct abox_dma_data *data = priv; int ret; abox_dbg(data->dev, "%s(%d)\n", __func__, enabled); if (enabled) ret = abox_dma_dump_start(data); else ret = abox_dma_dump_stop(data); return ret; } static int abox_dma_dump_register_event_notifier(struct abox_dma_data *data) { struct abox_data *abox_data = data->abox_data; enum abox_widget w; abox_dbg(data->dev, "%s\n", __func__); if (!data->of_data->get_src_widget) return 0; w = data->of_data->get_src_widget(data); if (w < 0) return w; abox_cmpnt_register_event_notifier(abox_data, w, abox_dma_dump_file_notify, data); return 0; } static int abox_dma_dump_unregister_event_notifier(struct abox_dma_data *data) { struct abox_data *abox_data = data->abox_data; enum abox_widget w; abox_dbg(data->dev, "%s\n", __func__); if (!data->of_data->get_src_widget) return 0; w = data->of_data->get_src_widget(data); if (w < 0) return w; abox_cmpnt_unregister_event_notifier(abox_data, w); return 0; } static ssize_t abox_dma_dump_file_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) { struct abox_dma_data *data = file->private_data; struct abox_dma_dump *dump = data->dump; struct device *dev = data->dev; size_t pointer, size, total, last; int ret; do { pointer = abox_dma_read_pointer(data); if (pointer < dump->pointer) size = pointer + dump->bytes - dump->pointer; else size = pointer - dump->pointer; if (size < count) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (abox_dma_progress(data->cmpnt)) { fsleep(1); continue; } if (size && !abox_dma_dump_started(data)) break; dump->updated = false; ret = wait_event_interruptible(dump->waitqueue, dump->updated); if (ret < 0) return ret; } } while (size < count); total = size = min(size, count); abox_dbg(dev, "%s: %#zx, %#zx, %#zx, %#zx\n", __func__, count, dump->pointer, pointer, size); if (size > dump->bytes - dump->pointer) { last = dump->bytes - dump->pointer; if (copy_to_user(buffer, dump->area + dump->pointer, last)) return -EFAULT; dump->pointer = 0; buffer += last; size -= last; } if (copy_to_user(buffer, dump->area + dump->pointer, size)) return -EFAULT; dump->pointer += size; dump->pointer %= dump->bytes; return total; } static irqreturn_t abox_dma_dump_file_buf_done(int irq, void *dev_id) { struct device *dev = dev_id; struct abox_dma_data *data = dev_get_drvdata(dev); abox_dbg(dev, "%s(%d)\n", __func__, irq); if (data->dump) { data->dump->updated = true; wake_up_interruptible(&data->dump->waitqueue); } return IRQ_HANDLED; } static int abox_dma_dump_file_open(struct inode *i, struct file *f) { struct abox_dma_data *data = abox_dump_get_data(f); struct abox_data *abox_data = data->abox_data; struct device *dev = data->dev; int ret; abox_dbg(dev, "%s\n", __func__); if (atomic_cmpxchg(&data->dump->open_state, 0, 1)) { abox_err(dev, "already opened\n"); return -EBUSY; } pm_runtime_get_sync(dev); abox_wait_for_boot(abox_data, abox_get_waiting_jiffies(true)); abox_gic_register_irq_handler(abox_data->dev_gic, abox_dma_get_irq(data, DMA_IRQ_BUF_DONE), abox_dma_dump_file_buf_done, dev); f->private_data = data; ret = abox_dma_dump_register_event_notifier(data); if (ret < 0) goto out; ret = abox_dma_dump_set_buffer(data); if (ret < 0) goto out; ret = abox_dma_dump_start(data); out: if (ret < 0) atomic_set(&data->dump->open_state, 0); return ret; } static int abox_dma_dump_file_release(struct inode *i, struct file *f) { struct abox_dma_data *data = f->private_data; struct device *dev = data->dev; int ret; abox_dbg(dev, "%s\n", __func__); ret = abox_dma_dump_stop(data); abox_dma_dump_free_buffer(data); abox_dma_dump_unregister_event_notifier(data); abox_dma_release_irq_all(data); abox_gic_register_irq_handler(data->abox_data->dev_gic, abox_dma_get_irq(data, DMA_IRQ_BUF_DONE), abox_dma_irq_handlers[DMA_IRQ_BUF_DONE], dev); pm_runtime_put(dev); atomic_cmpxchg(&data->dump->open_state, 1, 0); return ret; } static unsigned int abox_dma_dump_file_poll(struct file *file, poll_table *wait) { struct abox_dma_data *data = file->private_data; abox_dbg(data->dev, "%s\n", __func__); poll_wait(file, &data->dump->waitqueue, wait); return POLLIN | POLLRDNORM; } static const struct proc_ops abox_dma_dump_fops = { .proc_lseek = generic_file_llseek, .proc_read = abox_dma_dump_file_read, .proc_poll = abox_dma_dump_file_poll, .proc_open = abox_dma_dump_file_open, .proc_release = abox_dma_dump_file_release, }; static int abox_dma_dump_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd) { struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(dai); struct device *dev = data->dev; data->dump = devm_kzalloc(dev, sizeof(*data->dump), GFP_KERNEL); if (!data->dump) return -ENOMEM; atomic_set(&data->dump->open_state, 0); data->dump->file = abox_dump_register_file(data->dai_drv->name, data, &abox_dma_dump_fops); init_waitqueue_head(&data->dump->waitqueue); return abox_dma_pcm_new(component, rtd); } static void abox_dma_dump_pcm_free(struct snd_soc_component *component, struct snd_pcm *pcm) { struct snd_soc_pcm_runtime *rtd = snd_pcm_chip(pcm); struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0); struct abox_dma_data *data = snd_soc_dai_get_drvdata(dai); struct device *dev = data->dev; abox_dbg(dev, "%s\n", __func__); if (!IS_ERR_OR_NULL(data->dump->file)) abox_dump_unregister_file(data->dump->file); devm_kfree(dev, data->dump); abox_dma_pcm_free(component, pcm); } static enum abox_irq abox_ddma_get_irq(struct abox_dma_data *data, enum abox_dma_irq irq) { unsigned int id = data->id; enum abox_irq ret; switch (irq) { case DMA_IRQ_BUF_FULL: ret = IRQ_WDMA_DBG0_BUF_FULL + id; break; case DMA_IRQ_FADE_DONE: ret = IRQ_WDMA_DBG0_FADE_DONE + id; break; case DMA_IRQ_ERR: ret = IRQ_WDMA_DBG0_ERR + id; break; default: ret = -EINVAL; break; } return ret; } static enum abox_dai abox_ddma_get_dai_id(enum abox_dma_dai dai, int id) { return ABOX_DDMA0 + id; } static char *abox_ddma_get_dai_name(struct device *dev, enum abox_dma_dai dai, int id) { return devm_kasprintf(dev, GFP_KERNEL, "DBG%d", id); } static enum abox_widget abox_ddma_get_src_widget(struct abox_dma_data *data) { enum abox_widget w; unsigned int val; val = snd_soc_component_read(data->cmpnt, DMA_REG_CTRL); val = (val & ABOX_DMA_DEBUG_SRC_MASK) >> ABOX_DMA_DEBUG_SRC_L; switch (val) { case 0x0 ... 0xb: w = ABOX_WIDGET_SPUS_IN0 + val - 0x0; break; case 0x10 ... 0x17: w = ABOX_WIDGET_SPUS_ASRC0 + val - 0x10; break; case 0x18: w = ABOX_WIDGET_SIFS0 + val - 0x18; break; case 0x20 ... 0x27: w = ABOX_WIDGET_NSRC0 + val - 0x20; break; case 0x30 ... 0x33: w = ABOX_WIDGET_SPUM_ASRC0 + val - 0x30; break; default: w = -EINVAL; break; } return w; } const static struct snd_soc_dai_driver abox_ddma_dai_drv = { .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 8, .rates = ABOX_SAMPLING_RATES, .rate_min = 8000, .rate_max = 384000, .formats = ABOX_SAMPLE_FORMATS, }, }; static const char * const abox_ddma_src_enum_texts[] = { "RDMA0", "RDMA1", "RDMA2", "RDMA3", "RDMA4", "RDMA5", "RDMA6", "RDMA7", "RDMA8", "RDMA9", "RDMA10", "RDMA11", "SPUS_ASRC0", "SPUS_ASRC1", "SPUS_ASRC2", "SPUS_ASRC3", "SPUS_ASRC4", "SPUS_ASRC5", "SPUS_ASRC6", "SPUS_ASRC7", "SPUS_MIXER", "SPUM_SIFM0", "SPUM_SIFM1", "SPUM_SIFM2", "SPUM_SIFM3", "SPUM_SIFM4", "SPUM_SIFM5", "SPUM_SIFM6", "SPUM_SIFM7", "SPUM_ASRC0", "SPUM_ASRC1", "SPUM_ASRC2", "SPUM_ASRC3", }; static const unsigned int abox_ddma_src_enum_values[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, }; static const struct soc_enum abox_ddma_src_enum[] = { SOC_VALUE_ENUM_SINGLE(DMA_REG_CTRL, ABOX_DMA_DEBUG_SRC_L, ABOX_DMA_DEBUG_SRC_MASK >> ABOX_DMA_DEBUG_SRC_L, ARRAY_SIZE(abox_ddma_src_enum_texts), abox_ddma_src_enum_texts, abox_ddma_src_enum_values), }; static const struct snd_kcontrol_new abox_ddma_controls[] = { SOC_ENUM("SRC", abox_ddma_src_enum), SOC_ENUM_EXT("Func", abox_dma_func_enum, snd_soc_get_enum_double, abox_dma_func_put), SOC_SINGLE_EXT("Auto Fade In", DMA_REG_CTRL, ABOX_DMA_AUTO_FADE_IN_L, 1, 0, abox_dma_auto_fade_in_get, abox_dma_auto_fade_in_put), SOC_SINGLE("Vol Factor", DMA_REG_VOL_FACTOR, ABOX_DMA_VOL_FACTOR_L, 0xffffff, 0), SOC_SINGLE("Vol Change", DMA_REG_VOL_CHANGE, ABOX_DMA_VOL_FACTOR_L, 0xffffff, 0), }; static unsigned int abox_ddma_read(struct snd_soc_component *cmpnt, unsigned int reg) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_WDMA_DEBUG_CTRL(data->id); return abox_dma_read(cmpnt, base, reg); } static int abox_ddma_write(struct snd_soc_component *cmpnt, unsigned int reg, unsigned int val) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_WDMA_DEBUG_CTRL(data->id); return abox_dma_write(cmpnt, base, reg, val); } const static struct snd_soc_component_driver abox_ddma = { .controls = abox_ddma_controls, .num_controls = ARRAY_SIZE(abox_ddma_controls), .probe = abox_dma_probe, .remove = abox_dma_remove, .read = abox_ddma_read, .write = abox_ddma_write, .pcm_construct = abox_dma_dump_pcm_new, .pcm_destruct = abox_dma_dump_pcm_free, .open = abox_dma_open, .close = abox_dma_close, .hw_params = abox_dma_hw_params, .hw_free = abox_dma_hw_free, .prepare = abox_dma_prepare, .trigger = abox_dma_trigger, .pointer = abox_dma_pointer, .mmap = abox_dma_mmap, }; static enum abox_irq abox_udma_wr_dbg_get_irq(struct abox_dma_data *data, enum abox_dma_irq irq) { unsigned int id = data->id; enum abox_irq ret; switch (irq) { case DMA_IRQ_BUF_EMPTY: ret = IRQ_UDMA_WR_DBG0_BUF_FULL + id; break; case DMA_IRQ_ERR: ret = IRQ_UDMA_WR_DBG0_ERR + id; break; case DMA_IRQ_FADE_DONE: ret = IRQ_UDMA_WR_DBG0_FADE_DONE + id; break; default: ret = -EINVAL; break; } return ret; } static enum abox_dai abox_udma_wr_dbg_get_dai_id(enum abox_dma_dai dai, int id) { return ABOX_UDMA_WR_DBG0 + id; } static char *abox_udma_wr_dbg_get_dai_name(struct device *dev, enum abox_dma_dai dai, int id) { return devm_kasprintf(dev, GFP_KERNEL, "UDMA DBG%d", id); } static enum abox_widget abox_udma_wr_dbg_get_src_widget( struct abox_dma_data *data) { enum abox_widget w; unsigned int val; val = snd_soc_component_read(data->cmpnt, DMA_REG_CTRL); val = (val & ABOX_DMA_DEBUG_SRC_MASK) >> ABOX_DMA_DEBUG_SRC_L; switch (val) { case 0x20 ... 0x21: w = ABOX_WIDGET_UDMA_RD0 + val - 0x20; break; default: w = -EINVAL; break; } return w; } static const char * const abox_udma_wr_dbg_src_enum_texts[] = { "UDMA_RD0", "UDMA_RD1", }; static const unsigned int abox_udma_wr_dbg_src_enum_values[] = { 0x20, 0x21, }; static const struct soc_enum abox_udma_wr_dbg_src_enum[] = { SOC_VALUE_ENUM_SINGLE(DMA_REG_CTRL, ABOX_DMA_DEBUG_SRC_L, ABOX_DMA_DEBUG_SRC_MASK >> ABOX_DMA_DEBUG_SRC_L, ARRAY_SIZE(abox_udma_wr_dbg_src_enum_texts), abox_udma_wr_dbg_src_enum_texts, abox_udma_wr_dbg_src_enum_values), }; static const struct snd_kcontrol_new abox_udma_wr_dbg_controls[] = { SOC_ENUM("SRC", abox_udma_wr_dbg_src_enum), SOC_ENUM_EXT("Func", abox_dma_func_enum, snd_soc_get_enum_double, abox_dma_func_put), SOC_SINGLE_EXT("Auto Fade In", DMA_REG_CTRL, ABOX_DMA_AUTO_FADE_IN_L, 1, 0, abox_dma_auto_fade_in_get, abox_dma_auto_fade_in_put), SOC_SINGLE("Vol Factor", DMA_REG_VOL_FACTOR, ABOX_DMA_VOL_FACTOR_L, 0xffffff, 0), SOC_SINGLE("Vol Change", DMA_REG_VOL_CHANGE, ABOX_DMA_VOL_FACTOR_L, 0xffffff, 0), }; static unsigned int abox_udma_wr_dbg_read(struct snd_soc_component *cmpnt, unsigned int reg) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_UDMA_WR_DEBUG_CTRL(data->id); return abox_dma_read(cmpnt, base, reg); } static int abox_udma_wr_dbg_write(struct snd_soc_component *cmpnt, unsigned int reg, unsigned int val) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_UDMA_WR_DEBUG_CTRL(data->id); return abox_dma_write(cmpnt, base, reg, val); } const static struct snd_soc_component_driver abox_udma_wr_dbg = { .controls = abox_udma_wr_dbg_controls, .num_controls = ARRAY_SIZE(abox_udma_wr_dbg_controls), .probe = abox_dma_probe, .remove = abox_dma_remove, .read = abox_udma_wr_dbg_read, .write = abox_udma_wr_dbg_write, .pcm_construct = abox_dma_dump_pcm_new, .pcm_destruct = abox_dma_dump_pcm_free, .open = abox_dma_open, .close = abox_dma_close, .hw_params = abox_dma_hw_params, .hw_free = abox_dma_hw_free, .prepare = abox_dma_prepare, .trigger = abox_dma_trigger, .pointer = abox_dma_pointer, .mmap = abox_dma_mmap, }; static enum abox_irq abox_dual_get_irq(struct abox_dma_data *data, enum abox_dma_irq irq) { unsigned int id = data->id; enum abox_irq ret; switch (irq) { case DMA_IRQ_BUF_FULL: ret = IRQ_WDMA0_DUAL_BUF_FULL + id; break; case DMA_IRQ_ERR: ret = IRQ_WDMA0_DUAL_ERR + id; break; default: ret = -EINVAL; break; } return ret; } static enum abox_dai abox_dual_get_dai_id(enum abox_dma_dai dai, int id) { return ABOX_WDMA0_DUAL + id; } static char *abox_dual_get_dai_name(struct device *dev, enum abox_dma_dai dai, int id) { return devm_kasprintf(dev, GFP_KERNEL, "WDMA%d DUAL", id); } const static struct snd_soc_dai_driver abox_dual_dai_drv = { .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 8, .rates = ABOX_SAMPLING_RATES, .rate_min = 8000, .rate_max = 384000, .formats = ABOX_SAMPLE_FORMATS, }, }; static unsigned int abox_dual_read(struct snd_soc_component *cmpnt, unsigned int reg) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_WDMA_DUAL_CTRL(data->id); return abox_dma_read(cmpnt, base, reg); } static int abox_dual_write(struct snd_soc_component *cmpnt, unsigned int reg, unsigned int val) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_WDMA_DUAL_CTRL(data->id); return abox_dma_write(cmpnt, base, reg, val); } const static struct snd_soc_component_driver abox_dual = { .probe = abox_dma_probe, .remove = abox_dma_remove, .read = abox_dual_read, .write = abox_dual_write, .pcm_construct = abox_dma_dump_pcm_new, .pcm_destruct = abox_dma_dump_pcm_free, .open = abox_dma_open, .close = abox_dma_close, .hw_params = abox_dma_hw_params, .hw_free = abox_dma_hw_free, .prepare = abox_dma_prepare, .trigger = abox_dma_trigger, .pointer = abox_dma_pointer, .mmap = abox_dma_mmap, }; static enum abox_irq abox_udma_wr_dual_get_irq(struct abox_dma_data *data, enum abox_dma_irq irq) { unsigned int id = data->id; enum abox_irq ret; switch (irq) { case DMA_IRQ_BUF_EMPTY: ret = IRQ_UDMA_WR0_DUAL_BUF_FULL + id; break; case DMA_IRQ_ERR: ret = IRQ_UDMA_WR0_DUAL_ERR + id; break; default: ret = -EINVAL; break; } return ret; } static enum abox_dai abox_udma_wr_dual_get_dai_id(enum abox_dma_dai dai, int id) { return ABOX_UDMA_WR0_DUAL + id; } static char *abox_udma_wr_dual_get_dai_name(struct device *dev, enum abox_dma_dai dai, int id) { return devm_kasprintf(dev, GFP_KERNEL, "UDMA WR%d DUAL", id); } static enum abox_widget abox_udma_wr_dual_get_src_widget( struct abox_dma_data *data) { return ABOX_WIDGET_UDMA_WR0 + data->id; } static unsigned int abox_udma_wr_dual_read(struct snd_soc_component *cmpnt, unsigned int reg) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_UDMA_WR_DUAL_CTRL(data->id); return abox_dma_read(cmpnt, base, reg); } static int abox_udma_wr_dual_write(struct snd_soc_component *cmpnt, unsigned int reg, unsigned int val) { struct abox_dma_data *data = snd_soc_component_get_drvdata(cmpnt); unsigned int base = ABOX_UDMA_WR_DUAL_CTRL(data->id); return abox_dma_write(cmpnt, base, reg, val); } const static struct snd_soc_component_driver abox_udma_wr_dual = { .probe = abox_dma_probe, .remove = abox_dma_remove, .read = abox_udma_wr_dual_read, .write = abox_udma_wr_dual_write, .pcm_construct = abox_dma_dump_pcm_new, .pcm_destruct = abox_dma_dump_pcm_free, .open = abox_dma_open, .close = abox_dma_close, .hw_params = abox_dma_hw_params, .hw_free = abox_dma_hw_free, .prepare = abox_dma_prepare, .trigger = abox_dma_trigger, .pointer = abox_dma_pointer, .mmap = abox_dma_mmap, }; static const struct of_device_id samsung_abox_dma_match[] = { { .compatible = "samsung,abox-ddma", .data = (void *)&(struct abox_dma_of_data){ .get_irq = abox_ddma_get_irq, .get_dai_id = abox_ddma_get_dai_id, .get_dai_name = abox_ddma_get_dai_name, .get_src_widget = abox_ddma_get_src_widget, .dai_drv = &abox_ddma_dai_drv, .num_dai = 1, .cmpnt_drv = &abox_ddma, }, }, { .compatible = "samsung,abox-dual", .data = (void *)&(struct abox_dma_of_data){ .get_irq = abox_dual_get_irq, .get_dai_id = abox_dual_get_dai_id, .get_dai_name = abox_dual_get_dai_name, .dai_drv = &abox_dual_dai_drv, .num_dai = 1, .cmpnt_drv = &abox_dual, }, }, #if IS_ENABLED(CONFIG_SND_SOC_SAMSUNG_ABOX_UDMA) { .compatible = "samsung,abox-udma-rd", .data = &abox_udma_rd_of_data, }, { .compatible = "samsung,abox-udma-wr", .data = &abox_udma_wr_of_data, }, { .compatible = "samsung,abox-udma-wr-dual", .data = (void *)&(struct abox_dma_of_data){ .get_irq = abox_udma_wr_dual_get_irq, .get_dai_id = abox_udma_wr_dual_get_dai_id, .get_dai_name = abox_udma_wr_dual_get_dai_name, .get_src_widget = abox_udma_wr_dual_get_src_widget, .dai_drv = &abox_dual_dai_drv, .num_dai = 1, .cmpnt_drv = &abox_udma_wr_dual, }, }, { .compatible = "samsung,abox-udma-wr-debug", .data = (void *)&(struct abox_dma_of_data){ .get_irq = abox_udma_wr_dbg_get_irq, .get_dai_id = abox_udma_wr_dbg_get_dai_id, .get_dai_name = abox_udma_wr_dbg_get_dai_name, .get_src_widget = abox_udma_wr_dbg_get_src_widget, .dai_drv = &abox_ddma_dai_drv, .num_dai = 1, .cmpnt_drv = &abox_udma_wr_dbg, }, }, #endif {}, }; MODULE_DEVICE_TABLE(of, samsung_abox_dma_match); static int samsung_abox_dma_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device *dev_abox = dev->parent; struct abox_data *abox_data = dev_get_drvdata(dev_abox); struct device *dev_gic = abox_data->dev_gic; struct device_node *np = dev->of_node; struct abox_dma_data *data; const struct abox_dma_of_data *of_data; const char *type; u32 value; int i, ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; platform_set_drvdata(pdev, data); data->dev = dev; data->dev_abox = dev_abox; data->abox_data = abox_data; init_completion(&data->func_changed); dma_set_mask_and_coherent(dev, DMA_BIT_MASK(36)); data->sfr_base = devm_get_ioremap(pdev, "sfr", &data->sfr_phys, NULL); if (IS_ERR(data->sfr_base)) return PTR_ERR(data->sfr_base); ret = of_samsung_property_read_u32(dev, np, "id", &data->id); if (ret < 0) return ret; ret = of_samsung_property_read_u32(dev, np, "buffer_bytes", &value); if (ret < 0) value = 0; data->dmab.bytes = value; ret = of_samsung_property_read_string(dev, np, "buffer_type", &type); if (ret < 0) type = ""; if (!strncmp(type, "ion", sizeof("ion"))) data->buf_type = BUFFER_TYPE_ION; else if (!strncmp(type, "dma", sizeof("dma"))) data->buf_type = BUFFER_TYPE_DMA; else if (!strncmp(type, "ram", sizeof("ram"))) data->buf_type = BUFFER_TYPE_RAM; else data->buf_type = BUFFER_TYPE_DMA; of_data = data->of_data = of_device_get_match_data(dev); for (i = DMA_IRQ_BUF_DONE; i < DMA_IRQ_COUNT; i++) { enum abox_irq irq = abox_dma_get_irq(data, i); if (irq < 0 || irq > IRQ_COUNT) continue; ret = abox_gic_register_irq_handler(dev_gic, irq, abox_dma_irq_handlers[i], dev); if (ret < 0) return ret; } data->num_dai = of_data->num_dai; data->dai_drv = devm_kmemdup(dev, of_data->dai_drv, sizeof(*of_data->dai_drv) * data->num_dai, GFP_KERNEL); if (!data->dai_drv) return -ENOMEM; for (i = 0; i < data->num_dai; i++) { data->dai_drv[i].id = of_data->get_dai_id(i, data->id); data->dai_drv[i].name = of_data->get_dai_name(dev, i, data->id); } ret = devm_snd_soc_register_component(dev, of_data->cmpnt_drv, data->dai_drv, data->num_dai); if (ret < 0) return ret; pm_runtime_no_callbacks(dev); pm_runtime_enable(dev); return 0; } static int samsung_abox_dma_remove(struct platform_device *pdev) { return 0; } static void samsung_abox_dma_shutdown(struct platform_device *pdev) { struct device *dev = &pdev->dev; abox_dbg(dev, "%s\n", __func__); pm_runtime_disable(dev); } struct platform_driver samsung_abox_dma_driver = { .probe = samsung_abox_dma_probe, .remove = samsung_abox_dma_remove, .shutdown = samsung_abox_dma_shutdown, .driver = { .name = "abox-dma", .owner = THIS_MODULE, .of_match_table = of_match_ptr(samsung_abox_dma_match), }, };