Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * kirkwood-dma.c
0004  *
0005  * (c) 2010 Arnaud Patard <apatard@mandriva.com>
0006  * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
0007  */
0008 
0009 #include <linux/init.h>
0010 #include <linux/module.h>
0011 #include <linux/device.h>
0012 #include <linux/io.h>
0013 #include <linux/slab.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/dma-mapping.h>
0016 #include <linux/mbus.h>
0017 #include <sound/soc.h>
0018 #include "kirkwood.h"
0019 
0020 static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
0021 {
0022     struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
0023     return snd_soc_dai_get_drvdata(asoc_rtd_to_cpu(soc_runtime, 0));
0024 }
0025 
0026 static const struct snd_pcm_hardware kirkwood_dma_snd_hw = {
0027     .info = SNDRV_PCM_INFO_INTERLEAVED |
0028         SNDRV_PCM_INFO_MMAP |
0029         SNDRV_PCM_INFO_MMAP_VALID |
0030         SNDRV_PCM_INFO_BLOCK_TRANSFER |
0031         SNDRV_PCM_INFO_PAUSE |
0032         SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
0033     .buffer_bytes_max   = KIRKWOOD_SND_MAX_BUFFER_BYTES,
0034     .period_bytes_min   = KIRKWOOD_SND_MIN_PERIOD_BYTES,
0035     .period_bytes_max   = KIRKWOOD_SND_MAX_PERIOD_BYTES,
0036     .periods_min        = KIRKWOOD_SND_MIN_PERIODS,
0037     .periods_max        = KIRKWOOD_SND_MAX_PERIODS,
0038     .fifo_size      = 0,
0039 };
0040 
0041 static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
0042 {
0043     struct kirkwood_dma_data *priv = dev_id;
0044     unsigned long mask, status, cause;
0045 
0046     mask = readl(priv->io + KIRKWOOD_INT_MASK);
0047     status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
0048 
0049     cause = readl(priv->io + KIRKWOOD_ERR_CAUSE);
0050     if (unlikely(cause)) {
0051         printk(KERN_WARNING "%s: got err interrupt 0x%lx\n",
0052                 __func__, cause);
0053         writel(cause, priv->io + KIRKWOOD_ERR_CAUSE);
0054     }
0055 
0056     /* we've enabled only bytes interrupts ... */
0057     if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
0058             KIRKWOOD_INT_CAUSE_REC_BYTES)) {
0059         printk(KERN_WARNING "%s: unexpected interrupt %lx\n",
0060             __func__, status);
0061         return IRQ_NONE;
0062     }
0063 
0064     /* ack int */
0065     writel(status, priv->io + KIRKWOOD_INT_CAUSE);
0066 
0067     if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
0068         snd_pcm_period_elapsed(priv->substream_play);
0069 
0070     if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
0071         snd_pcm_period_elapsed(priv->substream_rec);
0072 
0073     return IRQ_HANDLED;
0074 }
0075 
0076 static void
0077 kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
0078                    unsigned long dma,
0079                    const struct mbus_dram_target_info *dram)
0080 {
0081     int i;
0082 
0083     /* First disable and clear windows */
0084     writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
0085     writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
0086 
0087     /* try to find matching cs for current dma address */
0088     for (i = 0; i < dram->num_cs; i++) {
0089         const struct mbus_dram_window *cs = dram->cs + i;
0090         if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
0091             writel(cs->base & 0xffff0000,
0092                 base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
0093             writel(((cs->size - 1) & 0xffff0000) |
0094                 (cs->mbus_attr << 8) |
0095                 (dram->mbus_dram_target_id << 4) | 1,
0096                 base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
0097         }
0098     }
0099 }
0100 
0101 static int kirkwood_dma_open(struct snd_soc_component *component,
0102                  struct snd_pcm_substream *substream)
0103 {
0104     int err;
0105     struct snd_pcm_runtime *runtime = substream->runtime;
0106     struct kirkwood_dma_data *priv = kirkwood_priv(substream);
0107 
0108     snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
0109 
0110     /* Ensure that all constraints linked to dma burst are fulfilled */
0111     err = snd_pcm_hw_constraint_minmax(runtime,
0112             SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
0113             priv->burst * 2,
0114             KIRKWOOD_AUDIO_BUF_MAX-1);
0115     if (err < 0)
0116         return err;
0117 
0118     err = snd_pcm_hw_constraint_step(runtime, 0,
0119             SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
0120             priv->burst);
0121     if (err < 0)
0122         return err;
0123 
0124     err = snd_pcm_hw_constraint_step(substream->runtime, 0,
0125              SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
0126              priv->burst);
0127     if (err < 0)
0128         return err;
0129 
0130     if (!priv->substream_play && !priv->substream_rec) {
0131         err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
0132                   "kirkwood-i2s", priv);
0133         if (err)
0134             return err;
0135 
0136         /*
0137          * Enable Error interrupts. We're only ack'ing them but
0138          * it's useful for diagnostics
0139          */
0140         writel((unsigned int)-1, priv->io + KIRKWOOD_ERR_MASK);
0141     }
0142 
0143     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0144         if (priv->substream_play)
0145             return -EBUSY;
0146         priv->substream_play = substream;
0147     } else {
0148         if (priv->substream_rec)
0149             return -EBUSY;
0150         priv->substream_rec = substream;
0151     }
0152 
0153     return 0;
0154 }
0155 
0156 static int kirkwood_dma_close(struct snd_soc_component *component,
0157                   struct snd_pcm_substream *substream)
0158 {
0159     struct kirkwood_dma_data *priv = kirkwood_priv(substream);
0160 
0161     if (!priv)
0162         return 0;
0163 
0164     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0165         priv->substream_play = NULL;
0166     else
0167         priv->substream_rec = NULL;
0168 
0169     if (!priv->substream_play && !priv->substream_rec) {
0170         writel(0, priv->io + KIRKWOOD_ERR_MASK);
0171         free_irq(priv->irq, priv);
0172     }
0173 
0174     return 0;
0175 }
0176 
0177 static int kirkwood_dma_hw_params(struct snd_soc_component *component,
0178                   struct snd_pcm_substream *substream,
0179                   struct snd_pcm_hw_params *params)
0180 {
0181     struct kirkwood_dma_data *priv = kirkwood_priv(substream);
0182     const struct mbus_dram_target_info *dram = mv_mbus_dram_info();
0183     unsigned long addr = substream->runtime->dma_addr;
0184 
0185     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0186         kirkwood_dma_conf_mbus_windows(priv->io,
0187             KIRKWOOD_PLAYBACK_WIN, addr, dram);
0188     else
0189         kirkwood_dma_conf_mbus_windows(priv->io,
0190             KIRKWOOD_RECORD_WIN, addr, dram);
0191     return 0;
0192 }
0193 
0194 static int kirkwood_dma_prepare(struct snd_soc_component *component,
0195                 struct snd_pcm_substream *substream)
0196 {
0197     struct snd_pcm_runtime *runtime = substream->runtime;
0198     struct kirkwood_dma_data *priv = kirkwood_priv(substream);
0199     unsigned long size, count;
0200 
0201     /* compute buffer size in term of "words" as requested in specs */
0202     size = frames_to_bytes(runtime, runtime->buffer_size);
0203     size = (size>>2)-1;
0204     count = snd_pcm_lib_period_bytes(substream);
0205 
0206     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
0207         writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
0208         writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
0209         writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
0210     } else {
0211         writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
0212         writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
0213         writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
0214     }
0215 
0216 
0217     return 0;
0218 }
0219 
0220 static snd_pcm_uframes_t kirkwood_dma_pointer(
0221     struct snd_soc_component *component,
0222     struct snd_pcm_substream *substream)
0223 {
0224     struct kirkwood_dma_data *priv = kirkwood_priv(substream);
0225     snd_pcm_uframes_t count;
0226 
0227     if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
0228         count = bytes_to_frames(substream->runtime,
0229             readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
0230     else
0231         count = bytes_to_frames(substream->runtime,
0232             readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
0233 
0234     return count;
0235 }
0236 
0237 static int kirkwood_dma_new(struct snd_soc_component *component,
0238                 struct snd_soc_pcm_runtime *rtd)
0239 {
0240     size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
0241     struct snd_card *card = rtd->card->snd_card;
0242     int ret;
0243 
0244     ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
0245     if (ret)
0246         return ret;
0247 
0248     snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
0249                        card->dev, size, size);
0250 
0251     return 0;
0252 }
0253 
0254 const struct snd_soc_component_driver kirkwood_soc_component = {
0255     .name       = DRV_NAME,
0256     .open       = kirkwood_dma_open,
0257     .close      = kirkwood_dma_close,
0258     .hw_params  = kirkwood_dma_hw_params,
0259     .prepare    = kirkwood_dma_prepare,
0260     .pointer    = kirkwood_dma_pointer,
0261     .pcm_construct  = kirkwood_dma_new,
0262 };