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; }