// SPDX-License-Identifier: GPL-2.0-only /* * Audio support for PS3 * Copyright (C) 2007 Sony Computer Entertainment Inc. * All rights reserved. * Copyright 2006, 2007 Sony Corporation
*/
staticint snd_ps3_verify_dma_stop(struct snd_ps3_card_info *card, int count, int force_stop)
{ int dma_ch, done, retries, stop_forced = 0;
uint32_t status;
for (dma_ch = 0; dma_ch < 8; dma_ch++) {
retries = count; do {
status = read_reg(PS3_AUDIO_KICK(dma_ch)) &
PS3_AUDIO_KICK_STATUS_MASK; switch (status) { case PS3_AUDIO_KICK_STATUS_DONE: case PS3_AUDIO_KICK_STATUS_NOTIFY: case PS3_AUDIO_KICK_STATUS_CLEAR: case PS3_AUDIO_KICK_STATUS_ERROR:
done = 1; break; default:
done = 0;
udelay(10);
}
} while (!done && --retries); if (!retries && force_stop) {
pr_info("%s: DMA ch %d is not stopped.",
__func__, dma_ch); /* last resort. force to stop dma. * NOTE: this cause DMA done interrupts
*/
update_reg(PS3_AUDIO_CONFIG, PS3_AUDIO_CONFIG_CLEAR);
stop_forced = 1;
}
} return stop_forced;
}
/* * wait for all dma is done. * NOTE: caller should reset card->running before call. * If not, the interrupt handler will re-start DMA, * then DMA is never stopped.
*/ staticvoid snd_ps3_wait_for_dma_stop(struct snd_ps3_card_info *card)
{ int stop_forced; /* * wait for the last dma is done
*/
/* * expected maximum DMA done time is 5.7ms + something (DMA itself). * 5.7ms is from 16bit/sample 2ch 44.1Khz; the time next * DMA kick event would occur.
*/
stop_forced = snd_ps3_verify_dma_stop(card, 700, 1);
/* *revert CLEAR bit since it will not reset automatically after DMA stop
*/ if (stop_forced)
update_mask_reg(PS3_AUDIO_CONFIG, ~PS3_AUDIO_CONFIG_CLEAR, 0); /* ensure the hardware sees changes */
wmb();
}
if (!card->running) {
update_reg(PS3_AUDIO_AX_IS, 0);
update_reg(PS3_AUDIO_INTR_0, 0); return IRQ_HANDLED;
}
port_intr = read_reg(PS3_AUDIO_AX_IS); /* *serial buffer empty detected (every 4 times), *program next dma and kick it
*/ if (port_intr & PS3_AUDIO_AX_IE_ASOBEIE(0)) {
write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBEIE(0)); if (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
write_reg(PS3_AUDIO_AX_IS, port_intr);
underflow_occured = 1;
} if (card->silent) { /* we are still in silent time */
snd_ps3_program_dma(card,
(underflow_occured) ?
SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL :
SND_PS3_DMA_FILLTYPE_SILENT_RUNNING);
snd_ps3_kick_dma(card);
card->silent--;
} else {
snd_ps3_program_dma(card,
(underflow_occured) ?
SND_PS3_DMA_FILLTYPE_FIRSTFILL :
SND_PS3_DMA_FILLTYPE_RUNNING);
snd_ps3_kick_dma(card);
snd_pcm_period_elapsed(card->substream);
}
} elseif (port_intr & PS3_AUDIO_AX_IE_ASOBUIE(0)) {
write_reg(PS3_AUDIO_AX_IS, PS3_AUDIO_AX_IE_ASOBUIE(0)); /* * serial out underflow, but buffer empty not detected. * in this case, fill fifo with 0 to recover. After * filling dummy data, serial automatically start to * consume them and then will generate normal buffer * empty interrupts. * If both buffer underflow and buffer empty are occurred, * it is better to do nomal data transfer than empty one
*/
snd_ps3_program_dma(card,
SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
snd_ps3_kick_dma(card);
snd_ps3_program_dma(card,
SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
snd_ps3_kick_dma(card);
} /* clear interrupt cause */ return IRQ_HANDLED;
};
/* * av setting * NOTE: calling this function may generate audio interrupt.
*/ staticint snd_ps3_change_avsetting(struct snd_ps3_card_info *card)
{ int ret, retries, i;
pr_debug("%s: start\n", __func__);
ret = ps3av_set_audio_mode(card->avs.avs_audio_ch,
card->avs.avs_audio_rate,
card->avs.avs_audio_width,
card->avs.avs_audio_format,
card->avs.avs_audio_source); /* * Reset the following unwanted settings:
*/
/* disable all 3wire buffers */
update_mask_reg(PS3_AUDIO_AO_3WMCTRL,
~(PS3_AUDIO_AO_3WMCTRL_ASOEN(0) |
PS3_AUDIO_AO_3WMCTRL_ASOEN(1) |
PS3_AUDIO_AO_3WMCTRL_ASOEN(2) |
PS3_AUDIO_AO_3WMCTRL_ASOEN(3)),
0);
wmb(); /* ensure the hardware sees the change */ /* wait for actually stopped */
retries = 1000; while ((read_reg(PS3_AUDIO_AO_3WMCTRL) &
(PS3_AUDIO_AO_3WMCTRL_ASORUN(0) |
PS3_AUDIO_AO_3WMCTRL_ASORUN(1) |
PS3_AUDIO_AO_3WMCTRL_ASORUN(2) |
PS3_AUDIO_AO_3WMCTRL_ASORUN(3))) &&
--retries) {
udelay(1);
}
/* reset buffer pointer */ for (i = 0; i < 4; i++) {
update_reg(PS3_AUDIO_AO_3WCTRL(i),
PS3_AUDIO_AO_3WCTRL_ASOBRST_RESET);
udelay(10);
}
wmb(); /* ensure the hardware actually start resetting */
/* In 24bit mode,ALSA inserts a zero byte at first byte of per sample */
update_mask_reg(PS3_AUDIO_AO_3WCTRL(0),
~PS3_AUDIO_AO_3WCTRL_ASODF,
PS3_AUDIO_AO_3WCTRL_ASODF_LSB);
update_mask_reg(PS3_AUDIO_AO_SPDCTRL(0),
~PS3_AUDIO_AO_SPDCTRL_SPODF,
PS3_AUDIO_AO_SPDCTRL_SPODF_LSB); /* ensure all the setting above is written back to register */
wmb(); /* avsetting driver altered AX_IE, caller must reset it if you want */
pr_debug("%s: end\n", __func__); return ret;
}
/* * set sampling rate according to the substream
*/ staticint snd_ps3_set_avsetting(struct snd_pcm_substream *substream)
{ struct snd_ps3_card_info *card = snd_pcm_substream_chip(substream); struct snd_ps3_avsetting_info avs; int ret;
avs = card->avs;
pr_debug("%s: called freq=%d width=%d\n", __func__,
substream->runtime->rate,
snd_pcm_format_width(substream->runtime->format));
pr_debug("%s: before freq=%d width=%d\n", __func__,
card->avs.avs_audio_rate, card->avs.avs_audio_width);
ret = lv1_gpu_attribute(0x100, 0x007, val); if (ret)
pr_info("%s: gpu_attribute failed %d\n", __func__,
ret);
}
staticvoid snd_ps3_audio_fixup(struct snd_ps3_card_info *card)
{ /* * avsetting driver seems to never change the following * so, init them here once
*/
/* no dma interrupt needed */
write_reg(PS3_AUDIO_INTR_EN_0, 0);
/* use every 4 buffer empty interrupt */
update_mask_reg(PS3_AUDIO_AX_IC,
PS3_AUDIO_AX_IC_AASOIMD_MASK,
PS3_AUDIO_AX_IC_AASOIMD_EVERY4);
/* to start to generate SPDIF signal, fill data */
snd_ps3_program_dma(card, SND_PS3_DMA_FILLTYPE_SILENT_FIRSTFILL);
snd_ps3_kick_dma(card);
pr_debug("%s: end\n", __func__); return ret;
}
staticint snd_ps3_driver_probe(struct ps3_system_bus_device *dev)
{ int i, ret;
u64 lpar_addr, lpar_size; static u64 dummy_mask;
/* create control elements */ for (i = 0; i < ARRAY_SIZE(spdif_ctls); i++) {
ret = snd_ctl_add(the_card.card,
snd_ctl_new1(&spdif_ctls[i], &the_card)); if (ret < 0) goto clean_card;
}
/* create PCM devices instance */ /* NOTE:this driver works assuming pcm:substream = 1:1 */
ret = snd_pcm_new(the_card.card, "SPDIF",
0, /* instance index, will be stored pcm.device*/
1, /* output substream */
0, /* input substream */
&(the_card.pcm)); if (ret) goto clean_card;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.