add support for opti924 and opti930 chips

both should work in non-pnp mode, the 924 should also work in its rather
braindead pnp mode- it will adopt port 0x530 unless given hints due to it
starting up in soundblaster mode and thus not requesting a valid mss port
address.

Submitted by:	George Reid <greid@ukug.uk.freebsd.org>
This commit is contained in:
cg 2001-03-24 00:22:01 +00:00
parent b64f865520
commit 3d10dbbf55
2 changed files with 270 additions and 7 deletions

View File

@ -1,4 +1,5 @@
/*
* Copyright (c) 2001 George Reid <greid@ukug.uk.freebsd.org>
* Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
* Copyright Luigi Rizzo, 1997,1998
* Copyright by Hannu Savolainen 1994, 1995
@ -72,6 +73,11 @@ struct mss_info {
*/
int opti_offset; /* offset from config_base for opti931 */
u_long bd_flags; /* board-specific flags */
int optibase; /* base address for OPTi9xx config */
struct resource *indir; /* Indirect register index address */
int indir_rid;
int password; /* password for opti9xx cards */
int passwdreg; /* password register */
struct mss_chinfo pch, rch;
};
@ -82,6 +88,7 @@ static driver_intr_t mss_intr;
/* prototypes for local functions */
static int mss_detect(device_t dev, struct mss_info *mss);
static int opti_detect(device_t dev, struct mss_info *mss);
static char *ymf_test(device_t dev, struct mss_info *mss);
static void ad_unmute(struct mss_info *mss);
@ -97,6 +104,12 @@ static void ad_write_cnt(struct mss_info *mss, int reg, u_short data);
static void ad_enter_MCE(struct mss_info *mss);
static void ad_leave_MCE(struct mss_info *mss);
/* OPTi-specific functions */
static void opti_write(struct mss_info *mss, u_char reg,
u_char data);
static u_char opti_read(struct mss_info *mss, u_char reg);
static int opti_init(device_t dev, struct mss_info *mss);
/* io primitives */
static void conf_wr(struct mss_info *mss, u_char reg, u_char data);
static u_char conf_rd(struct mss_info *mss, u_char reg);
@ -144,8 +157,10 @@ static pcmchan_caps opti931_caps = {4000, 48000, opti931_fmt, 0};
#define MD_AD1848 0x91
#define MD_AD1845 0x92
#define MD_CS42XX 0xA1
#define MD_OPTI930 0xB0
#define MD_OPTI931 0xB1
#define MD_OPTI925 0xB2
#define MD_OPTI924 0xB3
#define MD_GUSPNP 0xB8
#define MD_GUSMAX 0xB9
#define MD_YM0020 0xC1
@ -288,9 +303,9 @@ mss_alloc_resources(struct mss_info *mss, device_t dev)
mss->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq2_rid,
0, ~0, 1, RF_ACTIVE);
if (!mss->io_base || !mss->drq1 || !mss->irq) ok = 0;
if (mss->conf_rid >= 0 && !mss->conf_base) ok = 0;
if (mss->drq2_rid >= 0 && !mss->drq2) ok = 0;
if (!mss->io_base || !mss->drq1 || !mss->irq) ok = 0;
if (mss->conf_rid >= 0 && !mss->conf_base) ok = 0;
if (mss->drq2_rid >= 0 && !mss->drq2) ok = 0;
if (ok) {
pdma = rman_get_start(mss->drq1);
@ -344,9 +359,20 @@ static int
mss_mixer_set(struct mss_info *mss, int dev, int left, int right)
{
int regoffs;
mixer_tab *mix_d = (mss->bd_id == MD_OPTI931)? &opti931_devices : &mix_devices;
mixer_tab *mix_d;
u_char old, val;
switch (mss->bd_id) {
case MD_OPTI931:
mix_d = &opti931_devices;
break;
case MD_OPTI930:
mix_d = &opti930_devices;
break;
default:
mix_d = &mix_devices;
}
if ((*mix_d)[dev][LEFT_CHN].nbits == 0) {
DEB(printf("nbits = 0 for dev %d\n", dev));
return -1;
@ -390,6 +416,10 @@ mssmix_init(snd_mixer *m)
mix_setdevs(m, MODE2_MIXER_DEVICES);
mix_setrecdevs(m, MSS_REC_DEVICES);
switch(mss->bd_id) {
case MD_OPTI930:
mix_setdevs(m, OPTI930_MIXER_DEVICES);
break;
case MD_OPTI931:
mix_setdevs(m, OPTI931_MIXER_DEVICES);
ad_write(mss, 20, 0x88);
@ -1241,7 +1271,21 @@ mss_detect(device_t dev, struct mss_info *mss)
name = "AD1848";
mss->bd_id = MD_AD1848; /* AD1848 or CS4248 */
/*
if (opti_detect(dev, mss)) {
switch (mss->bd_id) {
case MD_OPTI924:
name = "OPTi924";
break;
case MD_OPTI930:
name = "OPTi930";
break;
}
printf("Found OPTi device %s\n", name);
if (opti_init(dev, mss) == 0) goto gotit;
}
/*
* Check that the I/O address is in use.
*
* bit 7 of the base I/O port is known to be 0 after the chip has
@ -1446,6 +1490,45 @@ mss_detect(device_t dev, struct mss_info *mss)
return ENXIO;
}
static int
opti_detect(device_t dev, struct mss_info *mss)
{
int c;
static const struct opticard {
int boardid;
int passwdreg;
int password;
int base;
int indir_reg;
} cards[] = {
{ MD_OPTI930, 0, 0xe4, 0xf8f, 0xe0e }, /* 930 */
{ MD_OPTI924, 3, 0xe5, 0xf8c, 0, }, /* 924 */
{ 0 },
};
mss->conf_rid = 3;
mss->indir_rid = 4;
for (c = 0; cards[c].base; c++) {
mss->optibase = cards[c].base;
mss->password = cards[c].password;
mss->passwdreg = cards[c].passwdreg;
mss->bd_id = cards[c].boardid;
if (cards[c].indir_reg)
mss->indir = bus_alloc_resource(dev, SYS_RES_IOPORT,
&mss->indir_rid, cards[c].indir_reg,
cards[c].indir_reg+1, 1, RF_ACTIVE);
mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&mss->conf_rid, mss->optibase, mss->optibase+9,
9, RF_ACTIVE);
if (opti_read(mss, 1) != 0xff) {
return 1;
}
}
return 0;
}
static char *
ymf_test(device_t dev, struct mss_info *mss)
{
@ -1707,6 +1790,7 @@ static struct isa_pnp_id pnpmss_ids[] = {
{0x1110d315, "ENSONIQ SoundscapeVIVO"}, /* ENS1011 */
{0x1093143e, "OPTi931"}, /* OPT9310 */
{0x5092143e, "OPTi925"}, /* OPT9250 XXX guess */
{0x0000143e, "OPTi924"}, /* OPT0924 */
{0x1022b839, "Neomagic 256AV (non-ac97)"}, /* NMX2210 */
#if 0
{0x0000561e, "GusPnP"}, /* GRV0000 */
@ -1772,6 +1856,18 @@ pnpmss_attach(device_t dev)
mss->bd_id = MD_OPTI925;
break;
case 0x0000143e: /* OPT0924 */
mss->password = 0xe5;
mss->passwdreg = 3;
mss->optibase = 0xf0c;
mss->io_rid = 2;
mss->conf_rid = 3;
mss->bd_id = MD_OPTI924;
mss->bd_flags |= BD_F_924PNP;
if(opti_init(dev, mss) != 0)
return ENXIO;
break;
case 0x1022b839: /* NMX2210 */
mss->io_rid = 1;
break;
@ -1798,6 +1894,147 @@ pnpmss_attach(device_t dev)
return mss_doattach(dev, mss);
}
static int
opti_init(device_t dev, struct mss_info *mss)
{
int flags = device_get_flags(dev);
int basebits = 0;
if (!mss->conf_base) {
bus_set_resource(dev, SYS_RES_IOPORT, mss->conf_rid,
mss->optibase, 0x9);
mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&mss->conf_rid, mss->optibase, mss->optibase+0x9,
0x9, RF_ACTIVE);
}
if (!mss->conf_base)
return ENXIO;
if (!mss->io_base)
mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&mss->io_rid, 0, ~0, 8, RF_ACTIVE);
if (!mss->io_base) /* No hint specified, use 0x530 */
mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT,
&mss->io_rid, 0x530, 0x537, 8, RF_ACTIVE);
if (!mss->io_base)
return ENXIO;
switch (rman_get_start(mss->io_base)) {
case 0x530:
basebits = 0x0;
break;
case 0xe80:
basebits = 0x10;
break;
case 0xf40:
basebits = 0x20;
break;
case 0x604:
basebits = 0x30;
break;
default:
printf("opti_init: invalid MSS base address!\n");
return ENXIO;
}
switch (mss->bd_id) {
case MD_OPTI924:
opti_write(mss, 1, 0x80 | basebits); /* MSS mode */
opti_write(mss, 2, 0x00); /* Disable CD */
opti_write(mss, 3, 0xf0); /* Disable SB IRQ */
opti_write(mss, 4, 0xf0);
opti_write(mss, 5, 0x00);
opti_write(mss, 6, 0x02); /* MPU stuff */
break;
case MD_OPTI930:
opti_write(mss, 1, 0x00 | basebits);
opti_write(mss, 3, 0x00); /* Disable SB IRQ/DMA */
opti_write(mss, 4, 0x52); /* Empty FIFO */
opti_write(mss, 5, 0x3c); /* Mode 2 */
opti_write(mss, 6, 0x02); /* Enable MSS */
break;
}
if (mss->bd_flags & BD_F_924PNP) {
u_int32_t irq = isa_get_irq(dev);
u_int32_t drq = isa_get_drq(dev);
bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
bus_set_resource(dev, SYS_RES_DRQ, mss->drq1_rid, drq, 1);
if (flags & DV_F_DUAL_DMA) {
bus_set_resource(dev, SYS_RES_DRQ, 1,
flags & DV_F_DRQ_MASK, 1);
mss->drq2_rid = 1;
}
}
/* OPTixxx has I/DRQ registers */
device_set_flags(dev, device_get_flags(dev) | DV_F_TRUE_MSS);
if (mss->indir) {
bus_release_resource(dev, SYS_RES_IOPORT, mss->indir_rid,
mss->indir);
mss->indir_rid = -1;
}
return 0;
}
static void
opti_write(struct mss_info *mss, u_char reg, u_char val)
{
port_wr(mss->conf_base, mss->passwdreg, mss->password);
switch(mss->bd_id) {
case MD_OPTI924:
if (reg > 7) { /* Indirect register */
port_wr(mss->conf_base, mss->passwdreg, reg);
port_wr(mss->conf_base, mss->passwdreg,
mss->password);
port_wr(mss->conf_base, 9, val);
return;
}
port_wr(mss->conf_base, reg, val);
break;
case MD_OPTI930: /* XXX should use proper bus calls */
port_wr(mss->indir, 0, reg);
port_wr(mss->conf_base, mss->passwdreg, mss->password);
port_wr(mss->indir, 1, val);
break;
}
}
u_char
opti_read(struct mss_info *mss, u_char reg)
{
port_wr(mss->conf_base, mss->passwdreg, mss->password);
switch(mss->bd_id) {
case MD_OPTI924:
if (reg > 7) { /* Indirect register */
port_wr(mss->conf_base, mss->passwdreg, reg);
port_wr(mss->conf_base, mss->passwdreg, mss->password);
return(port_rd(mss->conf_base, 9));
}
return(port_rd(mss->conf_base, reg));
break;
case MD_OPTI930:
port_wr(mss->indir, 0, reg);
port_wr(mss->conf_base, mss->passwdreg, mss->password);
return port_rd(mss->indir, 1);
break;
}
return -1;
}
static device_method_t pnpmss_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, pnpmss_probe),

