diff options
Diffstat (limited to 'sys/dev/sound/pci/ich.c')
-rw-r--r-- | sys/dev/sound/pci/ich.c | 258 |
1 files changed, 154 insertions, 104 deletions
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index 3b01553..8c20511 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -41,11 +41,81 @@ SND_DECLARE_FILE("$FreeBSD$"); #define ICH_DEFAULT_BUFSZ 16384 #define ICH_MAX_BUFSZ 65536 -#define SIS7012ID 0x70121039 /* SiS 7012 needs special handling */ -#define ICH4ID 0x24c58086 /* ICH4 needs special handling too */ -#define ICH5ID 0x24d58086 /* ICH5 needs to be treated as ICH4 */ -#define I6300ESBID 0x25a68086 /* 6300ESB needs to be treated as ICH4 */ -#define ICH6ID 0x266e8086 /* ICH6 needs to be treated as ICH4 */ +#define INTEL_VENDORID 0x8086 +#define SIS_VENDORID 0x1039 +#define NVIDIA_VENDORID 0x10de +#define AMD_VENDORID 0x1022 + +#define INTEL_82440MX 0x7195 +#define INTEL_82801AA 0x2415 +#define INTEL_82801AB 0x2425 +#define INTEL_82801BA 0x2445 +#define INTEL_82801CA 0x2485 +#define INTEL_82801DB 0x24c5 /* ICH4 needs special handling */ +#define INTEL_82801EB 0x24d5 /* ICH5 needs to be treated as ICH4 */ +#define INTEL_6300ESB 0x25a6 /* 6300ESB needs to be treated as ICH4 */ +#define INTEL_82801FB 0x266e /* ICH6 needs to be treated as ICH4 */ +#define INTEL_82801GB 0x27de /* ICH7 needs to be treated as ICH4 */ +#define SIS_7012 0x7012 /* SiS 7012 needs special handling */ +#define NVIDIA_NFORCE 0x01b1 +#define NVIDIA_NFORCE2 0x006a +#define NVIDIA_NFORCE2_400 0x008a +#define NVIDIA_NFORCE3 0x00da +#define NVIDIA_NFORCE3_250 0x00ea +#define NVIDIA_NFORCE4 0x0059 +#define AMD_768 0x7445 +#define AMD_8111 0x746d + +#define ICH_LOCK(sc) snd_mtxlock((sc)->ich_lock) +#define ICH_UNLOCK(sc) snd_mtxunlock((sc)->ich_lock) +#define ICH_LOCK_ASSERT(sc) snd_mtxassert((sc)->ich_lock) + +static const struct ich_type { + uint16_t vendor; + uint16_t devid; + uint32_t options; +#define PROBE_LOW 0x01 + char *name; +} ich_devs[] = { + { INTEL_VENDORID, INTEL_82440MX, 0, + "Intel 440MX" }, + { INTEL_VENDORID, INTEL_82801AA, 0, + "Intel ICH (82801AA)" }, + { INTEL_VENDORID, INTEL_82801AB, 0, + "Intel ICH (82801AB)" }, + { INTEL_VENDORID, INTEL_82801BA, 0, + "Intel ICH2 (82801BA)" }, + { INTEL_VENDORID, INTEL_82801CA, 0, + "Intel ICH3 (82801CA)" }, + { INTEL_VENDORID, INTEL_82801DB, PROBE_LOW, + "Intel ICH4 (82801DB)" }, + { INTEL_VENDORID, INTEL_82801EB, PROBE_LOW, + "Intel ICH5 (82801EB)" }, + { INTEL_VENDORID, INTEL_6300ESB, PROBE_LOW, + "Intel 6300ESB" }, + { INTEL_VENDORID, INTEL_82801FB, PROBE_LOW, + "Intel ICH6 (82801FB)" }, + { INTEL_VENDORID, INTEL_82801GB, PROBE_LOW, + "Intel ICH7 (82801GB)" }, + { SIS_VENDORID, SIS_7012, 0, + "SiS 7012" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE, 0, + "nVidia nForce" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE2, 0, + "nVidia nForce2" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE2_400, 0, + "nVidia nForce2 400" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE3, 0, + "nVidia nForce3" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE3_250, 0, + "nVidia nForce3 250" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE4, 0, + "nVidia nForce4" }, + { AMD_VENDORID, AMD_768, 0, + "AMD-768" }, + { AMD_VENDORID, AMD_8111, 0, + "AMD-8111" } +}; /* buffer descriptor */ struct ich_desc { @@ -93,6 +163,9 @@ struct sc_info { bus_addr_t desc_addr; struct intr_config_hook intrhook; int use_intrhook; + uint16_t vendor; + uint16_t devid; + struct mtx *ich_lock; }; /* -------------------------------------------------------------------- */ @@ -106,7 +179,7 @@ static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0}; /* -------------------------------------------------------------------- */ /* Hardware */ -static u_int32_t +static __inline u_int32_t ich_rd(struct sc_info *sc, int regno, int size) { switch (size) { @@ -121,7 +194,7 @@ ich_rd(struct sc_info *sc, int regno, int size) } } -static void +static __inline void ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { @@ -190,15 +263,15 @@ AC97_DECLARE(ich_ac97); static void ich_filldtbl(struct sc_chinfo *ch) { + struct sc_info *sc = ch->parent; u_int32_t base; int i; base = sndbuf_getbufaddr(ch->buffer); - ch->blkcnt = sndbuf_getsize(ch->buffer) / ch->blksz; - if (ch->blkcnt != 2 && ch->blkcnt != 4 && ch->blkcnt != 8 && ch->blkcnt != 16 && ch->blkcnt != 32) { - ch->blkcnt = 2; - ch->blksz = sndbuf_getsize(ch->buffer) / ch->blkcnt; - } + if (ch->blksz > sc->bufsz / ch->blkcnt) + ch->blksz = sc->bufsz / ch->blkcnt; + sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz); + ch->blksz = sndbuf_getblksz(ch->buffer); for (i = 0; i < ICH_DTBL_LENGTH; i++) { ch->dtbl[i].buffer = base + (ch->blksz * (i % ch->blkcnt)); @@ -249,6 +322,7 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * struct sc_chinfo *ch; unsigned int num; + ICH_LOCK(sc); num = sc->chnum++; ch = &sc->ch[num]; ch->num = num; @@ -288,10 +362,13 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * return NULL; } + ICH_UNLOCK(sc); if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0) return NULL; + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); + ICH_UNLOCK(sc); return ch; } @@ -309,16 +386,20 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed) struct sc_info *sc = ch->parent; if (ch->spdreg) { - int r; + int r, ac97rate; + + ICH_LOCK(sc); if (sc->ac97rate <= 32000 || sc->ac97rate >= 64000) sc->ac97rate = 48000; - r = (speed * 48000) / sc->ac97rate; + ac97rate = sc->ac97rate; + ICH_UNLOCK(sc); + r = (speed * 48000) / ac97rate; /* * Cast the return value of ac97_setrate() to u_int so that * the math don't overflow into the negative range. */ ch->spd = ((u_int)ac97_setrate(sc->codec, ch->spdreg, r) * - sc->ac97rate) / 48000; + ac97rate) / 48000; } else { ch->spd = 48000; } @@ -333,7 +414,9 @@ ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) ch->blksz = blocksize; ich_filldtbl(ch); + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ch->blkcnt - 1, 1); + ICH_UNLOCK(sc); return ch->blksz; } @@ -347,12 +430,16 @@ ichchan_trigger(kobj_t obj, void *data, int go) switch (go) { case PCMTRIG_START: ch->run = 1; + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1); + ICH_UNLOCK(sc); break; case PCMTRIG_ABORT: + ICH_LOCK(sc); ich_resetchan(sc, ch->num); + ICH_UNLOCK(sc); ch->run = 0; break; } @@ -366,7 +453,9 @@ ichchan_getptr(kobj_t obj, void *data) struct sc_info *sc = ch->parent; u_int32_t pos; + ICH_LOCK(sc); ch->civ = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1) % ch->blkcnt; + ICH_UNLOCK(sc); pos = ch->civ * ch->blksz; @@ -404,6 +493,7 @@ ich_intr(void *p) u_int32_t cbi, lbi, lvi, st, gs; int i; + ICH_LOCK(sc); gs = ich_rd(sc, ICH_REG_GLOB_STA, 4) & ICH_GLOB_STA_IMASK; if (gs & (ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES)) { /* Clear resume interrupt(s) - nothing doing with them */ @@ -422,8 +512,11 @@ ich_intr(void *p) st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI; if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) { /* block complete - update buffer */ - if (ch->run) + if (ch->run) { + ICH_UNLOCK(sc); chn_intr(ch->channel); + ICH_LOCK(sc); + } lvi = ich_rd(sc, ch->regbase + ICH_REG_X_LVI, 1); cbi = ch->civ % ch->blkcnt; if (cbi == 0) @@ -444,6 +537,7 @@ ich_intr(void *p) (sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR), st, 2); } + ICH_UNLOCK(sc); if (gs != 0) { device_printf(sc->dev, "Unhandled interrupt, gs_intr = %x\n", gs); @@ -586,10 +680,10 @@ ich_init(struct sc_info *sc) if ((stat & ICH_GLOB_STA_PCR) == 0) { /* ICH4/ICH5 may fail when busmastering is enabled. Continue */ - if ((pci_get_devid(sc->dev) != ICH4ID) && - (pci_get_devid(sc->dev) != ICH5ID) && - (pci_get_devid(sc->dev) != I6300ESBID) && - (pci_get_devid(sc->dev) != ICH6ID)) { + if (sc->vendor == INTEL_VENDORID && ( + sc->devid == INTEL_82801DB || sc->devid == INTEL_82801EB || + sc->devid == INTEL_6300ESB || sc->devid == INTEL_82801FB || + sc->devid == INTEL_82801GB)) { return ENXIO; } } @@ -616,104 +710,47 @@ ich_init(struct sc_info *sc) static int ich_pci_probe(device_t dev) { - switch(pci_get_devid(dev)) { - case 0x71958086: - device_set_desc(dev, "Intel 443MX"); - return BUS_PROBE_DEFAULT; - - case 0x24158086: - device_set_desc(dev, "Intel ICH (82801AA)"); - return BUS_PROBE_DEFAULT; - - case 0x24258086: - device_set_desc(dev, "Intel ICH (82801AB)"); - return BUS_PROBE_DEFAULT; - - case 0x24458086: - device_set_desc(dev, "Intel ICH2 (82801BA)"); - return BUS_PROBE_DEFAULT; - - case 0x24858086: - device_set_desc(dev, "Intel ICH3 (82801CA)"); - return BUS_PROBE_DEFAULT; - - case ICH4ID: - device_set_desc(dev, "Intel ICH4 (82801DB)"); - return BUS_PROBE_LOW_PRIORITY; - - case ICH5ID: - device_set_desc(dev, "Intel ICH5 (82801EB)"); - return BUS_PROBE_LOW_PRIORITY; - - case I6300ESBID: - device_set_desc(dev, "Intel 6300ESB"); - return BUS_PROBE_LOW_PRIORITY; - - case ICH6ID: - device_set_desc(dev, "Intel ICH6 (82801FB)"); - return BUS_PROBE_LOW_PRIORITY; - - case SIS7012ID: - device_set_desc(dev, "SiS 7012"); - return BUS_PROBE_DEFAULT; - - case 0x01b110de: - device_set_desc(dev, "nVidia nForce"); - return BUS_PROBE_DEFAULT; - - case 0x006a10de: - device_set_desc(dev, "nVidia nForce2"); - return BUS_PROBE_DEFAULT; - - case 0x008a10de: - device_set_desc(dev, "nVidia nForce2 400"); - return BUS_PROBE_DEFAULT; - - case 0x00da10de: - device_set_desc(dev, "nVidia nForce3"); - return BUS_PROBE_DEFAULT; - - case 0x00ea10de: - device_set_desc(dev, "nVidia nForce3 250"); - return BUS_PROBE_DEFAULT; - - case 0x005910de: - device_set_desc(dev, "nVidia nForce4"); - return BUS_PROBE_DEFAULT; - - case 0x74451022: - device_set_desc(dev, "AMD-768"); - return BUS_PROBE_DEFAULT; - - case 0x746d1022: - device_set_desc(dev, "AMD-8111"); - return BUS_PROBE_DEFAULT; - - default: - return ENXIO; + int i; + uint16_t devid, vendor; + + vendor = pci_get_vendor(dev); + devid = pci_get_device(dev); + for (i = 0; i < sizeof(ich_devs)/sizeof(ich_devs[0]); i++) { + if (vendor == ich_devs[i].vendor && + devid == ich_devs[i].devid) { + device_set_desc(dev, ich_devs[i].name); + /* allow a better driver to override us */ + if ((ich_devs[i].options & PROBE_LOW) != 0) + return (BUS_PROBE_LOW_PRIORITY); + return (BUS_PROBE_DEFAULT); + } } + return (ENXIO); } static int ich_pci_attach(device_t dev) { u_int16_t extcaps; + uint16_t devid, vendor; struct sc_info *sc; char status[SND_STATUSLEN]; - if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { + if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } - bzero(sc, sizeof(*sc)); + sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); sc->dev = dev; + vendor = sc->vendor = pci_get_vendor(dev); + devid = sc->devid = pci_get_device(dev); /* * The SiS 7012 register set isn't quite like the standard ich. * There really should be a general "quirks" mechanism. */ - if (pci_get_devid(dev) == SIS7012ID) { + if (vendor == SIS_VENDORID && devid == SIS_7012) { sc->swap_reg = 1; sc->sample_size = 1; } else { @@ -728,7 +765,7 @@ ich_pci_attach(device_t dev) * but doing so will mess things up here. ich4 has enough new * features it warrants it's own driver. */ - if (pci_get_devid(dev) == ICH4ID) { + if (vendor == INTEL_VENDORID && devid == INTEL_82801DB) { pci_write_config(dev, PCIR_ICH_LEGACY, ICH_LEGACY_ENABLE, 1); } @@ -738,9 +775,9 @@ ich_pci_attach(device_t dev) */ pci_enable_busmaster(dev); - if (pci_get_devid(dev) == ICH5ID || - pci_get_devid(dev) == I6300ESBID || - pci_get_devid(dev) == ICH6ID) { + if (vendor == INTEL_VENDORID && (devid == INTEL_82801EB || + devid == INTEL_6300ESB || devid == INTEL_82801FB || + devid == INTEL_82801GB)) { sc->nambarid = PCIR_MMBAR; sc->nabmbarid = PCIR_MBBAR; sc->regtype = SYS_RES_MEMORY; @@ -768,7 +805,7 @@ ich_pci_attach(device_t dev) sc->bufsz = pcm_getbuffersize(dev, 4096, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ); if (bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sc->bufsz, 1, 0x3ffff, 0, - busdma_lock_mutex, &Giant, &sc->dmat) != 0) { + NULL, NULL, &sc->dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -776,7 +813,7 @@ ich_pci_attach(device_t dev) sc->irqid = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || snd_setup_intr(dev, sc->irq, 0, ich_intr, sc, &sc->ih)) { + if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -837,6 +874,8 @@ bad: if (sc->nabmbar) bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); + if (sc->ich_lock) + snd_mtxfree(sc->ich_lock); free(sc, M_DEVBUF); return ENXIO; } @@ -857,6 +896,7 @@ ich_pci_detach(device_t dev) bus_release_resource(dev, sc->regtype, sc->nambarid, sc->nambar); bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); bus_dma_tag_destroy(sc->dmat); + snd_mtxfree(sc->ich_lock); free(sc, M_DEVBUF); return 0; } @@ -890,12 +930,16 @@ ich_pci_suspend(device_t dev) int i; sc = pcm_getdevinfo(dev); + ICH_LOCK(sc); for (i = 0 ; i < 3; i++) { sc->ch[i].run_save = sc->ch[i].run; if (sc->ch[i].run) { + ICH_UNLOCK(sc); ichchan_trigger(0, &sc->ch[i], PCMTRIG_ABORT); + ICH_LOCK(sc); } } + ICH_UNLOCK(sc); return 0; } @@ -913,9 +957,11 @@ ich_pci_resume(device_t dev) pci_enable_io(dev, SYS_RES_MEMORY); pci_enable_busmaster(dev); + ICH_LOCK(sc); /* Reinit audio device */ if (ich_init(sc) == -1) { device_printf(dev, "unable to reinitialize the card\n"); + ICH_UNLOCK(sc); return ENXIO; } /* Reinit mixer */ @@ -923,17 +969,21 @@ ich_pci_resume(device_t dev) ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm); if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); + ICH_UNLOCK(sc); return ENXIO; } /* Re-start DMA engines */ for (i = 0 ; i < 3; i++) { struct sc_chinfo *ch = &sc->ch[i]; if (sc->ch[i].run_save) { + ICH_UNLOCK(sc); ichchan_setblocksize(0, ch, ch->blksz); ichchan_setspeed(0, ch, ch->spd); ichchan_trigger(0, ch, PCMTRIG_START); + ICH_LOCK(sc); } } + ICH_UNLOCK(sc); return 0; } |