static snd_pcm_uframes_t wmt_pdm_pcm_pointer(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct audio_stream_a *prtd = runtime->private_data; int stream_id = substream->pstr->stream; struct audio_stream_a *s = &prtd[stream_id]; dma_addr_t ptr; snd_pcm_uframes_t offset = 0; //DBG_DETAIL(); ptr = wmt_get_dma_pos(s->dmach); /* if (((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) || ((runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_U8))) { offset = bytes_to_frames(runtime, (ptr - dump_buf[stream_id].addr) >> 1); } else if ((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_U8)) { offset = bytes_to_frames(runtime, (ptr - dump_buf[stream_id].addr) >> 2); } else if ((runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_FLOAT)) { offset = bytes_to_frames(runtime, (ptr - dump_buf[stream_id].addr) << 1); } else if ((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_FLOAT)) { offset = bytes_to_frames(runtime, ptr - dump_buf[stream_id].addr); } else offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); */ if (((runtime->channels == 2) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE)) || ((runtime->channels == 1) && (runtime->format == SNDRV_PCM_FORMAT_S16_LE))) { offset = bytes_to_frames(runtime, ptr - runtime->dma_addr); } if (offset >= runtime->buffer_size) offset = 0; spin_lock(&s->dma_lock); if (s->periods > 0 && s->periods < 2) { if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) { if (snd_pcm_playback_hw_avail(runtime) >= 2 * runtime->period_size) audio_process_dma(s); } else { if (snd_pcm_capture_hw_avail(runtime) >= 2* runtime->period_size) audio_process_dma(s); } } spin_unlock(&s->dma_lock); //DPRINTK("offset = %x", (unsigned int)offset); return offset; }
static void copy_play_buf(struct loopback_pcm *play, struct loopback_pcm *capt, unsigned int bytes) { struct snd_pcm_runtime *runtime = play->substream->runtime; char *src = runtime->dma_area; char *dst = capt->substream->runtime->dma_area; unsigned int src_off = play->buf_pos; unsigned int dst_off = capt->buf_pos; unsigned int clear_bytes = 0; /* check if playback is draining, trim the capture copy size * when our pointer is at the end of playback ring buffer */ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING && snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { snd_pcm_uframes_t appl_ptr, appl_ptr1, diff; appl_ptr = appl_ptr1 = runtime->control->appl_ptr; appl_ptr1 -= appl_ptr1 % runtime->buffer_size; appl_ptr1 += play->buf_pos / play->pcm_salign; if (appl_ptr < appl_ptr1) appl_ptr1 -= runtime->buffer_size; diff = (appl_ptr - appl_ptr1) * play->pcm_salign; if (diff < bytes) { clear_bytes = bytes - diff; bytes = diff; } } for (;;) { unsigned int size = bytes; if (src_off + size > play->pcm_buffer_size) size = play->pcm_buffer_size - src_off; if (dst_off + size > capt->pcm_buffer_size) size = capt->pcm_buffer_size - dst_off; memcpy(dst + dst_off, src + src_off, size); capt->silent_size = 0; bytes -= size; if (!bytes) break; src_off = (src_off + size) % play->pcm_buffer_size; dst_off = (dst_off + size) % capt->pcm_buffer_size; } if (clear_bytes > 0) { clear_capture_buf(capt, clear_bytes); capt->silent_size = 0; } }
/* call with interrupts locked? */ static int _ksnd_pcm_update_appl_ptr(snd_pcm_substream_t *substream, snd_pcm_uframes_t appl_ptr) { snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_sframes_t hw_avail; int err; runtime->control->appl_ptr = appl_ptr; if (substream->ops->ack) substream->ops->ack(substream); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) hw_avail = snd_pcm_playback_hw_avail(runtime); else hw_avail = snd_pcm_capture_hw_avail(runtime); if (runtime->status->state == SNDRV_PCM_STATE_PREPARED && hw_avail >= (snd_pcm_sframes_t) runtime->start_threshold) { err = snd_pcm_start(substream); if (err < 0) return err; } return 0; }
static int _ksnd_pcm_writei1(snd_pcm_substream_t *substream, unsigned long data, snd_pcm_uframes_t size, int srcchannels, transfer_f transfer) { snd_pcm_runtime_t *runtime = substream->runtime; snd_pcm_uframes_t xfer = 0; snd_pcm_uframes_t offset = 0; int err = 0; if (size == 0) return 0; snd_pcm_stream_lock_irq(substream); switch (_ksnd_pcm_state(substream)) { case SNDRV_PCM_STATE_PREPARED: case SNDRV_PCM_STATE_RUNNING: case SNDRV_PCM_STATE_PAUSED: break; case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: err = -EBADFD; goto _end_unlock; } while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t avail; snd_pcm_uframes_t cont; avail = _ksnd_pcm_avail_update(substream); #if defined(__TDT__) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) // attribute xfer_align is not used any more /* #warning Do we have to check the values 'size' and 'avail'? */ if ((avail < runtime->control->avail_min) && (size > avail)) { #else if (((avail < runtime->control->avail_min && size > avail) || (size >= runtime->xfer_align && avail < runtime->xfer_align))) { #endif int res; snd_pcm_stream_unlock_irq(substream); do { res = _ksnd_pcm_wait(substream, 10000); } while (res == 0 && _ksnd_pcm_state(substream) != SNDRV_PCM_STATE_PREPARED && _ksnd_pcm_state(substream) != SNDRV_PCM_STATE_PAUSED); snd_pcm_stream_lock_irq(substream); if (res == 0) /* timeout */ { if (_ksnd_pcm_state(substream) == SNDRV_PCM_STATE_SUSPENDED) { err = -ESTRPIPE; goto _end_unlock; } else { snd_printd("playback write error " "(DMA or IRQ trouble?)\n"); err = -EIO; goto _end_unlock; } } else if (res < 0) /* error */ { err = res; goto _end_unlock; } avail = snd_pcm_playback_avail(runtime); } frames = size > avail ? avail : size; cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size; if (frames > cont) frames = cont; if (snd_BUG_ON(!frames)) { snd_pcm_stream_unlock_irq(substream); return -EINVAL; } appl_ptr = runtime->control->appl_ptr; appl_ofs = appl_ptr % runtime->buffer_size; snd_pcm_stream_unlock_irq(substream); err = transfer(substream, appl_ofs, data, offset, frames, srcchannels); snd_pcm_stream_lock_irq(substream); if (err < 0) goto _end; switch (_ksnd_pcm_state(substream)) { case SNDRV_PCM_STATE_XRUN: err = -EPIPE; goto _end_unlock; case SNDRV_PCM_STATE_SUSPENDED: err = -ESTRPIPE; goto _end_unlock; default: break; } appl_ptr += frames; if (appl_ptr >= runtime->boundary) { runtime->control->appl_ptr = 0; } else { runtime->control->appl_ptr = appl_ptr; } if (substream->ops->ack) substream->ops->ack(substream); offset += frames; size -= frames; xfer += frames; if (_ksnd_pcm_state(substream) == SNDRV_PCM_STATE_PREPARED && snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t) runtime->start_threshold) { err = snd_pcm_start(substream); if (err < 0) goto _end_unlock; } } _end_unlock: snd_pcm_stream_unlock_irq(substream); _end: return xfer > 0 ? (snd_pcm_sframes_t) xfer : err; } int ksnd_pcm_writei(ksnd_pcm_t *kpcm, int *data, unsigned int size, unsigned int srcchannels) { snd_pcm_substream_t *substream = kpcm->substream; snd_pcm_runtime_t *runtime; int err; transfer_f out_func = 0; runtime = substream->runtime; if (substream->pcm->card->number == 2) { out_func = _ksnd_pcm_IEC60958_transfer; } else { out_func = _ksnd_pcm_write_transfer; } if (_ksnd_pcm_state(substream) == SNDRV_PCM_STATE_OPEN) return -EBADFD; if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED && runtime->channels > 1) return -EINVAL; if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) return -EINVAL; if (size == 0) return 0; do { err = _ksnd_pcm_writei1(substream, (unsigned long)data, size, srcchannels, out_func); if (err < 0) { if (err == -EAGAIN) { continue; } if (err == -EPIPE) { printk("ALSA Aud underrun for hw:%d,%d\n", substream->pcm->card->number, substream->pcm->device); if ((err = ksnd_pcm_prepare(kpcm)) < 0) return err; continue; } return err; } else { data += samples_to_bytes(runtime, err * srcchannels); size -= err; } } while (size > 0); return 0; }
static irqreturn_t hi3630_intr_src_up_handle(int irq, void *dev_id) { struct snd_pcm *pcm = dev_id; struct snd_pcm_substream *substream = NULL; struct snd_pcm_runtime *runtime = NULL; struct hi3630_srcup_runtime_data *prtd = NULL; struct hi3630_srcup_data *pdata = NULL; unsigned int rt_period_size = 0; unsigned int num_periods = 0; unsigned int irs = 0; unsigned int dma_cur = SRCUP_CH0_DMA_A; unsigned int dma_next = SRCUP_CH0_DMA_A; snd_pcm_uframes_t avail = 0; BUG_ON(NULL == pcm); substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; if (NULL == substream) { loge("substream is NULL\n"); return IRQ_HANDLED; } runtime = substream->runtime; prtd = runtime->private_data; pdata = prtd->pdata; rt_period_size = runtime->period_size; num_periods = runtime->periods; irs = hi3630_srcup_reg_read(pdata, HI3630_SRCUP_CH_INT_FLAG_REG) & SRCUP_CH0_MASK; if (0 == irs) { /* when hifi dsp playback is enable, it will go to here */ return IRQ_HANDLED; } spin_lock(&prtd->lock); prtd->period_cur = (prtd->period_cur + 1) % num_periods; spin_unlock(&prtd->lock); snd_pcm_period_elapsed(substream); spin_lock(&prtd->lock); if (STATUS_SRCUP_STOP == prtd->status) { logd("stop dma, irs = %#x\n", irs); hi3630_srcup_reg_write(pdata, HI3630_SRCUP_CH_INT_FLAG_REG, irs); spin_unlock(&prtd->lock); return IRQ_HANDLED; } if (0 != ((1 << SRCUP_CH0_DMA_A) & irs)) { hi3630_srcup_reg_write(pdata, HI3630_SRCUP_CH_INT_FLAG_REG, SRCUP_CH0_DMA_A_MASK); dma_cur = SRCUP_CH0_DMA_A; dma_next = SRCUP_CH0_DMA_B; } else { hi3630_srcup_reg_write(pdata, HI3630_SRCUP_CH_INT_FLAG_REG, SRCUP_CH0_DMA_B_MASK); dma_cur = SRCUP_CH0_DMA_B; dma_next = SRCUP_CH0_DMA_A; } avail = snd_pcm_playback_hw_avail(runtime); if (avail < rt_period_size) { logd("Run out of data\n"); prtd->two_dma_flag = false; } else { /* config & enable DMA */ config_srcup_dma(prtd, dma_cur); hi3630_srcup_enable_dma(pdata, dma_cur); prtd->period_next = (prtd->period_next + 1) % num_periods; if ((!prtd->two_dma_flag) && (avail >= rt_period_size * 2)) { /* config & enable DMA */ config_srcup_dma(prtd, dma_next); hi3630_srcup_enable_dma(pdata, dma_next); prtd->period_next = (prtd->period_next + 1) % num_periods; prtd->two_dma_flag = true; } } spin_unlock(&prtd->lock); return IRQ_HANDLED; }
/* handle one TX DMA interrupt at a time */ static irqreturn_t hi3620_intr_handle_digital_pb(struct snd_pcm_substream *substream) { struct hi3620_runtime_data *prtd = substream->runtime->private_data; unsigned int period_size = 0; unsigned int rt_period_size = substream->runtime->period_size; unsigned int num_period = substream->runtime->periods; snd_pcm_uframes_t avail = 0; int txindex = 0; int dmacur = 0; int dmanext = 0; unsigned int irs = hi3620_reg_read(ASP_IRSR) & (TX2_DMAS | TX3_DMAS); if (NULL == prtd) { loge("%s PCM = NULL \n", __FUNCTION__); /* CLEAR ALL TX DMA INTERRUPT */ hi3620_reg_write(irs, ASP_ICR); return IRQ_HANDLED; } if (prtd->tx[0].dmas == (irs & prtd->tx[0].dmas)) logd("%s : TWO INTS COME TOGETHER::PLAYBACK\n", __FUNCTION__); if (0 == (irs & prtd->tx[0].dmas)) { loge("%s : unexpected interrupt\n",__FUNCTION__); hi3620_reg_write(irs, ASP_ICR); return IRQ_HANDLED; } period_size = prtd->period_size; if (irs & prtd->tx[0].dmaa) { txindex = 0; dmacur = prtd->tx[0].dmaa; dmanext = prtd->tx[0].dmab; } else { txindex = 0; dmacur = prtd->tx[0].dmab; dmanext = prtd->tx[0].dmaa; } hi3620_reg_write(dmacur, ASP_ICR); prtd->tx[txindex].period_cur++; spin_lock(&prtd->lock); prtd->period_cur = prtd->tx[0].period_cur % num_period; spin_unlock(&prtd->lock); snd_pcm_period_elapsed(substream); spin_lock(&prtd->lock); /* DMA IS STOPPED CLEAR INTERRUPT */ if (STATUS_STOP == prtd->status) { logd("%s : stop dma, irs = %#x\n", __FUNCTION__, irs); hi3620_reg_write(irs, ASP_ICR); spin_unlock(&prtd->lock); return IRQ_HANDLED; } avail = snd_pcm_playback_hw_avail(substream->runtime); if(avail < rt_period_size) { prtd->tx[0].two_dma_flag = false; logd("Run out of data in both DMAs, disable both DMAs\n"); spin_unlock(&prtd->lock); return IRQ_HANDLED; } else { /* avail >= rt_period_size, enable one dma at least */ /* handle a work of TX data format transform and this work need to be done before current DMA enabled */ if (prtd->data_convert == true) { hi3620_intr_handle_digital_work(dmacur, prtd->tx[txindex].period_next, prtd); } else { enable_dma(dmacur, substream->runtime->dma_addr, prtd->tx[txindex].period_next, period_size); } prtd->tx[txindex].period_next = (prtd->tx[txindex].period_next + 1) % num_period; if ((!prtd->tx[txindex].two_dma_flag) && (avail >= rt_period_size * 2 )) { logd("enable both DMAs\n"); /* enable DMA B */ prtd->tx[txindex].two_dma_flag = true; /* handle a work of TX data format transform and this work need to be done before next DMA enabled*/ if (prtd->data_convert == true) { hi3620_intr_handle_digital_work(dmanext, prtd->tx[txindex].period_next, prtd); } else { enable_dma(dmanext, substream->runtime->dma_addr, prtd->tx[txindex].period_next, period_size); } prtd->tx[txindex].period_next = (prtd->tx[txindex].period_next + 1) % num_period; } } spin_unlock(&prtd->lock); return IRQ_HANDLED; }