summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2005-11-14 18:18:12 +0000
committerariff <ariff@FreeBSD.org>2005-11-14 18:18:12 +0000
commit4d50403e742296287fbcb97ba2f16c7bea90d01c (patch)
tree7453366a8c1ec99ccca0e446010d4ba986822dd7 /sys
parent00b7f829bdfebf59c3bf6f11e5642fd2d19a2a52 (diff)
downloadFreeBSD-src-4d50403e742296287fbcb97ba2f16c7bea90d01c.zip
FreeBSD-src-4d50403e742296287fbcb97ba2f16c7bea90d01c.tar.gz
- Added few more Intel HDA ids (ICH 6/7) which does have backward
compatible AC97 codec. - As the driver supports so many variants, create a table ids for ease of probing and maintenance. Submitted by: yongari Reviewed/Tested by: multimedia@ - From luigi: The code to compute fragment sizes in the ich driver almost invariably ends up using the full buffer available, no matter how the user specifies fragment size and number. With audio telephony (8khz, 16bit-stereo) and the 16k buffer size this results in an unbearable 500ms delay. This patch makes sure that we never use more than 4 fragments, (i don't think we need more unless there are huge interrupt servicing latencies), and obey to the requested fragment size, so that latency is acceptable. Based on this (and after much regression tests), I can conclude that this driver works best with 2 fragments, thus solving various long standing issues of ICH driver not capable to flush or play short files perfectly. Suggested by: luigi (the idea of smaller fragments) - MPSAFE conversion. Approved by: netchild (mentor)
Diffstat (limited to 'sys')
-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