static int pxa910_pcm_prepare(struct snd_pcm_substream *substream)
{
	struct pxa910_runtime_data *prtd = substream->runtime->private_data;

	SDCR(prtd->dma_ch) = (prtd->params->dcmd) & (~SDCR_CHANEN);
	SDIMR(prtd->dma_ch) = SDIMR_COMP ;

	return 0;
}
static int pxa910_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
	struct pxa910_runtime_data *prtd = substream->runtime->private_data;
	int ret = 0;

	switch (cmd) {
	case SNDRV_PCM_TRIGGER_START:
		SDNDPR(prtd->dma_ch) = prtd->squ_desc_array_phys;
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
			SDSAR(prtd->dma_ch) = substream->runtime->dma_addr;
		else
			SDDAR(prtd->dma_ch) = substream->runtime->dma_addr;
		SDCR(prtd->dma_ch) |= SDCR_CHANEN;
		break;

	case SNDRV_PCM_TRIGGER_STOP:
	case SNDRV_PCM_TRIGGER_SUSPEND:
	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
		SDCR(prtd->dma_ch) &= ~SDCR_CHANEN;
		wake_up(&dma_wq);
		break;

	case SNDRV_PCM_TRIGGER_RESUME:
		SDCR(prtd->dma_ch) |= SDCR_CHANEN;
		break;
	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
		SDNDPR(prtd->dma_ch) = prtd->squ_desc_array_phys;
		SDCR(prtd->dma_ch) |= SDCR_CHANEN;
		break;

	default:
		ret = -EINVAL;
	}

	return ret;
}
void pxa910_free_squ (int squ_ch)
{
	unsigned long flags;

	if (!squ_channels[squ_ch].name) {
		pr_debug(
			"%s: trying to free channel %d which is already freed\n",
			__func__, squ_ch);
		return;
	}

	spin_lock_irqsave(&squ_lock, flags);
	SDCR(squ_ch) = 0;
	squ_channels[squ_ch].name = NULL;
	spin_unlock_irqrestore(&squ_lock, flags);
}
int pxa910_request_squ (char *name, pxa910_squ_prio prio,
			 void (*irq_handler)(int, void *),
			 void *data)
{
	unsigned long flags;
	int i, found = 0;

	/* basic sanity checks */
	if (!name || !irq_handler)
		return -EINVAL;

	spin_lock_irqsave(&squ_lock, flags);

	do {
		/* try grabbing a SQU channel with the requested priority */
		for (i = 0; i < num_squ_channels; i++) {
			if ((squ_channels[i].prio == prio) &&
			    !squ_channels[i].name) {
				found = 1;
				break;
			}
		}
		/* if requested prio group is full, try a hier priority */
	} while (!found && prio--);

	if (found) {
		SDCR(i) = 0;
		squ_channels[i].name = name;
		squ_channels[i].irq_handler = irq_handler;
		squ_channels[i].data = data;
	} else {
		pr_debug("No more available SQU channels for %s\n", name);
		i = -ENODEV;
	}

	spin_unlock_irqrestore(&squ_lock, flags);
	return i;
}
static irqreturn_t squ_irq_handler(int irq, void *dev_id)
{
	int i, dint;

	for (i = 0; i < num_squ_channels; i++) {
		dint = SDISR(i);
		if (dint) {
			struct squ_channel *channel = &squ_channels[i];
			if (channel->name && channel->irq_handler) {
				channel->irq_handler(i, channel->data);
				SDISR(i)=0;
			} else {
				/*
				 * IRQ for an unregistered SQU channel:
				 * let's clear the interrupts and disable it.
				 */
				pr_debug("spurious IRQ for SQU channel %d\n", i);
				SDCR(i) = 0;
			}
		}
	}
	return IRQ_HANDLED;
}
static int pxa910_pcm_hw_params(struct snd_pcm_substream *substream,
	struct snd_pcm_hw_params *params)
{
	struct snd_pcm_runtime *runtime = substream->runtime;
	struct pxa910_runtime_data *prtd = runtime->private_data;
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct pxa3xx_pcm_dma_params *dma = rtd->dai->cpu_dai->dma_data;
	size_t totsize = params_buffer_bytes(params);
	size_t period = params_period_bytes(params);
	pxa910_squ_desc *squ_desc;
	dma_addr_t dma_buff_phys, next_desc_phys;
	int ret;

