fm801.c revision 65644
165205Scg/* 265205Scg * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com 365205Scg * All rights reserved. 465205Scg * 565205Scg * Redistribution and use in source and binary forms, with or without 665205Scg * modification, are permitted provided that the following conditions 765205Scg * are met: 865205Scg * 1. Redistributions of source code must retain the above copyright 965205Scg * notice, this list of conditions and the following disclaimer. 1065205Scg * 2. Redistributions in binary form must reproduce the above copyright 1165205Scg * notice, this list of conditions and the following disclaimer in the 1265205Scg * documentation and/or other materials provided with the distribution. 1365205Scg * 1465205Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND 1565205Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1665205Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1765205Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1865205Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1965205Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2065205Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2165205Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2265205Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2365205Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2465205Scg * SUCH DAMAGE. 2565205Scg * 2665205Scg * $FreeBSD: head/sys/dev/sound/pci/fm801.c 65644 2000-09-09 19:21:04Z cg $ 2765205Scg */ 2865205Scg 2965205Scg#include <dev/sound/pcm/sound.h> 3065205Scg#include <dev/sound/pcm/ac97.h> 3165205Scg#include <pci/pcireg.h> 3265205Scg#include <pci/pcivar.h> 3365205Scg 3465205Scg#define PCI_VENDOR_FORTEMEDIA 0x1319 3565205Scg#define PCI_DEVICE_FORTEMEDIA1 0x08011319 3665205Scg#define PCI_DEVICE_FORTEMEDIA2 0x08021319 /* ??? have no idea what's this... */ 3765205Scg 3865205Scg#define FM_PCM_VOLUME 0x00 3965205Scg#define FM_FM_VOLUME 0x02 4065205Scg#define FM_I2S_VOLUME 0x04 4165205Scg#define FM_RECORD_SOURCE 0x06 4265205Scg 4365205Scg#define FM_PLAY_CTL 0x08 4465205Scg#define FM_PLAY_RATE_MASK 0x0f00 4565205Scg#define FM_PLAY_BUF1_LAST 0x0001 4665205Scg#define FM_PLAY_BUF2_LAST 0x0002 4765205Scg#define FM_PLAY_START 0x0020 4865205Scg#define FM_PLAY_PAUSE 0x0040 4965205Scg#define FM_PLAY_STOPNOW 0x0080 5065205Scg#define FM_PLAY_16BIT 0x4000 5165205Scg#define FM_PLAY_STEREO 0x8000 5265205Scg 5365205Scg#define FM_PLAY_DMALEN 0x0a 5465205Scg#define FM_PLAY_DMABUF1 0x0c 5565205Scg#define FM_PLAY_DMABUF2 0x10 5665205Scg 5765205Scg 5865205Scg#define FM_REC_CTL 0x14 5965205Scg#define FM_REC_RATE_MASK 0x0f00 6065205Scg#define FM_REC_BUF1_LAST 0x0001 6165205Scg#define FM_REC_BUF2_LAST 0x0002 6265205Scg#define FM_REC_START 0x0020 6365205Scg#define FM_REC_PAUSE 0x0040 6465205Scg#define FM_REC_STOPNOW 0x0080 6565205Scg#define FM_REC_16BIT 0x4000 6665205Scg#define FM_REC_STEREO 0x8000 6765205Scg 6865205Scg 6965205Scg#define FM_REC_DMALEN 0x16 7065205Scg#define FM_REC_DMABUF1 0x18 7165205Scg#define FM_REC_DMABUF2 0x1c 7265205Scg 7365205Scg#define FM_CODEC_CTL 0x22 7465205Scg#define FM_VOLUME 0x26 7565205Scg#define FM_VOLUME_MUTE 0x8000 7665205Scg 7765205Scg#define FM_CODEC_CMD 0x2a 7865205Scg#define FM_CODEC_CMD_READ 0x0080 7965205Scg#define FM_CODEC_CMD_VALID 0x0100 8065205Scg#define FM_CODEC_CMD_BUSY 0x0200 8165205Scg 8265205Scg#define FM_CODEC_DATA 0x2c 8365205Scg 8465205Scg#define FM_IO_CTL 0x52 8565205Scg#define FM_CARD_CTL 0x54 8665205Scg 8765205Scg#define FM_INTMASK 0x56 8865205Scg#define FM_INTMASK_PLAY 0x0001 8965205Scg#define FM_INTMASK_REC 0x0002 9065205Scg#define FM_INTMASK_VOL 0x0040 9165205Scg#define FM_INTMASK_MPU 0x0080 9265205Scg 9365205Scg#define FM_INTSTATUS 0x5a 9465205Scg#define FM_INTSTATUS_PLAY 0x0100 9565205Scg#define FM_INTSTATUS_REC 0x0200 9665205Scg#define FM_INTSTATUS_VOL 0x4000 9765205Scg#define FM_INTSTATUS_MPU 0x8000 9865205Scg 9965205Scg#define FM801_BUFFSIZE 1024*4 /* Other values do not work!!! */ 10065205Scg 10165205Scg/* debug purposes */ 10265205Scg#define DPRINT if(0) printf 10365205Scg 10465205Scg 10565205Scg/* channel interface */ 10665205Scgstatic void *fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); 10765205Scgstatic int fm801ch_setformat(void *data, u_int32_t format); 10865205Scgstatic int fm801ch_setspeed(void *data, u_int32_t speed); 10965205Scgstatic int fm801ch_setblocksize(void *data, u_int32_t blocksize); 11065205Scgstatic int fm801ch_trigger(void *data, int go); 11165205Scgstatic int fm801ch_getptr(void *data); 11265205Scgstatic pcmchan_caps *fm801ch_getcaps(void *data); 11365205Scg/* 11465205Scgstatic int fm801ch_setup(pcm_channel *c); 11565205Scg*/ 11665205Scg 11765205Scgstatic u_int32_t fmts[] = { 11865205Scg AFMT_U8, 11965205Scg AFMT_STEREO | AFMT_U8, 12065205Scg AFMT_S16_LE, 12165644Scg AFMT_STEREO | AFMT_S16_LE, 12265205Scg 0 12365205Scg}; 12465205Scg 12565205Scgstatic pcmchan_caps fm801ch_caps = { 12665205Scg 4000, 48000, 12765205Scg fmts, 0 12865205Scg}; 12965205Scg 13065205Scgstatic pcm_channel fm801_chantemplate = { 13165205Scg fm801ch_init, 13265644Scg NULL, /* setdir */ 13365205Scg fm801ch_setformat, 13465205Scg fm801ch_setspeed, 13565205Scg fm801ch_setblocksize, 13665205Scg fm801ch_trigger, 13765205Scg fm801ch_getptr, 13865205Scg fm801ch_getcaps, 13965340Scg NULL, /* free */ 14065340Scg NULL, /* nop1 */ 14165340Scg NULL, /* nop2 */ 14265340Scg NULL, /* nop3 */ 14365340Scg NULL, /* nop4 */ 14465340Scg NULL, /* nop5 */ 14565340Scg NULL, /* nop6 */ 14665340Scg NULL, /* nop7 */ 14765205Scg}; 14865205Scg 14965205Scgstruct fm801_info; 15065205Scg 15165205Scgstruct fm801_chinfo { 15265205Scg struct fm801_info *parent; 15365205Scg pcm_channel *channel; 15465205Scg snd_dbuf *buffer; 15565205Scg u_int32_t spd, dir, fmt; /* speed, direction, format */ 15665205Scg u_int32_t shift; 15765205Scg}; 15865205Scg 15965205Scgstruct fm801_info { 16065205Scg int type; 16165205Scg bus_space_tag_t st; 16265205Scg bus_space_handle_t sh; 16365205Scg bus_dma_tag_t parent_dmat; 16465205Scg 16565205Scg device_t dev; 16665205Scg int num; 16765205Scg u_int32_t unit; 16865205Scg 16965205Scg struct resource *reg, *irq; 17065205Scg int regtype, regid, irqid; 17165205Scg void *ih; 17265205Scg 17365205Scg u_int32_t play_flip, 17465205Scg play_nextblk, 17565205Scg play_start, 17665205Scg play_blksize, 17765205Scg play_fmt, 17865205Scg play_shift, 17965205Scg play_size; 18065205Scg 18165205Scg u_int32_t rec_flip, 18265205Scg rec_nextblk, 18365205Scg rec_start, 18465205Scg rec_blksize, 18565205Scg rec_fmt, 18665205Scg rec_shift, 18765205Scg rec_size; 18865205Scg 18965205Scg struct fm801_chinfo pch, rch; 19065205Scg}; 19165205Scg 19265205Scg/* Bus Read / Write routines */ 19365205Scgstatic u_int32_t 19465205Scgfm801_rd(struct fm801_info *fm801, int regno, int size) 19565205Scg{ 19665205Scg switch(size) { 19765205Scg case 1: 19865205Scg return (bus_space_read_1(fm801->st, fm801->sh, regno)); 19965205Scg case 2: 20065205Scg return (bus_space_read_2(fm801->st, fm801->sh, regno)); 20165205Scg case 4: 20265205Scg return (bus_space_read_4(fm801->st, fm801->sh, regno)); 20365205Scg default: 20465205Scg return 0xffffffff; 20565205Scg } 20665205Scg} 20765205Scg 20865205Scgstatic void 20965205Scgfm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size) 21065205Scg{ 21165205Scg switch(size) { 21265205Scg case 1: 21365205Scg return bus_space_write_1(fm801->st, fm801->sh, regno, data); 21465205Scg case 2: 21565205Scg return bus_space_write_2(fm801->st, fm801->sh, regno, data); 21665205Scg case 4: 21765205Scg return bus_space_write_4(fm801->st, fm801->sh, regno, data); 21865205Scg default: 21965205Scg return; 22065205Scg } 22165205Scg} 22265205Scg 22365205Scg/* 22465205Scg * ac97 codec routines 22565205Scg */ 22665205Scg#define TIMO 50 22765205Scgstatic u_int32_t 22865205Scgfm801_rdcd(void *devinfo, int regno) 22965205Scg{ 23065205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 23165205Scg int i; 23265340Scg 23365205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 23465205Scg DELAY(10000); 23565205Scg DPRINT("fm801 rdcd: 1 - DELAY\n"); 23665205Scg } 23765205Scg if (i >= TIMO) { 23865205Scg printf("fm801 rdcd: codec busy\n"); 23965205Scg return 0; 24065205Scg } 24165340Scg 24265205Scg fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2); 24365205Scg 24465205Scg for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++) 24565205Scg { 24665205Scg DELAY(10000); 24765205Scg DPRINT("fm801 rdcd: 2 - DELAY\n"); 24865205Scg } 24965205Scg if (i >= TIMO) { 25065205Scg printf("fm801 rdcd: write codec invalid\n"); 25165205Scg return 0; 25265205Scg } 25365340Scg 25465205Scg return fm801_rd(fm801,FM_CODEC_DATA,2); 25565205Scg} 25665205Scg 25765205Scgstatic void 25865205Scgfm801_wrcd(void *devinfo, int regno, u_int32_t data) 25965205Scg{ 26065205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 26165205Scg int i; 26265340Scg 26365205Scg DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data); 26465340Scg/* 26565205Scg if(regno == AC97_REG_RECSEL) return; 26665340Scg*/ 26765205Scg /* Poll until codec is ready */ 26865205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 26965205Scg DELAY(10000); 27065205Scg DPRINT("fm801 rdcd: 1 - DELAY\n"); 27165205Scg } 27265205Scg if (i >= TIMO) { 27365205Scg printf("fm801 wrcd: read codec busy\n"); 27465205Scg return; 27565205Scg } 27665340Scg 27765205Scg fm801_wr(fm801,FM_CODEC_DATA,data, 2); 27865205Scg fm801_wr(fm801,FM_CODEC_CMD, regno,2); 27965340Scg 28065205Scg /* wait until codec is ready */ 28165205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 28265205Scg DELAY(10000); 28365205Scg DPRINT("fm801 wrcd: 2 - DELAY\n"); 28465205Scg } 28565205Scg if (i >= TIMO) { 28665205Scg printf("fm801 wrcd: read codec busy\n"); 28765205Scg return; 28865205Scg } 28965205Scg DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data); 29065205Scg return; 29165205Scg} 29265205Scg 29365340Scg/* 29465340Scg * The interrupt handler 29565205Scg */ 29665205Scgstatic void 29765205Scgfm801_intr(void *p) 29865205Scg{ 29965205Scg struct fm801_info *fm801 = (struct fm801_info *)p; 30065205Scg u_int32_t intsrc = fm801_rd(fm801, FM_INTSTATUS, 2); 30165205Scg struct fm801_chinfo *ch = &(fm801->pch); 30265205Scg snd_dbuf *b = ch->buffer; 30365340Scg 30465205Scg DPRINT("\nfm801_intr intsrc 0x%x ", intsrc); 30565205Scg DPRINT("rp %d, rl %d, fp %d fl %d, size=%d\n", 30665205Scg b->rp,b->rl, b->fp,b->fl, b->blksz); 30765340Scg 30865205Scg if(intsrc & FM_INTSTATUS_PLAY) { 30965205Scg fm801->play_flip++; 31065205Scg if(fm801->play_flip & 1) { 31165205Scg fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4); 31265205Scg } else 31365205Scg fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4); 31465205Scg chn_intr(fm801->pch.channel); 31565205Scg } 31665340Scg 31765205Scg if(intsrc & FM_INTSTATUS_REC) { 31865205Scg fm801->rec_flip++; 31965205Scg if(fm801->rec_flip & 1) { 32065205Scg fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4); 32165205Scg } else 32265205Scg fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4); 32365205Scg chn_intr(fm801->rch.channel); 32465205Scg } 32565340Scg 32665205Scg if ( intsrc & FM_INTSTATUS_MPU ) { 32765205Scg /* This is a TODOish thing... */ 32865205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2); 32965205Scg } 33065340Scg 33165205Scg if ( intsrc & FM_INTSTATUS_VOL ) { 33265205Scg /* This is a TODOish thing... */ 33365205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2); 33465205Scg } 33565340Scg 33665205Scg DPRINT("fm801_intr clear\n\n"); 33765205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2); 33865205Scg} 33965205Scg 34065205Scg/* 34165205Scg * Init routine is taken from an original NetBSD driver 34265205Scg */ 34365205Scgstatic int 34465205Scgfm801_init(struct fm801_info *fm801) 34565205Scg{ 34665205Scg u_int32_t k1; 34765340Scg 34865205Scg /* reset codec */ 34965205Scg fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2); 35065205Scg DELAY(100000); 35165205Scg fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2); 35265205Scg DELAY(100000); 35365340Scg 35465205Scg fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2); 35565205Scg fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2); 35665205Scg fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2); 35765205Scg fm801_wr(fm801, 0x40,0x107f,2); /* enable legacy audio */ 35865340Scg 35965205Scg fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2); 36065340Scg 36165205Scg /* Unmask playback, record and mpu interrupts, mask the rest */ 36265205Scg k1 = fm801_rd((void *)fm801, FM_INTMASK,2); 36365205Scg fm801_wr(fm801, FM_INTMASK, 36465205Scg (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) | 36565205Scg FM_INTMASK_VOL,2); 36665205Scg fm801_wr(fm801, FM_INTSTATUS, 36765205Scg FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU | 36865205Scg FM_INTSTATUS_VOL,2); 36965340Scg 37065205Scg DPRINT("FM801 init Ok\n"); 37165205Scg return 0; 37265205Scg} 37365205Scg 37465205Scgstatic int 37565205Scgfm801_pci_attach(device_t dev) 37665205Scg{ 37765205Scg u_int32_t data; 37865644Scg struct ac97_info *codec = 0; 37965205Scg struct fm801_info *fm801; 38065205Scg int i; 38165205Scg int mapped = 0; 38265205Scg char status[SND_STATUSLEN]; 38365340Scg 38465205Scg if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) { 38565205Scg device_printf(dev, "cannot allocate softc\n"); 38665205Scg return ENXIO; 38765205Scg } 38865340Scg 38965205Scg bzero(fm801, sizeof(*fm801)); 39065205Scg fm801->type = pci_get_devid(dev); 39165340Scg 39265205Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 39365205Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 39465205Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 39565205Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 39665340Scg 39765205Scg for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { 39865205Scg fm801->regid = PCIR_MAPS + i*4; 39965205Scg fm801->regtype = SYS_RES_MEMORY; 40065205Scg fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, 40165205Scg 0, ~0, 1, RF_ACTIVE); 40265205Scg if(!fm801->reg) 40365205Scg { 40465205Scg fm801->regtype = SYS_RES_IOPORT; 40565205Scg fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, 40665205Scg 0, ~0, 1, RF_ACTIVE); 40765205Scg } 40865340Scg 40965205Scg if(fm801->reg) { 41065205Scg fm801->st = rman_get_bustag(fm801->reg); 41165205Scg fm801->sh = rman_get_bushandle(fm801->reg); 41265205Scg mapped++; 41365205Scg } 41465205Scg } 41565205Scg 41665205Scg if (mapped == 0) { 41765205Scg device_printf(dev, "unable to map register space\n"); 41865205Scg goto oops; 41965205Scg } 42065340Scg 42165205Scg fm801_init(fm801); 42265340Scg 42365205Scg codec = ac97_create(dev, (void *)fm801, NULL, fm801_rdcd, fm801_wrcd); 42465205Scg if (codec == NULL) goto oops; 42565205Scg 42665340Scg if (mixer_init(dev, &ac97_mixer, codec) == -1) goto oops; 42765205Scg 42865205Scg fm801->irqid = 0; 42965205Scg fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid, 43065205Scg 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 43165205Scg if (!fm801->irq || 43265205Scg bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY, 43365205Scg fm801_intr, fm801, &fm801->ih)) { 43465205Scg device_printf(dev, "unable to map interrupt\n"); 43565205Scg goto oops; 43665205Scg } 43765340Scg 43865205Scg if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 43965205Scg /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 44065205Scg /*highaddr*/BUS_SPACE_MAXADDR, 44165205Scg /*filter*/NULL, /*filterarg*/NULL, 44265205Scg /*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 44365205Scg /*flags*/0, &fm801->parent_dmat) != 0) { 44465205Scg device_printf(dev, "unable to create dma tag\n"); 44565205Scg goto oops; 44665205Scg } 44765340Scg 44865205Scg snprintf(status, 64, "at %s 0x%lx irq %ld", 44965205Scg (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", 45065205Scg rman_get_start(fm801->reg), rman_get_start(fm801->irq)); 45165205Scg 45265205Scg#define FM801_MAXPLAYCH 1 45365205Scg if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; 45465205Scg pcm_addchan(dev, PCMDIR_PLAY, &fm801_chantemplate, fm801); 45565205Scg pcm_addchan(dev, PCMDIR_REC, &fm801_chantemplate, fm801); 45665205Scg pcm_setstatus(dev, status); 45765205Scg 45865205Scg return 0; 45965340Scg 46065340Scgoops: 46165644Scg if (codec) ac97_destroy(codec); 46265205Scg if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); 46365205Scg if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih); 46465205Scg if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); 46565644Scg if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat); 46665205Scg free(fm801, M_DEVBUF); 46765205Scg return ENXIO; 46865205Scg} 46965205Scg 47065205Scgstatic int 47165644Scgfm801_pci_detach(device_t dev) 47265644Scg{ 47365644Scg int r; 47465644Scg struct fm801_info *fm801; 47565644Scg 47665644Scg DPRINT("Forte Media FM801 detach\n"); 47765644Scg 47865644Scg r = pcm_unregister(dev); 47965644Scg if (r) 48065644Scg return r; 48165644Scg 48265644Scg fm801 = pcm_getdevinfo(dev); 48365644Scg bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); 48465644Scg bus_teardown_intr(dev, fm801->irq, fm801->ih); 48565644Scg bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); 48665644Scg bus_dma_tag_destroy(fm801->parent_dmat); 48765644Scg free(fm801, M_DEVBUF); 48865644Scg return 0; 48965644Scg} 49065644Scg 49165644Scgstatic int 49265205Scgfm801_pci_probe( device_t dev ) 49365205Scg{ 49465205Scg int id; 49565205Scg if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { 49665205Scg device_set_desc(dev, "Forte Media FM801 Audio Controller"); 49765205Scg return 0; 49865205Scg } 49965205Scg/* 50065205Scg if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) { 50165205Scg device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)"); 50265205Scg return ENXIO; 50365205Scg } 50465205Scg*/ 50565205Scg return ENXIO; 50665205Scg} 50765205Scg 50865205Scg 50965205Scg 51065205Scg/* channel interface */ 51165205Scgstatic void * 51265205Scgfm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 51365205Scg{ 51465205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 51565205Scg struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch; 51665340Scg 51765205Scg DPRINT("fm801ch_init, direction = %d\n", dir); 51865205Scg ch->parent = fm801; 51965205Scg ch->channel = c; 52065205Scg ch->buffer = b; 52165205Scg ch->buffer->bufsize = FM801_BUFFSIZE; 52265205Scg ch->dir = dir; 52365205Scg if( chn_allocbuf(ch->buffer, fm801->parent_dmat) == -1) return NULL; 52465205Scg return (void *)ch; 52565205Scg} 52665205Scg 52765205Scgstatic int 52865205Scgfm801ch_setformat(void *data, u_int32_t format) 52965205Scg{ 53065205Scg struct fm801_chinfo *ch = data; 53165205Scg struct fm801_info *fm801 = ch->parent; 53265340Scg 53365205Scg DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format, 53465205Scg (format & AFMT_STEREO)?"stereo":"mono", 53565205Scg (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit", 53665205Scg (format & AFMT_SIGNED)? "signed":"unsigned", 53765205Scg (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" ); 53865340Scg 53965205Scg if(ch->dir == PCMDIR_PLAY) { 54065205Scg fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0; 54165205Scg fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; 54265205Scg return 0; 54365205Scg } 54465340Scg 54565205Scg if(ch->dir == PCMDIR_REC ) { 54665205Scg fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0; 54765205Scg fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; 54865205Scg return 0; 54965205Scg } 55065340Scg 55165205Scg return 0; 55265205Scg} 55365205Scg 55465205Scgstruct { 55565205Scg int limit; 55665205Scg int rate; 55765205Scg} fm801_rates[11] = { 55865340Scg { 6600, 5500 }, 55965340Scg { 8750, 8000 }, 56065340Scg { 10250, 9600 }, 56165340Scg { 13200, 11025 }, 56265340Scg { 17500, 16000 }, 56365340Scg { 20500, 19200 }, 56465340Scg { 26500, 22050 }, 56565340Scg { 35000, 32000 }, 56665340Scg { 41000, 38400 }, 56765340Scg { 46000, 44100 }, 56865340Scg { 48000, 48000 }, 56965205Scg/* anything above -> 48000 */ 57065205Scg}; 57165205Scg 57265205Scgstatic int 57365205Scgfm801ch_setspeed(void *data, u_int32_t speed) 57465205Scg{ 57565205Scg struct fm801_chinfo *ch = data; 57665205Scg struct fm801_info *fm801 = ch->parent; 57765205Scg register int i; 57865340Scg 57965340Scg 58065205Scg for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ; 58165340Scg 58265205Scg if(ch->dir == PCMDIR_PLAY) { 58365205Scg fm801->pch.spd = fm801_rates[i].rate; 58465205Scg fm801->play_shift = (i<<8); 58565205Scg fm801->play_shift &= FM_PLAY_RATE_MASK; 58665205Scg } 58765340Scg 58865205Scg if(ch->dir == PCMDIR_REC ) { 58965205Scg fm801->rch.spd = fm801_rates[i].rate; 59065205Scg fm801->rec_shift = (i<<8); 59165205Scg fm801->rec_shift &= FM_REC_RATE_MASK; 59265205Scg } 59365340Scg 59465205Scg ch->spd = fm801_rates[i].rate; 59565340Scg 59665205Scg return fm801_rates[i].rate; 59765205Scg} 59865205Scg 59965205Scgstatic int 60065205Scgfm801ch_setblocksize(void *data, u_int32_t blocksize) 60165205Scg{ 60265205Scg struct fm801_chinfo *ch = data; 60365205Scg struct fm801_info *fm801 = ch->parent; 60465340Scg 60565205Scg if(ch->dir == PCMDIR_PLAY) { 60665205Scg if(fm801->play_flip) return fm801->play_blksize; 60765205Scg fm801->play_blksize = blocksize; 60865205Scg } 60965340Scg 61065205Scg if(ch->dir == PCMDIR_REC) { 61165205Scg if(fm801->rec_flip) return fm801->rec_blksize; 61265205Scg fm801->rec_blksize = blocksize; 61365205Scg } 61465340Scg 61565205Scg DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir); 61665205Scg 61765205Scg return blocksize; 61865205Scg} 61965205Scg 62065205Scgstatic int 62165205Scgfm801ch_trigger(void *data, int go) 62265205Scg{ 62365205Scg struct fm801_chinfo *ch = data; 62465205Scg struct fm801_info *fm801 = ch->parent; 62565205Scg u_int32_t baseaddr = vtophys(ch->buffer->buf); 62665205Scg snd_dbuf *b = ch->buffer; 62765205Scg u_int32_t k1; 62865340Scg 62965205Scg DPRINT("fm801ch_trigger go %d , ", go); 63065205Scg DPRINT("rp %d, rl %d, fp %d fl %d, dl %d, blksize=%d\n", 63165205Scg b->rp,b->rl, b->fp,b->fl, b->dl, b->blksz); 63265340Scg 63365205Scg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) { 63465205Scg return 0; 63565205Scg } 63665340Scg 63765205Scg if (ch->dir == PCMDIR_PLAY) { 63865205Scg if (go == PCMTRIG_START) { 63965340Scg 64065205Scg fm801->play_start = baseaddr; 64165205Scg fm801->play_nextblk = fm801->play_start + fm801->play_blksize; 64265205Scg fm801->play_flip = 0; 64365205Scg fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2); 64465205Scg fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4); 64565205Scg fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4); 64665205Scg fm801_wr(fm801, FM_PLAY_CTL, 64765340Scg FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift, 64865205Scg 2 ); 64965205Scg } else { 65065205Scg fm801->play_flip = 0; 65165205Scg k1 = fm801_rd(fm801, FM_PLAY_CTL,2); 65265205Scg fm801_wr(fm801, FM_PLAY_CTL, 65365205Scg (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) | 65465205Scg FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 ); 65565205Scg } 65665205Scg } else if(ch->dir == PCMDIR_REC) { 65765205Scg if (go == PCMTRIG_START) { 65865205Scg fm801->rec_start = baseaddr; 65965205Scg fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize; 66065205Scg fm801->rec_flip = 0; 66165205Scg fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2); 66265205Scg fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4); 66365205Scg fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4); 66465340Scg fm801_wr(fm801, FM_REC_CTL, 66565340Scg FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift, 66665205Scg 2 ); 66765205Scg } else { 66865205Scg fm801->rec_flip = 0; 66965205Scg k1 = fm801_rd(fm801, FM_REC_CTL,2); 67065205Scg fm801_wr(fm801, FM_REC_CTL, 67165205Scg (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) | 67265205Scg FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2); 67365205Scg } 67465205Scg } 67565340Scg 67665205Scg return 0; 67765205Scg} 67865205Scg 67965205Scg/* Almost ALSA copy */ 68065205Scgstatic int 68165205Scgfm801ch_getptr(void *data) 68265205Scg{ 68365205Scg struct fm801_chinfo *ch = data; 68465205Scg struct fm801_info *fm801 = ch->parent; 68565205Scg int result = 0; 68665205Scg snd_dbuf *b = ch->buffer; 68765340Scg 68865205Scg if (ch->dir == PCMDIR_PLAY) { 68965340Scg result = fm801_rd(fm801, 69065340Scg (fm801->play_flip&1) ? 69165205Scg FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start; 69265205Scg } 69365340Scg 69465205Scg if (ch->dir == PCMDIR_REC) { 69565205Scg result = fm801_rd(fm801, 69665205Scg (fm801->rec_flip&1) ? 69765205Scg FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start; 69865340Scg } 69965340Scg 70065205Scg DPRINT("fm801ch_getptr:%d, rp %d, rl %d, fp %d fl %d\n", 70165340Scg result, b->rp,b->rl, b->fp,b->fl); 70265205Scg 70365205Scg return result; 70465205Scg} 70565205Scg 70665205Scgstatic pcmchan_caps * 70765205Scgfm801ch_getcaps(void *data) 70865205Scg{ 70965205Scg return &fm801ch_caps; 71065205Scg} 71165205Scg 71265205Scgstatic device_method_t fm801_methods[] = { 71365205Scg /* Device interface */ 71465205Scg DEVMETHOD(device_probe, fm801_pci_probe), 71565205Scg DEVMETHOD(device_attach, fm801_pci_attach), 71665205Scg DEVMETHOD(device_detach, fm801_pci_detach), 71765205Scg { 0, 0} 71865205Scg}; 71965205Scg 72065205Scgstatic driver_t fm801_driver = { 72165205Scg "pcm", 72265205Scg fm801_methods, 72365205Scg sizeof(snddev_info), 72465205Scg}; 72565205Scg 72665205Scgstatic devclass_t pcm_devclass; 72765205Scg 72865644ScgDRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0); 729