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;
}
Exemple #5
0
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;
}