/* * wait for all dma is done. * NOTE: caller should reset card->running before call. * If not, the interrupt handler will re-start DMA, * then DMA is never stopped. */ static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card) { int stop_forced; /* * wait for the last dma is done */ /* * expected maximum DMA done time is 5.7ms + something (DMA itself). * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next * DMA kick event would occur. */ stop_forced = snd_ps3_verify_dma_stop(card, 700, 1); /* * clear outstanding interrupts. */ update_reg(PS3_AUDIO_INTR_0, 0); update_reg(PS3_AUDIO_AX_IS, 0); /* *revert CLEAR bit since it will not reset automatically after DMA stop */ if (stop_forced) update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); /* ensure the hardware sees changes */ wmb(); }
/* * av setting * NOTE: calling this function may generate audio interrupt. */ static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card) { int ret, retries, i; pr_debug("%s: start\n", __func__); ret = ps3av_set_audio_mode(card->avs.avs_audio_ch, card->avs.avs_audio_rate, card->avs.avs_audio_width, card->avs.avs_audio_format, card->avs.avs_audio_source); /* * Reset the following unwanted settings: */ /* disable all 3wire buffers */ update_mask_reg(PS3_AUDIO_AO_3WMCTRL, ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) | PS3_AUDIO_AO_3WMCTRL_ASOEN(1) | PS3_AUDIO_AO_3WMCTRL_ASOEN(2) | PS3_AUDIO_AO_3WMCTRL_ASOEN(3)), 0); wmb(); /* ensure the hardware sees the change */ /* wait for actually stopped */ retries = 1000; while ((read_reg(PS3_AUDIO_AO_3WMCTRL) & (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) | PS3_AUDIO_AO_3WMCTRL_ASORUN(1) | PS3_AUDIO_AO_3WMCTRL_ASORUN(2) | PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) && --retries) { udelay(1); } /* reset buffer pointer */ for (i = 0; i < 4; i++) { update_reg(PS3_AUDIO_AO_3WCTRL(i), PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET); udelay(10); } wmb(); /* ensure the hardware actually start resetting */ /* enable 3wire#0 buffer */ update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0)); /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */ update_mask_reg(PS3_AUDIO_AO_3WCTRL(0), ~PS3_AUDIO_AO_3WCTRL_ASODF, PS3_AUDIO_AO_3WCTRL_ASODF_LSB); update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0), ~PS3_AUDIO_AO_SPDCTRL_SPODF, PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); /* ensure all the setting above is written back to register */ wmb(); /* avsetting driver altered AX_IE, caller must reset it if you want */ pr_debug("%s: end\n", __func__); return ret; }
static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card) { int ret, retries, i; pr_debug("%s: start\n", __func__); ret = ps3av_set_audio_mode(card->avs.avs_audio_ch, card->avs.avs_audio_rate, card->avs.avs_audio_width, card->avs.avs_audio_format, card->avs.avs_audio_source); update_mask_reg(PS3_AUDIO_AO_3WMCTRL, ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) | PS3_AUDIO_AO_3WMCTRL_ASOEN(1) | PS3_AUDIO_AO_3WMCTRL_ASOEN(2) | PS3_AUDIO_AO_3WMCTRL_ASOEN(3)), 0); wmb(); retries = 1000; while ((read_reg(PS3_AUDIO_AO_3WMCTRL) & (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) | PS3_AUDIO_AO_3WMCTRL_ASORUN(1) | PS3_AUDIO_AO_3WMCTRL_ASORUN(2) | PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) && --retries) { udelay(1); } for (i = 0; i < 4; i++) { update_reg(PS3_AUDIO_AO_3WCTRL(i), PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET); udelay(10); } wmb(); update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0)); update_mask_reg(PS3_AUDIO_AO_3WCTRL(0), ~PS3_AUDIO_AO_3WCTRL_ASODF, PS3_AUDIO_AO_3WCTRL_ASODF_LSB); update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0), ~PS3_AUDIO_AO_SPDCTRL_SPODF, PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); /* ensure all the setting above is written back to register */ wmb(); pr_debug("%s: end\n", __func__); return ret; }
static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card) { int stop_forced; stop_forced = snd_ps3_verify_dma_stop(card, 700, 1); update_reg(PS3_AUDIO_INTR_0, 0); update_reg(PS3_AUDIO_AX_IS, 0); if (stop_forced) update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); wmb(); }
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 void snd_ps3_kick_dma(struct snd_ps3_card_info *card) { update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST); /* ensure the hardware sees the change */ wmb(); }
static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, int count, int force_stop) { int dma_ch, done, retries, stop_forced = 0; uint32_t status; for (dma_ch = 0; dma_ch < 8; dma_ch++) { retries = count; do { status = read_reg(PS3_AUDIO_KICK(dma_ch)) & PS3_AUDIO_KICK_STATUS_MASK; switch (status) { case PS3_AUDIO_KICK_STATUS_DONE: case PS3_AUDIO_KICK_STATUS_NOTIFY: case PS3_AUDIO_KICK_STATUS_CLEAR: case PS3_AUDIO_KICK_STATUS_ERROR: done = 1; break; default: done = 0; udelay(10); } } while (!done && --retries); if (!retries && force_stop) { pr_info("%s: DMA ch %d is not stopped.", __func__, dma_ch); /* last resort. force to stop dma. * NOTE: this cause DMA done interrupts */ update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR); stop_forced = 1; } } return stop_forced; }
static void snd_ps3_kick_dma(struct snd_ps3_card_info *card) { update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST); wmb(); }
static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); int ret = 0; switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* clear outstanding interrupts */ update_reg(PS3_AUDIO_AX_IS, 0); spin_lock(&card->dma_lock); { card->running = 1; } spin_unlock(&card->dma_lock); snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); while (read_reg(PS3_AUDIO_KICK(7)) & PS3_AUDIO_KICK_STATUS_MASK) { udelay(1); } snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); snd_ps3_kick_dma(card); break; case SNDRV_PCM_TRIGGER_STOP: spin_lock(&card->dma_lock); { card->running = 0; } spin_unlock(&card->dma_lock); snd_ps3_wait_for_dma_stop(card); break; default: break; } return ret; };
static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card) { write_reg(PS3_AUDIO_INTR_EN_0, 0); update_mask_reg(PS3_AUDIO_AX_IC, PS3_AUDIO_AX_IC_AASOIMD_MASK, PS3_AUDIO_AX_IC_AASOIMD_EVERY4); update_mask_reg(PS3_AUDIO_AO_3WMCTRL, ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED | PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED), 0); update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT); }
static void __devinit snd_ps3_audio_fixup(struct snd_ps3_card_info *card) { /* * avsetting driver seems to never change the followings * so, init them here once */ /* no dma interrupt needed */ write_reg(PS3_AUDIO_INTR_EN_0, 0); /* use every 4 buffer empty interrupt */ update_mask_reg(PS3_AUDIO_AX_IC, PS3_AUDIO_AX_IC_AASOIMD_MASK, PS3_AUDIO_AX_IC_AASOIMD_EVERY4); /* enable 3wire clocks */ update_mask_reg(PS3_AUDIO_AO_3WMCTRL, ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED | PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED), 0); update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT); }
/* * 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; };