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