int dma_request_specific_channel (int chn, char *name, void (*cb)(int, dma_irq_type_t, void *), void *data) { unsigned long flags; dma_setup_t dma_setup; if (chn >= DMA_MAX_CHANNELS || !name) return -EINVAL; if (dma_channels[chn].name) return -EBUSY; lpc313x_dma_lock(); memset(&dma_setup, 0, sizeof(dma_setup)); dma_increment_usage(); dma_channels[chn].name = name; if (cb) { dma_channels[chn].callback_handler = cb; dma_channels[chn].data = data; } dma_prog_channel (chn, &dma_setup); local_irq_save(flags); dma_irq_mask &= ~(1 << (2 * chn)); /* enable the IRQ: dafault behavior */ DMACH_IRQ_MASK = dma_irq_mask; local_irq_restore(flags); lpc313x_dma_unlock(); return chn; }
int dma_request_sg_channel (char *name, dma_cb_t cb, void *data, int usesoftirq) { unsigned int chn; unsigned long flags; dma_setup_t dma_setup; if (!name) return -EINVAL; if (softirqen & usesoftirq) return -EBUSY; lpc313x_dma_lock(); for (chn = 0; chn < DMA_MAX_CHANNELS - 1; chn++) if (!dma_channels[chn].name && !dma_channels[chn + 1].name) { sg_higher_channel[chn] = chn + 1; break; } if (!sg_higher_channel[chn]) { lpc313x_dma_unlock(); return -EBUSY; } memset(&dma_setup, 0, sizeof(dma_setup)); dma_increment_usage(); dma_channels[sg_higher_channel[chn]].name = name; dma_channels[sg_higher_channel[chn] - 1].name = name; if (cb) { dma_channels[sg_higher_channel[chn]].callback_handler = cb; dma_channels[sg_higher_channel[chn]].data = data; } dma_prog_channel (sg_higher_channel[chn], &dma_setup); if (usesoftirq) { local_irq_save(flags); softirqen = 1; softirqmask[chn] = 1; dma_irq_mask &= ~DMA_IRQS_SOFT; /* enable the soft IRQ */ DMACH_IRQ_MASK = dma_irq_mask; local_irq_restore(flags); } lpc313x_dma_unlock(); return sg_higher_channel[chn]; }
int dma_request_specific_sg_channel (int chn, char *name, dma_cb_t cb, void *data, int usesoftirq) { unsigned long flags; dma_setup_t dma_setup; if (!name) return -EINVAL; if (softirqen & usesoftirq) return -EBUSY; if (sg_higher_channel[chn] || dma_channels[chn].name || dma_channels[chn - 1].name) return -EBUSY; lpc313x_dma_lock(); sg_higher_channel[chn] = chn; memset(&dma_setup, 0, sizeof(dma_setup)); dma_increment_usage(); dma_channels[sg_higher_channel[chn]].name = name; dma_channels[sg_higher_channel[chn] - 1].name = name; if (cb) { dma_channels[sg_higher_channel[chn]].callback_handler = cb; dma_channels[sg_higher_channel[chn]].data = data; } dma_prog_channel (sg_higher_channel[chn], &dma_setup); if (usesoftirq) { local_irq_save(flags); softirqen = 1; softirqmask[chn] = 1; dma_irq_mask &= ~DMA_IRQS_SOFT; /* enable the soft IRQ */ DMACH_IRQ_MASK = dma_irq_mask; local_irq_restore(flags); } return sg_higher_channel[chn]; }
int dma_request_channel (char *name, dma_cb_t cb, void *data) { unsigned int mask; unsigned int chn; unsigned long flags; dma_setup_t dma_setup; if (!name) return -EINVAL; lpc313x_dma_lock(); memset(&dma_setup, 0, sizeof(dma_setup)); for (chn = 0, mask = 1; chn < DMA_MAX_CHANNELS; chn++) { if (!dma_channels[chn].name) { dma_increment_usage(); dma_channels[chn].name = name; if (cb) { dma_channels[chn].callback_handler = cb; dma_channels[chn].data = data; } dma_prog_channel (chn, &dma_setup); local_irq_save(flags); dma_irq_mask &= ~mask; /* enable the IRQ: dafault behavior */ DMACH_IRQ_MASK = dma_irq_mask; local_irq_restore(flags); lpc313x_dma_unlock(); return chn; } mask = mask << 2; } lpc313x_dma_unlock(); return -EBUSY; }
static int lpc313x_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *rtd = substream->runtime; struct lpc313x_dma_data *prtd = rtd->private_data; int ret = 0; #if defined (CONFIG_SND_USE_DMA_LINKLIST) int i, tch; u32 addr; dma_sg_ll_t *p_sg_cpuw, *p_sg_dmaw; unsigned long timeout; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { tch = 0; } else { tch = 1; } switch (cmd) { case SNDRV_PCM_TRIGGER_START: prtd->dma_cur = prtd->dma_buffer; p_sg_cpuw = prtd->p_sg_cpu; p_sg_dmaw = prtd->p_sg_dma; /* Build a linked list that wraps around */ addr = (u32) prtd->dma_buffer; for (i = 0; i < prtd->num_periods; i++) { p_sg_cpuw->setup.trans_length = (prtd->period_size / 4) - 1; p_sg_cpuw->setup.cfg = prtd->dma_cfg_base; p_sg_cpuw->next_entry = (u32) (p_sg_dmaw + 1); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { p_sg_cpuw->setup.src_address = addr; p_sg_cpuw->setup.dest_address = TX_FIFO_ADDR; } else { p_sg_cpuw->setup.dest_address = addr; p_sg_cpuw->setup.src_address = RX_FIFO_ADDR; } /* Wrap end of list back to start? */ if (i == (prtd->num_periods - 1)) p_sg_cpuw->next_entry = (u32) prtd->p_sg_dma; p_sg_cpuw++; p_sg_dmaw++; addr += prtd->period_size; } /* Add and start audio data position timer */ init_timer(&prtd->timer[tch]); prtd->timer[tch].data = (unsigned long) substream; prtd->timer[tch].function = lpc313x_check_dmall; prtd->timer[tch].expires = jiffies + MINTICKINC; add_timer(&prtd->timer[tch]); /* Program DMA channel and start it */ dma_prog_sg_channel(prtd->dmach, (u32) prtd->p_sg_dma); dma_set_irq_mask(prtd->dmach, 1, 1); #else dma_setup_t dmasetup; switch (cmd) { case SNDRV_PCM_TRIGGER_START: prtd->dma_cur = prtd->dma_buffer; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { dmasetup.src_address = (u32) prtd->dma_buffer; dmasetup.dest_address = TX_FIFO_ADDR; } else { dmasetup.dest_address = (u32) prtd->dma_buffer; dmasetup.src_address = RX_FIFO_ADDR; } dmasetup.cfg = prtd->dma_cfg_base; dmasetup.trans_length = (2 * prtd->period_size / 4) - 1; /* Program DMA channel and start it */ dma_prog_channel(prtd->dmach, &dmasetup); dma_set_irq_mask(prtd->dmach, 0, 0); #endif dma_start_channel(prtd->dmach); break; case SNDRV_PCM_TRIGGER_STOP: #if defined (CONFIG_SND_USE_DMA_LINKLIST) del_timer_sync(&prtd->timer[tch]); #endif /* Stop the companion channel and let the current DMA transfer finish */ dma_stop_channel_sg(prtd->dmach); timeout = jiffies + (HZ / 20); while ((dma_channel_enabled(prtd->dmach)) && (jiffies < timeout)) { cpu_relax(); } // dma_stop_channel(prtd->dmach); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: break; default: ret = -EINVAL; } return ret; } static snd_pcm_uframes_t lpc313x_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct lpc313x_dma_data *prtd = runtime->private_data; snd_pcm_uframes_t x; /* Return an offset into the DMA buffer for the next data */ x = bytes_to_frames(runtime, (prtd->dma_cur - runtime->dma_addr)); if (x >= runtime->buffer_size) x = 0; return x; } static int lpc313x_pcm_open(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct lpc313x_dma_data *prtd; int ret = 0; snd_soc_set_runtime_hwparams(substream, &lpc313x_pcm_hardware); /* ensure that buffer size is a multiple of period size */ ret = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) goto out; prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); if (prtd == NULL) { ret = -ENOMEM; goto out; } runtime->private_data = prtd; prtd->dmach = -1; out: return ret; } static int lpc313x_pcm_close(struct snd_pcm_substream *substream) { struct lpc313x_dma_data *prtd = substream->runtime->private_data; kfree(prtd); return 0; } static int lpc313x_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma) { struct snd_pcm_runtime *runtime = substream->runtime; return dma_mmap_writecombine(substream->pcm->card->dev, vma, runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); } static struct snd_pcm_ops lpc313x_pcm_ops = { .open = lpc313x_pcm_open, .close = lpc313x_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = lpc313x_pcm_hw_params, .hw_free = lpc313x_pcm_hw_free, .prepare = lpc313x_pcm_prepare, .trigger = lpc313x_pcm_trigger, .pointer = lpc313x_pcm_pointer, .mmap = lpc313x_pcm_mmap, }; /* * ASoC platform driver */ static int lpc313x_pcm_new(struct snd_card *card, struct snd_soc_dai *dai, struct snd_pcm *pcm) { int ret = 0; if (!card->dev->dma_mask) card->dev->dma_mask = &lpc313x_pcm_dmamask; if (!card->dev->coherent_dma_mask) card->dev->coherent_dma_mask = 0xffffffff; if (dai->playback.channels_min) { ret = lpc313x_pcm_allocate_dma_buffer( pcm, SNDRV_PCM_STREAM_PLAYBACK); if (ret) goto out; } if (dai->capture.channels_min) { pr_debug("%s: Allocating PCM capture DMA buffer\n", SND_NAME); ret = lpc313x_pcm_allocate_dma_buffer( pcm, SNDRV_PCM_STREAM_CAPTURE); if (ret) goto out; } out: return ret; }