Loading sound/ppc/snd_ps3.c +354 −399 Original line number Diff line number Diff line Loading @@ -18,81 +18,31 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/slab.h> #include <sound/asound.h> #include <sound/control.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/asound.h> #include <sound/memalloc.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/control.h> #include <linux/dmapool.h> #include <linux/dma-mapping.h> #include <asm/firmware.h> #include <asm/dma.h> #include <asm/firmware.h> #include <asm/lv1call.h> #include <asm/ps3.h> #include <asm/ps3av.h> #include "snd_ps3_reg.h" #include "snd_ps3.h" MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("PS3 sound driver"); MODULE_AUTHOR("Sony Computer Entertainment Inc."); /* module entries */ static int __init snd_ps3_init(void); static void __exit snd_ps3_exit(void); /* ALSA snd driver ops */ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream); static int snd_ps3_pcm_close(struct snd_pcm_substream *substream); static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream); static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, int cmd); static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream *substream); static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream); /* ps3_system_bus_driver entries */ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev); static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev); /* address setup */ static int snd_ps3_map_mmio(void); static void snd_ps3_unmap_mmio(void); static int snd_ps3_allocate_irq(void); static void snd_ps3_free_irq(void); static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start); /* interrupt handler */ static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id); /* set sampling rate/format */ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream); /* take effect parameter change */ static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card); /* initialize avsetting and take it effect */ static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card); /* setup dma */ static int snd_ps3_program_dma(struct snd_ps3_card_info *card, enum snd_ps3_dma_filltype filltype); static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card); static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void *vaddr, int ch); #include "snd_ps3_reg.h" module_init(snd_ps3_init); module_exit(snd_ps3_exit); /* * global */ Loading Loading @@ -165,17 +115,6 @@ static const struct snd_pcm_hardware snd_ps3_pcm_hw = { .fifo_size = PS3_AUDIO_FIFO_SIZE }; static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = { .open = snd_ps3_pcm_open, .close = snd_ps3_pcm_close, .prepare = snd_ps3_pcm_prepare, .ioctl = snd_pcm_lib_ioctl, .trigger = snd_ps3_pcm_trigger, .pointer = snd_ps3_pcm_pointer, .hw_params = snd_ps3_pcm_hw_params, .hw_free = snd_ps3_pcm_hw_free }; static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, int count, int force_stop) { Loading Loading @@ -368,6 +307,71 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card, 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 occured, * 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; }; /* * audio mute on/off * mute_on : 0 output enabled Loading @@ -378,6 +382,142 @@ static int snd_ps3_mute(int mute_on) return ps3av_audio_mute(mute_on); } /* * 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; } /* * set sampling rate according to the substream */ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); struct snd_ps3_avsetting_info avs; int ret; avs = card->avs; pr_debug("%s: called freq=%d width=%d\n", __func__, substream->runtime->rate, snd_pcm_format_width(substream->runtime->format)); pr_debug("%s: before freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); /* sample rate */ switch (substream->runtime->rate) { case 44100: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; break; case 48000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; break; case 88200: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; break; case 96000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; break; default: pr_info("%s: invalid rate %d\n", __func__, substream->runtime->rate); return 1; } /* width */ switch (snd_pcm_format_width(substream->runtime->format)) { case 16: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; break; case 24: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; break; default: pr_info("%s: invalid width %d\n", __func__, snd_pcm_format_width(substream->runtime->format)); return 1; } memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8); if (memcmp(&card->avs, &avs, sizeof(avs))) { pr_debug("%s: after freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); card->avs = avs; snd_ps3_change_avsetting(card); ret = 0; } else ret = 1; /* check CS non-audio bit and mute accordingly */ if (avs.avs_cs_info[0] & 0x02) ps3av_audio_mute_analog(1); /* mute if non-audio */ else ps3av_audio_mute_analog(0); return ret; } /* * PCM operators */ Loading @@ -403,6 +543,13 @@ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream) return 0; }; static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) { /* mute on */ snd_ps3_mute(1); return 0; }; static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { Loading @@ -414,6 +561,13 @@ static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, return 0; }; static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) { int ret; ret = snd_pcm_lib_free_pages(substream); return ret; }; static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, unsigned int delay_ms) { Loading Loading @@ -464,290 +618,94 @@ static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) 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_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; }; /* * report current pointer */ static snd_pcm_uframes_t snd_ps3_pcm_pointer( struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); size_t bytes; snd_pcm_uframes_t ret; spin_lock(&card->dma_lock); { bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - card->dma_start_vaddr[SND_PS3_CH_L]); } spin_unlock(&card->dma_lock); ret = bytes_to_frames(substream->runtime, bytes * 2); return ret; }; static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) { int ret; ret = snd_pcm_lib_free_pages(substream); return ret; }; static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) { /* mute on */ snd_ps3_mute(1); return 0; }; static void 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); } /* * 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_init_avsetting(struct snd_ps3_card_info *card) { int ret; pr_debug("%s: start\n", __func__); card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8); runtime->dma_area; card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; ret = snd_ps3_change_avsetting(card); 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); snd_ps3_audio_fixup(card); 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]); /* to start to generate SPDIF signal, fill data */ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); pr_debug("%s: end\n", __func__); return ret; } spin_unlock_irqrestore(&card->dma_lock, irqsave); /* * set sampling rate according to the substream */ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); struct snd_ps3_avsetting_info avs; int ret; /* ensure the hardware sees the change */ mb(); avs = card->avs; return 0; }; pr_debug("%s: called freq=%d width=%d\n", __func__, substream->runtime->rate, snd_pcm_format_width(substream->runtime->format)); 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; pr_debug("%s: before freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* clear outstanding interrupts */ update_reg(PS3_AUDIO_AX_IS, 0); /* sample rate */ switch (substream->runtime->rate) { case 44100: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; break; case 48000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; break; case 88200: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; break; case 96000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; break; default: pr_info("%s: invalid rate %d\n", __func__, substream->runtime->rate); return 1; spin_lock(&card->dma_lock); { card->running = 1; } spin_unlock(&card->dma_lock); /* width */ switch (snd_pcm_format_width(substream->runtime->format)) { case 16: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; 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 24: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; 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: pr_info("%s: invalid width %d\n", __func__, snd_pcm_format_width(substream->runtime->format)); return 1; break; } memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8); return ret; }; if (memcmp(&card->avs, &avs, sizeof(avs))) { pr_debug("%s: after freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); /* * report current pointer */ static snd_pcm_uframes_t snd_ps3_pcm_pointer( struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); size_t bytes; snd_pcm_uframes_t ret; card->avs = avs; snd_ps3_change_avsetting(card); ret = 0; } else ret = 1; spin_lock(&card->dma_lock); { bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - card->dma_start_vaddr[SND_PS3_CH_L]); } spin_unlock(&card->dma_lock); /* check CS non-audio bit and mute accordingly */ if (avs.avs_cs_info[0] & 0x02) ps3av_audio_mute_analog(1); /* mute if non-audio */ else ps3av_audio_mute_analog(0); ret = bytes_to_frames(substream->runtime, bytes * 2); return ret; } }; /* * SPDIF status bits controls Loading Loading @@ -815,6 +773,17 @@ static struct snd_kcontrol_new spdif_ctls[] = { }, }; static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = { .open = snd_ps3_pcm_open, .close = snd_ps3_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ps3_pcm_hw_params, .hw_free = snd_ps3_pcm_hw_free, .prepare = snd_ps3_pcm_prepare, .trigger = snd_ps3_pcm_trigger, .pointer = snd_ps3_pcm_pointer, }; static int snd_ps3_map_mmio(void) { Loading Loading @@ -912,6 +881,52 @@ static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) ret); } static void 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); } static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card) { int ret; pr_debug("%s: start\n", __func__); card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8); ret = snd_ps3_change_avsetting(card); snd_ps3_audio_fixup(card); /* to start to generate SPDIF signal, fill data */ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); pr_debug("%s: end\n", __func__); return ret; } static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) { int i, ret; Loading Loading @@ -1112,71 +1127,6 @@ static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { }; /* * 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 occured, * 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; }; /* * module/subsystem initialize/terminate */ Loading @@ -1195,10 +1145,15 @@ static int __init snd_ps3_init(void) return ret; } module_init(snd_ps3_init); static void __exit snd_ps3_exit(void) { ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info); } module_exit(snd_ps3_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("PS3 sound driver"); MODULE_AUTHOR("Sony Computer Entertainment Inc."); MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND); Loading
sound/ppc/snd_ps3.c +354 −399 Original line number Diff line number Diff line Loading @@ -18,81 +18,31 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/dma-mapping.h> #include <linux/dmapool.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/io.h> #include <linux/slab.h> #include <sound/asound.h> #include <sound/control.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> #include <sound/asound.h> #include <sound/memalloc.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/control.h> #include <linux/dmapool.h> #include <linux/dma-mapping.h> #include <asm/firmware.h> #include <asm/dma.h> #include <asm/firmware.h> #include <asm/lv1call.h> #include <asm/ps3.h> #include <asm/ps3av.h> #include "snd_ps3_reg.h" #include "snd_ps3.h" MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("PS3 sound driver"); MODULE_AUTHOR("Sony Computer Entertainment Inc."); /* module entries */ static int __init snd_ps3_init(void); static void __exit snd_ps3_exit(void); /* ALSA snd driver ops */ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream); static int snd_ps3_pcm_close(struct snd_pcm_substream *substream); static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream); static int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, int cmd); static snd_pcm_uframes_t snd_ps3_pcm_pointer(struct snd_pcm_substream *substream); static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params); static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream); /* ps3_system_bus_driver entries */ static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev); static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev); /* address setup */ static int snd_ps3_map_mmio(void); static void snd_ps3_unmap_mmio(void); static int snd_ps3_allocate_irq(void); static void snd_ps3_free_irq(void); static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start); /* interrupt handler */ static irqreturn_t snd_ps3_interrupt(int irq, void *dev_id); /* set sampling rate/format */ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream); /* take effect parameter change */ static int snd_ps3_change_avsetting(struct snd_ps3_card_info *card); /* initialize avsetting and take it effect */ static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card); /* setup dma */ static int snd_ps3_program_dma(struct snd_ps3_card_info *card, enum snd_ps3_dma_filltype filltype); static void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card); static dma_addr_t v_to_bus(struct snd_ps3_card_info *, void *vaddr, int ch); #include "snd_ps3_reg.h" module_init(snd_ps3_init); module_exit(snd_ps3_exit); /* * global */ Loading Loading @@ -165,17 +115,6 @@ static const struct snd_pcm_hardware snd_ps3_pcm_hw = { .fifo_size = PS3_AUDIO_FIFO_SIZE }; static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = { .open = snd_ps3_pcm_open, .close = snd_ps3_pcm_close, .prepare = snd_ps3_pcm_prepare, .ioctl = snd_pcm_lib_ioctl, .trigger = snd_ps3_pcm_trigger, .pointer = snd_ps3_pcm_pointer, .hw_params = snd_ps3_pcm_hw_params, .hw_free = snd_ps3_pcm_hw_free }; static int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, int count, int force_stop) { Loading Loading @@ -368,6 +307,71 @@ static int snd_ps3_program_dma(struct snd_ps3_card_info *card, 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 occured, * 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; }; /* * audio mute on/off * mute_on : 0 output enabled Loading @@ -378,6 +382,142 @@ static int snd_ps3_mute(int mute_on) return ps3av_audio_mute(mute_on); } /* * 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; } /* * set sampling rate according to the substream */ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); struct snd_ps3_avsetting_info avs; int ret; avs = card->avs; pr_debug("%s: called freq=%d width=%d\n", __func__, substream->runtime->rate, snd_pcm_format_width(substream->runtime->format)); pr_debug("%s: before freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); /* sample rate */ switch (substream->runtime->rate) { case 44100: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; break; case 48000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; break; case 88200: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; break; case 96000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; break; default: pr_info("%s: invalid rate %d\n", __func__, substream->runtime->rate); return 1; } /* width */ switch (snd_pcm_format_width(substream->runtime->format)) { case 16: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; break; case 24: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; break; default: pr_info("%s: invalid width %d\n", __func__, snd_pcm_format_width(substream->runtime->format)); return 1; } memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8); if (memcmp(&card->avs, &avs, sizeof(avs))) { pr_debug("%s: after freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); card->avs = avs; snd_ps3_change_avsetting(card); ret = 0; } else ret = 1; /* check CS non-audio bit and mute accordingly */ if (avs.avs_cs_info[0] & 0x02) ps3av_audio_mute_analog(1); /* mute if non-audio */ else ps3av_audio_mute_analog(0); return ret; } /* * PCM operators */ Loading @@ -403,6 +543,13 @@ static int snd_ps3_pcm_open(struct snd_pcm_substream *substream) return 0; }; static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) { /* mute on */ snd_ps3_mute(1); return 0; }; static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { Loading @@ -414,6 +561,13 @@ static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream, return 0; }; static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) { int ret; ret = snd_pcm_lib_free_pages(substream); return ret; }; static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, unsigned int delay_ms) { Loading Loading @@ -464,290 +618,94 @@ static int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) 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_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; }; /* * report current pointer */ static snd_pcm_uframes_t snd_ps3_pcm_pointer( struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); size_t bytes; snd_pcm_uframes_t ret; spin_lock(&card->dma_lock); { bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - card->dma_start_vaddr[SND_PS3_CH_L]); } spin_unlock(&card->dma_lock); ret = bytes_to_frames(substream->runtime, bytes * 2); return ret; }; static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream) { int ret; ret = snd_pcm_lib_free_pages(substream); return ret; }; static int snd_ps3_pcm_close(struct snd_pcm_substream *substream) { /* mute on */ snd_ps3_mute(1); return 0; }; static void 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); } /* * 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_init_avsetting(struct snd_ps3_card_info *card) { int ret; pr_debug("%s: start\n", __func__); card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8); runtime->dma_area; card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; ret = snd_ps3_change_avsetting(card); 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); snd_ps3_audio_fixup(card); 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]); /* to start to generate SPDIF signal, fill data */ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); pr_debug("%s: end\n", __func__); return ret; } spin_unlock_irqrestore(&card->dma_lock, irqsave); /* * set sampling rate according to the substream */ static int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); struct snd_ps3_avsetting_info avs; int ret; /* ensure the hardware sees the change */ mb(); avs = card->avs; return 0; }; pr_debug("%s: called freq=%d width=%d\n", __func__, substream->runtime->rate, snd_pcm_format_width(substream->runtime->format)); 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; pr_debug("%s: before freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); switch (cmd) { case SNDRV_PCM_TRIGGER_START: /* clear outstanding interrupts */ update_reg(PS3_AUDIO_AX_IS, 0); /* sample rate */ switch (substream->runtime->rate) { case 44100: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; break; case 48000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; break; case 88200: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; break; case 96000: avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; break; default: pr_info("%s: invalid rate %d\n", __func__, substream->runtime->rate); return 1; spin_lock(&card->dma_lock); { card->running = 1; } spin_unlock(&card->dma_lock); /* width */ switch (snd_pcm_format_width(substream->runtime->format)) { case 16: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; 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 24: avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; 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: pr_info("%s: invalid width %d\n", __func__, snd_pcm_format_width(substream->runtime->format)); return 1; break; } memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8); return ret; }; if (memcmp(&card->avs, &avs, sizeof(avs))) { pr_debug("%s: after freq=%d width=%d\n", __func__, card->avs.avs_audio_rate, card->avs.avs_audio_width); /* * report current pointer */ static snd_pcm_uframes_t snd_ps3_pcm_pointer( struct snd_pcm_substream *substream) { struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); size_t bytes; snd_pcm_uframes_t ret; card->avs = avs; snd_ps3_change_avsetting(card); ret = 0; } else ret = 1; spin_lock(&card->dma_lock); { bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - card->dma_start_vaddr[SND_PS3_CH_L]); } spin_unlock(&card->dma_lock); /* check CS non-audio bit and mute accordingly */ if (avs.avs_cs_info[0] & 0x02) ps3av_audio_mute_analog(1); /* mute if non-audio */ else ps3av_audio_mute_analog(0); ret = bytes_to_frames(substream->runtime, bytes * 2); return ret; } }; /* * SPDIF status bits controls Loading Loading @@ -815,6 +773,17 @@ static struct snd_kcontrol_new spdif_ctls[] = { }, }; static struct snd_pcm_ops snd_ps3_pcm_spdif_ops = { .open = snd_ps3_pcm_open, .close = snd_ps3_pcm_close, .ioctl = snd_pcm_lib_ioctl, .hw_params = snd_ps3_pcm_hw_params, .hw_free = snd_ps3_pcm_hw_free, .prepare = snd_ps3_pcm_prepare, .trigger = snd_ps3_pcm_trigger, .pointer = snd_ps3_pcm_pointer, }; static int snd_ps3_map_mmio(void) { Loading Loading @@ -912,6 +881,52 @@ static void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) ret); } static void 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); } static int snd_ps3_init_avsetting(struct snd_ps3_card_info *card) { int ret; pr_debug("%s: start\n", __func__); card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8); ret = snd_ps3_change_avsetting(card); snd_ps3_audio_fixup(card); /* to start to generate SPDIF signal, fill data */ snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); snd_ps3_kick_dma(card); pr_debug("%s: end\n", __func__); return ret; } static int __init snd_ps3_driver_probe(struct ps3_system_bus_device *dev) { int i, ret; Loading Loading @@ -1112,71 +1127,6 @@ static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { }; /* * 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 occured, * 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; }; /* * module/subsystem initialize/terminate */ Loading @@ -1195,10 +1145,15 @@ static int __init snd_ps3_init(void) return ret; } module_init(snd_ps3_init); static void __exit snd_ps3_exit(void) { ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info); } module_exit(snd_ps3_exit); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("PS3 sound driver"); MODULE_AUTHOR("Sony Computer Entertainment Inc."); MODULE_ALIAS(PS3_MODULE_ALIAS_SOUND);