summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pci/ich.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/sound/pci/ich.c')
-rw-r--r--sys/dev/sound/pci/ich.c258
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;
}
OpenPOWER on IntegriCloud