1139749Simp/*- 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 27193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 28193640Sariff#include "opt_snd.h" 29193640Sariff#endif 30193640Sariff 3165205Scg#include <dev/sound/pcm/sound.h> 3265205Scg#include <dev/sound/pcm/ac97.h> 33119287Simp#include <dev/pci/pcireg.h> 34119287Simp#include <dev/pci/pcivar.h> 3565205Scg 3682180ScgSND_DECLARE_FILE("$FreeBSD: stable/11/sys/dev/sound/pci/fm801.c 331722 2018-03-29 02:50:57Z eadler $"); 3782180Scg 3865205Scg#define PCI_VENDOR_FORTEMEDIA 0x1319 39169762Sjoel#define PCI_DEVICE_FORTEMEDIA1 0x08011319 /* Audio controller */ 40169762Sjoel#define PCI_DEVICE_FORTEMEDIA2 0x08021319 /* Joystick controller */ 4165205Scg 4265205Scg#define FM_PCM_VOLUME 0x00 4365205Scg#define FM_FM_VOLUME 0x02 4465205Scg#define FM_I2S_VOLUME 0x04 4565205Scg#define FM_RECORD_SOURCE 0x06 4665205Scg 4765205Scg#define FM_PLAY_CTL 0x08 4865205Scg#define FM_PLAY_RATE_MASK 0x0f00 4965205Scg#define FM_PLAY_BUF1_LAST 0x0001 5065205Scg#define FM_PLAY_BUF2_LAST 0x0002 5165205Scg#define FM_PLAY_START 0x0020 5265205Scg#define FM_PLAY_PAUSE 0x0040 5365205Scg#define FM_PLAY_STOPNOW 0x0080 5465205Scg#define FM_PLAY_16BIT 0x4000 5565205Scg#define FM_PLAY_STEREO 0x8000 5665205Scg 5765205Scg#define FM_PLAY_DMALEN 0x0a 5865205Scg#define FM_PLAY_DMABUF1 0x0c 5965205Scg#define FM_PLAY_DMABUF2 0x10 6065205Scg 6165205Scg 6265205Scg#define FM_REC_CTL 0x14 6365205Scg#define FM_REC_RATE_MASK 0x0f00 6465205Scg#define FM_REC_BUF1_LAST 0x0001 6565205Scg#define FM_REC_BUF2_LAST 0x0002 6665205Scg#define FM_REC_START 0x0020 6765205Scg#define FM_REC_PAUSE 0x0040 6865205Scg#define FM_REC_STOPNOW 0x0080 6965205Scg#define FM_REC_16BIT 0x4000 7065205Scg#define FM_REC_STEREO 0x8000 7165205Scg 7265205Scg 7365205Scg#define FM_REC_DMALEN 0x16 7465205Scg#define FM_REC_DMABUF1 0x18 7565205Scg#define FM_REC_DMABUF2 0x1c 7665205Scg 7765205Scg#define FM_CODEC_CTL 0x22 7865205Scg#define FM_VOLUME 0x26 7965205Scg#define FM_VOLUME_MUTE 0x8000 8065205Scg 8165205Scg#define FM_CODEC_CMD 0x2a 8265205Scg#define FM_CODEC_CMD_READ 0x0080 8365205Scg#define FM_CODEC_CMD_VALID 0x0100 8465205Scg#define FM_CODEC_CMD_BUSY 0x0200 8565205Scg 8665205Scg#define FM_CODEC_DATA 0x2c 8765205Scg 8865205Scg#define FM_IO_CTL 0x52 8965205Scg#define FM_CARD_CTL 0x54 9065205Scg 9165205Scg#define FM_INTMASK 0x56 9265205Scg#define FM_INTMASK_PLAY 0x0001 9365205Scg#define FM_INTMASK_REC 0x0002 9465205Scg#define FM_INTMASK_VOL 0x0040 9565205Scg#define FM_INTMASK_MPU 0x0080 9665205Scg 9765205Scg#define FM_INTSTATUS 0x5a 9865205Scg#define FM_INTSTATUS_PLAY 0x0100 9965205Scg#define FM_INTSTATUS_REC 0x0200 10065205Scg#define FM_INTSTATUS_VOL 0x4000 10165205Scg#define FM_INTSTATUS_MPU 0x8000 10265205Scg 10384658Scg#define FM801_DEFAULT_BUFSZ 4096 /* Other values do not work!!! */ 10465205Scg 10565205Scg/* debug purposes */ 10665205Scg#define DPRINT if(0) printf 10765205Scg 10865205Scg/* 10974763Scgstatic int fm801ch_setup(struct pcm_channel *c); 11065205Scg*/ 11165205Scg 11265205Scgstatic u_int32_t fmts[] = { 113193640Sariff SND_FORMAT(AFMT_U8, 1, 0), 114193640Sariff SND_FORMAT(AFMT_U8, 2, 0), 115193640Sariff SND_FORMAT(AFMT_S16_LE, 1, 0), 116193640Sariff SND_FORMAT(AFMT_S16_LE, 2, 0), 11765205Scg 0 11865205Scg}; 11965205Scg 12074763Scgstatic struct pcmchan_caps fm801ch_caps = { 121154127Sariff 5500, 48000, 12265205Scg fmts, 0 12365205Scg}; 12465205Scg 12565205Scgstruct fm801_info; 12665205Scg 12765205Scgstruct fm801_chinfo { 128102620Ssobomax struct fm801_info *parent; 129102620Ssobomax struct pcm_channel *channel; 130102620Ssobomax struct snd_dbuf *buffer; 131102620Ssobomax u_int32_t spd, dir, fmt; /* speed, direction, format */ 13265205Scg u_int32_t shift; 13365205Scg}; 13465205Scg 13565205Scgstruct fm801_info { 136102620Ssobomax int type; 137102620Ssobomax bus_space_tag_t st; 138102620Ssobomax bus_space_handle_t sh; 139102620Ssobomax bus_dma_tag_t parent_dmat; 14065205Scg 141102620Ssobomax device_t dev; 142102620Ssobomax int num; 143102620Ssobomax u_int32_t unit; 14465205Scg 145102620Ssobomax struct resource *reg, *irq; 146102620Ssobomax int regtype, regid, irqid; 147102620Ssobomax void *ih; 14865205Scg 14965205Scg u_int32_t play_flip, 15065205Scg play_nextblk, 15165205Scg play_start, 15265205Scg play_blksize, 15365205Scg play_fmt, 15465205Scg play_shift, 15565205Scg play_size; 15665205Scg 15765205Scg u_int32_t rec_flip, 15865205Scg rec_nextblk, 15965205Scg rec_start, 16065205Scg rec_blksize, 16165205Scg rec_fmt, 16265205Scg rec_shift, 16365205Scg rec_size; 16465205Scg 165102620Ssobomax unsigned int bufsz; 16684658Scg 167102620Ssobomax struct fm801_chinfo pch, rch; 168102889Ssobomax 169102889Ssobomax device_t radio; 17065205Scg}; 17165205Scg 17265205Scg/* Bus Read / Write routines */ 17365205Scgstatic u_int32_t 17465205Scgfm801_rd(struct fm801_info *fm801, int regno, int size) 17565205Scg{ 17665205Scg switch(size) { 17765205Scg case 1: 17865205Scg return (bus_space_read_1(fm801->st, fm801->sh, regno)); 17965205Scg case 2: 18065205Scg return (bus_space_read_2(fm801->st, fm801->sh, regno)); 18165205Scg case 4: 18265205Scg return (bus_space_read_4(fm801->st, fm801->sh, regno)); 18365205Scg default: 18465205Scg return 0xffffffff; 18565205Scg } 18665205Scg} 18765205Scg 18865205Scgstatic void 18965205Scgfm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size) 19065205Scg{ 191108064Ssemenu 19265205Scg switch(size) { 19365205Scg case 1: 194108064Ssemenu bus_space_write_1(fm801->st, fm801->sh, regno, data); 195108064Ssemenu break; 19665205Scg case 2: 197108064Ssemenu bus_space_write_2(fm801->st, fm801->sh, regno, data); 198108064Ssemenu break; 19965205Scg case 4: 200108064Ssemenu bus_space_write_4(fm801->st, fm801->sh, regno, data); 201108064Ssemenu break; 20265205Scg } 20365205Scg} 20465205Scg 20570134Scg/* -------------------------------------------------------------------- */ 20665205Scg/* 20765205Scg * ac97 codec routines 20865205Scg */ 20965205Scg#define TIMO 50 21070134Scgstatic int 21170134Scgfm801_rdcd(kobj_t obj, void *devinfo, int regno) 21265205Scg{ 21365205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 21465205Scg int i; 21565340Scg 21665205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 21765205Scg DELAY(10000); 21865205Scg DPRINT("fm801 rdcd: 1 - DELAY\n"); 21965205Scg } 22065205Scg if (i >= TIMO) { 22165205Scg printf("fm801 rdcd: codec busy\n"); 22265205Scg return 0; 22365205Scg } 22465340Scg 22565205Scg fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2); 22665205Scg 22765205Scg for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++) 22865205Scg { 22965205Scg DELAY(10000); 23065205Scg DPRINT("fm801 rdcd: 2 - DELAY\n"); 23165205Scg } 23265205Scg if (i >= TIMO) { 23365205Scg printf("fm801 rdcd: write codec invalid\n"); 234102889Ssobomax return 0; 23565205Scg } 23665340Scg 23765205Scg return fm801_rd(fm801,FM_CODEC_DATA,2); 23865205Scg} 23965205Scg 24070134Scgstatic int 24170134Scgfm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) 24265205Scg{ 24365205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 24465205Scg int i; 24565340Scg 24665205Scg DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data); 24765340Scg/* 24865205Scg if(regno == AC97_REG_RECSEL) return; 24965340Scg*/ 25065205Scg /* Poll until codec is ready */ 25165205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 25265205Scg DELAY(10000); 25365205Scg DPRINT("fm801 rdcd: 1 - DELAY\n"); 25465205Scg } 25565205Scg if (i >= TIMO) { 25665205Scg printf("fm801 wrcd: read codec busy\n"); 25770134Scg return -1; 25865205Scg } 25965340Scg 26065205Scg fm801_wr(fm801,FM_CODEC_DATA,data, 2); 26165205Scg fm801_wr(fm801,FM_CODEC_CMD, regno,2); 26265340Scg 26365205Scg /* wait until codec is ready */ 26465205Scg for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { 26565205Scg DELAY(10000); 26665205Scg DPRINT("fm801 wrcd: 2 - DELAY\n"); 26765205Scg } 26865205Scg if (i >= TIMO) { 26965205Scg printf("fm801 wrcd: read codec busy\n"); 27070134Scg return -1; 27165205Scg } 27265205Scg DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data); 27370134Scg return 0; 27465205Scg} 27565205Scg 27670134Scgstatic kobj_method_t fm801_ac97_methods[] = { 27770134Scg KOBJMETHOD(ac97_read, fm801_rdcd), 27870134Scg KOBJMETHOD(ac97_write, fm801_wrcd), 279227843Smarius DEVMETHOD_END 28070134Scg}; 28170134ScgAC97_DECLARE(fm801_ac97); 28270134Scg 28370134Scg/* -------------------------------------------------------------------- */ 28470134Scg 28565340Scg/* 28665340Scg * The interrupt handler 28765205Scg */ 28865205Scgstatic void 28965205Scgfm801_intr(void *p) 29065205Scg{ 29165205Scg struct fm801_info *fm801 = (struct fm801_info *)p; 29265205Scg u_int32_t intsrc = fm801_rd(fm801, FM_INTSTATUS, 2); 29365340Scg 29465205Scg DPRINT("\nfm801_intr intsrc 0x%x ", intsrc); 29565340Scg 29665205Scg if(intsrc & FM_INTSTATUS_PLAY) { 29765205Scg fm801->play_flip++; 29865205Scg if(fm801->play_flip & 1) { 29965205Scg fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4); 30065205Scg } else 30165205Scg fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4); 30265205Scg chn_intr(fm801->pch.channel); 30365205Scg } 30465340Scg 30565205Scg if(intsrc & FM_INTSTATUS_REC) { 30665205Scg fm801->rec_flip++; 30765205Scg if(fm801->rec_flip & 1) { 30865205Scg fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4); 30965205Scg } else 31065205Scg fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4); 31165205Scg chn_intr(fm801->rch.channel); 31265205Scg } 31365340Scg 31465205Scg if ( intsrc & FM_INTSTATUS_MPU ) { 31565205Scg /* This is a TODOish thing... */ 31665205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2); 31765205Scg } 31865340Scg 31965205Scg if ( intsrc & FM_INTSTATUS_VOL ) { 32065205Scg /* This is a TODOish thing... */ 32165205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2); 32265205Scg } 32365340Scg 32465205Scg DPRINT("fm801_intr clear\n\n"); 32565205Scg fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2); 32665205Scg} 32765205Scg 32870134Scg/* -------------------------------------------------------------------- */ 32965205Scg/* channel interface */ 33065205Scgstatic void * 33174763Scgfm801ch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) 33265205Scg{ 33365205Scg struct fm801_info *fm801 = (struct fm801_info *)devinfo; 33465205Scg struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch; 33565340Scg 33665205Scg DPRINT("fm801ch_init, direction = %d\n", dir); 33765205Scg ch->parent = fm801; 33865205Scg ch->channel = c; 33965205Scg ch->buffer = b; 34065205Scg ch->dir = dir; 341168847Sariff if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, 0, fm801->bufsz) != 0) 342136469Syongari return NULL; 34365205Scg return (void *)ch; 34465205Scg} 34565205Scg 34665205Scgstatic int 34770134Scgfm801ch_setformat(kobj_t obj, void *data, u_int32_t format) 34865205Scg{ 34965205Scg struct fm801_chinfo *ch = data; 35065205Scg struct fm801_info *fm801 = ch->parent; 35165340Scg 35265205Scg DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format, 353193640Sariff (AFMT_CHANNEL(format) > 1)?"stereo":"mono", 354193640Sariff (format & AFMT_16BIT) ? "16bit":"8bit", 35565205Scg (format & AFMT_SIGNED)? "signed":"unsigned", 35665205Scg (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" ); 35765340Scg 35865205Scg if(ch->dir == PCMDIR_PLAY) { 359193640Sariff fm801->play_fmt = 360193640Sariff (AFMT_CHANNEL(format) > 1)? FM_PLAY_STEREO : 0; 36165205Scg fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; 36265205Scg return 0; 36365205Scg } 36465340Scg 36565205Scg if(ch->dir == PCMDIR_REC ) { 366193640Sariff fm801->rec_fmt = (AFMT_CHANNEL(format) > 1)? FM_REC_STEREO:0; 36765205Scg fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; 36865205Scg return 0; 36965205Scg } 37065340Scg 37165205Scg return 0; 37265205Scg} 37365205Scg 37465205Scgstruct { 375193640Sariff u_int32_t limit; 376193640Sariff u_int32_t rate; 37765205Scg} fm801_rates[11] = { 37865340Scg { 6600, 5500 }, 37965340Scg { 8750, 8000 }, 38065340Scg { 10250, 9600 }, 38165340Scg { 13200, 11025 }, 38265340Scg { 17500, 16000 }, 38365340Scg { 20500, 19200 }, 38465340Scg { 26500, 22050 }, 38565340Scg { 35000, 32000 }, 38665340Scg { 41000, 38400 }, 38765340Scg { 46000, 44100 }, 38865340Scg { 48000, 48000 }, 38965205Scg/* anything above -> 48000 */ 39065205Scg}; 39165205Scg 392193640Sariffstatic u_int32_t 39370134Scgfm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed) 39465205Scg{ 39565205Scg struct fm801_chinfo *ch = data; 39665205Scg struct fm801_info *fm801 = ch->parent; 397331643Sdim int i; 39865340Scg 39965340Scg 40065205Scg for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ; 40165340Scg 40265205Scg if(ch->dir == PCMDIR_PLAY) { 40365205Scg fm801->pch.spd = fm801_rates[i].rate; 40465205Scg fm801->play_shift = (i<<8); 40565205Scg fm801->play_shift &= FM_PLAY_RATE_MASK; 40665205Scg } 40765340Scg 40865205Scg if(ch->dir == PCMDIR_REC ) { 40965205Scg fm801->rch.spd = fm801_rates[i].rate; 41065205Scg fm801->rec_shift = (i<<8); 41165205Scg fm801->rec_shift &= FM_REC_RATE_MASK; 41265205Scg } 41365340Scg 41465205Scg ch->spd = fm801_rates[i].rate; 41565340Scg 41665205Scg return fm801_rates[i].rate; 41765205Scg} 41865205Scg 419193640Sariffstatic u_int32_t 42070134Scgfm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) 42165205Scg{ 42265205Scg struct fm801_chinfo *ch = data; 42365205Scg struct fm801_info *fm801 = ch->parent; 42465340Scg 425169762Sjoel /* 426169762Sjoel * Don't mind for play_flip, set the blocksize to the 427169762Sjoel * desired values in any case - otherwise sound playback 428169762Sjoel * breaks here. 429169762Sjoel */ 430169762Sjoel if(ch->dir == PCMDIR_PLAY) 43165205Scg fm801->play_blksize = blocksize; 43265340Scg 433169762Sjoel if(ch->dir == PCMDIR_REC) 43465205Scg fm801->rec_blksize = blocksize; 43565340Scg 43665205Scg DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir); 43765205Scg 43865205Scg return blocksize; 43965205Scg} 44065205Scg 44165205Scgstatic int 44270134Scgfm801ch_trigger(kobj_t obj, void *data, int go) 44365205Scg{ 44465205Scg struct fm801_chinfo *ch = data; 44565205Scg struct fm801_info *fm801 = ch->parent; 446111183Scognet u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer); 44765205Scg u_int32_t k1; 44865340Scg 44965205Scg DPRINT("fm801ch_trigger go %d , ", go); 45065340Scg 451170521Sariff if (!PCMTRIG_COMMON(go)) 45265205Scg return 0; 45365340Scg 45465205Scg if (ch->dir == PCMDIR_PLAY) { 45565205Scg if (go == PCMTRIG_START) { 45665340Scg 45765205Scg fm801->play_start = baseaddr; 45865205Scg fm801->play_nextblk = fm801->play_start + fm801->play_blksize; 45965205Scg fm801->play_flip = 0; 46065205Scg fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2); 46165205Scg fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4); 46265205Scg fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4); 46365205Scg fm801_wr(fm801, FM_PLAY_CTL, 46465340Scg FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift, 46565205Scg 2 ); 46665205Scg } else { 46765205Scg fm801->play_flip = 0; 46865205Scg k1 = fm801_rd(fm801, FM_PLAY_CTL,2); 46965205Scg fm801_wr(fm801, FM_PLAY_CTL, 47065205Scg (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) | 47165205Scg FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 ); 47265205Scg } 47365205Scg } else if(ch->dir == PCMDIR_REC) { 47465205Scg if (go == PCMTRIG_START) { 47565205Scg fm801->rec_start = baseaddr; 47665205Scg fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize; 47765205Scg fm801->rec_flip = 0; 47865205Scg fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2); 47965205Scg fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4); 48065205Scg fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4); 48165340Scg fm801_wr(fm801, FM_REC_CTL, 48265340Scg FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift, 48365205Scg 2 ); 48465205Scg } else { 48565205Scg fm801->rec_flip = 0; 48665205Scg k1 = fm801_rd(fm801, FM_REC_CTL,2); 48765205Scg fm801_wr(fm801, FM_REC_CTL, 48865205Scg (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) | 48965205Scg FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2); 49065205Scg } 49165205Scg } 49265340Scg 49365205Scg return 0; 49465205Scg} 49565205Scg 49665205Scg/* Almost ALSA copy */ 497193640Sariffstatic u_int32_t 49870134Scgfm801ch_getptr(kobj_t obj, void *data) 49965205Scg{ 50065205Scg struct fm801_chinfo *ch = data; 50165205Scg struct fm801_info *fm801 = ch->parent; 502193640Sariff u_int32_t result = 0; 50365340Scg 50465205Scg if (ch->dir == PCMDIR_PLAY) { 50565340Scg result = fm801_rd(fm801, 50665340Scg (fm801->play_flip&1) ? 50765205Scg FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start; 50865205Scg } 50965340Scg 51065205Scg if (ch->dir == PCMDIR_REC) { 51165205Scg result = fm801_rd(fm801, 51265205Scg (fm801->rec_flip&1) ? 51365205Scg FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start; 51465340Scg } 51565340Scg 51665205Scg return result; 51765205Scg} 51865205Scg 51974763Scgstatic struct 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), 533227843Smarius DEVMETHOD_END 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{ 576297862Spfg struct ac97_info *codec = NULL; 57770134Scg struct fm801_info *fm801; 57870134Scg int i; 57970134Scg int mapped = 0; 58070134Scg char status[SND_STATUSLEN]; 58170134Scg 582170873Sariff fm801 = malloc(sizeof(*fm801), M_DEVBUF, M_WAITOK | M_ZERO); 58370134Scg fm801->type = pci_get_devid(dev); 58470134Scg 585254263Sscottl pci_enable_busmaster(dev); 58670134Scg 58770134Scg for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { 588119690Sjhb fm801->regid = PCIR_BAR(i); 58970134Scg fm801->regtype = SYS_RES_MEMORY; 590127135Snjl fm801->reg = bus_alloc_resource_any(dev, fm801->regtype, 591127135Snjl &fm801->regid, RF_ACTIVE); 59270134Scg if(!fm801->reg) 59370134Scg { 59470134Scg fm801->regtype = SYS_RES_IOPORT; 595127135Snjl fm801->reg = bus_alloc_resource_any(dev, 596127135Snjl fm801->regtype, 597127135Snjl &fm801->regid, 598127135Snjl RF_ACTIVE); 59970134Scg } 60070134Scg 60170134Scg if(fm801->reg) { 60270134Scg fm801->st = rman_get_bustag(fm801->reg); 60370134Scg fm801->sh = rman_get_bushandle(fm801->reg); 60470134Scg mapped++; 60570134Scg } 60670134Scg } 60770134Scg 60870134Scg if (mapped == 0) { 60970134Scg device_printf(dev, "unable to map register space\n"); 61070134Scg goto oops; 61170134Scg } 61270134Scg 61384658Scg fm801->bufsz = pcm_getbuffersize(dev, 4096, FM801_DEFAULT_BUFSZ, 65536); 61484658Scg 61570134Scg fm801_init(fm801); 61670134Scg 61770134Scg codec = AC97_CREATE(dev, fm801, fm801_ac97); 61870134Scg if (codec == NULL) goto oops; 61970134Scg 62070134Scg if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops; 62170134Scg 62270134Scg fm801->irqid = 0; 623127135Snjl fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid, 624127135Snjl RF_ACTIVE | RF_SHAREABLE); 625297862Spfg if (!fm801->irq || 626297862Spfg snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih)) { 62770134Scg device_printf(dev, "unable to map interrupt\n"); 62870134Scg goto oops; 62970134Scg } 63070134Scg 631166904Snetchild if (bus_dma_tag_create(/*parent*/bus_get_dma_tag(dev), /*alignment*/2, 632166904Snetchild /*boundary*/0, 63370134Scg /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, 63470134Scg /*highaddr*/BUS_SPACE_MAXADDR, 63570134Scg /*filter*/NULL, /*filterarg*/NULL, 63684658Scg /*maxsize*/fm801->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, 637117126Sscottl /*flags*/0, /*lockfunc*/busdma_lock_mutex, 638117126Sscottl /*lockarg*/&Giant, &fm801->parent_dmat) != 0) { 63970134Scg device_printf(dev, "unable to create dma tag\n"); 64070134Scg goto oops; 64170134Scg } 64270134Scg 643297000Sjhibbits snprintf(status, 64, "at %s 0x%jx irq %jd %s", 64470134Scg (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", 645126695Smatk rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801)); 64670134Scg 64770134Scg#define FM801_MAXPLAYCH 1 64870134Scg if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; 64970134Scg pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801); 65070134Scg pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801); 65170134Scg pcm_setstatus(dev, status); 65270134Scg 653102889Ssobomax fm801->radio = device_add_child(dev, "radio", -1); 654102889Ssobomax bus_generic_attach(dev); 655102889Ssobomax 65670134Scg return 0; 65770134Scg 65870134Scgoops: 65970134Scg if (codec) ac97_destroy(codec); 66070134Scg if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); 66170134Scg if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih); 66270134Scg if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); 66370134Scg if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat); 66470134Scg free(fm801, M_DEVBUF); 66570134Scg return ENXIO; 66670134Scg} 66770134Scg 66870134Scgstatic int 66970134Scgfm801_pci_detach(device_t dev) 67070134Scg{ 67170134Scg int r; 67270134Scg struct fm801_info *fm801; 67370134Scg 67470134Scg DPRINT("Forte Media FM801 detach\n"); 67570134Scg 676102889Ssobomax fm801 = pcm_getdevinfo(dev); 677102889Ssobomax 678102889Ssobomax r = bus_generic_detach(dev); 679102889Ssobomax if (r) 680102889Ssobomax return r; 681102889Ssobomax if (fm801->radio != NULL) { 682102889Ssobomax r = device_delete_child(dev, fm801->radio); 683102889Ssobomax if (r) 684102889Ssobomax return r; 685102889Ssobomax fm801->radio = NULL; 686102889Ssobomax } 687102889Ssobomax 68870134Scg r = pcm_unregister(dev); 68970134Scg if (r) 69070134Scg return r; 69170134Scg 69270134Scg bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); 69370134Scg bus_teardown_intr(dev, fm801->irq, fm801->ih); 69470134Scg bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); 69570134Scg bus_dma_tag_destroy(fm801->parent_dmat); 69670134Scg free(fm801, M_DEVBUF); 69770134Scg return 0; 69870134Scg} 69970134Scg 70070134Scgstatic int 70170134Scgfm801_pci_probe( device_t dev ) 70270134Scg{ 703135284Ssobomax int id; 704102889Ssobomax 70570134Scg if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { 706135284Ssobomax device_set_desc(dev, "Forte Media FM801 Audio Controller"); 707142890Simp return BUS_PROBE_DEFAULT; 70870134Scg } 70970134Scg/* 71070134Scg if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) { 71170134Scg device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)"); 71270134Scg return ENXIO; 71370134Scg } 71470134Scg*/ 715135284Ssobomax return ENXIO; 71670134Scg} 71770134Scg 718102889Ssobomaxstatic struct resource * 719102889Ssobomaxfm801_alloc_resource(device_t bus, device_t child, int type, int *rid, 720294883Sjhibbits rman_res_t start, rman_res_t end, rman_res_t count, 721294883Sjhibbits u_int flags) 722102889Ssobomax{ 723102889Ssobomax struct fm801_info *fm801; 724102889Ssobomax 725102889Ssobomax fm801 = pcm_getdevinfo(bus); 726102889Ssobomax 727119690Sjhb if (type == SYS_RES_IOPORT && *rid == PCIR_BAR(0)) 728102889Ssobomax return (fm801->reg); 729102889Ssobomax 730102889Ssobomax return (NULL); 731102889Ssobomax} 732102889Ssobomax 733102889Ssobomaxstatic int 734102889Ssobomaxfm801_release_resource(device_t bus, device_t child, int type, int rid, 735102889Ssobomax struct resource *r) 736102889Ssobomax{ 737102889Ssobomax return (0); 738102889Ssobomax} 739102889Ssobomax 74065205Scgstatic device_method_t fm801_methods[] = { 74165205Scg /* Device interface */ 74265205Scg DEVMETHOD(device_probe, fm801_pci_probe), 74365205Scg DEVMETHOD(device_attach, fm801_pci_attach), 74465205Scg DEVMETHOD(device_detach, fm801_pci_detach), 745102889Ssobomax DEVMETHOD(device_shutdown, bus_generic_shutdown), 746102889Ssobomax DEVMETHOD(device_suspend, bus_generic_suspend), 747102889Ssobomax DEVMETHOD(device_resume, bus_generic_resume), 748102889Ssobomax 749102889Ssobomax /* Bus interface */ 750102889Ssobomax DEVMETHOD(bus_alloc_resource, fm801_alloc_resource), 751102889Ssobomax DEVMETHOD(bus_release_resource, fm801_release_resource), 752102889Ssobomax DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 753102889Ssobomax DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 754227843Smarius 755227843Smarius DEVMETHOD_END 75665205Scg}; 75765205Scg 75865205Scgstatic driver_t fm801_driver = { 75965205Scg "pcm", 76065205Scg fm801_methods, 76182180Scg PCM_SOFTC_SIZE, 76265205Scg}; 76365205Scg 76479046ScgDRIVER_MODULE(snd_fm801, pci, fm801_driver, pcm_devclass, 0, 0); 765132236StanimuraMODULE_DEPEND(snd_fm801, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 76679046ScgMODULE_VERSION(snd_fm801, 1); 767