From 64738af72be9ec8b03535c0354b084351d5f0c57 Mon Sep 17 00:00:00 2001 From: Cameron Grant Date: Sat, 24 Mar 2001 00:22:01 +0000 Subject: [PATCH] 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 --- sys/dev/sound/isa/mss.c | 247 +++++++++++++++++++++++++++++++++++++++- sys/dev/sound/isa/mss.h | 30 ++++- 2 files changed, 270 insertions(+), 7 deletions(-) diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index 210e304d3373..cc9ad6b90737 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2001 George Reid * Copyright (c) 1999 Cameron Grant * 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), diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h index 2491dbd1a13a..b3a2f1cbe572 100644 --- a/sys/dev/sound/isa/mss.h +++ b/sys/dev/sound/isa/mss.h @@ -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... */