Bring in ad1816 patches from German Tischler.
Fix 'device not configured' problem that people were experiencing when only PCI devices are present.
This commit is contained in:
parent
30ad5c00bb
commit
ca08832936
@ -79,6 +79,25 @@ static void ad_write(snddev_info *d, int reg, u_char data);
|
||||
static void ad_write_cnt(snddev_info *d, int reg, u_short data);
|
||||
static int ad_read(snddev_info *d, int reg);
|
||||
|
||||
/* ad1816 prototypes */
|
||||
|
||||
/* IO primitives */
|
||||
static int ad1816_wait_init(snddev_info * d, int x);
|
||||
static unsigned short ad1816_read(snddev_info * d, unsigned int reg);
|
||||
static void ad1816_write(snddev_info * d, unsigned int reg, unsigned short data);
|
||||
/* intr and callback functions */
|
||||
static irq_proc_t ad1816_intr;
|
||||
static snd_callback_t ad1816_callback;
|
||||
/* device specific ioctl calls */
|
||||
static d_ioctl_t ad1816_ioctl;
|
||||
/* parameter set functions */
|
||||
static void ad1816_reinit(snddev_info * d);
|
||||
static int ad1816_mixer_set(snddev_info * d, int dev, int value);
|
||||
static int ad1816_set_recsrc(snddev_info * d, int mask);
|
||||
static void ad1816_mixer_reset(snddev_info * d);
|
||||
|
||||
/* ad1816 prototypes end */
|
||||
|
||||
/*
|
||||
* device descriptors for the boards supported by this module.
|
||||
*/
|
||||
@ -183,6 +202,31 @@ mss_probe_end:
|
||||
return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_attach(struct isa_device *dev)
|
||||
{
|
||||
snddev_info *d = &(pcm_info[dev->id_unit]);
|
||||
|
||||
dev->id_alive = 16; /* number of io ports */
|
||||
|
||||
if (FULL_DUPLEX(d))
|
||||
d->audio_fmt |= AFMT_FULLDUPLEX;
|
||||
|
||||
ad1816_write(d, 1, 0x2);/* disable interrupts */
|
||||
ad1816_write(d, 32, 0x90F0); /* SoundSystem Mode, split format */
|
||||
|
||||
ad1816_write(d, 5, 0x8080); /* FM volume mute */
|
||||
ad1816_write(d, 6, 0x8080); /* I2S1 volume mute */
|
||||
ad1816_write(d, 7, 0x8080); /* I2S0 volume mute */
|
||||
ad1816_write(d, 17, 0x8888); /* VID Volume mute */
|
||||
ad1816_write(d, 20, 0x5050); /* Source select Mic & auto gain ctrl
|
||||
* off */
|
||||
/* adc gain is set to 0 */
|
||||
ad1816_reinit(d);
|
||||
ad1816_mixer_reset(d);
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*
|
||||
* the address passed as io_base for mss_attach is also the old
|
||||
* MSS base address (e.g. 0x530). The codec is four locations ahead.
|
||||
@ -199,6 +243,9 @@ mss_attach(struct isa_device *dev)
|
||||
d->name, dev->id_unit,
|
||||
d->io_base, d->irq, d->dbuf_out.chan, d->dbuf_in.chan, dev->id_flags);
|
||||
|
||||
if (d->bd_id == MD_AD1816)
|
||||
return ad1816_attach(dev);
|
||||
|
||||
dev->id_alive = 8 ; /* number of io ports */
|
||||
/* should be already set but just in case... */
|
||||
|
||||
@ -374,7 +421,11 @@ mss_close(dev_t dev, int flags, int mode, struct proc * p)
|
||||
d->flags |= SND_F_CLOSING ;
|
||||
splx(s); /* is this ok here ? */
|
||||
snd_flush(d);
|
||||
outb(io_Status(d), 0); /* Clear interrupt status */
|
||||
/* Clear interrupt status */
|
||||
if ( d->bd_id == MD_AD1816 )
|
||||
outb(ad1816_int(d), 0);
|
||||
else
|
||||
outb(io_Status(d), 0);
|
||||
d->flags = 0 ;
|
||||
}
|
||||
return 0 ;
|
||||
@ -1370,11 +1421,12 @@ mss_reinit(snddev_info *d)
|
||||
#if NPNP > 0
|
||||
|
||||
static char * cs423x_probe(u_long csn, u_long vend_id);
|
||||
static void cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
static void
|
||||
cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
struct isa_device *dev);
|
||||
|
||||
static struct pnp_device cs423x = {
|
||||
"CS423x/Yamaha",
|
||||
"CS423x/Yamaha/AD1816",
|
||||
cs423x_probe,
|
||||
cs423x_attach,
|
||||
&nsnd, /* use this for all sound cards */
|
||||
@ -1405,6 +1457,10 @@ cs423x_probe(u_long csn, u_long vend_id)
|
||||
s = "Yamaha YMF719 OPL-SA3";
|
||||
else if (vend_id == 0x8140d315)
|
||||
s = "SoundscapeVIVO";
|
||||
else if (vend_id == 0x1114b250)
|
||||
s = "Terratec Soundsystem BASE 1";
|
||||
else if (vend_id == 0x50719304)
|
||||
s = "Generic AD1815";
|
||||
if (s) {
|
||||
struct pnp_cinfo d;
|
||||
read_pnp_parms(&d, 0);
|
||||
@ -1433,7 +1489,26 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
return ;
|
||||
}
|
||||
snddev_last_probed = &tmp_d;
|
||||
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
|
||||
|
||||
/* AD1816 */
|
||||
if (vend_id == 0x1114b250 || vend_id == 0x50719304) {
|
||||
dev->id_alive = 16; /* number of io ports ? */
|
||||
|
||||
tmp_d = mss_op_desc; /* copy it */
|
||||
|
||||
tmp_d.ioctl = ad1816_ioctl;
|
||||
tmp_d.isr = ad1816_intr;
|
||||
tmp_d.callback = ad1816_callback;
|
||||
tmp_d.audio_fmt = AFMT_STEREO | AFMT_U8 |
|
||||
AFMT_A_LAW | AFMT_MU_LAW |
|
||||
AFMT_S16_LE | AFMT_S16_BE;
|
||||
|
||||
dev->id_iobase = d.port[2];
|
||||
tmp_d.alt_base = d.port[0]; /* soundblaster comp. but we don't
|
||||
* use that */
|
||||
tmp_d.bd_id = MD_AD1816;
|
||||
strcpy(tmp_d.name, name);
|
||||
} else if (d.flags & DV_PNP_SBCODEC) { /* use sb-compatible codec */
|
||||
dev->id_alive = 16 ; /* number of io ports ? */
|
||||
tmp_d = sb_op_desc ;
|
||||
if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
|
||||
@ -1484,12 +1559,14 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
break;
|
||||
|
||||
default:
|
||||
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
|
||||
tmp_d.bd_id = MD_CS4232; /* to short-circuit the
|
||||
* detect routine */
|
||||
break;
|
||||
}
|
||||
snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name);
|
||||
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
|
||||
}
|
||||
|
||||
write_pnp_parms( &d, ldn );
|
||||
enable_pnp_card();
|
||||
|
||||
@ -1814,5 +1891,440 @@ gus_mem_cfg(snddev_info *d)
|
||||
}
|
||||
#endif /* gus mem cfg... */
|
||||
|
||||
static int
|
||||
ad1816_ioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
|
||||
{
|
||||
snddev_info *d;
|
||||
int unit;
|
||||
|
||||
dev = minor(dev);
|
||||
unit = dev >> 4;
|
||||
d = &pcm_info[unit];
|
||||
|
||||
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
|
||||
cmd &= 0xff;
|
||||
if (cmd == SOUND_MIXER_RECSRC)
|
||||
return ad1816_set_recsrc(d, *(int *) arg);
|
||||
else
|
||||
return ad1816_mixer_set(d, cmd, *(int *) arg);
|
||||
}
|
||||
switch (cmd) { /* driver specific ioctls other than mixer
|
||||
* calls */
|
||||
/* ad1816 has special features */
|
||||
case AIOGCAP: /* get capabilities */
|
||||
{
|
||||
snd_capabilities *p = (snd_capabilities *) arg;
|
||||
p->rate_min = 4000;
|
||||
p->rate_max = 55200;
|
||||
p->bufsize = d->bufsize;
|
||||
p->formats = d->audio_fmt;
|
||||
p->mixers = 1;
|
||||
p->inputs = d->mix_devs;
|
||||
p->left = p->right = 100;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return ENOSYS; /* fallback to default */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_callback(snddev_info * d, int reason)
|
||||
{
|
||||
int wr, cnt;
|
||||
|
||||
wr = reason & SND_CB_WR;
|
||||
reason &= SND_CB_REASON_MASK;
|
||||
|
||||
switch (reason) {
|
||||
case SND_CB_INIT:
|
||||
ad1816_reinit(d);
|
||||
reset_dbuf(&(d->dbuf_in), SND_CHAN_RD);
|
||||
reset_dbuf(&(d->dbuf_out), SND_CHAN_WR);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case SND_CB_START:
|
||||
cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl;
|
||||
|
||||
cnt /= 4;
|
||||
cnt--;
|
||||
|
||||
/* start only if not already running */
|
||||
if (wr && !(inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
/* set dma counter */
|
||||
ad1816_write(d, 8, cnt); /* playback count */
|
||||
/* int enable */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) | 0x8000);
|
||||
/* enable playback */
|
||||
outb(ad1816_play(d), (inb(ad1816_play(d)) | AD1816_ENABLE));
|
||||
/* check if we succeeded */
|
||||
if (!(inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to start write (playback) DMA !\n");
|
||||
}
|
||||
} else if (!wr && !(inb(ad1816_capt(d)) & AD1816_ENABLE)) {
|
||||
/* same for capture */
|
||||
ad1816_write(d, 10, cnt); /* capture count */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) | 0x4000); /* int */
|
||||
outb(ad1816_capt(d), (inb(ad1816_capt(d)) | AD1816_ENABLE)); /* CEN */
|
||||
if (!(inb(ad1816_capt(d)) & AD1816_ENABLE)) { /* check */
|
||||
printf("ad1816: failed to start read (capture) DMA !\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_CB_STOP:
|
||||
case SND_CB_ABORT: /* XXX check this... */
|
||||
/* we don't test here if it is running... */
|
||||
if (wr) {
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) & ~0x8000);
|
||||
/* disable int */
|
||||
outb(ad1816_play(d), (inb(ad1816_play(d)) & ~AD1816_ENABLE));
|
||||
/* disable playback */
|
||||
if ((inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to stop write (playback) DMA !\n");
|
||||
}
|
||||
ad1816_write(d, 8, 0); /* reset base counter */
|
||||
ad1816_write(d, 9, 0); /* reset cur counter */
|
||||
} else {
|
||||
/* same for capture */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) & ~0x4000);
|
||||
outb(ad1816_capt(d), (inb(ad1816_capt(d)) & ~AD1816_ENABLE));
|
||||
if ((inb(ad1816_capt(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to stop read (capture) DMA !\n");
|
||||
}
|
||||
ad1816_write(d, 10, 0);
|
||||
ad1816_write(d, 11, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_intr(int unit)
|
||||
{
|
||||
snddev_info *d = &pcm_info[unit];
|
||||
unsigned char c, served = 0;
|
||||
|
||||
/* get interupt status */
|
||||
c = inb(ad1816_int(d));
|
||||
|
||||
/* check for stray interupts */
|
||||
if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
|
||||
printf("ad1816: Stray interrupt 0x%x.\n", c);
|
||||
c = c & (AD1816_INTRCI | AD1816_INTRPI);
|
||||
outb(ad1816_int(d), c); /* ack it anyway */
|
||||
}
|
||||
/* check for capture interupt */
|
||||
if (d->dbuf_in.dl && (c & AD1816_INTRCI)) {
|
||||
outb(ad1816_int(d), c & ~AD1816_INTRCI); /* ack it */
|
||||
if (inb(ad1816_int(d)) & AD1816_INTRCI)
|
||||
printf("ad1816: Failed to clear cp int !!!\n");
|
||||
dsp_rdintr(d);
|
||||
served |= AD1816_INTRCI; /* cp served */
|
||||
}
|
||||
/* check for playback interupt */
|
||||
if (d->dbuf_out.dl && (c & AD1816_INTRPI)) {
|
||||
outb(ad1816_int(d), c & ~AD1816_INTRPI); /* ack it */
|
||||
if (inb(ad1816_int(d)) & AD1816_INTRPI != 0)
|
||||
printf("ad1816: Failed to clear pb int !!!\n");
|
||||
dsp_wrintr(d);
|
||||
served |= AD1816_INTRPI; /* pb served */
|
||||
}
|
||||
if (served == 0) {
|
||||
/* this probably means this is not a (working) ad1816 chip, */
|
||||
/* or an error in dma handling */
|
||||
printf("ad1816: raised an interrupt without reason 0x%x.\n", c);
|
||||
outb(ad1816_int(d), 0); /* Clear interrupt status anyway */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_wait_init(snddev_info * d, int x)
|
||||
{
|
||||
int n = 0; /* to shut up the compiler... */
|
||||
|
||||
for (; x--;)
|
||||
if (((n = (inb(ad1816_ale(d)) & AD1816_BUSY))) == 0)
|
||||
DELAY(10);
|
||||
else
|
||||
return n;
|
||||
printf("ad1816_wait_init failed 0x%02x.\n", inb(ad1816_ale(d)));
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned short
|
||||
ad1816_read(snddev_info * d, unsigned int reg)
|
||||
{
|
||||
int flags;
|
||||
u_short x;
|
||||
|
||||
/* we don't want to be blocked here */
|
||||
flags = spltty();
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_read: chip timeout before read.\n");
|
||||
return 0;
|
||||
}
|
||||
outb(ad1816_ale(d), (u_char) 0);
|
||||
outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK));
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_read: chip timeout during read.\n");
|
||||
return 0;
|
||||
}
|
||||
x = (inb(ad1816_high(d)) << 8) | inb(ad1816_low(d));
|
||||
splx(flags);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_write(snddev_info * d, unsigned int reg, unsigned short data)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = spltty();
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_write: chip timeout before write.\n");
|
||||
return;
|
||||
}
|
||||
outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK));
|
||||
outb(ad1816_low(d), (u_char) (data & 0x000000ff));
|
||||
outb(ad1816_high(d), (u_char) ((data & 0x0000ff00) >> 8));
|
||||
splx(flags);
|
||||
}
|
||||
|
||||
#if 0 /* unused right now..., and untested... */
|
||||
static void
|
||||
ad1816_mute(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 14, ad1816_read(d, 14) | 0x8000 | 0x80);
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_unmute(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 14, ad1816_read(d, 14) & ~(0x8000 | 0x80));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* only one rec source is possible */
|
||||
|
||||
static int
|
||||
ad1816_set_recsrc(snddev_info * d, int mask)
|
||||
{
|
||||
mask &= d->mix_rec_devs;
|
||||
|
||||
switch (mask) {
|
||||
case SOUND_MASK_LINE:
|
||||
case SOUND_MASK_LINE3:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x0000);
|
||||
break;
|
||||
|
||||
case SOUND_MASK_CD:
|
||||
case SOUND_MASK_LINE1:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x2020);
|
||||
break;
|
||||
|
||||
case SOUND_MASK_MIC:
|
||||
default:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x5050);
|
||||
}
|
||||
|
||||
d->mix_recsrc = mask;
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
#define AD1816_MUTE 31 /* value for mute */
|
||||
|
||||
static int
|
||||
ad1816_mixer_set(snddev_info * d, int dev, int value)
|
||||
{
|
||||
u_char left = (value & 0x000000ff);
|
||||
u_char right = (value & 0x0000ff00) >> 8;
|
||||
u_short reg = 0;
|
||||
|
||||
if (dev > 31)
|
||||
return EINVAL;
|
||||
|
||||
if (!(d->mix_devs & (1 << dev)))
|
||||
return EINVAL;
|
||||
|
||||
if (left > 100)
|
||||
left = 100;
|
||||
if (right > 100)
|
||||
right = 100;
|
||||
|
||||
d->mix_levels[dev] = left | (right << 8);
|
||||
|
||||
/* Scale volumes */
|
||||
left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
|
||||
right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
|
||||
|
||||
reg = (left << 8) | right;
|
||||
|
||||
/* do channel selective muting if volume is zero */
|
||||
if (left == AD1816_MUTE)
|
||||
reg |= 0x8000;
|
||||
if (right == AD1816_MUTE)
|
||||
reg |= 0x0080;
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME: /* Register 14 master volume */
|
||||
ad1816_write(d, 14, reg);
|
||||
break;
|
||||
case SOUND_MIXER_CD: /* Register 15 cd */
|
||||
case SOUND_MIXER_LINE1:
|
||||
ad1816_write(d, 15, reg);
|
||||
break;
|
||||
case SOUND_MIXER_SYNTH: /* Register 16 synth */
|
||||
ad1816_write(d, 16, reg);
|
||||
break;
|
||||
case SOUND_MIXER_PCM: /* Register 4 pcm */
|
||||
ad1816_write(d, 4, reg);
|
||||
break;
|
||||
case SOUND_MIXER_LINE:
|
||||
case SOUND_MIXER_LINE3: /* Register 18 line in */
|
||||
ad1816_write(d, 18, reg);
|
||||
break;
|
||||
case SOUND_MIXER_MIC: /* Register 19 mic volume */
|
||||
ad1816_write(d, 19, reg & ~0xff); /* mic is mono */
|
||||
break;
|
||||
case SOUND_MIXER_IGAIN:
|
||||
/* and now to something completely different ... */
|
||||
ad1816_write(d, 20, ((ad1816_read(d, 20) & ~0x0f0f)
|
||||
| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
|
||||
| ((AD1816_MUTE - right) / 2)));
|
||||
break;
|
||||
default:
|
||||
printf("ad1816_mixer_set(): unknown device.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_mixer_reset(snddev_info * d)
|
||||
{
|
||||
int i;
|
||||
|
||||
d->mix_devs = AD1816_MIXER_DEVICES;
|
||||
d->mix_rec_devs = AD1816_REC_DEVICES;
|
||||
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
||||
if (d->mix_devs & (1 << i))
|
||||
ad1816_mixer_set(d, i, default_mixer_levels[i]);
|
||||
ad1816_set_recsrc(d, SOUND_MASK_MIC);
|
||||
}
|
||||
|
||||
/* Set the playback and capture rates. */
|
||||
|
||||
static int
|
||||
ad1816_speed(snddev_info * d)
|
||||
{
|
||||
RANGE(d->play_speed,4000,55200);
|
||||
RANGE(d->rec_speed,4000,55200);
|
||||
|
||||
ad1816_write(d, 2, d->play_speed);
|
||||
ad1816_write(d, 3, d->rec_speed);
|
||||
|
||||
return d->play_speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* ad1816_format checks that the format is supported (or defaults to AFMT_U8)
|
||||
* and sets the chip to the desired format.
|
||||
*/
|
||||
|
||||
static int
|
||||
ad1816_format(snddev_info * d)
|
||||
{
|
||||
int oldplay =inb(ad1816_play(d)) & ~AD1816_FORMASK;
|
||||
int oldrec = inb(ad1816_capt(d)) & ~AD1816_FORMASK;
|
||||
int play = (d->play_fmt & d->audio_fmt) ? d->play_fmt : AFMT_U8;
|
||||
int rec = (d->rec_fmt & d->audio_fmt) ? d->rec_fmt : AFMT_U8;
|
||||
|
||||
/*
|
||||
* check that arg is one of the supported formats in d->format; otherwise
|
||||
* fallback to AFMT_U8
|
||||
*/
|
||||
|
||||
switch (play) {
|
||||
case AFMT_A_LAW:
|
||||
outb(ad1816_play(d), oldplay | AD1816_ALAW);
|
||||
break;
|
||||
case AFMT_MU_LAW:
|
||||
outb(ad1816_play(d), oldplay | AD1816_MULAW);
|
||||
break;
|
||||
case AFMT_S16_LE:
|
||||
outb(ad1816_play(d), oldplay | AD1816_S16LE);
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
outb(ad1816_play(d), oldplay | AD1816_S16BE);
|
||||
break;
|
||||
default:
|
||||
/* unlikely to happen */
|
||||
printf("ad1816: unknown play format. defaulting to U8.\n");
|
||||
case AFMT_U8:
|
||||
outb(ad1816_play(d), oldplay | AD1816_U8);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rec) {
|
||||
case AFMT_A_LAW:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_ALAW);
|
||||
break;
|
||||
case AFMT_MU_LAW:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_MULAW);
|
||||
break;
|
||||
case AFMT_S16_LE:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_S16LE);
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_S16BE);
|
||||
break;
|
||||
default:
|
||||
printf("ad1816: unknown capture format. defaulting to U8.\n");
|
||||
case AFMT_U8:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_U8);
|
||||
break;
|
||||
}
|
||||
|
||||
d->play_fmt = play;
|
||||
d->rec_fmt = rec;
|
||||
|
||||
return (play);
|
||||
}
|
||||
|
||||
/*
|
||||
* ad1816_reinit resets codec registers
|
||||
*/
|
||||
static void
|
||||
ad1816_reinit(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 8, 0x0000); /* reset base and current counter */
|
||||
ad1816_write(d, 9, 0x0000); /* for playback and capture */
|
||||
ad1816_write(d, 10, 0x0000);
|
||||
ad1816_write(d, 11, 0x0000);
|
||||
|
||||
if (d->flags & SND_F_STEREO) {
|
||||
outb((ad1816_play(d)), AD1816_STEREO); /* set playback to stereo */
|
||||
outb((ad1816_capt(d)), AD1816_STEREO); /* set capture to stereo */
|
||||
} else {
|
||||
outb((ad1816_play(d)), 0x00); /* set playback to mono */
|
||||
outb((ad1816_capt(d)), 0x00); /* set capture to mono */
|
||||
}
|
||||
|
||||
ad1816_format(d);
|
||||
ad1816_speed(d);
|
||||
|
||||
snd_set_blocksize(d); /* update blocksize if user did not force it */
|
||||
}
|
||||
|
||||
#endif /* NPNP > 0 */
|
||||
#endif /* NPCM > 0 */
|
||||
|
@ -105,6 +105,55 @@ ahead.
|
||||
#define BD_F_IRQ_OK 0x0002
|
||||
#define BD_F_TMR_RUN 0x0004
|
||||
|
||||
/* AD1816 register macros */
|
||||
|
||||
#define ad1816_ale(d) ((d)->io_base+0) /* indirect reg access */
|
||||
#define ad1816_int(d) ((d)->io_base+1) /* interupt status */
|
||||
#define ad1816_low(d) ((d)->io_base+2) /* indirect low byte */
|
||||
#define ad1816_high(d) ((d)->io_base+3) /* indirect high byte */
|
||||
/* unused */
|
||||
#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */
|
||||
#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */
|
||||
#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */
|
||||
/* end of unused */
|
||||
/* values for playback/capture config:
|
||||
bits: 0 enable/disable
|
||||
1 pio/dma
|
||||
2 stereo/mono
|
||||
3 companded/linearPCM
|
||||
4-5 format : 00 8bit linear (uncomp)
|
||||
00 8bit mulaw (comp)
|
||||
01 16bit le (uncomp)
|
||||
01 8bit alaw (comp)
|
||||
11 16bit be (uncomp)
|
||||
*/
|
||||
#define ad1816_play(d) ((d)->io_base+8) /* playback config */
|
||||
#define ad1816_capt(d) ((d)->io_base+9) /* capture config */
|
||||
|
||||
#define AD1816_BUSY 0x80 /* chip is busy */
|
||||
#define AD1816_ALEMASK 0x3F /* mask for indirect adr. */
|
||||
/* unusud */
|
||||
#define AD1816_INTRSI 0x01 /* sb intr */
|
||||
#define AD1816_INTRGI 0x02 /* game intr */
|
||||
#define AD1816_INTRRI 0x04 /* ring intr */
|
||||
#define AD1816_INTRDI 0x08 /* dsp intr */
|
||||
#define AD1816_INTRVI 0x10 /* vol intr */
|
||||
#define AD1816_INTRTI 0x20 /* timer intr */
|
||||
/* used again */
|
||||
#define AD1816_INTRCI 0x40 /* capture intr */
|
||||
#define AD1816_INTRPI 0x80 /* playback intr */
|
||||
/* PIO stuff is not supplied here */
|
||||
/* playback / capture config */
|
||||
#define AD1816_ENABLE 0x01 /* enable pl/cp */
|
||||
#define AD1816_PIO 0x02 /* use pio */
|
||||
#define AD1816_STEREO 0x04
|
||||
#define AD1816_COMP 0x08 /* data is companded */
|
||||
#define AD1816_U8 0x00 /* 8 bit linear pcm */
|
||||
#define AD1816_MULAW 0x08 /* 8 bit mulaw */
|
||||
#define AD1816_ALAW 0x18 /* 8 bit alaw */
|
||||
#define AD1816_S16LE 0x10 /* 16 bit linear little endian */
|
||||
#define AD1816_S16BE 0x30 /* 16 bit linear big endian */
|
||||
#define AD1816_FORMASK 0x38 /* format mask */
|
||||
|
||||
/*
|
||||
* sound/ad1848_mixer.h
|
||||
@ -225,6 +274,12 @@ MIX_NONE(SOUND_MIXER_LINE3),
|
||||
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
|
||||
SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
|
||||
|
||||
#define AD1816_REC_DEVICES \
|
||||
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
|
||||
|
||||
#define AD1816_MIXER_DEVICES \
|
||||
(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH | \
|
||||
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN)
|
||||
|
||||
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
|
||||
0x5a5a, /* Master Volume */
|
||||
|
@ -79,6 +79,25 @@ static void ad_write(snddev_info *d, int reg, u_char data);
|
||||
static void ad_write_cnt(snddev_info *d, int reg, u_short data);
|
||||
static int ad_read(snddev_info *d, int reg);
|
||||
|
||||
/* ad1816 prototypes */
|
||||
|
||||
/* IO primitives */
|
||||
static int ad1816_wait_init(snddev_info * d, int x);
|
||||
static unsigned short ad1816_read(snddev_info * d, unsigned int reg);
|
||||
static void ad1816_write(snddev_info * d, unsigned int reg, unsigned short data);
|
||||
/* intr and callback functions */
|
||||
static irq_proc_t ad1816_intr;
|
||||
static snd_callback_t ad1816_callback;
|
||||
/* device specific ioctl calls */
|
||||
static d_ioctl_t ad1816_ioctl;
|
||||
/* parameter set functions */
|
||||
static void ad1816_reinit(snddev_info * d);
|
||||
static int ad1816_mixer_set(snddev_info * d, int dev, int value);
|
||||
static int ad1816_set_recsrc(snddev_info * d, int mask);
|
||||
static void ad1816_mixer_reset(snddev_info * d);
|
||||
|
||||
/* ad1816 prototypes end */
|
||||
|
||||
/*
|
||||
* device descriptors for the boards supported by this module.
|
||||
*/
|
||||
@ -183,6 +202,31 @@ mss_probe_end:
|
||||
return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_attach(struct isa_device *dev)
|
||||
{
|
||||
snddev_info *d = &(pcm_info[dev->id_unit]);
|
||||
|
||||
dev->id_alive = 16; /* number of io ports */
|
||||
|
||||
if (FULL_DUPLEX(d))
|
||||
d->audio_fmt |= AFMT_FULLDUPLEX;
|
||||
|
||||
ad1816_write(d, 1, 0x2);/* disable interrupts */
|
||||
ad1816_write(d, 32, 0x90F0); /* SoundSystem Mode, split format */
|
||||
|
||||
ad1816_write(d, 5, 0x8080); /* FM volume mute */
|
||||
ad1816_write(d, 6, 0x8080); /* I2S1 volume mute */
|
||||
ad1816_write(d, 7, 0x8080); /* I2S0 volume mute */
|
||||
ad1816_write(d, 17, 0x8888); /* VID Volume mute */
|
||||
ad1816_write(d, 20, 0x5050); /* Source select Mic & auto gain ctrl
|
||||
* off */
|
||||
/* adc gain is set to 0 */
|
||||
ad1816_reinit(d);
|
||||
ad1816_mixer_reset(d);
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*
|
||||
* the address passed as io_base for mss_attach is also the old
|
||||
* MSS base address (e.g. 0x530). The codec is four locations ahead.
|
||||
@ -199,6 +243,9 @@ mss_attach(struct isa_device *dev)
|
||||
d->name, dev->id_unit,
|
||||
d->io_base, d->irq, d->dbuf_out.chan, d->dbuf_in.chan, dev->id_flags);
|
||||
|
||||
if (d->bd_id == MD_AD1816)
|
||||
return ad1816_attach(dev);
|
||||
|
||||
dev->id_alive = 8 ; /* number of io ports */
|
||||
/* should be already set but just in case... */
|
||||
|
||||
@ -374,7 +421,11 @@ mss_close(dev_t dev, int flags, int mode, struct proc * p)
|
||||
d->flags |= SND_F_CLOSING ;
|
||||
splx(s); /* is this ok here ? */
|
||||
snd_flush(d);
|
||||
outb(io_Status(d), 0); /* Clear interrupt status */
|
||||
/* Clear interrupt status */
|
||||
if ( d->bd_id == MD_AD1816 )
|
||||
outb(ad1816_int(d), 0);
|
||||
else
|
||||
outb(io_Status(d), 0);
|
||||
d->flags = 0 ;
|
||||
}
|
||||
return 0 ;
|
||||
@ -1370,11 +1421,12 @@ mss_reinit(snddev_info *d)
|
||||
#if NPNP > 0
|
||||
|
||||
static char * cs423x_probe(u_long csn, u_long vend_id);
|
||||
static void cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
static void
|
||||
cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
struct isa_device *dev);
|
||||
|
||||
static struct pnp_device cs423x = {
|
||||
"CS423x/Yamaha",
|
||||
"CS423x/Yamaha/AD1816",
|
||||
cs423x_probe,
|
||||
cs423x_attach,
|
||||
&nsnd, /* use this for all sound cards */
|
||||
@ -1405,6 +1457,10 @@ cs423x_probe(u_long csn, u_long vend_id)
|
||||
s = "Yamaha YMF719 OPL-SA3";
|
||||
else if (vend_id == 0x8140d315)
|
||||
s = "SoundscapeVIVO";
|
||||
else if (vend_id == 0x1114b250)
|
||||
s = "Terratec Soundsystem BASE 1";
|
||||
else if (vend_id == 0x50719304)
|
||||
s = "Generic AD1815";
|
||||
if (s) {
|
||||
struct pnp_cinfo d;
|
||||
read_pnp_parms(&d, 0);
|
||||
@ -1433,7 +1489,26 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
return ;
|
||||
}
|
||||
snddev_last_probed = &tmp_d;
|
||||
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
|
||||
|
||||
/* AD1816 */
|
||||
if (vend_id == 0x1114b250 || vend_id == 0x50719304) {
|
||||
dev->id_alive = 16; /* number of io ports ? */
|
||||
|
||||
tmp_d = mss_op_desc; /* copy it */
|
||||
|
||||
tmp_d.ioctl = ad1816_ioctl;
|
||||
tmp_d.isr = ad1816_intr;
|
||||
tmp_d.callback = ad1816_callback;
|
||||
tmp_d.audio_fmt = AFMT_STEREO | AFMT_U8 |
|
||||
AFMT_A_LAW | AFMT_MU_LAW |
|
||||
AFMT_S16_LE | AFMT_S16_BE;
|
||||
|
||||
dev->id_iobase = d.port[2];
|
||||
tmp_d.alt_base = d.port[0]; /* soundblaster comp. but we don't
|
||||
* use that */
|
||||
tmp_d.bd_id = MD_AD1816;
|
||||
strcpy(tmp_d.name, name);
|
||||
} else if (d.flags & DV_PNP_SBCODEC) { /* use sb-compatible codec */
|
||||
dev->id_alive = 16 ; /* number of io ports ? */
|
||||
tmp_d = sb_op_desc ;
|
||||
if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
|
||||
@ -1484,12 +1559,14 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
break;
|
||||
|
||||
default:
|
||||
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
|
||||
tmp_d.bd_id = MD_CS4232; /* to short-circuit the
|
||||
* detect routine */
|
||||
break;
|
||||
}
|
||||
snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name);
|
||||
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
|
||||
}
|
||||
|
||||
write_pnp_parms( &d, ldn );
|
||||
enable_pnp_card();
|
||||
|
||||
@ -1814,5 +1891,440 @@ gus_mem_cfg(snddev_info *d)
|
||||
}
|
||||
#endif /* gus mem cfg... */
|
||||
|
||||
static int
|
||||
ad1816_ioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
|
||||
{
|
||||
snddev_info *d;
|
||||
int unit;
|
||||
|
||||
dev = minor(dev);
|
||||
unit = dev >> 4;
|
||||
d = &pcm_info[unit];
|
||||
|
||||
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
|
||||
cmd &= 0xff;
|
||||
if (cmd == SOUND_MIXER_RECSRC)
|
||||
return ad1816_set_recsrc(d, *(int *) arg);
|
||||
else
|
||||
return ad1816_mixer_set(d, cmd, *(int *) arg);
|
||||
}
|
||||
switch (cmd) { /* driver specific ioctls other than mixer
|
||||
* calls */
|
||||
/* ad1816 has special features */
|
||||
case AIOGCAP: /* get capabilities */
|
||||
{
|
||||
snd_capabilities *p = (snd_capabilities *) arg;
|
||||
p->rate_min = 4000;
|
||||
p->rate_max = 55200;
|
||||
p->bufsize = d->bufsize;
|
||||
p->formats = d->audio_fmt;
|
||||
p->mixers = 1;
|
||||
p->inputs = d->mix_devs;
|
||||
p->left = p->right = 100;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return ENOSYS; /* fallback to default */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_callback(snddev_info * d, int reason)
|
||||
{
|
||||
int wr, cnt;
|
||||
|
||||
wr = reason & SND_CB_WR;
|
||||
reason &= SND_CB_REASON_MASK;
|
||||
|
||||
switch (reason) {
|
||||
case SND_CB_INIT:
|
||||
ad1816_reinit(d);
|
||||
reset_dbuf(&(d->dbuf_in), SND_CHAN_RD);
|
||||
reset_dbuf(&(d->dbuf_out), SND_CHAN_WR);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case SND_CB_START:
|
||||
cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl;
|
||||
|
||||
cnt /= 4;
|
||||
cnt--;
|
||||
|
||||
/* start only if not already running */
|
||||
if (wr && !(inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
/* set dma counter */
|
||||
ad1816_write(d, 8, cnt); /* playback count */
|
||||
/* int enable */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) | 0x8000);
|
||||
/* enable playback */
|
||||
outb(ad1816_play(d), (inb(ad1816_play(d)) | AD1816_ENABLE));
|
||||
/* check if we succeeded */
|
||||
if (!(inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to start write (playback) DMA !\n");
|
||||
}
|
||||
} else if (!wr && !(inb(ad1816_capt(d)) & AD1816_ENABLE)) {
|
||||
/* same for capture */
|
||||
ad1816_write(d, 10, cnt); /* capture count */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) | 0x4000); /* int */
|
||||
outb(ad1816_capt(d), (inb(ad1816_capt(d)) | AD1816_ENABLE)); /* CEN */
|
||||
if (!(inb(ad1816_capt(d)) & AD1816_ENABLE)) { /* check */
|
||||
printf("ad1816: failed to start read (capture) DMA !\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_CB_STOP:
|
||||
case SND_CB_ABORT: /* XXX check this... */
|
||||
/* we don't test here if it is running... */
|
||||
if (wr) {
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) & ~0x8000);
|
||||
/* disable int */
|
||||
outb(ad1816_play(d), (inb(ad1816_play(d)) & ~AD1816_ENABLE));
|
||||
/* disable playback */
|
||||
if ((inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to stop write (playback) DMA !\n");
|
||||
}
|
||||
ad1816_write(d, 8, 0); /* reset base counter */
|
||||
ad1816_write(d, 9, 0); /* reset cur counter */
|
||||
} else {
|
||||
/* same for capture */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) & ~0x4000);
|
||||
outb(ad1816_capt(d), (inb(ad1816_capt(d)) & ~AD1816_ENABLE));
|
||||
if ((inb(ad1816_capt(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to stop read (capture) DMA !\n");
|
||||
}
|
||||
ad1816_write(d, 10, 0);
|
||||
ad1816_write(d, 11, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_intr(int unit)
|
||||
{
|
||||
snddev_info *d = &pcm_info[unit];
|
||||
unsigned char c, served = 0;
|
||||
|
||||
/* get interupt status */
|
||||
c = inb(ad1816_int(d));
|
||||
|
||||
/* check for stray interupts */
|
||||
if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
|
||||
printf("ad1816: Stray interrupt 0x%x.\n", c);
|
||||
c = c & (AD1816_INTRCI | AD1816_INTRPI);
|
||||
outb(ad1816_int(d), c); /* ack it anyway */
|
||||
}
|
||||
/* check for capture interupt */
|
||||
if (d->dbuf_in.dl && (c & AD1816_INTRCI)) {
|
||||
outb(ad1816_int(d), c & ~AD1816_INTRCI); /* ack it */
|
||||
if (inb(ad1816_int(d)) & AD1816_INTRCI)
|
||||
printf("ad1816: Failed to clear cp int !!!\n");
|
||||
dsp_rdintr(d);
|
||||
served |= AD1816_INTRCI; /* cp served */
|
||||
}
|
||||
/* check for playback interupt */
|
||||
if (d->dbuf_out.dl && (c & AD1816_INTRPI)) {
|
||||
outb(ad1816_int(d), c & ~AD1816_INTRPI); /* ack it */
|
||||
if (inb(ad1816_int(d)) & AD1816_INTRPI != 0)
|
||||
printf("ad1816: Failed to clear pb int !!!\n");
|
||||
dsp_wrintr(d);
|
||||
served |= AD1816_INTRPI; /* pb served */
|
||||
}
|
||||
if (served == 0) {
|
||||
/* this probably means this is not a (working) ad1816 chip, */
|
||||
/* or an error in dma handling */
|
||||
printf("ad1816: raised an interrupt without reason 0x%x.\n", c);
|
||||
outb(ad1816_int(d), 0); /* Clear interrupt status anyway */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_wait_init(snddev_info * d, int x)
|
||||
{
|
||||
int n = 0; /* to shut up the compiler... */
|
||||
|
||||
for (; x--;)
|
||||
if (((n = (inb(ad1816_ale(d)) & AD1816_BUSY))) == 0)
|
||||
DELAY(10);
|
||||
else
|
||||
return n;
|
||||
printf("ad1816_wait_init failed 0x%02x.\n", inb(ad1816_ale(d)));
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned short
|
||||
ad1816_read(snddev_info * d, unsigned int reg)
|
||||
{
|
||||
int flags;
|
||||
u_short x;
|
||||
|
||||
/* we don't want to be blocked here */
|
||||
flags = spltty();
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_read: chip timeout before read.\n");
|
||||
return 0;
|
||||
}
|
||||
outb(ad1816_ale(d), (u_char) 0);
|
||||
outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK));
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_read: chip timeout during read.\n");
|
||||
return 0;
|
||||
}
|
||||
x = (inb(ad1816_high(d)) << 8) | inb(ad1816_low(d));
|
||||
splx(flags);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_write(snddev_info * d, unsigned int reg, unsigned short data)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = spltty();
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_write: chip timeout before write.\n");
|
||||
return;
|
||||
}
|
||||
outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK));
|
||||
outb(ad1816_low(d), (u_char) (data & 0x000000ff));
|
||||
outb(ad1816_high(d), (u_char) ((data & 0x0000ff00) >> 8));
|
||||
splx(flags);
|
||||
}
|
||||
|
||||
#if 0 /* unused right now..., and untested... */
|
||||
static void
|
||||
ad1816_mute(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 14, ad1816_read(d, 14) | 0x8000 | 0x80);
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_unmute(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 14, ad1816_read(d, 14) & ~(0x8000 | 0x80));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* only one rec source is possible */
|
||||
|
||||
static int
|
||||
ad1816_set_recsrc(snddev_info * d, int mask)
|
||||
{
|
||||
mask &= d->mix_rec_devs;
|
||||
|
||||
switch (mask) {
|
||||
case SOUND_MASK_LINE:
|
||||
case SOUND_MASK_LINE3:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x0000);
|
||||
break;
|
||||
|
||||
case SOUND_MASK_CD:
|
||||
case SOUND_MASK_LINE1:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x2020);
|
||||
break;
|
||||
|
||||
case SOUND_MASK_MIC:
|
||||
default:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x5050);
|
||||
}
|
||||
|
||||
d->mix_recsrc = mask;
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
#define AD1816_MUTE 31 /* value for mute */
|
||||
|
||||
static int
|
||||
ad1816_mixer_set(snddev_info * d, int dev, int value)
|
||||
{
|
||||
u_char left = (value & 0x000000ff);
|
||||
u_char right = (value & 0x0000ff00) >> 8;
|
||||
u_short reg = 0;
|
||||
|
||||
if (dev > 31)
|
||||
return EINVAL;
|
||||
|
||||
if (!(d->mix_devs & (1 << dev)))
|
||||
return EINVAL;
|
||||
|
||||
if (left > 100)
|
||||
left = 100;
|
||||
if (right > 100)
|
||||
right = 100;
|
||||
|
||||
d->mix_levels[dev] = left | (right << 8);
|
||||
|
||||
/* Scale volumes */
|
||||
left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
|
||||
right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
|
||||
|
||||
reg = (left << 8) | right;
|
||||
|
||||
/* do channel selective muting if volume is zero */
|
||||
if (left == AD1816_MUTE)
|
||||
reg |= 0x8000;
|
||||
if (right == AD1816_MUTE)
|
||||
reg |= 0x0080;
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME: /* Register 14 master volume */
|
||||
ad1816_write(d, 14, reg);
|
||||
break;
|
||||
case SOUND_MIXER_CD: /* Register 15 cd */
|
||||
case SOUND_MIXER_LINE1:
|
||||
ad1816_write(d, 15, reg);
|
||||
break;
|
||||
case SOUND_MIXER_SYNTH: /* Register 16 synth */
|
||||
ad1816_write(d, 16, reg);
|
||||
break;
|
||||
case SOUND_MIXER_PCM: /* Register 4 pcm */
|
||||
ad1816_write(d, 4, reg);
|
||||
break;
|
||||
case SOUND_MIXER_LINE:
|
||||
case SOUND_MIXER_LINE3: /* Register 18 line in */
|
||||
ad1816_write(d, 18, reg);
|
||||
break;
|
||||
case SOUND_MIXER_MIC: /* Register 19 mic volume */
|
||||
ad1816_write(d, 19, reg & ~0xff); /* mic is mono */
|
||||
break;
|
||||
case SOUND_MIXER_IGAIN:
|
||||
/* and now to something completely different ... */
|
||||
ad1816_write(d, 20, ((ad1816_read(d, 20) & ~0x0f0f)
|
||||
| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
|
||||
| ((AD1816_MUTE - right) / 2)));
|
||||
break;
|
||||
default:
|
||||
printf("ad1816_mixer_set(): unknown device.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_mixer_reset(snddev_info * d)
|
||||
{
|
||||
int i;
|
||||
|
||||
d->mix_devs = AD1816_MIXER_DEVICES;
|
||||
d->mix_rec_devs = AD1816_REC_DEVICES;
|
||||
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
||||
if (d->mix_devs & (1 << i))
|
||||
ad1816_mixer_set(d, i, default_mixer_levels[i]);
|
||||
ad1816_set_recsrc(d, SOUND_MASK_MIC);
|
||||
}
|
||||
|
||||
/* Set the playback and capture rates. */
|
||||
|
||||
static int
|
||||
ad1816_speed(snddev_info * d)
|
||||
{
|
||||
RANGE(d->play_speed,4000,55200);
|
||||
RANGE(d->rec_speed,4000,55200);
|
||||
|
||||
ad1816_write(d, 2, d->play_speed);
|
||||
ad1816_write(d, 3, d->rec_speed);
|
||||
|
||||
return d->play_speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* ad1816_format checks that the format is supported (or defaults to AFMT_U8)
|
||||
* and sets the chip to the desired format.
|
||||
*/
|
||||
|
||||
static int
|
||||
ad1816_format(snddev_info * d)
|
||||
{
|
||||
int oldplay =inb(ad1816_play(d)) & ~AD1816_FORMASK;
|
||||
int oldrec = inb(ad1816_capt(d)) & ~AD1816_FORMASK;
|
||||
int play = (d->play_fmt & d->audio_fmt) ? d->play_fmt : AFMT_U8;
|
||||
int rec = (d->rec_fmt & d->audio_fmt) ? d->rec_fmt : AFMT_U8;
|
||||
|
||||
/*
|
||||
* check that arg is one of the supported formats in d->format; otherwise
|
||||
* fallback to AFMT_U8
|
||||
*/
|
||||
|
||||
switch (play) {
|
||||
case AFMT_A_LAW:
|
||||
outb(ad1816_play(d), oldplay | AD1816_ALAW);
|
||||
break;
|
||||
case AFMT_MU_LAW:
|
||||
outb(ad1816_play(d), oldplay | AD1816_MULAW);
|
||||
break;
|
||||
case AFMT_S16_LE:
|
||||
outb(ad1816_play(d), oldplay | AD1816_S16LE);
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
outb(ad1816_play(d), oldplay | AD1816_S16BE);
|
||||
break;
|
||||
default:
|
||||
/* unlikely to happen */
|
||||
printf("ad1816: unknown play format. defaulting to U8.\n");
|
||||
case AFMT_U8:
|
||||
outb(ad1816_play(d), oldplay | AD1816_U8);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rec) {
|
||||
case AFMT_A_LAW:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_ALAW);
|
||||
break;
|
||||
case AFMT_MU_LAW:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_MULAW);
|
||||
break;
|
||||
case AFMT_S16_LE:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_S16LE);
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_S16BE);
|
||||
break;
|
||||
default:
|
||||
printf("ad1816: unknown capture format. defaulting to U8.\n");
|
||||
case AFMT_U8:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_U8);
|
||||
break;
|
||||
}
|
||||
|
||||
d->play_fmt = play;
|
||||
d->rec_fmt = rec;
|
||||
|
||||
return (play);
|
||||
}
|
||||
|
||||
/*
|
||||
* ad1816_reinit resets codec registers
|
||||
*/
|
||||
static void
|
||||
ad1816_reinit(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 8, 0x0000); /* reset base and current counter */
|
||||
ad1816_write(d, 9, 0x0000); /* for playback and capture */
|
||||
ad1816_write(d, 10, 0x0000);
|
||||
ad1816_write(d, 11, 0x0000);
|
||||
|
||||
if (d->flags & SND_F_STEREO) {
|
||||
outb((ad1816_play(d)), AD1816_STEREO); /* set playback to stereo */
|
||||
outb((ad1816_capt(d)), AD1816_STEREO); /* set capture to stereo */
|
||||
} else {
|
||||
outb((ad1816_play(d)), 0x00); /* set playback to mono */
|
||||
outb((ad1816_capt(d)), 0x00); /* set capture to mono */
|
||||
}
|
||||
|
||||
ad1816_format(d);
|
||||
ad1816_speed(d);
|
||||
|
||||
snd_set_blocksize(d); /* update blocksize if user did not force it */
|
||||
}
|
||||
|
||||
#endif /* NPNP > 0 */
|
||||
#endif /* NPCM > 0 */
|
||||
|
@ -105,6 +105,55 @@ ahead.
|
||||
#define BD_F_IRQ_OK 0x0002
|
||||
#define BD_F_TMR_RUN 0x0004
|
||||
|
||||
/* AD1816 register macros */
|
||||
|
||||
#define ad1816_ale(d) ((d)->io_base+0) /* indirect reg access */
|
||||
#define ad1816_int(d) ((d)->io_base+1) /* interupt status */
|
||||
#define ad1816_low(d) ((d)->io_base+2) /* indirect low byte */
|
||||
#define ad1816_high(d) ((d)->io_base+3) /* indirect high byte */
|
||||
/* unused */
|
||||
#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */
|
||||
#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */
|
||||
#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */
|
||||
/* end of unused */
|
||||
/* values for playback/capture config:
|
||||
bits: 0 enable/disable
|
||||
1 pio/dma
|
||||
2 stereo/mono
|
||||
3 companded/linearPCM
|
||||
4-5 format : 00 8bit linear (uncomp)
|
||||
00 8bit mulaw (comp)
|
||||
01 16bit le (uncomp)
|
||||
01 8bit alaw (comp)
|
||||
11 16bit be (uncomp)
|
||||
*/
|
||||
#define ad1816_play(d) ((d)->io_base+8) /* playback config */
|
||||
#define ad1816_capt(d) ((d)->io_base+9) /* capture config */
|
||||
|
||||
#define AD1816_BUSY 0x80 /* chip is busy */
|
||||
#define AD1816_ALEMASK 0x3F /* mask for indirect adr. */
|
||||
/* unusud */
|
||||
#define AD1816_INTRSI 0x01 /* sb intr */
|
||||
#define AD1816_INTRGI 0x02 /* game intr */
|
||||
#define AD1816_INTRRI 0x04 /* ring intr */
|
||||
#define AD1816_INTRDI 0x08 /* dsp intr */
|
||||
#define AD1816_INTRVI 0x10 /* vol intr */
|
||||
#define AD1816_INTRTI 0x20 /* timer intr */
|
||||
/* used again */
|
||||
#define AD1816_INTRCI 0x40 /* capture intr */
|
||||
#define AD1816_INTRPI 0x80 /* playback intr */
|
||||
/* PIO stuff is not supplied here */
|
||||
/* playback / capture config */
|
||||
#define AD1816_ENABLE 0x01 /* enable pl/cp */
|
||||
#define AD1816_PIO 0x02 /* use pio */
|
||||
#define AD1816_STEREO 0x04
|
||||
#define AD1816_COMP 0x08 /* data is companded */
|
||||
#define AD1816_U8 0x00 /* 8 bit linear pcm */
|
||||
#define AD1816_MULAW 0x08 /* 8 bit mulaw */
|
||||
#define AD1816_ALAW 0x18 /* 8 bit alaw */
|
||||
#define AD1816_S16LE 0x10 /* 16 bit linear little endian */
|
||||
#define AD1816_S16BE 0x30 /* 16 bit linear big endian */
|
||||
#define AD1816_FORMASK 0x38 /* format mask */
|
||||
|
||||
/*
|
||||
* sound/ad1848_mixer.h
|
||||
@ -225,6 +274,12 @@ MIX_NONE(SOUND_MIXER_LINE3),
|
||||
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
|
||||
SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
|
||||
|
||||
#define AD1816_REC_DEVICES \
|
||||
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
|
||||
|
||||
#define AD1816_MIXER_DEVICES \
|
||||
(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH | \
|
||||
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN)
|
||||
|
||||
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
|
||||
0x5a5a, /* Master Volume */
|
||||
|
@ -99,6 +99,25 @@ COMMENTS:
|
||||
and working in playback mode in MSS emulation.
|
||||
Also have reports from some user that it works ok.
|
||||
|
||||
--------------------------
|
||||
CHIPSET:
|
||||
AD1815/1816
|
||||
|
||||
MANUFACTURER:
|
||||
Analog Devices
|
||||
|
||||
DOCUMENTATION:
|
||||
http://www.analog.com
|
||||
|
||||
COMMENTS:
|
||||
This is a chip for ISA-PnP cards, and so should be configured
|
||||
using the PnP interface. For full function configure port2,
|
||||
irq0, drq0 and drq1 of ldn0.
|
||||
The driver is contributed by German Tischler
|
||||
|
||||
FORMATS:
|
||||
ALAW/ULAW/8bit/16bit(le)/16bit(be),8kHz-55.2kHz,full duplex
|
||||
|
||||
--------------------------
|
||||
CHIPSET:
|
||||
OPTi931: PnP id 0x3109143e
|
||||
|
@ -79,6 +79,25 @@ static void ad_write(snddev_info *d, int reg, u_char data);
|
||||
static void ad_write_cnt(snddev_info *d, int reg, u_short data);
|
||||
static int ad_read(snddev_info *d, int reg);
|
||||
|
||||
/* ad1816 prototypes */
|
||||
|
||||
/* IO primitives */
|
||||
static int ad1816_wait_init(snddev_info * d, int x);
|
||||
static unsigned short ad1816_read(snddev_info * d, unsigned int reg);
|
||||
static void ad1816_write(snddev_info * d, unsigned int reg, unsigned short data);
|
||||
/* intr and callback functions */
|
||||
static irq_proc_t ad1816_intr;
|
||||
static snd_callback_t ad1816_callback;
|
||||
/* device specific ioctl calls */
|
||||
static d_ioctl_t ad1816_ioctl;
|
||||
/* parameter set functions */
|
||||
static void ad1816_reinit(snddev_info * d);
|
||||
static int ad1816_mixer_set(snddev_info * d, int dev, int value);
|
||||
static int ad1816_set_recsrc(snddev_info * d, int mask);
|
||||
static void ad1816_mixer_reset(snddev_info * d);
|
||||
|
||||
/* ad1816 prototypes end */
|
||||
|
||||
/*
|
||||
* device descriptors for the boards supported by this module.
|
||||
*/
|
||||
@ -183,6 +202,31 @@ mss_probe_end:
|
||||
return mss_detect(dev) ? 8 : 0 ; /* mss uses 8 regs */
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_attach(struct isa_device *dev)
|
||||
{
|
||||
snddev_info *d = &(pcm_info[dev->id_unit]);
|
||||
|
||||
dev->id_alive = 16; /* number of io ports */
|
||||
|
||||
if (FULL_DUPLEX(d))
|
||||
d->audio_fmt |= AFMT_FULLDUPLEX;
|
||||
|
||||
ad1816_write(d, 1, 0x2);/* disable interrupts */
|
||||
ad1816_write(d, 32, 0x90F0); /* SoundSystem Mode, split format */
|
||||
|
||||
ad1816_write(d, 5, 0x8080); /* FM volume mute */
|
||||
ad1816_write(d, 6, 0x8080); /* I2S1 volume mute */
|
||||
ad1816_write(d, 7, 0x8080); /* I2S0 volume mute */
|
||||
ad1816_write(d, 17, 0x8888); /* VID Volume mute */
|
||||
ad1816_write(d, 20, 0x5050); /* Source select Mic & auto gain ctrl
|
||||
* off */
|
||||
/* adc gain is set to 0 */
|
||||
ad1816_reinit(d);
|
||||
ad1816_mixer_reset(d);
|
||||
return 0 ;
|
||||
}
|
||||
|
||||
/*
|
||||
* the address passed as io_base for mss_attach is also the old
|
||||
* MSS base address (e.g. 0x530). The codec is four locations ahead.
|
||||
@ -199,6 +243,9 @@ mss_attach(struct isa_device *dev)
|
||||
d->name, dev->id_unit,
|
||||
d->io_base, d->irq, d->dbuf_out.chan, d->dbuf_in.chan, dev->id_flags);
|
||||
|
||||
if (d->bd_id == MD_AD1816)
|
||||
return ad1816_attach(dev);
|
||||
|
||||
dev->id_alive = 8 ; /* number of io ports */
|
||||
/* should be already set but just in case... */
|
||||
|
||||
@ -374,7 +421,11 @@ mss_close(dev_t dev, int flags, int mode, struct proc * p)
|
||||
d->flags |= SND_F_CLOSING ;
|
||||
splx(s); /* is this ok here ? */
|
||||
snd_flush(d);
|
||||
outb(io_Status(d), 0); /* Clear interrupt status */
|
||||
/* Clear interrupt status */
|
||||
if ( d->bd_id == MD_AD1816 )
|
||||
outb(ad1816_int(d), 0);
|
||||
else
|
||||
outb(io_Status(d), 0);
|
||||
d->flags = 0 ;
|
||||
}
|
||||
return 0 ;
|
||||
@ -1370,11 +1421,12 @@ mss_reinit(snddev_info *d)
|
||||
#if NPNP > 0
|
||||
|
||||
static char * cs423x_probe(u_long csn, u_long vend_id);
|
||||
static void cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
static void
|
||||
cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
struct isa_device *dev);
|
||||
|
||||
static struct pnp_device cs423x = {
|
||||
"CS423x/Yamaha",
|
||||
"CS423x/Yamaha/AD1816",
|
||||
cs423x_probe,
|
||||
cs423x_attach,
|
||||
&nsnd, /* use this for all sound cards */
|
||||
@ -1405,6 +1457,10 @@ cs423x_probe(u_long csn, u_long vend_id)
|
||||
s = "Yamaha YMF719 OPL-SA3";
|
||||
else if (vend_id == 0x8140d315)
|
||||
s = "SoundscapeVIVO";
|
||||
else if (vend_id == 0x1114b250)
|
||||
s = "Terratec Soundsystem BASE 1";
|
||||
else if (vend_id == 0x50719304)
|
||||
s = "Generic AD1815";
|
||||
if (s) {
|
||||
struct pnp_cinfo d;
|
||||
read_pnp_parms(&d, 0);
|
||||
@ -1433,7 +1489,26 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
return ;
|
||||
}
|
||||
snddev_last_probed = &tmp_d;
|
||||
if (d.flags & DV_PNP_SBCODEC) { /*** use sb-compatible codec ***/
|
||||
|
||||
/* AD1816 */
|
||||
if (vend_id == 0x1114b250 || vend_id == 0x50719304) {
|
||||
dev->id_alive = 16; /* number of io ports ? */
|
||||
|
||||
tmp_d = mss_op_desc; /* copy it */
|
||||
|
||||
tmp_d.ioctl = ad1816_ioctl;
|
||||
tmp_d.isr = ad1816_intr;
|
||||
tmp_d.callback = ad1816_callback;
|
||||
tmp_d.audio_fmt = AFMT_STEREO | AFMT_U8 |
|
||||
AFMT_A_LAW | AFMT_MU_LAW |
|
||||
AFMT_S16_LE | AFMT_S16_BE;
|
||||
|
||||
dev->id_iobase = d.port[2];
|
||||
tmp_d.alt_base = d.port[0]; /* soundblaster comp. but we don't
|
||||
* use that */
|
||||
tmp_d.bd_id = MD_AD1816;
|
||||
strcpy(tmp_d.name, name);
|
||||
} else if (d.flags & DV_PNP_SBCODEC) { /* use sb-compatible codec */
|
||||
dev->id_alive = 16 ; /* number of io ports ? */
|
||||
tmp_d = sb_op_desc ;
|
||||
if (vend_id==0x2000a865 || vend_id==0x3000a865 ||
|
||||
@ -1484,12 +1559,14 @@ cs423x_attach(u_long csn, u_long vend_id, char *name,
|
||||
break;
|
||||
|
||||
default:
|
||||
tmp_d.bd_id = MD_CS4232 ; /* to short-circuit the detect routine */
|
||||
tmp_d.bd_id = MD_CS4232; /* to short-circuit the
|
||||
* detect routine */
|
||||
break;
|
||||
}
|
||||
snprintf(tmp_d.name, sizeof(tmp_d.name), "%s", name);
|
||||
tmp_d.audio_fmt |= AFMT_FULLDUPLEX ;
|
||||
}
|
||||
|
||||
write_pnp_parms( &d, ldn );
|
||||
enable_pnp_card();
|
||||
|
||||
@ -1814,5 +1891,440 @@ gus_mem_cfg(snddev_info *d)
|
||||
}
|
||||
#endif /* gus mem cfg... */
|
||||
|
||||
static int
|
||||
ad1816_ioctl(dev_t dev, u_long cmd, caddr_t arg, int mode, struct proc * p)
|
||||
{
|
||||
snddev_info *d;
|
||||
int unit;
|
||||
|
||||
dev = minor(dev);
|
||||
unit = dev >> 4;
|
||||
d = &pcm_info[unit];
|
||||
|
||||
if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
|
||||
cmd &= 0xff;
|
||||
if (cmd == SOUND_MIXER_RECSRC)
|
||||
return ad1816_set_recsrc(d, *(int *) arg);
|
||||
else
|
||||
return ad1816_mixer_set(d, cmd, *(int *) arg);
|
||||
}
|
||||
switch (cmd) { /* driver specific ioctls other than mixer
|
||||
* calls */
|
||||
/* ad1816 has special features */
|
||||
case AIOGCAP: /* get capabilities */
|
||||
{
|
||||
snd_capabilities *p = (snd_capabilities *) arg;
|
||||
p->rate_min = 4000;
|
||||
p->rate_max = 55200;
|
||||
p->bufsize = d->bufsize;
|
||||
p->formats = d->audio_fmt;
|
||||
p->mixers = 1;
|
||||
p->inputs = d->mix_devs;
|
||||
p->left = p->right = 100;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return ENOSYS; /* fallback to default */
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_callback(snddev_info * d, int reason)
|
||||
{
|
||||
int wr, cnt;
|
||||
|
||||
wr = reason & SND_CB_WR;
|
||||
reason &= SND_CB_REASON_MASK;
|
||||
|
||||
switch (reason) {
|
||||
case SND_CB_INIT:
|
||||
ad1816_reinit(d);
|
||||
reset_dbuf(&(d->dbuf_in), SND_CHAN_RD);
|
||||
reset_dbuf(&(d->dbuf_out), SND_CHAN_WR);
|
||||
return 1;
|
||||
break;
|
||||
|
||||
case SND_CB_START:
|
||||
cnt = wr ? d->dbuf_out.dl : d->dbuf_in.dl;
|
||||
|
||||
cnt /= 4;
|
||||
cnt--;
|
||||
|
||||
/* start only if not already running */
|
||||
if (wr && !(inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
/* set dma counter */
|
||||
ad1816_write(d, 8, cnt); /* playback count */
|
||||
/* int enable */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) | 0x8000);
|
||||
/* enable playback */
|
||||
outb(ad1816_play(d), (inb(ad1816_play(d)) | AD1816_ENABLE));
|
||||
/* check if we succeeded */
|
||||
if (!(inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to start write (playback) DMA !\n");
|
||||
}
|
||||
} else if (!wr && !(inb(ad1816_capt(d)) & AD1816_ENABLE)) {
|
||||
/* same for capture */
|
||||
ad1816_write(d, 10, cnt); /* capture count */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) | 0x4000); /* int */
|
||||
outb(ad1816_capt(d), (inb(ad1816_capt(d)) | AD1816_ENABLE)); /* CEN */
|
||||
if (!(inb(ad1816_capt(d)) & AD1816_ENABLE)) { /* check */
|
||||
printf("ad1816: failed to start read (capture) DMA !\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SND_CB_STOP:
|
||||
case SND_CB_ABORT: /* XXX check this... */
|
||||
/* we don't test here if it is running... */
|
||||
if (wr) {
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) & ~0x8000);
|
||||
/* disable int */
|
||||
outb(ad1816_play(d), (inb(ad1816_play(d)) & ~AD1816_ENABLE));
|
||||
/* disable playback */
|
||||
if ((inb(ad1816_play(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to stop write (playback) DMA !\n");
|
||||
}
|
||||
ad1816_write(d, 8, 0); /* reset base counter */
|
||||
ad1816_write(d, 9, 0); /* reset cur counter */
|
||||
} else {
|
||||
/* same for capture */
|
||||
ad1816_write(d, 1, ad1816_read(d, 1) & ~0x4000);
|
||||
outb(ad1816_capt(d), (inb(ad1816_capt(d)) & ~AD1816_ENABLE));
|
||||
if ((inb(ad1816_capt(d)) & AD1816_ENABLE)) {
|
||||
printf("ad1816: failed to stop read (capture) DMA !\n");
|
||||
}
|
||||
ad1816_write(d, 10, 0);
|
||||
ad1816_write(d, 11, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_intr(int unit)
|
||||
{
|
||||
snddev_info *d = &pcm_info[unit];
|
||||
unsigned char c, served = 0;
|
||||
|
||||
/* get interupt status */
|
||||
c = inb(ad1816_int(d));
|
||||
|
||||
/* check for stray interupts */
|
||||
if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) {
|
||||
printf("ad1816: Stray interrupt 0x%x.\n", c);
|
||||
c = c & (AD1816_INTRCI | AD1816_INTRPI);
|
||||
outb(ad1816_int(d), c); /* ack it anyway */
|
||||
}
|
||||
/* check for capture interupt */
|
||||
if (d->dbuf_in.dl && (c & AD1816_INTRCI)) {
|
||||
outb(ad1816_int(d), c & ~AD1816_INTRCI); /* ack it */
|
||||
if (inb(ad1816_int(d)) & AD1816_INTRCI)
|
||||
printf("ad1816: Failed to clear cp int !!!\n");
|
||||
dsp_rdintr(d);
|
||||
served |= AD1816_INTRCI; /* cp served */
|
||||
}
|
||||
/* check for playback interupt */
|
||||
if (d->dbuf_out.dl && (c & AD1816_INTRPI)) {
|
||||
outb(ad1816_int(d), c & ~AD1816_INTRPI); /* ack it */
|
||||
if (inb(ad1816_int(d)) & AD1816_INTRPI != 0)
|
||||
printf("ad1816: Failed to clear pb int !!!\n");
|
||||
dsp_wrintr(d);
|
||||
served |= AD1816_INTRPI; /* pb served */
|
||||
}
|
||||
if (served == 0) {
|
||||
/* this probably means this is not a (working) ad1816 chip, */
|
||||
/* or an error in dma handling */
|
||||
printf("ad1816: raised an interrupt without reason 0x%x.\n", c);
|
||||
outb(ad1816_int(d), 0); /* Clear interrupt status anyway */
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ad1816_wait_init(snddev_info * d, int x)
|
||||
{
|
||||
int n = 0; /* to shut up the compiler... */
|
||||
|
||||
for (; x--;)
|
||||
if (((n = (inb(ad1816_ale(d)) & AD1816_BUSY))) == 0)
|
||||
DELAY(10);
|
||||
else
|
||||
return n;
|
||||
printf("ad1816_wait_init failed 0x%02x.\n", inb(ad1816_ale(d)));
|
||||
return n;
|
||||
}
|
||||
|
||||
static unsigned short
|
||||
ad1816_read(snddev_info * d, unsigned int reg)
|
||||
{
|
||||
int flags;
|
||||
u_short x;
|
||||
|
||||
/* we don't want to be blocked here */
|
||||
flags = spltty();
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_read: chip timeout before read.\n");
|
||||
return 0;
|
||||
}
|
||||
outb(ad1816_ale(d), (u_char) 0);
|
||||
outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK));
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_read: chip timeout during read.\n");
|
||||
return 0;
|
||||
}
|
||||
x = (inb(ad1816_high(d)) << 8) | inb(ad1816_low(d));
|
||||
splx(flags);
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_write(snddev_info * d, unsigned int reg, unsigned short data)
|
||||
{
|
||||
int flags;
|
||||
|
||||
flags = spltty();
|
||||
if (ad1816_wait_init(d, 100) == 0) {
|
||||
printf("ad1816_write: chip timeout before write.\n");
|
||||
return;
|
||||
}
|
||||
outb(ad1816_ale(d), (u_char) (reg & AD1816_ALEMASK));
|
||||
outb(ad1816_low(d), (u_char) (data & 0x000000ff));
|
||||
outb(ad1816_high(d), (u_char) ((data & 0x0000ff00) >> 8));
|
||||
splx(flags);
|
||||
}
|
||||
|
||||
#if 0 /* unused right now..., and untested... */
|
||||
static void
|
||||
ad1816_mute(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 14, ad1816_read(d, 14) | 0x8000 | 0x80);
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_unmute(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 14, ad1816_read(d, 14) & ~(0x8000 | 0x80));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* only one rec source is possible */
|
||||
|
||||
static int
|
||||
ad1816_set_recsrc(snddev_info * d, int mask)
|
||||
{
|
||||
mask &= d->mix_rec_devs;
|
||||
|
||||
switch (mask) {
|
||||
case SOUND_MASK_LINE:
|
||||
case SOUND_MASK_LINE3:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x0000);
|
||||
break;
|
||||
|
||||
case SOUND_MASK_CD:
|
||||
case SOUND_MASK_LINE1:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x2020);
|
||||
break;
|
||||
|
||||
case SOUND_MASK_MIC:
|
||||
default:
|
||||
ad1816_write(d, 20, (ad1816_read(d, 20) & ~0x7070) | 0x5050);
|
||||
}
|
||||
|
||||
d->mix_recsrc = mask;
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
#define AD1816_MUTE 31 /* value for mute */
|
||||
|
||||
static int
|
||||
ad1816_mixer_set(snddev_info * d, int dev, int value)
|
||||
{
|
||||
u_char left = (value & 0x000000ff);
|
||||
u_char right = (value & 0x0000ff00) >> 8;
|
||||
u_short reg = 0;
|
||||
|
||||
if (dev > 31)
|
||||
return EINVAL;
|
||||
|
||||
if (!(d->mix_devs & (1 << dev)))
|
||||
return EINVAL;
|
||||
|
||||
if (left > 100)
|
||||
left = 100;
|
||||
if (right > 100)
|
||||
right = 100;
|
||||
|
||||
d->mix_levels[dev] = left | (right << 8);
|
||||
|
||||
/* Scale volumes */
|
||||
left = AD1816_MUTE - (AD1816_MUTE * left) / 100;
|
||||
right = AD1816_MUTE - (AD1816_MUTE * right) / 100;
|
||||
|
||||
reg = (left << 8) | right;
|
||||
|
||||
/* do channel selective muting if volume is zero */
|
||||
if (left == AD1816_MUTE)
|
||||
reg |= 0x8000;
|
||||
if (right == AD1816_MUTE)
|
||||
reg |= 0x0080;
|
||||
|
||||
switch (dev) {
|
||||
case SOUND_MIXER_VOLUME: /* Register 14 master volume */
|
||||
ad1816_write(d, 14, reg);
|
||||
break;
|
||||
case SOUND_MIXER_CD: /* Register 15 cd */
|
||||
case SOUND_MIXER_LINE1:
|
||||
ad1816_write(d, 15, reg);
|
||||
break;
|
||||
case SOUND_MIXER_SYNTH: /* Register 16 synth */
|
||||
ad1816_write(d, 16, reg);
|
||||
break;
|
||||
case SOUND_MIXER_PCM: /* Register 4 pcm */
|
||||
ad1816_write(d, 4, reg);
|
||||
break;
|
||||
case SOUND_MIXER_LINE:
|
||||
case SOUND_MIXER_LINE3: /* Register 18 line in */
|
||||
ad1816_write(d, 18, reg);
|
||||
break;
|
||||
case SOUND_MIXER_MIC: /* Register 19 mic volume */
|
||||
ad1816_write(d, 19, reg & ~0xff); /* mic is mono */
|
||||
break;
|
||||
case SOUND_MIXER_IGAIN:
|
||||
/* and now to something completely different ... */
|
||||
ad1816_write(d, 20, ((ad1816_read(d, 20) & ~0x0f0f)
|
||||
| (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */
|
||||
| ((AD1816_MUTE - right) / 2)));
|
||||
break;
|
||||
default:
|
||||
printf("ad1816_mixer_set(): unknown device.\n");
|
||||
break;
|
||||
}
|
||||
|
||||
return 0; /* success */
|
||||
}
|
||||
|
||||
static void
|
||||
ad1816_mixer_reset(snddev_info * d)
|
||||
{
|
||||
int i;
|
||||
|
||||
d->mix_devs = AD1816_MIXER_DEVICES;
|
||||
d->mix_rec_devs = AD1816_REC_DEVICES;
|
||||
|
||||
for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
|
||||
if (d->mix_devs & (1 << i))
|
||||
ad1816_mixer_set(d, i, default_mixer_levels[i]);
|
||||
ad1816_set_recsrc(d, SOUND_MASK_MIC);
|
||||
}
|
||||
|
||||
/* Set the playback and capture rates. */
|
||||
|
||||
static int
|
||||
ad1816_speed(snddev_info * d)
|
||||
{
|
||||
RANGE(d->play_speed,4000,55200);
|
||||
RANGE(d->rec_speed,4000,55200);
|
||||
|
||||
ad1816_write(d, 2, d->play_speed);
|
||||
ad1816_write(d, 3, d->rec_speed);
|
||||
|
||||
return d->play_speed;
|
||||
}
|
||||
|
||||
/*
|
||||
* ad1816_format checks that the format is supported (or defaults to AFMT_U8)
|
||||
* and sets the chip to the desired format.
|
||||
*/
|
||||
|
||||
static int
|
||||
ad1816_format(snddev_info * d)
|
||||
{
|
||||
int oldplay =inb(ad1816_play(d)) & ~AD1816_FORMASK;
|
||||
int oldrec = inb(ad1816_capt(d)) & ~AD1816_FORMASK;
|
||||
int play = (d->play_fmt & d->audio_fmt) ? d->play_fmt : AFMT_U8;
|
||||
int rec = (d->rec_fmt & d->audio_fmt) ? d->rec_fmt : AFMT_U8;
|
||||
|
||||
/*
|
||||
* check that arg is one of the supported formats in d->format; otherwise
|
||||
* fallback to AFMT_U8
|
||||
*/
|
||||
|
||||
switch (play) {
|
||||
case AFMT_A_LAW:
|
||||
outb(ad1816_play(d), oldplay | AD1816_ALAW);
|
||||
break;
|
||||
case AFMT_MU_LAW:
|
||||
outb(ad1816_play(d), oldplay | AD1816_MULAW);
|
||||
break;
|
||||
case AFMT_S16_LE:
|
||||
outb(ad1816_play(d), oldplay | AD1816_S16LE);
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
outb(ad1816_play(d), oldplay | AD1816_S16BE);
|
||||
break;
|
||||
default:
|
||||
/* unlikely to happen */
|
||||
printf("ad1816: unknown play format. defaulting to U8.\n");
|
||||
case AFMT_U8:
|
||||
outb(ad1816_play(d), oldplay | AD1816_U8);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rec) {
|
||||
case AFMT_A_LAW:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_ALAW);
|
||||
break;
|
||||
case AFMT_MU_LAW:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_MULAW);
|
||||
break;
|
||||
case AFMT_S16_LE:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_S16LE);
|
||||
break;
|
||||
case AFMT_S16_BE:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_S16BE);
|
||||
break;
|
||||
default:
|
||||
printf("ad1816: unknown capture format. defaulting to U8.\n");
|
||||
case AFMT_U8:
|
||||
outb(ad1816_capt(d), oldrec | AD1816_U8);
|
||||
break;
|
||||
}
|
||||
|
||||
d->play_fmt = play;
|
||||
d->rec_fmt = rec;
|
||||
|
||||
return (play);
|
||||
}
|
||||
|
||||
/*
|
||||
* ad1816_reinit resets codec registers
|
||||
*/
|
||||
static void
|
||||
ad1816_reinit(snddev_info * d)
|
||||
{
|
||||
ad1816_write(d, 8, 0x0000); /* reset base and current counter */
|
||||
ad1816_write(d, 9, 0x0000); /* for playback and capture */
|
||||
ad1816_write(d, 10, 0x0000);
|
||||
ad1816_write(d, 11, 0x0000);
|
||||
|
||||
if (d->flags & SND_F_STEREO) {
|
||||
outb((ad1816_play(d)), AD1816_STEREO); /* set playback to stereo */
|
||||
outb((ad1816_capt(d)), AD1816_STEREO); /* set capture to stereo */
|
||||
} else {
|
||||
outb((ad1816_play(d)), 0x00); /* set playback to mono */
|
||||
outb((ad1816_capt(d)), 0x00); /* set capture to mono */
|
||||
}
|
||||
|
||||
ad1816_format(d);
|
||||
ad1816_speed(d);
|
||||
|
||||
snd_set_blocksize(d); /* update blocksize if user did not force it */
|
||||
}
|
||||
|
||||
#endif /* NPNP > 0 */
|
||||
#endif /* NPCM > 0 */
|
||||
|
@ -105,6 +105,55 @@ ahead.
|
||||
#define BD_F_IRQ_OK 0x0002
|
||||
#define BD_F_TMR_RUN 0x0004
|
||||
|
||||
/* AD1816 register macros */
|
||||
|
||||
#define ad1816_ale(d) ((d)->io_base+0) /* indirect reg access */
|
||||
#define ad1816_int(d) ((d)->io_base+1) /* interupt status */
|
||||
#define ad1816_low(d) ((d)->io_base+2) /* indirect low byte */
|
||||
#define ad1816_high(d) ((d)->io_base+3) /* indirect high byte */
|
||||
/* unused */
|
||||
#define ad1816_pioD(d) ((d)->io_base+4) /* PIO debug */
|
||||
#define ad1816_pios(d) ((d)->io_base+5) /* PIO status */
|
||||
#define ad1816_piod(d) ((d)->io_base+6) /* PIO data */
|
||||
/* end of unused */
|
||||
/* values for playback/capture config:
|
||||
bits: 0 enable/disable
|
||||
1 pio/dma
|
||||
2 stereo/mono
|
||||
3 companded/linearPCM
|
||||
4-5 format : 00 8bit linear (uncomp)
|
||||
00 8bit mulaw (comp)
|
||||
01 16bit le (uncomp)
|
||||
01 8bit alaw (comp)
|
||||
11 16bit be (uncomp)
|
||||
*/
|
||||
#define ad1816_play(d) ((d)->io_base+8) /* playback config */
|
||||
#define ad1816_capt(d) ((d)->io_base+9) /* capture config */
|
||||
|
||||
#define AD1816_BUSY 0x80 /* chip is busy */
|
||||
#define AD1816_ALEMASK 0x3F /* mask for indirect adr. */
|
||||
/* unusud */
|
||||
#define AD1816_INTRSI 0x01 /* sb intr */
|
||||
#define AD1816_INTRGI 0x02 /* game intr */
|
||||
#define AD1816_INTRRI 0x04 /* ring intr */
|
||||
#define AD1816_INTRDI 0x08 /* dsp intr */
|
||||
#define AD1816_INTRVI 0x10 /* vol intr */
|
||||
#define AD1816_INTRTI 0x20 /* timer intr */
|
||||
/* used again */
|
||||
#define AD1816_INTRCI 0x40 /* capture intr */
|
||||
#define AD1816_INTRPI 0x80 /* playback intr */
|
||||
/* PIO stuff is not supplied here */
|
||||
/* playback / capture config */
|
||||
#define AD1816_ENABLE 0x01 /* enable pl/cp */
|
||||
#define AD1816_PIO 0x02 /* use pio */
|
||||
#define AD1816_STEREO 0x04
|
||||
#define AD1816_COMP 0x08 /* data is companded */
|
||||
#define AD1816_U8 0x00 /* 8 bit linear pcm */
|
||||
#define AD1816_MULAW 0x08 /* 8 bit mulaw */
|
||||
#define AD1816_ALAW 0x18 /* 8 bit alaw */
|
||||
#define AD1816_S16LE 0x10 /* 16 bit linear little endian */
|
||||
#define AD1816_S16BE 0x30 /* 16 bit linear big endian */
|
||||
#define AD1816_FORMASK 0x38 /* format mask */
|
||||
|
||||
/*
|
||||
* sound/ad1848_mixer.h
|
||||
@ -225,6 +274,12 @@ MIX_NONE(SOUND_MIXER_LINE3),
|
||||
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \
|
||||
SOUND_MASK_IGAIN | SOUND_MASK_LINE1 )
|
||||
|
||||
#define AD1816_REC_DEVICES \
|
||||
(SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
|
||||
|
||||
#define AD1816_MIXER_DEVICES \
|
||||
(SOUND_MASK_VOLUME | SOUND_MASK_PCM | SOUND_MASK_SYNTH | \
|
||||
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_IGAIN)
|
||||
|
||||
static u_short default_mixer_levels[SOUND_MIXER_NRDEVICES] = {
|
||||
0x5a5a, /* Master Volume */
|
||||
|
@ -238,7 +238,6 @@ pcmattach(struct isa_device * dev)
|
||||
snddev_info *d = NULL ;
|
||||
struct isa_device *dvp;
|
||||
int stat = 0;
|
||||
dev_t isadev;
|
||||
|
||||
dev->id_ointr = pcmintr;
|
||||
|
||||
@ -284,8 +283,6 @@ pcmattach(struct isa_device * dev)
|
||||
if (FULL_DUPLEX(d))
|
||||
isa_dma_acquire(d->dbuf_in.chan);
|
||||
|
||||
isadev = makedev(CDEV_MAJOR, 0);
|
||||
cdevsw_add(&isadev, &snd_cdevsw, NULL);
|
||||
|
||||
/*
|
||||
* should try and find a suitable value for id_id, otherwise
|
||||
@ -331,6 +328,10 @@ pcminit(snddev_info *d, int unit)
|
||||
#ifdef DEVFS
|
||||
void *cookie;
|
||||
#endif
|
||||
dev_t isadev;
|
||||
|
||||
isadev = makedev(CDEV_MAJOR, 0);
|
||||
cdevsw_add(&isadev, &snd_cdevsw, NULL);
|
||||
|
||||
/*
|
||||
* initialize standard parameters for the device. This can be
|
||||
@ -604,6 +605,14 @@ sndread(dev_t i_dev, struct uio * buf, int flag)
|
||||
return uiomove(p, l, buf) ;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX read from the ad1816 with a single DMA channel is unsupported.
|
||||
* This is really not the place for machine-dependent functions,
|
||||
* a proper device routine will be supplied in the future - luigi
|
||||
*/
|
||||
if ((d->bd_id == MD_AD1816) && (!(FULL_DUPLEX(d))))
|
||||
return EIO;
|
||||
|
||||
if (d->read) /* device-specific read */
|
||||
return d->read(i_dev, buf, flag);
|
||||
|
||||
|
@ -313,6 +313,7 @@ struct _snddev_info {
|
||||
*/
|
||||
#define MD_AD1848 0x91
|
||||
#define MD_AD1845 0x92
|
||||
#define MD_AD1816 0x93
|
||||
#define MD_CS4248 0xA1
|
||||
#define MD_CS4231 0xA2
|
||||
#define MD_CS4231A 0xA3
|
||||
|
Loading…
x
Reference in New Issue
Block a user