static void dmaengine_pcm_dma_complete(void *arg) { struct snd_pcm_substream *substream = arg; struct dmaengine_pcm_runtime_data *prtd; snd_pcm_stream_lock(substream); if (!substream || !substream->runtime) { snd_pcm_stream_unlock(substream); return; } prtd = substream_to_prtd(substream); prtd->pos += snd_pcm_lib_period_bytes(substream); if (prtd->pos >= snd_pcm_lib_buffer_bytes(substream)) prtd->pos = 0; snd_pcm_stream_unlock(substream); snd_pcm_period_elapsed(substream); #ifdef CONFIG_SND_PXA_SSP_DUMP if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && ssp_playback_enable) { prtd->playback_transfer_addr = substream->runtime->dma_addr + prtd->pos; queue_work(ssp_playback_wq, &prtd->playback_dump_work); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE && ssp_capture_enable) { prtd->capture_transfer_addr = substream->runtime->dma_addr + prtd->pos; queue_work(ssp_capture_wq, &prtd->capture_dump_work); } #endif }
/* * uni_reader_irq_handler * In case of error audio stream is stopped; stop action is protected via PCM * stream lock to avoid race condition with trigger callback. */ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id) { irqreturn_t ret = IRQ_NONE; struct uniperif *reader = dev_id; unsigned int status; if (reader->state == UNIPERIF_STATE_STOPPED) { /* Unexpected IRQ: do nothing */ dev_warn(reader->dev, "unexpected IRQ "); return IRQ_HANDLED; } /* Get interrupt status & clear them immediately */ status = GET_UNIPERIF_ITS(reader); SET_UNIPERIF_ITS_BCLR(reader, status); /* Check for fifo overflow error */ if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) { dev_err(reader->dev, "FIFO error detected"); snd_pcm_stream_lock(reader->substream); snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(reader->substream); return IRQ_HANDLED; } return ret; }
/** * atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC * * We use DMAENGINE to send/receive data to/from SSC so this ISR is only to * check if any overrun occured. */ static void atmel_pcm_dma_irq(u32 ssc_sr, struct snd_pcm_substream *substream) { struct atmel_pcm_dma_params *prtd; prtd = snd_dmaengine_pcm_get_data(substream); if (ssc_sr & prtd->mask->ssc_error) { if (snd_pcm_running(substream)) pr_warn("atmel-pcm: buffer %s on %s (SSC_SR=%#x)\n", substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? "underrun" : "overrun", prtd->name, ssc_sr); /* stop RX and capture: will be enabled again at restart */ ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_disable); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); /* now drain RHR and read status to remove xrun condition */ ssc_readx(prtd->ssc->regs, SSC_RHR); ssc_readx(prtd->ssc->regs, SSC_SR); } }
/* * XRUN detected, and stop the PCM substream */ static void snd_atiixp_xrun_dma(struct atiixp *chip, struct atiixp_dma *dma) { if (! dma->substream || ! dma->running) return; dev_dbg(chip->card->dev, "XRUN detected (DMA %d)\n", dma->ops->type); snd_pcm_stream_lock(dma->substream); snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(dma->substream); }
/* * XRUN detected, and stop the PCM substream */ static void snd_atiixp_xrun_dma(struct atiixp_modem *chip, struct atiixp_dma *dma) { if (! dma->substream || ! dma->running) return; snd_printdd("atiixp-modem: XRUN detected (DMA %d)\n", dma->ops->type); snd_pcm_stream_lock(dma->substream); snd_pcm_stop(dma->substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(dma->substream); }
static irqreturn_t stm32_sai_isr(int irq, void *devid) { struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid; struct snd_pcm_substream *substream = sai->substream; struct platform_device *pdev = sai->pdev; unsigned int sr, imr, flags; snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING; regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr); regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr); flags = sr & imr; if (!flags) return IRQ_NONE; regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK, SAI_XCLRFR_MASK); if (flags & SAI_XIMR_OVRUDRIE) { dev_err(&pdev->dev, "IT %s\n", STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun"); status = SNDRV_PCM_STATE_XRUN; } if (flags & SAI_XIMR_MUTEDETIE) dev_dbg(&pdev->dev, "IT mute detected\n"); if (flags & SAI_XIMR_WCKCFGIE) { dev_err(&pdev->dev, "IT wrong clock configuration\n"); status = SNDRV_PCM_STATE_DISCONNECTED; } if (flags & SAI_XIMR_CNRDYIE) dev_warn(&pdev->dev, "IT Codec not ready\n"); if (flags & SAI_XIMR_AFSDETIE) { dev_warn(&pdev->dev, "IT Anticipated frame synchro\n"); status = SNDRV_PCM_STATE_XRUN; } if (flags & SAI_XIMR_LFSDETIE) { dev_warn(&pdev->dev, "IT Late frame synchro\n"); status = SNDRV_PCM_STATE_XRUN; } if (status != SNDRV_PCM_STATE_RUNNING) { snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); } return IRQ_HANDLED; }
void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id) { struct snd_pcm_substream *substream = dev_id; int dcsr; dcsr = DCSR(dma_ch); DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN; if (dcsr & DCSR_ENDINTR) { snd_pcm_period_elapsed(substream); } else { printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n", dma_ch, dcsr); snd_pcm_stream_lock(substream); snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(substream); } }
static inline void handle_audio_data(struct urb *urb, int *period_elapsed) { struct poseidon_audio *pa = urb->context; struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime; int stride = runtime->frame_bits >> 3; int len = urb->actual_length / stride; unsigned char *cp = urb->transfer_buffer; unsigned int oldptr = pa->rcv_position; if (urb->actual_length == AUDIO_BUF_SIZE - 4) len -= (AUDIO_TRAILER_SIZE / stride); /* do the copy */ if (oldptr + len >= runtime->buffer_size) { unsigned int cnt = runtime->buffer_size - oldptr; memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); memcpy(runtime->dma_area, (cp + cnt * stride), (len * stride - cnt * stride)); } else memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); /* update the statas */ snd_pcm_stream_lock(pa->capture_pcm_substream); pa->rcv_position += len; if (pa->rcv_position >= runtime->buffer_size) pa->rcv_position -= runtime->buffer_size; pa->copied_position += (len); if (pa->copied_position >= runtime->period_size) { pa->copied_position -= runtime->period_size; *period_elapsed = 1; } snd_pcm_stream_unlock(pa->capture_pcm_substream); }
static void cx231xx_audio_isocirq(struct urb *urb) { struct cx231xx *dev = urb->context; int i; unsigned int oldptr; int period_elapsed = 0; int status; unsigned char *cp; unsigned int stride; struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; if (dev->state & DEV_DISCONNECTED) return; switch (urb->status) { case 0: /* success */ case -ETIMEDOUT: /* NAK */ break; case -ECONNRESET: /* kill */ case -ENOENT: case -ESHUTDOWN: return; default: /* error */ dprintk("urb completition error %d.\n", urb->status); break; } if (atomic_read(&dev->stream_started) == 0) return; if (dev->adev.capture_pcm_substream) { substream = dev->adev.capture_pcm_substream; runtime = substream->runtime; stride = runtime->frame_bits >> 3; for (i = 0; i < urb->number_of_packets; i++) { int length = urb->iso_frame_desc[i].actual_length / stride; cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (!length) continue; oldptr = dev->adev.hwptr_done_capture; if (oldptr + length >= runtime->buffer_size) { unsigned int cnt; cnt = runtime->buffer_size - oldptr; memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); memcpy(runtime->dma_area, cp + cnt * stride, length * stride - cnt * stride); } else { memcpy(runtime->dma_area + oldptr * stride, cp, length * stride); } snd_pcm_stream_lock(substream); dev->adev.hwptr_done_capture += length; if (dev->adev.hwptr_done_capture >= runtime->buffer_size) dev->adev.hwptr_done_capture -= runtime->buffer_size; dev->adev.capture_transfer_done += length; if (dev->adev.capture_transfer_done >= runtime->period_size) { dev->adev.capture_transfer_done -= runtime->period_size; period_elapsed = 1; } snd_pcm_stream_unlock(substream); } if (period_elapsed) snd_pcm_period_elapsed(substream); }
static void usbtv_audio_urb_received(struct urb *urb) { struct usbtv *chip = urb->context; struct snd_pcm_substream *substream = chip->snd_substream; struct snd_pcm_runtime *runtime = substream->runtime; size_t i, frame_bytes, chunk_length, buffer_pos, period_pos; int period_elapsed; void *urb_current; switch (urb->status) { case 0: case -ETIMEDOUT: break; case -ENOENT: case -EPROTO: case -ECONNRESET: case -ESHUTDOWN: return; default: dev_warn(chip->dev, "unknown audio urb status %i\n", urb->status); } if (!atomic_read(&chip->snd_stream)) return; frame_bytes = runtime->frame_bits >> 3; chunk_length = USBTV_CHUNK / frame_bytes; buffer_pos = chip->snd_buffer_pos; period_pos = chip->snd_period_pos; period_elapsed = 0; for (i = 0; i < urb->actual_length; i += USBTV_CHUNK_SIZE) { urb_current = urb->transfer_buffer + i + USBTV_AUDIO_HDRSIZE; if (buffer_pos + chunk_length >= runtime->buffer_size) { size_t cnt = (runtime->buffer_size - buffer_pos) * frame_bytes; memcpy(runtime->dma_area + buffer_pos * frame_bytes, urb_current, cnt); memcpy(runtime->dma_area, urb_current + cnt, chunk_length * frame_bytes - cnt); } else { memcpy(runtime->dma_area + buffer_pos * frame_bytes, urb_current, chunk_length * frame_bytes); } buffer_pos += chunk_length; period_pos += chunk_length; if (buffer_pos >= runtime->buffer_size) buffer_pos -= runtime->buffer_size; if (period_pos >= runtime->period_size) { period_pos -= runtime->period_size; period_elapsed = 1; } } snd_pcm_stream_lock(substream); chip->snd_buffer_pos = buffer_pos; chip->snd_period_pos = period_pos; snd_pcm_stream_unlock(substream); if (period_elapsed) snd_pcm_period_elapsed(substream); usb_submit_urb(urb, GFP_ATOMIC); }
void smi2021_audio(struct smi2021 *smi2021, u8 *data, int len) { struct snd_pcm_runtime *runtime; u8 offset; int new_offset = 0; int skip; unsigned int stride, oldptr, headptr; int diff = 0; int samples = 0; bool period_elapsed = false; if (smi2021->udev == NULL) return; if (atomic_read(&smi2021->adev_capturing) == 0) return; if (smi2021->pcm_substream == NULL) return; runtime = smi2021->pcm_substream->runtime; if (!runtime || !runtime->dma_area) return; offset = smi2021->pcm_read_offset; stride = runtime->frame_bits >> 3; if (stride == 0) return; diff = smi2021->pcm_write_ptr; /* * Check that the end of the last buffer was correct. * If not correct, we mark any partial frames in buffer as complete */ headptr = smi2021->pcm_write_ptr - offset - 4; if (smi2021->pcm_write_ptr > 10 && runtime->dma_area[headptr] != 0x00) { skip = stride - (smi2021->pcm_write_ptr % stride); snd_pcm_stream_lock(smi2021->pcm_substream); smi2021->pcm_write_ptr += skip; if (smi2021->pcm_write_ptr >= runtime->dma_bytes) smi2021->pcm_write_ptr -= runtime->dma_bytes; snd_pcm_stream_unlock(smi2021->pcm_substream); offset = smi2021->pcm_read_offset = 0; } /* * The device is actually sending 24Bit pcm data * with 0x00 as the header byte before each sample. * We look for this byte to make sure we did not * loose any bytes during transfer. */ while (len > stride && (data[offset] != 0x00 || data[offset + (stride / 2)] != 0x00)) { new_offset++; data++; len--; } if (len <= stride) { /* We exhausted the buffer looking for 0x00 */ smi2021->pcm_read_offset = 0; return; } if (new_offset != 0) { /* * This buffer can not be appended to the current buffer, * so we mark any partial frames in the buffer as complete. */ skip = stride - (smi2021->pcm_write_ptr % stride); snd_pcm_stream_lock(smi2021->pcm_substream); smi2021->pcm_write_ptr += skip; if (smi2021->pcm_write_ptr >= runtime->dma_bytes) smi2021->pcm_write_ptr -= runtime->dma_bytes; snd_pcm_stream_unlock(smi2021->pcm_substream); offset = smi2021->pcm_read_offset = new_offset % (stride / 2); } oldptr = smi2021->pcm_write_ptr; if (oldptr + len >= runtime->dma_bytes) { unsigned int cnt = runtime->dma_bytes - oldptr; memcpy(runtime->dma_area + oldptr, data, cnt); memcpy(runtime->dma_area, data + cnt, len - cnt); } else { memcpy(runtime->dma_area + oldptr, data, len); } snd_pcm_stream_lock(smi2021->pcm_substream); smi2021->pcm_write_ptr += len; if (smi2021->pcm_write_ptr >= runtime->dma_bytes) smi2021->pcm_write_ptr -= runtime->dma_bytes; samples = smi2021->pcm_write_ptr - diff; if (samples < 0) samples += runtime->dma_bytes; samples /= (stride / 2); smi2021->pcm_complete_samples += samples; if (smi2021->pcm_complete_samples / 2 >= runtime->period_size) { smi2021->pcm_complete_samples -= runtime->period_size * 2; period_elapsed = true; } snd_pcm_stream_unlock(smi2021->pcm_substream); if (period_elapsed) snd_pcm_period_elapsed(smi2021->pcm_substream); }
static int tm6000_fillbuf(struct tm6000_core *core, char *buf, int size) { struct snd_tm6000_card *chip = core->adev; struct snd_pcm_substream *substream = chip->substream; struct snd_pcm_runtime *runtime; int period_elapsed = 0; unsigned int stride, buf_pos; int length; if (atomic_read(&core->stream_started) == 0) return 0; if (!size || !substream) { dprintk(1, "substream was NULL\n"); return -EINVAL; } runtime = substream->runtime; if (!runtime || !runtime->dma_area) { dprintk(1, "runtime was NULL\n"); return -EINVAL; } buf_pos = chip->buf_pos; stride = runtime->frame_bits >> 3; if (stride == 0) { dprintk(1, "stride is zero\n"); return -EINVAL; } length = size / stride; if (length == 0) { dprintk(1, "%s: length was zero\n", __func__); return -EINVAL; } dprintk(1, "Copying %d bytes at %p[%d] - buf size=%d x %d\n", size, runtime->dma_area, buf_pos, (unsigned int)runtime->buffer_size, stride); if (buf_pos + length >= runtime->buffer_size) { unsigned int cnt = runtime->buffer_size - buf_pos; memcpy(runtime->dma_area + buf_pos * stride, buf, cnt * stride); memcpy(runtime->dma_area, buf + cnt * stride, length * stride - cnt * stride); } else memcpy(runtime->dma_area + buf_pos * stride, buf, length * stride); #ifndef NO_PCM_LOCK snd_pcm_stream_lock(substream); #endif chip->buf_pos += length; if (chip->buf_pos >= runtime->buffer_size) chip->buf_pos -= runtime->buffer_size; chip->period_pos += length; if (chip->period_pos >= runtime->period_size) { chip->period_pos -= runtime->period_size; period_elapsed = 1; } #ifndef NO_PCM_LOCK snd_pcm_stream_unlock(substream); #endif if (period_elapsed) snd_pcm_period_elapsed(substream); return 0; }
/* * uni_player_irq_handler * In case of error audio stream is stopped; stop action is protected via PCM * stream lock to avoid race condition with trigger callback. */ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id) { irqreturn_t ret = IRQ_NONE; struct uniperif *player = dev_id; unsigned int status; unsigned int tmp; if (player->state == UNIPERIF_STATE_STOPPED) { /* Unexpected IRQ: do nothing */ return IRQ_NONE; } /* Get interrupt status & clear them immediately */ status = GET_UNIPERIF_ITS(player); SET_UNIPERIF_ITS_BCLR(player, status); /* Check for fifo error (underrun) */ if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(player))) { dev_err(player->dev, "FIFO underflow error detected\n"); /* Interrupt is just for information when underflow recovery */ if (player->underflow_enabled) { /* Update state to underflow */ player->state = UNIPERIF_STATE_UNDERFLOW; } else { /* Disable interrupt so doesn't continually fire */ SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player); /* Stop the player */ snd_pcm_stream_lock(player->substream); snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(player->substream); } ret = IRQ_HANDLED; } /* Check for dma error (overrun) */ if (unlikely(status & UNIPERIF_ITS_DMA_ERROR_MASK(player))) { dev_err(player->dev, "DMA error detected\n"); /* Disable interrupt so doesn't continually fire */ SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player); /* Stop the player */ snd_pcm_stream_lock(player->substream); snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(player->substream); ret = IRQ_HANDLED; } /* Check for underflow recovery done */ if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) { if (!player->underflow_enabled) { dev_err(player->dev, "unexpected Underflow recovering\n"); return -EPERM; } /* Read the underflow recovery duration */ tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player); dev_dbg(player->dev, "Underflow recovered (%d LR clocks max)\n", tmp); /* Clear the underflow recovery duration */ SET_UNIPERIF_BIT_CONTROL_CLR_UNDERFLOW_DURATION(player); /* Update state to started */ player->state = UNIPERIF_STATE_STARTED; ret = IRQ_HANDLED; } /* Check if underflow recovery failed */ if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_FAILED_MASK(player))) { dev_err(player->dev, "Underflow recovery failed\n"); /* Stop the player */ snd_pcm_stream_lock(player->substream); snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN); snd_pcm_stream_unlock(player->substream); ret = IRQ_HANDLED; } return ret; }
void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, size_t num_bytes) { struct snd_pcm_substream *substream; struct snd_pcm_runtime *runtime; unsigned int oldptr; unsigned int stride; int period_elapsed = 0; int length; dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zu\n", cxsc, pcm_data, num_bytes); substream = cxsc->capture_pcm_substream; if (substream == NULL) { dprintk("substream was NULL\n"); return; } runtime = substream->runtime; if (runtime == NULL) { dprintk("runtime was NULL\n"); return; } stride = runtime->frame_bits >> 3; if (stride == 0) { dprintk("stride is zero\n"); return; } length = num_bytes / stride; if (length == 0) { dprintk("%s: length was zero\n", __func__); return; } if (runtime->dma_area == NULL) { dprintk("dma area was NULL - ignoring\n"); return; } oldptr = cxsc->hwptr_done_capture; if (oldptr + length >= runtime->buffer_size) { unsigned int cnt = runtime->buffer_size - oldptr; memcpy(runtime->dma_area + oldptr * stride, pcm_data, cnt * stride); memcpy(runtime->dma_area, pcm_data + cnt * stride, length * stride - cnt * stride); } else { memcpy(runtime->dma_area + oldptr * stride, pcm_data, length * stride); } snd_pcm_stream_lock(substream); cxsc->hwptr_done_capture += length; if (cxsc->hwptr_done_capture >= runtime->buffer_size) cxsc->hwptr_done_capture -= runtime->buffer_size; cxsc->capture_transfer_done += length; if (cxsc->capture_transfer_done >= runtime->period_size) { cxsc->capture_transfer_done -= runtime->period_size; period_elapsed = 1; } snd_pcm_stream_unlock(substream); if (period_elapsed) snd_pcm_period_elapsed(substream); }