static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id) { uint32_t port_intr; int underflow_occured = 0; struct snd_ps3_card_info *card = dev_id; if (!card->running) { update_reg(PS3_AUDIO_AX_IS, 0); update_reg(PS3_AUDIO_INTR_0, 0); return IRQ_HANDLED; } port_intr = read_reg(PS3_AUDIO_AX_IS); if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) { write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { write_reg(PS3_AUDIO_AX_IS, port_intr); underflow_occured = 1; } if (card->silent) { snd_ps3_program_dma(card, (underflow_occured) ? SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL : SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); snd_ps3_kick_dma(card); card->silent--; } else { snd_ps3_program_dma(card, (underflow_occured) ? SND_PS3_DMA_FILLTYPE_FIRSTFILL : SND_PS3_DMA_FILLTYPE_RUNNING); snd_ps3_kick_dma(card); snd_pcm_period_elapsed(card->substream); } } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); } return IRQ_HANDLED; };
static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); unsigned long irqsave; if (!snd_ps3_set_avsetting(substream)) { /* some parameter changed */ write_reg(PS3_AUDIO_AX_IE, PS3_AUDIO_AX_IE_ASOBEIE(0) | PS3_AUDIO_AX_IE_ASOBUIE(0)); /* * let SPDIF device re-lock with SPDIF signal, * start with some silence */ card->silent = snd_ps3_delay_to_bytes(substream, card->start_delay) / (PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */ } /* restart ring buffer pointer */ spin_lock_irqsave(&card->dma_lock, irqsave); { card->dma_buffer_size = runtime->dma_bytes; card->dma_last_transfer_vaddr[SND_PS3_CH_L] = card->dma_next_transfer_vaddr[SND_PS3_CH_L] = card->dma_start_vaddr[SND_PS3_CH_L] = runtime->dma_area; card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; card->dma_last_transfer_vaddr[SND_PS3_CH_R] = card->dma_next_transfer_vaddr[SND_PS3_CH_R] = card->dma_start_vaddr[SND_PS3_CH_R] = runtime->dma_area + (runtime->dma_bytes / 2); card->dma_start_bus_addr[SND_PS3_CH_R] = runtime->dma_addr + (runtime->dma_bytes / 2); pr_debug("%s: vaddr=%p bus=%#llx\n", __func__, card->dma_start_vaddr[SND_PS3_CH_L], card->dma_start_bus_addr[SND_PS3_CH_L]); } spin_unlock_irqrestore(&card->dma_lock, irqsave); /* ensure the hardware sees the change */ mb(); return 0; };
static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); unsigned long irqsave; if (!snd_ps3_set_avsetting(substream)) { write_reg(PS3_AUDIO_AX_IE, PS3_AUDIO_AX_IE_ASOBEIE(0) | PS3_AUDIO_AX_IE_ASOBUIE(0)); card->silent = snd_ps3_delay_to_bytes(substream, card->start_delay) / (PS3_AUDIO_FIFO_STAGE_SIZE * 4); } spin_lock_irqsave(&card->dma_lock, irqsave); { card->dma_buffer_size = runtime->dma_bytes; card->dma_last_transfer_vaddr[SND_PS3_CH_L] = card->dma_next_transfer_vaddr[SND_PS3_CH_L] = card->dma_start_vaddr[SND_PS3_CH_L] = runtime->dma_area; card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; card->dma_last_transfer_vaddr[SND_PS3_CH_R] = card->dma_next_transfer_vaddr[SND_PS3_CH_R] = card->dma_start_vaddr[SND_PS3_CH_R] = runtime->dma_area + (runtime->dma_bytes / 2); card->dma_start_bus_addr[SND_PS3_CH_R] = runtime->dma_addr + (runtime->dma_bytes / 2); pr_debug("%s: vaddr=%p bus=%#llx\n", __func__, card->dma_start_vaddr[SND_PS3_CH_L], card->dma_start_bus_addr[SND_PS3_CH_L]); } spin_unlock_irqrestore(&card->dma_lock, irqsave); mb(); return 0; };
/* * Interrupt handler */ static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id) { uint32_t port_intr; int underflow_occured = 0; struct snd_ps3_card_info *card = dev_id; if (!card->running) { update_reg(PS3_AUDIO_AX_IS, 0); update_reg(PS3_AUDIO_INTR_0, 0); return IRQ_HANDLED; } port_intr = read_reg(PS3_AUDIO_AX_IS); /* *serial buffer empty detected (every 4 times), *program next dma and kick it */ if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) { write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { write_reg(PS3_AUDIO_AX_IS, port_intr); underflow_occured = 1; } if (card->silent) { /* we are still in silent time */ snd_ps3_program_dma(card, (underflow_occured) ? SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL : SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); snd_ps3_kick_dma(card); card->silent--; } else { snd_ps3_program_dma(card, (underflow_occured) ? SND_PS3_DMA_FILLTYPE_FIRSTFILL : SND_PS3_DMA_FILLTYPE_RUNNING); snd_ps3_kick_dma(card); snd_pcm_period_elapsed(card->substream); } } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); /* * serial out underflow, but buffer empty not detected. * in this case, fill fifo with 0 to recover. After * filling dummy data, serial automatically start to * consume them and then will generate normal buffer * empty interrupts. * If both buffer underflow and buffer empty are occurred, * it is better to do nomal data transfer than empty one */ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); } /* clear interrupt cause */ return IRQ_HANDLED; };