162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Audio support for PS3 462306a36Sopenharmony_ci * Copyright (C) 2007 Sony Computer Entertainment Inc. 562306a36Sopenharmony_ci * All rights reserved. 662306a36Sopenharmony_ci * Copyright 2006, 2007 Sony Corporation 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1062306a36Sopenharmony_ci#include <linux/dmapool.h> 1162306a36Sopenharmony_ci#include <linux/gfp.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <sound/asound.h> 1862306a36Sopenharmony_ci#include <sound/control.h> 1962306a36Sopenharmony_ci#include <sound/core.h> 2062306a36Sopenharmony_ci#include <sound/initval.h> 2162306a36Sopenharmony_ci#include <sound/memalloc.h> 2262306a36Sopenharmony_ci#include <sound/pcm.h> 2362306a36Sopenharmony_ci#include <sound/pcm_params.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include <asm/dma.h> 2662306a36Sopenharmony_ci#include <asm/firmware.h> 2762306a36Sopenharmony_ci#include <asm/lv1call.h> 2862306a36Sopenharmony_ci#include <asm/ps3.h> 2962306a36Sopenharmony_ci#include <asm/ps3av.h> 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#include "snd_ps3.h" 3262306a36Sopenharmony_ci#include "snd_ps3_reg.h" 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* 3662306a36Sopenharmony_ci * global 3762306a36Sopenharmony_ci */ 3862306a36Sopenharmony_cistatic struct snd_ps3_card_info the_card; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int snd_ps3_start_delay = CONFIG_SND_PS3_DEFAULT_START_DELAY; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cimodule_param_named(start_delay, snd_ps3_start_delay, uint, 0644); 4362306a36Sopenharmony_ciMODULE_PARM_DESC(start_delay, "time to insert silent data in ms"); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int index = SNDRV_DEFAULT_IDX1; 4662306a36Sopenharmony_cistatic char *id = SNDRV_DEFAULT_STR1; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cimodule_param(index, int, 0444); 4962306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for PS3 soundchip."); 5062306a36Sopenharmony_cimodule_param(id, charp, 0444); 5162306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for PS3 soundchip."); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* 5562306a36Sopenharmony_ci * PS3 audio register access 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic inline u32 read_reg(unsigned int reg) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci return in_be32(the_card.mapped_mmio_vaddr + reg); 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_cistatic inline void write_reg(unsigned int reg, u32 val) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci out_be32(the_card.mapped_mmio_vaddr + reg, val); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_cistatic inline void update_reg(unsigned int reg, u32 or_val) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci u32 newval = read_reg(reg) | or_val; 6862306a36Sopenharmony_ci write_reg(reg, newval); 6962306a36Sopenharmony_ci} 7062306a36Sopenharmony_cistatic inline void update_mask_reg(unsigned int reg, u32 mask, u32 or_val) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci u32 newval = (read_reg(reg) & mask) | or_val; 7362306a36Sopenharmony_ci write_reg(reg, newval); 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * ALSA defs 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic const struct snd_pcm_hardware snd_ps3_pcm_hw = { 8062306a36Sopenharmony_ci .info = (SNDRV_PCM_INFO_MMAP | 8162306a36Sopenharmony_ci SNDRV_PCM_INFO_NONINTERLEAVED | 8262306a36Sopenharmony_ci SNDRV_PCM_INFO_MMAP_VALID), 8362306a36Sopenharmony_ci .formats = (SNDRV_PCM_FMTBIT_S16_BE | 8462306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_BE), 8562306a36Sopenharmony_ci .rates = (SNDRV_PCM_RATE_44100 | 8662306a36Sopenharmony_ci SNDRV_PCM_RATE_48000 | 8762306a36Sopenharmony_ci SNDRV_PCM_RATE_88200 | 8862306a36Sopenharmony_ci SNDRV_PCM_RATE_96000), 8962306a36Sopenharmony_ci .rate_min = 44100, 9062306a36Sopenharmony_ci .rate_max = 96000, 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci .channels_min = 2, /* stereo only */ 9362306a36Sopenharmony_ci .channels_max = 2, 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci .buffer_bytes_max = PS3_AUDIO_FIFO_SIZE * 64, 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* interrupt by four stages */ 9862306a36Sopenharmony_ci .period_bytes_min = PS3_AUDIO_FIFO_STAGE_SIZE * 4, 9962306a36Sopenharmony_ci .period_bytes_max = PS3_AUDIO_FIFO_STAGE_SIZE * 4, 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci .periods_min = 16, 10262306a36Sopenharmony_ci .periods_max = 32, /* buffer_size_max/ period_bytes_max */ 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci .fifo_size = PS3_AUDIO_FIFO_SIZE 10562306a36Sopenharmony_ci}; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, 10862306a36Sopenharmony_ci int count, int force_stop) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci int dma_ch, done, retries, stop_forced = 0; 11162306a36Sopenharmony_ci uint32_t status; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci for (dma_ch = 0; dma_ch < 8; dma_ch++) { 11462306a36Sopenharmony_ci retries = count; 11562306a36Sopenharmony_ci do { 11662306a36Sopenharmony_ci status = read_reg(PS3_AUDIO_KICK(dma_ch)) & 11762306a36Sopenharmony_ci PS3_AUDIO_KICK_STATUS_MASK; 11862306a36Sopenharmony_ci switch (status) { 11962306a36Sopenharmony_ci case PS3_AUDIO_KICK_STATUS_DONE: 12062306a36Sopenharmony_ci case PS3_AUDIO_KICK_STATUS_NOTIFY: 12162306a36Sopenharmony_ci case PS3_AUDIO_KICK_STATUS_CLEAR: 12262306a36Sopenharmony_ci case PS3_AUDIO_KICK_STATUS_ERROR: 12362306a36Sopenharmony_ci done = 1; 12462306a36Sopenharmony_ci break; 12562306a36Sopenharmony_ci default: 12662306a36Sopenharmony_ci done = 0; 12762306a36Sopenharmony_ci udelay(10); 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } while (!done && --retries); 13062306a36Sopenharmony_ci if (!retries && force_stop) { 13162306a36Sopenharmony_ci pr_info("%s: DMA ch %d is not stopped.", 13262306a36Sopenharmony_ci __func__, dma_ch); 13362306a36Sopenharmony_ci /* last resort. force to stop dma. 13462306a36Sopenharmony_ci * NOTE: this cause DMA done interrupts 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR); 13762306a36Sopenharmony_ci stop_forced = 1; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci return stop_forced; 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* 14462306a36Sopenharmony_ci * wait for all dma is done. 14562306a36Sopenharmony_ci * NOTE: caller should reset card->running before call. 14662306a36Sopenharmony_ci * If not, the interrupt handler will re-start DMA, 14762306a36Sopenharmony_ci * then DMA is never stopped. 14862306a36Sopenharmony_ci */ 14962306a36Sopenharmony_cistatic void snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci int stop_forced; 15262306a36Sopenharmony_ci /* 15362306a36Sopenharmony_ci * wait for the last dma is done 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* 15762306a36Sopenharmony_ci * expected maximum DMA done time is 5.7ms + something (DMA itself). 15862306a36Sopenharmony_ci * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next 15962306a36Sopenharmony_ci * DMA kick event would occur. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci stop_forced = snd_ps3_verify_dma_stop(card, 700, 1); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci /* 16462306a36Sopenharmony_ci * clear outstanding interrupts. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci update_reg(PS3_AUDIO_INTR_0, 0); 16762306a36Sopenharmony_ci update_reg(PS3_AUDIO_AX_IS, 0); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci *revert CLEAR bit since it will not reset automatically after DMA stop 17162306a36Sopenharmony_ci */ 17262306a36Sopenharmony_ci if (stop_forced) 17362306a36Sopenharmony_ci update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); 17462306a36Sopenharmony_ci /* ensure the hardware sees changes */ 17562306a36Sopenharmony_ci wmb(); 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic void snd_ps3_kick_dma(struct snd_ps3_card_info *card) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci update_reg(PS3_AUDIO_KICK(0), PS3_AUDIO_KICK_REQUEST); 18262306a36Sopenharmony_ci /* ensure the hardware sees the change */ 18362306a36Sopenharmony_ci wmb(); 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci/* 18762306a36Sopenharmony_ci * convert virtual addr to ioif bus addr. 18862306a36Sopenharmony_ci */ 18962306a36Sopenharmony_cistatic dma_addr_t v_to_bus(struct snd_ps3_card_info *card, void *paddr, int ch) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci return card->dma_start_bus_addr[ch] + 19262306a36Sopenharmony_ci (paddr - card->dma_start_vaddr[ch]); 19362306a36Sopenharmony_ci}; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * increment ring buffer pointer. 19862306a36Sopenharmony_ci * NOTE: caller must hold write spinlock 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic void snd_ps3_bump_buffer(struct snd_ps3_card_info *card, 20162306a36Sopenharmony_ci enum snd_ps3_ch ch, size_t byte_count, 20262306a36Sopenharmony_ci int stage) 20362306a36Sopenharmony_ci{ 20462306a36Sopenharmony_ci if (!stage) 20562306a36Sopenharmony_ci card->dma_last_transfer_vaddr[ch] = 20662306a36Sopenharmony_ci card->dma_next_transfer_vaddr[ch]; 20762306a36Sopenharmony_ci card->dma_next_transfer_vaddr[ch] += byte_count; 20862306a36Sopenharmony_ci if ((card->dma_start_vaddr[ch] + (card->dma_buffer_size / 2)) <= 20962306a36Sopenharmony_ci card->dma_next_transfer_vaddr[ch]) { 21062306a36Sopenharmony_ci card->dma_next_transfer_vaddr[ch] = card->dma_start_vaddr[ch]; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci/* 21462306a36Sopenharmony_ci * setup dmac to send data to audio and attenuate samples on the ring buffer 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int snd_ps3_program_dma(struct snd_ps3_card_info *card, 21762306a36Sopenharmony_ci enum snd_ps3_dma_filltype filltype) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci /* this dmac does not support over 4G */ 22062306a36Sopenharmony_ci uint32_t dma_addr; 22162306a36Sopenharmony_ci int fill_stages, dma_ch, stage; 22262306a36Sopenharmony_ci enum snd_ps3_ch ch; 22362306a36Sopenharmony_ci uint32_t ch0_kick_event = 0; /* initialize to mute gcc */ 22462306a36Sopenharmony_ci unsigned long irqsave; 22562306a36Sopenharmony_ci int silent = 0; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci switch (filltype) { 22862306a36Sopenharmony_ci case SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL: 22962306a36Sopenharmony_ci silent = 1; 23062306a36Sopenharmony_ci fallthrough; 23162306a36Sopenharmony_ci case SND_PS3_DMA_FILLTYPE_FIRSTFILL: 23262306a36Sopenharmony_ci ch0_kick_event = PS3_AUDIO_KICK_EVENT_ALWAYS; 23362306a36Sopenharmony_ci break; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci case SND_PS3_DMA_FILLTYPE_SILENT_RUNNING: 23662306a36Sopenharmony_ci silent = 1; 23762306a36Sopenharmony_ci fallthrough; 23862306a36Sopenharmony_ci case SND_PS3_DMA_FILLTYPE_RUNNING: 23962306a36Sopenharmony_ci ch0_kick_event = PS3_AUDIO_KICK_EVENT_SERIALOUT0_EMPTY; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci snd_ps3_verify_dma_stop(card, 700, 0); 24462306a36Sopenharmony_ci fill_stages = 4; 24562306a36Sopenharmony_ci spin_lock_irqsave(&card->dma_lock, irqsave); 24662306a36Sopenharmony_ci for (ch = 0; ch < 2; ch++) { 24762306a36Sopenharmony_ci for (stage = 0; stage < fill_stages; stage++) { 24862306a36Sopenharmony_ci dma_ch = stage * 2 + ch; 24962306a36Sopenharmony_ci if (silent) 25062306a36Sopenharmony_ci dma_addr = card->null_buffer_start_dma_addr; 25162306a36Sopenharmony_ci else 25262306a36Sopenharmony_ci dma_addr = 25362306a36Sopenharmony_ci v_to_bus(card, 25462306a36Sopenharmony_ci card->dma_next_transfer_vaddr[ch], 25562306a36Sopenharmony_ci ch); 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci write_reg(PS3_AUDIO_SOURCE(dma_ch), 25862306a36Sopenharmony_ci (PS3_AUDIO_SOURCE_TARGET_SYSTEM_MEMORY | 25962306a36Sopenharmony_ci dma_addr)); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* dst: fixed to 3wire#0 */ 26262306a36Sopenharmony_ci if (ch == 0) 26362306a36Sopenharmony_ci write_reg(PS3_AUDIO_DEST(dma_ch), 26462306a36Sopenharmony_ci (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | 26562306a36Sopenharmony_ci PS3_AUDIO_AO_3W_LDATA(0))); 26662306a36Sopenharmony_ci else 26762306a36Sopenharmony_ci write_reg(PS3_AUDIO_DEST(dma_ch), 26862306a36Sopenharmony_ci (PS3_AUDIO_DEST_TARGET_AUDIOFIFO | 26962306a36Sopenharmony_ci PS3_AUDIO_AO_3W_RDATA(0))); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* count always 1 DMA block (1/2 stage = 128 bytes) */ 27262306a36Sopenharmony_ci write_reg(PS3_AUDIO_DMASIZE(dma_ch), 0); 27362306a36Sopenharmony_ci /* bump pointer if needed */ 27462306a36Sopenharmony_ci if (!silent) 27562306a36Sopenharmony_ci snd_ps3_bump_buffer(card, ch, 27662306a36Sopenharmony_ci PS3_AUDIO_DMAC_BLOCK_SIZE, 27762306a36Sopenharmony_ci stage); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci /* kick event */ 28062306a36Sopenharmony_ci if (dma_ch == 0) 28162306a36Sopenharmony_ci write_reg(PS3_AUDIO_KICK(dma_ch), 28262306a36Sopenharmony_ci ch0_kick_event); 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci write_reg(PS3_AUDIO_KICK(dma_ch), 28562306a36Sopenharmony_ci PS3_AUDIO_KICK_EVENT_AUDIO_DMA(dma_ch 28662306a36Sopenharmony_ci - 1) | 28762306a36Sopenharmony_ci PS3_AUDIO_KICK_REQUEST); 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci /* ensure the hardware sees the change */ 29162306a36Sopenharmony_ci wmb(); 29262306a36Sopenharmony_ci spin_unlock_irqrestore(&card->dma_lock, irqsave); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci/* 29862306a36Sopenharmony_ci * Interrupt handler 29962306a36Sopenharmony_ci */ 30062306a36Sopenharmony_cistatic irqreturn_t snd_ps3_interrupt(int irq, void *dev_id) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci uint32_t port_intr; 30462306a36Sopenharmony_ci int underflow_occured = 0; 30562306a36Sopenharmony_ci struct snd_ps3_card_info *card = dev_id; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci if (!card->running) { 30862306a36Sopenharmony_ci update_reg(PS3_AUDIO_AX_IS, 0); 30962306a36Sopenharmony_ci update_reg(PS3_AUDIO_INTR_0, 0); 31062306a36Sopenharmony_ci return IRQ_HANDLED; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci port_intr = read_reg(PS3_AUDIO_AX_IS); 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci *serial buffer empty detected (every 4 times), 31662306a36Sopenharmony_ci *program next dma and kick it 31762306a36Sopenharmony_ci */ 31862306a36Sopenharmony_ci if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) { 31962306a36Sopenharmony_ci write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); 32062306a36Sopenharmony_ci if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { 32162306a36Sopenharmony_ci write_reg(PS3_AUDIO_AX_IS, port_intr); 32262306a36Sopenharmony_ci underflow_occured = 1; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci if (card->silent) { 32562306a36Sopenharmony_ci /* we are still in silent time */ 32662306a36Sopenharmony_ci snd_ps3_program_dma(card, 32762306a36Sopenharmony_ci (underflow_occured) ? 32862306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL : 32962306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); 33062306a36Sopenharmony_ci snd_ps3_kick_dma(card); 33162306a36Sopenharmony_ci card->silent--; 33262306a36Sopenharmony_ci } else { 33362306a36Sopenharmony_ci snd_ps3_program_dma(card, 33462306a36Sopenharmony_ci (underflow_occured) ? 33562306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_FIRSTFILL : 33662306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_RUNNING); 33762306a36Sopenharmony_ci snd_ps3_kick_dma(card); 33862306a36Sopenharmony_ci snd_pcm_period_elapsed(card->substream); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci } else if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) { 34162306a36Sopenharmony_ci write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); 34262306a36Sopenharmony_ci /* 34362306a36Sopenharmony_ci * serial out underflow, but buffer empty not detected. 34462306a36Sopenharmony_ci * in this case, fill fifo with 0 to recover. After 34562306a36Sopenharmony_ci * filling dummy data, serial automatically start to 34662306a36Sopenharmony_ci * consume them and then will generate normal buffer 34762306a36Sopenharmony_ci * empty interrupts. 34862306a36Sopenharmony_ci * If both buffer underflow and buffer empty are occurred, 34962306a36Sopenharmony_ci * it is better to do nomal data transfer than empty one 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci snd_ps3_program_dma(card, 35262306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); 35362306a36Sopenharmony_ci snd_ps3_kick_dma(card); 35462306a36Sopenharmony_ci snd_ps3_program_dma(card, 35562306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); 35662306a36Sopenharmony_ci snd_ps3_kick_dma(card); 35762306a36Sopenharmony_ci } 35862306a36Sopenharmony_ci /* clear interrupt cause */ 35962306a36Sopenharmony_ci return IRQ_HANDLED; 36062306a36Sopenharmony_ci}; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 36362306a36Sopenharmony_ci * audio mute on/off 36462306a36Sopenharmony_ci * mute_on : 0 output enabled 36562306a36Sopenharmony_ci * 1 mute 36662306a36Sopenharmony_ci */ 36762306a36Sopenharmony_cistatic int snd_ps3_mute(int mute_on) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci return ps3av_audio_mute(mute_on); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* 37362306a36Sopenharmony_ci * av setting 37462306a36Sopenharmony_ci * NOTE: calling this function may generate audio interrupt. 37562306a36Sopenharmony_ci */ 37662306a36Sopenharmony_cistatic int snd_ps3_change_avsetting(struct snd_ps3_card_info *card) 37762306a36Sopenharmony_ci{ 37862306a36Sopenharmony_ci int ret, retries, i; 37962306a36Sopenharmony_ci pr_debug("%s: start\n", __func__); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci ret = ps3av_set_audio_mode(card->avs.avs_audio_ch, 38262306a36Sopenharmony_ci card->avs.avs_audio_rate, 38362306a36Sopenharmony_ci card->avs.avs_audio_width, 38462306a36Sopenharmony_ci card->avs.avs_audio_format, 38562306a36Sopenharmony_ci card->avs.avs_audio_source); 38662306a36Sopenharmony_ci /* 38762306a36Sopenharmony_ci * Reset the following unwanted settings: 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci /* disable all 3wire buffers */ 39162306a36Sopenharmony_ci update_mask_reg(PS3_AUDIO_AO_3WMCTRL, 39262306a36Sopenharmony_ci ~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) | 39362306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASOEN(1) | 39462306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASOEN(2) | 39562306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASOEN(3)), 39662306a36Sopenharmony_ci 0); 39762306a36Sopenharmony_ci wmb(); /* ensure the hardware sees the change */ 39862306a36Sopenharmony_ci /* wait for actually stopped */ 39962306a36Sopenharmony_ci retries = 1000; 40062306a36Sopenharmony_ci while ((read_reg(PS3_AUDIO_AO_3WMCTRL) & 40162306a36Sopenharmony_ci (PS3_AUDIO_AO_3WMCTRL_ASORUN(0) | 40262306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASORUN(1) | 40362306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASORUN(2) | 40462306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) && 40562306a36Sopenharmony_ci --retries) { 40662306a36Sopenharmony_ci udelay(1); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* reset buffer pointer */ 41062306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 41162306a36Sopenharmony_ci update_reg(PS3_AUDIO_AO_3WCTRL(i), 41262306a36Sopenharmony_ci PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET); 41362306a36Sopenharmony_ci udelay(10); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci wmb(); /* ensure the hardware actually start resetting */ 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* enable 3wire#0 buffer */ 41862306a36Sopenharmony_ci update_reg(PS3_AUDIO_AO_3WMCTRL, PS3_AUDIO_AO_3WMCTRL_ASOEN(0)); 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci /* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */ 42262306a36Sopenharmony_ci update_mask_reg(PS3_AUDIO_AO_3WCTRL(0), 42362306a36Sopenharmony_ci ~PS3_AUDIO_AO_3WCTRL_ASODF, 42462306a36Sopenharmony_ci PS3_AUDIO_AO_3WCTRL_ASODF_LSB); 42562306a36Sopenharmony_ci update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0), 42662306a36Sopenharmony_ci ~PS3_AUDIO_AO_SPDCTRL_SPODF, 42762306a36Sopenharmony_ci PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); 42862306a36Sopenharmony_ci /* ensure all the setting above is written back to register */ 42962306a36Sopenharmony_ci wmb(); 43062306a36Sopenharmony_ci /* avsetting driver altered AX_IE, caller must reset it if you want */ 43162306a36Sopenharmony_ci pr_debug("%s: end\n", __func__); 43262306a36Sopenharmony_ci return ret; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/* 43662306a36Sopenharmony_ci * set sampling rate according to the substream 43762306a36Sopenharmony_ci */ 43862306a36Sopenharmony_cistatic int snd_ps3_set_avsetting(struct snd_pcm_substream *substream) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); 44162306a36Sopenharmony_ci struct snd_ps3_avsetting_info avs; 44262306a36Sopenharmony_ci int ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci avs = card->avs; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci pr_debug("%s: called freq=%d width=%d\n", __func__, 44762306a36Sopenharmony_ci substream->runtime->rate, 44862306a36Sopenharmony_ci snd_pcm_format_width(substream->runtime->format)); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci pr_debug("%s: before freq=%d width=%d\n", __func__, 45162306a36Sopenharmony_ci card->avs.avs_audio_rate, card->avs.avs_audio_width); 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* sample rate */ 45462306a36Sopenharmony_ci switch (substream->runtime->rate) { 45562306a36Sopenharmony_ci case 44100: 45662306a36Sopenharmony_ci avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_44K; 45762306a36Sopenharmony_ci break; 45862306a36Sopenharmony_ci case 48000: 45962306a36Sopenharmony_ci avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; 46062306a36Sopenharmony_ci break; 46162306a36Sopenharmony_ci case 88200: 46262306a36Sopenharmony_ci avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_88K; 46362306a36Sopenharmony_ci break; 46462306a36Sopenharmony_ci case 96000: 46562306a36Sopenharmony_ci avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_96K; 46662306a36Sopenharmony_ci break; 46762306a36Sopenharmony_ci default: 46862306a36Sopenharmony_ci pr_info("%s: invalid rate %d\n", __func__, 46962306a36Sopenharmony_ci substream->runtime->rate); 47062306a36Sopenharmony_ci return 1; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* width */ 47462306a36Sopenharmony_ci switch (snd_pcm_format_width(substream->runtime->format)) { 47562306a36Sopenharmony_ci case 16: 47662306a36Sopenharmony_ci avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci case 24: 47962306a36Sopenharmony_ci avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_24; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci default: 48262306a36Sopenharmony_ci pr_info("%s: invalid width %d\n", __func__, 48362306a36Sopenharmony_ci snd_pcm_format_width(substream->runtime->format)); 48462306a36Sopenharmony_ci return 1; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci memcpy(avs.avs_cs_info, ps3av_mode_cs_info, 8); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (memcmp(&card->avs, &avs, sizeof(avs))) { 49062306a36Sopenharmony_ci pr_debug("%s: after freq=%d width=%d\n", __func__, 49162306a36Sopenharmony_ci card->avs.avs_audio_rate, card->avs.avs_audio_width); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci card->avs = avs; 49462306a36Sopenharmony_ci snd_ps3_change_avsetting(card); 49562306a36Sopenharmony_ci ret = 0; 49662306a36Sopenharmony_ci } else 49762306a36Sopenharmony_ci ret = 1; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* check CS non-audio bit and mute accordingly */ 50062306a36Sopenharmony_ci if (avs.avs_cs_info[0] & 0x02) 50162306a36Sopenharmony_ci ps3av_audio_mute_analog(1); /* mute if non-audio */ 50262306a36Sopenharmony_ci else 50362306a36Sopenharmony_ci ps3av_audio_mute_analog(0); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci return ret; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/* 50962306a36Sopenharmony_ci * PCM operators 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_cistatic int snd_ps3_pcm_open(struct snd_pcm_substream *substream) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 51462306a36Sopenharmony_ci struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* to retrieve substream/runtime in interrupt handler */ 51762306a36Sopenharmony_ci card->substream = substream; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci runtime->hw = snd_ps3_pcm_hw; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci card->start_delay = snd_ps3_start_delay; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* mute off */ 52462306a36Sopenharmony_ci snd_ps3_mute(0); /* this function sleep */ 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 52762306a36Sopenharmony_ci PS3_AUDIO_FIFO_STAGE_SIZE * 4 * 2); 52862306a36Sopenharmony_ci return 0; 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic int snd_ps3_pcm_close(struct snd_pcm_substream *substream) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci /* mute on */ 53462306a36Sopenharmony_ci snd_ps3_mute(1); 53562306a36Sopenharmony_ci return 0; 53662306a36Sopenharmony_ci}; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_cistatic int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream, 53962306a36Sopenharmony_ci unsigned int delay_ms) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci int ret; 54262306a36Sopenharmony_ci int rate ; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci rate = substream->runtime->rate; 54562306a36Sopenharmony_ci ret = snd_pcm_format_size(substream->runtime->format, 54662306a36Sopenharmony_ci rate * delay_ms / 1000) 54762306a36Sopenharmony_ci * substream->runtime->channels; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci pr_debug("%s: time=%d rate=%d bytes=%ld, frames=%d, ret=%d\n", 55062306a36Sopenharmony_ci __func__, 55162306a36Sopenharmony_ci delay_ms, 55262306a36Sopenharmony_ci rate, 55362306a36Sopenharmony_ci snd_pcm_format_size(substream->runtime->format, rate), 55462306a36Sopenharmony_ci rate * delay_ms / 1000, 55562306a36Sopenharmony_ci ret); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci return ret; 55862306a36Sopenharmony_ci}; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_cistatic int snd_ps3_pcm_prepare(struct snd_pcm_substream *substream) 56162306a36Sopenharmony_ci{ 56262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 56362306a36Sopenharmony_ci struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); 56462306a36Sopenharmony_ci unsigned long irqsave; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (!snd_ps3_set_avsetting(substream)) { 56762306a36Sopenharmony_ci /* some parameter changed */ 56862306a36Sopenharmony_ci write_reg(PS3_AUDIO_AX_IE, 56962306a36Sopenharmony_ci PS3_AUDIO_AX_IE_ASOBEIE(0) | 57062306a36Sopenharmony_ci PS3_AUDIO_AX_IE_ASOBUIE(0)); 57162306a36Sopenharmony_ci /* 57262306a36Sopenharmony_ci * let SPDIF device re-lock with SPDIF signal, 57362306a36Sopenharmony_ci * start with some silence 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_ci card->silent = snd_ps3_delay_to_bytes(substream, 57662306a36Sopenharmony_ci card->start_delay) / 57762306a36Sopenharmony_ci (PS3_AUDIO_FIFO_STAGE_SIZE * 4); /* every 4 times */ 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci /* restart ring buffer pointer */ 58162306a36Sopenharmony_ci spin_lock_irqsave(&card->dma_lock, irqsave); 58262306a36Sopenharmony_ci { 58362306a36Sopenharmony_ci card->dma_buffer_size = runtime->dma_bytes; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci card->dma_last_transfer_vaddr[SND_PS3_CH_L] = 58662306a36Sopenharmony_ci card->dma_next_transfer_vaddr[SND_PS3_CH_L] = 58762306a36Sopenharmony_ci card->dma_start_vaddr[SND_PS3_CH_L] = 58862306a36Sopenharmony_ci runtime->dma_area; 58962306a36Sopenharmony_ci card->dma_start_bus_addr[SND_PS3_CH_L] = runtime->dma_addr; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci card->dma_last_transfer_vaddr[SND_PS3_CH_R] = 59262306a36Sopenharmony_ci card->dma_next_transfer_vaddr[SND_PS3_CH_R] = 59362306a36Sopenharmony_ci card->dma_start_vaddr[SND_PS3_CH_R] = 59462306a36Sopenharmony_ci runtime->dma_area + (runtime->dma_bytes / 2); 59562306a36Sopenharmony_ci card->dma_start_bus_addr[SND_PS3_CH_R] = 59662306a36Sopenharmony_ci runtime->dma_addr + (runtime->dma_bytes / 2); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci pr_debug("%s: vaddr=%p bus=%#llx\n", __func__, 59962306a36Sopenharmony_ci card->dma_start_vaddr[SND_PS3_CH_L], 60062306a36Sopenharmony_ci card->dma_start_bus_addr[SND_PS3_CH_L]); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci spin_unlock_irqrestore(&card->dma_lock, irqsave); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* ensure the hardware sees the change */ 60662306a36Sopenharmony_ci mb(); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci}; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_cistatic int snd_ps3_pcm_trigger(struct snd_pcm_substream *substream, 61262306a36Sopenharmony_ci int cmd) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci switch (cmd) { 61762306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_START: 61862306a36Sopenharmony_ci /* clear outstanding interrupts */ 61962306a36Sopenharmony_ci update_reg(PS3_AUDIO_AX_IS, 0); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci spin_lock(&card->dma_lock); 62262306a36Sopenharmony_ci { 62362306a36Sopenharmony_ci card->running = 1; 62462306a36Sopenharmony_ci } 62562306a36Sopenharmony_ci spin_unlock(&card->dma_lock); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci snd_ps3_program_dma(card, 62862306a36Sopenharmony_ci SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); 62962306a36Sopenharmony_ci snd_ps3_kick_dma(card); 63062306a36Sopenharmony_ci while (read_reg(PS3_AUDIO_KICK(7)) & 63162306a36Sopenharmony_ci PS3_AUDIO_KICK_STATUS_MASK) { 63262306a36Sopenharmony_ci udelay(1); 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_RUNNING); 63562306a36Sopenharmony_ci snd_ps3_kick_dma(card); 63662306a36Sopenharmony_ci break; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci case SNDRV_PCM_TRIGGER_STOP: 63962306a36Sopenharmony_ci spin_lock(&card->dma_lock); 64062306a36Sopenharmony_ci { 64162306a36Sopenharmony_ci card->running = 0; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci spin_unlock(&card->dma_lock); 64462306a36Sopenharmony_ci snd_ps3_wait_for_dma_stop(card); 64562306a36Sopenharmony_ci break; 64662306a36Sopenharmony_ci default: 64762306a36Sopenharmony_ci break; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci}; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci/* 65562306a36Sopenharmony_ci * report current pointer 65662306a36Sopenharmony_ci */ 65762306a36Sopenharmony_cistatic snd_pcm_uframes_t snd_ps3_pcm_pointer( 65862306a36Sopenharmony_ci struct snd_pcm_substream *substream) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); 66162306a36Sopenharmony_ci size_t bytes; 66262306a36Sopenharmony_ci snd_pcm_uframes_t ret; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci spin_lock(&card->dma_lock); 66562306a36Sopenharmony_ci { 66662306a36Sopenharmony_ci bytes = (size_t)(card->dma_last_transfer_vaddr[SND_PS3_CH_L] - 66762306a36Sopenharmony_ci card->dma_start_vaddr[SND_PS3_CH_L]); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci spin_unlock(&card->dma_lock); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci ret = bytes_to_frames(substream->runtime, bytes * 2); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci return ret; 67462306a36Sopenharmony_ci}; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci/* 67762306a36Sopenharmony_ci * SPDIF status bits controls 67862306a36Sopenharmony_ci */ 67962306a36Sopenharmony_cistatic int snd_ps3_spdif_mask_info(struct snd_kcontrol *kcontrol, 68062306a36Sopenharmony_ci struct snd_ctl_elem_info *uinfo) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 68362306a36Sopenharmony_ci uinfo->count = 1; 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci} 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci/* FIXME: ps3av_set_audio_mode() assumes only consumer mode */ 68862306a36Sopenharmony_cistatic int snd_ps3_spdif_cmask_get(struct snd_kcontrol *kcontrol, 68962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci memset(ucontrol->value.iec958.status, 0xff, 8); 69262306a36Sopenharmony_ci return 0; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int snd_ps3_spdif_pmask_get(struct snd_kcontrol *kcontrol, 69662306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic int snd_ps3_spdif_default_get(struct snd_kcontrol *kcontrol, 70262306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci memcpy(ucontrol->value.iec958.status, ps3av_mode_cs_info, 8); 70562306a36Sopenharmony_ci return 0; 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_cistatic int snd_ps3_spdif_default_put(struct snd_kcontrol *kcontrol, 70962306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci if (memcmp(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8)) { 71262306a36Sopenharmony_ci memcpy(ps3av_mode_cs_info, ucontrol->value.iec958.status, 8); 71362306a36Sopenharmony_ci return 1; 71462306a36Sopenharmony_ci } 71562306a36Sopenharmony_ci return 0; 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_cistatic const struct snd_kcontrol_new spdif_ctls[] = { 71962306a36Sopenharmony_ci { 72062306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 72162306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 72262306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), 72362306a36Sopenharmony_ci .info = snd_ps3_spdif_mask_info, 72462306a36Sopenharmony_ci .get = snd_ps3_spdif_cmask_get, 72562306a36Sopenharmony_ci }, 72662306a36Sopenharmony_ci { 72762306a36Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 72862306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 72962306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PRO_MASK), 73062306a36Sopenharmony_ci .info = snd_ps3_spdif_mask_info, 73162306a36Sopenharmony_ci .get = snd_ps3_spdif_pmask_get, 73262306a36Sopenharmony_ci }, 73362306a36Sopenharmony_ci { 73462306a36Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_PCM, 73562306a36Sopenharmony_ci .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 73662306a36Sopenharmony_ci .info = snd_ps3_spdif_mask_info, 73762306a36Sopenharmony_ci .get = snd_ps3_spdif_default_get, 73862306a36Sopenharmony_ci .put = snd_ps3_spdif_default_put, 73962306a36Sopenharmony_ci }, 74062306a36Sopenharmony_ci}; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic const struct snd_pcm_ops snd_ps3_pcm_spdif_ops = { 74362306a36Sopenharmony_ci .open = snd_ps3_pcm_open, 74462306a36Sopenharmony_ci .close = snd_ps3_pcm_close, 74562306a36Sopenharmony_ci .prepare = snd_ps3_pcm_prepare, 74662306a36Sopenharmony_ci .trigger = snd_ps3_pcm_trigger, 74762306a36Sopenharmony_ci .pointer = snd_ps3_pcm_pointer, 74862306a36Sopenharmony_ci}; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_cistatic int snd_ps3_map_mmio(void) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci the_card.mapped_mmio_vaddr = 75462306a36Sopenharmony_ci ioremap(the_card.ps3_dev->m_region->bus_addr, 75562306a36Sopenharmony_ci the_card.ps3_dev->m_region->len); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (!the_card.mapped_mmio_vaddr) { 75862306a36Sopenharmony_ci pr_info("%s: ioremap 0 failed p=%#lx l=%#lx \n", 75962306a36Sopenharmony_ci __func__, the_card.ps3_dev->m_region->lpar_addr, 76062306a36Sopenharmony_ci the_card.ps3_dev->m_region->len); 76162306a36Sopenharmony_ci return -ENXIO; 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci return 0; 76562306a36Sopenharmony_ci}; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic void snd_ps3_unmap_mmio(void) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci iounmap(the_card.mapped_mmio_vaddr); 77062306a36Sopenharmony_ci the_card.mapped_mmio_vaddr = NULL; 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int snd_ps3_allocate_irq(void) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci int ret; 77662306a36Sopenharmony_ci u64 lpar_addr, lpar_size; 77762306a36Sopenharmony_ci u64 __iomem *mapped; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci /* FIXME: move this to device_init (H/W probe) */ 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci /* get irq outlet */ 78262306a36Sopenharmony_ci ret = lv1_gpu_device_map(1, &lpar_addr, &lpar_size); 78362306a36Sopenharmony_ci if (ret) { 78462306a36Sopenharmony_ci pr_info("%s: device map 1 failed %d\n", __func__, 78562306a36Sopenharmony_ci ret); 78662306a36Sopenharmony_ci return -ENXIO; 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci mapped = ioremap(lpar_addr, lpar_size); 79062306a36Sopenharmony_ci if (!mapped) { 79162306a36Sopenharmony_ci pr_info("%s: ioremap 1 failed \n", __func__); 79262306a36Sopenharmony_ci return -ENXIO; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci the_card.audio_irq_outlet = in_be64(mapped); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci iounmap(mapped); 79862306a36Sopenharmony_ci ret = lv1_gpu_device_unmap(1); 79962306a36Sopenharmony_ci if (ret) 80062306a36Sopenharmony_ci pr_info("%s: unmap 1 failed\n", __func__); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* irq */ 80362306a36Sopenharmony_ci ret = ps3_irq_plug_setup(PS3_BINDING_CPU_ANY, 80462306a36Sopenharmony_ci the_card.audio_irq_outlet, 80562306a36Sopenharmony_ci &the_card.irq_no); 80662306a36Sopenharmony_ci if (ret) { 80762306a36Sopenharmony_ci pr_info("%s:ps3_alloc_irq failed (%d)\n", __func__, ret); 80862306a36Sopenharmony_ci return ret; 80962306a36Sopenharmony_ci } 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci ret = request_irq(the_card.irq_no, snd_ps3_interrupt, 0, 81262306a36Sopenharmony_ci SND_PS3_DRIVER_NAME, &the_card); 81362306a36Sopenharmony_ci if (ret) { 81462306a36Sopenharmony_ci pr_info("%s: request_irq failed (%d)\n", __func__, ret); 81562306a36Sopenharmony_ci goto cleanup_irq; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci return 0; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci cleanup_irq: 82162306a36Sopenharmony_ci ps3_irq_plug_destroy(the_card.irq_no); 82262306a36Sopenharmony_ci return ret; 82362306a36Sopenharmony_ci}; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void snd_ps3_free_irq(void) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci free_irq(the_card.irq_no, &the_card); 82862306a36Sopenharmony_ci ps3_irq_plug_destroy(the_card.irq_no); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic void snd_ps3_audio_set_base_addr(uint64_t ioaddr_start) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci uint64_t val; 83462306a36Sopenharmony_ci int ret; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci val = (ioaddr_start & (0x0fUL << 32)) >> (32 - 20) | 83762306a36Sopenharmony_ci (0x03UL << 24) | 83862306a36Sopenharmony_ci (0x0fUL << 12) | 83962306a36Sopenharmony_ci (PS3_AUDIO_IOID); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci ret = lv1_gpu_attribute(0x100, 0x007, val); 84262306a36Sopenharmony_ci if (ret) 84362306a36Sopenharmony_ci pr_info("%s: gpu_attribute failed %d\n", __func__, 84462306a36Sopenharmony_ci ret); 84562306a36Sopenharmony_ci} 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_cistatic void snd_ps3_audio_fixup(struct snd_ps3_card_info *card) 84862306a36Sopenharmony_ci{ 84962306a36Sopenharmony_ci /* 85062306a36Sopenharmony_ci * avsetting driver seems to never change the following 85162306a36Sopenharmony_ci * so, init them here once 85262306a36Sopenharmony_ci */ 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* no dma interrupt needed */ 85562306a36Sopenharmony_ci write_reg(PS3_AUDIO_INTR_EN_0, 0); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci /* use every 4 buffer empty interrupt */ 85862306a36Sopenharmony_ci update_mask_reg(PS3_AUDIO_AX_IC, 85962306a36Sopenharmony_ci PS3_AUDIO_AX_IC_AASOIMD_MASK, 86062306a36Sopenharmony_ci PS3_AUDIO_AX_IC_AASOIMD_EVERY4); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci /* enable 3wire clocks */ 86362306a36Sopenharmony_ci update_mask_reg(PS3_AUDIO_AO_3WMCTRL, 86462306a36Sopenharmony_ci ~(PS3_AUDIO_AO_3WMCTRL_ASOBCLKD_DISABLED | 86562306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASOLRCKD_DISABLED), 86662306a36Sopenharmony_ci 0); 86762306a36Sopenharmony_ci update_reg(PS3_AUDIO_AO_3WMCTRL, 86862306a36Sopenharmony_ci PS3_AUDIO_AO_3WMCTRL_ASOPLRCK_DEFAULT); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int snd_ps3_init_avsetting(struct snd_ps3_card_info *card) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci int ret; 87462306a36Sopenharmony_ci pr_debug("%s: start\n", __func__); 87562306a36Sopenharmony_ci card->avs.avs_audio_ch = PS3AV_CMD_AUDIO_NUM_OF_CH_2; 87662306a36Sopenharmony_ci card->avs.avs_audio_rate = PS3AV_CMD_AUDIO_FS_48K; 87762306a36Sopenharmony_ci card->avs.avs_audio_width = PS3AV_CMD_AUDIO_WORD_BITS_16; 87862306a36Sopenharmony_ci card->avs.avs_audio_format = PS3AV_CMD_AUDIO_FORMAT_PCM; 87962306a36Sopenharmony_ci card->avs.avs_audio_source = PS3AV_CMD_AUDIO_SOURCE_SERIAL; 88062306a36Sopenharmony_ci memcpy(card->avs.avs_cs_info, ps3av_mode_cs_info, 8); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci ret = snd_ps3_change_avsetting(card); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci snd_ps3_audio_fixup(card); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* to start to generate SPDIF signal, fill data */ 88762306a36Sopenharmony_ci snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL); 88862306a36Sopenharmony_ci snd_ps3_kick_dma(card); 88962306a36Sopenharmony_ci pr_debug("%s: end\n", __func__); 89062306a36Sopenharmony_ci return ret; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci int i, ret; 89662306a36Sopenharmony_ci u64 lpar_addr, lpar_size; 89762306a36Sopenharmony_ci static u64 dummy_mask; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci the_card.ps3_dev = dev; 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci ret = ps3_open_hv_device(dev); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (ret) 90462306a36Sopenharmony_ci return -ENXIO; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci /* setup MMIO */ 90762306a36Sopenharmony_ci ret = lv1_gpu_device_map(2, &lpar_addr, &lpar_size); 90862306a36Sopenharmony_ci if (ret) { 90962306a36Sopenharmony_ci pr_info("%s: device map 2 failed %d\n", __func__, ret); 91062306a36Sopenharmony_ci goto clean_open; 91162306a36Sopenharmony_ci } 91262306a36Sopenharmony_ci ps3_mmio_region_init(dev, dev->m_region, lpar_addr, lpar_size, 91362306a36Sopenharmony_ci PAGE_SHIFT); 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci ret = snd_ps3_map_mmio(); 91662306a36Sopenharmony_ci if (ret) 91762306a36Sopenharmony_ci goto clean_dev_map; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* setup DMA area */ 92062306a36Sopenharmony_ci ps3_dma_region_init(dev, dev->d_region, 92162306a36Sopenharmony_ci PAGE_SHIFT, /* use system page size */ 92262306a36Sopenharmony_ci 0, /* dma type; not used */ 92362306a36Sopenharmony_ci NULL, 92462306a36Sopenharmony_ci ALIGN(SND_PS3_DMA_REGION_SIZE, PAGE_SIZE)); 92562306a36Sopenharmony_ci dev->d_region->ioid = PS3_AUDIO_IOID; 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci ret = ps3_dma_region_create(dev->d_region); 92862306a36Sopenharmony_ci if (ret) { 92962306a36Sopenharmony_ci pr_info("%s: region_create\n", __func__); 93062306a36Sopenharmony_ci goto clean_mmio; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci dummy_mask = DMA_BIT_MASK(32); 93462306a36Sopenharmony_ci dev->core.dma_mask = &dummy_mask; 93562306a36Sopenharmony_ci dma_set_coherent_mask(&dev->core, dummy_mask); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci snd_ps3_audio_set_base_addr(dev->d_region->bus_addr); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci /* CONFIG_SND_PS3_DEFAULT_START_DELAY */ 94062306a36Sopenharmony_ci the_card.start_delay = snd_ps3_start_delay; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci /* irq */ 94362306a36Sopenharmony_ci if (snd_ps3_allocate_irq()) { 94462306a36Sopenharmony_ci ret = -ENXIO; 94562306a36Sopenharmony_ci goto clean_dma_region; 94662306a36Sopenharmony_ci } 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci /* create card instance */ 94962306a36Sopenharmony_ci ret = snd_card_new(&dev->core, index, id, THIS_MODULE, 95062306a36Sopenharmony_ci 0, &the_card.card); 95162306a36Sopenharmony_ci if (ret < 0) 95262306a36Sopenharmony_ci goto clean_irq; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci strcpy(the_card.card->driver, "PS3"); 95562306a36Sopenharmony_ci strcpy(the_card.card->shortname, "PS3"); 95662306a36Sopenharmony_ci strcpy(the_card.card->longname, "PS3 sound"); 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* create control elements */ 95962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) { 96062306a36Sopenharmony_ci ret = snd_ctl_add(the_card.card, 96162306a36Sopenharmony_ci snd_ctl_new1(&spdif_ctls[i], &the_card)); 96262306a36Sopenharmony_ci if (ret < 0) 96362306a36Sopenharmony_ci goto clean_card; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* create PCM devices instance */ 96762306a36Sopenharmony_ci /* NOTE:this driver works assuming pcm:substream = 1:1 */ 96862306a36Sopenharmony_ci ret = snd_pcm_new(the_card.card, 96962306a36Sopenharmony_ci "SPDIF", 97062306a36Sopenharmony_ci 0, /* instance index, will be stored pcm.device*/ 97162306a36Sopenharmony_ci 1, /* output substream */ 97262306a36Sopenharmony_ci 0, /* input substream */ 97362306a36Sopenharmony_ci &(the_card.pcm)); 97462306a36Sopenharmony_ci if (ret) 97562306a36Sopenharmony_ci goto clean_card; 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci the_card.pcm->private_data = &the_card; 97862306a36Sopenharmony_ci strcpy(the_card.pcm->name, "SPDIF"); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci /* set pcm ops */ 98162306a36Sopenharmony_ci snd_pcm_set_ops(the_card.pcm, SNDRV_PCM_STREAM_PLAYBACK, 98262306a36Sopenharmony_ci &snd_ps3_pcm_spdif_ops); 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; 98562306a36Sopenharmony_ci /* pre-alloc PCM DMA buffer*/ 98662306a36Sopenharmony_ci snd_pcm_set_managed_buffer_all(the_card.pcm, 98762306a36Sopenharmony_ci SNDRV_DMA_TYPE_DEV, 98862306a36Sopenharmony_ci &dev->core, 98962306a36Sopenharmony_ci SND_PS3_PCM_PREALLOC_SIZE, 99062306a36Sopenharmony_ci SND_PS3_PCM_PREALLOC_SIZE); 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_ci /* 99362306a36Sopenharmony_ci * allocate null buffer 99462306a36Sopenharmony_ci * its size should be lager than PS3_AUDIO_FIFO_STAGE_SIZE * 2 99562306a36Sopenharmony_ci * PAGE_SIZE is enogh 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_ci the_card.null_buffer_start_vaddr = 99862306a36Sopenharmony_ci dma_alloc_coherent(&the_card.ps3_dev->core, 99962306a36Sopenharmony_ci PAGE_SIZE, 100062306a36Sopenharmony_ci &the_card.null_buffer_start_dma_addr, 100162306a36Sopenharmony_ci GFP_KERNEL); 100262306a36Sopenharmony_ci if (!the_card.null_buffer_start_vaddr) { 100362306a36Sopenharmony_ci pr_info("%s: nullbuffer alloc failed\n", __func__); 100462306a36Sopenharmony_ci ret = -ENOMEM; 100562306a36Sopenharmony_ci goto clean_card; 100662306a36Sopenharmony_ci } 100762306a36Sopenharmony_ci pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__, 100862306a36Sopenharmony_ci the_card.null_buffer_start_vaddr, 100962306a36Sopenharmony_ci the_card.null_buffer_start_dma_addr); 101062306a36Sopenharmony_ci /* set default sample rate/word width */ 101162306a36Sopenharmony_ci snd_ps3_init_avsetting(&the_card); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci /* register the card */ 101462306a36Sopenharmony_ci ret = snd_card_register(the_card.card); 101562306a36Sopenharmony_ci if (ret < 0) 101662306a36Sopenharmony_ci goto clean_dma_map; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci pr_info("%s started. start_delay=%dms\n", 101962306a36Sopenharmony_ci the_card.card->longname, the_card.start_delay); 102062306a36Sopenharmony_ci return 0; 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ciclean_dma_map: 102362306a36Sopenharmony_ci dma_free_coherent(&the_card.ps3_dev->core, 102462306a36Sopenharmony_ci PAGE_SIZE, 102562306a36Sopenharmony_ci the_card.null_buffer_start_vaddr, 102662306a36Sopenharmony_ci the_card.null_buffer_start_dma_addr); 102762306a36Sopenharmony_ciclean_card: 102862306a36Sopenharmony_ci snd_card_free(the_card.card); 102962306a36Sopenharmony_ciclean_irq: 103062306a36Sopenharmony_ci snd_ps3_free_irq(); 103162306a36Sopenharmony_ciclean_dma_region: 103262306a36Sopenharmony_ci ps3_dma_region_free(dev->d_region); 103362306a36Sopenharmony_ciclean_mmio: 103462306a36Sopenharmony_ci snd_ps3_unmap_mmio(); 103562306a36Sopenharmony_ciclean_dev_map: 103662306a36Sopenharmony_ci lv1_gpu_device_unmap(2); 103762306a36Sopenharmony_ciclean_open: 103862306a36Sopenharmony_ci ps3_close_hv_device(dev); 103962306a36Sopenharmony_ci /* 104062306a36Sopenharmony_ci * there is no destructor function to pcm. 104162306a36Sopenharmony_ci * midlayer automatically releases if the card removed 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci return ret; 104462306a36Sopenharmony_ci}; /* snd_ps3_probe */ 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_ci/* called when module removal */ 104762306a36Sopenharmony_cistatic void snd_ps3_driver_remove(struct ps3_system_bus_device *dev) 104862306a36Sopenharmony_ci{ 104962306a36Sopenharmony_ci pr_info("%s:start id=%d\n", __func__, dev->match_id); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci /* 105262306a36Sopenharmony_ci * ctl and preallocate buffer will be freed in 105362306a36Sopenharmony_ci * snd_card_free 105462306a36Sopenharmony_ci */ 105562306a36Sopenharmony_ci snd_card_free(the_card.card); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci dma_free_coherent(&dev->core, 105862306a36Sopenharmony_ci PAGE_SIZE, 105962306a36Sopenharmony_ci the_card.null_buffer_start_vaddr, 106062306a36Sopenharmony_ci the_card.null_buffer_start_dma_addr); 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci ps3_dma_region_free(dev->d_region); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ci snd_ps3_free_irq(); 106562306a36Sopenharmony_ci snd_ps3_unmap_mmio(); 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci lv1_gpu_device_unmap(2); 106862306a36Sopenharmony_ci ps3_close_hv_device(dev); 106962306a36Sopenharmony_ci pr_info("%s:end id=%d\n", __func__, dev->match_id); 107062306a36Sopenharmony_ci} /* snd_ps3_remove */ 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_cistatic struct ps3_system_bus_driver snd_ps3_bus_driver_info = { 107362306a36Sopenharmony_ci .match_id = PS3_MATCH_ID_SOUND, 107462306a36Sopenharmony_ci .probe = snd_ps3_driver_probe, 107562306a36Sopenharmony_ci .remove = snd_ps3_driver_remove, 107662306a36Sopenharmony_ci .shutdown = snd_ps3_driver_remove, 107762306a36Sopenharmony_ci .core = { 107862306a36Sopenharmony_ci .name = SND_PS3_DRIVER_NAME, 107962306a36Sopenharmony_ci .owner = THIS_MODULE, 108062306a36Sopenharmony_ci }, 108162306a36Sopenharmony_ci}; 108262306a36Sopenharmony_ci 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci/* 108562306a36Sopenharmony_ci * module/subsystem initialize/terminate 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_cistatic int __init snd_ps3_init(void) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci int ret; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) 109262306a36Sopenharmony_ci return -ENXIO; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci memset(&the_card, 0, sizeof(the_card)); 109562306a36Sopenharmony_ci spin_lock_init(&the_card.dma_lock); 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci /* register systembus DRIVER, this calls our probe() func */ 109862306a36Sopenharmony_ci ret = ps3_system_bus_driver_register(&snd_ps3_bus_driver_info); 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci return ret; 110162306a36Sopenharmony_ci} 110262306a36Sopenharmony_cimodule_init(snd_ps3_init); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic void __exit snd_ps3_exit(void) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci ps3_system_bus_driver_unregister(&snd_ps3_bus_driver_info); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_cimodule_exit(snd_ps3_exit); 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 111162306a36Sopenharmony_ciMODULE_DESCRIPTION("PS3 sound driver"); 111262306a36Sopenharmony_ciMODULE_AUTHOR("Sony Computer Entertainment Inc."); 111362306a36Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_SOUND); 1114