diff options
Diffstat (limited to 'ANDROID_3.4.5/sound/ppc/snd_ps3.c')
-rw-r--r-- | ANDROID_3.4.5/sound/ppc/snd_ps3.c | 1160 |
1 files changed, 0 insertions, 1160 deletions
diff --git a/ANDROID_3.4.5/sound/ppc/snd_ps3.c b/ANDROID_3.4.5/sound/ppc/snd_ps3.c deleted file mode 100644 index 1aa52eff..00000000 --- a/ANDROID_3.4.5/sound/ppc/snd_ps3.c +++ /dev/null @@ -1,1160 +0,0 @@ -/* - * Audio support for PS3 - * Copyright (C) 2007 Sony Computer Entertainment Inc. - * All rights reserved. - * Copyright 2006, 2007 Sony Corporation - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 of the Licence. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/dma-mapping.h> -#include <linux/dmapool.h> -#include <linux/gfp.h> -#include <linux/init.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> - -#include <sound/asound.h> -#include <sound/control.h> -#include <sound/core.h> -#include <sound/initval.h> -#include <sound/memalloc.h> -#include <sound/pcm.h> -#include <sound/pcm_params.h> - -#include <asm/dma.h> -#include <asm/firmware.h> -#include <asm/lv1call.h> -#include <asm/ps3.h> -#include <asm/ps3av.h> - -#include "snd_ps3.h" -#include "snd_ps3_reg.h" - - -/* - * global - */ -static struct snd_ps3_card_info the_card; - -static int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY; - -module_param_named(start_delay, snd_ps3_start_delay, uint, 0644); -MODULE_PARM_DESC(start_delay, "time to insert silent data in ms"); - -static int index = SNDRV_DEFAULT_IDX1; -static char *id = SNDRV_DEFAULT_STR1; - -module_param(index, int, 0444); -MODULE_PARM_DESC(index, "Index value for PS3 soundchip."); -module_param(id, charp, 0444); -MODULE_PARM_DESC(id, "ID string for PS3 soundchip."); - - -/* - * PS3 audio register access - */ -static inline u32 read_reg(unsigned int reg) -{ - return in_be32(the_card.mapped_mmio_vaddr + reg); -} -static inline void write_reg(unsigned int reg, u32 val) -{ - out_be32(the_card.mapped_mmio_vaddr + reg, val); -} -static inline void update_reg(unsigned int reg, u32 or_val) -{ - u32 newval = read_reg(reg) | or_val; - write_reg(reg, newval); -} -static inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val) -{ - u32 newval = (read_reg(reg) & mask) | or_val; - write_reg(reg, newval); -} - -/* - * ALSA defs - */ -static const struct snd_pcm_hardware snd_ps3_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_NONINTERLEAVED | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = (SNDRV_PCM_FMTBIT_S16_BE | - SNDRV_PCM_FMTBIT_S24_BE), - .rates = (SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | - SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000), - .rate_min = 44100, - .rate_max = 96000, - - .channels_min = 2, /* stereo only */ - .channels_max = 2, - - .buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64, - - /* interrupt by four stages */ - .period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4, - .period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4, - - .periods_min = 16, - .periods_max = 32, /* buffer_size_max/ period_bytes_max */ - - .fifo_size = PS3_AUDIO_FIFO_SIZE -}; - -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; -} - -/* - * 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(); -} - -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(); -} - -/* - * convert virtual addr to ioif bus addr. - */ -static dma_addr_t v_to_bus(struct snd_ps3_card_info *card, void *paddr, int ch) -{ - return card->dma_start_bus_addr[ch] + - (paddr - card->dma_start_vaddr[ch]); -}; - - -/* - * increment ring buffer pointer. - * NOTE: caller must hold write spinlock - */ -static void snd_ps3_bump_buffer(struct snd_ps3_card_info *card, - enum snd_ps3_ch ch, size_t byte_count, - int stage) -{ - if (!stage) - card->dma_last_transfer_vaddr[ch] = - card->dma_next_transfer_vaddr[ch]; - card->dma_next_transfer_vaddr[ch] += byte_count; - if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <= - card->dma_next_transfer_vaddr[ch]) { - card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch]; - } -} -/* - * setup dmac to send data to audio and attenuate samples on the ring buffer - */ -static int snd_ps3_program_dma(struct snd_ps3_card_info *card, - enum snd_ps3_dma_filltype filltype) -{ - /* this dmac does not support over 4G */ - uint32_t dma_addr; - int fill_stages, dma_ch, stage; - enum snd_ps3_ch ch; - uint32_t ch0_kick_event = 0; /* initialize to mute gcc */ - void *start_vaddr; - unsigned long irqsave; - int silent = 0; - - switch (filltype) { - case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL: - silent = 1; - /* intentionally fall thru */ - case SND_PS3_DMA_FILLTYPE_FIRSTFILL: - ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS; - break; - - case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING: - silent = 1; - /* intentionally fall thru */ - case SND_PS3_DMA_FILLTYPE_RUNNING: - ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY; - break; - } - - snd_ps3_verify_dma_stop(card, 700, 0); - fill_stages = 4; - spin_lock_irqsave(&card->dma_lock, irqsave); - for (ch = 0; ch < 2; ch++) { - start_vaddr = card->dma_next_transfer_vaddr[0]; - for (stage = 0; stage < fill_stages; stage++) { - dma_ch = stage * 2 + ch; - if (silent) - dma_addr = card->null_buffer_start_dma_addr; - else - dma_addr = - v_to_bus(card, - card->dma_next_transfer_vaddr[ch], - ch); - - write_reg(PS3_AUDIO_SOURCE(dma_ch), - (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY | - dma_addr)); - - /* dst: fixed to 3wire#0 */ - if (ch == 0) - write_reg(PS3_AUDIO_DEST(dma_ch), - (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | - PS3_AUDIO_AO_3W_LDATA(0))); - else - write_reg(PS3_AUDIO_DEST(dma_ch), - (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | - PS3_AUDIO_AO_3W_RDATA(0))); - - /* count always 1 DMA block (1/2 stage = 128 bytes) */ - write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0); - /* bump pointer if needed */ - if (!silent) - snd_ps3_bump_buffer(card, ch, - PS3_AUDIO_DMAC_BLOCK_SIZE, - stage); - - /* kick event */ - if (dma_ch == 0) - write_reg(PS3_AUDIO_KICK(dma_ch), - ch0_kick_event); - else - write_reg(PS3_AUDIO_KICK(dma_ch), - PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch - - 1) | - PS3_AUDIO_KICK_REQUEST); - } - } - /* ensure the hardware sees the change */ - wmb(); - spin_unlock_irqrestore(&card->dma_lock, irqsave); - - 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; -}; - -/* - * audio mute on/off - * mute_on : 0 output enabled - * 1 mute - */ -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 - */ -static int snd_ps3_pcm_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); - int pcm_index; - - pcm_index = substream->pcm->device; - /* to retrieve substream/runtime in interrupt handler */ - card->substream = substream; - - runtime->hw = snd_ps3_pcm_hw; - - card->start_delay = snd_ps3_start_delay; - - /* mute off */ - snd_ps3_mute(0); /* this function sleep */ - - snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, - PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2); - 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) -{ - size_t size; - - /* alloc transport buffer */ - size = params_buffer_bytes(hw_params); - snd_pcm_lib_malloc_pages(substream, size); - 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) -{ - int ret; - int rate ; - - rate = substream->runtime->rate; - ret = snd_pcm_format_size(substream->runtime->format, - rate * delay_ms / 1000) - * substream->runtime->channels; - - pr_debug("%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", - __func__, - delay_ms, - rate, - snd_pcm_format_size(substream->runtime->format, rate), - rate * delay_ms / 1000, - ret); - - return ret; -}; - -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_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; -}; - -/* - * SPDIF status bits controls - */ -static int snd_ps3_spdif_mask_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; - uinfo->count = 1; - return 0; -} - -/* FIXME: ps3av_set_audio_mode() assumes only consumer mode */ -static int snd_ps3_spdif_cmask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - memset(ucontrol->value.iec958.status, 0xff, 8); - return 0; -} - -static int snd_ps3_spdif_pmask_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - return 0; -} - -static int snd_ps3_spdif_default_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - memcpy(ucontrol->value.iec958.status, ps3av_mode_cs_info, 8); - return 0; -} - -static int snd_ps3_spdif_default_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - if (memcmp(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8)) { - memcpy(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8); - return 1; - } - return 0; -} - -static struct snd_kcontrol_new spdif_ctls[] = { - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), - .info = snd_ps3_spdif_mask_info, - .get = snd_ps3_spdif_cmask_get, - }, - { - .access = SNDRV_CTL_ELEM_ACCESS_READ, - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), - .info = snd_ps3_spdif_mask_info, - .get = snd_ps3_spdif_pmask_get, - }, - { - .iface = SNDRV_CTL_ELEM_IFACE_PCM, - .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), - .info = snd_ps3_spdif_mask_info, - .get = snd_ps3_spdif_default_get, - .put = snd_ps3_spdif_default_put, - }, -}; - -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 __devinit snd_ps3_map_mmio(void) -{ - the_card.mapped_mmio_vaddr = - ioremap(the_card.ps3_dev->m_region->bus_addr, - the_card.ps3_dev->m_region->len); - - if (!the_card.mapped_mmio_vaddr) { - pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n", - __func__, the_card.ps3_dev->m_region->lpar_addr, - the_card.ps3_dev->m_region->len); - return -ENXIO; - } - - return 0; -}; - -static void snd_ps3_unmap_mmio(void) -{ - iounmap(the_card.mapped_mmio_vaddr); - the_card.mapped_mmio_vaddr = NULL; -} - -static int __devinit snd_ps3_allocate_irq(void) -{ - int ret; - u64 lpar_addr, lpar_size; - u64 __iomem *mapped; - - /* FIXME: move this to device_init (H/W probe) */ - - /* get irq outlet */ - ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size); - if (ret) { - pr_info("%s: device map 1 failed %d\n", __func__, - ret); - return -ENXIO; - } - - mapped = ioremap(lpar_addr, lpar_size); - if (!mapped) { - pr_info("%s: ioremap 1 failed \n", __func__); - return -ENXIO; - } - - the_card.audio_irq_outlet = in_be64(mapped); - - iounmap(mapped); - ret = lv1_gpu_device_unmap(1); - if (ret) - pr_info("%s: unmap 1 failed\n", __func__); - - /* irq */ - ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, - the_card.audio_irq_outlet, - &the_card.irq_no); - if (ret) { - pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret); - return ret; - } - - ret = request_irq(the_card.irq_no, snd_ps3_interrupt, 0, - SND_PS3_DRIVER_NAME, &the_card); - if (ret) { - pr_info("%s: request_irq failed (%d)\n", __func__, ret); - goto cleanup_irq; - } - - return 0; - - cleanup_irq: - ps3_irq_plug_destroy(the_card.irq_no); - return ret; -}; - -static void snd_ps3_free_irq(void) -{ - free_irq(the_card.irq_no, &the_card); - ps3_irq_plug_destroy(the_card.irq_no); -} - -static void __devinit snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) -{ - uint64_t val; - int ret; - - val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) | - (0x03UL << 24) | - (0x0fUL << 12) | - (PS3_AUDIO_IOID); - - ret = lv1_gpu_attribute(0x100, 0x007, val); - if (ret) - pr_info("%s: gpu_attribute failed %d\n", __func__, - ret); -} - -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); -} - -static int __devinit 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 __devinit snd_ps3_driver_probe(struct ps3_system_bus_device *dev) -{ - int i, ret; - u64 lpar_addr, lpar_size; - - BUG_ON(!firmware_has_feature(FW_FEATURE_PS3_LV1)); - BUG_ON(dev->match_id != PS3_MATCH_ID_SOUND); - - the_card.ps3_dev = dev; - - ret = ps3_open_hv_device(dev); - - if (ret) - return -ENXIO; - - /* setup MMIO */ - ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); - if (ret) { - pr_info("%s: device map 2 failed %d\n", __func__, ret); - goto clean_open; - } - ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, - PAGE_SHIFT); - - ret = snd_ps3_map_mmio(); - if (ret) - goto clean_dev_map; - - /* setup DMA area */ - ps3_dma_region_init(dev, dev->d_region, - PAGE_SHIFT, /* use system page size */ - 0, /* dma type; not used */ - NULL, - _ALIGN_UP(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); - dev->d_region->ioid = PS3_AUDIO_IOID; - - ret = ps3_dma_region_create(dev->d_region); - if (ret) { - pr_info("%s: region_create\n", __func__); - goto clean_mmio; - } - - snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); - - /* CONFIG_SND_PS3_DEFAULT_START_DELAY */ - the_card.start_delay = snd_ps3_start_delay; - - /* irq */ - if (snd_ps3_allocate_irq()) { - ret = -ENXIO; - goto clean_dma_region; - } - - /* create card instance */ - ret = snd_card_create(index, id, THIS_MODULE, 0, &the_card.card); - if (ret < 0) - goto clean_irq; - - strcpy(the_card.card->driver, "PS3"); - strcpy(the_card.card->shortname, "PS3"); - strcpy(the_card.card->longname, "PS3 sound"); - - /* create control elements */ - for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) { - ret = snd_ctl_add(the_card.card, - snd_ctl_new1(&spdif_ctls[i], &the_card)); - if (ret < 0) - goto clean_card; - } - - /* create PCM devices instance */ - /* NOTE:this driver works assuming pcm:substream = 1:1 */ - ret = snd_pcm_new(the_card.card, - "SPDIF", - 0, /* instance index, will be stored pcm.device*/ - 1, /* output substream */ - 0, /* input substream */ - &(the_card.pcm)); - if (ret) - goto clean_card; - - the_card.pcm->private_data = &the_card; - strcpy(the_card.pcm->name, "SPDIF"); - - /* set pcm ops */ - snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, - &snd_ps3_pcm_spdif_ops); - - the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; - /* pre-alloc PCM DMA buffer*/ - ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, - SNDRV_DMA_TYPE_DEV, - &dev->core, - SND_PS3_PCM_PREALLOC_SIZE, - SND_PS3_PCM_PREALLOC_SIZE); - if (ret < 0) { - pr_info("%s: prealloc failed\n", __func__); - goto clean_card; - } - - /* - * allocate null buffer - * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2 - * PAGE_SIZE is enogh - */ - the_card.null_buffer_start_vaddr = - dma_alloc_coherent(&the_card.ps3_dev->core, - PAGE_SIZE, - &the_card.null_buffer_start_dma_addr, - GFP_KERNEL); - if (!the_card.null_buffer_start_vaddr) { - pr_info("%s: nullbuffer alloc failed\n", __func__); - goto clean_preallocate; - } - pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__, - the_card.null_buffer_start_vaddr, - the_card.null_buffer_start_dma_addr); - /* set default sample rate/word width */ - snd_ps3_init_avsetting(&the_card); - - /* register the card */ - snd_card_set_dev(the_card.card, &dev->core); - ret = snd_card_register(the_card.card); - if (ret < 0) - goto clean_dma_map; - - pr_info("%s started. start_delay=%dms\n", - the_card.card->longname, the_card.start_delay); - return 0; - -clean_dma_map: - dma_free_coherent(&the_card.ps3_dev->core, - PAGE_SIZE, - the_card.null_buffer_start_vaddr, - the_card.null_buffer_start_dma_addr); -clean_preallocate: - snd_pcm_lib_preallocate_free_for_all(the_card.pcm); -clean_card: - snd_card_free(the_card.card); -clean_irq: - snd_ps3_free_irq(); -clean_dma_region: - ps3_dma_region_free(dev->d_region); -clean_mmio: - snd_ps3_unmap_mmio(); -clean_dev_map: - lv1_gpu_device_unmap(2); -clean_open: - ps3_close_hv_device(dev); - /* - * there is no destructor function to pcm. - * midlayer automatically releases if the card removed - */ - return ret; -}; /* snd_ps3_probe */ - -/* called when module removal */ -static int snd_ps3_driver_remove(struct ps3_system_bus_device *dev) -{ - int ret; - pr_info("%s:start id=%d\n", __func__, dev->match_id); - if (dev->match_id != PS3_MATCH_ID_SOUND) - return -ENXIO; - - /* - * ctl and preallocate buffer will be freed in - * snd_card_free - */ - ret = snd_card_free(the_card.card); - if (ret) - pr_info("%s: ctl freecard=%d\n", __func__, ret); - - dma_free_coherent(&dev->core, - PAGE_SIZE, - the_card.null_buffer_start_vaddr, - the_card.null_buffer_start_dma_addr); - - ps3_dma_region_free(dev->d_region); - - snd_ps3_free_irq(); - snd_ps3_unmap_mmio(); - - lv1_gpu_device_unmap(2); - ps3_close_hv_device(dev); - pr_info("%s:end id=%d\n", __func__, dev->match_id); - return 0; -} /* snd_ps3_remove */ - -static struct ps3_system_bus_driver snd_ps3_bus_driver_info = { - .match_id = PS3_MATCH_ID_SOUND, - .probe = snd_ps3_driver_probe, - .remove = snd_ps3_driver_remove, - .shutdown = snd_ps3_driver_remove, - .core = { - .name = SND_PS3_DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - - -/* - * module/subsystem initialize/terminate - */ -static int __init snd_ps3_init(void) -{ - int ret; - - if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) - return -ENXIO; - - memset(&the_card, 0, sizeof(the_card)); - spin_lock_init(&the_card.dma_lock); - - /* register systembus DRIVER, this calls our probe() func */ - ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info); - - 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); |