aureal.c revision 170521
149004Sgreen/*- 249004Sgreen * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 349004Sgreen * All rights reserved. 449004Sgreen * 549004Sgreen * Redistribution and use in source and binary forms, with or without 649004Sgreen * modification, are permitted provided that the following conditions 749004Sgreen * are met: 849004Sgreen * 1. Redistributions of source code must retain the above copyright 949004Sgreen * notice, this list of conditions and the following disclaimer. 1049004Sgreen * 2. Redistributions in binary form must reproduce the above copyright 1149004Sgreen * notice, this list of conditions and the following disclaimer in the 1249004Sgreen * documentation and/or other materials provided with the distribution. 1349004Sgreen * 1449004Sgreen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1549004Sgreen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1649004Sgreen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1749004Sgreen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1849004Sgreen * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1949004Sgreen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2049004Sgreen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2149004Sgreen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2249004Sgreen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2349004Sgreen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2449004Sgreen * SUCH DAMAGE. 2549004Sgreen */ 2649004Sgreen 2798563Sjmallett#include <dev/sound/pcm/sound.h> 2898563Sjmallett#include <dev/sound/pcm/ac97.h> 2998563Sjmallett#include <dev/sound/pci/aureal.h> 3049004Sgreen 3149004Sgreen#include <dev/pci/pcireg.h> 3248981Ssheldonh#include <dev/pci/pcivar.h> 3348981Ssheldonh 3448981SsheldonhSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pci/aureal.c 170521 2007-06-11 00:49:46Z ariff $"); 3548981Ssheldonh 3648981Ssheldonh/* PCI IDs of supported chips */ 3748981Ssheldonh#define AU8820_PCI_ID 0x000112eb 3849004Sgreen 3948981Ssheldonh/* channel interface */ 4048981Ssheldonhstatic u_int32_t au_playfmt[] = { 4148981Ssheldonh AFMT_U8, 4248981Ssheldonh AFMT_STEREO | AFMT_U8, 4369144Sgreen AFMT_S16_LE, 4448981Ssheldonh AFMT_STEREO | AFMT_S16_LE, 4548981Ssheldonh 0 4648981Ssheldonh}; 4749004Sgreenstatic struct pcmchan_caps au_playcaps = {4000, 48000, au_playfmt, 0}; 4848981Ssheldonh 4949030Ssheldonhstatic u_int32_t au_recfmt[] = { 5049030Ssheldonh AFMT_U8, 5148981Ssheldonh AFMT_STEREO | AFMT_U8, 5248981Ssheldonh AFMT_S16_LE, 5348981Ssheldonh AFMT_STEREO | AFMT_S16_LE, 5448981Ssheldonh 0 5598562Sjmallett}; 5698562Sjmallettstatic struct pcmchan_caps au_reccaps = {4000, 48000, au_recfmt, 0}; 5798562Sjmallett 5898562Sjmallett/* -------------------------------------------------------------------- */ 5998562Sjmallett 6098562Sjmallettstruct au_info; 6198562Sjmallett 6298562Sjmallettstruct au_chinfo { 63299356Sbapt struct au_info *parent; 6498562Sjmallett struct pcm_channel *channel; 6598562Sjmallett struct snd_dbuf *buffer; 6698562Sjmallett int dir; 67157816Sdwmalone}; 6898562Sjmallett 6998562Sjmallettstruct au_info { 7048981Ssheldonh int unit; 7148981Ssheldonh 7248981Ssheldonh bus_space_tag_t st[3]; 7348981Ssheldonh bus_space_handle_t sh[3]; 7448981Ssheldonh 7548981Ssheldonh bus_dma_tag_t parent_dmat; 7648981Ssheldonh struct mtx *lock; 7748981Ssheldonh 7848981Ssheldonh u_int32_t x[32], y[128]; 7948981Ssheldonh char z[128]; 8048981Ssheldonh u_int32_t routes[4], interrupts; 8148981Ssheldonh struct au_chinfo pch; 8248981Ssheldonh}; 8348981Ssheldonh 8477685Sdwmalonestatic int au_init(device_t dev, struct au_info *au); 8548981Ssheldonhstatic void au_intr(void *); 8648981Ssheldonh 8748981Ssheldonh/* -------------------------------------------------------------------- */ 8848981Ssheldonh 8948981Ssheldonhstatic u_int32_t 9048981Ssheldonhau_rd(struct au_info *au, int mapno, int regno, int size) 9148981Ssheldonh{ 9248981Ssheldonh switch(size) { 9348981Ssheldonh case 1: 9448981Ssheldonh return bus_space_read_1(au->st[mapno], au->sh[mapno], regno); 9548981Ssheldonh case 2: 9678694Sdwmalone return bus_space_read_2(au->st[mapno], au->sh[mapno], regno); 9748981Ssheldonh case 4: 9848981Ssheldonh return bus_space_read_4(au->st[mapno], au->sh[mapno], regno); 9948981Ssheldonh default: 10078694Sdwmalone return 0xffffffff; 10148981Ssheldonh } 10248981Ssheldonh} 10349052Ssheldonh 10449052Ssheldonhstatic void 10549052Ssheldonhau_wr(struct au_info *au, int mapno, int regno, u_int32_t data, int size) 10649052Ssheldonh{ 10749052Ssheldonh switch(size) { 10848981Ssheldonh case 1: 10998558Sjmallett bus_space_write_1(au->st[mapno], au->sh[mapno], regno, data); 11048981Ssheldonh break; 11148981Ssheldonh case 2: 11248981Ssheldonh bus_space_write_2(au->st[mapno], au->sh[mapno], regno, data); 11348981Ssheldonh break; 11448981Ssheldonh case 4: 11548981Ssheldonh bus_space_write_4(au->st[mapno], au->sh[mapno], regno, data); 11648981Ssheldonh break; 11748981Ssheldonh } 11848981Ssheldonh} 11948981Ssheldonh 120157820Sdwmalone/* -------------------------------------------------------------------- */ 121157820Sdwmalone 122157820Sdwmalonestatic int 123157820Sdwmaloneau_rdcd(kobj_t obj, void *arg, int regno) 12448981Ssheldonh{ 12548981Ssheldonh struct au_info *au = (struct au_info *)arg; 12698559Sjmallett int i=0, j=0; 12748981Ssheldonh 12856590Sshin regno<<=16; 12948981Ssheldonh au_wr(au, 0, AU_REG_CODECIO, regno, 4); 13057857Sshin while (j<50) { 13157857Sshin i=au_rd(au, 0, AU_REG_CODECIO, 4); 13248981Ssheldonh if ((i & 0x00ff0000) == (regno | 0x00800000)) break; 13348981Ssheldonh DELAY(j * 200 + 2000); 13448981Ssheldonh j++; 13548981Ssheldonh } 13648981Ssheldonh if (j==50) printf("pcm%d: codec timeout reading register %x (%x)\n", 13748981Ssheldonh au->unit, (regno & AU_CDC_REGMASK)>>16, i); 13848981Ssheldonh return i & AU_CDC_DATAMASK; 13956590Sshin} 14048981Ssheldonh 14156590Sshinstatic int 14248981Ssheldonhau_wrcd(kobj_t obj, void *arg, int regno, u_int32_t data) 14348981Ssheldonh{ 14456590Sshin struct au_info *au = (struct au_info *)arg; 14548981Ssheldonh int i, j, tries; 14648981Ssheldonh i=j=tries=0; 14748981Ssheldonh do { 14848981Ssheldonh while (j<50 && (i & AU_CDC_WROK) == 0) { 14948981Ssheldonh i=au_rd(au, 0, AU_REG_CODECST, 4); 15048981Ssheldonh DELAY(2000); 15148981Ssheldonh j++; 15248981Ssheldonh } 15348981Ssheldonh if (j==50) printf("codec timeout during write of register %x, data %x\n", 15448981Ssheldonh regno, data); 15548981Ssheldonh au_wr(au, 0, AU_REG_CODECIO, (regno<<16) | AU_CDC_REGSET | data, 4); 15648981Ssheldonh/* DELAY(20000); 15757857Sshin i=au_rdcd(au, regno); 15848981Ssheldonh*/ tries++; 15948981Ssheldonh } while (0); /* (i != data && tries < 3); */ 16098559Sjmallett /* 16148981Ssheldonh if (tries == 3) printf("giving up writing 0x%4x to codec reg %2x\n", data, regno); 16248981Ssheldonh */ 16398559Sjmallett 16448981Ssheldonh return 0; 16548981Ssheldonh} 16648981Ssheldonh 16748981Ssheldonhstatic kobj_method_t au_ac97_methods[] = { 16848981Ssheldonh KOBJMETHOD(ac97_read, au_rdcd), 16948981Ssheldonh KOBJMETHOD(ac97_write, au_wrcd), 17048981Ssheldonh { 0, 0 } 17148981Ssheldonh}; 17248981SsheldonhAC97_DECLARE(au_ac97); 17348981Ssheldonh 17448981Ssheldonh/* -------------------------------------------------------------------- */ 17548981Ssheldonh 17648981Ssheldonhstatic void 17748981Ssheldonhau_setbit(u_int32_t *p, char bit, u_int32_t value) 17848981Ssheldonh{ 17948981Ssheldonh p += bit >> 5; 18048981Ssheldonh bit &= 0x1f; 18148981Ssheldonh *p &= ~ (1 << bit); 18248981Ssheldonh *p |= (value << bit); 18348981Ssheldonh} 18448981Ssheldonh 18548981Ssheldonhstatic void 18648981Ssheldonhau_addroute(struct au_info *au, int a, int b, int route) 18748981Ssheldonh{ 18848981Ssheldonh int j = 0x1099c+(a<<2); 18948981Ssheldonh if (au->x[a] != a+0x67) j = AU_REG_RTBASE+(au->x[a]<<2); 19048981Ssheldonh 19148981Ssheldonh au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xffffffff, 4); 19249052Ssheldonh au_wr(au, 0, j, route | (b<<7), 4); 19349052Ssheldonh au->y[route]=au->x[a]; 19449052Ssheldonh au->x[a]=route; 19549052Ssheldonh au->z[route]=a & 0x000000ff; 19649052Ssheldonh au_setbit(au->routes, route, 1); 19798559Sjmallett} 19848981Ssheldonh 19948981Ssheldonhstatic void 20098559Sjmallettau_delroute(struct au_info *au, int route) 20148981Ssheldonh{ 20248981Ssheldonh int i; 20378694Sdwmalone int j=au->z[route]; 20456590Sshin 20557857Sshin au_setbit(au->routes, route, 0); 20648981Ssheldonh au->z[route]=0x1f; 20778694Sdwmalone i=au_rd(au, 0, AU_REG_RTBASE+(route<<2), 4); 20848981Ssheldonh au_wr(au, 0, AU_REG_RTBASE+(au->y[route]<<2), i, 4); 20956590Sshin au->y[i & 0x7f]=au->y[route]; 21048981Ssheldonh au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xfffffffe, 4); 21156590Sshin if (au->x[j] == route) au->x[j]=au->y[route]; 21248981Ssheldonh au->y[route]=0x7f; 21348981Ssheldonh} 21456590Sshin 21548981Ssheldonhstatic void 21648981Ssheldonhau_encodec(struct au_info *au, char channel) 21778694Sdwmalone{ 21848981Ssheldonh au_wr(au, 0, AU_REG_CODECEN, 21957857Sshin au_rd(au, 0, AU_REG_CODECEN, 4) | (1 << (channel + 8)), 4); 22048981Ssheldonh} 22148981Ssheldonh 22298559Sjmallettstatic void 22348981Ssheldonhau_clrfifo(struct au_info *au, u_int32_t c) 22448981Ssheldonh{ 22598559Sjmallett u_int32_t i; 22648981Ssheldonh 22748981Ssheldonh for (i=0; i<32; i++) au_wr(au, 0, AU_REG_FIFOBASE+(c<<7)+(i<<2), 0, 4); 22878694Sdwmalone} 22948981Ssheldonh 23078694Sdwmalonestatic void 23148981Ssheldonhau_setadb(struct au_info *au, u_int32_t c, u_int32_t enable) 23278694Sdwmalone{ 23358735Ssheldonh int x; 23448981Ssheldonh 23548981Ssheldonh x = au_rd(au, 0, AU_REG_ADB, 4); 23649052Ssheldonh x &= ~(1 << c); 23749052Ssheldonh x |= (enable << c); 23849052Ssheldonh au_wr(au, 0, AU_REG_ADB, x, 4); 23949052Ssheldonh} 24049052Ssheldonh 24198559Sjmallettstatic void 24248981Ssheldonhau_prepareoutput(struct au_chinfo *ch, u_int32_t format) 24348981Ssheldonh{ 24498561Sjmallett struct au_info *au = ch->parent; 24548981Ssheldonh int i, stereo = (format & AFMT_STEREO)? 1 : 0; 24648981Ssheldonh u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer); 24748981Ssheldonh 24848981Ssheldonh au_wr(au, 0, 0x1061c, 0, 4); 24948981Ssheldonh au_wr(au, 0, 0x10620, 0, 4); 25048981Ssheldonh au_wr(au, 0, 0x10624, 0, 4); 25198559Sjmallett switch(format & ~AFMT_STEREO) { 25248981Ssheldonh case 1: 25348981Ssheldonh i=0xb000; 25498559Sjmallett break; 25548981Ssheldonh case 2: 25648981Ssheldonh i=0xf000; 25748981Ssheldonh break; 25848981Ssheldonh case 8: 25948981Ssheldonh i=0x7000; 26048981Ssheldonh break; 26148981Ssheldonh case 16: 26248981Ssheldonh i=0x23000; 26348981Ssheldonh break; 26448981Ssheldonh default: 26548981Ssheldonh i=0x3000; 26648981Ssheldonh } 26748981Ssheldonh au_wr(au, 0, 0x10200, baseaddr, 4); 26848981Ssheldonh au_wr(au, 0, 0x10204, baseaddr+0x1000, 4); 26949052Ssheldonh au_wr(au, 0, 0x10208, baseaddr+0x2000, 4); 27049052Ssheldonh au_wr(au, 0, 0x1020c, baseaddr+0x3000, 4); 27149052Ssheldonh 27249052Ssheldonh au_wr(au, 0, 0x10400, 0xdeffffff, 4); 27349052Ssheldonh au_wr(au, 0, 0x10404, 0xfcffffff, 4); 27498559Sjmallett 27548981Ssheldonh au_wr(au, 0, 0x10580, i, 4); 27648981Ssheldonh 27798559Sjmallett au_wr(au, 0, 0x10210, baseaddr, 4); 27848981Ssheldonh au_wr(au, 0, 0x10214, baseaddr+0x1000, 4); 27977231Sdwmalone au_wr(au, 0, 0x10218, baseaddr+0x2000, 4); 28057857Sshin au_wr(au, 0, 0x1021c, baseaddr+0x3000, 4); 28157857Sshin 28256590Sshin au_wr(au, 0, 0x10408, 0x00fff000 | 0x56000000 | 0x00000fff, 4); 28348981Ssheldonh au_wr(au, 0, 0x1040c, 0x00fff000 | 0x74000000 | 0x00000fff, 4); 28456590Sshin 28548981Ssheldonh au_wr(au, 0, 0x10584, i, 4); 28656590Sshin 28748981Ssheldonh au_wr(au, 0, 0x0f800, stereo? 0x00030032 : 0x00030030, 4); 28848981Ssheldonh au_wr(au, 0, 0x0f804, stereo? 0x00030032 : 0x00030030, 4); 28956590Sshin 29048981Ssheldonh au_addroute(au, 0x11, 0, 0x58); 29148981Ssheldonh au_addroute(au, 0x11, stereo? 0 : 1, 0x59); 29257857Sshin} 29348981Ssheldonh 29448981Ssheldonh/* -------------------------------------------------------------------- */ 29598559Sjmallett/* channel interface */ 29648981Ssheldonhstatic void * 29748981Ssheldonhauchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 29898559Sjmallett{ 29948981Ssheldonh struct au_info *au = devinfo; 30048981Ssheldonh struct au_chinfo *ch = (dir == PCMDIR_PLAY)? &au->pch : NULL; 30148981Ssheldonh 30248981Ssheldonh ch->parent = au; 30348981Ssheldonh ch->channel = c; 30448981Ssheldonh ch->buffer = b; 30548981Ssheldonh ch->dir = dir; 30648981Ssheldonh if (sndbuf_alloc(ch->buffer, au->parent_dmat, 0, AU_BUFFSIZE) != 0) 30748981Ssheldonh return NULL; 30848981Ssheldonh return ch; 30948981Ssheldonh} 31049052Ssheldonh 31149052Ssheldonhstatic int 31249052Ssheldonhauchan_setformat(kobj_t obj, void *data, u_int32_t format) 31349057Sgreen{ 31449057Sgreen struct au_chinfo *ch = data; 31549052Ssheldonh 31649052Ssheldonh if (ch->dir == PCMDIR_PLAY) au_prepareoutput(ch, format); 31777684Sdwmalone return 0; 31877684Sdwmalone} 31977684Sdwmalone 32077684Sdwmalonestatic int 32177684Sdwmaloneauchan_setspeed(kobj_t obj, void *data, u_int32_t speed) 32277684Sdwmalone{ 32398559Sjmallett struct au_chinfo *ch = data; 32448981Ssheldonh if (ch->dir == PCMDIR_PLAY) { 32548981Ssheldonh } else { 32698559Sjmallett } 32748981Ssheldonh return speed; 32849004Sgreen} 32949004Sgreen 33077684Sdwmalonestatic int 33149030Ssheldonhauchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 33249057Sgreen{ 33349030Ssheldonh return blocksize; 33449030Ssheldonh} 33558735Ssheldonh 33649004Sgreenstatic int 33748981Ssheldonhauchan_trigger(kobj_t obj, void *data, int go) 33848981Ssheldonh{ 33948981Ssheldonh struct au_chinfo *ch = data; 34048981Ssheldonh struct au_info *au = ch->parent; 34198559Sjmallett 34248981Ssheldonh if (!PCMTRIG_COMMON(go)) 34348981Ssheldonh return 0; 34498559Sjmallett 34548981Ssheldonh if (ch->dir == PCMDIR_PLAY) { 34649089Sgreen au_setadb(au, 0x11, (go)? 1 : 0); 34749089Sgreen if (go != PCMTRIG_START) { 34878694Sdwmalone au_wr(au, 0, 0xf800, 0, 4); 34957906Sshin au_wr(au, 0, 0xf804, 0, 4); 35056590Sshin au_delroute(au, 0x58); 35157906Sshin au_delroute(au, 0x59); 35256590Sshin } 35372650Sgreen } else { 35449004Sgreen } 35549004Sgreen return 0; 35649004Sgreen} 35763045Sdwmalone 35856298Sgreenstatic int 35949004Sgreenauchan_getptr(kobj_t obj, void *data) 36077684Sdwmalone{ 36177684Sdwmalone struct au_chinfo *ch = data; 36261099Sgreen struct au_info *au = ch->parent; 36361099Sgreen if (ch->dir == PCMDIR_PLAY) { 36463045Sdwmalone return au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1); 36577684Sdwmalone } else { 36677684Sdwmalone return 0; 36748981Ssheldonh } 36848981Ssheldonh} 36948981Ssheldonh 37049104Sgreenstatic struct pcmchan_caps * 37149104Sgreenauchan_getcaps(kobj_t obj, void *data) 37249104Sgreen{ 37349104Sgreen struct au_chinfo *ch = data; 37448981Ssheldonh return (ch->dir == PCMDIR_PLAY)? &au_playcaps : &au_reccaps; 37548981Ssheldonh} 37649104Sgreen 37749104Sgreenstatic kobj_method_t auchan_methods[] = { 37849104Sgreen KOBJMETHOD(channel_init, auchan_init), 37949104Sgreen KOBJMETHOD(channel_setformat, auchan_setformat), 38049104Sgreen KOBJMETHOD(channel_setspeed, auchan_setspeed), 38148981Ssheldonh KOBJMETHOD(channel_setblocksize, auchan_setblocksize), 38248981Ssheldonh KOBJMETHOD(channel_trigger, auchan_trigger), 38348981Ssheldonh KOBJMETHOD(channel_getptr, auchan_getptr), 38449054Sgreen KOBJMETHOD(channel_getcaps, auchan_getcaps), 38556298Sgreen { 0, 0 } 38678694Sdwmalone}; 38749054SgreenCHANNEL_DECLARE(auchan); 38877684Sdwmalone 38948981Ssheldonh/* -------------------------------------------------------------------- */ 39056298Sgreen/* The interrupt handler */ 39177684Sdwmalonestatic void 39277684Sdwmaloneau_intr (void *p) 39356298Sgreen{ 39448981Ssheldonh struct au_info *au = p; 39548981Ssheldonh u_int32_t intsrc, i; 39648981Ssheldonh 39769620Sdwmalone au->interrupts++; 39869620Sdwmalone intsrc=au_rd(au, 0, AU_REG_IRQSRC, 4); 39969620Sdwmalone printf("pcm%d: interrupt with src %x\n", au->unit, intsrc); 40069620Sdwmalone if (intsrc & AU_IRQ_FATAL) printf("pcm%d: fatal error irq\n", au->unit); 40156298Sgreen if (intsrc & AU_IRQ_PARITY) printf("pcm%d: parity error irq\n", au->unit); 40256298Sgreen if (intsrc & AU_IRQ_UNKNOWN) { 40378694Sdwmalone (void)au_rd(au, 0, AU_REG_UNK1, 4); 40456303Sgreen au_wr(au, 0, AU_REG_UNK1, 0, 4); 40578694Sdwmalone au_wr(au, 0, AU_REG_UNK1, 0x10000, 4); 40656303Sgreen } 40756303Sgreen if (intsrc & AU_IRQ_PCMOUT) { 40856303Sgreen i=au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1); 40956303Sgreen chn_intr(au->pch.channel); 410100498Sfanf (void)au_rd(au, 0, AU_REG_UNK3, 4); 41177684Sdwmalone (void)au_rd(au, 0, AU_REG_UNK4, 4); 41256303Sgreen (void)au_rd(au, 0, AU_REG_UNK5, 4); 41377684Sdwmalone } 41477684Sdwmalone/* don't support midi 41556303Sgreen if (intsrc & AU_IRQ_MIDI) { 41656303Sgreen i=au_rd(au, 0, 0x11004, 4); 41777684Sdwmalone j=10; 41878694Sdwmalone while (i & 0xff) { 41978694Sdwmalone if (j-- <= 0) break; 42078694Sdwmalone i=au_rd(au, 0, 0x11000, 4); 42156298Sgreen if ((au->midi_stat & 1) && (au->midi_out)) 42277684Sdwmalone au->midi_out(au->midi_devno, i); 42356298Sgreen i=au_rd(au, 0, 0x11004); 42477684Sdwmalone } 42577684Sdwmalone } 42677684Sdwmalone*/ 42749057Sgreen au_wr(au, 0, AU_REG_IRQSRC, intsrc & 0x7ff, 4); 42849057Sgreen au_rd(au, 0, AU_REG_IRQSRC, 4); 42948981Ssheldonh} 43049004Sgreen 43149004Sgreen 43249004Sgreen/* -------------------------------------------------------------------- */ 43349057Sgreen 43449057Sgreen/* Probe and attach the card */ 43549057Sgreen 43649051Ssheldonhstatic int 43749030Ssheldonhau_init(device_t dev, struct au_info *au) 43849030Ssheldonh{ 43949030Ssheldonh u_int32_t i, j; 44077684Sdwmalone 44149030Ssheldonh au_wr(au, 0, AU_REG_IRQGLOB, 0xffffffff, 4); 44249030Ssheldonh DELAY(100000); 44349030Ssheldonh 44449030Ssheldonh /* init codec */ 44549030Ssheldonh /* cold reset */ 44649030Ssheldonh for (i=0; i<32; i++) { 44749030Ssheldonh au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4); 44849030Ssheldonh DELAY(10000); 44949054Sgreen } 45048981Ssheldonh if (1) { 45148981Ssheldonh au_wr(au, 0, AU_REG_CODECST, 0x8068, 4); 45248981Ssheldonh DELAY(10000); 45348981Ssheldonh au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4); 45449004Sgreen DELAY(10000); 45549033Sgreen } else { 45677684Sdwmalone au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4); 45749004Sgreen DELAY(100000); 45849004Sgreen au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4); 45977684Sdwmalone DELAY(100000); 46049104Sgreen au_wr(au, 0, AU_REG_CODECST, 0x80e8, 4); 46149104Sgreen DELAY(100000); 46249104Sgreen au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4); 46349104Sgreen DELAY(100000); 46449104Sgreen au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4); 46549104Sgreen DELAY(100000); 46663045Sdwmalone au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4); 46763045Sdwmalone DELAY(100000); 46869532Sgreen } 46969532Sgreen 47063045Sdwmalone /* init */ 47163045Sdwmalone for (i=0; i<32; i++) { 47263045Sdwmalone au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4); 47363045Sdwmalone DELAY(10000); 47463045Sdwmalone } 47563045Sdwmalone au_wr(au, 0, AU_REG_CODECST, 0xe8, 4); 47649004Sgreen DELAY(10000); 47774934Sdwmalone au_wr(au, 0, AU_REG_CODECEN, 0, 4); 47863045Sdwmalone 47963045Sdwmalone /* setup codec */ 48063045Sdwmalone i=j=0; 48163045Sdwmalone while (j<100 && (i & AU_CDC_READY)==0) { 48263045Sdwmalone i=au_rd(au, 0, AU_REG_CODECST, 4); 48363045Sdwmalone DELAY(1000); 48463045Sdwmalone j++; 48563045Sdwmalone } 48663045Sdwmalone if (j==100) device_printf(dev, "codec not ready, status 0x%x\n", i); 48763045Sdwmalone 48863045Sdwmalone /* init adb */ 48977684Sdwmalone /*au->x5c=0;*/ 49063045Sdwmalone for (i=0; i<32; i++) au->x[i]=i+0x67; 49177684Sdwmalone for (i=0; i<128; i++) au->y[i]=0x7f; 49278694Sdwmalone for (i=0; i<128; i++) au->z[i]=0x1f; 49363045Sdwmalone au_wr(au, 0, AU_REG_ADB, 0, 4); 49463045Sdwmalone for (i=0; i<124; i++) au_wr(au, 0, AU_REG_RTBASE+(i<<2), 0xffffffff, 4); 49563045Sdwmalone 49677684Sdwmalone /* test */ 49766543Sdwmalone i=au_rd(au, 0, 0x107c0, 4); 49866543Sdwmalone if (i!=0xdeadbeef) device_printf(dev, "dma check failed: 0x%x\n", i); 49963045Sdwmalone 50063045Sdwmalone /* install mixer */ 50174934Sdwmalone au_wr(au, 0, AU_REG_IRQGLOB, 50274934Sdwmalone au_rd(au, 0, AU_REG_IRQGLOB, 4) | AU_IRQ_ENABLE, 4); 50363045Sdwmalone /* braindead but it's what the oss/linux driver does 50463045Sdwmalone * for (i=0; i<0x80000000; i++) au_wr(au, 0, i<<2, 0, 4); 50563045Sdwmalone */ 50663045Sdwmalone au->routes[0]=au->routes[1]=au->routes[2]=au->routes[3]=0; 50777684Sdwmalone /*au->x1e4=0;*/ 50877684Sdwmalone 50977684Sdwmalone /* attach channel */ 51077684Sdwmalone au_addroute(au, 0x11, 0x48, 0x02); 51156298Sgreen au_addroute(au, 0x11, 0x49, 0x03); 51277684Sdwmalone au_encodec(au, 0); 51349104Sgreen au_encodec(au, 1); 51458712Sgreen 51558712Sgreen for (i=0; i<48; i++) au_wr(au, 0, 0xf800+(i<<2), 0x20, 4); 51658712Sgreen for (i=2; i<6; i++) au_wr(au, 0, 0xf800+(i<<2), 0, 4); 51758712Sgreen au_wr(au, 0, 0xf8c0, 0x0843, 4); 51858712Sgreen for (i=0; i<4; i++) au_clrfifo(au, i); 51977684Sdwmalone 52077684Sdwmalone return (0); 52177684Sdwmalone} 52258712Sgreen 52377684Sdwmalonestatic int 52458712Sgreenau_testirq(struct au_info *au) 52549104Sgreen{ 52649104Sgreen au_wr(au, 0, AU_REG_UNK1, 0x80001000, 4); 52749104Sgreen au_wr(au, 0, AU_REG_IRQEN, 0x00001030, 4); 52849104Sgreen au_wr(au, 0, AU_REG_IRQSRC, 0x000007ff, 4); 52977684Sdwmalone DELAY(1000000); 53049104Sgreen if (au->interrupts==0) printf("pcm%d: irq test failed\n", au->unit); 53177684Sdwmalone /* this apparently generates an irq */ 53277684Sdwmalone return 0; 53377684Sdwmalone} 53477684Sdwmalone 53577684Sdwmalonestatic int 53677684Sdwmaloneau_pci_probe(device_t dev) 53756590Sshin{ 53877684Sdwmalone if (pci_get_devid(dev) == AU8820_PCI_ID) { 53961099Sgreen device_set_desc(dev, "Aureal Vortex 8820"); 54056590Sshin return BUS_PROBE_DEFAULT; 54156590Sshin } 54278694Sdwmalone 54378694Sdwmalone return ENXIO; 54478694Sdwmalone} 54578694Sdwmalone 54678694Sdwmalonestatic int 54778694Sdwmaloneau_pci_attach(device_t dev) 54869532Sgreen{ 54956590Sshin u_int32_t data; 55056590Sshin struct au_info *au; 55156590Sshin int type[10]; 55256590Sshin int regid[10]; 55356590Sshin struct resource *reg[10]; 55456590Sshin int i, j, mapped = 0; 55556590Sshin int irqid; 55661099Sgreen struct resource *irq = 0; 55756590Sshin void *ih = 0; 55869532Sgreen struct ac97_info *codec; 55956590Sshin char status[SND_STATUSLEN]; 56056590Sshin 56156590Sshin if ((au = malloc(sizeof(*au), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { 56269532Sgreen device_printf(dev, "cannot allocate softc\n"); 56356590Sshin return ENXIO; 56456590Sshin } 56591354Sdd 56677684Sdwmalone au->unit = device_get_unit(dev); 56777684Sdwmalone 56877684Sdwmalone data = pci_read_config(dev, PCIR_COMMAND, 2); 56977684Sdwmalone data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 57056298Sgreen pci_write_config(dev, PCIR_COMMAND, data, 2); 57177684Sdwmalone data = pci_read_config(dev, PCIR_COMMAND, 2); 57277684Sdwmalone 57377684Sdwmalone j=0; 57477684Sdwmalone /* XXX dfr: is this strictly necessary? */ 57577684Sdwmalone for (i=0; i<PCI_MAXMAPS_0; i++) { 57677684Sdwmalone#if 0 57777684Sdwmalone /* Slapped wrist: config_id and map are private structures */ 57877684Sdwmalone if (bootverbose) { 57977684Sdwmalone printf("pcm%d: map %d - allocating ", unit, i+1); 58077684Sdwmalone printf("0x%x bytes of ", 1<<config_id->map[i].ln2size); 58177684Sdwmalone printf("%s space ", (config_id->map[i].type & PCI_MAPPORT)? 58277684Sdwmalone "io" : "memory"); 58349104Sgreen printf("at 0x%x...", config_id->map[i].base); 58449104Sgreen } 58549104Sgreen#endif 58649104Sgreen regid[j] = PCIR_BAR(i); 58777684Sdwmalone type[j] = SYS_RES_MEMORY; 58849089Sgreen reg[j] = bus_alloc_resource_any(dev, type[j], ®id[j], 58977684Sdwmalone RF_ACTIVE); 59049089Sgreen if (!reg[j]) { 59149089Sgreen type[j] = SYS_RES_IOPORT; 59277684Sdwmalone reg[j] = bus_alloc_resource_any(dev, type[j], 59349057Sgreen ®id[j], RF_ACTIVE); 59449089Sgreen } 59549057Sgreen if (reg[j]) { 59677684Sdwmalone au->st[i] = rman_get_bustag(reg[j]); 59749104Sgreen au->sh[i] = rman_get_bushandle(reg[j]); 59849104Sgreen mapped++; 59949104Sgreen } 60049104Sgreen#if 0 60149104Sgreen if (bootverbose) printf("%s\n", mapped? "ok" : "failed"); 60277684Sdwmalone#endif 60369144Sgreen if (mapped) j++; 60449033Sgreen if (j == 10) { 60549104Sgreen /* XXX */ 60649104Sgreen device_printf(dev, "too many resources"); 60749104Sgreen goto bad; 60849104Sgreen } 60949104Sgreen } 61069144Sgreen 61177684Sdwmalone#if 0 61277684Sdwmalone if (j < config_id->nummaps) { 61377684Sdwmalone printf("pcm%d: unable to map a required resource\n", unit); 61449104Sgreen free(au, M_DEVBUF); 61569532Sgreen return; 61669532Sgreen } 61749104Sgreen#endif 61849104Sgreen 61949104Sgreen au_wr(au, 0, AU_REG_IRQEN, 0, 4); 62049104Sgreen 62177684Sdwmalone irqid = 0; 62277684Sdwmalone irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, 62369144Sgreen RF_ACTIVE | RF_SHAREABLE); 62449089Sgreen if (!irq || snd_setup_intr(dev, irq, 0, au_intr, au, &ih)) { 62577684Sdwmalone device_printf(dev, "unable to map interrupt\n"); 62677684Sdwmalone goto bad; 62777684Sdwmalone } 62877684Sdwmalone 62977684Sdwmalone if (au_testirq(au)) device_printf(dev, "irq test failed\n"); 63077684Sdwmalone 63177684Sdwmalone if (au_init(dev, au) == -1) { 63277684Sdwmalone device_printf(dev, "unable to initialize the card\n"); 63377684Sdwmalone goto bad; 63477684Sdwmalone } 63577684Sdwmalone 63677684Sdwmalone codec = AC97_CREATE(dev, au, au_ac97); 63777684Sdwmalone if (codec == NULL) goto bad; 63877684Sdwmalone if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; 63977684Sdwmalone 64077684Sdwmalone if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 64177684Sdwmalone /*boundary*/0, 64277684Sdwmalone /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 64377684Sdwmalone /*highaddr*/BUS_SPACE_MAXADDR, 64477684Sdwmalone /*filter*/NULL, /*filterarg*/NULL, 64577684Sdwmalone /*maxsize*/AU_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 64677684Sdwmalone /*flags*/0, /*lockfunc*/busdma_lock_mutex, 64777684Sdwmalone /*lockarg*/&Giant, &au->parent_dmat) != 0) { 64877684Sdwmalone device_printf(dev, "unable to create dma tag\n"); 64977684Sdwmalone goto bad; 65077684Sdwmalone } 65177684Sdwmalone 65277684Sdwmalone snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", 65377684Sdwmalone (type[0] == SYS_RES_IOPORT)? "io" : "memory", 65477684Sdwmalone rman_get_start(reg[0]), rman_get_start(irq),PCM_KLDSTRING(snd_aureal)); 65577684Sdwmalone 65677684Sdwmalone if (pcm_register(dev, au, 1, 1)) goto bad; 65777684Sdwmalone /* pcm_addchan(dev, PCMDIR_REC, &au_chantemplate, au); */ 65877684Sdwmalone pcm_addchan(dev, PCMDIR_PLAY, &auchan_class, au); 65948981Ssheldonh pcm_setstatus(dev, status); 66077684Sdwmalone 66177684Sdwmalone return 0; 66277684Sdwmalone 66377684Sdwmalone bad: 66477684Sdwmalone if (au) free(au, M_DEVBUF); 66577684Sdwmalone for (i = 0; i < j; i++) 66669144Sgreen bus_release_resource(dev, type[i], regid[i], reg[i]); 66777684Sdwmalone if (ih) bus_teardown_intr(dev, irq, ih); 66877684Sdwmalone if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); 66948981Ssheldonh return ENXIO; 67049104Sgreen} 67149004Sgreen 67277684Sdwmalonestatic device_method_t au_methods[] = { 67349057Sgreen /* Device interface */ 67449051Ssheldonh DEVMETHOD(device_probe, au_pci_probe), 67549051Ssheldonh DEVMETHOD(device_attach, au_pci_attach), 67658735Ssheldonh 67749004Sgreen { 0, 0 } 67848981Ssheldonh}; 67948981Ssheldonh 68048981Ssheldonhstatic driver_t au_driver = { 68148981Ssheldonh "pcm", 68248981Ssheldonh au_methods, 683157820Sdwmalone PCM_SOFTC_SIZE, 68448981Ssheldonh}; 68548981Ssheldonh 68648981SsheldonhDRIVER_MODULE(snd_aureal, pci, au_driver, pcm_devclass, 0, 0); 68748981SsheldonhMODULE_DEPEND(snd_aureal, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 68848981SsheldonhMODULE_VERSION(snd_aureal, 1); 68948981Ssheldonh