// SPDX-License-Identifier: GPL-2.0-only /* * linux/sound/oss/dmasound/dmasound_paula.c * * Amiga `Paula' DMA Sound Driver * * See linux/sound/oss/dmasound/dmasound_core.c for copyright and credits * prior to 28/01/2001 * * 28/01/2001 [0.1] Iain Sandoe * - added versioning * - put in and populated the hardware_afmts field. * [0.2] - put in SNDCTL_DSP_GETCAPS value. * [0.3] - put in constraint on state buffer usage. * [0.4] - put in default hard/soft settings
*/
/* ++TeSche: radically changed for new expanding purposes... * * These two routines now deal with copying/expanding/translating the samples * from user space into our buffer at the right frequency. They take care about * how much data there's actually to read, how much buffer space there is and * to convert samples into the right frequency/encoding. They will only work on * complete samples so it may happen they leave some bytes in the input stream * if the user didn't write a multiple of the current sample size. They both * return the number of bytes they've used from both streams so you may detect * such a situation. Luckily all programs should be able to cope with that. * * I think I've optimized anything as far as one can do in plain C, all * variables should fit in registers and the loops are really short. There's * one loop for every possible situation. Writing a more generalized and thus * parameterized loop would only produce slower code. Feel free to optimize * this in assembler if you like. :) * * I think these routines belong here because they're not yet really hardware * independent, especially the fact that the Falcon can play 16bit samples * only in stereo is hardcoded in both of them! * * ++geert: split in even more functions (one per format)
*/
#ifdef MODULE staticvoid AmiIrqCleanUp(void)
{ /* turn off DMA for audio channels */
StopDMA(); /* release the interrupt */
free_irq(IRQ_AMIGA_AUD0, AmiInterrupt);
} #endif/* MODULE */
staticvoid AmiSilence(void)
{ /* turn off DMA for audio channels */
StopDMA();
}
staticvoid AmiInit(void)
{ int period, i;
AmiSilence();
if (dmasound.soft.speed)
period = amiga_colorclock/dmasound.soft.speed-1; else
period = amiga_audio_min_period;
dmasound.hard = dmasound.soft;
dmasound.trans_write = &transAmiga;
if (period < amiga_audio_min_period) { /* we would need to squeeze the sound, but we won't do that */
period = amiga_audio_min_period;
} elseif (period > 65535) {
period = 65535;
}
dmasound.hard.speed = amiga_colorclock/(period+1);
for (i = 0; i < 4; i++)
custom.aud[i].audper = period;
amiga_audio_period = period;
}
switch (format) { case AFMT_QUERY: return dmasound.soft.format; case AFMT_MU_LAW: case AFMT_A_LAW: case AFMT_U8: case AFMT_S8:
size = 8; break; case AFMT_S16_BE: case AFMT_U16_BE: case AFMT_S16_LE: case AFMT_U16_LE:
size = 16; break; default: /* :-) */
size = 8;
format = AFMT_S8;
}
/* used by AmiPlay() if all doubts whether there really is something * to be played are already wiped out.
*/
start = write_sq.buffers[write_sq.front];
size = (write_sq.count == index ? write_sq.rear_size
: write_sq.block_size)>>1;
disable_heartbeat();
custom.aud[0].audvol = dmasound.volume_left;
custom.aud[1].audvol = dmasound.volume_right; if (dmasound.hard.size == 8) {
custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
custom.aud[0].audlen = size;
custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
custom.aud[1].audlen = size;
custom.dmacon = AMI_AUDIO_8;
} else {
size >>= 1;
custom.aud[0].audlc = (u_short *)ZTWO_PADDR(ch0);
custom.aud[0].audlen = size;
custom.aud[1].audlc = (u_short *)ZTWO_PADDR(ch1);
custom.aud[1].audlen = size; if (dmasound.volume_left == 64 && dmasound.volume_right == 64) { /* We can play pseudo 14-bit only with the maximum volume */
ch3 = ch0+write_sq_block_size_quarter;
ch2 = ch1+write_sq_block_size_quarter;
custom.aud[2].audvol = 1; /* we are being affected by the beeps */
custom.aud[3].audvol = 1; /* restoring volume here helps a bit */
custom.aud[2].audlc = (u_short *)ZTWO_PADDR(ch2);
custom.aud[2].audlen = size;
custom.aud[3].audlc = (u_short *)ZTWO_PADDR(ch3);
custom.aud[3].audlen = size;
custom.dmacon = AMI_AUDIO_14;
} else {
custom.aud[2].audvol = 0;
custom.aud[3].audvol = 0;
custom.dmacon = AMI_AUDIO_8;
}
}
write_sq.front = (write_sq.front+1) % write_sq.max_count;
write_sq.active |= AMI_PLAY_LOADED;
}
staticvoid AmiPlay(void)
{ int minframes = 1;
custom.intena = IF_AUD0;
if (write_sq.active & AMI_PLAY_LOADED) { /* There's already a frame loaded */
custom.intena = IF_SETCLR | IF_AUD0; return;
}
if (write_sq.active & AMI_PLAY_PLAYING) /* Increase threshold: frame 1 is already being played */
minframes = 2;
if (write_sq.count < minframes) { /* Nothing to do */
custom.intena = IF_SETCLR | IF_AUD0; return;
}
if (write_sq.count <= minframes &&
write_sq.rear_size < write_sq.block_size && !write_sq.syncing) { /* hmmm, the only existing frame is not * yet filled and we're not syncing?
*/
custom.intena = IF_SETCLR | IF_AUD0; return;
}
if (!write_sq.active) { /* Playing was interrupted and sq_reset() has already cleared * the sq variables, so better don't do anything here.
*/
WAKE_UP(write_sq.sync_queue); return IRQ_HANDLED;
}
if (write_sq.active & AMI_PLAY_PLAYING) { /* We've just finished a frame */
write_sq.count--;
WAKE_UP(write_sq.action_queue);
}
if (write_sq.active & AMI_PLAY_LOADED) /* Increase threshold: frame 1 is already being played */
minframes = 2;
/* Shift the flags */
write_sq.active = (write_sq.active<<1) & AMI_PLAY_MASK;
if (!write_sq.active) /* No frame is playing, disable audio DMA */
StopDMA();
custom.intena = IF_SETCLR | IF_AUD0;
if (write_sq.count >= minframes) /* Try to play the next frame */
AmiPlay();
if (!write_sq.active) /* Nothing to play anymore.
Wake up a process waiting for audio output to drain. */
WAKE_UP(write_sq.sync_queue); return IRQ_HANDLED;
}
/* * amiga_audio_remove() lives in .exit.text. For drivers registered via * module_platform_driver_probe() this is ok because they cannot get unbound at * runtime. So mark the driver struct with __refdata to prevent modpost * triggering a section mismatch warning.
*/ staticstruct platform_driver amiga_audio_driver __refdata = {
.remove = __exit_p(amiga_audio_remove),
.driver = {
.name = "amiga-audio",
},
};
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.