/* * The Spreadtrum Audio compress offload mode will use 2-stage DMA transfer to * save power. That means we can request 2 dma channels, one for source channel, * and another one for destination channel. Once the source channel's transaction * is done, it will trigger the destination channel's transaction automatically * by hardware signal. * * For 2-stage DMA transfer, we can allocate 2 buffers: IRAM buffer (always * power-on) and DDR buffer. The source channel will transfer data from IRAM * buffer to the DSP fifo to decoding/encoding, once IRAM buffer is empty by * transferring done, the destination channel will start to transfer data from * DDR buffer to IRAM buffer. * * Since the DSP fifo is only 512B, IRAM buffer is allocated by 32K, and DDR * buffer is larger to 2M. That means only the IRAM 32k data is transferred * done, we can wake up the AP system to transfer data from DDR to IRAM, and * other time the AP system can be suspended to save power.
*/ struct sprd_compr_stream { struct snd_compr_stream *cstream; struct sprd_compr_ops *compr_ops; struct sprd_compr_dma dma[SPRD_COMPR_DMA_CHANS];
/* DSP play information IRAM buffer */
dma_addr_t info_phys; void *info_area; int info_size;
/* Data size copied to IRAM buffer */ int copied_total; /* Total received data size from userspace */ int received_total; /* Stage 0 IRAM buffer received data size */ int received_stage0; /* Stage 1 DDR buffer received data size */ int received_stage1; /* Stage 1 DDR buffer pointer */ int stage1_pointer;
};
staticint sprd_platform_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *cstream, int cmd);
/* * Configure the link-list address for the DMA engine link-list * mode.
*/
link.virt_addr = (unsignedlong)dma->virt;
link.phy_addr = dma->phys;
ret = dmaengine_slave_config(dma->chan, &config); if (ret) {
dev_err(dev, "failed to set slave configuration: %d\n", ret); goto config_err;
}
/* * We configure the DMA request mode, interrupt mode, channel * mode and channel trigger mode by the flags.
*/
dma->desc = dma->chan->device->device_prep_slave_sg(dma->chan, sg,
sg_num, dir,
flags, &link); if (!dma->desc) {
dev_err(dev, "failed to prepare slave sg\n");
ret = -ENOMEM; goto config_err;
}
/* Only channel 1 transfer can wake up the AP system. */ if (!params->no_wake_mode && channel == 1) {
dma->desc->callback = sprd_platform_compr_dma_complete;
dma->desc->callback_param = cstream;
}
/* * Configure the DMA engine 2-stage transfer mode. Channel 1 set as the * destination channel, and channel 0 set as the source channel, that * means once the source channel's transaction is done, it will trigger * the destination channel's transaction automatically.
*/
ret = sprd_platform_compr_dma_config(component, cstream, params, 1); if (ret) {
dev_err(dev, "failed to config stage 1 DMA: %d\n", ret); return ret;
}
ret = sprd_platform_compr_dma_config(component, cstream, params, 0); if (ret) {
dev_err(dev, "failed to config stage 0 DMA: %d\n", ret); goto config_err;
}
ret = stream->compr_ops->set_params(cstream->direction, &compr_params); if (ret) {
dev_err(dev, "failed to set parameters: %d\n", ret); goto params_err;
}
/* * Allocate the stage 0 IRAM buffer size, including the DMA 0 * link-list size and play information of DSP address size.
*/
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_IRAM, dev,
SPRD_COMPR_IRAM_SIZE, &stream->iram_buffer); if (ret < 0) goto err_iram;
/* Use to save link-list configuration for DMA 0. */
stream->dma[0].virt = stream->iram_buffer.area + SPRD_COMPR_IRAM_SIZE;
stream->dma[0].phys = stream->iram_buffer.addr + SPRD_COMPR_IRAM_SIZE;
/* Use to update the current data offset of DSP. */
stream->info_phys = stream->iram_buffer.addr + SPRD_COMPR_IRAM_SIZE +
SPRD_COMPR_IRAM_LINKLIST_SIZE;
stream->info_area = stream->iram_buffer.area + SPRD_COMPR_IRAM_SIZE +
SPRD_COMPR_IRAM_LINKLIST_SIZE;
stream->info_size = SPRD_COMPR_IRAM_INFO_SIZE;
/* * Allocate the stage 1 DDR buffer size, including the DMA 1 link-list * size.
*/
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev,
SPRD_COMPR_AREA_SIZE, &stream->compr_buffer); if (ret < 0) goto err_compr;
/* Use to save link-list configuration for DMA 1. */
stream->dma[1].virt = stream->compr_buffer.area + SPRD_COMPR_AREA_SIZE;
stream->dma[1].phys = stream->compr_buffer.addr + SPRD_COMPR_AREA_SIZE;
cb.drain_notify = sprd_platform_compr_drain_notify;
cb.drain_data = cstream;
ret = stream->compr_ops->open(stream_id, &cb); if (ret) {
dev_err(dev, "failed to open compress platform: %d\n", ret); goto err_open;
}
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: for (i = channels - 1; i >= 0; i--) { struct sprd_compr_dma *dma = &stream->dma[i];
if (dma->chan)
dmaengine_pause(dma->chan);
}
ret = stream->compr_ops->pause(stream_id); break;
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: for (i = channels - 1; i >= 0; i--) { struct sprd_compr_dma *dma = &stream->dma[i];
if (dma->chan)
dmaengine_resume(dma->chan);
}
ret = stream->compr_ops->pause_release(stream_id); break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN: case SND_COMPR_TRIGGER_DRAIN:
ret = stream->compr_ops->drain(stream->received_total); break;
/* * We usually set fragment size as 32K, and the stage 0 IRAM buffer * size is 32K too. So if now the received data size of the stage 0 * IRAM buffer is less than 32K, that means we have some available * spaces for the stage 0 IRAM buffer.
*/ if (stream->received_stage0 < runtime->fragment_size) {
avail_bytes = runtime->fragment_size - stream->received_stage0;
dst = stream->iram_buffer.area + stream->received_stage0;
if (avail_bytes >= data_count) { /* * Copy data to the stage 0 IRAM buffer directly if * spaces are enough.
*/ if (copy_from_user(dst, buf, data_count)) return -EFAULT;
stream->received_stage0 += data_count;
stream->copied_total += data_count; goto copy_done;
} else { /* * If the data count is larger than the available spaces * of the stage 0 IRAM buffer, we should copy one * partial data to the stage 0 IRAM buffer, and copy * the left to the stage 1 DDR buffer.
*/ if (copy_from_user(dst, buf, avail_bytes)) return -EFAULT;
/* * Copy data to the stage 1 DDR buffer if no spaces for the stage 0 IRAM * buffer.
*/
dst = stream->compr_buffer.area + stream->stage1_pointer; if (data_count < stream->compr_buffer.bytes - stream->stage1_pointer) { if (copy_from_user(dst, buf, data_count)) return -EFAULT;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.