	/* return if this is a bufferless transfer e.g.
	 * codec <--> BT codec or GSM modem -- lg FIXME */
	 if (!dma)
		return 0;

	 if(substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
	 {
		if (0 == wait_event_timeout(dma_wq, !(SDCR(1)&SDCR_CHANEN), HZ))
			return -EBUSY;
		dma->dcmd = SDCR_DST_ADDR_HOLD | SDCR_SRC_ADDR_INC |
			SDCR_SSPMOD | SDCR_DMA_BURST_32B | SDCR_FETCHND;
	 } else {
		if (0 == wait_event_timeout(dma_wq, !(SDCR(0)&SDCR_CHANEN), HZ))
			return -EBUSY;
		dma->dcmd = SDCR_SRC_ADDR_HOLD | SDCR_DST_ADDR_INC |
			SDCR_SSPMOD | SDCR_DMA_BURST_32B | SDCR_FETCHND;
	 }

	/* this may get called several times by oss emulation
	 * with different params */
	if (prtd->params == NULL) {
		prtd->params = dma;
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			ret = pxa910_request_squ(prtd->params->name, SQU_PRIO_LOW,
						pxa910_squ_dma_irq, substream);
			if (ret != 0)
				return -ENODEV;
		} else {
			ret = pxa910_request_squ(prtd->params->name, SQU_PRIO_LOW,
						pxa910_squ_dma_irq, substream);
			if (ret < 0)
				return ret;
			if (ret == 0) {
				ret = pxa910_request_squ(prtd->params->name, SQU_PRIO_LOW,
							pxa910_squ_dma_irq, substream);
				pxa910_free_squ(0);
				if (ret != 1)
					return -ENODEV;
			}
		}
		prtd->dma_ch = ret;
	} else if (prtd->params != dma) {
		pxa910_free_squ(prtd->dma_ch);
		prtd->params = dma;
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			ret = pxa910_request_squ(prtd->params->name, SQU_PRIO_LOW,
						pxa910_squ_dma_irq, substream);
			if (ret != 0)
				return -ENODEV;
		} else {
			ret = pxa910_request_squ(prtd->params->name, SQU_PRIO_LOW,
						pxa910_squ_dma_irq, substream);
			if (ret < 0)
				return ret;
			if (ret == 0) {
				ret = pxa910_request_squ(prtd->params->name, SQU_PRIO_LOW,
							pxa910_squ_dma_irq, substream);
				pxa910_free_squ(0);
				if (ret != 1)
					return -ENODEV;
			}
		}
		prtd->dma_ch = ret;
	}

	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
	runtime->dma_bytes = totsize;

	next_desc_phys = prtd->squ_desc_array_phys;
	dma_buff_phys = runtime->dma_addr;
		
	squ_desc = prtd->squ_desc_array;
	do {
		next_desc_phys += sizeof(pxa910_squ_desc);

		squ_desc->nxt_desc = next_desc_phys;
		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
			squ_desc->src_addr = dma_buff_phys;
			squ_desc->dst_addr = prtd->params->dev_addr;
		} else {
			squ_desc->src_addr = prtd->params->dev_addr;
			squ_desc->dst_addr = dma_buff_phys;
		}
		if (period > totsize)
			period = totsize;
		squ_desc->byte_cnt = period;
		squ_desc++;
		dma_buff_phys += period;
	} while (totsize -= period);
	squ_desc[-1].nxt_desc = prtd->squ_desc_array_phys;

	return 0;
}