Esempio n. 1
0
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;
}
Esempio n. 2
0
File: aloop.c Progetto: 3null/linux
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;
	}
}
Esempio n. 3
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;
}
Esempio n. 4
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;
}
Esempio n. 6
0
/* 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;
}