Rev 1063 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/* Project: HARTIK 3.0 Sound Library */
/* Description: Hard Real TIme Kernel for 8086 compatible */
/* Author: Luca Abeni */
/* Date: 5/12/1997 */
/* File: Blaster.C */
/* Revision: 3.0 */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/* Sound Blaster 16 specific functions and structures */
#include <kernel/kern.h>
#include <drivers/dma.h>
#include "sbio.h"
#include "blaster.h"
#include <drivers/sound.h>
#include <semaphore.h>
struct sb_device sb_dev;
struct sound_buffer buff_des;
void sbmixer_reset(void)
{
sbmixer_write(sb_dev.BaseAddress, MIXER_RESET, 0);
}
/* Enable or disable specified mixer inputs */
void sbmixer_setinput(BYTE in, BYTE onoff)
{
BYTE inreg;
inreg = sbmixer_read(sb_dev.BaseAddress, MIXER_INCTRLEFT);
if (onoff == ENABLE)
inreg = inreg | in;
else inreg = inreg & (!in);
sbmixer_write(sb_dev.BaseAddress, MIXER_INCTRLEFT, inreg);
inreg = sbmixer_read(sb_dev.BaseAddress, MIXER_INCTRRIGHT);
if (onoff == ENABLE)
inreg = inreg | in;
else inreg = inreg & (!in);
sbmixer_write(sb_dev.BaseAddress, MIXER_INCTRRIGHT, inreg);
}
/* Enable or disable specified mixer outputs */
void sbmixer_setoutput(BYTE in, BYTE onoff)
{
BYTE inreg;
inreg = sbmixer_read(sb_dev.BaseAddress, MIXER_OUTCTR);
if (onoff == ENABLE)
inreg = inreg | in;
else inreg = inreg & (~in);
sbmixer_write(sb_dev.BaseAddress, MIXER_OUTCTR, inreg);
}
/* Set the output level */
void sbmixer_setmiclev(BYTE level)
{
BYTE levreg;
levreg = sbmixer_read(sb_dev.BaseAddress, MIXER_MICLEV);
levreg = (levreg & 0x07) | (level << 3);
sbmixer_write(sb_dev.BaseAddress, MIXER_MICLEV, levreg);
}
/* Set the mixer input level */
void sbmixer_setingainlev(BYTE level)
{
BYTE gainreg;
gainreg = sbmixer_read(sb_dev.BaseAddress, MIXER_IGCRIGHT);
gainreg = (gainreg & 0x3F) | (level << 6);
sbmixer_write(sb_dev.BaseAddress, MIXER_IGCRIGHT, gainreg);
gainreg = sbmixer_read(sb_dev.BaseAddress, MIXER_IGCLEFT);
gainreg = (gainreg & 0x3F) | (level << 6);
sbmixer_write(sb_dev.BaseAddress, MIXER_IGCLEFT, gainreg);
}
/* Enable or disable the mixer input Automatic Gain Control */
void sbmixer_setAGC(BYTE onoff)
{
BYTE reg;
reg = sbmixer_read(sb_dev.BaseAddress, MIXER_AGC);
if (onoff == ENABLE)
reg = reg & 0xFE;
else reg = reg | 0x01;
sbmixer_write(sb_dev.BaseAddress, MIXER_AGC, reg);
}
int sb_init(void)
{
BYTE cfgreg;
/* Is there a SB16 in the system? */
if((sb_dev.BaseAddress = sb_probe()) == 0)
return ENODEV;
/* Yes: get the DSP version and the used interrupt and DMA Channels */
sbdsp_write(sb_dev.BaseAddress, DSPCMD_GETVER);
sb_dev.DSPVersionM = sbdsp_read(sb_dev.BaseAddress);
sb_dev.DSPVersionm = sbdsp_read(sb_dev.BaseAddress);
cfgreg = sbmixer_read(sb_dev.BaseAddress, MIXER_IRQREG);
if (cfgreg & 1)
sb_dev.IntLine = 2;
if (cfgreg & 2)
sb_dev.IntLine = 5;
if (cfgreg & 4)
sb_dev.IntLine = 7;
if (cfgreg & 8)
sb_dev.IntLine = 10;
// force irq line if you use a SB PNP!!!
// sb_dev.IntLine = 9;
cfgreg = sbmixer_read(sb_dev.BaseAddress, MIXER_DMAREG);
if (cfgreg & 1)
sb_dev.DMA8Channel= 0;
if (cfgreg & 2)
sb_dev.DMA8Channel = 1;
if (cfgreg & 8)
sb_dev.DMA8Channel = 3;
if (cfgreg & 0x20)
sb_dev.DMA16Channel = 5;
if (cfgreg & 0x40)
sb_dev.DMA16Channel = 6;
if (cfgreg & 0x80)
sb_dev.DMA16Channel = 7;
return 0;
}
/* Show the card informations */
void sb_show(void)
{
cprintf(" BaseAddress: %x\n", (unsigned int)sb_dev.BaseAddress);
cprintf(" Interrupt Line: %d\n", sb_dev.IntLine);
cprintf(" DSP Version: %d.%d\n", sb_dev.DSPVersionM, sb_dev.DSPVersionm);
cprintf(" 8 bit DMA channel: %d\n", sb_dev.DMA8Channel);
cprintf(" 16 bit DMA channel: %d\n", sb_dev.DMA16Channel);
}
void sb_spkon (void)
{
sbdsp_write(sb_dev.BaseAddress, DSPCMD_SPKON);
}
void sb_spkoff (void)
{
sbdsp_write(sb_dev.BaseAddress, DSPCMD_SPKOFF);
}
/* Set the DMA sampling/playing rate */
void sb_setrate (int sps, BYTE i_o)
{
BYTE cmd;
if (i_o == IN) cmd = DSPCMD_SETINRATE;
else cmd = DSPCMD_SETOUTRATE;
sbdsp_write(sb_dev.BaseAddress, cmd);
/* MSB...*/
sbdsp_write(sb_dev.BaseAddress, (sps >> 8) & 0xFF);
/*...and then LSB */
sbdsp_write(sb_dev.BaseAddress, sps & 0xFF);
}
/* Start an 8 bit sampling/playing operation */
void sb_dmaop(BYTE i_o)
{
DWORD len;
BYTE cmd;
BYTE *buff;
buff = buff_des.sound_dma->p;
len = buff_des.sound_dma->len;
cmd = DSPCMD_8BITIO;
/* Stop any previous operation and reset the DMAC */
dma_stop(sb_dev.DMA8Channel);
dma_reset();
/* Prepare the DMAC for the operation */
if (i_o == OUT) {
cmd |= SBIO_OUT;
dma_setmode(sb_dev.DMA8Channel, 0x48);
} else {
cmd |= SBIO_IN;
dma_setmode(sb_dev.DMA8Channel, 0x44);
}
dma_setbuff(sb_dev.DMA8Channel, buff, len);
dma_start(sb_dev.DMA8Channel);
/*...and start it!!! */
sbdsp_write(sb_dev.BaseAddress, cmd);
sbdsp_write(sb_dev.BaseAddress, IOMODE_UNSIGNED | IOMODE_MONO);
/* LSB...*/
sbdsp_write(sb_dev.BaseAddress, (BYTE)(len & 0xFF));
/*...and then MSB */
sbdsp_write(sb_dev.BaseAddress, (BYTE)((len >> 8) & 0xFF));
}
/* Start a 16 bit sampling/playing operation */
void sb_dma16op(BYTE i_o)
{
DWORD len;
BYTE cmd;
BYTE *buff;
buff = buff_des.sound_dma->p;
len = buff_des.sound_dma->len;
cmd = DSPCMD_16BITIO;
/* Stop any previous operation and reset the DMAC */
dma16_stop(sb_dev.DMA16Channel);
dma16_reset();
if (i_o == OUT) {
cmd |= SBIO_OUT;
dma16_setmode(sb_dev.DMA16Channel, 0x48);
} else {
cmd |= SBIO_IN;
dma16_setmode(sb_dev.DMA16Channel, 0x44);
}
dma16_setbuff(sb_dev.DMA16Channel, buff, len);
dma16_start(sb_dev.DMA16Channel);
/*...and start it!!! */
sbdsp_write(sb_dev.BaseAddress, cmd);
sbdsp_write(sb_dev.BaseAddress, IOMODE_SIGNED | IOMODE_MONO);
/* LSB...*/
sbdsp_write(sb_dev.BaseAddress, (BYTE)(len & 0xFF));
/*...and then MSB */
sbdsp_write(sb_dev.BaseAddress, (BYTE)((len >> 8) & 0xFF));
}
/* Start an 8 bit sampling/playing operation using double buffer */
void sb_dmabuffop(BYTE i_o)
{
DWORD len;
BYTE cmd;
struct dma_buff *buff;
buff = buff_des.sound_dma;
cmd = DSPCMD_8BITIO | SBIO_AUTOINIT;
len = buff->len;
if (i_o == OUT) {
cmd |= SBIO_OUT;
dma_out(sb_dev.DMA8Channel, buff);
} else {
cmd |= SBIO_IN;
dma_in(sb_dev.DMA8Channel, buff);
}
sbdsp_write(sb_dev.BaseAddress, cmd);
sbdsp_write(sb_dev.BaseAddress, IOMODE_UNSIGNED | IOMODE_MONO);
sbdsp_write(sb_dev.BaseAddress, (BYTE)(((buff->dma_bufflen >> 1) - 1) & 0xFF));
sbdsp_write(sb_dev.BaseAddress, (BYTE)((((buff->dma_bufflen >> 1) - 1) >> 8) & 0xFF));
}
/* Start a 16 bit sampling/playing operation using double buffer */
void sb_dma16buffop(BYTE i_o)
{
DWORD len;
BYTE cmd;
struct dma_buff *buff;
buff = buff_des.sound_dma;
cmd = DSPCMD_16BITIO | SBIO_AUTOINIT;
len = buff->len;
if (i_o == OUT) {
cmd |= SBIO_OUT;
dma16_out(sb_dev.DMA16Channel, buff);
} else {
cmd |= SBIO_IN;
dma16_in(sb_dev.DMA16Channel, buff);
}
sbdsp_write(sb_dev.BaseAddress, cmd);
sbdsp_write(sb_dev.BaseAddress, IOMODE_SIGNED | IOMODE_MONO);
sbdsp_write(sb_dev.BaseAddress, (BYTE)(((buff->dma_bufflen >> 2) - 1) & 0xFF));
sbdsp_write(sb_dev.BaseAddress, (BYTE)((((buff->dma_bufflen >> 2) - 1) >> 8) & 0xFF));
}
void sb_stopdsp(BYTE bit)
{
if (bit == 8)
sbdsp_write(sb_dev.BaseAddress, DSPCMD_EXIT8);
else if (bit == 16)
sbdsp_write(sb_dev.BaseAddress, DSPCMD_EXIT16);
else cprintf("How many bits?\n");
}
/* Sound Blaster 16 interrupt's Fast Handler */
void sb_handler(int n)
{
switch(sbmixer_read(sb_dev.BaseAddress, MIXER_INTSTATUS) & 3) {
case 1: ll_in(sb_dev.BaseAddress + ACK8);
break;
case 2: ll_in(sb_dev.BaseAddress + ACK16);
break;
default: cprintf("Unknown int: %x\n", sbmixer_read(sb_dev.BaseAddress, 0x82));
}
}
/*
This process is activated when the sound card generates an interrupt:
it calls the function to copy data from/to the sound buffer (this function
can be the double buffering-function or the user's self-buffering function
*/
TASK sb_proc(void)
{
for(;;) {
/* call the copy function */
switch(buff_des.fun(buff_des.sound_dma)) {
/* the operation will finish with the next interrupt */
case 1: buff_des.fun = dummyfun1;
break;
/* operation finished */
case 2: dummyfun2(buff_des.sound_dma);
buff_des.fun = dummyfun2;
break;
default: break;
}
task_endcycle();
}
return 0;
}
/* PIO mode sampling process */
TASK proc_sample()
{
DWORD i;
sbdsp_reset(sb_dev.BaseAddress);
for(i = 0; i < buff_des.sound_dma->len; i++) {
sbdsp_write(sb_dev.BaseAddress, DSPCMD_DIRECTIN);
buff_des.sound_dma->p[i] = sbdsp_read(sb_dev.BaseAddress);
task_endcycle();
}
/* Operation finished: if it was blocking signal */
if (buff_des.synch) sem_post(&buff_des.synchr);
//task_abort();
return 0;
}
/* PIO mode playing process */
TASK proc_play()
{
DWORD i;
sbdsp_reset(sb_dev.BaseAddress);
for(i = 0; i < buff_des.sound_dma->len; i++) {
sbdsp_write(sb_dev.BaseAddress, DSPCMD_DIRECTOUT);
sbdsp_write(sb_dev.BaseAddress, buff_des.sound_dma->p[i]);
task_endcycle();
}
/* Operation finished: if it was blocking signal */
if (buff_des.synch) sem_post(&buff_des.synchr);
//task_abort();
return 0;
}