fm801.c revision 70134
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 70134 2000-12-18 01:36:41Z 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/* 10565205Scgstatic int fm801ch_setup(pcm_channel *c); 10665205Scg*/ 10765205Scg 10865205Scgstatic u_int32_t fmts[] = { 10965205Scg AFMT_U8, 11065205Scg AFMT_STEREO | AFMT_U8, 11165205Scg AFMT_S16_LE, 11265644Scg AFMT_STEREO | AFMT_S16_LE, 11365205Scg 0 11465205Scg}; 11565205Scg 11665205Scgstatic pcmchan_caps fm801ch_caps = { 11765205Scg 4000, 48000, 11865205Scg fmts, 0 11965205Scg}; 12065205Scg 12165205Scgstruct fm801_info; 12265205Scg 12365205Scgstruct fm801_chinfo { 12465205Scg struct fm801_info *parent; 12565205Scg pcm_channel *channel; 12665205Scg snd_dbuf *buffer; 12765205Scg u_int32_t spd, dir, fmt; /* speed, direction, format */ 12865205Scg u_int32_t shift; 12965205Scg}; 13065205Scg 13165205Scgstruct fm801_info { 13265205Scg int type; 13365205Scg bus_space_tag_t st; 13465205Scg bus_space_handle_t sh; 13565205Scg bus_dma_tag_t parent_dmat; 13665205Scg 13765205Scg device_t dev; 13865205Scg int num; 13965205Scg u_int32_t unit; 14065205Scg 14165205Scg struct resource *reg, *irq; 14265205Scg int regtype, regid, irqid; 14365205Scg void *ih; 14465205Scg 14565205Scg u_int32_t play_flip, 14665205Scg play_nextblk, 14765205Scg play_start, 14865205Scg play_blksize, 14965205Scg play_fmt, 15065205Scg play_shift, 15165205Scg play_size; 15265205Scg 15365205Scg u_int32_t rec_flip, 15465205Scg rec_nextblk, 15565205Scg rec_start, 15665205Scg rec_blksize, 15765205Scg rec_fmt, 15865205Scg rec_shift, 15965205Scg rec_size; 16065205Scg 16165205Scg struct fm801_chinfo pch, rch; 16265205Scg}; 16365205Scg 16465205Scg/* Bus Read / Write routines */ 16565205Scgstatic u_int32_t 16665205Scgfm801_rd(struct fm801_info *fm801, int regno, int size) 16765205Scg{ 16865205Scg switch(size) { 16965205Scg case 1: 17065205Scg return (bus_space_read_1(fm801->st, fm801->sh, regno)); 17165205Scg case 2: 17265205Scg return (bus_space_read_2(fm801->st, fm801->sh, regno)); 17365205Scg case 4: 17465205Scg return (bus_space_read_4(fm801->st, fm801->sh, regno)); 17565205Scg default: 17665205Scg return 0xffffffff; 17765205Scg } 17865205Scg} 17965205Scg 18065205Scgstatic void 18165205Scgfm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size) 18265205Scg{ 18365205Scg switch(size) { 18465205Scg case 1: 18565205Scg return bus_space_write_1(fm801->st, fm801->sh, regno, data); 18665205Scg case 2: 18765205Scg return bus_space_write_2(fm801->st, fm801->sh, regno, data); 18865205Scg case 4: 18965205Scg return bus_space_write_4(fm801->st, fm801->sh, regno, data); 19065205Scg default: 19165205Scg return; 19265205Scg } 19365205Scg} 19465205Scg 19570134Scg/* -------------------------------------------------------------------- */ 19665205Scg/* 19765205Scg * ac97 codec routines 19865205Scg */ 19965205Scg#define TIMO 50 20070134Scgstatic int 20170134Scgfm801_rdcd(kobj_t obj, void *devinfo, int regno) 20265205Scg{ 20365205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 20465205Scg int i; 20565340Scg 20665205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 20765205Scg DELAY(10000); 20865205Scg DPRINT("fm801 rdcd: 1 - DELAY\n"); 20965205Scg } 21065205Scg if (i >= TIMO) { 21165205Scg printf("fm801 rdcd: codec busy\n"); 21265205Scg return 0; 21365205Scg } 21465340Scg 21565205Scg fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2); 21665205Scg 21765205Scg for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++) 21865205Scg { 21965205Scg DELAY(10000); 22065205Scg DPRINT("fm801 rdcd: 2 - DELAY\n"); 22165205Scg } 22265205Scg if (i >= TIMO) { 22365205Scg printf("fm801 rdcd: write codec invalid\n"); 22465205Scg return 0; 22565205Scg } 22665340Scg 22765205Scg return fm801_rd(fm801,FM_CODEC_DATA,2); 22865205Scg} 22965205Scg 23070134Scgstatic int 23170134Scgfm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 23265205Scg{ 23365205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 23465205Scg int i; 23565340Scg 23665205Scg DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data); 23765340Scg/* 23865205Scg if(regno == AC97_REG_RECSEL) return; 23965340Scg*/ 24065205Scg /* Poll until codec is ready */ 24165205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 24265205Scg DELAY(10000); 24365205Scg DPRINT("fm801 rdcd: 1 - DELAY\n"); 24465205Scg } 24565205Scg if (i >= TIMO) { 24665205Scg printf("fm801 wrcd: read codec busy\n"); 24770134Scg return -1; 24865205Scg } 24965340Scg 25065205Scg fm801_wr(fm801,FM_CODEC_DATA,data, 2); 25165205Scg fm801_wr(fm801,FM_CODEC_CMD, regno,2); 25265340Scg 25365205Scg /* wait until codec is ready */ 25465205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 25565205Scg DELAY(10000); 25665205Scg DPRINT("fm801 wrcd: 2 - DELAY\n"); 25765205Scg } 25865205Scg if (i >= TIMO) { 25965205Scg printf("fm801 wrcd: read codec busy\n"); 26070134Scg return -1; 26165205Scg } 26265205Scg DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data); 26370134Scg return 0; 26465205Scg} 26565205Scg 26670134Scgstatic kobj_method_t fm801_ac97_methods[] = { 26770134Scg KOBJMETHOD(ac97_read, fm801_rdcd), 26870134Scg KOBJMETHOD(ac97_write, fm801_wrcd), 26970134Scg { 0, 0 } 27070134Scg}; 27170134ScgAC97_DECLARE(fm801_ac97); 27270134Scg 27370134Scg/* -------------------------------------------------------------------- */ 27470134Scg 27565340Scg/* 27665340Scg * The interrupt handler 27765205Scg */ 27865205Scgstatic void 27965205Scgfm801_intr(void *p) 28065205Scg{ 28165205Scg struct fm801_info *fm801 = (struct fm801_info *)p; 28265205Scg u_int32_t intsrc = fm801_rd(fm801, FM_INTSTATUS, 2); 28365205Scg struct fm801_chinfo *ch = &(fm801->pch); 28465205Scg snd_dbuf *b = ch->buffer; 28565340Scg 28665205Scg DPRINT("\nfm801_intr intsrc 0x%x ", intsrc); 28765205Scg DPRINT("rp %d, rl %d, fp %d fl %d, size=%d\n", 28865205Scg b->rp,b->rl, b->fp,b->fl, b->blksz); 28965340Scg 29065205Scg if(intsrc & FM_INTSTATUS_PLAY) { 29165205Scg fm801->play_flip++; 29265205Scg if(fm801->play_flip & 1) { 29365205Scg fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4); 29465205Scg } else 29565205Scg fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4); 29665205Scg chn_intr(fm801->pch.channel); 29765205Scg } 29865340Scg 29965205Scg if(intsrc & FM_INTSTATUS_REC) { 30065205Scg fm801->rec_flip++; 30165205Scg if(fm801->rec_flip & 1) { 30265205Scg fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4); 30365205Scg } else 30465205Scg fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4); 30565205Scg chn_intr(fm801->rch.channel); 30665205Scg } 30765340Scg 30865205Scg if ( intsrc & FM_INTSTATUS_MPU ) { 30965205Scg /* This is a TODOish thing... */ 31065205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2); 31165205Scg } 31265340Scg 31365205Scg if ( intsrc & FM_INTSTATUS_VOL ) { 31465205Scg /* This is a TODOish thing... */ 31565205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2); 31665205Scg } 31765340Scg 31865205Scg DPRINT("fm801_intr clear\n\n"); 31965205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2); 32065205Scg} 32165205Scg 32270134Scg/* -------------------------------------------------------------------- */ 32365205Scg/* channel interface */ 32465205Scgstatic void * 32570134Scgfm801ch_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) 32665205Scg{ 32765205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 32865205Scg struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch; 32965340Scg 33065205Scg DPRINT("fm801ch_init, direction = %d\n", dir); 33165205Scg ch->parent = fm801; 33265205Scg ch->channel = c; 33365205Scg ch->buffer = b; 33465205Scg ch->buffer->bufsize = FM801_BUFFSIZE; 33565205Scg ch->dir = dir; 33665205Scg if( chn_allocbuf(ch->buffer, fm801->parent_dmat) == -1) return NULL; 33765205Scg return (void *)ch; 33865205Scg} 33965205Scg 34065205Scgstatic int 34170134Scgfm801ch_setformat(kobj_t obj, void *data, u_int32_t format) 34265205Scg{ 34365205Scg struct fm801_chinfo *ch = data; 34465205Scg struct fm801_info *fm801 = ch->parent; 34565340Scg 34665205Scg DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format, 34765205Scg (format & AFMT_STEREO)?"stereo":"mono", 34865205Scg (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit", 34965205Scg (format & AFMT_SIGNED)? "signed":"unsigned", 35065205Scg (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" ); 35165340Scg 35265205Scg if(ch->dir == PCMDIR_PLAY) { 35365205Scg fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0; 35465205Scg fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; 35565205Scg return 0; 35665205Scg } 35765340Scg 35865205Scg if(ch->dir == PCMDIR_REC ) { 35965205Scg fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0; 36065205Scg fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; 36165205Scg return 0; 36265205Scg } 36365340Scg 36465205Scg return 0; 36565205Scg} 36665205Scg 36765205Scgstruct { 36865205Scg int limit; 36965205Scg int rate; 37065205Scg} fm801_rates[11] = { 37165340Scg { 6600, 5500 }, 37265340Scg { 8750, 8000 }, 37365340Scg { 10250, 9600 }, 37465340Scg { 13200, 11025 }, 37565340Scg { 17500, 16000 }, 37665340Scg { 20500, 19200 }, 37765340Scg { 26500, 22050 }, 37865340Scg { 35000, 32000 }, 37965340Scg { 41000, 38400 }, 38065340Scg { 46000, 44100 }, 38165340Scg { 48000, 48000 }, 38265205Scg/* anything above -> 48000 */ 38365205Scg}; 38465205Scg 38565205Scgstatic int 38670134Scgfm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed) 38765205Scg{ 38865205Scg struct fm801_chinfo *ch = data; 38965205Scg struct fm801_info *fm801 = ch->parent; 39065205Scg register int i; 39165340Scg 39265340Scg 39365205Scg for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ; 39465340Scg 39565205Scg if(ch->dir == PCMDIR_PLAY) { 39665205Scg fm801->pch.spd = fm801_rates[i].rate; 39765205Scg fm801->play_shift = (i<<8); 39865205Scg fm801->play_shift &= FM_PLAY_RATE_MASK; 39965205Scg } 40065340Scg 40165205Scg if(ch->dir == PCMDIR_REC ) { 40265205Scg fm801->rch.spd = fm801_rates[i].rate; 40365205Scg fm801->rec_shift = (i<<8); 40465205Scg fm801->rec_shift &= FM_REC_RATE_MASK; 40565205Scg } 40665340Scg 40765205Scg ch->spd = fm801_rates[i].rate; 40865340Scg 40965205Scg return fm801_rates[i].rate; 41065205Scg} 41165205Scg 41265205Scgstatic int 41370134Scgfm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 41465205Scg{ 41565205Scg struct fm801_chinfo *ch = data; 41665205Scg struct fm801_info *fm801 = ch->parent; 41765340Scg 41865205Scg if(ch->dir == PCMDIR_PLAY) { 41965205Scg if(fm801->play_flip) return fm801->play_blksize; 42065205Scg fm801->play_blksize = blocksize; 42165205Scg } 42265340Scg 42365205Scg if(ch->dir == PCMDIR_REC) { 42465205Scg if(fm801->rec_flip) return fm801->rec_blksize; 42565205Scg fm801->rec_blksize = blocksize; 42665205Scg } 42765340Scg 42865205Scg DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir); 42965205Scg 43065205Scg return blocksize; 43165205Scg} 43265205Scg 43365205Scgstatic int 43470134Scgfm801ch_trigger(kobj_t obj, void *data, int go) 43565205Scg{ 43665205Scg struct fm801_chinfo *ch = data; 43765205Scg struct fm801_info *fm801 = ch->parent; 43865205Scg u_int32_t baseaddr = vtophys(ch->buffer->buf); 43965205Scg snd_dbuf *b = ch->buffer; 44065205Scg u_int32_t k1; 44165340Scg 44265205Scg DPRINT("fm801ch_trigger go %d , ", go); 44365205Scg DPRINT("rp %d, rl %d, fp %d fl %d, dl %d, blksize=%d\n", 44465205Scg b->rp,b->rl, b->fp,b->fl, b->dl, b->blksz); 44565340Scg 44665205Scg if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) { 44765205Scg return 0; 44865205Scg } 44965340Scg 45065205Scg if (ch->dir == PCMDIR_PLAY) { 45165205Scg if (go == PCMTRIG_START) { 45265340Scg 45365205Scg fm801->play_start = baseaddr; 45465205Scg fm801->play_nextblk = fm801->play_start + fm801->play_blksize; 45565205Scg fm801->play_flip = 0; 45665205Scg fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2); 45765205Scg fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4); 45865205Scg fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4); 45965205Scg fm801_wr(fm801, FM_PLAY_CTL, 46065340Scg FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift, 46165205Scg 2 ); 46265205Scg } else { 46365205Scg fm801->play_flip = 0; 46465205Scg k1 = fm801_rd(fm801, FM_PLAY_CTL,2); 46565205Scg fm801_wr(fm801, FM_PLAY_CTL, 46665205Scg (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) | 46765205Scg FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 ); 46865205Scg } 46965205Scg } else if(ch->dir == PCMDIR_REC) { 47065205Scg if (go == PCMTRIG_START) { 47165205Scg fm801->rec_start = baseaddr; 47265205Scg fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize; 47365205Scg fm801->rec_flip = 0; 47465205Scg fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2); 47565205Scg fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4); 47665205Scg fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4); 47765340Scg fm801_wr(fm801, FM_REC_CTL, 47865340Scg FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift, 47965205Scg 2 ); 48065205Scg } else { 48165205Scg fm801->rec_flip = 0; 48265205Scg k1 = fm801_rd(fm801, FM_REC_CTL,2); 48365205Scg fm801_wr(fm801, FM_REC_CTL, 48465205Scg (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) | 48565205Scg FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2); 48665205Scg } 48765205Scg } 48865340Scg 48965205Scg return 0; 49065205Scg} 49165205Scg 49265205Scg/* Almost ALSA copy */ 49365205Scgstatic int 49470134Scgfm801ch_getptr(kobj_t obj, void *data) 49565205Scg{ 49665205Scg struct fm801_chinfo *ch = data; 49765205Scg struct fm801_info *fm801 = ch->parent; 49865205Scg int result = 0; 49965205Scg snd_dbuf *b = ch->buffer; 50065340Scg 50165205Scg if (ch->dir == PCMDIR_PLAY) { 50265340Scg result = fm801_rd(fm801, 50365340Scg (fm801->play_flip&1) ? 50465205Scg FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start; 50565205Scg } 50665340Scg 50765205Scg if (ch->dir == PCMDIR_REC) { 50865205Scg result = fm801_rd(fm801, 50965205Scg (fm801->rec_flip&1) ? 51065205Scg FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start; 51165340Scg } 51265340Scg 51365205Scg DPRINT("fm801ch_getptr:%d, rp %d, rl %d, fp %d fl %d\n", 51465340Scg result, b->rp,b->rl, b->fp,b->fl); 51565205Scg 51665205Scg return result; 51765205Scg} 51865205Scg 51965205Scgstatic pcmchan_caps * 52070134Scgfm801ch_getcaps(kobj_t obj, void *data) 52165205Scg{ 52265205Scg return &fm801ch_caps; 52365205Scg} 52465205Scg 52570134Scgstatic kobj_method_t fm801ch_methods[] = { 52670134Scg KOBJMETHOD(channel_init, fm801ch_init), 52770134Scg KOBJMETHOD(channel_setformat, fm801ch_setformat), 52870134Scg KOBJMETHOD(channel_setspeed, fm801ch_setspeed), 52970134Scg KOBJMETHOD(channel_setblocksize, fm801ch_setblocksize), 53070134Scg KOBJMETHOD(channel_trigger, fm801ch_trigger), 53170134Scg KOBJMETHOD(channel_getptr, fm801ch_getptr), 53270134Scg KOBJMETHOD(channel_getcaps, fm801ch_getcaps), 53370134Scg { 0, 0 } 53470134Scg}; 53570134ScgCHANNEL_DECLARE(fm801ch); 53670134Scg 53770134Scg/* -------------------------------------------------------------------- */ 53870134Scg 53970134Scg/* 54070134Scg * Init routine is taken from an original NetBSD driver 54170134Scg */ 54270134Scgstatic int 54370134Scgfm801_init(struct fm801_info *fm801) 54470134Scg{ 54570134Scg u_int32_t k1; 54670134Scg 54770134Scg /* reset codec */ 54870134Scg fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2); 54970134Scg DELAY(100000); 55070134Scg fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2); 55170134Scg DELAY(100000); 55270134Scg 55370134Scg fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2); 55470134Scg fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2); 55570134Scg fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2); 55670134Scg fm801_wr(fm801, 0x40,0x107f,2); /* enable legacy audio */ 55770134Scg 55870134Scg fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2); 55970134Scg 56070134Scg /* Unmask playback, record and mpu interrupts, mask the rest */ 56170134Scg k1 = fm801_rd((void *)fm801, FM_INTMASK,2); 56270134Scg fm801_wr(fm801, FM_INTMASK, 56370134Scg (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) | 56470134Scg FM_INTMASK_VOL,2); 56570134Scg fm801_wr(fm801, FM_INTSTATUS, 56670134Scg FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU | 56770134Scg FM_INTSTATUS_VOL,2); 56870134Scg 56970134Scg DPRINT("FM801 init Ok\n"); 57070134Scg return 0; 57170134Scg} 57270134Scg 57370134Scgstatic int 57470134Scgfm801_pci_attach(device_t dev) 57570134Scg{ 57670134Scg u_int32_t data; 57770134Scg struct ac97_info *codec = 0; 57870134Scg struct fm801_info *fm801; 57970134Scg int i; 58070134Scg int mapped = 0; 58170134Scg char status[SND_STATUSLEN]; 58270134Scg 58370134Scg if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) { 58470134Scg device_printf(dev, "cannot allocate softc\n"); 58570134Scg return ENXIO; 58670134Scg } 58770134Scg 58870134Scg bzero(fm801, sizeof(*fm801)); 58970134Scg fm801->type = pci_get_devid(dev); 59070134Scg 59170134Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 59270134Scg data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); 59370134Scg pci_write_config(dev, PCIR_COMMAND, data, 2); 59470134Scg data = pci_read_config(dev, PCIR_COMMAND, 2); 59570134Scg 59670134Scg for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { 59770134Scg fm801->regid = PCIR_MAPS + i*4; 59870134Scg fm801->regtype = SYS_RES_MEMORY; 59970134Scg fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, 60070134Scg 0, ~0, 1, RF_ACTIVE); 60170134Scg if(!fm801->reg) 60270134Scg { 60370134Scg fm801->regtype = SYS_RES_IOPORT; 60470134Scg fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, 60570134Scg 0, ~0, 1, RF_ACTIVE); 60670134Scg } 60770134Scg 60870134Scg if(fm801->reg) { 60970134Scg fm801->st = rman_get_bustag(fm801->reg); 61070134Scg fm801->sh = rman_get_bushandle(fm801->reg); 61170134Scg mapped++; 61270134Scg } 61370134Scg } 61470134Scg 61570134Scg if (mapped == 0) { 61670134Scg device_printf(dev, "unable to map register space\n"); 61770134Scg goto oops; 61870134Scg } 61970134Scg 62070134Scg fm801_init(fm801); 62170134Scg 62270134Scg codec = AC97_CREATE(dev, fm801, fm801_ac97); 62370134Scg if (codec == NULL) goto oops; 62470134Scg 62570134Scg if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops; 62670134Scg 62770134Scg fm801->irqid = 0; 62870134Scg fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid, 62970134Scg 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 63070134Scg if (!fm801->irq || 63170134Scg bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY, 63270134Scg fm801_intr, fm801, &fm801->ih)) { 63370134Scg device_printf(dev, "unable to map interrupt\n"); 63470134Scg goto oops; 63570134Scg } 63670134Scg 63770134Scg if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, 63870134Scg /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 63970134Scg /*highaddr*/BUS_SPACE_MAXADDR, 64070134Scg /*filter*/NULL, /*filterarg*/NULL, 64170134Scg /*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, 64270134Scg /*flags*/0, &fm801->parent_dmat) != 0) { 64370134Scg device_printf(dev, "unable to create dma tag\n"); 64470134Scg goto oops; 64570134Scg } 64670134Scg 64770134Scg snprintf(status, 64, "at %s 0x%lx irq %ld", 64870134Scg (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", 64970134Scg rman_get_start(fm801->reg), rman_get_start(fm801->irq)); 65070134Scg 65170134Scg#define FM801_MAXPLAYCH 1 65270134Scg if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; 65370134Scg pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801); 65470134Scg pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801); 65570134Scg pcm_setstatus(dev, status); 65670134Scg 65770134Scg return 0; 65870134Scg 65970134Scgoops: 66070134Scg if (codec) ac97_destroy(codec); 66170134Scg if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); 66270134Scg if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih); 66370134Scg if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); 66470134Scg if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat); 66570134Scg free(fm801, M_DEVBUF); 66670134Scg return ENXIO; 66770134Scg} 66870134Scg 66970134Scgstatic int 67070134Scgfm801_pci_detach(device_t dev) 67170134Scg{ 67270134Scg int r; 67370134Scg struct fm801_info *fm801; 67470134Scg 67570134Scg DPRINT("Forte Media FM801 detach\n"); 67670134Scg 67770134Scg r = pcm_unregister(dev); 67870134Scg if (r) 67970134Scg return r; 68070134Scg 68170134Scg fm801 = pcm_getdevinfo(dev); 68270134Scg bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); 68370134Scg bus_teardown_intr(dev, fm801->irq, fm801->ih); 68470134Scg bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); 68570134Scg bus_dma_tag_destroy(fm801->parent_dmat); 68670134Scg free(fm801, M_DEVBUF); 68770134Scg return 0; 68870134Scg} 68970134Scg 69070134Scgstatic int 69170134Scgfm801_pci_probe( device_t dev ) 69270134Scg{ 69370134Scg int id; 69470134Scg if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { 69570134Scg device_set_desc(dev, "Forte Media FM801 Audio Controller"); 69670134Scg return 0; 69770134Scg } 69870134Scg/* 69970134Scg if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) { 70070134Scg device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)"); 70170134Scg return ENXIO; 70270134Scg } 70370134Scg*/ 70470134Scg return ENXIO; 70570134Scg} 70670134Scg 70765205Scgstatic device_method_t fm801_methods[] = { 70865205Scg /* Device interface */ 70965205Scg DEVMETHOD(device_probe, fm801_pci_probe), 71065205Scg DEVMETHOD(device_attach, fm801_pci_attach), 71165205Scg DEVMETHOD(device_detach, fm801_pci_detach), 71265205Scg { 0, 0} 71365205Scg}; 71465205Scg 71565205Scgstatic driver_t fm801_driver = { 71665205Scg "pcm", 71765205Scg fm801_methods, 71865205Scg sizeof(snddev_info), 71965205Scg}; 72065205Scg 72165205Scgstatic devclass_t pcm_devclass; 72265205Scg 72365644ScgDRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0); 724