diff options
author | cg <cg@FreeBSD.org> | 2001-07-03 17:27:13 +0000 |
---|---|---|
committer | cg <cg@FreeBSD.org> | 2001-07-03 17:27:13 +0000 |
commit | 5a82aadeb1b9d81151a5e1d339e517dd4bc477e7 (patch) | |
tree | 3c935f8916ed7f6706a0c1a886f588d0fd6998ec /sys/dev | |
parent | 19461a1e25b2ed79c83c9b80c62fb272df5ec443 (diff) | |
download | FreeBSD-src-5a82aadeb1b9d81151a5e1d339e517dd4bc477e7.zip FreeBSD-src-5a82aadeb1b9d81151a5e1d339e517dd4bc477e7.tar.gz |
* simplify
* add support for mic record channel
* add support for setblocksize
* make mpsafe
* make getptr accurate
* reduce buffer size from 64k to 16k for better synchronisation
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/sound/pci/ich.c | 1056 | ||||
-rw-r--r-- | sys/dev/sound/pci/ich.h | 92 |
2 files changed, 325 insertions, 823 deletions
diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index 714d3ef..1031536 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2000 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp> + * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,92 +29,17 @@ #include <dev/sound/pcm/sound.h> #include <dev/sound/pcm/ac97.h> +#include <dev/sound/pci/ich.h> #include <pci/pcireg.h> #include <pci/pcivar.h> - /* -------------------------------------------------------------------- */ -#define ICH_RECPRIMARY 0 - #define ICH_TIMEOUT 1000 /* semaphore timeout polling count */ +#define ICH_DTBL_LENGTH 32 +#define ICH_DEFAULT_BUFSZ 16384 -#define PCIR_NAMBAR 0x10 -#define PCIR_NABMBAR 0x14 - -/* Native Audio Bus Master Control Registers */ -#define ICH_REG_PI_BDBAR 0x00 -#define ICH_REG_PI_CIV 0x04 -#define ICH_REG_PI_LVI 0x05 -#define ICH_REG_PI_SR 0x06 -#define ICH_REG_PI_PICB 0x08 -#define ICH_REG_PI_PIV 0x0a -#define ICH_REG_PI_CR 0x0b -#define ICH_REG_PO_BDBAR 0x10 -#define ICH_REG_PO_CIV 0x14 -#define ICH_REG_PO_LVI 0x15 -#define ICH_REG_PO_SR 0x16 -#define ICH_REG_PO_PICB 0x18 -#define ICH_REG_PO_PIV 0x1a -#define ICH_REG_PO_CR 0x1b -#define ICH_REG_MC_BDBAR 0x20 -#define ICH_REG_MC_CIV 0x24 -#define ICH_REG_MC_LVI 0x25 -#define ICH_REG_MC_SR 0x26 -#define ICH_REG_MC_PICB 0x28 -#define ICH_REG_MC_PIV 0x2a -#define ICH_REG_MC_CR 0x2b -#define ICH_REG_GLOB_CNT 0x2c -#define ICH_REG_GLOB_STA 0x30 -#define ICH_REG_ACC_SEMA 0x34 -/* Status Register Values */ -#define ICH_X_SR_DCH 0x0001 -#define ICH_X_SR_CELV 0x0002 -#define ICH_X_SR_LVBCI 0x0004 -#define ICH_X_SR_BCIS 0x0008 -#define ICH_X_SR_FIFOE 0x0010 -/* Control Register Values */ -#define ICH_X_CR_RPBM 0x01 -#define ICH_X_CR_RR 0x02 -#define ICH_X_CR_LVBIE 0x04 -#define ICH_X_CR_FEIE 0x08 -#define ICH_X_CR_IOCE 0x10 -/* Global Control Register Values */ -#define ICH_GLOB_CTL_GIE 0x00000001 -#define ICH_GLOB_CTL_COLD 0x00000002 /* negate */ -#define ICH_GLOB_CTL_WARM 0x00000004 -#define ICH_GLOB_CTL_SHUT 0x00000008 -#define ICH_GLOB_CTL_PRES 0x00000010 -#define ICH_GLOB_CTL_SRES 0x00000020 -/* Global Status Register Values */ -#define ICH_GLOB_STA_GSCI 0x00000001 -#define ICH_GLOB_STA_MIINT 0x00000002 -#define ICH_GLOB_STA_MOINT 0x00000004 -#define ICH_GLOB_STA_PIINT 0x00000020 -#define ICH_GLOB_STA_POINT 0x00000040 -#define ICH_GLOB_STA_MINT 0x00000080 -#define ICH_GLOB_STA_PCR 0x00000100 -#define ICH_GLOB_STA_SCR 0x00000200 -#define ICH_GLOB_STA_PRES 0x00000400 -#define ICH_GLOB_STA_SRES 0x00000800 -#define ICH_GLOB_STA_SLOT12 0x00007000 -#define ICH_GLOB_STA_RCODEC 0x00008000 -#define ICH_GLOB_STA_AD3 0x00010000 -#define ICH_GLOB_STA_MD3 0x00020000 -#define ICH_GLOB_STA_IMASK (ICH_GLOB_STA_MIINT | ICH_GLOB_STA_MOINT | ICH_GLOB_STA_PIINT | ICH_GLOB_STA_POINT | ICH_GLOB_STA_MINT | ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES) - -/* AC'97 power/ready functions */ -#define AC97_POWER_PINPOWER 0x0100 -#define AC97_POWER_PINREADY 0x0001 -#define AC97_POWER_POUTPOWER 0x0200 -#define AC97_POWER_POUTREADY 0x0002 - -/* play/record buffer */ -#define ICH_FIFOINDEX 32 -#define ICH_BDC_IOC 0x80000000 -#define ICH_BDC_BUP 0x40000000 -#define ICH_DEFAULT_BLOCKSZ 2048 /* buffer descriptor */ struct ich_desc { volatile u_int32_t buffer; @@ -124,91 +50,44 @@ struct sc_info; /* channel registers */ struct sc_chinfo { - int run, spd, dir, fmt; + u_int32_t num, run; + u_int32_t blksz, blkcnt; + u_int32_t regbase, spdreg; + struct snd_dbuf *buffer; struct pcm_channel *channel; struct sc_info *parent; - struct ich_desc *index; - bus_dmamap_t imap; - u_int32_t lvi; + + struct ich_desc *dtbl; }; /* device private data */ struct sc_info { - device_t dev; - u_int32_t type, rev; - u_int32_t cd2id, ctrlbase; + device_t dev; + int hasvra, hasvrm; + int chnum; - struct resource *nambar, *nabmbar; - int nambarid, nabmbarid; + struct resource *nambar, *nabmbar, *irq; + int nambarid, nabmbarid, irqid; bus_space_tag_t nambart, nabmbart; bus_space_handle_t nambarh, nabmbarh; bus_dma_tag_t dmat; - struct resource *irq; - int irqid; - void *ih; + bus_dmamap_t dtmap; + void *ih; struct ac97_info *codec; - struct sc_chinfo *pi, *po; -}; - -struct { - u_int32_t dev, subdev; - char *name; -} ich_devs[] = { - {0x71958086, 0, "Intel 443MX"}, - {0x24138086, 0, "Intel 82801AA (ICH)"}, - {0x24158086, 0, "Intel 82801AA (ICH)"}, - {0x24258086, 0, "Intel 82901AB (ICH)"}, - {0x24458086, 0, "Intel 82801BA (ICH2)"}, - {0, 0, NULL} -}; - -/* variable rate audio */ -static u_int32_t ich_rate[] = { - 48000, 44100, 22050, 16000, 11025, 8000, 0 + struct sc_chinfo ch[3]; + struct ich_desc *dtbl; }; /* -------------------------------------------------------------------- */ -/* - * prototypes - */ - -/* channel interface */ -static void *ichpchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); -static int ichpchan_setformat(kobj_t, void *, u_int32_t); -static int ichpchan_setspeed(kobj_t, void *, u_int32_t); -static int ichpchan_setblocksize(kobj_t, void *, u_int32_t); -static int ichpchan_trigger(kobj_t, void *, int); -static int ichpchan_getptr(kobj_t, void *); -static struct pcmchan_caps *ichpchan_getcaps(kobj_t, void *); - -static void *ichrchan_init(kobj_t, void *, struct snd_dbuf *, struct pcm_channel *, int); -static int ichrchan_setformat(kobj_t, void *, u_int32_t); -static int ichrchan_setspeed(kobj_t, void *, u_int32_t); -static int ichrchan_setblocksize(kobj_t, void *, u_int32_t); -static int ichrchan_trigger(kobj_t, void *, int); -static int ichrchan_getptr(kobj_t, void *); -static struct pcmchan_caps *ichrchan_getcaps(kobj_t, void *); - -/* stuff */ -static int ich_init(struct sc_info *); -static void ich_intr(void *); - -/* -------------------------------------------------------------------- */ - -static u_int32_t ich_recfmt[] = { - AFMT_STEREO | AFMT_S16_LE, - 0 -}; -static struct pcmchan_caps ich_reccaps = {8000, 48000, ich_recfmt, 0}; - -static u_int32_t ich_playfmt[] = { +static u_int32_t ich_fmt[] = { AFMT_STEREO | AFMT_S16_LE, 0 }; -static struct pcmchan_caps ich_playcaps = {8000, 48000, ich_playfmt, 0}; +static struct pcmchan_caps ich_vrcaps = {8000, 48000, ich_fmt, 0}; +static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0}; /* -------------------------------------------------------------------- */ /* Hardware */ @@ -217,11 +96,11 @@ ich_rd(struct sc_info *sc, int regno, int size) { switch (size) { case 1: - return bus_space_read_1(sc->nambart, sc->nambarh, regno); + return bus_space_read_1(sc->nabmbart, sc->nabmbarh, regno); case 2: - return bus_space_read_2(sc->nambart, sc->nambarh, regno); + return bus_space_read_2(sc->nabmbart, sc->nabmbarh, regno); case 4: - return bus_space_read_4(sc->nambart, sc->nambarh, regno); + return bus_space_read_4(sc->nabmbart, sc->nabmbarh, regno); default: return 0xffffffff; } @@ -232,13 +111,13 @@ ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { case 1: - bus_space_write_1(sc->nambart, sc->nambarh, regno, data); + bus_space_write_1(sc->nabmbart, sc->nabmbarh, regno, data); break; case 2: - bus_space_write_2(sc->nambart, sc->nambarh, regno, data); + bus_space_write_2(sc->nabmbart, sc->nabmbarh, regno, data); break; case 4: - bus_space_write_4(sc->nambart, sc->nambarh, regno, data); + bus_space_write_4(sc->nabmbart, sc->nabmbarh, regno, data); break; } } @@ -250,9 +129,9 @@ ich_waitcd(void *devinfo) int i; u_int32_t data; struct sc_info *sc = (struct sc_info *)devinfo; - for (i = 0;i < ICH_TIMEOUT;i++) { - data = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_ACC_SEMA); + + for (i = 0; i < ICH_TIMEOUT; i++) { + data = ich_rd(sc, ICH_REG_ACC_SEMA, 1); if ((data & 0x01) == 0) return 0; } @@ -264,18 +143,22 @@ static int ich_rdcd(kobj_t obj, void *devinfo, int regno) { struct sc_info *sc = (struct sc_info *)devinfo; + regno &= 0xff; ich_waitcd(sc); - return ich_rd(sc, regno, 2); + + return bus_space_read_2(sc->nambart, sc->nambarh, regno); } static int -ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) +ich_wrcd(kobj_t obj, void *devinfo, int regno, u_int16_t data) { struct sc_info *sc = (struct sc_info *)devinfo; + regno &= 0xff; ich_waitcd(sc); - ich_wr(sc, regno, data, 2); + bus_space_write_2(sc->nambart, sc->nambarh, regno, data); + return 0; } @@ -287,758 +170,299 @@ static kobj_method_t ich_ac97_methods[] = { AC97_DECLARE(ich_ac97); /* -------------------------------------------------------------------- */ +/* common routines */ -/* channel common routines */ static void -ichchan_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +ich_filldtbl(struct sc_chinfo *ch) { - struct sc_chinfo *ch = arg; - - if (bootverbose) { - device_printf(ch->parent->dev, "setmap(0x%lx, 0x%lx)\n", (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len); - } -} - -static int -ichchan_initbuf(struct sc_chinfo *ch) -{ - struct sc_info *sc = ch->parent; + u_int32_t base; int i; - if (sndbuf_alloc(ch->buffer, sc->dmat, ICH_DEFAULT_BLOCKSZ * ICH_FIFOINDEX)) { - return ENOSPC; - } - if (bus_dmamem_alloc(sc->dmat,(void **)&ch->index, BUS_DMA_NOWAIT, &ch->imap)) { - sndbuf_free(ch->buffer); - return ENOSPC; + base = vtophys(sndbuf_getbuf(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 (bus_dmamap_load(sc->dmat, ch->imap, ch->index, - sizeof(struct ich_desc) * ICH_FIFOINDEX, ichchan_setmap, ch, 0)) { - bus_dmamem_free(sc->dmat, (void **)&ch->index, ch->imap); - sndbuf_free(ch->buffer); - return ENOSPC; - } - for (i = 0;i < ICH_FIFOINDEX;i++) { - ch->index[i].buffer = vtophys(sndbuf_getbuf(ch->buffer)) + - ICH_DEFAULT_BLOCKSZ * i; - if (ch->dir == PCMDIR_PLAY) - ch->index[i].length = 0; - else - ch->index[i].length = ICH_BDC_IOC + - ICH_DEFAULT_BLOCKSZ / 2; - } - return 0; -} - -static void -ichchan_free(struct sc_chinfo *ch) -{ - struct sc_info *sc = ch->parent; - bus_dmamap_unload(sc->dmat, ch->imap); - bus_dmamem_free(sc->dmat, (void **)&ch->index, ch->imap); - sndbuf_free(ch->buffer); + for (i = 0; i < ICH_DTBL_LENGTH; i++) { + ch->dtbl[i].buffer = base + (ch->blksz * (i % ch->blkcnt)); + ch->dtbl[i].length = ICH_BDC_IOC | (ch->blksz / 2); + } } -/* play channel interface */ static int -ichpchan_power(kobj_t obj, struct sc_info *sc, int sw) +ich_resetchan(struct sc_info *sc, int num) { - u_int32_t cr; - int i; + int i, cr, regbase; - cr = ich_rdcd(obj, sc, AC97_REG_POWER); - if (sw) { /* power on */ - cr &= ~AC97_POWER_POUTPOWER; - ich_wrcd(obj, sc, AC97_REG_POWER, cr); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = ich_rdcd(obj, sc, AC97_REG_POWER); - if ((cr & AC97_POWER_POUTREADY) != 0) - break; - } - } - else { /* power off */ - cr |= AC97_POWER_POUTPOWER; - ich_wrcd(obj, sc, AC97_REG_POWER, cr); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = ich_rdcd(obj, sc, AC97_REG_POWER); - if ((cr & AC97_POWER_POUTREADY) == 0) - break; - } - } - if (i == ICH_TIMEOUT) - return -1; - return 0; -} + if (num == 0) + regbase = ICH_REG_PO_BASE; + else if (num == 1) + regbase = ICH_REG_PI_BASE; + else if (num == 2) + regbase = ICH_REG_MC_BASE; + else + return ENXIO; -static u_int32_t -ichpchan_getminspeed(kobj_t obj, void *data) -{ - struct sc_chinfo *ch = data; - u_int32_t extcap; - u_int32_t minspeed = 48000; /* before AC'97 R2.0 */ - int i = 0; - - extcap = ac97_getextmode(ch->parent->codec); - if (extcap & AC97_EXTCAP_VRA) { - for (i = 0;ich_rate[i] != 0;i++) { - if (ac97_setrate(ch->parent->codec, AC97_REGEXT_FDACRATE, ich_rate[i]) == ich_rate[i]) - minspeed = ich_rate[i]; - } + ich_wr(sc, regbase + ICH_REG_X_CR, 0, 1); + DELAY(100); + ich_wr(sc, regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1); + for (i = 0; i < ICH_TIMEOUT; i++) { + cr = ich_rd(sc, regbase + ICH_REG_X_CR, 1); + if (cr == 0) + return 0; } - return minspeed; + + device_printf(sc->dev, "cannot reset channel %d\n", num); + return ENXIO; } +/* -------------------------------------------------------------------- */ +/* channel interface */ + static void * -ichpchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_chinfo *ch; - u_int32_t cr; - int i; + int num; - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, 0); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - ICH_X_CR_RR); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PO_CR); - if (cr == 0) - break; - } - if (i == ICH_TIMEOUT) { - device_printf(sc->dev, "cannot reset play codec\n"); - return NULL; - } - if (ichpchan_power(obj, sc, 1) == -1) { - device_printf(sc->dev, "play DAC not ready\n"); - return NULL; - } - ichpchan_power(obj, sc, 0); - if ((ch = malloc(sizeof(*ch), M_DEVBUF, M_NOWAIT)) == NULL) { - device_printf(sc->dev, "cannot allocate channel info area\n"); - return NULL; - } + num = sc->chnum++; + ch = &sc->ch[num]; + ch->num = num; ch->buffer = b; ch->channel = c; ch->parent = sc; - ch->dir = PCMDIR_PLAY; ch->run = 0; - ch->lvi = 0; - if (ichchan_initbuf(ch)) { - device_printf(sc->dev, "cannot allocate channel buffer\n"); - free(ch, M_DEVBUF); - return NULL; - } - bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_BDBAR, - (u_int32_t)vtophys(ch->index)); - sc->po = ch; - if (bootverbose) { - device_printf(sc->dev,"Play codec support rate(Hz): "); - for (i = 0;ich_rate[i] != 0;i++) { - if (ichpchan_setspeed(obj, ch, ich_rate[i]) == ich_rate[i]) { - printf("%d ", ich_rate[i]); - } - } - printf("\n"); - } - ich_playcaps.minspeed = ichpchan_getminspeed(obj, ch); - return ch; -} - -static int -ichpchan_free(kobj_t obj, void *data) -{ - struct sc_chinfo *ch = data; - ichchan_free(ch); - return ichpchan_power(obj, ch->parent, 0); -} - -static int -ichpchan_setformat(kobj_t obj, void *data, u_int32_t format) -{ - struct sc_chinfo *ch = data; - - ch->fmt = format; - return 0; -} - -static int -ichpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) -{ - struct sc_chinfo *ch = data; - u_int32_t extcap; - - extcap = ac97_getextmode(ch->parent->codec); - if (extcap & AC97_EXTCAP_VRA) { - ch->spd = (u_int32_t)ac97_setrate(ch->parent->codec, AC97_REGEXT_FDACRATE, speed); - } - else { - ch->spd = 48000; /* before AC'97 R2.0 */ - } -#if(0) - device_printf(ch->parent->dev, "ichpchan_setspeed():ch->spd = %d\n", ch->spd); -#endif - return ch->spd; -} - -static int -ichpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) -{ - return blocksize; -} - -/* update index */ -static void -ichpchan_update(struct sc_chinfo *ch) -{ - struct sc_info *sc = ch->parent; - u_int32_t lvi; - int fp; - int last; - int i; - - fp = sndbuf_getfreeptr(ch->buffer); - last = fp - 1; - if (last < 0) - last = ICH_DEFAULT_BLOCKSZ * ICH_FIFOINDEX - 1; - lvi = last / ICH_DEFAULT_BLOCKSZ; - if (lvi >= ch->lvi) { - for (i = ch->lvi;i < lvi;i++) - ch->index[i].length = - ICH_BDC_IOC + ICH_DEFAULT_BLOCKSZ / 2; - ch->index[i].length = ICH_BDC_IOC + ICH_BDC_BUP - + (last % ICH_DEFAULT_BLOCKSZ + 1) / 2; - } - else { - for (i = ch->lvi;i < ICH_FIFOINDEX;i++) - ch->index[i].length = - ICH_BDC_IOC + ICH_DEFAULT_BLOCKSZ / 2; - for (i = 0;i < lvi;i++) - ch->index[i].length = - ICH_BDC_IOC + ICH_DEFAULT_BLOCKSZ / 2; - ch->index[i].length = ICH_BDC_IOC + ICH_BDC_BUP - + (last % ICH_DEFAULT_BLOCKSZ + 1) / 2; - } - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_LVI, lvi); - ch->lvi = lvi; -#if(0) - device_printf(ch->parent->dev, "ichpchan_update():fp = %d, lvi = %d\n", fp, lvi); -#endif - return; -} -static void -ichpchan_fillblank(struct sc_chinfo *ch) -{ - struct sc_info *sc = ch->parent; - - ch->lvi++; - if (ch->lvi == ICH_FIFOINDEX) - ch->lvi = 0; - ch->index[ch->lvi].length = ICH_BDC_BUP + ICH_DEFAULT_BLOCKSZ / 2; - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_LVI, ch->lvi); - return; -} -/* semantic note: must start at beginning of buffer */ -static int -ichpchan_trigger(kobj_t obj, void *data, int go) -{ - struct sc_chinfo *ch = data; - struct sc_info *sc = ch->parent; - u_int32_t cr; - int i; - -#if(0) - device_printf(ch->parent->dev, "ichpchan_trigger(0x%08x, %d)\n", data, go); -#endif - switch (go) { - case PCMTRIG_START: -#if(0) - device_printf(ch->parent->dev, "ichpchan_trigger():PCMTRIG_START\n"); -#endif - ch->run = 1; - ichpchan_power(obj, sc, 1); - bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_BDBAR, - (u_int32_t)vtophys(ch->index)); - ch->lvi = ICH_FIFOINDEX - 1; - ichpchan_update(ch); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE | - ICH_X_CR_FEIE); - break; - case PCMTRIG_STOP: -#if(0) - device_printf(ch->parent->dev, "ichpchan_trigger():PCMTRIG_STOP\n"); -#endif - cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PO_CR); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - cr & ~ICH_X_CR_RPBM); - ichpchan_power(obj, sc, 0); - ch->run = 0; - break; - case PCMTRIG_ABORT: -#if(0) - device_printf(ch->parent->dev, "ichpchan_trigger():PCMTRIG_ABORT\n"); -#endif - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - 0); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - ICH_X_CR_RR); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PO_CR); - if (cr == 0) - break; - } - ichpchan_power(obj, sc, 0); - ch->run = 0; - ch->lvi = 0; + ch->dtbl = sc->dtbl + (ch->num * ICH_DTBL_LENGTH); + ch->blkcnt = 2; + ch->blksz = ICH_DEFAULT_BUFSZ / ch->blkcnt; + + switch(ch->num) { + case 0: /* play */ + KASSERT(dir == PCMDIR_PLAY, ("wrong direction")); + ch->regbase = ICH_REG_PO_BASE; + ch->spdreg = sc->hasvra? AC97_REGEXT_FDACRATE : 0; break; - default: - break; - } - return 0; -} -static int -ichpchan_getptr(kobj_t obj, void *data) -{ - struct sc_chinfo *ch = data; - struct sc_info *sc = ch->parent; - u_int32_t ci; - -#if(0) - device_printf(ch->parent->dev, "ichpchan_getptr(0x%08x)\n", data); -#endif - ci = bus_space_read_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CIV); -#if(0) - device_printf(ch->parent->dev, "ichpchan_getptr():ICH_REG_PO_CIV = %d\n", ci); -#endif - return ICH_DEFAULT_BLOCKSZ * ci; -} - -static struct pcmchan_caps * -ichpchan_getcaps(kobj_t obj, void *data) -{ - return &ich_playcaps; -} - -static kobj_method_t ichpchan_methods[] = { - KOBJMETHOD(channel_init, ichpchan_init), - KOBJMETHOD(channel_free, ichpchan_free), - KOBJMETHOD(channel_setformat, ichpchan_setformat), - KOBJMETHOD(channel_setspeed, ichpchan_setspeed), - KOBJMETHOD(channel_setblocksize, ichpchan_setblocksize), - KOBJMETHOD(channel_trigger, ichpchan_trigger), - KOBJMETHOD(channel_getptr, ichpchan_getptr), - KOBJMETHOD(channel_getcaps, ichpchan_getcaps), - { 0, 0 } -}; -CHANNEL_DECLARE(ichpchan); + case 1: /* mic */ + KASSERT(dir == PCMDIR_REC, ("wrong direction")); + ch->regbase = ICH_REG_MC_BASE; + ch->spdreg = sc->hasvrm? AC97_REGEXT_MADCRATE : 0; + break; -/* -------------------------------------------------------------------- */ -/* record channel interface */ -static int -ichrchan_power(kobj_t obj, struct sc_info *sc, int sw) -{ - u_int32_t cr; - int i; + case 2: /* record */ + KASSERT(dir == PCMDIR_REC, ("wrong direction")); + ch->regbase = ICH_REG_PI_BASE; + ch->spdreg = sc->hasvra? AC97_REGEXT_LADCRATE : 0; + break; - cr = ich_rdcd(obj, sc, AC97_REG_POWER); - if (sw) { /* power on */ - cr &= ~AC97_POWER_PINPOWER; - ich_wrcd(obj, sc, AC97_REG_POWER, cr); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = ich_rdcd(obj, sc, AC97_REG_POWER); - if ((cr & AC97_POWER_PINREADY) != 0) - break; - } - } - else { /* power off */ - cr |= AC97_POWER_PINPOWER; - ich_wrcd(obj, sc, AC97_REG_POWER, cr); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = ich_rdcd(obj, sc, AC97_REG_POWER); - if ((cr & AC97_POWER_PINREADY) == 0) - break; - } + default: + return NULL; } - if (i == ICH_TIMEOUT) - return -1; - return 0; -} -static u_int32_t -ichrchan_getminspeed(kobj_t obj, void *data) -{ - struct sc_chinfo *ch = data; - u_int32_t extcap; - u_int32_t minspeed = 48000; /* before AC'97 R2.0 */ - int i = 0; - - extcap = ac97_getextmode(ch->parent->codec); - if (extcap & AC97_EXTCAP_VRM) { - for (i = 0;ich_rate[i] != 0;i++) { - if (ac97_setrate(ch->parent->codec, AC97_REGEXT_LADCRATE, ich_rate[i]) == ich_rate[i]) - minspeed = ich_rate[i]; - } - } - return minspeed; -} + if (sndbuf_alloc(ch->buffer, sc->dmat, ICH_DEFAULT_BUFSZ)) + return NULL; -static void * -ichrchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) -{ - struct sc_info *sc = devinfo; - struct sc_chinfo *ch; - u_int32_t cr; - int i; + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); - /* reset codec */ - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, 0); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - ICH_X_CR_RR); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PI_CR); - if (cr == 0) - break; - } - if (i == ICH_TIMEOUT) { - device_printf(sc->dev, "cannot reset record codec\n"); - return NULL; - } - if (ichrchan_power(obj, sc, 1) == -1) { - device_printf(sc->dev, "record ADC not ready\n"); - return NULL; - } - ichrchan_power(obj, sc, 0); - if ((ch = malloc(sizeof(*ch), M_DEVBUF, M_NOWAIT)) == NULL) { - device_printf(sc->dev, "cannot allocate channel info area\n"); - return NULL; - } - ch->buffer = b; - ch->channel = c; - ch->parent = sc; - ch->dir = PCMDIR_REC; - ch->run = 0; - ch->lvi = 0; - if (ichchan_initbuf(ch)) { - device_printf(sc->dev, "cannot allocate channel buffer\n"); - free(ch, M_DEVBUF); - return NULL; - } - bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_BDBAR, - (u_int32_t)vtophys(ch->index)); - sc->pi = ch; - if (bootverbose) { - device_printf(sc->dev,"Record codec support rate(Hz): "); - for (i = 0;ich_rate[i] != 0;i++) { - if (ichrchan_setspeed(obj, ch, ich_rate[i]) == ich_rate[i]) { - printf("%d ", ich_rate[i]); - } - } - printf("\n"); - } - ich_reccaps.minspeed = ichrchan_getminspeed(obj, ch); return ch; } static int -ichrchan_free(kobj_t obj, void *data) +ichchan_setformat(kobj_t obj, void *data, u_int32_t format) { - struct sc_chinfo *ch = data; - ichchan_free(ch); - return ichrchan_power(obj, ch->parent, 0); + return 0; } static int -ichrchan_setformat(kobj_t obj, void *data, u_int32_t format) +ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; - ch->fmt = format; - - return 0; + if (ch->spdreg) + return ac97_setrate(sc->codec, ch->spdreg, speed); + else + return 48000; } static int -ichrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_chinfo *ch = data; - u_int32_t extcap; - - extcap = ac97_getextmode(ch->parent->codec); - if (extcap & AC97_EXTCAP_VRM) { - ch->spd = (u_int32_t)ac97_setrate(ch->parent->codec, AC97_REGEXT_LADCRATE, speed); - } - else { - ch->spd = 48000; /* before AC'97 R2.0 */ - } + struct sc_info *sc = ch->parent; - return ch->spd; -} + ch->blksz = blocksize; + ich_filldtbl(ch); + ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ICH_DTBL_LENGTH - 1, 1); -static int -ichrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) -{ - return blocksize; + return ch->blksz; } -/* semantic note: must start at beginning of buffer */ static int -ichrchan_trigger(kobj_t obj, void *data, int go) +ichchan_trigger(kobj_t obj, void *data, int go) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; - u_int32_t cr; - int i; switch (go) { case PCMTRIG_START: ch->run = 1; - ichrchan_power(obj, sc, 1); - bus_space_write_4(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_BDBAR, - (u_int32_t)vtophys(ch->index)); - ch->lvi = ICH_FIFOINDEX - 1; - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_LVI, - ch->lvi); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE | - ICH_X_CR_FEIE); - break; - case PCMTRIG_STOP: - cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PI_CR); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - cr & ~ICH_X_CR_RPBM); - ichrchan_power(obj, sc, 0); - ch->run = 0; + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); + ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE | ICH_X_CR_FEIE, 1); break; + case PCMTRIG_ABORT: - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - 0); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - ICH_X_CR_RR); - for (i = 0;i < ICH_TIMEOUT;i++) { - cr = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PI_CR); - if (cr == 0) - break; - } - ichrchan_power(obj, sc, 0); + ich_resetchan(sc, ch->num); ch->run = 0; - ch->lvi = 0; - break; - default: break; } return 0; } static int -ichrchan_getptr(kobj_t obj, void *data) +ichchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; - u_int32_t ci; + u_int32_t ci, ofs, pos; + + ofs = 0; + ci = 1234; + while (ci != ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1)) { + ci = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1); + ofs = ich_rd(sc, ch->regbase + ICH_REG_X_PICB, 2) * 2; + } - ci = bus_space_read_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CIV); - return ci * ICH_DEFAULT_BLOCKSZ; + ofs = ch->blksz - ofs; + ci %= ch->blkcnt; + pos = (ch->blksz * ci) + ofs; + + return pos; } static struct pcmchan_caps * -ichrchan_getcaps(kobj_t obj, void *data) +ichchan_getcaps(kobj_t obj, void *data) { - return &ich_reccaps; + struct sc_chinfo *ch = data; + + return ch->spdreg? &ich_vrcaps : &ich_caps; } -static kobj_method_t ichrchan_methods[] = { - KOBJMETHOD(channel_init, ichrchan_init), - KOBJMETHOD(channel_free, ichrchan_free), - KOBJMETHOD(channel_setformat, ichrchan_setformat), - KOBJMETHOD(channel_setspeed, ichrchan_setspeed), - KOBJMETHOD(channel_setblocksize, ichrchan_setblocksize), - KOBJMETHOD(channel_trigger, ichrchan_trigger), - KOBJMETHOD(channel_getptr, ichrchan_getptr), - KOBJMETHOD(channel_getcaps, ichrchan_getcaps), +static kobj_method_t ichchan_methods[] = { + KOBJMETHOD(channel_init, ichchan_init), + KOBJMETHOD(channel_setformat, ichchan_setformat), + KOBJMETHOD(channel_setspeed, ichchan_setspeed), + KOBJMETHOD(channel_setblocksize, ichchan_setblocksize), + KOBJMETHOD(channel_trigger, ichchan_trigger), + KOBJMETHOD(channel_getptr, ichchan_getptr), + KOBJMETHOD(channel_getcaps, ichchan_getcaps), { 0, 0 } }; -CHANNEL_DECLARE(ichrchan); +CHANNEL_DECLARE(ichchan); /* -------------------------------------------------------------------- */ /* The interrupt handler */ + static void ich_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; struct sc_chinfo *ch; - u_int32_t cp; - u_int32_t sg; - u_int32_t st; - u_int32_t lvi = 0; - -#if(0) - device_printf(sc->dev, "ich_intr(0x%08x)\n", p); -#endif - /* check interface status */ - sg = bus_space_read_4(sc->nabmbart, sc->nabmbarh, ICH_REG_GLOB_STA); -#if(0) - device_printf(sc->dev, "ich_intr():REG_GLOB_STA = 0x%08x\n", sg); -#endif - if (sg & ICH_GLOB_STA_POINT) { - /* PCM Out INTerrupt */ - /* mask interrupt */ - cp = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PO_CR); - cp &= ~(ICH_X_CR_LVBIE | ICH_X_CR_IOCE | ICH_X_CR_FEIE); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - cp); - /* check channel status */ - ch = sc->po; - st = bus_space_read_2(sc->nabmbart, sc->nabmbarh, - ICH_REG_PO_SR); -#if(0) - device_printf(sc->dev, "ich_intr():REG_PO_SR = 0x%02x\n", st); -#endif - if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) { - /* play buffer block complete */ - if (st & ICH_X_SR_LVBCI) - lvi = ch->lvi; - /* update buffer */ - chn_intr(ch->channel); - ichpchan_update(ch); - if (st & ICH_X_SR_LVBCI) { - /* re-check underflow status */ - if (lvi == ch->lvi) { - /* ch->buffer->underflow = 1; */ - ichpchan_fillblank(ch); - } - } - } - /* clear status bit */ - bus_space_write_2(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_SR, - st & (ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI)); - /* set interrupt */ - cp |= (ICH_X_CR_LVBIE | ICH_X_CR_IOCE | ICH_X_CR_FEIE); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PO_CR, - cp); - } - if (sg & ICH_GLOB_STA_PIINT) { - /* PCM In INTerrupt */ - /* mask interrupt */ - cp = bus_space_read_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PI_CR); - cp &= ~(ICH_X_CR_LVBIE | ICH_X_CR_IOCE | ICH_X_CR_FEIE); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - cp); + u_int32_t st, lvi; + int i; + + for (i = 0; i < 3; i++) { + ch = &sc->ch[i]; /* check channel status */ - ch = sc->pi; - st = bus_space_read_2(sc->nabmbart, sc->nabmbarh, - ICH_REG_PI_SR); -#if(0) - device_printf(sc->dev, "ich_intr():REG_PI_SR = 0x%02x\n", st); -#endif - if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) { - /* record buffer block filled */ - if (st & ICH_X_SR_LVBCI) - lvi = ch->lvi; - /* update space */ - chn_intr(ch->channel); - ch->lvi = sndbuf_getreadyptr(ch->buffer) / ICH_DEFAULT_BLOCKSZ - 1; - if (ch->lvi < 0) - ch->lvi = ICH_FIFOINDEX - 1; - bus_space_write_1(sc->nabmbart, sc->nabmbarh, - ICH_REG_PI_LVI, ch->lvi); - if (st & ICH_X_SR_LVBCI) { - /* re-check underflow status */ - if (lvi == ch->lvi) { - ch->lvi++; - if (ch->lvi == ICH_FIFOINDEX) - ch->lvi = 0; - bus_space_write_1(sc->nabmbart, - sc->nabmbarh, ICH_REG_PI_LVI, - ch->lvi); - } + st = ich_rd(sc, ch->regbase + ICH_REG_X_SR, 2); + st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI; + if (st != 0) { + if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) { + /* block complete - update buffer */ + if (ch->run) + chn_intr(ch->channel); + lvi = ich_rd(sc, ch->regbase + ICH_REG_X_LVI, 1); + lvi++; + lvi %= ICH_DTBL_LENGTH; + ich_wr(sc, ch->regbase + ICH_REG_X_LVI, lvi, 1); } + /* clear status bit */ + ich_wr(sc, ch->regbase + ICH_REG_X_SR, st, 2); } - /* clear status bit */ - bus_space_write_2(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_SR, - st & (ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI)); - /* set interrupt */ - cp |= (ICH_X_CR_LVBIE | ICH_X_CR_IOCE | ICH_X_CR_FEIE); - bus_space_write_1(sc->nabmbart, sc->nabmbarh, ICH_REG_PI_CR, - cp); } } /* -------------------------------------------------------------------- */ +/* Probe and attach the card */ -/* - * Probe and attach the card - */ +static void +ich_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) +{ + return; +} static int ich_init(struct sc_info *sc) { u_int32_t stat; - u_int32_t save; + int sz; - bus_space_write_4(sc->nabmbart, sc->nabmbarh, - ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD); + ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD, 4); DELAY(600000); - stat = bus_space_read_4(sc->nabmbart, sc->nabmbarh, ICH_REG_GLOB_STA); + stat = ich_rd(sc, ICH_REG_GLOB_STA, 4); + if ((stat & ICH_GLOB_STA_PCR) == 0) - return -1; - bus_space_write_4(sc->nabmbart, sc->nabmbarh, - ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD | ICH_GLOB_CTL_PRES); - save = bus_space_read_2(sc->nambart, sc->nambarh, AC97_MIX_MASTER); - bus_space_write_2(sc->nambart, sc->nambarh, AC97_MIX_MASTER, - AC97_MUTE); - if (ich_waitcd(sc) == ETIMEDOUT) - return -1; - DELAY(600); /* it is need for some system */ - stat = bus_space_read_2(sc->nambart, sc->nambarh, AC97_MIX_MASTER); - if (stat != AC97_MUTE) - return -1; - bus_space_write_2(sc->nambart, sc->nambarh, AC97_MIX_MASTER, save); - return 0; -} + return ENXIO; -static int -ich_finddev(u_int32_t dev, u_int32_t subdev) -{ - int i; + ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD | ICH_GLOB_CTL_PRES, 4); - for (i = 0; ich_devs[i].dev; i++) { - if (ich_devs[i].dev == dev && - (ich_devs[i].subdev == subdev || ich_devs[i].subdev == 0)) - return i; + if (ich_resetchan(sc, 0) || ich_resetchan(sc, 0)) + return ENXIO; + + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, BUS_DMA_NOWAIT, &sc->dtmap)) + return ENOSPC; + + sz = sizeof(struct ich_desc) * ICH_DTBL_LENGTH * 3; + if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, sz, ich_setmap, NULL, 0)) { + bus_dmamem_free(sc->dmat, (void **)&sc->dtbl, sc->dtmap); + return ENOSPC; } - return -1; + + return 0; } static int ich_pci_probe(device_t dev) { - int i; - u_int32_t subdev; + switch(pci_get_devid(dev)) { + case 0x71958086: + device_set_desc(dev, "Intel 443MX"); + return 0; + + case 0x24158086: + device_set_desc(dev, "Intel 82801AA (ICH)"); + return 0; - subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); - i = ich_finddev(pci_get_devid(dev), subdev); - if (i >= 0) { - device_set_desc(dev, ich_devs[i].name); + case 0x24258086: + device_set_desc(dev, "Intel 82901AB (ICH)"); return 0; - } else + + case 0x24458086: + device_set_desc(dev, "Intel 82801BA (ICH2)"); + return 0; + + default: return ENXIO; + } } static int ich_pci_attach(device_t dev) { u_int32_t data; - u_int32_t subdev; struct sc_info *sc; char status[SND_STATUSLEN]; @@ -1049,9 +473,6 @@ ich_pci_attach(device_t dev) bzero(sc, sizeof(*sc)); sc->dev = dev; - subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); - sc->type = ich_finddev(pci_get_devid(dev), subdev); - sc->rev = pci_get_revid(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); @@ -1060,30 +481,26 @@ ich_pci_attach(device_t dev) sc->nambarid = PCIR_NAMBAR; sc->nabmbarid = PCIR_NABMBAR; - sc->nambar = bus_alloc_resource(dev, SYS_RES_IOPORT, - &sc->nambarid, 0, ~0, 256, RF_ACTIVE); - sc->nabmbar = bus_alloc_resource(dev, SYS_RES_IOPORT, - &sc->nabmbarid, 0, ~0, 64, RF_ACTIVE); + sc->nambar = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->nambarid, 0, ~0, 1, RF_ACTIVE); + sc->nabmbar = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->nabmbarid, 0, ~0, 1, RF_ACTIVE); + if (!sc->nambar || !sc->nabmbar) { device_printf(dev, "unable to map IO port space\n"); goto bad; } + sc->nambart = rman_get_bustag(sc->nambar); sc->nambarh = rman_get_bushandle(sc->nambar); sc->nabmbart = rman_get_bustag(sc->nabmbar); sc->nabmbarh = rman_get_bushandle(sc->nabmbar); - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/4, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/65536, /*nsegments*/1, /*maxsegsz*/0x3ffff, - /*flags*/0, &sc->dmat) != 0) { + if (bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, + NULL, NULL, ICH_DEFAULT_BUFSZ, 1, 0x3ffff, 0, &sc->dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } - if (ich_init(sc) == -1) { + if (ich_init(sc)) { device_printf(dev, "unable to initialize the card\n"); goto bad; } @@ -1092,37 +509,30 @@ ich_pci_attach(device_t dev) if (sc->codec == NULL) goto bad; mixer_init(dev, ac97_getmixerclass(), sc->codec); + /* check and set VRA function */ - if (ac97_setextmode(sc->codec, AC97_EXTCAP_VRA) == 0) { - if (bootverbose) { - device_printf(sc->dev, "set VRA function\n"); - } - } - if (ac97_setextmode(sc->codec, AC97_EXTCAP_VRM) == 0) { - if (bootverbose) { - device_printf(sc->dev, "set VRM function\n"); - } - } + if (ac97_setextmode(sc->codec, AC97_EXTCAP_VRA) == 0) + sc->hasvra = 1; + if (ac97_setextmode(sc->codec, AC97_EXTCAP_VRM) == 0) + sc->hasvrm = 1; sc->irqid = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ich_intr, sc, &sc->ih)) { + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + 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; } - snprintf(status, SND_STATUSLEN, - "at io 0x%lx-0x%lx, 0x%lx-0x%lx irq %ld", - rman_get_start(sc->nambar), rman_get_end(sc->nambar), - rman_get_start(sc->nabmbar), rman_get_end(sc->nabmbar), - rman_get_start(sc->irq)); - - if (pcm_register(dev, sc, 1, 1)) + if (pcm_register(dev, sc, 1, 2)) goto bad; - pcm_addchan(dev, PCMDIR_PLAY, &ichpchan_class, sc); - pcm_addchan(dev, PCMDIR_REC, &ichrchan_class, sc); + + pcm_addchan(dev, PCMDIR_PLAY, &ichchan_class, sc); + pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); + pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); + + snprintf(status, SND_STATUSLEN, "at io 0x%lx, 0x%lx irq %ld", + rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq)); + pcm_setstatus(dev, status); return 0; @@ -1155,11 +565,11 @@ ich_pci_detach(device_t dev) return r; sc = pcm_getdevinfo(dev); + bus_teardown_intr(dev, sc->irq, sc->ih); + bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); bus_release_resource(dev, SYS_RES_IOPORT, sc->nambarid, sc->nambar); bus_release_resource(dev, SYS_RES_IOPORT, sc->nabmbarid, sc->nabmbar); bus_dma_tag_destroy(sc->dmat); - bus_teardown_intr(dev, sc->irq, sc->ih); - bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); free(sc, M_DEVBUF); return 0; } diff --git a/sys/dev/sound/pci/ich.h b/sys/dev/sound/pci/ich.h new file mode 100644 index 0000000..78ecd8b --- /dev/null +++ b/sys/dev/sound/pci/ich.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2000 Katsurajima Naoto <raven@katsurajima.seya.yokohama.jp> + * Copyright (c) 2001 Cameron Grant <cg@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#define PCIR_NAMBAR 0x10 +#define PCIR_NABMBAR 0x14 + +/* Native Audio Bus Master Control Registers */ +#define ICH_REG_X_BDBAR 0x00 +#define ICH_REG_X_CIV 0x04 +#define ICH_REG_X_LVI 0x05 +#define ICH_REG_X_SR 0x06 +#define ICH_REG_X_PICB 0x08 +#define ICH_REG_X_PIV 0x0a +#define ICH_REG_X_CR 0x0b + +#define ICH_REG_PI_BASE 0x00 +#define ICH_REG_PO_BASE 0x10 +#define ICH_REG_MC_BASE 0x20 + +#define ICH_REG_GLOB_CNT 0x2c +#define ICH_REG_GLOB_STA 0x30 +#define ICH_REG_ACC_SEMA 0x34 + +/* Status Register Values */ +#define ICH_X_SR_DCH 0x0001 +#define ICH_X_SR_CELV 0x0002 +#define ICH_X_SR_LVBCI 0x0004 +#define ICH_X_SR_BCIS 0x0008 +#define ICH_X_SR_FIFOE 0x0010 + +/* Control Register Values */ +#define ICH_X_CR_RPBM 0x01 +#define ICH_X_CR_RR 0x02 +#define ICH_X_CR_LVBIE 0x04 +#define ICH_X_CR_FEIE 0x08 +#define ICH_X_CR_IOCE 0x10 + +/* Global Control Register Values */ +#define ICH_GLOB_CTL_GIE 0x00000001 +#define ICH_GLOB_CTL_COLD 0x00000002 /* negate */ +#define ICH_GLOB_CTL_WARM 0x00000004 +#define ICH_GLOB_CTL_SHUT 0x00000008 +#define ICH_GLOB_CTL_PRES 0x00000010 +#define ICH_GLOB_CTL_SRES 0x00000020 + +/* Global Status Register Values */ +#define ICH_GLOB_STA_GSCI 0x00000001 +#define ICH_GLOB_STA_MIINT 0x00000002 +#define ICH_GLOB_STA_MOINT 0x00000004 +#define ICH_GLOB_STA_PIINT 0x00000020 +#define ICH_GLOB_STA_POINT 0x00000040 +#define ICH_GLOB_STA_MINT 0x00000080 +#define ICH_GLOB_STA_PCR 0x00000100 +#define ICH_GLOB_STA_SCR 0x00000200 +#define ICH_GLOB_STA_PRES 0x00000400 +#define ICH_GLOB_STA_SRES 0x00000800 +#define ICH_GLOB_STA_SLOT12 0x00007000 +#define ICH_GLOB_STA_RCODEC 0x00008000 +#define ICH_GLOB_STA_AD3 0x00010000 +#define ICH_GLOB_STA_MD3 0x00020000 +#define ICH_GLOB_STA_IMASK (ICH_GLOB_STA_MIINT | ICH_GLOB_STA_MOINT | ICH_GLOB_STA_PIINT | ICH_GLOB_STA_POINT | ICH_GLOB_STA_MINT | ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES) + +/* play/record buffer */ +#define ICH_BDC_IOC 0x80000000 +#define ICH_BDC_BUP 0x40000000 + |