View File

@ -107,6 +107,7 @@ ahead.
#define BD_F_TMR_RUN 0x0004
#define BD_F_MSS_OFFSET 0x0008 /* offset mss writes by -4 */
#define BD_F_DUPLEX 0x0010
#define BD_F_924PNP 0x0020 /* OPTi924 is in PNP mode */
/*
* sound/ad1848_mixer.h
@ -152,8 +153,9 @@ ahead.
/*
* Table of mixer registers. There is a default table for the
* AD1848/CS423x clones, and one for the OPTI931. As more MSS
* clones come out, there ought to be more tables.
* AD1848/CS423x clones, one for the OPTI931 and one for the
* OPTi930. As more MSS clones come out, there ought to be
* more tables.
*
* Fields in the table are : polarity, register, offset, bits
*
@ -206,6 +208,30 @@ MIX_NONE(SOUND_MIXER_LINE3),
SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN )
mixer_ent opti930_devices[32][2] = {
MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 0, 4, 23, 1, 0, 4),
MIX_NONE(SOUND_MIXER_BASS),
MIX_NONE(SOUND_MIXER_TREBLE),
MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 4, 5, 1, 0, 4),
MIX_ENT(SOUND_MIXER_PCM, 6, 1, 1, 5, 7, 1, 1, 5),
MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4),
MIX_NONE(SOUND_MIXER_SPEAKER),
MIX_ENT(SOUND_MIXER_MIC, 21, 1, 0, 4, 22, 1, 0, 4),
MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4),
MIX_NONE(SOUND_MIXER_IMIX),
MIX_NONE(SOUND_MIXER_ALTPCM),
MIX_NONE(SOUND_MIXER_RECLEV),
MIX_NONE(SOUND_MIXER_IGAIN),
MIX_NONE(SOUND_MIXER_OGAIN),
MIX_NONE(SOUND_MIXER_LINE1),
MIX_NONE(SOUND_MIXER_LINE2),
MIX_NONE(SOUND_MIXER_LINE3),
};
#define OPTI930_MIXER_DEVICES \
(SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \
SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD )
/*
* entries for the opti931...
*/