Index: stable/4/sys/conf/files =================================================================== --- stable/4/sys/conf/files (revision 71948) +++ stable/4/sys/conf/files (revision 71949) @@ -1,1156 +1,1221 @@ # $FreeBSD$ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and # dependency lines other than the first are silently ignored. # aicasm optional ahc \ dependency "$S/dev/aic7xxx/aicasm/*.[chyl]" \ compile-with "${MAKE} -f $S/dev/aic7xxx/aicasm/Makefile MAKESRCPATH=$S/dev/aic7xxx/aicasm" \ no-obj no-implicit-rule \ clean "aicasm aicasm_gram.c aicasm_scan.c y.tab.h" aic7xxx_{seq,reg}.h optional ahc \ compile-with "./aicasm ${INCLUDES} -I$S/cam/scsi -I$S/dev/aic7xxx -o aic7xxx_seq.h -r aic7xxx_reg.h $S/dev/aic7xxx/aic7xxx.seq" \ no-obj no-implicit-rule before-depend \ clean "aic7xxx_seq.h aic7xxx_reg.h" \ dependency "$S/dev/aic7xxx/aic7xxx.{reg,seq} $S/cam/scsi/scsi_message.h aicasm" device_if.o standard \ compile-with "${NORMAL_C}" \ no-implicit-rule local device_if.c standard \ dependency "$S/kern/makeops.pl $S/kern/device_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/kern/device_if.m" \ no-obj no-implicit-rule before-depend local \ clean "device_if.c" device_if.h standard \ dependency "$S/kern/makeops.pl $S/kern/device_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/kern/device_if.m" \ no-obj no-implicit-rule before-depend \ clean "device_if.h" bus_if.o standard \ compile-with "${NORMAL_C}" \ no-implicit-rule local bus_if.c standard \ dependency "$S/kern/makeops.pl $S/kern/bus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/kern/bus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "bus_if.c" bus_if.h standard \ dependency "$S/kern/makeops.pl $S/kern/bus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/kern/bus_if.m" \ no-obj no-implicit-rule before-depend \ clean "bus_if.h" coda/coda_namecache.c optional vcoda coda/coda_fbsd.c optional vcoda coda/coda_psdev.c optional vcoda coda/coda_subr.c optional vcoda coda/coda_venus.c optional vcoda coda/coda_vfsops.c optional vcoda coda/coda_vnops.c optional vcoda cam/cam.c optional scbus cam/cam_xpt.c optional scbus cam/cam_extend.c optional scbus cam/cam_queue.c optional scbus cam/cam_periph.c optional scbus cam/cam_sim.c optional scbus cam/scsi/scsi_all.c optional scbus cam/scsi/scsi_da.c optional da cam/scsi/scsi_pt.c optional pt cam/scsi/scsi_sa.c optional sa cam/scsi/scsi_cd.c optional cd cam/scsi/scsi_ch.c optional ch cam/scsi/scsi_low.c optional ncv cam/scsi/scsi_low.c optional nsp cam/scsi/scsi_low.c optional stg cam/scsi/scsi_low_pisa.c optional ncv cam/scsi/scsi_low_pisa.c optional nsp cam/scsi/scsi_low_pisa.c optional stg cam/scsi/scsi_pass.c optional pass cam/scsi/scsi_ses.c optional ses cam/scsi/scsi_target.c optional targ cam/scsi/scsi_targ_bh.c optional targbh crypto/sha1.c optional ipsec crypto/des/des_cbc.c optional ipsec ipsec_esp crypto/des/des_ecb.c optional ipsec ipsec_esp crypto/des/des_setkey.c optional ipsec ipsec_esp crypto/des/des_3cbc.c optional ipsec ipsec_esp crypto/blowfish/bf_cbc.c optional ipsec ipsec_esp crypto/blowfish/bf_cbc_m.c optional ipsec ipsec_esp crypto/blowfish/bf_enc.c optional ipsec ipsec_esp crypto/blowfish/bf_skey.c optional ipsec ipsec_esp crypto/cast128/cast128.c optional ipsec ipsec_esp crypto/cast128/cast128_cbc.c optional ipsec ipsec_esp crypto/rc5/rc5.c optional ipsec ipsec_esp crypto/rc5/rc5_cbc.c optional ipsec ipsec_esp ddb/db_access.c optional ddb ddb/db_kld.c optional ddb ddb/db_break.c optional ddb ddb/db_command.c optional ddb ddb/db_examine.c optional ddb ddb/db_expr.c optional ddb ddb/db_input.c optional ddb ddb/db_lex.c optional ddb ddb/db_output.c optional ddb ddb/db_print.c optional ddb ddb/db_ps.c optional ddb ddb/db_run.c optional ddb ddb/db_sym.c optional ddb ddb/db_sysctl.c optional ddb ddb/db_trap.c optional ddb ddb/db_variables.c optional ddb ddb/db_watch.c optional ddb ddb/db_write_cmd.c optional ddb dev/aac/aac.c optional aac #dev/aac/aac_debug.c optional aac dev/aac/aac_disk.c optional aac dev/aac/aac_pci.c optional aac pci dev/advansys/adv_eisa.c optional adv eisa dev/advansys/adv_pci.c optional adv pci dev/advansys/advansys.c optional adv dev/advansys/advlib.c optional adv dev/advansys/advmcode.c optional adv dev/advansys/adw_pci.c optional adw pci dev/advansys/adwcam.c optional adw dev/advansys/adwlib.c optional adw dev/advansys/adwmcode.c optional adw dev/an/if_an.c optional an dev/an/if_an_isa.c optional an isa dev/an/if_an_pci.c optional an pci dev/an/if_an_pccard.c optional an card dev/asr/asr.c optional asr pci #dev/amr/amr_cam.c optional amr dev/amr/amr_disk.c optional amr dev/amr/amr_pci.c optional amr dev/amr/amr.c optional amr dev/aha/aha.c optional aha dev/aha/aha_isa.c optional aha isa dev/aha/aha_mca.c optional aha mca dev/ahb/ahb.c optional ahb eisa dev/aic/aic.c optional aic dev/aic/aic_pccard.c optional aic card dev/aic7xxx/aic7770.c optional ahc eisa dev/aic7xxx/ahc_eisa.c optional ahc eisa #dev/aic7xxx/ahc_isa.c optional ahc isa dev/aic7xxx/ahc_pci.c optional ahc pci dev/aic7xxx/aic7xxx.c optional ahc dev/aic7xxx/aic7xxx_93cx6.c optional ahc dev/aic7xxx/aic7xxx_freebsd.c optional ahc dev/aic7xxx/aic7xxx_pci.c optional ahc pci dev/buslogic/bt.c optional bt dev/buslogic/bt_isa.c optional bt isa dev/buslogic/bt_mca.c optional bt mca dev/buslogic/bt_eisa.c optional bt eisa dev/buslogic/bt_pci.c optional bt pci dev/cardbus/cardbus.c optional cardbus dev/ccd/ccd.c optional ccd dev/cs/if_cs.c optional cs dev/cs/if_cs_isa.c optional cs isa dev/cs/if_cs_pccard.c optional cs card #dev/dpt/dpt_control.c optional dpt dev/dpt/dpt_scsi.c optional dpt dev/dpt/dpt_eisa.c optional dpt eisa dev/dpt/dpt_pci.c optional dpt pci dev/ed/if_ed_pci.c optional ed pci dev/ep/if_ep.c optional ep dev/ep/if_ep_isa.c optional ep isa dev/ep/if_ep_eisa.c optional ep eisa dev/ep/if_ep_mca.c optional ep mca dev/ep/if_ep_pccard.c optional ep card dev/en/midway.c optional en dev/ex/if_ex.c optional ex isa dev/fe/if_fe.c optional fe dev/fe/if_fe_pccard.c optional fe card dev/hea/eni.c optional hea dev/hea/eni_buffer.c optional hea dev/hea/eni_globals.c optional hea dev/hea/eni_if.c optional hea dev/hea/eni_init.c optional hea dev/hea/eni_intr.c optional hea dev/hea/eni_receive.c optional hea dev/hea/eni_transmit.c optional hea dev/hea/eni_vcm.c optional hea dev/hfa/fore_buffer.c optional hfa dev/hfa/fore_command.c optional hfa dev/hfa/fore_globals.c optional hfa dev/hfa/fore_if.c optional hfa dev/hfa/fore_init.c optional hfa dev/hfa/fore_intr.c optional hfa dev/hfa/fore_load.c optional hfa dev/hfa/fore_output.c optional hfa dev/hfa/fore_receive.c optional hfa dev/hfa/fore_stats.c optional hfa dev/hfa/fore_timer.c optional hfa dev/hfa/fore_transmit.c optional hfa dev/hfa/fore_vcm.c optional hfa dev/ie/if_ie.c optional ie isa dev/ichsmb/ichsmb.c optional ichsmb dev/ichsmb/ichsmb_pci.c optional ichsmb pci dev/ida/ida.c optional ida dev/ida/ida_disk.c optional ida dev/ida/ida_eisa.c optional ida eisa dev/ida/ida_pci.c optional ida pci dev/isp/isp_freebsd.c optional isp dev/isp/isp.c optional isp dev/isp/isp_target.c optional isp dev/ispfw/ispfw.c optional ispfw dev/mca/mca_bus.c optional mca dev/md/md.c optional md dev/mii/mii.c optional miibus dev/mii/mii_physubr.c optional miibus dev/mii/ukphy.c optional miibus dev/mii/ukphy_subr.c optional miibus dev/mii/amphy.c optional miibus dev/mii/brgphy.c optional miibus dev/mii/dcphy.c optional miibus dev/mii/exphy.c optional miibus dev/mii/mlphy.c optional miibus dev/mii/nsphy.c optional miibus dev/mii/pnphy.c optional miibus dev/mii/pnaphy.c optional miibus dev/mii/tlphy.c optional miibus dev/mii/rlphy.c optional miibus dev/mii/xmphy.c optional miibus miibus_if.o optional miibus \ dependency "miibus_if.c miibus_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local miibus_if.c optional miibus \ dependency "$S/kern/makeops.pl $S/dev/mii/miibus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/mii/miibus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "miibus_if.c" miibus_if.h optional miibus \ dependency "$S/kern/makeops.pl $S/dev/mii/miibus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/mii/miibus_if.m" \ no-obj no-implicit-rule before-depend \ clean "miibus_if.h" dev/mlx/mlx_disk.c optional mlx dev/mlx/mlx_pci.c optional mlx dev/mlx/mlx.c optional mlx dev/mly/mly.c optional mly dev/mly/mly_cam.c optional mly dev/mly/mly_pci.c optional mly dev/musycc/musycc.c optional musycc dev/ncv/ncr53c500.c optional ncv dev/ncv/ncr53c500_pccard.c optional ncv card dev/nsp/nsp.c optional nsp dev/nsp/nsp_pccard.c optional nsp card dev/pccard/pccard.c optional pccard dev/pccard/pccard_cis.c optional pccard dev/pccard/pccard_cis_quirks.c optional pccard power_if.o optional pccard \ dependency "power_if.c power_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local power_if.c optional pccard \ dependency "$S/kern/makeops.pl $S/dev/pccard/power_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/pccard/power_if.m" \ no-obj no-implicit-rule before-depend local \ clean "power_if.c" power_if.h optional pccard \ dependency "$S/kern/makeops.pl $S/dev/pccard/power_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/pccard/power_if.m" \ no-obj no-implicit-rule before-depend \ clean "power_if.h" power_if.o optional card \ dependency "power_if.c power_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local power_if.c optional card \ dependency "$S/kern/makeops.pl $S/dev/pccard/power_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/pccard/power_if.m" \ no-obj no-implicit-rule before-depend local \ clean "power_if.c" power_if.h optional card \ dependency "$S/kern/makeops.pl $S/dev/pccard/power_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/pccard/power_if.m" \ no-obj no-implicit-rule before-depend \ clean "power_if.h" card_if.o optional pccard \ dependency "card_if.c card_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local card_if.c optional pccard \ dependency "$S/kern/makeops.pl $S/dev/pccard/card_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/pccard/card_if.m" \ no-obj no-implicit-rule before-depend local \ clean "card_if.c" card_if.h optional pccard \ dependency "$S/kern/makeops.pl $S/dev/pccard/card_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/pccard/card_if.m" \ no-obj no-implicit-rule before-depend \ clean "card_if.h" card_if.o optional card \ dependency "card_if.c card_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local card_if.c optional card \ dependency "$S/kern/makeops.pl $S/dev/pccard/card_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/pccard/card_if.m" \ no-obj no-implicit-rule before-depend local \ clean "card_if.c" card_if.h optional card \ dependency "$S/kern/makeops.pl $S/dev/pccard/card_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/pccard/card_if.m" \ no-obj no-implicit-rule before-depend \ clean "card_if.h" dev/pcic/i82365.c optional pcic pccard dev/pcic/i82365_isa.c optional pcic pccard dev/pdq/if_fea.c optional fea eisa dev/pdq/if_fpa.c optional fpa pci dev/pdq/pdq.c optional fea eisa dev/pdq/pdq_ifsubr.c optional fea eisa dev/pdq/pdq.c optional fpa pci dev/pdq/pdq_ifsubr.c optional fpa pci ppbus_if.o optional ppbus \ dependency "ppbus_if.c ppbus_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local ppbus_if.c optional ppbus \ dependency "$S/kern/makeops.pl $S/dev/ppbus/ppbus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/ppbus/ppbus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "ppbus_if.c" ppbus_if.h optional ppbus \ dependency "$S/kern/makeops.pl $S/dev/ppbus/ppbus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/ppbus/ppbus_if.m" \ no-obj no-implicit-rule before-depend \ clean "ppbus_if.h" dev/ppbus/immio.c optional vpo dev/ppbus/if_plip.c optional plip dev/ppbus/lpbb.c optional lpbb dev/ppbus/lpt.c optional lpt dev/ppbus/pcfclock.c optional pcfclock dev/ppbus/ppb_base.c optional ppbus dev/ppbus/ppb_1284.c optional ppbus dev/ppbus/ppb_msq.c optional ppbus dev/ppbus/ppbconf.c optional ppbus dev/ppbus/ppi.c optional ppi dev/ppbus/pps.c optional pps dev/ppbus/vpo.c optional vpo dev/ppbus/vpoio.c optional vpo dev/ray/if_ray.c optional ray card dev/ray/if_ray.c optional ray pccard smbus_if.o optional smbus \ dependency "smbus_if.c smbus_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local smbus_if.c optional smbus \ dependency "$S/kern/makeops.pl $S/dev/smbus/smbus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/smbus/smbus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "smbus_if.c" smbus_if.h optional smbus \ dependency "$S/kern/makeops.pl $S/dev/smbus/smbus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/smbus/smbus_if.m" \ no-obj no-implicit-rule before-depend \ clean "smbus_if.h" dev/smbus/smbconf.c optional smbus dev/smbus/smbus.c optional smbus dev/smbus/smb.c optional smb dev/si/si.c optional si dev/si/si2_z280.c optional si dev/si/si3_t225.c optional si dev/si/si_eisa.c optional si eisa dev/si/si_isa.c optional si isa dev/si/si_pci.c optional si pci dev/sn/if_sn.c optional sn dev/sn/if_sn_isa.c optional sn isa dev/sn/if_sn_pccard.c optional sn card dev/stg/tmc18c30.c optional stg dev/stg/tmc18c30_isa.c optional stg isa dev/stg/tmc18c30_pccard.c optional stg card dev/iicbus/iicbb.c optional iicbb iicbb_if.o optional iicbb \ dependency "iicbb_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local iicbb_if.c optional iicbb \ dependency "$S/kern/makeops.pl $S/dev/iicbus/iicbb_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/iicbus/iicbb_if.m" \ no-obj no-implicit-rule before-depend local \ clean "iicbb_if.c" iicbb_if.h optional iicbb \ dependency "$S/kern/makeops.pl $S/dev/iicbus/iicbb_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/iicbus/iicbb_if.m" \ no-obj no-implicit-rule before-depend \ clean "iicbb_if.h" dev/iicbus/iicsmb.c optional iicsmb \ dependency "iicbus_if.h" iicbus_if.o optional iicbus \ dependency "iicbus_if.c iicbus_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local iicbus_if.c optional iicbus \ dependency "$S/kern/makeops.pl $S/dev/iicbus/iicbus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/iicbus/iicbus_if.m" \ no-obj no-implicit-rule before-depend local \ clean "iicbus_if.c" iicbus_if.h optional iicbus \ dependency "$S/kern/makeops.pl $S/dev/iicbus/iicbus_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/iicbus/iicbus_if.m" \ no-obj no-implicit-rule before-depend \ clean "iicbus_if.h" dev/iicbus/iiconf.c optional iicbus dev/iicbus/iicbus.c optional iicbus dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/twe/twe.c optional twe dev/twe/twe_freebsd.c optional twe dev/vinum/vinum.c optional vinum dev/vinum/vinumconfig.c optional vinum dev/vinum/vinumdaemon.c optional vinum dev/vinum/vinuminterrupt.c optional vinum dev/vinum/vinumio.c optional vinum dev/vinum/vinumioctl.c optional vinum dev/vinum/vinumlock.c optional vinum dev/vinum/vinummemory.c optional vinum dev/vinum/vinumparser.c optional vinum dev/vinum/vinumraid5.c optional vinum dev/vinum/vinumrequest.c optional vinum dev/vinum/vinumrevive.c optional vinum dev/vinum/vinumstate.c optional vinum dev/vinum/vinumutil.c optional vinum dev/vn/vn.c optional vn dev/vx/if_vx.c optional vx dev/vx/if_vx_eisa.c optional vx eisa dev/vx/if_vx_pci.c optional vx pci dev/xe/if_xe.c optional xe gnu/ext2fs/ext2_alloc.c optional ext2fs gnu/ext2fs/ext2_balloc.c optional ext2fs gnu/ext2fs/ext2_inode.c optional ext2fs gnu/ext2fs/ext2_inode_cnv.c optional ext2fs gnu/ext2fs/ext2_linux_balloc.c optional ext2fs gnu/ext2fs/ext2_linux_ialloc.c optional ext2fs gnu/ext2fs/ext2_lookup.c optional ext2fs gnu/ext2fs/ext2_subr.c optional ext2fs gnu/ext2fs/ext2_vfsops.c optional ext2fs gnu/ext2fs/ext2_vnops.c optional ext2fs # device drivers i4b/driver/i4b_trace.c optional i4btrc i4b/driver/i4b_rbch.c optional i4brbch i4b/driver/i4b_tel.c optional i4btel i4b/driver/i4b_ipr.c optional i4bipr i4b/driver/i4b_ctl.c optional i4bctl i4b/driver/i4b_isppp.c optional i4bisppp net/if_spppsubr.c optional sppp # needed by i4bipr net/slcompress.c optional i4bipr # tina-dd control driver i4b/tina-dd/i4b_tina_dd.c optional tina # support i4b/layer2/i4b_mbuf.c optional i4btrc # Q.921 handler i4b/layer2/i4b_l2.c optional i4bq921 i4b/layer2/i4b_l2fsm.c optional i4bq921 i4b/layer2/i4b_uframe.c optional i4bq921 i4b/layer2/i4b_tei.c optional i4bq921 i4b/layer2/i4b_sframe.c optional i4bq921 i4b/layer2/i4b_iframe.c optional i4bq921 i4b/layer2/i4b_l2timer.c optional i4bq921 i4b/layer2/i4b_util.c optional i4bq921 i4b/layer2/i4b_lme.c optional i4bq921 # Q.931 handler i4b/layer3/i4b_q931.c optional i4bq931 i4b/layer3/i4b_l3fsm.c optional i4bq931 i4b/layer3/i4b_l3timer.c optional i4bq931 i4b/layer3/i4b_l2if.c optional i4bq931 i4b/layer3/i4b_l4if.c optional i4bq931 i4b/layer3/i4b_q932fac.c optional i4bq931 # isdn device driver, interface to i4bd i4b/layer4/i4b_i4bdrv.c optional i4b i4b/layer4/i4b_l4.c optional i4b i4b/layer4/i4b_l4mgmt.c optional i4b i4b/layer4/i4b_l4timer.c optional i4b isofs/cd9660/cd9660_bmap.c optional cd9660 isofs/cd9660/cd9660_lookup.c optional cd9660 isofs/cd9660/cd9660_node.c optional cd9660 isofs/cd9660/cd9660_rrip.c optional cd9660 isofs/cd9660/cd9660_util.c optional cd9660 isofs/cd9660/cd9660_vfsops.c optional cd9660 isofs/cd9660/cd9660_vnops.c optional cd9660 kern/imgact_aout.c standard kern/imgact_elf.c standard kern/imgact_gzip.c optional gzip kern/imgact_shell.c standard kern/inflate.c optional gzip kern/init_main.c standard kern/init_sysent.c standard kern/kern_intr.c standard kern/kern_module.c standard kern/kern_linker.c standard kern/link_aout.c standard kern/link_elf.c standard kern/kern_acct.c standard kern/kern_acl.c standard kern/kern_clock.c standard kern/kern_conf.c standard kern/kern_descrip.c standard kern/kern_environment.c standard kern/kern_event.c standard kern/kern_exec.c standard kern/kern_exit.c standard kern/kern_fork.c standard kern/kern_jail.c standard kern/kern_kthread.c standard kern/kern_ktrace.c standard kern/kern_lock.c standard kern/kern_lockf.c standard kern/kern_malloc.c standard kern/kern_mib.c standard kern/kern_ntptime.c standard kern/kern_physio.c standard kern/kern_proc.c standard kern/kern_prot.c standard kern/kern_random.c standard kern/kern_resource.c standard kern/kern_shutdown.c standard kern/kern_sig.c standard kern/kern_subr.c standard kern/kern_switch.c standard kern/kern_synch.c standard kern/kern_syscalls.c standard kern/kern_sysctl.c standard kern/kern_time.c standard kern/kern_timeout.c standard kern/kern_xxx.c standard kern/md5c.c standard kern/subr_autoconf.c standard kern/subr_bus.c standard kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_diskslice.c standard kern/subr_eventhandler.c standard kern/subr_kobj.c standard kern/subr_log.c standard kern/subr_module.c standard kern/subr_prf.c standard kern/subr_prof.c standard kern/subr_blist.c standard kern/subr_scanf.c standard kern/subr_taskqueue.c standard kern/subr_xxx.c standard kern/sys_generic.c standard kern/sys_pipe.c standard kern/sys_process.c standard kern/subr_rman.c standard kern/sys_socket.c standard kern/sysv_ipc.c standard kern/sysv_msg.c optional sysvmsg kern/sysv_sem.c optional sysvsem kern/sysv_shm.c optional sysvshm kern/tty.c standard kern/tty_compat.c standard kern/tty_conf.c standard kern/tty_cons.c standard kern/tty_pty.c optional pty kern/tty_snoop.c optional snp kern/tty_subr.c standard kern/tty_tty.c standard kern/uipc_accf.c optional inet kern/uipc_domain.c standard kern/uipc_mbuf.c standard kern/uipc_mbuf2.c standard kern/uipc_proto.c standard kern/uipc_socket.c standard kern/uipc_socket2.c standard kern/uipc_syscalls.c standard kern/uipc_usrreq.c standard kern/vfs_bio.c standard kern/vfs_cache.c standard kern/vfs_cluster.c standard kern/vfs_conf.c standard kern/vfs_default.c standard kern/vfs_init.c standard kern/vfs_lookup.c standard kern/vfs_subr.c standard kern/vfs_syscalls.c standard kern/vfs_vnops.c standard kern/kern_threads.c standard kern/vfs_aio.c standard miscfs/deadfs/dead_vnops.c standard miscfs/devfs/devfs_tree.c optional devfs miscfs/devfs/devfs_vfsops.c optional devfs miscfs/devfs/devfs_vnops.c optional devfs miscfs/fdesc/fdesc_vfsops.c optional fdesc miscfs/fdesc/fdesc_vnops.c optional fdesc miscfs/fifofs/fifo_vnops.c standard miscfs/kernfs/kernfs_vfsops.c optional kernfs miscfs/kernfs/kernfs_vnops.c optional kernfs miscfs/nullfs/null_subr.c optional nullfs miscfs/nullfs/null_vfsops.c optional nullfs miscfs/nullfs/null_vnops.c optional nullfs miscfs/portal/portal_vfsops.c optional portal miscfs/portal/portal_vnops.c optional portal miscfs/procfs/procfs_ctl.c optional procfs miscfs/procfs/procfs_dbregs.c standard miscfs/procfs/procfs_fpregs.c standard miscfs/procfs/procfs_map.c optional procfs miscfs/procfs/procfs_mem.c standard miscfs/procfs/procfs_note.c optional procfs miscfs/procfs/procfs_regs.c standard miscfs/procfs/procfs_status.c optional procfs miscfs/procfs/procfs_subr.c optional procfs miscfs/procfs/procfs_type.c optional procfs miscfs/procfs/procfs_vfsops.c optional procfs miscfs/procfs/procfs_vnops.c optional procfs miscfs/procfs/procfs_rlimit.c optional procfs miscfs/specfs/spec_vnops.c standard miscfs/umapfs/umap_subr.c optional umapfs miscfs/umapfs/umap_vfsops.c optional umapfs miscfs/umapfs/umap_vnops.c optional umapfs miscfs/union/union_subr.c optional union miscfs/union/union_vfsops.c optional union miscfs/union/union_vnops.c optional union msdosfs/msdosfs_conv.c optional msdosfs msdosfs/msdosfs_denode.c optional msdosfs msdosfs/msdosfs_fat.c optional msdosfs msdosfs/msdosfs_lookup.c optional msdosfs msdosfs/msdosfs_vfsops.c optional msdosfs msdosfs/msdosfs_vnops.c optional msdosfs ntfs/ntfs_vfsops.c optional ntfs ntfs/ntfs_vnops.c optional ntfs ntfs/ntfs_subr.c optional ntfs ntfs/ntfs_compr.c optional ntfs ntfs/ntfs_ihash.c optional ntfs net/bpf.c standard net/bpf_filter.c optional bpf net/bridge.c optional bridge net/bsd_comp.c optional ppp_bsdcomp #net/hostcache.c standard net/if.c standard net/if_atmsubr.c optional atm net/if_disc.c optional disc net/if_ef.c optional ef net/if_ethersubr.c optional ether net/if_iso88025subr.c optional token net/if_faith.c optional faith net/if_fddisubr.c optional fddi net/if_gif.c optional gif net/if_loop.c optional loop net/if_media.c standard net/if_mib.c standard net/if_ppp.c optional ppp net/if_sl.c optional sl net/if_spppsubr.c optional sppp net/if_stf.c optional stf net/if_tun.c optional tun net/if_tap.c optional tap net/if_vlan.c optional vlan net/net_osdep.c standard net/ppp_deflate.c optional ppp_deflate net/ppp_tty.c optional ppp net/intrq.c standard net/radix.c standard net/raw_cb.c standard net/raw_usrreq.c standard net/route.c standard net/rtsock.c standard net/slcompress.c optional ppp net/slcompress.c optional sl net/zlib.c optional ppp_deflate net/zlib.c optional ipsec netatalk/aarp.c optional netatalk netatalk/at_control.c optional netatalk netatalk/at_proto.c optional netatalk netatalk/at_rmx.c optional netatalkdebug netatalk/ddp_input.c optional netatalk netatalk/ddp_output.c optional netatalk netatalk/ddp_usrreq.c optional netatalk netatm/atm_aal5.c optional atm_core netatm/atm_cm.c optional atm_core netatm/atm_device.c optional atm_core netatm/atm_if.c optional atm_core netatm/atm_proto.c optional atm_core netatm/atm_signal.c optional atm_core netatm/atm_socket.c optional atm_core netatm/atm_subr.c optional atm_core netatm/atm_usrreq.c optional atm_core netatm/ipatm/ipatm_event.c optional atm_ip atm_core netatm/ipatm/ipatm_if.c optional atm_ip atm_core netatm/ipatm/ipatm_input.c optional atm_ip atm_core netatm/ipatm/ipatm_load.c optional atm_ip atm_core netatm/ipatm/ipatm_output.c optional atm_ip atm_core netatm/ipatm/ipatm_usrreq.c optional atm_ip atm_core netatm/ipatm/ipatm_vcm.c optional atm_ip atm_core netatm/sigpvc/sigpvc_if.c optional atm_sigpvc atm_core netatm/sigpvc/sigpvc_subr.c optional atm_sigpvc atm_core netatm/spans/spans_arp.c optional atm_spans atm_core \ dependency "spans_xdr.h" netatm/spans/spans_cls.c optional atm_spans atm_core netatm/spans/spans_if.c optional atm_spans atm_core netatm/spans/spans_kxdr.c optional atm_spans atm_core netatm/spans/spans_msg.c optional atm_spans atm_core netatm/spans/spans_print.c optional atm_spans atm_core netatm/spans/spans_proto.c optional atm_spans atm_core netatm/spans/spans_subr.c optional atm_spans atm_core netatm/spans/spans_util.c optional atm_spans atm_core spans_xdr.h optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -h -C $S/netatm/spans/spans_xdr.x > spans_xdr.h" \ clean "spans_xdr.h" \ no-obj no-implicit-rule spans_xdr.c optional atm_spans atm_core \ before-depend \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "rpcgen -c -C $S/netatm/spans/spans_xdr.x > spans_xdr.c" \ clean "spans_xdr.c" \ no-obj no-implicit-rule local spans_xdr.o optional atm_spans atm_core \ dependency "$S/netatm/spans/spans_xdr.x" \ compile-with "${NORMAL_C}" \ no-implicit-rule local netatm/uni/q2110_sigaa.c optional atm_uni atm_core netatm/uni/q2110_sigcpcs.c optional atm_uni atm_core netatm/uni/q2110_subr.c optional atm_uni atm_core netatm/uni/qsaal1_sigaa.c optional atm_uni atm_core netatm/uni/qsaal1_sigcpcs.c optional atm_uni atm_core netatm/uni/qsaal1_subr.c optional atm_uni atm_core netatm/uni/sscf_uni.c optional atm_uni atm_core netatm/uni/sscf_uni_lower.c optional atm_uni atm_core netatm/uni/sscf_uni_upper.c optional atm_uni atm_core netatm/uni/sscop.c optional atm_uni atm_core netatm/uni/sscop_lower.c optional atm_uni atm_core netatm/uni/sscop_pdu.c optional atm_uni atm_core netatm/uni/sscop_sigaa.c optional atm_uni atm_core netatm/uni/sscop_sigcpcs.c optional atm_uni atm_core netatm/uni/sscop_subr.c optional atm_uni atm_core netatm/uni/sscop_timer.c optional atm_uni atm_core netatm/uni/sscop_upper.c optional atm_uni atm_core netatm/uni/uni_load.c optional atm_uni atm_core netatm/uni/uniarp.c optional atm_uni atm_core netatm/uni/uniarp_cache.c optional atm_uni atm_core netatm/uni/uniarp_input.c optional atm_uni atm_core netatm/uni/uniarp_output.c optional atm_uni atm_core netatm/uni/uniarp_timer.c optional atm_uni atm_core netatm/uni/uniarp_vcm.c optional atm_uni atm_core netatm/uni/uniip.c optional atm_uni atm_core netatm/uni/unisig_decode.c optional atm_uni atm_core netatm/uni/unisig_encode.c optional atm_uni atm_core netatm/uni/unisig_if.c optional atm_uni atm_core netatm/uni/unisig_mbuf.c optional atm_uni atm_core netatm/uni/unisig_msg.c optional atm_uni atm_core netatm/uni/unisig_print.c optional atm_uni atm_core netatm/uni/unisig_proto.c optional atm_uni atm_core netatm/uni/unisig_sigmgr_state.c optional atm_uni atm_core netatm/uni/unisig_subr.c optional atm_uni atm_core netatm/uni/unisig_util.c optional atm_uni atm_core netatm/uni/unisig_vc_state.c optional atm_uni atm_core netgraph/ng_base.c optional netgraph netgraph/ng_parse.c optional netgraph netgraph/ng_async.c optional netgraph_async netgraph/ng_bpf.c optional netgraph_bpf net/bpf_filter.c optional netgraph_bpf netgraph/ng_cisco.c optional netgraph_cisco netgraph/ng_echo.c optional netgraph_echo netgraph/ng_ether.c optional netgraph_ether netgraph/ng_frame_relay.c optional netgraph_frame_relay netgraph/ng_hole.c optional netgraph_hole netgraph/ng_iface.c optional netgraph_iface netgraph/ng_ksocket.c optional netgraph_ksocket netgraph/ng_lmi.c optional netgraph_lmi netgraph/ng_mppc.c optional netgraph_mppc_compression # The next two files (plus the header file net/mppc.h) are proprietary and # must be obtained elsewhere in order to enable NETGRAPH_MPPC_COMPRESSION net/mppcc.c optional netgraph_mppc_compression net/mppcd.c optional netgraph_mppc_compression netgraph/ng_mppc.c optional netgraph_mppc_encryption crypto/rc4/rc4.c optional awi crypto/rc4/rc4.c optional netgraph_mppc_encryption crypto/sha1.c optional netgraph_mppc_encryption netgraph/ng_one2many.c optional netgraph_one2many netgraph/ng_parse.c optional netgraph netgraph/ng_ppp.c optional netgraph_ppp netgraph/ng_pppoe.c optional netgraph_pppoe netgraph/ng_pptpgre.c optional netgraph_pptpgre netgraph/ng_rfc1490.c optional netgraph_rfc1490 netgraph/ng_socket.c optional netgraph_socket netgraph/ng_tee.c optional netgraph_tee netgraph/ng_tty.c optional netgraph_tty netgraph/ng_UI.c optional netgraph_UI netgraph/ng_vjc.c optional netgraph_vjc net/slcompress.c optional netgraph_vjc netinet/accf_data.c optional accept_filter_data netinet/accf_http.c optional accept_filter_http netinet/if_atm.c optional atm netinet/if_ether.c optional ether netinet/in_gif.c optional gif inet netinet/igmp.c optional inet netinet/in.c optional inet #netinet/in_hostcache.c optional inet netinet/in_pcb.c optional inet netinet/in_proto.c optional inet netinet/in_rmx.c optional inet netinet/ip_ecn.c optional inet netinet/ip_ecn.c optional inet6 netinet/ip_encap.c optional inet netinet/ip_encap.c optional inet6 netinet/ip_divert.c optional ipdivert netinet/ip_dummynet.c optional dummynet netinet/ip_flow.c optional inet netinet/ip_fw.c optional ipfirewall netinet/ip_icmp.c optional inet netinet/ip_input.c optional inet netinet/ip_mroute.c optional inet netinet/ip_output.c optional inet netinet/raw_ip.c optional inet netinet/tcp_debug.c optional tcpdebug netinet/tcp_input.c optional inet netinet/tcp_output.c optional inet netinet/tcp_subr.c optional inet netinet/tcp_timer.c optional inet netinet/tcp_usrreq.c optional inet netinet/udp_usrreq.c optional inet netinet/ip_fil.c optional ipfilter inet netinet/fil.c optional ipfilter inet netinet/ip_nat.c optional ipfilter inet netinet/ip_frag.c optional ipfilter inet netinet/ip_state.c optional ipfilter inet netinet/ip_auth.c optional ipfilter inet netinet/ip_proxy.c optional ipfilter inet netinet/ip_log.c optional ipfilter inet netinet/mlfk_ipl.c optional ipfilter inet netinet6/ah_core.c optional ipsec netinet6/ah_input.c optional ipsec netinet6/ah_output.c optional ipsec netinet6/esp_core.c optional ipsec ipsec_esp netinet6/esp_input.c optional ipsec ipsec_esp netinet6/esp_output.c optional ipsec ipsec_esp netinet6/ipsec.c optional ipsec netinet6/dest6.c optional inet6 netinet6/frag6.c optional inet6 netinet6/icmp6.c optional inet6 netinet6/in6.c optional inet6 netinet6/in6_cksum.c optional inet6 netinet6/in6_gif.c optional gif inet6 netinet6/ip6_forward.c optional inet6 netinet6/ip6_fw.c optional inet6 ipv6firewall netinet6/in6_ifattach.c optional inet6 netinet6/ip6_input.c optional inet6 netinet6/ip6_mroute.c optional inet6 netinet6/ip6_output.c optional inet6 netinet6/in6_src.c optional inet6 netinet6/ipcomp_core.c optional ipsec netinet6/ipcomp_input.c optional ipsec netinet6/ipcomp_output.c optional ipsec netinet6/in6_pcb.c optional inet6 netinet6/in6_prefix.c optional inet6 netinet6/in6_proto.c optional inet6 netinet6/in6_rmx.c optional inet6 netinet6/mld6.c optional inet6 netinet6/nd6.c optional inet6 netinet6/nd6_nbr.c optional inet6 netinet6/nd6_rtr.c optional inet6 netinet6/raw_ip6.c optional inet6 netinet6/route6.c optional inet6 netinet6/scope6.c optional inet6 netinet6/udp6_output.c optional inet6 netinet6/udp6_usrreq.c optional inet6 netipx/ipx.c optional ipx netipx/ipx_cksum.c optional ipx netipx/ipx_input.c optional ipx netipx/ipx_ip.c optional ipx netipx/ipx_outputfl.c optional ipx netipx/ipx_pcb.c optional ipx netipx/ipx_proto.c optional ipx netipx/ipx_tun.c optional ipx netipx/ipx_usrreq.c optional ipx netipx/spx_debug.c optional ipx netipx/spx_usrreq.c optional ipx netkey/key.c optional ipsec netkey/keydb.c optional ipsec netkey/key_debug.c optional ipsec netkey/keysock.c optional ipsec netnatm/natm.c optional natm netnatm/natm_pcb.c optional natm netnatm/natm_proto.c optional natm netncp/ncp_conn.c optional ncp netncp/ncp_crypt.c optional ncp netncp/ncp_login.c optional ncp netncp/ncp_mod.c optional ncp netncp/ncp_ncp.c optional ncp netncp/ncp_nls.c optional ncp netncp/ncp_rq.c optional ncp netncp/ncp_sock.c optional ncp netncp/ncp_subr.c optional ncp netns/idp_usrreq.c optional ns netns/ns.c optional ns netns/ns_error.c optional ns netns/ns_input.c optional ns netns/ns_ip.c optional ns netns/ns_output.c optional ns netns/ns_pcb.c optional ns netns/ns_proto.c optional ns netns/spp_debug.c optional ns netns/spp_usrreq.c optional ns nfs/nfs_bio.c optional nfs nfs/nfs_node.c optional nfs nfs/nfs_nqlease.c optional nfs nfs/nfs_serv.c optional nfs nfs/nfs_socket.c optional nfs nfs/nfs_srvcache.c optional nfs nfs/nfs_subs.c optional nfs nfs/nfs_syscalls.c optional nfs nfs/nfs_vfsops.c optional nfs nfs/nfs_vnops.c optional nfs nfs/bootp_subr.c optional bootp nfs/krpc_subr.c optional bootp nwfs/nwfs_io.c optional nwfs nwfs/nwfs_ioctl.c optional nwfs nwfs/nwfs_node.c optional nwfs nwfs/nwfs_subr.c optional nwfs nwfs/nwfs_vfsops.c optional nwfs nwfs/nwfs_vnops.c optional nwfs pccard/pccard.c optional card pccard/pccard_beep.c optional card pccard/pccard_nbk.c optional card pccard/pcic.c optional pcic card pci/agp.c optional agp pci/agp_intel.c optional agp pci/agp_via.c optional agp pci/agp_sis.c optional agp pci/agp_ali.c optional agp pci/agp_amd.c optional agp pci/agp_i810.c optional agp agp_if.o optional agp \ dependency "agp_if.c agp_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local agp_if.c optional agp \ dependency "$S/kern/makeops.pl $S/pci/agp_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/pci/agp_if.m" \ no-obj no-implicit-rule before-depend local \ clean "agp_if.c" agp_if.h optional agp \ dependency "$S/kern/makeops.pl $S/pci/agp_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/pci/agp_if.m" \ no-obj no-implicit-rule before-depend \ clean "agp_if.h" pci/amd.c optional amd pci/pcic_p.c optional pcic pci dev/awi/am79c930.c optional awi dev/awi/awi.c optional awi dev/awi/awi_wep.c optional awi dev/awi/awi_wicfg.c optional awi dev/awi/if_awi_pccard.c optional awi card dev/bktr/bktr_core.c optional bktr pci dev/bktr/bktr_i2c.c optional bktr pci smbus dev/bktr/bktr_card.c optional bktr pci dev/bktr/bktr_tuner.c optional bktr pci dev/bktr/bktr_audio.c optional bktr pci dev/bktr/bktr_os.c optional bktr pci pci/pccbb.c optional pccbb cardbus pci/cy_pci.c optional cy pci pci/if_ar_p.c optional ar pci pci/if_dc.c optional dc pci/if_de.c optional de pci/if_en_pci.c optional en pci pci/if_fxp.c optional fxp pci/if_lnc_p.c optional lnc pci pci/if_pcn.c optional pcn pci/if_mn.c optional mn pci/if_rl.c optional rl pci/if_sf.c optional sf pci/if_sis.c optional sis pci/if_sk.c optional sk pci/if_ste.c optional ste pci/if_sr_p.c optional sr pci pci/if_ti.c optional ti pci/if_tl.c optional tl pci/if_tx.c optional tx pci/if_vr.c optional vr pci/if_wb.c optional wb pci/if_wx.c optional wx pci/if_xl.c optional xl pci/isp_pci.c optional isp pci/intpm.c optional intpm pci/meteor.c optional meteor pci pci/ncr.c optional ncr dev/sym/sym_hipd.c optional sym \ dependency "$S/dev/sym/sym_{conf,defs}.h" pci/pci.c optional pci pci/pci_compat.c optional pci pci/pcisupport.c optional pci pci_if.o optional pci \ dependency "pci_if.c pci_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local pci_if.c optional pci \ dependency "$S/kern/makeops.pl $S/pci/pci_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/pci/pci_if.m" \ no-obj no-implicit-rule before-depend local \ clean "pci_if.c" pci_if.h optional pci \ dependency "$S/kern/makeops.pl $S/pci/pci_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/pci/pci_if.m" \ no-obj no-implicit-rule before-depend \ clean "pci_if.h" pci/simos.c optional simos pci/alpm.c optional alpm pci/xrpu.c optional xrpu posix4/posix4_mib.c standard posix4/p1003_1b.c standard posix4/ksched.c optional _kposix_priority_scheduling ufs/ffs/ffs_alloc.c optional ffs ufs/ffs/ffs_alloc.c optional mfs ufs/ffs/ffs_balloc.c optional ffs ufs/ffs/ffs_balloc.c optional mfs ufs/ffs/ffs_inode.c optional ffs ufs/ffs/ffs_inode.c optional mfs ufs/ffs/ffs_softdep_stub.c standard ufs/ffs/ffs_softdep.c optional softupdates ufs/ffs/ffs_subr.c optional ffs ufs/ffs/ffs_subr.c optional mfs ufs/ffs/ffs_tables.c optional ffs ufs/ffs/ffs_tables.c optional mfs ufs/ffs/ffs_vfsops.c optional ffs ufs/ffs/ffs_vfsops.c optional mfs ufs/ffs/ffs_vnops.c optional ffs ufs/ffs/ffs_vnops.c optional mfs ufs/mfs/mfs_vfsops.c optional mfs ufs/mfs/mfs_vnops.c optional mfs ufs/ufs/ufs_bmap.c standard ufs/ufs/ufs_disksubr.c standard ufs/ufs/ufs_ihash.c standard ufs/ufs/ufs_inode.c standard ufs/ufs/ufs_lookup.c standard ufs/ufs/ufs_quota.c standard ufs/ufs/ufs_vfsops.c standard ufs/ufs/ufs_vnops.c standard vm/default_pager.c standard vm/device_pager.c standard vm/phys_pager.c standard vm/swap_pager.c standard vm/vm_fault.c standard vm/vm_glue.c standard vm/vm_init.c standard vm/vm_kern.c standard vm/vm_map.c standard vm/vm_meter.c standard vm/vm_mmap.c standard vm/vm_object.c standard vm/vm_page.c standard vm/vm_pageout.c standard vm/vm_pager.c standard vm/vm_swap.c standard vm/vm_unix.c standard vm/vnode_pager.c standard vm/vm_zone.c standard dev/streams/streams.c optional streams # # USB support pci/uhci_pci.c optional uhci pci/ohci_pci.c optional ohci usb_if.o optional usb \ dependency "usb_if.c" \ compile-with "${NORMAL_C}" \ no-implicit-rule local usb_if.c optional usb \ dependency "$S/kern/makeops.pl $S/dev/usb/usb_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/dev/usb/usb_if.m" \ no-obj no-implicit-rule before-depend local \ clean "usb_if.c" usb_if.h optional usb \ dependency "$S/kern/makeops.pl $S/dev/usb/usb_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/dev/usb/usb_if.m" \ no-obj no-implicit-rule before-depend \ clean "usb_if.h" dev/usb/uhci.c optional uhci dev/usb/ohci.c optional ohci dev/usb/usb.c optional usb dev/usb/usbdi.c optional usb dev/usb/usbdi_util.c optional usb #dev/usb/usb_mem.c optional usb dev/usb/usb_ethersubr.c optional usb dev/usb/usb_subr.c optional usb dev/usb/usb_quirks.c optional usb dev/usb/hid.c optional usb dev/usb/ugen.c optional ugen dev/usb/uhid.c optional uhid dev/usb/ums.c optional ums dev/usb/ulpt.c optional ulpt dev/usb/ukbd.c optional ukbd dev/usb/umass.c optional umass dev/usb/umodem.c optional umodem dev/usb/uhub.c optional usb dev/usb/uscanner.c optional uscanner dev/usb/if_aue.c optional aue dev/usb/if_cue.c optional cue dev/usb/if_kue.c optional kue isa_if.o optional isa \ dependency "isa_if.c isa_if.h" \ compile-with "${NORMAL_C}" \ no-implicit-rule local isa_if.c optional isa \ dependency "$S/kern/makeops.pl $S/isa/isa_if.m" \ compile-with "perl5 $S/kern/makeops.pl -c $S/isa/isa_if.m" \ no-obj no-implicit-rule before-depend local \ clean "isa_if.c" isa_if.h optional isa \ dependency "$S/kern/makeops.pl $S/isa/isa_if.m" \ compile-with "perl5 $S/kern/makeops.pl -h $S/isa/isa_if.m" \ no-obj no-implicit-rule before-depend \ clean "isa_if.h" isa/isa_common.c optional isa isa/isahint.c optional isa isa/joy.c optional joy isa/pnp.c optional isa isa/pnpparse.c optional isa +# dev/sound/isa/ad1816.c optional pcm isa dev/sound/isa/es1888.c optional pcm isa dev/sound/isa/ess.c optional pcm isa dev/sound/isa/gusc.c optional gusc isa dev/sound/isa/gusc.c optional pcm isa dev/sound/isa/mss.c optional pcm isa -dev/sound/isa/sb.c optional pcm isa +dev/sound/isa/sb16.c optional pcm isa +dev/sound/isa/sb8.c optional pcm isa dev/sound/isa/sbc.c optional pcm isa dev/sound/isa/sbc.c optional sbc isa -#dev/sound/pci/aureal.c optional pcm pci +# dev/sound/pci/csa.c optional csa pci dev/sound/pci/csa.c optional pcm pci dev/sound/pci/csapcm.c optional pcm pci dev/sound/pci/ds1.c optional pcm pci dev/sound/pci/emu10k1.c optional pcm pci dev/sound/pci/es137x.c optional pcm pci dev/sound/pci/fm801.c optional pcm pci dev/sound/pci/maestro.c optional pcm pci dev/sound/pci/neomagic.c optional pcm pci dev/sound/pci/solo.c optional pcm pci dev/sound/pci/t4dwave.c optional pcm pci dev/sound/pci/via82c686.c optional pcm pci +# +ac97_if.o optional pcm \ + dependency "ac97_if.c ac97_if.h" \ + compile-with "${NORMAL_C}" \ + no-implicit-rule local +ac97_if.c optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/ac97_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -c $S/dev/sound/pcm/ac97_if.m" \ + no-obj no-implicit-rule before-depend local \ + clean "ac97_if.c" +ac97_if.h optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/ac97_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -h $S/dev/sound/pcm/ac97_if.m" \ + no-obj no-implicit-rule before-depend \ + clean "ac97_if.h" + +channel_if.o optional pcm \ + dependency "channel_if.c channel_if.h" \ + compile-with "${NORMAL_C}" \ + no-implicit-rule local +channel_if.c optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/channel_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -c $S/dev/sound/pcm/channel_if.m" \ + no-obj no-implicit-rule before-depend local \ + clean "channel_if.c" +channel_if.h optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/channel_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -h $S/dev/sound/pcm/channel_if.m" \ + no-obj no-implicit-rule before-depend \ + clean "channel_if.h" + +feeder_if.o optional pcm \ + dependency "feeder_if.c feeder_if.h" \ + compile-with "${NORMAL_C}" \ + no-implicit-rule local +feeder_if.c optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/feeder_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -c $S/dev/sound/pcm/feeder_if.m" \ + no-obj no-implicit-rule before-depend local \ + clean "feeder_if.c" +feeder_if.h optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/feeder_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -h $S/dev/sound/pcm/feeder_if.m" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_if.h" + +mixer_if.o optional pcm \ + dependency "mixer_if.c mixer_if.h" \ + compile-with "${NORMAL_C}" \ + no-implicit-rule local +mixer_if.c optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/mixer_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -c $S/dev/sound/pcm/mixer_if.m" \ + no-obj no-implicit-rule before-depend local \ + clean "mixer_if.c" +mixer_if.h optional pcm \ + dependency "$S/kern/makeops.pl $S/dev/sound/pcm/mixer_if.m" \ + compile-with "perl5 $S/kern/makeops.pl -h $S/dev/sound/pcm/mixer_if.m" \ + no-obj no-implicit-rule before-depend \ + clean "mixer_if.h" + +# dev/sound/pcm/ac97.c optional pcm +dev/sound/pcm/buffer.c optional pcm dev/sound/pcm/channel.c optional pcm dev/sound/pcm/dsp.c optional pcm dev/sound/pcm/fake.c optional pcm dev/sound/pcm/feeder.c optional pcm dev/sound/pcm/feeder_fmt.c optional pcm dev/sound/pcm/mixer.c optional pcm dev/sound/pcm/sound.c optional pcm # # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures, e.g., # libkern/divdi3.c is needed by i386 but not alpha. Also, some of these # routines may be optimized for a particular platform. In either case, # the file should be moved to /conf/files. from here. # libkern/arc4random.c standard libkern/bcd.c standard libkern/index.c standard libkern/inet_ntoa.c standard libkern/mcount.c optional profiling-routine libkern/qsort.c standard libkern/random.c standard libkern/rindex.c standard libkern/scanc.c standard libkern/skpc.c standard libkern/strcat.c standard libkern/strcmp.c standard libkern/strcpy.c standard libkern/strlen.c standard libkern/strncmp.c standard libkern/strncpy.c standard libkern/strtol.c standard libkern/strtoq.c standard libkern/strtoul.c standard libkern/strtouq.c standard Index: stable/4/sys/conf/kmod.mk =================================================================== --- stable/4/sys/conf/kmod.mk (revision 71948) +++ stable/4/sys/conf/kmod.mk (revision 71949) @@ -1,313 +1,316 @@ # From: @(#)bsd.prog.mk 5.26 (Berkeley) 6/25/91 # $FreeBSD$ # # The include file handles installing Kernel Loadable Device # drivers (KLD's). # # # +++ variables +++ # # CLEANFILES Additional files to remove for the clean and cleandir targets. # # DISTRIBUTION Name of distribution. [bin] # # KMOD The name of the kernel module to build. # # KMODDIR Base path for kernel modules (see kld(4)). [/modules] # # KMODOWN KLD owner. [${BINOWN}] # # KMODGRP KLD group. [${BINGRP}] # # KMODMODE KLD mode. [${BINMODE}] # # LINKS The list of KLD links; should be full pathnames, the # linked-to file coming first, followed by the linked # file. The files are hard-linked. For example, to link # /modules/master and /modules/meister, use: # # LINKS= /modules/master /modules/meister # # KMODLOAD Command to load a kernel module [/sbin/kldload] # # KMODUNLOAD Command to unload a kernel module [/sbin/kldunload] # # NOMAN KLD does not have a manual page if set. # # PROG The name of the kernel module to build. # If not supplied, ${KMOD}.o is used. # # SRCS List of source files # # KMODDEPS List of modules which this one is dependant on # # SUBDIR A list of subdirectories that should be built as well. # Each of the targets will execute the same target in the # subdirectories. # # SYMLINKS Same as LINKS, except it creates symlinks and the # linked-to pathname may be relative. # # DESTDIR, DISTDIR are set by other Makefiles (e.g. bsd.own.mk) # # MFILES Optionally a list of interfaces used by the module. # This file contains a default list of interfaces. # # +++ targets +++ # # distribute: # This is a variant of install, which will # put the stuff into the right "distribution". # # install: # install the program and its manual pages; if the Makefile # does not itself define the target install, the targets # beforeinstall and afterinstall may also be used to cause # actions immediately before and after the install target # is executed. # # load: # Load KLD. # # unload: # Unload KLD. # # bsd.obj.mk: clean, cleandir and obj # bsd.dep.mk: cleandepend, depend and tags # bsd.man.mk: maninstall # KMODLOAD?= /sbin/kldload KMODUNLOAD?= /sbin/kldunload .if !target(__initialized__) __initialized__: .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" .endif .endif .SUFFIXES: .out .o .c .cc .cxx .C .y .l .s .S CFLAGS+= ${COPTS} -D_KERNEL ${CWARNFLAGS} CFLAGS+= -DKLD_MODULE # Don't use any standard or source-relative include directories. # Since -nostdinc will annull any previous -I paths, we repeat all # such paths after -nostdinc. It doesn't seem to be possible to # add to the front of `make' variable. _ICFLAGS:= ${CFLAGS:M-I*} CFLAGS+= -nostdinc -I- ${_ICFLAGS} # Add -I paths for system headers. Individual KLD makefiles don't # need any -I paths for this. Similar defaults for .PATH can't be # set because there are no standard paths for non-headers. CFLAGS+= -I. -I@ # Add a -I path to standard headers like . Use a relative # path to src/include if possible. If the @ symlink hasn't been built # yet, then we can't tell if the relative path exists. Add both the # potential relative path and an absolute path in that case. .if exists(@) .if exists(@/../include) CFLAGS+= -I@/../include .else CFLAGS+= -I${DESTDIR}/usr/include .endif .else # !@ CFLAGS+= -I@/../include -I${DESTDIR}/usr/include .endif # @ CFLAGS+= ${DEBUG_FLAGS} .if ${OBJFORMAT} == elf CLEANFILES+= setdef0.c setdef1.c setdefs.h CLEANFILES+= setdef0.o setdef1.o .endif OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if !defined(PROG) PROG= ${KMOD}.ko .endif ${PROG}: ${KMOD}.kld ${KMODDEPS} .if ${OBJFORMAT} == elf gensetdefs ${KMOD}.kld ${CC} ${CFLAGS} -c setdef0.c ${CC} ${CFLAGS} -c setdef1.c ${LD} -Bshareable ${LDFLAGS} -o ${.TARGET} setdef0.o ${KMOD}.kld setdef1.o ${KMODDEPS} .else ${LD} -Bshareable ${LDFLAGS} -o ${.TARGET} ${KMOD}.kld ${KMODDEPS} .endif .if defined(KMODDEPS) .for dep in ${KMODDEPS} CLEANFILES+= ${dep} __${dep}_hack_dep.c ${dep}: touch __${dep}_hack_dep.c ${CC} -shared ${CFLAGS} -o ${dep} __${dep}_hack_dep.c .endfor .endif ${KMOD}.kld: ${OBJS} ${LD} ${LDFLAGS} -r -o ${.TARGET} ${OBJS} .if !defined(NOMAN) .include .if !defined(_MANPAGES) || empty(_MANPAGES) MAN1= ${KMOD}.4 .endif .elif !target(maninstall) maninstall: _SUBDIR all-man: .endif _ILINKS=@ machine .MAIN: all all: objwarn ${PROG} all-man _SUBDIR beforedepend ${OBJS}: ${_ILINKS} # Search for kernel source tree in standard places. .for _dir in ${.CURDIR}/../.. ${.CURDIR}/../../.. /sys /usr/src/sys .if !defined(SYSDIR) && exists(${_dir}/kern/) SYSDIR= ${_dir} .endif .endfor .if !defined(SYSDIR) || !exists(${SYSDIR}/kern/) .error "can't find kernel source tree" .endif ${_ILINKS}: @case ${.TARGET} in \ machine) \ path=${SYSDIR}/${MACHINE_ARCH}/include ;; \ @) \ path=${SYSDIR} ;; \ esac ; \ path=`(cd $$path && /bin/pwd)` ; \ ${ECHO} ${.TARGET} "->" $$path ; \ ln -s $$path ${.TARGET} CLEANFILES+= ${PROG} ${KMOD}.kld ${OBJS} ${_ILINKS} symb.tmp tmp.o .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !target(afterinstall) afterinstall: .endif _INSTALLFLAGS:= ${INSTALLFLAGS} .for ie in ${INSTALLFLAGS_EDIT} _INSTALLFLAGS:= ${_INSTALLFLAGS${ie}} .endfor realinstall: _SUBDIR ${INSTALL} ${COPY} -o ${KMODOWN} -g ${KMODGRP} -m ${KMODMODE} \ ${_INSTALLFLAGS} ${PROG} ${DESTDIR}${KMODDIR} .if defined(LINKS) && !empty(LINKS) @set ${LINKS}; \ while test $$# -ge 2; do \ l=${DESTDIR}$$1; \ shift; \ t=${DESTDIR}$$1; \ shift; \ ${ECHO} $$t -\> $$l; \ ln -f $$l $$t; \ done; true .endif .if defined(SYMLINKS) && !empty(SYMLINKS) @set ${SYMLINKS}; \ while test $$# -ge 2; do \ l=$$1; \ shift; \ t=${DESTDIR}$$1; \ shift; \ ${ECHO} $$t -\> $$l; \ ln -fs $$l $$t; \ done; true .endif install: afterinstall _SUBDIR .if !defined(NOMAN) afterinstall: realinstall maninstall .else afterinstall: realinstall .endif realinstall: beforeinstall .endif DISTRIBUTION?= bin .if !target(distribute) distribute: _SUBDIR .for dist in ${DISTRIBUTION} cd ${.CURDIR} ; $(MAKE) install DESTDIR=${DISTDIR}/${dist} SHARED=copies .endfor .endif .if !target(load) load: ${PROG} ${KMODLOAD} -v ./${KMOD}.ko .endif .if !target(unload) unload: ${KMODUNLOAD} -v ${KMOD} .endif .for _src in ${SRCS:Mopt_*.h} CLEANFILES+= ${_src} .if !target(${_src}) ${_src}: touch ${.TARGET} .endif .endfor MFILES?= kern/bus_if.m kern/device_if.m dev/iicbus/iicbb_if.m \ dev/iicbus/iicbus_if.m isa/isa_if.m dev/mii/miibus_if.m \ dev/pccard/card_if.m dev/pccard/power_if.m pci/pci_if.m \ - dev/ppbus/ppbus_if.m dev/smbus/smbus_if.m dev/usb/usb_if.m + dev/ppbus/ppbus_if.m dev/smbus/smbus_if.m dev/usb/usb_if.m \ + dev/sound/pcm/ac97_if.m dev/sound/pcm/channel_if.m \ + dev/sound/pcm/feeder_if.m dev/sound/pcm/mixer_if.m \ + pci/agp_if.m .for _srcsrc in ${MFILES} .for _ext in c h .for _src in ${SRCS:M${_srcsrc:T:R}.${_ext}} CLEANFILES+= ${_src} .if !target(${_src}) ${_src}: @ .if exists(@) ${_src}: @/kern/makeops.pl @/${_srcsrc} .endif perl @/kern/makeops.pl -${_ext} @/${_srcsrc} .endif .endfor # _src .endfor # _ext .endfor # _srcsrc .for _ext in c h .if ${SRCS:Mvnode_if.${_ext}} != "" CLEANFILES+= vnode_if.${_ext} vnode_if.${_ext}: @ .if exists(@) vnode_if.${_ext}: @/kern/vnode_if.pl @/kern/vnode_if.src .endif perl @/kern/vnode_if.pl -${_ext} @/kern/vnode_if.src .endif .endfor regress: .include .if !exists(${DEPENDFILE}) ${OBJS}: ${SRCS:M*.h} .endif .include .include Index: stable/4/sys/dev/sound/isa/ad1816.c =================================================================== --- stable/4/sys/dev/sound/isa/ad1816.c (revision 71948) +++ stable/4/sys/dev/sound/isa/ad1816.c (revision 71949) @@ -1,664 +1,654 @@ /* * Copyright (c) 1999 Cameron Grant * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include +#include "mixer_if.h" + struct ad1816_info; struct ad1816_chinfo { struct ad1816_info *parent; pcm_channel *channel; snd_dbuf *buffer; - int dir; + int dir, blksz; }; struct ad1816_info { struct resource *io_base; /* primary I/O address for the board */ int io_rid; struct resource *irq; int irq_rid; struct resource *drq1; /* play */ int drq1_rid; struct resource *drq2; /* rec */ int drq2_rid; void *ih; bus_dma_tag_t parent_dmat; struct ad1816_chinfo pch, rch; }; static driver_intr_t ad1816_intr; static int ad1816_probe(device_t dev); static int ad1816_attach(device_t dev); /* IO primitives */ static int ad1816_wait_init(struct ad1816_info *ad1816, int x); static u_short ad1816_read(struct ad1816_info *ad1816, u_int reg); static void ad1816_write(struct ad1816_info *ad1816, u_int reg, u_short data); -static int ad1816mix_init(snd_mixer *m); -static int ad1816mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int ad1816mix_setrecsrc(snd_mixer *m, u_int32_t src); -static snd_mixer ad1816_mixer = { - "ad1816 mixer", - ad1816mix_init, - NULL, - ad1816mix_set, - ad1816mix_setrecsrc, -}; - static devclass_t pcm_devclass; -/* channel interface */ -static void *ad1816chan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int ad1816chan_setdir(void *data, int dir); -static int ad1816chan_setformat(void *data, u_int32_t format); -static int ad1816chan_setspeed(void *data, u_int32_t speed); -static int ad1816chan_setblocksize(void *data, u_int32_t blocksize); -static int ad1816chan_trigger(void *data, int go); -static int ad1816chan_getptr(void *data); -static pcmchan_caps *ad1816chan_getcaps(void *data); - static u_int32_t ad1816_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_MU_LAW, AFMT_STEREO | AFMT_MU_LAW, AFMT_A_LAW, AFMT_STEREO | AFMT_A_LAW, 0 }; static pcmchan_caps ad1816_caps = {4000, 55200, ad1816_fmt, 0}; -static pcm_channel ad1816_chantemplate = { - ad1816chan_init, - ad1816chan_setdir, - ad1816chan_setformat, - ad1816chan_setspeed, - ad1816chan_setblocksize, - ad1816chan_trigger, - ad1816chan_getptr, - ad1816chan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - #define AD1816_MUTE 31 /* value for mute */ static int port_rd(struct resource *port, int off) { if (port) return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off); else return -1; } static void port_wr(struct resource *port, int off, u_int8_t data) { if (port) return bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data); } static int io_rd(struct ad1816_info *ad1816, int reg) { return port_rd(ad1816->io_base, reg); } static void io_wr(struct ad1816_info *ad1816, int reg, u_int8_t data) { return port_wr(ad1816->io_base, reg, data); } static void ad1816_intr(void *arg) { struct ad1816_info *ad1816 = (struct ad1816_info *)arg; unsigned char c, served = 0; /* get interupt status */ c = io_rd(ad1816, AD1816_INT); /* check for stray interupts */ if (c & ~(AD1816_INTRCI | AD1816_INTRPI)) { printf("pcm: stray int (%x)\n", c); c &= AD1816_INTRCI | AD1816_INTRPI; } /* check for capture interupt */ - if (ad1816->rch.buffer->dl && (c & AD1816_INTRCI)) { + if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) { chn_intr(ad1816->rch.channel); served |= AD1816_INTRCI; /* cp served */ } /* check for playback interupt */ - if (ad1816->pch.buffer->dl && (c & AD1816_INTRPI)) { + if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) { chn_intr(ad1816->pch.channel); served |= AD1816_INTRPI; /* pb served */ } if (served == 0) { /* this probably means this is not a (working) ad1816 chip, */ /* or an error in dma handling */ printf("pcm: int without reason (%x)\n", c); c = 0; } else c &= ~served; io_wr(ad1816, AD1816_INT, c); c = io_rd(ad1816, AD1816_INT); if (c != 0) printf("pcm: int clear failed (%x)\n", c); } static int ad1816_wait_init(struct ad1816_info *ad1816, int x) { int n = 0; /* to shut up the compiler... */ for (; x--;) if ((n = (io_rd(ad1816, AD1816_ALE) & AD1816_BUSY)) == 0) DELAY(10); else return n; printf("ad1816_wait_init failed 0x%02x.\n", n); return -1; } static unsigned short ad1816_read(struct ad1816_info *ad1816, unsigned int reg) { int flags; u_short x = 0; /* we don't want to be blocked here */ flags = spltty(); if (ad1816_wait_init(ad1816, 100) == -1) return 0; io_wr(ad1816, AD1816_ALE, 0); io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); if (ad1816_wait_init(ad1816, 100) == -1) return 0; x = (io_rd(ad1816, AD1816_HIGH) << 8) | io_rd(ad1816, AD1816_LOW); splx(flags); return x; } static void ad1816_write(struct ad1816_info *ad1816, unsigned int reg, unsigned short data) { int flags; flags = spltty(); if (ad1816_wait_init(ad1816, 100) == -1) return; io_wr(ad1816, AD1816_ALE, (reg & AD1816_ALEMASK)); io_wr(ad1816, AD1816_LOW, (data & 0x000000ff)); io_wr(ad1816, AD1816_HIGH, (data & 0x0000ff00) >> 8); splx(flags); } +/* -------------------------------------------------------------------- */ + static int ad1816mix_init(snd_mixer *m) { mix_setdevs(m, AD1816_MIXER_DEVICES); mix_setrecdevs(m, AD1816_REC_DEVICES); return 0; } static int ad1816mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct ad1816_info *ad1816 = mix_getdevinfo(m); u_short reg = 0; /* Scale volumes */ left = AD1816_MUTE - (AD1816_MUTE * left) / 100; right = AD1816_MUTE - (AD1816_MUTE * right) / 100; reg = (left << 8) | right; /* do channel selective muting if volume is zero */ if (left == AD1816_MUTE) reg |= 0x8000; if (right == AD1816_MUTE) reg |= 0x0080; switch (dev) { case SOUND_MIXER_VOLUME: /* Register 14 master volume */ ad1816_write(ad1816, 14, reg); break; case SOUND_MIXER_CD: /* Register 15 cd */ case SOUND_MIXER_LINE1: ad1816_write(ad1816, 15, reg); break; case SOUND_MIXER_SYNTH: /* Register 16 synth */ ad1816_write(ad1816, 16, reg); break; case SOUND_MIXER_PCM: /* Register 4 pcm */ ad1816_write(ad1816, 4, reg); break; case SOUND_MIXER_LINE: case SOUND_MIXER_LINE3: /* Register 18 line in */ ad1816_write(ad1816, 18, reg); break; case SOUND_MIXER_MIC: /* Register 19 mic volume */ ad1816_write(ad1816, 19, reg & ~0xff); /* mic is mono */ break; case SOUND_MIXER_IGAIN: /* and now to something completely different ... */ ad1816_write(ad1816, 20, ((ad1816_read(ad1816, 20) & ~0x0f0f) | (((AD1816_MUTE - left) / 2) << 8) /* four bits of adc gain */ | ((AD1816_MUTE - right) / 2))); break; default: printf("ad1816_mixer_set(): unknown device.\n"); break; } left = ((AD1816_MUTE - left) * 100) / AD1816_MUTE; right = ((AD1816_MUTE - right) * 100) / AD1816_MUTE; return left | (right << 8); } static int ad1816mix_setrecsrc(snd_mixer *m, u_int32_t src) { struct ad1816_info *ad1816 = mix_getdevinfo(m); int dev; switch (src) { case SOUND_MASK_LINE: case SOUND_MASK_LINE3: dev = 0x00; break; case SOUND_MASK_CD: case SOUND_MASK_LINE1: dev = 0x20; break; case SOUND_MASK_MIC: default: dev = 0x50; src = SOUND_MASK_MIC; } dev |= dev << 8; ad1816_write(ad1816, 20, (ad1816_read(ad1816, 20) & ~0x7070) | dev); return src; } +static kobj_method_t ad1816mixer_methods[] = { + KOBJMETHOD(mixer_init, ad1816mix_init), + KOBJMETHOD(mixer_set, ad1816mix_set), + KOBJMETHOD(mixer_setrecsrc, ad1816mix_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(ad1816mixer); + +/* -------------------------------------------------------------------- */ /* channel interface */ static void * -ad1816chan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +ad1816chan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct ad1816_info *ad1816 = devinfo; struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch; ch->parent = ad1816; ch->channel = c; ch->buffer = b; - ch->buffer->bufsize = DSP_BUFFSIZE; - if (chn_allocbuf(ch->buffer, ad1816->parent_dmat) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, DSP_BUFFSIZE) == -1) return NULL; return ch; } static int -ad1816chan_setdir(void *data, int dir) +ad1816chan_setdir(kobj_t obj, void *data, int dir) { struct ad1816_chinfo *ch = data; struct ad1816_info *ad1816 = ch->parent; - ch->buffer->chan = rman_get_start((dir == PCMDIR_PLAY)? - ad1816->drq1 : ad1816->drq2); + sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); ch->dir = dir; return 0; } static int -ad1816chan_setformat(void *data, u_int32_t format) +ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) { struct ad1816_chinfo *ch = data; struct ad1816_info *ad1816 = ch->parent; int fmt = AD1816_U8, reg; if (ch->dir == PCMDIR_PLAY) { reg = AD1816_PLAY; ad1816_write(ad1816, 8, 0x0000); /* reset base and current counter */ ad1816_write(ad1816, 9, 0x0000); /* for playback and capture */ } else { reg = AD1816_CAPT; ad1816_write(ad1816, 10, 0x0000); ad1816_write(ad1816, 11, 0x0000); } switch (format & ~AFMT_STEREO) { case AFMT_A_LAW: fmt = AD1816_ALAW; break; case AFMT_MU_LAW: fmt = AD1816_MULAW; break; case AFMT_S16_LE: fmt = AD1816_S16LE; break; case AFMT_S16_BE: fmt = AD1816_S16BE; break; case AFMT_U8: fmt = AD1816_U8; break; } if (format & AFMT_STEREO) fmt |= AD1816_STEREO; io_wr(ad1816, reg, fmt); return format; } static int -ad1816chan_setspeed(void *data, u_int32_t speed) +ad1816chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct ad1816_chinfo *ch = data; struct ad1816_info *ad1816 = ch->parent; RANGE(speed, 4000, 55200); ad1816_write(ad1816, (ch->dir == PCMDIR_PLAY)? 2 : 3, speed); return speed; } static int -ad1816chan_setblocksize(void *data, u_int32_t blocksize) +ad1816chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { - return blocksize; + struct ad1816_chinfo *ch = data; + + ch->blksz = blocksize; + return ch->blksz; } static int -ad1816chan_trigger(void *data, int go) +ad1816chan_trigger(kobj_t obj, void *data, int go) { struct ad1816_chinfo *ch = data; struct ad1816_info *ad1816 = ch->parent; int wr, reg; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - buf_isadma(ch->buffer, go); + sndbuf_isadma(ch->buffer, go); wr = (ch->dir == PCMDIR_PLAY); reg = wr? AD1816_PLAY : AD1816_CAPT; switch (go) { case PCMTRIG_START: /* start only if not already running */ if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) { - int cnt = ((ch->buffer->dl) >> 2) - 1; + int cnt = ((ch->blksz) >> 2) - 1; ad1816_write(ad1816, wr? 8 : 10, cnt); /* count */ ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) | (wr? 0x8000 : 0x4000)); /* enable int */ /* enable playback */ io_wr(ad1816, reg, io_rd(ad1816, reg) | AD1816_ENABLE); if (!(io_rd(ad1816, reg) & AD1816_ENABLE)) printf("ad1816: failed to start %s DMA!\n", wr? "play" : "rec"); } break; case PCMTRIG_STOP: case PCMTRIG_ABORT: /* XXX check this... */ /* we don't test here if it is running... */ if (wr) { ad1816_write(ad1816, 1, ad1816_read(ad1816, 1) & ~(wr? 0x8000 : 0x4000)); /* disable int */ io_wr(ad1816, reg, io_rd(ad1816, reg) & ~AD1816_ENABLE); /* disable playback */ if (io_rd(ad1816, reg) & AD1816_ENABLE) printf("ad1816: failed to stop %s DMA!\n", wr? "play" : "rec"); ad1816_write(ad1816, wr? 8 : 10, 0); /* reset base cnt */ ad1816_write(ad1816, wr? 9 : 11, 0); /* reset cur cnt */ } break; } return 0; } static int -ad1816chan_getptr(void *data) +ad1816chan_getptr(kobj_t obj, void *data) { struct ad1816_chinfo *ch = data; - return buf_isadmaptr(ch->buffer); + return sndbuf_isadmaptr(ch->buffer); } static pcmchan_caps * -ad1816chan_getcaps(void *data) +ad1816chan_getcaps(kobj_t obj, void *data) { return &ad1816_caps; } +static kobj_method_t ad1816chan_methods[] = { + KOBJMETHOD(channel_init, ad1816chan_init), + KOBJMETHOD(channel_setdir, ad1816chan_setdir), + KOBJMETHOD(channel_setformat, ad1816chan_setformat), + KOBJMETHOD(channel_setspeed, ad1816chan_setspeed), + KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize), + KOBJMETHOD(channel_trigger, ad1816chan_trigger), + KOBJMETHOD(channel_getptr, ad1816chan_getptr), + KOBJMETHOD(channel_getcaps, ad1816chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(ad1816chan); + +/* -------------------------------------------------------------------- */ + static void ad1816_release_resources(struct ad1816_info *ad1816, device_t dev) { if (ad1816->irq) { if (ad1816->ih) bus_teardown_intr(dev, ad1816->irq, ad1816->ih); bus_release_resource(dev, SYS_RES_IRQ, ad1816->irq_rid, ad1816->irq); ad1816->irq = 0; } if (ad1816->drq1) { bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq1_rid, ad1816->drq1); ad1816->drq1 = 0; } if (ad1816->drq2) { bus_release_resource(dev, SYS_RES_DRQ, ad1816->drq2_rid, ad1816->drq2); ad1816->drq2 = 0; } if (ad1816->io_base) { bus_release_resource(dev, SYS_RES_IOPORT, ad1816->io_rid, ad1816->io_base); ad1816->io_base = 0; } if (ad1816->parent_dmat) { bus_dma_tag_destroy(ad1816->parent_dmat); ad1816->parent_dmat = 0; } free(ad1816, M_DEVBUF); } static int ad1816_alloc_resources(struct ad1816_info *ad1816, device_t dev) { int ok = 1, pdma, rdma; if (!ad1816->io_base) ad1816->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &ad1816->io_rid, 0, ~0, 1, RF_ACTIVE); if (!ad1816->irq) ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid, 0, ~0, 1, RF_ACTIVE); if (!ad1816->drq1) ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid, 0, ~0, 1, RF_ACTIVE); if (!ad1816->drq2) ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid, 0, ~0, 1, RF_ACTIVE); if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0; if (ok) { pdma = rman_get_start(ad1816->drq1); isa_dma_acquire(pdma); isa_dmainit(pdma, DSP_BUFFSIZE); if (ad1816->drq2) { rdma = rman_get_start(ad1816->drq2); isa_dma_acquire(rdma); isa_dmainit(rdma, DSP_BUFFSIZE); } else rdma = pdma; if (pdma == rdma) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); } return ok; } static int ad1816_init(struct ad1816_info *ad1816, device_t dev) { ad1816_write(ad1816, 1, 0x2); /* disable interrupts */ ad1816_write(ad1816, 32, 0x90F0); /* SoundSys Mode, split fmt */ ad1816_write(ad1816, 5, 0x8080); /* FM volume mute */ ad1816_write(ad1816, 6, 0x8080); /* I2S1 volume mute */ ad1816_write(ad1816, 7, 0x8080); /* I2S0 volume mute */ ad1816_write(ad1816, 17, 0x8888); /* VID Volume mute */ ad1816_write(ad1816, 20, 0x5050); /* recsrc mic, agc off */ /* adc gain is set to 0 */ return 0; } static int ad1816_probe(device_t dev) { char *s = NULL; u_int32_t logical_id = isa_get_logicalid(dev); switch (logical_id) { case 0x80719304: /* ADS7180 */ s = "AD1816"; break; } if (s) { device_set_desc(dev, s); return 0; } return ENXIO; } static int ad1816_attach(device_t dev) { struct ad1816_info *ad1816; char status[SND_STATUSLEN]; ad1816 = (struct ad1816_info *)malloc(sizeof *ad1816, M_DEVBUF, M_NOWAIT); if (!ad1816) return ENXIO; bzero(ad1816, sizeof *ad1816); ad1816->io_rid = 2; ad1816->irq_rid = 0; ad1816->drq1_rid = 0; ad1816->drq2_rid = 1; if (!ad1816_alloc_resources(ad1816, dev)) goto no; ad1816_init(ad1816, dev); - mixer_init(dev, &ad1816_mixer, ad1816); + if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; + bus_setup_intr(dev, ad1816->irq, INTR_TYPE_TTY, ad1816_intr, ad1816, &ad1816->ih); if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/DSP_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &ad1816->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld", rman_get_start(ad1816->io_base), rman_get_start(ad1816->irq), rman_get_start(ad1816->drq1)); if (ad1816->drq2) snprintf(status + strlen(status), SND_STATUSLEN - strlen(status), ":%ld", rman_get_start(ad1816->drq2)); if (pcm_register(dev, ad1816, 1, 1)) goto no; - pcm_addchan(dev, PCMDIR_REC, &ad1816_chantemplate, ad1816); - pcm_addchan(dev, PCMDIR_PLAY, &ad1816_chantemplate, ad1816); + pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816); + pcm_addchan(dev, PCMDIR_PLAY, &ad1816chan_class, ad1816); pcm_setstatus(dev, status); return 0; no: ad1816_release_resources(ad1816, dev); return ENXIO; } static int ad1816_detach(device_t dev) { int r; struct ad1816_info *ad1816; r = pcm_unregister(dev); if (r) return r; ad1816 = pcm_getdevinfo(dev); ad1816_release_resources(ad1816, dev); return 0; } static device_method_t ad1816_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ad1816_probe), DEVMETHOD(device_attach, ad1816_attach), DEVMETHOD(device_detach, ad1816_detach), { 0, 0 } }; static driver_t ad1816_driver = { "pcm", ad1816_methods, sizeof(snddev_info), }; DRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_ad1816, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_ad1816, 1); Index: stable/4/sys/dev/sound/isa/ess.c =================================================================== --- stable/4/sys/dev/sound/isa/ess.c (revision 71948) +++ stable/4/sys/dev/sound/isa/ess.c (revision 71949) @@ -1,972 +1,964 @@ /* * Copyright (c) 1999 Cameron Grant * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include +#include "mixer_if.h" + #define ESS_BUFFSIZE (4096) #define ABS(x) (((x) < 0)? -(x) : (x)) /* audio2 never generates irqs and sounds very noisy */ #undef ESS18XX_DUPLEX /* more accurate clocks and split audio1/audio2 rates */ #define ESS18XX_NEWSPEED -/* channel interface for ESS */ -static void *esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int esschan_setformat(void *data, u_int32_t format); -static int esschan_setspeed(void *data, u_int32_t speed); -static int esschan_setblocksize(void *data, u_int32_t blocksize); -static int esschan_trigger(void *data, int go); -static int esschan_getptr(void *data); -static pcmchan_caps *esschan_getcaps(void *data); - static u_int32_t ess_pfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static pcmchan_caps ess_playcaps = {5000, 49000, ess_pfmt, 0}; static u_int32_t ess_rfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static pcmchan_caps ess_reccaps = {5000, 49000, ess_rfmt, 0}; -static pcm_channel ess_chantemplate = { - esschan_init, - NULL, /* setdir */ - esschan_setformat, - esschan_setspeed, - esschan_setblocksize, - esschan_trigger, - esschan_getptr, - esschan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - struct ess_info; struct ess_chinfo { struct ess_info *parent; pcm_channel *channel; snd_dbuf *buffer; - int dir, hwch, stopping; - u_int32_t fmt, spd; + int dir, hwch, stopping, run; + u_int32_t fmt, spd, blksz; }; struct ess_info { struct resource *io_base; /* I/O address for the board */ struct resource *irq; struct resource *drq1; struct resource *drq2; void *ih; bus_dma_tag_t parent_dmat; int type, duplex:1, newspeed:1; u_long bd_flags; /* board-specific flags */ struct ess_chinfo pch, rch; }; static int ess_rd(struct ess_info *sc, int reg); static void ess_wr(struct ess_info *sc, int reg, u_int8_t val); static int ess_dspready(struct ess_info *sc); static int ess_cmd(struct ess_info *sc, u_char val); static int ess_cmd1(struct ess_info *sc, u_char cmd, int val); static int ess_get_byte(struct ess_info *sc); static void ess_setmixer(struct ess_info *sc, u_int port, u_int value); static int ess_getmixer(struct ess_info *sc, u_int port); static int ess_reset_dsp(struct ess_info *sc); static int ess_write(struct ess_info *sc, u_char reg, int val); static int ess_read(struct ess_info *sc, u_char reg); static void ess_intr(void *arg); static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len); static int ess_start(struct ess_chinfo *ch); static int ess_stop(struct ess_chinfo *ch); -static int essmix_init(snd_mixer *m); -static int essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int essmix_setrecsrc(snd_mixer *m, u_int32_t src); - -static snd_mixer ess_mixer = { - "ESS mixer", - essmix_init, - NULL, - essmix_set, - essmix_setrecsrc, -}; - static devclass_t pcm_devclass; /* * Common code for the midi and pcm functions * * ess_cmd write a single byte to the CMD port. * ess_cmd1 write a CMD + 1 byte arg * ess_cmd2 write a CMD + 2 byte arg * ess_get_byte returns a single byte from the DSP data port * * ess_write is actually ess_cmd1 * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte */ static int port_rd(struct resource *port, int off) { return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off); } static void port_wr(struct resource *port, int off, u_int8_t data) { return bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data); } static int ess_rd(struct ess_info *sc, int reg) { return port_rd(sc->io_base, reg); } static void ess_wr(struct ess_info *sc, int reg, u_int8_t val) { port_wr(sc->io_base, reg, val); } static int ess_dspready(struct ess_info *sc) { return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0); } static int ess_dspwr(struct ess_info *sc, u_char val) { int i; for (i = 0; i < 1000; i++) { if (ess_dspready(sc)) { ess_wr(sc, SBDSP_CMD, val); return 1; } if (i > 10) DELAY((i > 100)? 1000 : 10); } printf("ess_dspwr(0x%02x) timed out.\n", val); return 0; } static int ess_cmd(struct ess_info *sc, u_char val) { #if 0 printf("ess_cmd: %x\n", val); #endif return ess_dspwr(sc, val); } static int ess_cmd1(struct ess_info *sc, u_char cmd, int val) { #if 0 printf("ess_cmd1: %x, %x\n", cmd, val); #endif if (ess_dspwr(sc, cmd)) { return ess_dspwr(sc, val & 0xff); } else return 0; } static void ess_setmixer(struct ess_info *sc, u_int port, u_int value) { u_long flags; DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);) flags = spltty(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff)); DELAY(10); splx(flags); } static int ess_getmixer(struct ess_info *sc, u_int port) { int val; u_long flags; flags = spltty(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); val = ess_rd(sc, SB_MIX_DATA); DELAY(10); splx(flags); return val; } static int ess_get_byte(struct ess_info *sc) { int i; for (i = 1000; i > 0; i--) { if (ess_rd(sc, DSP_DATA_AVAIL) & 0x80) return ess_rd(sc, DSP_READ); else DELAY(20); } return -1; } static int ess_write(struct ess_info *sc, u_char reg, int val) { return ess_cmd1(sc, reg, val); } static int ess_read(struct ess_info *sc, u_char reg) { return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1; } static int ess_reset_dsp(struct ess_info *sc) { ess_wr(sc, SBDSP_RST, 3); DELAY(100); ess_wr(sc, SBDSP_RST, 0); if (ess_get_byte(sc) != 0xAA) { DEB(printf("ess_reset_dsp 0x%lx failed\n", rman_get_start(d->io_base))); return ENXIO; /* Sorry */ } ess_cmd(sc, 0xc6); return 0; } static void ess_release_resources(struct ess_info *sc, device_t dev) { if (sc->irq) { if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); sc->irq = 0; } if (sc->drq1) { bus_release_resource(dev, SYS_RES_DRQ, 0, sc->drq1); sc->drq1 = 0; } if (sc->drq2) { bus_release_resource(dev, SYS_RES_DRQ, 1, sc->drq2); sc->drq2 = 0; } if (sc->io_base) { bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->io_base); sc->io_base = 0; } if (sc->parent_dmat) { bus_dma_tag_destroy(sc->parent_dmat); sc->parent_dmat = 0; } free(sc, M_DEVBUF); } static int ess_alloc_resources(struct ess_info *sc, device_t dev) { int rid; rid = 0; if (!sc->io_base) sc->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); rid = 0; if (!sc->irq) sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE); rid = 0; if (!sc->drq1) sc->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE); rid = 1; if (!sc->drq2) sc->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE); if (sc->io_base && sc->drq1 && sc->irq) { isa_dma_acquire(rman_get_start(sc->drq1)); isa_dmainit(rman_get_start(sc->drq1), ESS_BUFFSIZE); if (sc->drq2) { isa_dma_acquire(rman_get_start(sc->drq2)); isa_dmainit(rman_get_start(sc->drq2), ESS_BUFFSIZE); } return 0; } else return ENXIO; } -static int -ess_doattach(device_t dev, struct ess_info *sc) -{ - char status[SND_STATUSLEN], buf[64]; - int ver; - - if (ess_alloc_resources(sc, dev)) - goto no; - if (ess_reset_dsp(sc)) - goto no; - mixer_init(dev, &ess_mixer, sc); - - sc->duplex = 0; - sc->newspeed = 0; - ver = (ess_getmixer(sc, 0x40) << 8) | ess_rd(sc, SB_MIX_DATA); - snprintf(buf, sizeof buf, "ESS %x DSP", ver); - device_set_desc_copy(dev, buf); - if (bootverbose) - device_printf(dev, "ESS%x detected", ver); - - switch (ver) { - case 0x1869: - case 0x1879: -#ifdef ESS18XX_DUPLEX - sc->duplex = sc->drq2? 1 : 0; -#endif -#ifdef ESS18XX_NEWSPEED - sc->newspeed = 1; -#endif - break; - } - if (bootverbose) - printf("%s%s\n", sc->duplex? ", duplex" : "", - sc->newspeed? ", newspeed" : ""); - - if (sc->newspeed) - ess_setmixer(sc, 0x71, 0x22); - - bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &sc->ih); - if (!sc->duplex) - pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); - - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/ESS_BUFFSIZE, /*nsegments*/1, - /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { - device_printf(dev, "unable to create dma tag\n"); - goto no; - } - - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld", - rman_get_start(sc->io_base), rman_get_start(sc->irq), - rman_get_start(sc->drq1)); - if (sc->drq2) - snprintf(status + strlen(status), SND_STATUSLEN - strlen(status), - ":%ld", rman_get_start(sc->drq2)); - - if (pcm_register(dev, sc, 1, 1)) - goto no; - pcm_addchan(dev, PCMDIR_REC, &ess_chantemplate, sc); - pcm_addchan(dev, PCMDIR_PLAY, &ess_chantemplate, sc); - pcm_setstatus(dev, status); - - return 0; - -no: - ess_release_resources(sc, dev); - return ENXIO; -} - -static int -ess_detach(device_t dev) -{ - int r; - struct ess_info *sc; - - r = pcm_unregister(dev); - if (r) - return r; - - sc = pcm_getdevinfo(dev); - ess_release_resources(sc, dev); - return 0; -} - static void ess_intr(void *arg) { struct ess_info *sc = (struct ess_info *)arg; int src, pirq, rirq; src = 0; if (ess_getmixer(sc, 0x7a) & 0x80) src |= 2; if (ess_rd(sc, 0x0c) & 0x01) src |= 1; pirq = (src & sc->pch.hwch)? 1 : 0; rirq = (src & sc->rch.hwch)? 1 : 0; if (pirq) { + if (sc->pch.run) + chn_intr(sc->pch.channel); if (sc->pch.stopping) { - buf_isadma(sc->pch.buffer, PCMTRIG_STOP); + sc->pch.run = 0; + sndbuf_isadma(sc->pch.buffer, PCMTRIG_STOP); sc->pch.stopping = 0; if (sc->pch.hwch == 1) ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03); } - chn_intr(sc->pch.channel); } if (rirq) { + if (sc->rch.run) + chn_intr(sc->rch.channel); if (sc->rch.stopping) { - buf_isadma(sc->rch.buffer, PCMTRIG_STOP); + sc->rch.run = 0; + sndbuf_isadma(sc->rch.buffer, PCMTRIG_STOP); sc->rch.stopping = 0; /* XXX: will this stop audio2? */ ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); } - chn_intr(sc->rch.channel); } if (src & 2) ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80); if (src & 1) ess_rd(sc, DSP_DATA_AVAIL); } /* utility functions for ESS */ static u_int8_t ess_calcspeed8(int *spd) { int speed = *spd; u_int32_t t; if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t; t = (256 - t) | 0x80; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t; t = 128 - t; } *spd = speed; return t & 0x000000ff; } static u_int8_t ess_calcspeed9(int *spd) { int speed, s0, s1, use0; u_int8_t t0, t1; /* rate = source / (256 - divisor) */ /* divisor = 256 - (source / rate) */ speed = *spd; t0 = 128 - (793800 / speed); s0 = 793800 / (128 - t0); t1 = 128 - (768000 / speed); s1 = 768000 / (128 - t1); t1 |= 0x80; use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0; *spd = use0? s0 : s1; return use0? t0 : t1; } static u_int8_t ess_calcfilter(int spd) { int cutoff; /* cutoff = 7160000 / (256 - divisor) */ /* divisor = 256 - (7160000 / cutoff) */ cutoff = (spd * 9 * 82) / 20; return (256 - (7160000 / cutoff)); } static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len) { int play = (dir == PCMDIR_PLAY)? 1 : 0; int b16 = (fmt & AFMT_16BIT)? 1 : 0; int stereo = (fmt & AFMT_STEREO)? 1 : 0; int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE)? 1 : 0; u_int8_t spdval, fmtval; spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd); len = -len; if (ch == 1) { KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad")); /* transfer length low */ ess_write(sc, 0xa4, len & 0x00ff); /* transfer length high */ ess_write(sc, 0xa5, (len & 0xff00) >> 8); /* autoinit, dma dir */ ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a)); /* mono/stereo */ ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02)); /* demand mode, 4 bytes/xfer */ ess_write(sc, 0xb9, 0x02); /* sample rate */ ess_write(sc, 0xa1, spdval); /* filter cutoff */ ess_write(sc, 0xa2, ess_calcfilter(spd)); /* setup dac/adc */ if (play) ess_write(sc, 0xb6, unsign? 0x80 : 0x00); /* mono, b16: signed, load signal */ ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20)); /* setup fifo */ ess_write(sc, 0xb7, 0x90 | (unsign? 0x00 : 0x20) | (b16? 0x04 : 0x00) | (stereo? 0x08 : 0x40)); /* irq control */ ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50); /* drq control */ ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50); } else if (ch == 2) { KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad")); /* transfer length low */ ess_setmixer(sc, 0x74, len & 0x00ff); /* transfer length high */ ess_setmixer(sc, 0x76, (len & 0xff00) >> 8); /* autoinit, 4 bytes/req */ ess_setmixer(sc, 0x78, 0x90); fmtval = b16 | (stereo << 1) | (unsign << 2); /* enable irq, set format */ ess_setmixer(sc, 0x7a, 0x40 | fmtval); if (sc->newspeed) { /* sample rate */ ess_setmixer(sc, 0x70, spdval); /* filter cutoff */ ess_setmixer(sc, 0x72, ess_calcfilter(spd)); } } return 0; } static int ess_start(struct ess_chinfo *ch) { struct ess_info *sc = ch->parent; int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; - ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->buffer->dl); + ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->blksz); ch->stopping = 0; if (ch->hwch == 1) ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01); else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03); if (play) ess_cmd(sc, DSP_CMD_SPKON); return 0; } static int ess_stop(struct ess_chinfo *ch) { struct ess_info *sc = ch->parent; int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; ch->stopping = 1; if (ch->hwch == 1) ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04); else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10); if (play) ess_cmd(sc, DSP_CMD_SPKOFF); return 0; } +/* -------------------------------------------------------------------- */ /* channel interface for ESS18xx */ static void * -esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +esschan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct ess_info *sc = devinfo; struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; ch->parent = sc; ch->channel = c; ch->buffer = b; - ch->buffer->bufsize = ESS_BUFFSIZE; - if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, ESS_BUFFSIZE) == -1) return NULL; ch->dir = dir; ch->hwch = 1; if ((dir == PCMDIR_PLAY) && (sc->duplex)) ch->hwch = 2; - ch->buffer->chan = rman_get_start((ch->hwch == 1)? sc->drq1 : sc->drq2); + sndbuf_isadmasetup(ch->buffer, (ch->hwch == 1)? sc->drq1 : sc->drq2); return ch; } static int -esschan_setformat(void *data, u_int32_t format) +esschan_setformat(kobj_t obj, void *data, u_int32_t format) { struct ess_chinfo *ch = data; ch->fmt = format; return 0; } static int -esschan_setspeed(void *data, u_int32_t speed) +esschan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct ess_chinfo *ch = data; struct ess_info *sc = ch->parent; ch->spd = speed; if (sc->newspeed) ess_calcspeed9(&ch->spd); else ess_calcspeed8(&ch->spd); return ch->spd; } static int -esschan_setblocksize(void *data, u_int32_t blocksize) +esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { - return blocksize; + struct ess_chinfo *ch = data; + + ch->blksz = blocksize; + return ch->blksz; } static int -esschan_trigger(void *data, int go) +esschan_trigger(kobj_t obj, void *data, int go) { struct ess_chinfo *ch = data; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; switch (go) { case PCMTRIG_START: - buf_isadma(ch->buffer, go); + ch->run = 1; + sndbuf_isadma(ch->buffer, go); ess_start(ch); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: default: ess_stop(ch); break; } return 0; } static int -esschan_getptr(void *data) +esschan_getptr(kobj_t obj, void *data) { struct ess_chinfo *ch = data; - return buf_isadmaptr(ch->buffer); + return sndbuf_isadmaptr(ch->buffer); } static pcmchan_caps * -esschan_getcaps(void *data) +esschan_getcaps(kobj_t obj, void *data) { struct ess_chinfo *ch = data; return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; } +static kobj_method_t esschan_methods[] = { + KOBJMETHOD(channel_init, esschan_init), + KOBJMETHOD(channel_setformat, esschan_setformat), + KOBJMETHOD(channel_setspeed, esschan_setspeed), + KOBJMETHOD(channel_setblocksize, esschan_setblocksize), + KOBJMETHOD(channel_trigger, esschan_trigger), + KOBJMETHOD(channel_getptr, esschan_getptr), + KOBJMETHOD(channel_getcaps, esschan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(esschan); + /************************************************************/ static int essmix_init(snd_mixer *m) { struct ess_info *sc = mix_getdevinfo(m); mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_IMIX); mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | SOUND_MASK_LINE1); ess_setmixer(sc, 0, 0); /* reset */ return 0; } static int essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct ess_info *sc = mix_getdevinfo(m); int preg = 0, rreg = 0, l, r; l = (left * 15) / 100; r = (right * 15) / 100; switch (dev) { case SOUND_MIXER_SYNTH: preg = 0x36; rreg = 0x6b; break; case SOUND_MIXER_PCM: preg = 0x14; rreg = 0x7c; break; case SOUND_MIXER_LINE: preg = 0x3e; rreg = 0x6e; break; case SOUND_MIXER_MIC: preg = 0x1a; rreg = 0x68; break; case SOUND_MIXER_LINE1: preg = 0x3a; rreg = 0x6c; break; case SOUND_MIXER_CD: preg = 0x38; rreg = 0x6a; break; case SOUND_MIXER_VOLUME: l = left? (left * 63) / 100 : 64; r = right? (right * 63) / 100 : 64; ess_setmixer(sc, 0x60, l); ess_setmixer(sc, 0x62, r); left = (l == 64)? 0 : (l * 100) / 63; right = (r == 64)? 0 : (r * 100) / 63; return left | (right << 8); } if (preg) ess_setmixer(sc, preg, (l << 4) | r); if (rreg) ess_setmixer(sc, rreg, (l << 4) | r); left = (l * 100) / 15; right = (r * 100) / 15; return left | (right << 8); } static int essmix_setrecsrc(snd_mixer *m, u_int32_t src) { struct ess_info *sc = mix_getdevinfo(m); u_char recdev; switch (src) { case SOUND_MASK_CD: recdev = 0x02; break; case SOUND_MASK_LINE: recdev = 0x06; break; case SOUND_MASK_IMIX: recdev = 0x05; break; case SOUND_MASK_MIC: default: recdev = 0x00; src = SOUND_MASK_MIC; break; } ess_setmixer(sc, 0x1c, recdev); return src; } +static kobj_method_t essmixer_methods[] = { + KOBJMETHOD(mixer_init, essmix_init), + KOBJMETHOD(mixer_set, essmix_set), + KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(essmixer); + +/************************************************************/ + static int ess_probe(device_t dev) { uintptr_t func, ver, r, f; /* The parent device has already been probed. */ r = BUS_READ_IVAR(device_get_parent(dev), dev, 0, &func); if (func != SCF_PCM) return (ENXIO); r = BUS_READ_IVAR(device_get_parent(dev), dev, 1, &ver); f = (ver & 0xffff0000) >> 16; if (!(f & BD_F_ESS)) return (ENXIO); device_set_desc(dev, "ESS 18xx DSP"); return 0; } static int ess_attach(device_t dev) { struct ess_info *sc; + char status[SND_STATUSLEN], buf[64]; + int ver; sc = (struct ess_info *)malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); if (!sc) return ENXIO; bzero(sc, sizeof *sc); - return ess_doattach(dev, sc); + if (ess_alloc_resources(sc, dev)) + goto no; + if (ess_reset_dsp(sc)) + goto no; + if (mixer_init(dev, &essmixer_class, sc)) + goto no; + + sc->duplex = 0; + sc->newspeed = 0; + ver = (ess_getmixer(sc, 0x40) << 8) | ess_rd(sc, SB_MIX_DATA); + snprintf(buf, sizeof buf, "ESS %x DSP", ver); + device_set_desc_copy(dev, buf); + if (bootverbose) + device_printf(dev, "ESS%x detected", ver); + + switch (ver) { + case 0x1869: + case 0x1879: +#ifdef ESS18XX_DUPLEX + sc->duplex = sc->drq2? 1 : 0; +#endif +#ifdef ESS18XX_NEWSPEED + sc->newspeed = 1; +#endif + break; + } + if (bootverbose) + printf("%s%s\n", sc->duplex? ", duplex" : "", + sc->newspeed? ", newspeed" : ""); + + if (sc->newspeed) + ess_setmixer(sc, 0x71, 0x22); + + bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &sc->ih); + if (!sc->duplex) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); + + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/ESS_BUFFSIZE, /*nsegments*/1, + /*maxsegz*/0x3ffff, + /*flags*/0, &sc->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto no; + } + + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld", + rman_get_start(sc->io_base), rman_get_start(sc->irq), + rman_get_start(sc->drq1)); + if (sc->drq2) + snprintf(status + strlen(status), SND_STATUSLEN - strlen(status), + ":%ld", rman_get_start(sc->drq2)); + + if (pcm_register(dev, sc, 1, 1)) + goto no; + pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc); + pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc); + pcm_setstatus(dev, status); + + return 0; + +no: + ess_release_resources(sc, dev); + return ENXIO; } +static int +ess_detach(device_t dev) +{ + int r; + struct ess_info *sc; + + r = pcm_unregister(dev); + if (r) + return r; + + sc = pcm_getdevinfo(dev); + ess_release_resources(sc, dev); + return 0; +} + static device_method_t ess_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ess_probe), DEVMETHOD(device_attach, ess_attach), DEVMETHOD(device_detach, ess_detach), { 0, 0 } }; static driver_t ess_driver = { "pcm", ess_methods, sizeof(snddev_info), }; DRIVER_MODULE(snd_ess, sbc, ess_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_ess, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_ess, 1); +/************************************************************/ static devclass_t esscontrol_devclass; static struct isa_pnp_id essc_ids[] = { {0x06007316, "ESS Control"}, {0} }; static int esscontrol_probe(device_t dev) { - return ISA_PNP_PROBE(device_get_parent(dev), dev, essc_ids); + int i; + + i = ISA_PNP_PROBE(device_get_parent(dev), dev, essc_ids); + if (i == 0) + device_quiet(dev); + return i; } static int esscontrol_attach(device_t dev) { #ifdef notyet struct resource *io; int rid, i, x; rid = 0; io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); x = 0; for (i = 0; i < 0x100; i++) { port_wr(io, 0, i); x = port_rd(io, 1); if ((i & 0x0f) == 0) printf("%3.3x: ", i); printf("%2.2x ", x); if ((i & 0x0f) == 0x0f) printf("\n"); } bus_release_resource(dev, SYS_RES_IOPORT, 0, io); io = NULL; #endif return 0; } static int esscontrol_detach(device_t dev) { return 0; } static device_method_t esscontrol_methods[] = { /* Device interface */ DEVMETHOD(device_probe, esscontrol_probe), DEVMETHOD(device_attach, esscontrol_attach), DEVMETHOD(device_detach, esscontrol_detach), { 0, 0 } }; static driver_t esscontrol_driver = { "esscontrol", esscontrol_methods, sizeof(snddev_info), }; DRIVER_MODULE(esscontrol, isa, esscontrol_driver, esscontrol_devclass, 0, 0); Index: stable/4/sys/dev/sound/isa/gusc.c =================================================================== --- stable/4/sys/dev/sound/isa/gusc.c (revision 71948) +++ stable/4/sys/dev/sound/isa/gusc.c (revision 71949) @@ -1,663 +1,662 @@ /*- * Copyright (c) 1999 Seigo Tanimura * Copyright (c) 1999 Ville-Pertti Keinonen * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include -#include #include #include #include #include #include "bus_if.h" #include #include #ifdef __alpha__ /* XXX workaround a stupid warning */ #include #endif #define LOGICALID_NOPNP 0 #define LOGICALID_PCM 0x0000561e #define LOGICALID_OPL 0x0300561e #define LOGICALID_MIDI 0x0400561e /* PnP IDs */ static struct isa_pnp_id gusc_ids[] = { {LOGICALID_PCM, "GRV0000 Gravis UltraSound PnP PCM"}, /* GRV0000 */ {LOGICALID_OPL, "GRV0003 Gravis UltraSound PnP OPL"}, /* GRV0003 */ {LOGICALID_MIDI, "GRV0004 Gravis UltraSound PnP MIDI"}, /* GRV0004 */ }; /* Interrupt handler. */ struct gusc_ihandler { void (*intr)(void *); void *arg; }; /* Here is the parameter structure per a device. */ struct gusc_softc { device_t dev; /* device */ int io_rid[3]; /* io port rids */ struct resource *io[3]; /* io port resources */ int io_alloced[3]; /* io port alloc flag */ int irq_rid; /* irq rids */ struct resource *irq; /* irq resources */ int irq_alloced; /* irq alloc flag */ int drq_rid[2]; /* drq rids */ struct resource *drq[2]; /* drq resources */ int drq_alloced[2]; /* drq alloc flag */ /* Interrupts are shared (XXX non-PnP only?) */ struct gusc_ihandler midi_intr; struct gusc_ihandler pcm_intr; }; typedef struct gusc_softc *sc_p; static int gusc_probe(device_t dev); static int gusc_attach(device_t dev); static int gusisa_probe(device_t dev); static void gusc_intr(void *); static struct resource *gusc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int gusc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static device_t find_masterdev(sc_p scp); static int alloc_resource(sc_p scp); static int release_resource(sc_p scp); static devclass_t gusc_devclass; static int gusc_probe(device_t dev) { device_t child; u_int32_t logical_id; char *s; struct sndcard_func *func; int ret; logical_id = isa_get_logicalid(dev); s = NULL; /* Check isapnp ids */ if (logical_id != 0 && (ret = ISA_PNP_PROBE(device_get_parent(dev), dev, gusc_ids)) != 0) return (ret); else { if (logical_id == 0) return gusisa_probe(dev); } switch (logical_id) { case LOGICALID_PCM: s = "Gravis UltraSound Plug & Play PCM"; func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_PCM; child = device_add_child(dev, "pcm", -1); device_set_ivars(child, func); break; case LOGICALID_OPL: s = "Gravis UltraSound Plug & Play OPL"; func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_SYNTH; child = device_add_child(dev, "midi", -1); device_set_ivars(child, func); break; case LOGICALID_MIDI: s = "Gravis UltraSound Plug & Play MIDI"; func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->func = SCF_MIDI; child = device_add_child(dev, "midi", -1); device_set_ivars(child, func); break; } if (s != NULL) { device_set_desc(dev, s); return (0); } return (ENXIO); } static void port_wr(struct resource *r, int i, unsigned char v) { bus_space_write_1(rman_get_bustag(r), rman_get_bushandle(r), i, v); } static int port_rd(struct resource *r, int i) { return bus_space_read_1(rman_get_bustag(r), rman_get_bushandle(r), i); } /* * Probe for an old (non-PnP) GUS card on the ISA bus. */ static int gusisa_probe(device_t dev) { device_t child; struct resource *res, *res2; int base, rid, rid2, s, flags; unsigned char val; base = isa_get_port(dev); flags = device_get_flags(dev); rid = 1; res = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, base + 0x100, base + 0x107, 8, RF_ACTIVE); if (res == NULL) return ENXIO; res2 = NULL; /* * Check for the presence of some GUS card. Reset the card, * then see if we can access the memory on it. */ port_wr(res, 3, 0x4c); port_wr(res, 5, 0); DELAY(30 * 1000); port_wr(res, 3, 0x4c); port_wr(res, 5, 1); DELAY(30 * 1000); s = splhigh(); /* Write to DRAM. */ port_wr(res, 3, 0x43); /* Register select */ port_wr(res, 4, 0); /* Low addr */ port_wr(res, 5, 0); /* Med addr */ port_wr(res, 3, 0x44); /* Register select */ port_wr(res, 4, 0); /* High addr */ port_wr(res, 7, 0x55); /* DRAM */ /* Read from DRAM. */ port_wr(res, 3, 0x43); /* Register select */ port_wr(res, 4, 0); /* Low addr */ port_wr(res, 5, 0); /* Med addr */ port_wr(res, 3, 0x44); /* Register select */ port_wr(res, 4, 0); /* High addr */ val = port_rd(res, 7); /* DRAM */ splx(s); if (val != 0x55) goto fail; rid2 = 0; res2 = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid2, base, base, 1, RF_ACTIVE); if (res2 == NULL) goto fail; s = splhigh(); port_wr(res2, 0x0f, 0x20); val = port_rd(res2, 0x0f); splx(s); if (val == 0xff || (val & 0x06) == 0) val = 0; else { val = port_rd(res2, 0x506); /* XXX Out of range. */ if (val == 0xff) val = 0; } bus_release_resource(dev, SYS_RES_IOPORT, rid2, res2); bus_release_resource(dev, SYS_RES_IOPORT, rid, res); if (val >= 10) { struct sndcard_func *func; /* Looks like a GUS MAX. Set the rest of the resources. */ bus_set_resource(dev, SYS_RES_IOPORT, 2, base + 0x10c, 8); if (flags & DV_F_DUAL_DMA) bus_set_resource(dev, SYS_RES_DRQ, 1, flags & DV_F_DRQ_MASK, 1); /* We can support the CS4231 and MIDI devices. */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return ENOMEM; bzero(func, sizeof *func); func->func = SCF_MIDI; child = device_add_child(dev, "midi", -1); device_set_ivars(child, func); func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) printf("xxx: gus pcm not attached, out of memory\n"); else { bzero(func, sizeof *func); func->func = SCF_PCM; child = device_add_child(dev, "pcm", -1); device_set_ivars(child, func); } device_set_desc(dev, "Gravis UltraSound MAX"); return 0; } else { /* * TODO: Support even older GUS cards. MIDI should work on * all models. */ return ENXIO; } fail: bus_release_resource(dev, SYS_RES_IOPORT, rid, res); return ENXIO; } static int gusc_attach(device_t dev) { sc_p scp; int unit; void *ih; scp = device_get_softc(dev); unit = device_get_unit(dev); bzero(scp, sizeof(*scp)); scp->dev = dev; if (alloc_resource(scp)) { release_resource(scp); return (ENXIO); } if (scp->irq != NULL) bus_setup_intr(dev, scp->irq, INTR_TYPE_TTY, gusc_intr, scp, &ih); bus_generic_attach(dev); return (0); } /* * Handle interrupts on GUS devices until there aren't any left. */ static void gusc_intr(void *arg) { sc_p scp = (sc_p)arg; int did_something; do { did_something = 0; if (scp->pcm_intr.intr != NULL && (port_rd(scp->io[2], 2) & 1)) { (*scp->pcm_intr.intr)(scp->pcm_intr.arg); did_something = 1; } if (scp->midi_intr.intr != NULL && (port_rd(scp->io[1], 0) & 0x80)) { (*scp->midi_intr.intr)(scp->midi_intr.arg); did_something = 1; } } while (did_something != 0); } static struct resource * gusc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { sc_p scp; int *alloced, rid_max, alloced_max; struct resource **res; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; res = scp->io; rid_max = 2; alloced_max = 2; /* pcm + midi (more to include synth) */ break; case SYS_RES_IRQ: alloced = &scp->irq_alloced; res = &scp->irq; rid_max = 0; alloced_max = 2; /* pcm and midi share the single irq. */ break; case SYS_RES_DRQ: alloced = scp->drq_alloced; res = scp->drq; rid_max = 1; alloced_max = 1; break; default: return (NULL); } if (*rid > rid_max || alloced[*rid] == alloced_max) return (NULL); alloced[*rid]++; return (res[*rid]); } static int gusc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { sc_p scp; int *alloced, rid_max; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; rid_max = 2; break; case SYS_RES_IRQ: alloced = &scp->irq_alloced; rid_max = 0; break; case SYS_RES_DRQ: alloced = scp->drq_alloced; rid_max = 1; break; default: return (1); } if (rid > rid_max || alloced[rid] == 0) return (1); alloced[rid]--; return (0); } static int gusc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp = (sc_p)device_get_softc(dev); devclass_t devclass; devclass = device_get_devclass(child); if (strcmp(devclass_get_name(devclass), "midi") == 0) { scp->midi_intr.intr = intr; scp->midi_intr.arg = arg; return 0; } else if (strcmp(devclass_get_name(devclass), "pcm") == 0) { scp->pcm_intr.intr = intr; scp->pcm_intr.arg = arg; return 0; } return bus_generic_setup_intr(dev, child, irq, flags, intr, arg, cookiep); } static device_t find_masterdev(sc_p scp) { int i, units; devclass_t devclass; device_t dev; devclass = device_get_devclass(scp->dev); units = devclass_get_maxunit(devclass); dev = NULL; for (i = 0 ; i < units ; i++) { dev = devclass_get_device(devclass, i); if (isa_get_vendorid(dev) == isa_get_vendorid(scp->dev) && isa_get_logicalid(dev) == LOGICALID_PCM && isa_get_serial(dev) == isa_get_serial(scp->dev)) break; } if (i == units) return (NULL); return (dev); } static int io_range[3] = {0x10, 0x8 , 0x4 }; static int io_offset[3] = {0x0 , 0x100, 0x10c}; static int alloc_resource(sc_p scp) { int i, base, lid, flags; device_t dev; flags = 0; if (isa_get_vendorid(scp->dev)) lid = isa_get_logicalid(scp->dev); else { lid = LOGICALID_NOPNP; flags = device_get_flags(scp->dev); } switch(lid) { case LOGICALID_PCM: case LOGICALID_NOPNP: /* XXX Non-PnP */ if (lid == LOGICALID_NOPNP) base = isa_get_port(scp->dev); else base = 0; for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { if (scp->io[i] == NULL) { scp->io_rid[i] = i; if (base == 0) scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 0, ~0, io_range[i], RF_ACTIVE); else scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], base + io_offset[i], base + io_offset[i] + io_range[i] - 1 , io_range[i], RF_ACTIVE); if (scp->io[i] == NULL) return (1); scp->io_alloced[i] = 0; } } if (scp->irq == NULL) { scp->irq_rid = 0; scp->irq = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (scp->irq == NULL) return (1); scp->irq_alloced = 0; } for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { if (scp->drq[i] == NULL) { scp->drq_rid[i] = i; if (base == 0 || i == 0) scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 0, ~0, 1, RF_ACTIVE); else if ((flags & DV_F_DUAL_DMA) != 0) /* XXX The secondary drq is specified in the flag. */ scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], flags & DV_F_DRQ_MASK, flags & DV_F_DRQ_MASK, 1, RF_ACTIVE); if (scp->drq[i] == NULL) return (1); scp->drq_alloced[i] = 0; } } break; case LOGICALID_OPL: if (scp->io[0] == NULL) { scp->io_rid[0] = 0; scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0], 0, ~0, io_range[0], RF_ACTIVE); if (scp->io[0] == NULL) return (1); scp->io_alloced[0] = 0; } break; case LOGICALID_MIDI: if (scp->io[0] == NULL) { scp->io_rid[0] = 0; scp->io[0] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[0], 0, ~0, io_range[0], RF_ACTIVE); if (scp->io[0] == NULL) return (1); scp->io_alloced[0] = 0; } if (scp->irq == NULL) { /* The irq is shared with pcm audio. */ dev = find_masterdev(scp); if (dev == NULL) return (1); scp->irq_rid = 0; scp->irq = BUS_ALLOC_RESOURCE(dev, NULL, SYS_RES_IRQ, &scp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (scp->irq == NULL) return (1); scp->irq_alloced = 0; } break; } return (0); } static int release_resource(sc_p scp) { int i, lid, flags; device_t dev; flags = 0; if (isa_get_vendorid(scp->dev)) lid = isa_get_logicalid(scp->dev); else { lid = LOGICALID_NOPNP; flags = device_get_flags(scp->dev); } switch(lid) { case LOGICALID_PCM: case LOGICALID_NOPNP: /* XXX Non-PnP */ for (i = 0 ; i < sizeof(scp->io) / sizeof(*scp->io) ; i++) { if (scp->io[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); scp->io[i] = NULL; } } if (scp->irq != NULL) { bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid, scp->irq); scp->irq = NULL; } for (i = 0 ; i < sizeof(scp->drq) / sizeof(*scp->drq) ; i++) { if (scp->drq[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); scp->drq[i] = NULL; } } break; case LOGICALID_OPL: if (scp->io[0] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); scp->io[0] = NULL; } break; case LOGICALID_MIDI: if (scp->io[0] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[0], scp->io[0]); scp->io[0] = NULL; } if (scp->irq != NULL) { /* The irq is shared with pcm audio. */ dev = find_masterdev(scp); if (dev == NULL) return (1); BUS_RELEASE_RESOURCE(dev, NULL, SYS_RES_IOPORT, scp->irq_rid, scp->irq); scp->irq = NULL; } break; } return (0); } static device_method_t gusc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, gusc_probe), DEVMETHOD(device_attach, gusc_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, gusc_alloc_resource), DEVMETHOD(bus_release_resource, gusc_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, gusc_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), { 0, 0 } }; static driver_t gusc_driver = { "gusc", gusc_methods, sizeof(struct gusc_softc), }; /* * gusc can be attached to an isa bus. */ DRIVER_MODULE(snd_gusc, isa, gusc_driver, gusc_devclass, 0, 0); MODULE_DEPEND(snd_gusc, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_gusc, 1); Index: stable/4/sys/dev/sound/isa/mss.c =================================================================== --- stable/4/sys/dev/sound/isa/mss.c (revision 71948) +++ stable/4/sys/dev/sound/isa/mss.c (revision 71949) @@ -1,1928 +1,1907 @@ /* * Copyright (c) 1999 Cameron Grant * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include /* board-specific include files */ #include #include -#define MSS_BUFFSIZE (65536 - 256) +#include "mixer_if.h" + +#define MSS_BUFFSIZE (4096) #define abs(x) (((x) < 0) ? -(x) : (x)) #define MSS_INDEXED_REGS 0x20 #define OPL_INDEXED_REGS 0x19 struct mss_info; struct mss_chinfo { struct mss_info *parent; pcm_channel *channel; snd_dbuf *buffer; int dir; - u_int32_t fmt; + u_int32_t fmt, blksz; }; struct mss_info { struct resource *io_base; /* primary I/O address for the board */ int io_rid; struct resource *conf_base; /* and the opti931 also has a config space */ int conf_rid; struct resource *irq; int irq_rid; struct resource *drq1; /* play */ int drq1_rid; struct resource *drq2; /* rec */ int drq2_rid; void *ih; bus_dma_tag_t parent_dmat; char mss_indexed_regs[MSS_INDEXED_REGS]; char opl_indexed_regs[OPL_INDEXED_REGS]; - int pdma, rdma; int bd_id; /* used to hold board-id info, eg. sb version, * mss codec type, etc. etc. */ int opti_offset; /* offset from config_base for opti931 */ u_long bd_flags; /* board-specific flags */ struct mss_chinfo pch, rch; }; static int mss_probe(device_t dev); static int mss_attach(device_t dev); static driver_intr_t mss_intr; /* prototypes for local functions */ static int mss_detect(device_t dev, struct mss_info *mss); static char *ymf_test(device_t dev, struct mss_info *mss); static void ad_unmute(struct mss_info *mss); /* mixer set funcs */ static int mss_mixer_set(struct mss_info *mss, int dev, int left, int right); static int mss_set_recsrc(struct mss_info *mss, int mask); /* io funcs */ static int ad_wait_init(struct mss_info *mss, int x); static int ad_read(struct mss_info *mss, int reg); static void ad_write(struct mss_info *mss, int reg, u_char data); static void ad_write_cnt(struct mss_info *mss, int reg, u_short data); static void ad_enter_MCE(struct mss_info *mss); static void ad_leave_MCE(struct mss_info *mss); /* io primitives */ static void conf_wr(struct mss_info *mss, u_char reg, u_char data); static u_char conf_rd(struct mss_info *mss, u_char reg); static int pnpmss_probe(device_t dev); static int pnpmss_attach(device_t dev); static driver_intr_t opti931_intr; -static int mssmix_init(snd_mixer *m); -static int mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int mssmix_setrecsrc(snd_mixer *m, u_int32_t src); -static snd_mixer mss_mixer = { - "MSS mixer", - mssmix_init, - NULL, - mssmix_set, - mssmix_setrecsrc, -}; - -static int ymmix_init(snd_mixer *m); -static int ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int ymmix_setrecsrc(snd_mixer *m, u_int32_t src); -static snd_mixer yamaha_mixer = { - "OPL3-SAx mixer", - ymmix_init, - NULL, - ymmix_set, - ymmix_setrecsrc, -}; - static devclass_t pcm_devclass; -/* channel interface */ -static void *msschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int msschan_setdir(void *data, int dir); -static int msschan_setformat(void *data, u_int32_t format); -static int msschan_setspeed(void *data, u_int32_t speed); -static int msschan_setblocksize(void *data, u_int32_t blocksize); -static int msschan_trigger(void *data, int go); -static int msschan_getptr(void *data); -static pcmchan_caps *msschan_getcaps(void *data); - static u_int32_t mss_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_MU_LAW, AFMT_STEREO | AFMT_MU_LAW, AFMT_A_LAW, AFMT_STEREO | AFMT_A_LAW, 0 }; static pcmchan_caps mss_caps = {4000, 48000, mss_fmt, 0}; static u_int32_t guspnp_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_A_LAW, AFMT_STEREO | AFMT_A_LAW, 0 }; static pcmchan_caps guspnp_caps = {4000, 48000, guspnp_fmt, 0}; static u_int32_t opti931_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps opti931_caps = {4000, 48000, opti931_fmt, 0}; -static pcm_channel mss_chantemplate = { - msschan_init, - msschan_setdir, - msschan_setformat, - msschan_setspeed, - msschan_setblocksize, - msschan_trigger, - msschan_getptr, - msschan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - #define MD_AD1848 0x91 #define MD_AD1845 0x92 #define MD_CS42XX 0xA1 #define MD_OPTI931 0xB1 #define MD_OPTI925 0xB2 #define MD_GUSPNP 0xB8 #define MD_GUSMAX 0xB9 #define MD_YM0020 0xC1 #define MD_VIVO 0xD1 #define DV_F_TRUE_MSS 0x00010000 /* mss _with_ base regs */ #define FULL_DUPLEX(x) ((x)->bd_flags & BD_F_DUPLEX) static int port_rd(struct resource *port, int off) { if (port) return bus_space_read_1(rman_get_bustag(port), rman_get_bushandle(port), off); else return -1; } static void port_wr(struct resource *port, int off, u_int8_t data) { if (port) return bus_space_write_1(rman_get_bustag(port), rman_get_bushandle(port), off, data); } static int io_rd(struct mss_info *mss, int reg) { if (mss->bd_flags & BD_F_MSS_OFFSET) reg -= 4; return port_rd(mss->io_base, reg); } static void io_wr(struct mss_info *mss, int reg, u_int8_t data) { if (mss->bd_flags & BD_F_MSS_OFFSET) reg -= 4; return port_wr(mss->io_base, reg, data); } static void conf_wr(struct mss_info *mss, u_char reg, u_char value) { port_wr(mss->conf_base, 0, reg); port_wr(mss->conf_base, 1, value); } static u_char conf_rd(struct mss_info *mss, u_char reg) { port_wr(mss->conf_base, 0, reg); return port_rd(mss->conf_base, 1); } static void opti_wr(struct mss_info *mss, u_char reg, u_char value) { port_wr(mss->conf_base, mss->opti_offset + 0, reg); port_wr(mss->conf_base, mss->opti_offset + 1, value); } static u_char opti_rd(struct mss_info *mss, u_char reg) { port_wr(mss->conf_base, mss->opti_offset + 0, reg); return port_rd(mss->conf_base, mss->opti_offset + 1); } static void gus_wr(struct mss_info *mss, u_char reg, u_char value) { port_wr(mss->conf_base, 3, reg); port_wr(mss->conf_base, 5, value); } static u_char gus_rd(struct mss_info *mss, u_char reg) { port_wr(mss->conf_base, 3, reg); return port_rd(mss->conf_base, 5); } static void mss_release_resources(struct mss_info *mss, device_t dev) { if (mss->irq) { if (mss->ih) bus_teardown_intr(dev, mss->irq, mss->ih); bus_release_resource(dev, SYS_RES_IRQ, mss->irq_rid, mss->irq); mss->irq = 0; } - if (mss->drq1) { - bus_release_resource(dev, SYS_RES_DRQ, mss->drq1_rid, - mss->drq1); - mss->drq1 = 0; - mss->pdma = -1; - } - if (mss->drq2) { + if (mss->drq2 && mss->drq2 != mss->drq1) { bus_release_resource(dev, SYS_RES_DRQ, mss->drq2_rid, mss->drq2); mss->drq2 = 0; - mss->rdma = -1; } - if (mss->io_base) { + if (mss->drq1) { + bus_release_resource(dev, SYS_RES_DRQ, mss->drq1_rid, + mss->drq1); + mss->drq1 = 0; + } + if (mss->io_base) { bus_release_resource(dev, SYS_RES_IOPORT, mss->io_rid, mss->io_base); mss->io_base = 0; } if (mss->conf_base) { bus_release_resource(dev, SYS_RES_IOPORT, mss->conf_rid, mss->conf_base); mss->conf_base = 0; } if (mss->parent_dmat) { bus_dma_tag_destroy(mss->parent_dmat); mss->parent_dmat = 0; } free(mss, M_DEVBUF); } static int mss_alloc_resources(struct mss_info *mss, device_t dev) { - int ok = 1; + int pdma, rdma, ok = 1; if (!mss->io_base) mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, 0, ~0, 1, RF_ACTIVE); if (!mss->irq) mss->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &mss->irq_rid, 0, ~0, 1, RF_ACTIVE); if (!mss->drq1) mss->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq1_rid, 0, ~0, 1, RF_ACTIVE); if (mss->conf_rid >= 0 && !mss->conf_base) mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid, 0, ~0, 1, RF_ACTIVE); if (mss->drq2_rid >= 0 && !mss->drq2) mss->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq2_rid, 0, ~0, 1, RF_ACTIVE); if (!mss->io_base || !mss->drq1 || !mss->irq) ok = 0; if (mss->conf_rid >= 0 && !mss->conf_base) ok = 0; if (mss->drq2_rid >= 0 && !mss->drq2) ok = 0; if (ok) { - mss->pdma = rman_get_start(mss->drq1); - isa_dma_acquire(mss->pdma); - isa_dmainit(mss->pdma, MSS_BUFFSIZE); + pdma = rman_get_start(mss->drq1); + isa_dma_acquire(pdma); + isa_dmainit(pdma, MSS_BUFFSIZE); mss->bd_flags &= ~BD_F_DUPLEX; if (mss->drq2) { - mss->rdma = rman_get_start(mss->drq2); - isa_dma_acquire(mss->rdma); - isa_dmainit(mss->rdma, MSS_BUFFSIZE); + rdma = rman_get_start(mss->drq2); + isa_dma_acquire(rdma); + isa_dmainit(rdma, MSS_BUFFSIZE); mss->bd_flags |= BD_F_DUPLEX; - } else mss->rdma = mss->pdma; + } else mss->drq2 = mss->drq1; } return ok; } +/* -------------------------------------------------------------------- */ +/* only one source can be set... */ +static int +mss_set_recsrc(struct mss_info *mss, int mask) +{ + u_char recdev; + + switch (mask) { + case SOUND_MASK_LINE: + case SOUND_MASK_LINE3: + recdev = 0; + break; + + case SOUND_MASK_CD: + case SOUND_MASK_LINE1: + recdev = 0x40; + break; + + case SOUND_MASK_IMIX: + recdev = 0xc0; + break; + + case SOUND_MASK_MIC: + default: + mask = SOUND_MASK_MIC; + recdev = 0x80; + } + ad_write(mss, 0, (ad_read(mss, 0) & 0x3f) | recdev); + ad_write(mss, 1, (ad_read(mss, 1) & 0x3f) | recdev); + return mask; +} + +/* there are differences in the mixer depending on the actual sound card. */ +static int +mss_mixer_set(struct mss_info *mss, int dev, int left, int right) +{ + int regoffs; + mixer_tab *mix_d = (mss->bd_id == MD_OPTI931)? &opti931_devices : &mix_devices; + u_char old, val; + + if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { + DEB(printf("nbits = 0 for dev %d\n", dev)); + return -1; + } + + if ((*mix_d)[dev][RIGHT_CHN].nbits == 0) right = left; /* mono */ + + /* Set the left channel */ + + regoffs = (*mix_d)[dev][LEFT_CHN].regno; + old = val = ad_read(mss, regoffs); + /* if volume is 0, mute chan. Otherwise, unmute. */ + if (regoffs != 0) val = (left == 0)? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, LEFT_CHN, left); + ad_write(mss, regoffs, val); + + DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); + + if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ + /* Set the right channel */ + regoffs = (*mix_d)[dev][RIGHT_CHN].regno; + old = val = ad_read(mss, regoffs); + if (regoffs != 1) val = (right == 0)? old | 0x80 : old & 0x7f; + change_bits(mix_d, &val, dev, RIGHT_CHN, right); + ad_write(mss, regoffs, val); + + DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", + dev, regoffs, old, val)); + } + return 0; /* success */ +} + +/* -------------------------------------------------------------------- */ + +static int +mssmix_init(snd_mixer *m) +{ + struct mss_info *mss = mix_getdevinfo(m); + + mix_setdevs(m, MODE2_MIXER_DEVICES); + mix_setrecdevs(m, MSS_REC_DEVICES); + switch(mss->bd_id) { + case MD_OPTI931: + mix_setdevs(m, OPTI931_MIXER_DEVICES); + ad_write(mss, 20, 0x88); + ad_write(mss, 21, 0x88); + break; + + case MD_AD1848: + mix_setdevs(m, MODE1_MIXER_DEVICES); + break; + + case MD_GUSPNP: + case MD_GUSMAX: + /* this is only necessary in mode 3 ... */ + ad_write(mss, 22, 0x88); + ad_write(mss, 23, 0x88); + break; + } + return 0; +} + +static int +mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct mss_info *mss = mix_getdevinfo(m); + + mss_mixer_set(mss, dev, left, right); + + return left | (right << 8); +} + +static int +mssmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct mss_info *mss = mix_getdevinfo(m); + + src = mss_set_recsrc(mss, src); + return src; +} + +static kobj_method_t mssmix_mixer_methods[] = { + KOBJMETHOD(mixer_init, mssmix_init), + KOBJMETHOD(mixer_set, mssmix_set), + KOBJMETHOD(mixer_setrecsrc, mssmix_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(mssmix_mixer); + +/* -------------------------------------------------------------------- */ + +static int +ymmix_init(snd_mixer *m) +{ + struct mss_info *mss = mix_getdevinfo(m); + + mssmix_init(m); + mix_setdevs(m, mix_getdevs(m) | SOUND_MASK_VOLUME | SOUND_MASK_MIC + | SOUND_MASK_BASS | SOUND_MASK_TREBLE); + /* Set master volume */ + conf_wr(mss, OPL3SAx_VOLUMEL, 7); + conf_wr(mss, OPL3SAx_VOLUMER, 7); + + return 0; +} + +static int +ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) +{ + struct mss_info *mss = mix_getdevinfo(m); + int t, l, r; + + switch (dev) { + case SOUND_MIXER_VOLUME: + if (left) t = 15 - (left * 15) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_VOLUMEL, t); + if (right) t = 15 - (right * 15) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_VOLUMER, t); + break; + + case SOUND_MIXER_MIC: + t = left; + if (left) t = 31 - (left * 31) / 100; + else t = 0x80; /* mute */ + conf_wr(mss, OPL3SAx_MIC, t); + break; + + case SOUND_MIXER_BASS: + l = (left * 7) / 100; + r = (right * 7) / 100; + t = (r << 4) | l; + conf_wr(mss, OPL3SAx_BASS, t); + break; + + case SOUND_MIXER_TREBLE: + l = (left * 7) / 100; + r = (right * 7) / 100; + t = (r << 4) | l; + conf_wr(mss, OPL3SAx_TREBLE, t); + break; + + default: + mss_mixer_set(mss, dev, left, right); + } + + return left | (right << 8); +} + +static int +ymmix_setrecsrc(snd_mixer *m, u_int32_t src) +{ + struct mss_info *mss = mix_getdevinfo(m); + src = mss_set_recsrc(mss, src); + return src; +} + +static kobj_method_t ymmix_mixer_methods[] = { + KOBJMETHOD(mixer_init, ymmix_init), + KOBJMETHOD(mixer_set, ymmix_set), + KOBJMETHOD(mixer_setrecsrc, ymmix_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(ymmix_mixer); + +/* -------------------------------------------------------------------- */ /* * XXX This might be better off in the gusc driver. */ static void gusmax_setup(struct mss_info *mss, device_t dev, struct resource *alt) { static const unsigned char irq_bits[16] = { 0, 0, 0, 3, 0, 2, 0, 4, 0, 1, 0, 5, 6, 0, 0, 7 }; static const unsigned char dma_bits[8] = { 0, 1, 0, 2, 0, 3, 4, 5 }; device_t parent = device_get_parent(dev); unsigned char irqctl, dmactl; int s; s = splhigh(); port_wr(alt, 0x0f, 0x05); port_wr(alt, 0x00, 0x0c); port_wr(alt, 0x0b, 0x00); port_wr(alt, 0x0f, 0x00); irqctl = irq_bits[isa_get_irq(parent)]; /* Share the IRQ with the MIDI driver. */ irqctl |= 0x40; dmactl = dma_bits[isa_get_drq(parent)]; if (device_get_flags(parent) & DV_F_DUAL_DMA) dmactl |= dma_bits[device_get_flags(parent) & DV_F_DRQ_MASK] << 3; /* * Set the DMA and IRQ control latches. */ port_wr(alt, 0x00, 0x0c); port_wr(alt, 0x0b, dmactl | 0x80); port_wr(alt, 0x00, 0x4c); port_wr(alt, 0x0b, irqctl); port_wr(alt, 0x00, 0x0c); port_wr(alt, 0x0b, dmactl); port_wr(alt, 0x00, 0x4c); port_wr(alt, 0x0b, irqctl); port_wr(mss->conf_base, 2, 0); port_wr(alt, 0x00, 0x0c); port_wr(mss->conf_base, 2, 0); splx(s); } static int mss_init(struct mss_info *mss, device_t dev) { u_char r6, r9; struct resource *alt; int rid, tmp; mss->bd_flags |= BD_F_MCE_BIT; switch(mss->bd_id) { case MD_OPTI931: /* * The MED3931 v.1.0 allocates 3 bytes for the config * space, whereas v.2.0 allocates 4 bytes. What I know * for sure is that the upper two ports must be used, * and they should end on a boundary of 4 bytes. So I * need the following trick. */ mss->opti_offset = (rman_get_start(mss->conf_base) & ~3) + 2 - rman_get_start(mss->conf_base); BVDDB(printf("mss_init: opti_offset=%d\n", mss->opti_offset)); opti_wr(mss, 4, 0xd6); /* fifo empty, OPL3, audio enable, SB3.2 */ ad_write(mss, 10, 2); /* enable interrupts */ opti_wr(mss, 6, 2); /* MCIR6: mss enable, sb disable */ opti_wr(mss, 5, 0x28); /* MCIR5: codec in exp. mode,fifo */ break; case MD_GUSPNP: case MD_GUSMAX: gus_wr(mss, 0x4c /* _URSTI */, 0);/* Pull reset */ DELAY(1000 * 30); /* release reset and enable DAC */ gus_wr(mss, 0x4c /* _URSTI */, 3); DELAY(1000 * 30); /* end of reset */ rid = 0; alt = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); if (alt == NULL) { printf("XXX couldn't init GUS PnP/MAX\n"); break; } port_wr(alt, 0, 0xC); /* enable int and dma */ if (mss->bd_id == MD_GUSMAX) gusmax_setup(mss, dev, alt); bus_release_resource(dev, SYS_RES_IOPORT, rid, alt); /* * unmute left & right line. Need to go in mode3, unmute, * and back to mode 2 */ tmp = ad_read(mss, 0x0c); ad_write(mss, 0x0c, 0x6c); /* special value to enter mode 3 */ ad_write(mss, 0x19, 0); /* unmute left */ ad_write(mss, 0x1b, 0); /* unmute right */ ad_write(mss, 0x0c, tmp); /* restore old mode */ /* send codec interrupts on irq1 and only use that one */ gus_wr(mss, 0x5a, 0x4f); /* enable access to hidden regs */ tmp = gus_rd(mss, 0x5b /* IVERI */); gus_wr(mss, 0x5b, tmp | 1); BVDDB(printf("GUS: silicon rev %c\n", 'A' + ((tmp & 0xf) >> 4))); break; case MD_YM0020: conf_wr(mss, OPL3SAx_DMACONF, 0xa9); /* dma-b rec, dma-a play */ r6 = conf_rd(mss, OPL3SAx_DMACONF); r9 = conf_rd(mss, OPL3SAx_MISC); /* version */ BVDDB(printf("Yamaha: ver 0x%x DMA config 0x%x\n", r6, r9);) /* yamaha - set volume to max */ conf_wr(mss, OPL3SAx_VOLUMEL, 0); conf_wr(mss, OPL3SAx_VOLUMER, 0); conf_wr(mss, OPL3SAx_DMACONF, FULL_DUPLEX(mss)? 0xa9 : 0x8b); break; } if (FULL_DUPLEX(mss) && mss->bd_id != MD_OPTI931) ad_write(mss, 12, ad_read(mss, 12) | 0x40); /* mode 2 */ ad_enter_MCE(mss); ad_write(mss, 9, FULL_DUPLEX(mss)? 0 : 4); ad_leave_MCE(mss); ad_write(mss, 10, 2); /* int enable */ io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ /* the following seem required on the CS4232 */ ad_unmute(mss); return 0; } + /* + * main irq handler for the CS423x. The OPTi931 code is + * a separate one. + * The correct way to operate for a device with multiple internal + * interrupt sources is to loop on the status register and ack + * interrupts until all interrupts are served and none are reported. At + * this point the IRQ line to the ISA IRQ controller should go low + * and be raised at the next interrupt. + * + * Since the ISA IRQ controller is sent EOI _before_ passing control + * to the isr, it might happen that we serve an interrupt early, in + * which case the status register at the next interrupt should just + * say that there are no more interrupts... + */ + +static void +mss_intr(void *arg) +{ + struct mss_info *mss = arg; + u_char c = 0, served = 0; + int i; + + DEB(printf("mss_intr\n")); + ad_read(mss, 11); /* fake read of status bits */ + + /* loop until there are interrupts, but no more than 10 times. */ + for (i = 10; i > 0 && io_rd(mss, MSS_STATUS) & 1; i--) { + /* get exact reason for full-duplex boards */ + c = FULL_DUPLEX(mss)? ad_read(mss, 24) : 0x30; + c &= ~served; + if (sndbuf_runsz(mss->pch.buffer) && (c & 0x10)) { + served |= 0x10; + chn_intr(mss->pch.channel); + } + if (sndbuf_runsz(mss->rch.buffer) && (c & 0x20)) { + served |= 0x20; + chn_intr(mss->rch.channel); + } + /* now ack the interrupt */ + if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */ + else io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + } + if (i == 10) { + BVDDB(printf("mss_intr: irq, but not from mss\n")); + } else if (served == 0) { + BVDDB(printf("mss_intr: unexpected irq with reason %x\n", c)); + /* + * this should not happen... I have no idea what to do now. + * maybe should do a sanity check and restart dmas ? + */ + io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ + } +} + +/* + * AD_WAIT_INIT waits if we are initializing the board and + * we cannot modify its settings + */ +static int +ad_wait_init(struct mss_info *mss, int x) +{ + int arg = x, n = 0; /* to shut up the compiler... */ + for (; x > 0; x--) + if ((n = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10); + else return n; + printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); + return n; +} + +static int +ad_read(struct mss_info *mss, int reg) +{ + u_long flags; + int x; + + flags = spltty(); + ad_wait_init(mss, 201); + x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; + io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); + x = io_rd(mss, MSS_IDATA); + splx(flags); + /* printf("ad_read %d, %x\n", reg, x); */ + return x; +} + +static void +ad_write(struct mss_info *mss, int reg, u_char data) +{ + u_long flags; + + int x; + /* printf("ad_write %d, %x\n", reg, data); */ + flags = spltty(); + ad_wait_init(mss, 1002); + x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; + io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); + io_wr(mss, MSS_IDATA, data); + splx(flags); +} + +static void +ad_write_cnt(struct mss_info *mss, int reg, u_short cnt) +{ + ad_write(mss, reg+1, cnt & 0xff); + ad_write(mss, reg, cnt >> 8); /* upper base must be last */ +} + +static void +wait_for_calibration(struct mss_info *mss) +{ + int t; + + /* + * Wait until the auto calibration process has finished. + * + * 1) Wait until the chip becomes ready (reads don't return 0x80). + * 2) Wait until the ACI bit of I11 gets on + * 3) Wait until the ACI bit of I11 gets off + */ + + t = ad_wait_init(mss, 1000); + if (t & MSS_IDXBUSY) printf("mss: Auto calibration timed out(1).\n"); + + /* + * The calibration mode for chips that support it is set so that + * we never see ACI go on. + */ + if (mss->bd_id == MD_GUSMAX || mss->bd_id == MD_GUSPNP) { + for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--); + } else { + /* + * XXX This should only be enabled for cards that *really* + * need it. Are there any? + */ + for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) DELAY(100); + } + for (t = 100; t > 0 && ad_read(mss, 11) & 0x20; t--) DELAY(100); +} + +static void +ad_unmute(struct mss_info *mss) +{ + ad_write(mss, 6, ad_read(mss, 6) & ~I6_MUTE); + ad_write(mss, 7, ad_read(mss, 7) & ~I6_MUTE); +} + +static void +ad_enter_MCE(struct mss_info *mss) +{ + int prev; + + mss->bd_flags |= BD_F_MCE_BIT; + ad_wait_init(mss, 203); + prev = io_rd(mss, MSS_INDEX); + prev &= ~MSS_TRD; + io_wr(mss, MSS_INDEX, prev | MSS_MCE); +} + +static void +ad_leave_MCE(struct mss_info *mss) +{ + u_long flags; + u_char prev; + + if ((mss->bd_flags & BD_F_MCE_BIT) == 0) { + DEB(printf("--- hey, leave_MCE: MCE bit was not set!\n")); + return; + } + + ad_wait_init(mss, 1000); + + flags = spltty(); + mss->bd_flags &= ~BD_F_MCE_BIT; + + prev = io_rd(mss, MSS_INDEX); + prev &= ~MSS_TRD; + io_wr(mss, MSS_INDEX, prev & ~MSS_MCE); /* Clear the MCE bit */ + wait_for_calibration(mss); + splx(flags); +} + +static int +mss_speed(struct mss_chinfo *ch, int speed) +{ + struct mss_info *mss = ch->parent; + /* + * In the CS4231, the low 4 bits of I8 are used to hold the + * sample rate. Only a fixed number of values is allowed. This + * table lists them. The speed-setting routines scans the table + * looking for the closest match. This is the only supported method. + * + * In the CS4236, there is an alternate metod (which we do not + * support yet) which provides almost arbitrary frequency setting. + * In the AD1845, it looks like the sample rate can be + * almost arbitrary, and written directly to a register. + * In the OPTi931, there is a SB command which provides for + * almost arbitrary frequency setting. + * + */ + ad_enter_MCE(mss); + if (mss->bd_id == MD_AD1845) { /* Use alternate speed select regs */ + ad_write(mss, 22, (speed >> 8) & 0xff); /* Speed MSB */ + ad_write(mss, 23, speed & 0xff); /* Speed LSB */ + /* XXX must also do something in I27 for the ad1845 */ + } else { + int i, sel = 0; /* assume entry 0 does not contain -1 */ + static int speeds[] = + {8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, + -1, 37800, -1, 44100, 48000, 33075, 9600, 6615}; + + for (i = 1; i < 16; i++) + if (speeds[i] > 0 && + abs(speed-speeds[i]) < abs(speed-speeds[sel])) sel = i; + speed = speeds[sel]; + ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel); + } + ad_leave_MCE(mss); + + return speed; +} + +/* + * mss_format checks that the format is supported (or defaults to AFMT_U8) + * and returns the bit setting for the 1848 register corresponding to + * the desired format. + * + * fixed lr970724 + */ + +static int +mss_format(struct mss_chinfo *ch, u_int32_t format) +{ + struct mss_info *mss = ch->parent; + int i, arg = format & ~AFMT_STEREO; + + /* + * The data format uses 3 bits (just 2 on the 1848). For each + * bit setting, the following array returns the corresponding format. + * The code scans the array looking for a suitable format. In + * case it is not found, default to AFMT_U8 (not such a good + * choice, but let's do it for compatibility...). + */ + + static int fmts[] = + {AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW, + -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1}; + + ch->fmt = format; + for (i = 0; i < 8; i++) if (arg == fmts[i]) break; + arg = i << 1; + if (format & AFMT_STEREO) arg |= 1; + arg <<= 4; + ad_enter_MCE(mss); + ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg); + if (FULL_DUPLEX(mss)) ad_write(mss, 28, arg); /* capture mode */ + ad_leave_MCE(mss); + return format; +} + +static int +mss_trigger(struct mss_chinfo *ch, int go) +{ + struct mss_info *mss = ch->parent; + u_char m; + int retry, wr, cnt, ss; + + ss = 1; + ss <<= (ch->fmt & AFMT_STEREO)? 1 : 0; + ss <<= (ch->fmt & AFMT_16BIT)? 1 : 0; + + wr = (ch->dir == PCMDIR_PLAY)? 1 : 0; + m = ad_read(mss, 9); + switch (go) { + case PCMTRIG_START: + cnt = (ch->blksz / ss) - 1; + + DEB(if (m & 4) printf("OUCH! reg 9 0x%02x\n", m);); + m |= wr? I9_PEN : I9_CEN; /* enable DMA */ + ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, cnt); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: /* XXX check this... */ + m &= ~(wr? I9_PEN : I9_CEN); /* Stop DMA */ +#if 0 + /* + * try to disable DMA by clearing count registers. Not sure it + * is needed, and it might cause false interrupts when the + * DMA is re-enabled later. + */ + ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, 0); +#endif + } + /* on the OPTi931 the enable bit seems hard to set... */ + for (retry = 10; retry > 0; retry--) { + ad_write(mss, 9, m); + if (ad_read(mss, 9) == m) break; + } + if (retry == 0) BVDDB(printf("stop dma, failed to set bit 0x%02x 0x%02x\n", \ + m, ad_read(mss, 9))); + return 0; +} + + +/* + * the opti931 seems to miss interrupts when working in full + * duplex, so we try some heuristics to catch them. + */ +static void +opti931_intr(void *arg) +{ + struct mss_info *mss = (struct mss_info *)arg; + u_char masked = 0, i11, mc11, c = 0; + u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ + int loops = 10; + +#if 0 + reason = io_rd(mss, MSS_STATUS); + if (!(reason & 1)) {/* no int, maybe a shared line ? */ + DEB(printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11))); + return; + } +#endif + i11 = ad_read(mss, 11); /* XXX what's for ? */ + again: + + c = mc11 = FULL_DUPLEX(mss)? opti_rd(mss, 11) : 0xc; + mc11 &= 0x0c; + if (c & 0x10) { + DEB(printf("Warning: CD interrupt\n");) + mc11 |= 0x10; + } + if (c & 0x20) { + DEB(printf("Warning: MPU interrupt\n");) + mc11 |= 0x20; + } + if (mc11 & masked) BVDDB(printf("irq reset failed, mc11 0x%02x, 0x%02x\n",\ + mc11, masked)); + masked |= mc11; + /* + * the nice OPTi931 sets the IRQ line before setting the bits in + * mc11. So, on some occasions I have to retry (max 10 times). + */ + if (mc11 == 0) { /* perhaps can return ... */ + reason = io_rd(mss, MSS_STATUS); + if (reason & 1) { + DEB(printf("one more try...\n");) + if (--loops) goto again; + else DDB(printf("intr, but mc11 not set\n");) + } + if (loops == 0) BVDDB(printf("intr, nothing in mcir11 0x%02x\n", mc11)); + return; + } + + if (sndbuf_runsz(mss->rch.buffer) && (mc11 & 8)) chn_intr(mss->rch.channel); + if (sndbuf_runsz(mss->pch.buffer) && (mc11 & 4)) chn_intr(mss->pch.channel); + opti_wr(mss, 11, ~mc11); /* ack */ + if (--loops) goto again; + DEB(printf("xxx too many loops\n");) +} + +/* -------------------------------------------------------------------- */ +/* channel interface */ +static void * +msschan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct mss_info *mss = devinfo; + struct mss_chinfo *ch = (dir == PCMDIR_PLAY)? &mss->pch : &mss->rch; + + ch->parent = mss; + ch->channel = c; + ch->buffer = b; + ch->dir = dir; + if (sndbuf_alloc(ch->buffer, mss->parent_dmat, MSS_BUFFSIZE) == -1) return NULL; + sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? mss->drq1 : mss->drq2); + return ch; +} + +static int +msschan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct mss_chinfo *ch = data; + + mss_format(ch, format); + return 0; +} + +static int +msschan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct mss_chinfo *ch = data; + + return mss_speed(ch, speed); +} + +static int +msschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct mss_chinfo *ch = data; + + ch->blksz = blocksize; + return ch->blksz; +} + +static int +msschan_trigger(kobj_t obj, void *data, int go) +{ + struct mss_chinfo *ch = data; + + if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + return 0; + + sndbuf_isadma(ch->buffer, go); + mss_trigger(ch, go); + return 0; +} + +static int +msschan_getptr(kobj_t obj, void *data) +{ + struct mss_chinfo *ch = data; + return sndbuf_isadmaptr(ch->buffer); +} + +static pcmchan_caps * +msschan_getcaps(kobj_t obj, void *data) +{ + struct mss_chinfo *ch = data; + + switch(ch->parent->bd_id) { + case MD_OPTI931: + return &opti931_caps; + break; + + case MD_GUSPNP: + case MD_GUSMAX: + return &guspnp_caps; + break; + + default: + return &mss_caps; + break; + } +} + +static kobj_method_t msschan_methods[] = { + KOBJMETHOD(channel_init, msschan_init), + KOBJMETHOD(channel_setformat, msschan_setformat), + KOBJMETHOD(channel_setspeed, msschan_setspeed), + KOBJMETHOD(channel_setblocksize, msschan_setblocksize), + KOBJMETHOD(channel_trigger, msschan_trigger), + KOBJMETHOD(channel_getptr, msschan_getptr), + KOBJMETHOD(channel_getcaps, msschan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(msschan); + +/* -------------------------------------------------------------------- */ + +/* * mss_probe() is the probe routine. Note, it is not necessary to * go through this for PnP devices, since they are already * indentified precisely using their PnP id. * * The base address supplied in the device refers to the old MSS * specs where the four 4 registers in io space contain configuration * information. Some boards (as an example, early MSS boards) * has such a block of registers, whereas others (generally CS42xx) * do not. In order to distinguish between the two and do not have * to supply two separate probe routines, the flags entry in isa_device * has a bit to mark this. * */ static int mss_probe(device_t dev) { u_char tmp, tmpx; int flags, irq, drq, result = ENXIO, setres = 0; struct mss_info *mss; if (isa_get_logicalid(dev)) return ENXIO; /* not yet */ mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); if (!mss) return ENXIO; bzero(mss, sizeof *mss); mss->io_rid = 0; mss->conf_rid = -1; mss->irq_rid = 0; mss->drq1_rid = 0; mss->drq2_rid = -1; mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, 0, ~0, 8, RF_ACTIVE); if (!mss->io_base) { BVDDB(printf("mss_probe: no address given, try 0x%x\n", 0x530)); mss->io_rid = 0; /* XXX verify this */ setres = 1; bus_set_resource(dev, SYS_RES_IOPORT, mss->io_rid, 0x530, 8); mss->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->io_rid, 0, ~0, 8, RF_ACTIVE); } if (!mss->io_base) goto no; /* got irq/dma regs? */ flags = device_get_flags(dev); irq = isa_get_irq(dev); drq = isa_get_drq(dev); if (!(device_get_flags(dev) & DV_F_TRUE_MSS)) goto mss_probe_end; /* * Check if the IO port returns valid signature. The original MS * Sound system returns 0x04 while some cards * (AudioTriX Pro for example) return 0x00 or 0x0f. */ device_set_desc(dev, "MSS"); tmpx = tmp = io_rd(mss, 3); if (tmp == 0xff) { /* Bus float */ BVDDB(printf("I/O addr inactive (%x), try pseudo_mss\n", tmp)); device_set_flags(dev, flags & ~DV_F_TRUE_MSS); goto mss_probe_end; } tmp &= 0x3f; if (!(tmp == 0x04 || tmp == 0x0f || tmp == 0x00)) { BVDDB(printf("No MSS signature detected on port 0x%lx (0x%x)\n", rman_get_start(mss->io_base), tmpx)); goto no; } #ifdef PC98 if (irq > 12) { #else if (irq > 11) { #endif printf("MSS: Bad IRQ %d\n", irq); goto no; } if (!(drq == 0 || drq == 1 || drq == 3)) { printf("MSS: Bad DMA %d\n", drq); goto no; } if (tmpx & 0x80) { /* 8-bit board: only drq1/3 and irq7/9 */ if (drq == 0) { printf("MSS: Can't use DMA0 with a 8 bit card/slot\n"); goto no; } if (!(irq == 7 || irq == 9)) { printf("MSS: Can't use IRQ%d with a 8 bit card/slot\n", irq); goto no; } } mss_probe_end: result = mss_detect(dev, mss); no: mss_release_resources(mss, dev); #if 0 if (setres) ISA_DELETE_RESOURCE(device_get_parent(dev), dev, SYS_RES_IOPORT, mss->io_rid); /* XXX ? */ #endif return result; } static int mss_detect(device_t dev, struct mss_info *mss) { int i; u_char tmp = 0, tmp1, tmp2; char *name, *yamaha; if (mss->bd_id != 0) { device_printf(dev, "presel bd_id 0x%04x -- %s\n", mss->bd_id, device_get_desc(dev)); return 0; } name = "AD1848"; mss->bd_id = MD_AD1848; /* AD1848 or CS4248 */ /* * Check that the I/O address is in use. * * bit 7 of the base I/O port is known to be 0 after the chip has * performed its power on initialization. Just assume this has * happened before the OS is starting. * * If the I/O address is unused, it typically returns 0xff. */ for (i = 0; i < 10; i++) if ((tmp = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10000); else break; if (i >= 10) { /* Not a AD1848 */ BVDDB(printf("mss_detect, busy still set (0x%02x)\n", tmp)); goto no; } /* * Test if it's possible to change contents of the indirect * registers. Registers 0 and 1 are ADC volume registers. The bit * 0x10 is read only so try to avoid using it. */ ad_write(mss, 0, 0xaa); ad_write(mss, 1, 0x45);/* 0x55 with bit 0x10 clear */ tmp1 = ad_read(mss, 0); tmp2 = ad_read(mss, 1); if (tmp1 != 0xaa || tmp2 != 0x45) { BVDDB(printf("mss_detect error - IREG (%x/%x)\n", tmp1, tmp2)); goto no; } ad_write(mss, 0, 0x45); ad_write(mss, 1, 0xaa); tmp1 = ad_read(mss, 0); tmp2 = ad_read(mss, 1); if (tmp1 != 0x45 || tmp2 != 0xaa) { BVDDB(printf("mss_detect error - IREG2 (%x/%x)\n", tmp1, tmp2)); goto no; } /* * The indirect register I12 has some read only bits. Lets try to * change them. */ tmp = ad_read(mss, 12); ad_write(mss, 12, (~tmp) & 0x0f); tmp1 = ad_read(mss, 12); if ((tmp & 0x0f) != (tmp1 & 0x0f)) { BVDDB(printf("mss_detect - I12 (0x%02x was 0x%02x)\n", tmp1, tmp)); goto no; } /* * NOTE! Last 4 bits of the reg I12 tell the chip revision. * 0x01=RevB * 0x0A=RevC. also CS4231/CS4231A and OPTi931 */ BVDDB(printf("mss_detect - chip revision 0x%02x\n", tmp & 0x0f);) /* * The original AD1848/CS4248 has just 16 indirect registers. This * means that I0 and I16 should return the same value (etc.). Ensure * that the Mode2 enable bit of I12 is 0. Otherwise this test fails * with new parts. */ ad_write(mss, 12, 0); /* Mode2=disabled */ #if 0 for (i = 0; i < 16; i++) { if ((tmp1 = ad_read(mss, i)) != (tmp2 = ad_read(mss, i + 16))) { BVDDB(printf("mss_detect warning - I%d: 0x%02x/0x%02x\n", i, tmp1, tmp2)); /* * note - this seems to fail on the 4232 on I11. So we just break * rather than fail. (which makes this test pointless - cg) */ break; /* return 0; */ } } #endif /* * Try to switch the chip to mode2 (CS4231) by setting the MODE2 bit * (0x40). The bit 0x80 is always 1 in CS4248 and CS4231. * * On the OPTi931, however, I12 is readonly and only contains the * chip revision ID (as in the CS4231A). The upper bits return 0. */ ad_write(mss, 12, 0x40); /* Set mode2, clear 0x80 */ tmp1 = ad_read(mss, 12); if (tmp1 & 0x80) name = "CS4248"; /* Our best knowledge just now */ if ((tmp1 & 0xf0) == 0x00) { BVDDB(printf("this should be an OPTi931\n");) } else if ((tmp1 & 0xc0) != 0xC0) goto gotit; /* * The 4231 has bit7=1 always, and bit6 we just set to 1. * We want to check that this is really a CS4231 * Verify that setting I0 doesn't change I16. */ ad_write(mss, 16, 0); /* Set I16 to known value */ ad_write(mss, 0, 0x45); if ((tmp1 = ad_read(mss, 16)) == 0x45) goto gotit; ad_write(mss, 0, 0xaa); if ((tmp1 = ad_read(mss, 16)) == 0xaa) { /* Rotten bits? */ BVDDB(printf("mss_detect error - step H(%x)\n", tmp1)); goto no; } /* Verify that some bits of I25 are read only. */ tmp1 = ad_read(mss, 25); /* Original bits */ ad_write(mss, 25, ~tmp1); /* Invert all bits */ if ((ad_read(mss, 25) & 0xe7) == (tmp1 & 0xe7)) { int id; /* It's at least CS4231 */ name = "CS4231"; mss->bd_id = MD_CS42XX; /* * It could be an AD1845 or CS4231A as well. * CS4231 and AD1845 report the same revision info in I25 * while the CS4231A reports different. */ id = ad_read(mss, 25) & 0xe7; /* * b7-b5 = version number; * 100 : all CS4231 * 101 : CS4231A * * b2-b0 = chip id; */ switch (id) { case 0xa0: name = "CS4231A"; mss->bd_id = MD_CS42XX; break; case 0xa2: name = "CS4232"; mss->bd_id = MD_CS42XX; break; case 0xb2: /* strange: the 4231 data sheet says b4-b3 are XX * so this should be the same as 0xa2 */ name = "CS4232A"; mss->bd_id = MD_CS42XX; break; case 0x80: /* * It must be a CS4231 or AD1845. The register I23 * of CS4231 is undefined and it appears to be read * only. AD1845 uses I23 for setting sample rate. * Assume the chip is AD1845 if I23 is changeable. */ tmp = ad_read(mss, 23); ad_write(mss, 23, ~tmp); if (ad_read(mss, 23) != tmp) { /* AD1845 ? */ name = "AD1845"; mss->bd_id = MD_AD1845; } ad_write(mss, 23, tmp); /* Restore */ yamaha = ymf_test(dev, mss); if (yamaha) { mss->bd_id = MD_YM0020; name = yamaha; } break; case 0x83: /* CS4236 */ case 0x03: /* CS4236 on Intel PR440FX motherboard XXX */ name = "CS4236"; mss->bd_id = MD_CS42XX; break; default: /* Assume CS4231 */ BVDDB(printf("unknown id 0x%02x, assuming CS4231\n", id);) mss->bd_id = MD_CS42XX; } } ad_write(mss, 25, tmp1); /* Restore bits */ gotit: BVDDB(printf("mss_detect() - Detected %s\n", name)); device_set_desc(dev, name); device_set_flags(dev, ((device_get_flags(dev) & ~DV_F_DEV_MASK) | ((mss->bd_id << DV_F_DEV_SHIFT) & DV_F_DEV_MASK))); return 0; no: return ENXIO; } static char * ymf_test(device_t dev, struct mss_info *mss) { static int ports[] = {0x370, 0x310, 0x538}; int p, i, j, version; static char *chipset[] = { NULL, /* 0 */ "OPL3-SA2 (YMF711)", /* 1 */ "OPL3-SA3 (YMF715)", /* 2 */ "OPL3-SA3 (YMF715)", /* 3 */ "OPL3-SAx (YMF719)", /* 4 */ "OPL3-SAx (YMF719)", /* 5 */ "OPL3-SAx (YMF719)", /* 6 */ "OPL3-SAx (YMF719)", /* 7 */ }; for (p = 0; p < 3; p++) { mss->conf_rid = 1; mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid, ports[p], ports[p] + 1, 2, RF_ACTIVE); if (!mss->conf_base) return 0; /* Test the index port of the config registers */ i = port_rd(mss->conf_base, 0); port_wr(mss->conf_base, 0, OPL3SAx_DMACONF); j = (port_rd(mss->conf_base, 0) == OPL3SAx_DMACONF)? 1 : 0; port_wr(mss->conf_base, 0, i); if (!j) { bus_release_resource(dev, SYS_RES_IOPORT, mss->conf_rid, mss->conf_base); #ifdef PC98 /* PC98 need this. I don't know reason why. */ bus_delete_resource(dev, SYS_RES_IOPORT, mss->conf_rid); #endif mss->conf_base = 0; continue; } version = conf_rd(mss, OPL3SAx_MISC) & 0x07; return chipset[version]; } return NULL; } static int mss_doattach(device_t dev, struct mss_info *mss) { - int flags = device_get_flags(dev); + int pdma, rdma, flags = device_get_flags(dev); char status[SND_STATUSLEN]; if (!mss_alloc_resources(mss, dev)) goto no; mss_init(mss, dev); + pdma = rman_get_start(mss->drq1); + rdma = rman_get_start(mss->drq2); if (flags & DV_F_TRUE_MSS) { /* has IRQ/DMA registers, set IRQ and DMA addr */ #ifdef PC98 /* CS423[12] in PC98 can use IRQ3,5,10,12 */ static char interrupt_bits[13] = {-1, -1, -1, 0x08, -1, 0x10, -1, -1, -1, -1, 0x18, -1, 0x20}; #else static char interrupt_bits[12] = {-1, -1, -1, -1, -1, 0x28, -1, 0x08, -1, 0x10, 0x18, 0x20}; #endif static char pdma_bits[4] = {1, 2, -1, 3}; static char valid_rdma[4] = {1, 0, -1, 0}; char bits; if (!mss->irq || (bits = interrupt_bits[rman_get_start(mss->irq)]) == -1) goto no; #ifndef PC98 /* CS423[12] in PC98 don't support this. */ io_wr(mss, 0, bits | 0x40); /* config port */ if ((io_rd(mss, 3) & 0x40) == 0) device_printf(dev, "IRQ Conflict?\n"); #endif /* Write IRQ+DMA setup */ - if (pdma_bits[mss->pdma] == -1) goto no; - bits |= pdma_bits[mss->pdma]; - if (mss->pdma != mss->rdma) { - if (mss->rdma == valid_rdma[mss->pdma]) bits |= 4; + if (pdma_bits[pdma] == -1) goto no; + bits |= pdma_bits[pdma]; + if (pdma != rdma) { + if (rdma == valid_rdma[pdma]) bits |= 4; else { - printf("invalid dual dma config %d:%d\n", - mss->pdma, mss->rdma); + printf("invalid dual dma config %d:%d\n", pdma, rdma); goto no; } } io_wr(mss, 0, bits); printf("drq/irq conf %x\n", io_rd(mss, 0)); } - mixer_init(dev, (mss->bd_id == MD_YM0020)? &yamaha_mixer : &mss_mixer, mss); + mixer_init(dev, (mss->bd_id == MD_YM0020)? &ymmix_mixer_class : &mssmix_mixer_class, mss); switch (mss->bd_id) { case MD_OPTI931: bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, opti931_intr, mss, &mss->ih); break; default: bus_setup_intr(dev, mss->irq, INTR_TYPE_TTY, mss_intr, mss, &mss->ih); } - if (mss->pdma == mss->rdma) + if (pdma == rdma) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/MSS_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &mss->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %d", - rman_get_start(mss->io_base), rman_get_start(mss->irq), mss->pdma); - if (mss->pdma != mss->rdma) snprintf(status + strlen(status), - SND_STATUSLEN - strlen(status), ":%d", mss->rdma); + rman_get_start(mss->io_base), rman_get_start(mss->irq), pdma); + if (pdma != rdma) snprintf(status + strlen(status), + SND_STATUSLEN - strlen(status), ":%d", rdma); if (pcm_register(dev, mss, 1, 1)) goto no; - pcm_addchan(dev, PCMDIR_REC, &mss_chantemplate, mss); - pcm_addchan(dev, PCMDIR_PLAY, &mss_chantemplate, mss); + pcm_addchan(dev, PCMDIR_REC, &msschan_class, mss); + pcm_addchan(dev, PCMDIR_PLAY, &msschan_class, mss); pcm_setstatus(dev, status); return 0; no: mss_release_resources(mss, dev); return ENXIO; } static int mss_detach(device_t dev) { int r; struct mss_info *mss; r = pcm_unregister(dev); if (r) return r; mss = pcm_getdevinfo(dev); mss_release_resources(mss, dev); return 0; } static int mss_attach(device_t dev) { struct mss_info *mss; int flags = device_get_flags(dev); mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); if (!mss) return ENXIO; bzero(mss, sizeof *mss); mss->io_rid = 0; mss->conf_rid = -1; mss->irq_rid = 0; mss->drq1_rid = 0; mss->drq2_rid = -1; if (flags & DV_F_DUAL_DMA) { bus_set_resource(dev, SYS_RES_DRQ, 1, flags & DV_F_DRQ_MASK, 1); mss->drq2_rid = 1; } mss->bd_id = (device_get_flags(dev) & DV_F_DEV_MASK) >> DV_F_DEV_SHIFT; if (mss->bd_id == MD_YM0020) ymf_test(dev, mss); return mss_doattach(dev, mss); } /* * mss_resume() is the code to allow a laptop to resume using the sound * card. * * This routine re-sets the state of the board to the state before going * to sleep. According to the yamaha docs this is the right thing to do, * but getting DMA restarted appears to be a bit of a trick, so the device * has to be closed and re-opened to be re-used, but there is no skipping * problem, and volume, bass/treble and most other things are restored * properly. * */ static int mss_resume(device_t dev) { /* * Restore the state taken below. */ struct mss_info *mss; int i; mss = pcm_getdevinfo(dev); if (mss->bd_id == MD_YM0020) { /* This works on a Toshiba Libretto 100CT. */ for (i = 0; i < MSS_INDEXED_REGS; i++) ad_write(mss, i, mss->mss_indexed_regs[i]); for (i = 0; i < OPL_INDEXED_REGS; i++) conf_wr(mss, i, mss->opl_indexed_regs[i]); mss_intr(mss); } return 0; } /* * mss_suspend() is the code that gets called right before a laptop * suspends. * * This code saves the state of the sound card right before shutdown * so it can be restored above. * */ static int mss_suspend(device_t dev) { int i; struct mss_info *mss; mss = pcm_getdevinfo(dev); if(mss->bd_id == MD_YM0020) { /* this stops playback. */ conf_wr(mss, 0x12, 0x0c); for(i = 0; i < MSS_INDEXED_REGS; i++) mss->mss_indexed_regs[i] = ad_read(mss, i); for(i = 0; i < OPL_INDEXED_REGS; i++) mss->opl_indexed_regs[i] = conf_rd(mss, i); mss->opl_indexed_regs[0x12] = 0x0; } return 0; } static device_method_t mss_methods[] = { /* Device interface */ DEVMETHOD(device_probe, mss_probe), DEVMETHOD(device_attach, mss_attach), DEVMETHOD(device_detach, mss_detach), DEVMETHOD(device_suspend, mss_suspend), DEVMETHOD(device_resume, mss_resume), { 0, 0 } }; static driver_t mss_driver = { "pcm", mss_methods, sizeof(snddev_info), }; DRIVER_MODULE(snd_mss, isa, mss_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_mss, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_mss, 1); - -/* - * main irq handler for the CS423x. The OPTi931 code is - * a separate one. - * The correct way to operate for a device with multiple internal - * interrupt sources is to loop on the status register and ack - * interrupts until all interrupts are served and none are reported. At - * this point the IRQ line to the ISA IRQ controller should go low - * and be raised at the next interrupt. - * - * Since the ISA IRQ controller is sent EOI _before_ passing control - * to the isr, it might happen that we serve an interrupt early, in - * which case the status register at the next interrupt should just - * say that there are no more interrupts... - */ - -static void -mss_intr(void *arg) -{ - struct mss_info *mss = arg; - u_char c = 0, served = 0; - int i; - - DEB(printf("mss_intr\n")); - ad_read(mss, 11); /* fake read of status bits */ - - /* loop until there are interrupts, but no more than 10 times. */ - for (i = 10; i > 0 && io_rd(mss, MSS_STATUS) & 1; i--) { - /* get exact reason for full-duplex boards */ - c = FULL_DUPLEX(mss)? ad_read(mss, 24) : 0x30; - c &= ~served; - if (mss->pch.buffer->dl && (c & 0x10)) { - served |= 0x10; - chn_intr(mss->pch.channel); - } - if (mss->rch.buffer->dl && (c & 0x20)) { - served |= 0x20; - chn_intr(mss->rch.channel); - } - /* now ack the interrupt */ - if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */ - else io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ - } - if (i == 10) { - BVDDB(printf("mss_intr: irq, but not from mss\n")); - } else if (served == 0) { - BVDDB(printf("mss_intr: unexpected irq with reason %x\n", c)); - /* - * this should not happen... I have no idea what to do now. - * maybe should do a sanity check and restart dmas ? - */ - io_wr(mss, MSS_STATUS, 0); /* Clear interrupt status */ - } -} - -/* - * AD_WAIT_INIT waits if we are initializing the board and - * we cannot modify its settings - */ -static int -ad_wait_init(struct mss_info *mss, int x) -{ - int arg = x, n = 0; /* to shut up the compiler... */ - for (; x > 0; x--) - if ((n = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10); - else return n; - printf("AD_WAIT_INIT FAILED %d 0x%02x\n", arg, n); - return n; -} - -static int -ad_read(struct mss_info *mss, int reg) -{ - u_long flags; - int x; - - flags = spltty(); - ad_wait_init(mss, 201); - x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; - io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); - x = io_rd(mss, MSS_IDATA); - splx(flags); - /* printf("ad_read %d, %x\n", reg, x); */ - return x; -} - -static void -ad_write(struct mss_info *mss, int reg, u_char data) -{ - u_long flags; - - int x; - /* printf("ad_write %d, %x\n", reg, data); */ - flags = spltty(); - ad_wait_init(mss, 1002); - x = io_rd(mss, MSS_INDEX) & ~MSS_IDXMASK; - io_wr(mss, MSS_INDEX, (u_char)(reg & MSS_IDXMASK) | x); - io_wr(mss, MSS_IDATA, data); - splx(flags); -} - -static void -ad_write_cnt(struct mss_info *mss, int reg, u_short cnt) -{ - ad_write(mss, reg+1, cnt & 0xff); - ad_write(mss, reg, cnt >> 8); /* upper base must be last */ -} - -static void -wait_for_calibration(struct mss_info *mss) -{ - int t; - - /* - * Wait until the auto calibration process has finished. - * - * 1) Wait until the chip becomes ready (reads don't return 0x80). - * 2) Wait until the ACI bit of I11 gets on - * 3) Wait until the ACI bit of I11 gets off - */ - - t = ad_wait_init(mss, 1000); - if (t & MSS_IDXBUSY) printf("mss: Auto calibration timed out(1).\n"); - - /* - * The calibration mode for chips that support it is set so that - * we never see ACI go on. - */ - if (mss->bd_id == MD_GUSMAX || mss->bd_id == MD_GUSPNP) { - for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--); - } else { - /* - * XXX This should only be enabled for cards that *really* - * need it. Are there any? - */ - for (t = 100; t > 0 && (ad_read(mss, 11) & 0x20) == 0; t--) DELAY(100); - } - for (t = 100; t > 0 && ad_read(mss, 11) & 0x20; t--) DELAY(100); -} - -static void -ad_unmute(struct mss_info *mss) -{ - ad_write(mss, 6, ad_read(mss, 6) & ~I6_MUTE); - ad_write(mss, 7, ad_read(mss, 7) & ~I6_MUTE); -} - -static void -ad_enter_MCE(struct mss_info *mss) -{ - int prev; - - mss->bd_flags |= BD_F_MCE_BIT; - ad_wait_init(mss, 203); - prev = io_rd(mss, MSS_INDEX); - prev &= ~MSS_TRD; - io_wr(mss, MSS_INDEX, prev | MSS_MCE); -} - -static void -ad_leave_MCE(struct mss_info *mss) -{ - u_long flags; - u_char prev; - - if ((mss->bd_flags & BD_F_MCE_BIT) == 0) { - DEB(printf("--- hey, leave_MCE: MCE bit was not set!\n")); - return; - } - - ad_wait_init(mss, 1000); - - flags = spltty(); - mss->bd_flags &= ~BD_F_MCE_BIT; - - prev = io_rd(mss, MSS_INDEX); - prev &= ~MSS_TRD; - io_wr(mss, MSS_INDEX, prev & ~MSS_MCE); /* Clear the MCE bit */ - wait_for_calibration(mss); - splx(flags); -} - -/* - * only one source can be set... - */ -static int -mss_set_recsrc(struct mss_info *mss, int mask) -{ - u_char recdev; - - switch (mask) { - case SOUND_MASK_LINE: - case SOUND_MASK_LINE3: - recdev = 0; - break; - - case SOUND_MASK_CD: - case SOUND_MASK_LINE1: - recdev = 0x40; - break; - - case SOUND_MASK_IMIX: - recdev = 0xc0; - break; - - case SOUND_MASK_MIC: - default: - mask = SOUND_MASK_MIC; - recdev = 0x80; - } - ad_write(mss, 0, (ad_read(mss, 0) & 0x3f) | recdev); - ad_write(mss, 1, (ad_read(mss, 1) & 0x3f) | recdev); - return mask; -} - -/* there are differences in the mixer depending on the actual sound card. */ -static int -mss_mixer_set(struct mss_info *mss, int dev, int left, int right) -{ - int regoffs; - mixer_tab *mix_d = (mss->bd_id == MD_OPTI931)? &opti931_devices : &mix_devices; - u_char old, val; - - if ((*mix_d)[dev][LEFT_CHN].nbits == 0) { - DEB(printf("nbits = 0 for dev %d\n", dev)); - return -1; - } - - if ((*mix_d)[dev][RIGHT_CHN].nbits == 0) right = left; /* mono */ - - /* Set the left channel */ - - regoffs = (*mix_d)[dev][LEFT_CHN].regno; - old = val = ad_read(mss, regoffs); - /* if volume is 0, mute chan. Otherwise, unmute. */ - if (regoffs != 0) val = (left == 0)? old | 0x80 : old & 0x7f; - change_bits(mix_d, &val, dev, LEFT_CHN, left); - ad_write(mss, regoffs, val); - - DEB(printf("LEFT: dev %d reg %d old 0x%02x new 0x%02x\n", - dev, regoffs, old, val)); - - if ((*mix_d)[dev][RIGHT_CHN].nbits != 0) { /* have stereo */ - /* Set the right channel */ - regoffs = (*mix_d)[dev][RIGHT_CHN].regno; - old = val = ad_read(mss, regoffs); - if (regoffs != 1) val = (right == 0)? old | 0x80 : old & 0x7f; - change_bits(mix_d, &val, dev, RIGHT_CHN, right); - ad_write(mss, regoffs, val); - - DEB(printf("RIGHT: dev %d reg %d old 0x%02x new 0x%02x\n", - dev, regoffs, old, val)); - } - return 0; /* success */ -} - -static int -mss_speed(struct mss_chinfo *ch, int speed) -{ - struct mss_info *mss = ch->parent; - /* - * In the CS4231, the low 4 bits of I8 are used to hold the - * sample rate. Only a fixed number of values is allowed. This - * table lists them. The speed-setting routines scans the table - * looking for the closest match. This is the only supported method. - * - * In the CS4236, there is an alternate metod (which we do not - * support yet) which provides almost arbitrary frequency setting. - * In the AD1845, it looks like the sample rate can be - * almost arbitrary, and written directly to a register. - * In the OPTi931, there is a SB command which provides for - * almost arbitrary frequency setting. - * - */ - ad_enter_MCE(mss); - if (mss->bd_id == MD_AD1845) { /* Use alternate speed select regs */ - ad_write(mss, 22, (speed >> 8) & 0xff); /* Speed MSB */ - ad_write(mss, 23, speed & 0xff); /* Speed LSB */ - /* XXX must also do something in I27 for the ad1845 */ - } else { - int i, sel = 0; /* assume entry 0 does not contain -1 */ - static int speeds[] = - {8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, - -1, 37800, -1, 44100, 48000, 33075, 9600, 6615}; - - for (i = 1; i < 16; i++) - if (speeds[i] > 0 && - abs(speed-speeds[i]) < abs(speed-speeds[sel])) sel = i; - speed = speeds[sel]; - ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel); - } - ad_leave_MCE(mss); - - return speed; -} - -/* - * mss_format checks that the format is supported (or defaults to AFMT_U8) - * and returns the bit setting for the 1848 register corresponding to - * the desired format. - * - * fixed lr970724 - */ - -static int -mss_format(struct mss_chinfo *ch, u_int32_t format) -{ - struct mss_info *mss = ch->parent; - int i, arg = format & ~AFMT_STEREO; - - /* - * The data format uses 3 bits (just 2 on the 1848). For each - * bit setting, the following array returns the corresponding format. - * The code scans the array looking for a suitable format. In - * case it is not found, default to AFMT_U8 (not such a good - * choice, but let's do it for compatibility...). - */ - - static int fmts[] = - {AFMT_U8, AFMT_MU_LAW, AFMT_S16_LE, AFMT_A_LAW, - -1, AFMT_IMA_ADPCM, AFMT_U16_BE, -1}; - - ch->fmt = format; - for (i = 0; i < 8; i++) if (arg == fmts[i]) break; - arg = i << 1; - if (format & AFMT_STEREO) arg |= 1; - arg <<= 4; - ad_enter_MCE(mss); - ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg); - if (FULL_DUPLEX(mss)) ad_write(mss, 28, arg); /* capture mode */ - ad_leave_MCE(mss); - return format; -} - -static int -mss_trigger(struct mss_chinfo *ch, int go) -{ - struct mss_info *mss = ch->parent; - u_char m; - int retry, wr, cnt, ss; - - ss = 1; - ss <<= (ch->fmt & AFMT_STEREO)? 1 : 0; - ss <<= (ch->fmt & AFMT_16BIT)? 1 : 0; - - wr = (ch->dir == PCMDIR_PLAY)? 1 : 0; - m = ad_read(mss, 9); - switch (go) { - case PCMTRIG_START: - cnt = (ch->buffer->dl / ss) - 1; - - DEB(if (m & 4) printf("OUCH! reg 9 0x%02x\n", m);); - m |= wr? I9_PEN : I9_CEN; /* enable DMA */ - ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, cnt); - break; - - case PCMTRIG_STOP: - case PCMTRIG_ABORT: /* XXX check this... */ - m &= ~(wr? I9_PEN : I9_CEN); /* Stop DMA */ -#if 0 - /* - * try to disable DMA by clearing count registers. Not sure it - * is needed, and it might cause false interrupts when the - * DMA is re-enabled later. - */ - ad_write_cnt(mss, (wr || !FULL_DUPLEX(mss))? 14 : 30, 0); -#endif - } - /* on the OPTi931 the enable bit seems hard to set... */ - for (retry = 10; retry > 0; retry--) { - ad_write(mss, 9, m); - if (ad_read(mss, 9) == m) break; - } - if (retry == 0) BVDDB(printf("stop dma, failed to set bit 0x%02x 0x%02x\n", \ - m, ad_read(mss, 9))); - return 0; -} - static struct isa_pnp_id pnpmss_ids[] = { {0x0000630e, "CS423x"}, /* CSC0000 */ {0x0001630e, "CS423x-PCI"}, /* CSC0100 */ {0x01000000, "CMI8330"}, /* @@@0001 */ {0x2100a865, "Yamaha OPL-SAx"}, /* YMH0021 */ {0x1110d315, "ENSONIQ SoundscapeVIVO"}, /* ENS1011 */ {0x1093143e, "OPTi931"}, /* OPT9310 */ {0x5092143e, "OPTi925"}, /* OPT9250 XXX guess */ {0x1022b839, "Neomagic 256AV (non-ac97)"}, /* NMX2210 */ #if 0 {0x0000561e, "GusPnP"}, /* GRV0000 */ #endif {0}, }; static int pnpmss_probe(device_t dev) { u_int32_t lid, vid; lid = isa_get_logicalid(dev); vid = isa_get_vendorid(dev); if (lid == 0x01000000 && vid != 0x0100a90d) /* CMI0001 */ return ENXIO; return ISA_PNP_PROBE(device_get_parent(dev), dev, pnpmss_ids); } static int pnpmss_attach(device_t dev) { struct mss_info *mss; mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); if (!mss) return ENXIO; bzero(mss, sizeof *mss); mss->io_rid = 0; mss->conf_rid = -1; mss->irq_rid = 0; mss->drq1_rid = 0; mss->drq2_rid = 1; mss->bd_id = MD_CS42XX; switch (isa_get_logicalid(dev)) { case 0x0000630e: /* CSC0000 */ case 0x0001630e: /* CSC0100 */ mss->bd_flags |= BD_F_MSS_OFFSET; break; case 0x2100a865: /* YHM0021 */ mss->io_rid = 1; mss->conf_rid = 4; mss->bd_id = MD_YM0020; break; case 0x1110d315: /* ENS1011 */ mss->io_rid = 1; mss->bd_id = MD_VIVO; break; case 0x1093143e: /* OPT9310 */ mss->bd_flags |= BD_F_MSS_OFFSET; mss->conf_rid = 3; mss->bd_id = MD_OPTI931; break; case 0x5092143e: /* OPT9250 XXX guess */ mss->io_rid = 1; mss->conf_rid = 3; mss->bd_id = MD_OPTI925; break; case 0x1022b839: /* NMX2210 */ mss->io_rid = 1; break; #if 0 case 0x0000561e: /* GRV0000 */ mss->bd_flags |= BD_F_MSS_OFFSET; mss->io_rid = 2; mss->conf_rid = 1; mss->drq1_rid = 1; mss->drq2_rid = 0; mss->bd_id = MD_GUSPNP; break; #endif case 0x01000000: /* @@@0001 */ mss->drq2_rid = -1; break; /* Unknown MSS default. We could let the CSC0000 stuff match too */ default: mss->bd_flags |= BD_F_MSS_OFFSET; break; } return mss_doattach(dev, mss); } static device_method_t pnpmss_methods[] = { /* Device interface */ DEVMETHOD(device_probe, pnpmss_probe), DEVMETHOD(device_attach, pnpmss_attach), DEVMETHOD(device_detach, mss_detach), DEVMETHOD(device_suspend, mss_suspend), DEVMETHOD(device_resume, mss_resume), { 0, 0 } }; static driver_t pnpmss_driver = { "pcm", pnpmss_methods, sizeof(snddev_info), }; DRIVER_MODULE(snd_pnpmss, isa, pnpmss_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_pnpmss, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_pnpmss, 1); - -/* - * the opti931 seems to miss interrupts when working in full - * duplex, so we try some heuristics to catch them. - */ -static void -opti931_intr(void *arg) -{ - struct mss_info *mss = (struct mss_info *)arg; - u_char masked = 0, i11, mc11, c = 0; - u_char reason; /* b0 = playback, b1 = capture, b2 = timer */ - int loops = 10; - -#if 0 - reason = io_rd(mss, MSS_STATUS); - if (!(reason & 1)) {/* no int, maybe a shared line ? */ - DEB(printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11))); - return; - } -#endif - i11 = ad_read(mss, 11); /* XXX what's for ? */ - again: - - c = mc11 = FULL_DUPLEX(mss)? opti_rd(mss, 11) : 0xc; - mc11 &= 0x0c; - if (c & 0x10) { - DEB(printf("Warning: CD interrupt\n");) - mc11 |= 0x10; - } - if (c & 0x20) { - DEB(printf("Warning: MPU interrupt\n");) - mc11 |= 0x20; - } - if (mc11 & masked) BVDDB(printf("irq reset failed, mc11 0x%02x, 0x%02x\n",\ - mc11, masked)); - masked |= mc11; - /* - * the nice OPTi931 sets the IRQ line before setting the bits in - * mc11. So, on some occasions I have to retry (max 10 times). - */ - if (mc11 == 0) { /* perhaps can return ... */ - reason = io_rd(mss, MSS_STATUS); - if (reason & 1) { - DEB(printf("one more try...\n");) - if (--loops) goto again; - else DDB(printf("intr, but mc11 not set\n");) - } - if (loops == 0) BVDDB(printf("intr, nothing in mcir11 0x%02x\n", mc11)); - return; - } - - if (mss->rch.buffer->dl && (mc11 & 8)) chn_intr(mss->rch.channel); - if (mss->pch.buffer->dl && (mc11 & 4)) chn_intr(mss->pch.channel); - opti_wr(mss, 11, ~mc11); /* ack */ - if (--loops) goto again; - DEB(printf("xxx too many loops\n");) -} - static int guspcm_probe(device_t dev) { struct sndcard_func *func; func = device_get_ivars(dev); if (func == NULL || func->func != SCF_PCM) return ENXIO; device_set_desc(dev, "GUS CS4231"); return 0; } static int guspcm_attach(device_t dev) { device_t parent = device_get_parent(dev); struct mss_info *mss; int base, flags; unsigned char ctl; mss = (struct mss_info *)malloc(sizeof *mss, M_DEVBUF, M_NOWAIT); if (mss == NULL) return ENOMEM; bzero(mss, sizeof *mss); mss->bd_flags = BD_F_MSS_OFFSET; mss->io_rid = 2; mss->conf_rid = 1; mss->irq_rid = 0; mss->drq1_rid = 1; mss->drq2_rid = -1; if (isa_get_logicalid(parent) == 0) mss->bd_id = MD_GUSMAX; else { mss->bd_id = MD_GUSPNP; mss->drq2_rid = 0; goto skip_setup; } flags = device_get_flags(parent); if (flags & DV_F_DUAL_DMA) mss->drq2_rid = 0; mss->conf_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &mss->conf_rid, 0, ~0, 8, RF_ACTIVE); if (mss->conf_base == NULL) { mss_release_resources(mss, dev); return ENXIO; } base = isa_get_port(parent); ctl = 0x40; /* CS4231 enable */ if (isa_get_drq(dev) > 3) ctl |= 0x10; /* 16-bit dma channel 1 */ if ((flags & DV_F_DUAL_DMA) != 0 && (flags & DV_F_DRQ_MASK) > 3) ctl |= 0x20; /* 16-bit dma channel 2 */ ctl |= (base >> 4) & 0x0f; /* 2X0 -> 3XC */ port_wr(mss->conf_base, 6, ctl); skip_setup: return mss_doattach(dev, mss); } static device_method_t guspcm_methods[] = { DEVMETHOD(device_probe, guspcm_probe), DEVMETHOD(device_attach, guspcm_attach), DEVMETHOD(device_detach, mss_detach), { 0, 0 } }; static driver_t guspcm_driver = { "pcm", guspcm_methods, sizeof(snddev_info), }; DRIVER_MODULE(snd_guspcm, gusc, guspcm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_guspcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_guspcm, 1); -static int -mssmix_init(snd_mixer *m) -{ - struct mss_info *mss = mix_getdevinfo(m); - - mix_setdevs(m, MODE2_MIXER_DEVICES); - mix_setrecdevs(m, MSS_REC_DEVICES); - switch(mss->bd_id) { - case MD_OPTI931: - mix_setdevs(m, OPTI931_MIXER_DEVICES); - ad_write(mss, 20, 0x88); - ad_write(mss, 21, 0x88); - break; - - case MD_AD1848: - mix_setdevs(m, MODE1_MIXER_DEVICES); - break; - - case MD_GUSPNP: - case MD_GUSMAX: - /* this is only necessary in mode 3 ... */ - ad_write(mss, 22, 0x88); - ad_write(mss, 23, 0x88); - break; - } - return 0; -} - -static int -mssmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) -{ - struct mss_info *mss = mix_getdevinfo(m); - - mss_mixer_set(mss, dev, left, right); - - return left | (right << 8); -} - -static int -mssmix_setrecsrc(snd_mixer *m, u_int32_t src) -{ - struct mss_info *mss = mix_getdevinfo(m); - - src = mss_set_recsrc(mss, src); - return src; -} - -static int -ymmix_init(snd_mixer *m) -{ - struct mss_info *mss = mix_getdevinfo(m); - - mssmix_init(m); - mix_setdevs(m, mix_getdevs(m) | SOUND_MASK_VOLUME | SOUND_MASK_MIC - | SOUND_MASK_BASS | SOUND_MASK_TREBLE); - /* Set master volume */ - conf_wr(mss, OPL3SAx_VOLUMEL, 7); - conf_wr(mss, OPL3SAx_VOLUMER, 7); - - return 0; -} - -static int -ymmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) -{ - struct mss_info *mss = mix_getdevinfo(m); - int t, l, r; - - switch (dev) { - case SOUND_MIXER_VOLUME: - if (left) t = 15 - (left * 15) / 100; - else t = 0x80; /* mute */ - conf_wr(mss, OPL3SAx_VOLUMEL, t); - if (right) t = 15 - (right * 15) / 100; - else t = 0x80; /* mute */ - conf_wr(mss, OPL3SAx_VOLUMER, t); - break; - - case SOUND_MIXER_MIC: - t = left; - if (left) t = 31 - (left * 31) / 100; - else t = 0x80; /* mute */ - conf_wr(mss, OPL3SAx_MIC, t); - break; - - case SOUND_MIXER_BASS: - l = (left * 7) / 100; - r = (right * 7) / 100; - t = (r << 4) | l; - conf_wr(mss, OPL3SAx_BASS, t); - break; - - case SOUND_MIXER_TREBLE: - l = (left * 7) / 100; - r = (right * 7) / 100; - t = (r << 4) | l; - conf_wr(mss, OPL3SAx_TREBLE, t); - break; - - default: - mss_mixer_set(mss, dev, left, right); - } - - return left | (right << 8); -} - -static int -ymmix_setrecsrc(snd_mixer *m, u_int32_t src) -{ - struct mss_info *mss = mix_getdevinfo(m); - src = mss_set_recsrc(mss, src); - return src; -} - -/* channel interface */ -static void * -msschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) -{ - struct mss_info *mss = devinfo; - struct mss_chinfo *ch = (dir == PCMDIR_PLAY)? &mss->pch : &mss->rch; - - ch->parent = mss; - ch->channel = c; - ch->buffer = b; - ch->buffer->bufsize = MSS_BUFFSIZE; - if (chn_allocbuf(ch->buffer, mss->parent_dmat) == -1) return NULL; - return ch; -} - -static int -msschan_setdir(void *data, int dir) -{ - struct mss_chinfo *ch = data; - - ch->buffer->chan = (dir == PCMDIR_PLAY)? ch->parent->pdma : ch->parent->rdma; - ch->dir = dir; - return 0; -} - -static int -msschan_setformat(void *data, u_int32_t format) -{ - struct mss_chinfo *ch = data; - - mss_format(ch, format); - return 0; -} - -static int -msschan_setspeed(void *data, u_int32_t speed) -{ - struct mss_chinfo *ch = data; - - return mss_speed(ch, speed); -} - -static int -msschan_setblocksize(void *data, u_int32_t blocksize) -{ - return blocksize; -} - -static int -msschan_trigger(void *data, int go) -{ - struct mss_chinfo *ch = data; - - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) - return 0; - - buf_isadma(ch->buffer, go); - mss_trigger(ch, go); - return 0; -} - -static int -msschan_getptr(void *data) -{ - struct mss_chinfo *ch = data; - return buf_isadmaptr(ch->buffer); -} - -static pcmchan_caps * -msschan_getcaps(void *data) -{ - struct mss_chinfo *ch = data; - - switch(ch->parent->bd_id) { - case MD_OPTI931: - return &opti931_caps; - break; - - case MD_GUSPNP: - case MD_GUSMAX: - return &guspnp_caps; - break; - - default: - return &mss_caps; - break; - } -} Index: stable/4/sys/dev/sound/isa/mss.h =================================================================== --- stable/4/sys/dev/sound/isa/mss.h (revision 71948) +++ stable/4/sys/dev/sound/isa/mss.h (revision 71949) @@ -1,382 +1,382 @@ /* * file: mss.h * * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) * * This file contains information and macro definitions for * AD1848-compatible devices, used in the MSS/WSS compatible boards. * */ /* * The codec part of the board is seen as a set of 4 registers mapped at the base address for the board (default 0x534). Note that some (early) boards implemented 4 additional registers 4 location before (usually 0x530) to store configuration information. This is a source of confusion in that one never knows what address to specify. The (current) convention is to use the old address (0x530) in the kernel configuration file and consider MSS registers start four location ahead. * */ /* * The four visible registers of the MSS : * */ #define MSS_INDEX (0 + 4) #define MSS_IDXBUSY 0x80 /* readonly, set when busy */ #define MSS_MCE 0x40 /* the MCE bit. */ /* * the MCE bit must be set whenever the current mode of the * codec is changed; this in particular is true for the * Data Format (I8, I28) and Interface Config(I9) registers. * Only exception are CEN and PEN which can be changed on the fly. * The DAC output is muted when MCE is set. */ #define MSS_TRD 0x20 /* Transfer request disable */ /* * When TRD is set, DMA transfers cease when the INT bit in * the MSS status reg is set. Must be cleared for automode * DMA, set otherwise. */ #define MSS_IDXMASK 0x1f /* mask for indirect address */ #define MSS_IDATA (1 + 4) /* * data to be transferred to the indirect register addressed * by index addr. During init and sw. powerdown, cannot be * written to, and is always read as 0x80 (consistent with the * busy flag). */ #define MSS_STATUS (2 + 4) #define IS_CUL 0x80 /* capture upper/lower */ #define IS_CLR 0x40 /* capture left/right */ #define IS_CRDY 0x20 /* capture ready for programmed i/o */ #define IS_SER 0x10 /* sample error (overrun/underrun) */ #define IS_PUL 0x08 /* playback upper/lower */ #define IS_PLR 0x04 /* playback left/right */ #define IS_PRDY 0x02 /* playback ready for programmed i/o */ #define IS_INT 0x01 /* int status (1 = active) */ /* * IS_INT is clreared by any write to the status register. */ #if 0 #define io_Polled_IO(d) ((d)->io_base+3+4) /* * this register is used in case of polled i/o */ #endif /* * The MSS has a set of 16 (or 32 depending on the model) indirect * registers accessible through the data port by specifying the * appropriate address in the address register. * * The 16 low registers are uniformly handled in AD1848/CS4248 compatible * mode (often called MODE1). For the upper 16 registers there are * some differences among different products, mainly Crystal uses them * differently from OPTi. * */ /* * volume registers */ #define I6_MUTE 0x80 /* * register I9 -- interface configuration. */ #define I9_PEN 0x01 /* playback enable */ #define I9_CEN 0x02 /* capture enable */ /* * values used in bd_flags */ #define BD_F_MCE_BIT 0x0001 #define BD_F_IRQ_OK 0x0002 #define BD_F_TMR_RUN 0x0004 #define BD_F_MSS_OFFSET 0x0008 /* offset mss writes by -4 */ #define BD_F_DUPLEX 0x0010 /* * sound/ad1848_mixer.h * * Definitions for the mixer of AD1848 and compatible codecs. * * Copyright by Hannu Savolainen 1994 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * The AD1848 codec has generic input lines called Line, Aux1 and Aux2. * Soundcard manufacturers have connected actual inputs (CD, synth, line, * etc) to these inputs in different order. Therefore it's difficult * to assign mixer channels to to these inputs correctly. The following * contains two alternative mappings. The first one is for GUS MAX and * the second is just a generic one (line1, line2 and line3). * (Actually this is not a mapping but rather some kind of interleaving * solution). */ #define MSS_REC_DEVICES \ (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD|SOUND_MASK_IMIX) /* * Table of mixer registers. There is a default table for the * AD1848/CS423x clones, and one for the OPTI931. As more MSS * clones come out, there ought to be more tables. * * Fields in the table are : polarity, register, offset, bits * * The channel numbering used by individual soundcards is not fixed. * Some cards have assigned different meanings for the AUX1, AUX2 * and LINE inputs. Some have different features... * * Following there is a macro ...MIXER_DEVICES which is a bitmap * of all non-zero fields in the table. * MODE1_MIXER_DEVICES is the basic mixer of the 1848 in mode 1 * registers I0..I15) * */ mixer_ent mix_devices[32][2] = { MIX_NONE(SOUND_MIXER_VOLUME), MIX_NONE(SOUND_MIXER_BASS), MIX_NONE(SOUND_MIXER_TREBLE), -#ifdef PC98 /* PC98's synth is assigned to AUX#2 */ +#ifdef PC98 /* PC98's synth is assigned to AUX#2 */ MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5), -#else /* AT386's synth is assigned to AUX#1 */ +#else /* AT386's synth is assigned to AUX#1 */ MIX_ENT(SOUND_MIXER_SYNTH, 2, 1, 0, 5, 3, 1, 0, 5), #endif MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 6, 7, 1, 0, 6), MIX_ENT(SOUND_MIXER_SPEAKER, 26, 1, 0, 4, 0, 0, 0, 0), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 0, 5, 19, 1, 0, 5), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), -#ifdef PC98 /* PC98's cd-audio is assigned to AUX#1 */ +#ifdef PC98 /* PC98's cd-audio is assigned to AUX#1 */ MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5), -#else /* AT386's cd-audio is assigned to AUX#2 */ +#else /* AT386's cd-audio is assigned to AUX#2 */ MIX_ENT(SOUND_MIXER_CD, 4, 1, 0, 5, 5, 1, 0, 5), #endif MIX_ENT(SOUND_MIXER_IMIX, 13, 1, 2, 6, 0, 0, 0, 0), MIX_NONE(SOUND_MIXER_ALTPCM), MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), MIX_NONE(SOUND_MIXER_OGAIN), MIX_NONE(SOUND_MIXER_LINE1), MIX_NONE(SOUND_MIXER_LINE2), MIX_NONE(SOUND_MIXER_LINE3), }; #define MODE2_MIXER_DEVICES \ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) #define MODE1_MIXER_DEVICES \ (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_MIC | \ SOUND_MASK_CD | SOUND_MASK_IMIX | SOUND_MASK_IGAIN ) /* * entries for the opti931... */ mixer_ent opti931_devices[32][2] = { /* for the opti931 */ MIX_ENT(SOUND_MIXER_VOLUME, 22, 1, 1, 5, 23, 1, 1, 5), MIX_NONE(SOUND_MIXER_BASS), MIX_NONE(SOUND_MIXER_TREBLE), MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 1, 4, 5, 1, 1, 4), MIX_ENT(SOUND_MIXER_PCM, 6, 1, 0, 5, 7, 1, 0, 5), MIX_NONE(SOUND_MIXER_SPEAKER), MIX_ENT(SOUND_MIXER_LINE, 18, 1, 1, 4, 19, 1, 1, 4), MIX_ENT(SOUND_MIXER_MIC, 0, 0, 5, 1, 1, 0, 5, 1), MIX_ENT(SOUND_MIXER_CD, 2, 1, 1, 4, 3, 1, 1, 4), MIX_NONE(SOUND_MIXER_IMIX), MIX_NONE(SOUND_MIXER_ALTPCM), MIX_NONE(SOUND_MIXER_RECLEV), MIX_ENT(SOUND_MIXER_IGAIN, 0, 0, 0, 4, 1, 0, 0, 4), MIX_NONE(SOUND_MIXER_OGAIN), MIX_ENT(SOUND_MIXER_LINE1, 16, 1, 1, 4, 17, 1, 1, 4), MIX_NONE(SOUND_MIXER_LINE2), MIX_NONE(SOUND_MIXER_LINE3), }; #define OPTI931_MIXER_DEVICES \ (SOUND_MASK_VOLUME | SOUND_MASK_SYNTH | SOUND_MASK_PCM | \ SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ SOUND_MASK_IGAIN | SOUND_MASK_LINE1 ) /*- * Copyright (c) 1999 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Register definitions for the Yamaha OPL3-SA[23x]. */ #define OPL3SAx_POWER 0x01 /* Power Management (R/W) */ #define OPL3SAx_POWER_PDX 0x01 /* Set to 1 to halt oscillator */ #define OPL3SAx_POWER_PDN 0x02 /* Set to 1 to power down */ #define OPL3SAx_POWER_PSV 0x04 /* Set to 1 to power save */ #define OPL3SAx_POWER_ADOWN 0x20 /* Analog power (?) */ #define OPL3SAx_SYSTEM 0x02 /* System control (R/W) */ #define OPL3SAx_SYSTEM_VZE 0x01 /* I2S audio routing */ #define OPL3SAx_SYSTEM_IDSEL 0x03 /* SB compat version select */ #define OPL3SAx_SYSTEM_SBHE 0x80 /* 0 for AT bus, 1 for XT bus */ #define OPL3SAx_IRQCONF 0x03 /* Interrupt configuration (R/W */ #define OPL3SAx_IRQCONF_WSSA 0x01 /* WSS interrupts through IRQA */ #define OPL3SAx_IRQCONF_SBA 0x02 /* WSS interrupts through IRQA */ #define OPL3SAx_IRQCONF_MPUA 0x04 /* WSS interrupts through IRQA */ #define OPL3SAx_IRQCONF_OPL3A 0x08 /* WSS interrupts through IRQA */ #define OPL3SAx_IRQCONF_WSSB 0x10 /* WSS interrupts through IRQB */ #define OPL3SAx_IRQCONF_SBB 0x20 /* WSS interrupts through IRQB */ #define OPL3SAx_IRQCONF_MPUB 0x40 /* WSS interrupts through IRQB */ #define OPL3SAx_IRQCONF_OPL3B 0x80 /* WSS interrupts through IRQB */ #define OPL3SAx_IRQSTATUSA 0x04 /* Interrupt (IRQ-A) Status (RO) */ #define OPL3SAx_IRQSTATUSB 0x05 /* Interrupt (IRQ-B) Status (RO) */ #define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */ #define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */ #define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */ #define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */ #define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */ #define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */ #define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */ #define OPL3SAx_IRQSTATUS_PI 0x01 /* Playback Flag of CODEC */ #define OPL3SAx_IRQSTATUS_CI 0x02 /* Recording Flag of CODEC */ #define OPL3SAx_IRQSTATUS_TI 0x04 /* Timer Flag of CODEC */ #define OPL3SAx_IRQSTATUS_SB 0x08 /* SB compat Playback Interrupt Flag */ #define OPL3SAx_IRQSTATUS_MPU 0x10 /* MPU401 Interrupt Flag */ #define OPL3SAx_IRQSTATUS_OPL3 0x20 /* Internal FM Timer Flag */ #define OPL3SAx_IRQSTATUS_MV 0x40 /* HW Volume Interrupt Flag */ #define OPL3SAx_DMACONF 0x06 /* DMA configuration (R/W) */ #define OPL3SAx_DMACONF_WSSPA 0x01 /* WSS Playback on DMA-A */ #define OPL3SAx_DMACONF_WSSRA 0x02 /* WSS Recording on DMA-A */ #define OPL3SAx_DMACONF_SBA 0x02 /* SB Playback on DMA-A */ #define OPL3SAx_DMACONF_WSSPB 0x10 /* WSS Playback on DMA-A */ #define OPL3SAx_DMACONF_WSSRB 0x20 /* WSS Recording on DMA-A */ #define OPL3SAx_DMACONF_SBB 0x20 /* SB Playback on DMA-A */ #define OPL3SAx_VOLUMEL 0x07 /* Master Volume Left (R/W) */ #define OPL3SAx_VOLUMEL_MVL 0x0f /* Attenuation level */ #define OPL3SAx_VOLUMEL_MVLM 0x80 /* Mute */ #define OPL3SAx_VOLUMER 0x08 /* Master Volume Right (R/W) */ #define OPL3SAx_VOLUMER_MVR 0x0f /* Attenuation level */ #define OPL3SAx_VOLUMER_MVRM 0x80 /* Mute */ #define OPL3SAx_MIC 0x09 /* MIC Volume (R/W) */ #define OPL3SAx_VOLUMER_MCV 0x1f /* Attenuation level */ #define OPL3SAx_VOLUMER_MICM 0x80 /* Mute */ #define OPL3SAx_MISC 0x0a /* Miscellaneous */ #define OPL3SAx_MISC_VER 0x07 /* Version */ #define OPL3SAx_MISC_MODE 0x08 /* SB or WSS mode */ #define OPL3SAx_MISC_MCSW 0x10 /* */ #define OPL3SAx_MISC_VEN 0x80 /* Enable hardware volume control */ #define OPL3SAx_WSSDMA 0x0b /* WSS DMA Counter (RW) (4 regs) */ #define OPL3SAx_WSSIRQSCAN 0x0f /* WSS Interrupt Scan out/in (R/W) */ #define OPL3SAx_WSSIRQSCAN_SPI 0x01 #define OPL3SAx_WSSIRQSCAN_SCI 0x02 #define OPL3SAx_WSSIRQSCAN_STI 0x04 #define OPL3SAx_SBSTATE 0x10 /* SB compat Internal State (R/W) */ #define OPL3SAx_SBSTATE_SBPDR 0x01 /* SB Power Down Request */ #define OPL3SAx_SBSTATE_SE 0x02 /* Scan Enable */ #define OPL3SAx_SBSTATE_SM 0x04 /* Scan Mode */ #define OPL3SAx_SBSTATE_SS 0x08 /* Scan Select */ #define OPL3SAx_SBSTATE_SBPDA 0x80 /* SB Power Down Acknowledge */ #define OPL3SAx_SBDATA 0x11 /* SB compat State Scan Data (R/W) */ #define OPL3SAx_DIGITALPOWER 0x12 /* Digital Partial Power Down (R/W) */ #define OPL3SAx_DIGITALPOWER_PnP 0x01 #define OPL3SAx_DIGITALPOWER_SB 0x02 #define OPL3SAx_DIGITALPOWER_WSSP 0x04 #define OPL3SAx_DIGITALPOWER_WSSR 0x08 #define OPL3SAx_DIGITALPOWER_FM 0x10 #define OPL3SAx_DIGITALPOWER_MCLK0 0x20 #define OPL3SAx_DIGITALPOWER_MPU 0x40 #define OPL3SAx_DIGITALPOWER_JOY 0x80 #define OPL3SAx_ANALOGPOWER 0x13 /* Analog Partial Power Down (R/W) */ #define OPL3SAx_ANALOGPOWER_WIDE 0x01 #define OPL3SAx_ANALOGPOWER_SBDAC 0x02 #define OPL3SAx_ANALOGPOWER_DA 0x04 #define OPL3SAx_ANALOGPOWER_AD 0x08 #define OPL3SAx_ANALOGPOWER_FMDAC 0x10 #define OPL3SAx_WIDE 0x14 /* Enhanced control(WIDE) (R/W) */ #define OPL3SAx_WIDE_WIDEL 0x07 /* Wide level on Left Channel */ #define OPL3SAx_WIDE_WIDER 0x70 /* Wide level on Right Channel */ #define OPL3SAx_BASS 0x15 /* Enhanced control(BASS) (R/W) */ #define OPL3SAx_BASS_BASSL 0x07 /* Bass level on Left Channel */ #define OPL3SAx_BASS_BASSR 0x70 /* Bass level on Right Channel */ #define OPL3SAx_TREBLE 0x16 /* Enhanced control(TREBLE) (R/W) */ #define OPL3SAx_TREBLE_TREBLEL 0x07 /* Treble level on Left Channel */ #define OPL3SAx_TREBLE_TREBLER 0x70 /* Treble level on Right Channel */ #define OPL3SAx_HWVOL 0x17 /* HW Volume IRQ Configuration (R/W) */ #define OPL3SAx_HWVOL_IRQA 0x10 /* HW Volume IRQ on IRQ-A */ #define OPL3SAx_HWVOL_IRQB 0x20 /* HW Volume IRQ on IRQ-B */ Index: stable/4/sys/dev/sound/isa/sb.h =================================================================== --- stable/4/sys/dev/sound/isa/sb.h (revision 71948) +++ stable/4/sys/dev/sound/isa/sb.h (revision 71949) @@ -1,276 +1,187 @@ /* * file: sbcard.h * $FreeBSD$ */ /* * sound blaster registers */ #define SBDSP_RST 0x6 #define DSP_READ 0xA #define DSP_WRITE 0xC #define SBDSP_CMD 0xC #define SBDSP_STATUS 0xC #define DSP_DATA_AVAIL 0xE #define DSP_DATA_AVL16 0xF #define SB_MIX_ADDR 0x4 #define SB_MIX_DATA 0x5 #if 0 #define OPL3_LEFT (io_base + 0x0) #define OPL3_RIGHT (io_base + 0x2) #define OPL3_BOTH (io_base + 0x8) #endif /* * DSP Commands. There are many, and in many cases they are used explicitly */ /* these are not used except for programmed I/O (not in this driver) */ #define DSP_DAC8 0x10 /* direct DAC output */ #define DSP_ADC8 0x20 /* direct ADC input */ /* these should be used in the SB 1.0 */ #define DSP_CMD_DAC8 0x14 /* single cycle 8-bit dma out */ #define DSP_CMD_ADC8 0x24 /* single cycle 8-bit dma in */ /* these should be used in the SB 2.0 and 2.01 */ #define DSP_CMD_DAC8_AUTO 0x1c /* auto 8-bit dma out */ #define DSP_CMD_ADC8_AUTO 0x2c /* auto 8-bit dma out */ #define DSP_CMD_HSSIZE 0x48 /* high speed dma count */ #define DSP_CMD_HSDAC_AUTO 0x90 /* high speed dac, auto */ #define DSP_CMD_HSADC_AUTO 0x98 /* high speed adc, auto */ /* SBPro commands. Some cards (JAZZ, SMW) also support 16 bits */ /* prepare for dma input */ #define DSP_CMD_DMAMODE(stereo, bit16) (0xA0 | (stereo ? 8:0) | (bit16 ? 4:0)) #define DSP_CMD_DAC2 0x16 /* 2-bit adpcm dma out (cont) */ #define DSP_CMD_DAC2S 0x17 /* 2-bit adpcm dma out (start) */ #define DSP_CMD_DAC2S_AUTO 0x1f /* auto 2-bit adpcm dma out (start) */ /* SB16 commands */ #define DSP_CMD_O16 0xb0 #define DSP_CMD_I16 0xb8 #define DSP_CMD_O8 0xc0 #define DSP_CMD_I8 0xc8 #define DSP_MODE_U8MONO 0x00 #define DSP_MODE_U8STEREO 0x20 #define DSP_MODE_S16MONO 0x10 #define DSP_MODE_S16STEREO 0x30 #define DSP_CMD_SPKON 0xD1 #define DSP_CMD_SPKOFF 0xD3 #define DSP_CMD_SPKR(on) (0xD1 | (on ? 0:2)) #define DSP_CMD_DMAPAUSE_8 0xD0 #define DSP_CMD_DMAPAUSE_16 0xD5 #define DSP_CMD_DMAEXIT_8 0xDA #define DSP_CMD_DMAEXIT_16 0xD9 #define DSP_CMD_TCONST 0x40 /* set time constant */ #define DSP_CMD_HSDAC 0x91 /* high speed dac */ #define DSP_CMD_HSADC 0x99 /* high speed adc */ #define DSP_CMD_GETVER 0xE1 #define DSP_CMD_GETID 0xE7 /* return id bytes */ #define DSP_CMD_OUT16 0x41 /* send parms for dma out on sb16 */ #define DSP_CMD_IN16 0x42 /* send parms for dma in on sb16 */ #if 0 /*** unknown ***/ #define DSP_CMD_FA 0xFA /* get version from prosonic*/ #define DSP_CMD_FB 0xFB /* set irq/dma for prosonic*/ #endif /* * in fact, for the SB16, dma commands are as follows: * * cmd, mode, len_low, len_high. * * cmd is a combination of DSP_DMA16 or DSP_DMA8 and */ #define DSP_DMA16 0xb0 #define DSP_DMA8 0xc0 # define DSP_F16_DAC 0x00 # define DSP_F16_ADC 0x08 # define DSP_F16_AUTO 0x04 # define DSP_F16_FIFO_ON 0x02 /* * mode is a combination of the following: */ #define DSP_F16_STEREO 0x20 #define DSP_F16_SIGNED 0x10 #define IMODE_NONE 0 #define IMODE_OUTPUT PCM_ENABLE_OUTPUT #define IMODE_INPUT PCM_ENABLE_INPUT #define IMODE_INIT 3 #define IMODE_MIDI 4 #define NORMAL_MIDI 0 #define UART_MIDI 1 /* * values used for bd_flags in SoundBlaster driver */ #define BD_F_HISPEED 0x0001 /* doing high speed ... */ #if 0 #define BD_F_JAZZ16 0x0002 /* jazz16 detected */ #define BD_F_JAZZ16_2 0x0004 /* jazz16 type 2 */ #endif #define BD_F_DUP_MIDI 0x0008 /* duplex midi */ #define BD_F_MIX_MASK 0x0070 /* up to 8 mixers (I know of 3) */ #define BD_F_MIX_CT1335 0x0010 /* CT1335 */ #define BD_F_MIX_CT1345 0x0020 /* CT1345 */ #define BD_F_MIX_CT1745 0x0030 /* CT1745 */ #define BD_F_SB16 0x0100 /* this is a SB16 */ #define BD_F_SB16X 0x0200 /* this is a vibra16X or clone */ #if 0 #define BD_F_MIDIBUSY 0x0400 /* midi busy */ #endif #define BD_F_ESS 0x0800 /* this is an ESS chip */ -/* - * on some SB16 cards, at times I swap DMA channels. Remember this - * so that they can be restored later. - */ -#if 0 -#define BD_F_SWAPPED 0x1000 /* have swapped DMA channels */ -#endif #define BD_F_DMARUN 0x2000 #define BD_F_DMARUN2 0x4000 /* * Mixer registers of SB Pro */ #define VOC_VOL 0x04 #define MIC_VOL 0x0A #define MIC_MIX 0x0A #define RECORD_SRC 0x0C #define IN_FILTER 0x0C #define OUT_FILTER 0x0E #define MASTER_VOL 0x22 #define FM_VOL 0x26 #define CD_VOL 0x28 #define LINE_VOL 0x2E #define IRQ_NR 0x80 #define DMA_NR 0x81 #define IRQ_STAT 0x82 /* * Additional registers on the SG NX Pro */ #define COVOX_VOL 0x42 #define TREBLE_LVL 0x44 #define BASS_LVL 0x46 #define FREQ_HI (1 << 3)/* Use High-frequency ANFI filters */ #define FREQ_LOW 0 /* Use Low-frequency ANFI filters */ #define FILT_ON 0 /* Yes, 0 to turn it on, 1 for off */ #define FILT_OFF (1 << 5) #define MONO_DAC 0x00 #define STEREO_DAC 0x02 /* * Mixer registers of SB16 */ #define SB16_IMASK_L 0x3d #define SB16_IMASK_R 0x3e #define SB16_OMASK 0x3c -/* - * sound/sb_mixer.h - * - * Definitions for the SB Pro and SB16 mixers - * - * Copyright by Hannu Savolainen 1993 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. 2. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR - * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * Modified: Hunyue Yau Jan 6 1994 Added defines for the Sound Galaxy NX Pro - * mixer. - * - */ -#define SBPRO_RECORDING_DEVICES \ - (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) - -#define SBPRO_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \ - SOUND_MASK_CD | SOUND_MASK_VOLUME) - -#define SB16_RECORDING_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD) - -#define SB16_MIXER_DEVICES \ - (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | \ - SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | \ - SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \ - SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE) - -#ifdef __SB_MIXER_C__ -mixer_tab sbpro_mix = { - PMIX_ENT(SOUND_MIXER_VOLUME, 0x22, 4, 4, 0x22, 0, 4), - PMIX_ENT(SOUND_MIXER_SYNTH, 0x26, 4, 4, 0x26, 0, 4), - PMIX_ENT(SOUND_MIXER_PCM, 0x04, 4, 4, 0x04, 0, 4), - PMIX_ENT(SOUND_MIXER_LINE, 0x2e, 4, 4, 0x2e, 0, 4), - PMIX_ENT(SOUND_MIXER_MIC, 0x0a, 0, 3, 0x00, 0, 0), - PMIX_ENT(SOUND_MIXER_CD, 0x28, 4, 4, 0x28, 0, 4), -}; - -mixer_tab ess_mix = { - PMIX_ENT(SOUND_MIXER_VOLUME, 0x32, 4, 4, 0x32, 0, 4), - PMIX_ENT(SOUND_MIXER_SYNTH, 0x36, 4, 4, 0x26, 0, 4), - PMIX_ENT(SOUND_MIXER_PCM, 0x14, 4, 4, 0x04, 0, 4), - PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3c, 0, 3, 0x00, 0, 0), - PMIX_ENT(SOUND_MIXER_LINE, 0x3e, 4, 4, 0x2e, 0, 4), - PMIX_ENT(SOUND_MIXER_MIC, 0x1a, 4, 4, 0x1a, 0, 4), - PMIX_ENT(SOUND_MIXER_CD, 0x38, 4, 4, 0x28, 0, 4), -}; - -mixer_tab sb16_mix = { - PMIX_ENT(SOUND_MIXER_VOLUME, 0x30, 3, 5, 0x31, 3, 5), - PMIX_ENT(SOUND_MIXER_BASS, 0x46, 4, 4, 0x47, 4, 4), - PMIX_ENT(SOUND_MIXER_TREBLE, 0x44, 4, 4, 0x45, 4, 4), - PMIX_ENT(SOUND_MIXER_SYNTH, 0x34, 3, 5, 0x35, 3, 5), - PMIX_ENT(SOUND_MIXER_PCM, 0x32, 3, 5, 0x33, 3, 5), - PMIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 6, 2, 0x00, 0, 0), - PMIX_ENT(SOUND_MIXER_LINE, 0x38, 3, 5, 0x39, 3, 5), - PMIX_ENT(SOUND_MIXER_MIC, 0x3a, 3, 5, 0x00, 0, 0), - PMIX_ENT(SOUND_MIXER_CD, 0x36, 3, 5, 0x37, 3, 5), - PMIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 6, 2, 0x40, 6, 2), - PMIX_ENT(SOUND_MIXER_OGAIN, 0x41, 6, 2, 0x42, 6, 2) -}; - -#endif /* __SB_MIXER_C__ */ Index: stable/4/sys/dev/sound/isa/sbc.c =================================================================== --- stable/4/sys/dev/sound/isa/sbc.c (revision 71948) +++ stable/4/sys/dev/sound/isa/sbc.c (revision 71949) @@ -1,737 +1,740 @@ /*- * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #define IO_MAX 3 #define IRQ_MAX 1 #define DRQ_MAX 2 #define INTR_MAX 2 struct sbc_ihl { driver_intr_t *intr[INTR_MAX]; void *intr_arg[INTR_MAX]; }; /* Here is the parameter structure per a device. */ struct sbc_softc { device_t dev; /* device */ int io_rid[IO_MAX]; /* io port rids */ struct resource *io[IO_MAX]; /* io port resources */ int io_alloced[IO_MAX]; /* io port alloc flag */ int irq_rid[IRQ_MAX]; /* irq rids */ struct resource *irq[IRQ_MAX]; /* irq resources */ int irq_alloced[IRQ_MAX]; /* irq alloc flag */ int drq_rid[DRQ_MAX]; /* drq rids */ struct resource *drq[DRQ_MAX]; /* drq resources */ int drq_alloced[DRQ_MAX]; /* drq alloc flag */ struct sbc_ihl ihl[IRQ_MAX]; void *ih[IRQ_MAX]; u_int32_t bd_ver; }; static int sbc_probe(device_t dev); static int sbc_attach(device_t dev); static void sbc_intr(void *p); static struct resource *sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int sbc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); static int alloc_resource(struct sbc_softc *scp); static int release_resource(struct sbc_softc *scp); static devclass_t sbc_devclass; static int io_range[3] = {0x10, 0x2, 0x4}; #ifdef PC98 /* I/O address table for PC98 */ static bus_addr_t pcm_iat[] = { 0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900, 0xa00, 0xb00, 0xc00, 0xd00, 0xe00, 0xf00 }; static bus_addr_t midi_iat[] = { 0x000, 0x100 }; static bus_addr_t opl_iat[] = { 0x000, 0x100, 0x200, 0x300 }; static bus_addr_t *sb_iat[] = { pcm_iat, midi_iat, opl_iat }; #endif static int sb_rd(struct resource *io, int reg); static void sb_wr(struct resource *io, int reg, u_int8_t val); static int sb_dspready(struct resource *io); static int sb_cmd(struct resource *io, u_char val); static u_int sb_get_byte(struct resource *io); static void sb_setmixer(struct resource *io, u_int port, u_int value); static int sb_rd(struct resource *io, int reg) { return bus_space_read_1(rman_get_bustag(io), rman_get_bushandle(io), reg); } static void sb_wr(struct resource *io, int reg, u_int8_t val) { return bus_space_write_1(rman_get_bustag(io), rman_get_bushandle(io), reg, val); } static int sb_dspready(struct resource *io) { return ((sb_rd(io, SBDSP_STATUS) & 0x80) == 0); } static int sb_dspwr(struct resource *io, u_char val) { int i; for (i = 0; i < 1000; i++) { if (sb_dspready(io)) { sb_wr(io, SBDSP_CMD, val); return 1; } if (i > 10) DELAY((i > 100)? 1000 : 10); } printf("sb_dspwr(0x%02x) timed out.\n", val); return 0; } static int sb_cmd(struct resource *io, u_char val) { return sb_dspwr(io, val); } static void sb_setmixer(struct resource *io, u_int port, u_int value) { u_long flags; flags = spltty(); sb_wr(io, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); sb_wr(io, SB_MIX_DATA, (u_char) (value & 0xff)); DELAY(10); splx(flags); } static u_int sb_get_byte(struct resource *io) { int i; for (i = 1000; i > 0; i--) { if (sb_rd(io, DSP_DATA_AVAIL) & 0x80) return sb_rd(io, DSP_READ); else DELAY(20); } return 0xffff; } static int sb_reset_dsp(struct resource *io) { sb_wr(io, SBDSP_RST, 3); DELAY(100); sb_wr(io, SBDSP_RST, 0); return (sb_get_byte(io) == 0xAA)? 0 : ENXIO; } static int sb_identify_board(struct resource *io) { int ver, essver, rev; sb_cmd(io, DSP_CMD_GETVER); /* Get version */ ver = (sb_get_byte(io) << 8) | sb_get_byte(io); if (ver < 0x100 || ver > 0x4ff) return 0; if (ver == 0x0301) { /* Try to detect ESS chips. */ sb_cmd(io, DSP_CMD_GETID); /* Return ident. bytes. */ essver = (sb_get_byte(io) << 8) | sb_get_byte(io); rev = essver & 0x000f; essver &= 0xfff0; if (essver == 0x4880) ver |= 0x1000; else if (essver == 0x6880) ver = 0x0500 | rev; } return ver; } static struct isa_pnp_id sbc_ids[] = { {0x01008c0e, "Creative ViBRA16C"}, /* CTL0001 */ {0x31008c0e, "Creative SB16/SB32"}, /* CTL0031 */ {0x41008c0e, "Creative SB16/SB32"}, /* CTL0041 */ {0x42008c0e, "Creative SB AWE64"}, /* CTL0042 */ {0x43008c0e, "Creative ViBRA16X"}, /* CTL0043 */ {0x44008c0e, "Creative SB AWE64 Gold"}, /* CTL0044 */ {0x45008c0e, "Creative SB AWE64"}, /* CTL0045 */ {0x01000000, "Avance Logic ALS100+"}, /* @@@0001 - ViBRA16X clone */ {0x01100000, "Avance Asound 110"}, /* @@@1001 */ {0x01200000, "Avance Logic ALS120"}, /* @@@2001 - ViBRA16X clone */ + {0x81167316, "ESS ES1681"}, /* ESS1681 */ {0x02017316, "ESS ES1688"}, /* ESS1688 */ {0x68187316, "ESS ES1868"}, /* ESS1868 */ {0x03007316, "ESS ES1869"}, /* ESS1869 */ {0x69187316, "ESS ES1869"}, /* ESS1869 */ {0xabb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ab */ {0xacb0110e, "ESS ES1869 (Compaq OEM)"}, /* CPQb0ac */ {0x78187316, "ESS ES1878"}, /* ESS1878 */ {0x79187316, "ESS ES1879"}, /* ESS1879 */ {0x88187316, "ESS ES1888"}, /* ESS1888 */ {0x07017316, "ESS ES1888 (DEC OEM)"}, /* ESS0107 */ {0x06017316, "ESS ES1888 (Dell OEM)"}, /* ESS0106 */ {0} }; static int sbc_probe(device_t dev) { char *s = NULL; u_int32_t lid, vid; lid = isa_get_logicalid(dev); vid = isa_get_vendorid(dev); if (lid) { if (lid == 0x01000000 && vid != 0x01009305) /* ALS0001 */ return ENXIO; /* Check pnp ids */ return ISA_PNP_PROBE(device_get_parent(dev), dev, sbc_ids); } else { int rid = 0, ver; struct resource *io; #ifdef PC98 io = isa_alloc_resourcev(dev, SYS_RES_IOPORT, &rid, pcm_iat, 16, RF_ACTIVE); #else io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 16, RF_ACTIVE); #endif if (!io) goto bad; #ifdef PC98 isa_load_resourcev(io, pcm_iat, 16); #endif if (sb_reset_dsp(io)) goto bad2; ver = sb_identify_board(io); if (ver == 0) goto bad2; switch ((ver & 0x00000f00) >> 8) { case 1: + device_set_desc(dev, "SoundBlaster 1.0 (not supported)"); + s = NULL; + break; + case 2: - s = "Soundblaster"; + s = "SoundBlaster 2.0"; break; case 3: - s = (ver & 0x0000f000)? "ESS 488" : "Soundblaster Pro"; + s = (ver & 0x0000f000)? "ESS 488" : "SoundBlaster Pro"; break; case 4: - s = "Soundblaster 16"; + s = "SoundBlaster 16"; break; case 5: s = (ver & 0x00000008)? "ESS 688" : "ESS 1688"; break; } if (s) device_set_desc(dev, s); bad2: bus_release_resource(dev, SYS_RES_IOPORT, rid, io); bad: return s? 0 : ENXIO; } } static int sbc_attach(device_t dev) { char *err = NULL; struct sbc_softc *scp; struct sndcard_func *func; device_t child; u_int32_t logical_id = isa_get_logicalid(dev); int flags = device_get_flags(dev); int f, dh, dl, x, irq, i; if (!logical_id && (flags & DV_F_DUAL_DMA)) { bus_set_resource(dev, SYS_RES_DRQ, 1, flags & DV_F_DRQ_MASK, 1); } scp = device_get_softc(dev); bzero(scp, sizeof(*scp)); scp->dev = dev; err = "alloc_resource"; if (alloc_resource(scp)) goto bad; err = "sb_reset_dsp"; if (sb_reset_dsp(scp->io[0])) goto bad; err = "sb_identify_board"; scp->bd_ver = sb_identify_board(scp->io[0]) & 0x00000fff; if (scp->bd_ver == 0) goto bad; f = 0; if (logical_id == 0x01200000 && scp->bd_ver < 0x0400) scp->bd_ver = 0x0499; switch ((scp->bd_ver & 0x0f00) >> 8) { case 1: /* old sound blaster has nothing... */ break; case 2: f |= BD_F_DUP_MIDI; if (scp->bd_ver > 0x200) f |= BD_F_MIX_CT1335; break; case 5: f |= BD_F_ESS; scp->bd_ver = 0x0301; case 3: f |= BD_F_DUP_MIDI | BD_F_MIX_CT1345; break; case 4: f |= BD_F_SB16 | BD_F_MIX_CT1745; if (scp->drq[0]) dl = rman_get_start(scp->drq[0]); else dl = -1; if (scp->drq[1]) dh = rman_get_start(scp->drq[1]); else dh = dl; if (!logical_id && (dh < dl)) { struct resource *r; r = scp->drq[0]; scp->drq[0] = scp->drq[1]; scp->drq[1] = r; dl = rman_get_start(scp->drq[0]); dh = rman_get_start(scp->drq[1]); } /* soft irq/dma configuration */ x = -1; irq = rman_get_start(scp->irq[0]); #ifdef PC98 /* SB16 in PC98 use different IRQ table */ if (irq == 3) x = 1; else if (irq == 5) x = 8; else if (irq == 10) x = 2; else if (irq == 12) x = 4; if (x == -1) { err = "bad irq (3/5/10/12 valid)"; goto bad; } else sb_setmixer(scp->io[0], IRQ_NR, x); /* SB16 in PC98 use different dma setting */ sb_setmixer(scp->io[0], DMA_NR, dh == 0 ? 1 : 2); #else if (irq == 5) x = 2; else if (irq == 7) x = 4; else if (irq == 9) x = 1; else if (irq == 10) x = 8; if (x == -1) { err = "bad irq (5/7/9/10 valid)"; goto bad; } else sb_setmixer(scp->io[0], IRQ_NR, x); sb_setmixer(scp->io[0], DMA_NR, (1 << dh) | (1 << dl)); #endif if (bootverbose) { device_printf(dev, "setting card to irq %d, drq %d", irq, dl); if (dl != dh) printf(", %d", dh); printf("\n"); } break; } switch (logical_id) { case 0x43008c0e: /* CTL0043 */ case 0x01200000: case 0x01000000: f |= BD_F_SB16X; break; } scp->bd_ver |= f << 16; err = "setup_intr"; for (i = 0; i < IRQ_MAX; i++) { if (bus_setup_intr(dev, scp->irq[i], INTR_TYPE_TTY, sbc_intr, &scp->ihl[i], &scp->ih[i])) goto bad; } /* PCM Audio */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) goto bad; bzero(func, sizeof(*func)); func->func = SCF_PCM; child = device_add_child(dev, "pcm", -1); device_set_ivars(child, func); /* Midi Interface */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) goto bad; bzero(func, sizeof(*func)); func->func = SCF_MIDI; child = device_add_child(dev, "midi", -1); device_set_ivars(child, func); /* OPL FM Synthesizer */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) goto bad; bzero(func, sizeof(*func)); func->func = SCF_SYNTH; child = device_add_child(dev, "midi", -1); device_set_ivars(child, func); /* probe/attach kids */ bus_generic_attach(dev); return (0); bad: if (err) device_printf(dev, "%s\n", err); release_resource(scp); return (ENXIO); } static int sbc_detach(device_t dev) { struct sbc_softc *scp = device_get_softc(dev); release_resource(scp); return bus_generic_detach(dev); } static void sbc_intr(void *p) { struct sbc_ihl *ihl = p; int i; i = 0; while (i < INTR_MAX) { if (ihl->intr[i] != NULL) ihl->intr[i](ihl->intr_arg[i]); i++; } } static int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { struct sbc_softc *scp = device_get_softc(dev); struct sbc_ihl *ihl = NULL; int i; i = 0; while (i < IRQ_MAX) { if (irq == scp->irq[i]) ihl = &scp->ihl[i]; i++; } if (ihl == NULL) return (EINVAL); i = 0; while (i < INTR_MAX) { if (ihl->intr[i] == NULL) { ihl->intr[i] = intr; ihl->intr_arg[i] = arg; *cookiep = &ihl->intr[i]; return 0; } else i++; } return (EINVAL); } static int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie) { struct sbc_softc *scp = device_get_softc(dev); struct sbc_ihl *ihl = NULL; int i; i = 0; while (i < IRQ_MAX) { if (irq == scp->irq[i]) ihl = &scp->ihl[i]; i++; } if (ihl == NULL) return (EINVAL); i = 0; while (i < INTR_MAX) { if (cookie == &ihl->intr[i]) { ihl->intr[i] = NULL; ihl->intr_arg[i] = NULL; return 0; } else i++; } return (EINVAL); } static struct resource * sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct sbc_softc *scp; int *alloced, rid_max, alloced_max; struct resource **res; #ifdef PC98 int i; #endif scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; res = scp->io; #ifdef PC98 rid_max = 0; for (i = 0; i < IO_MAX; i++) rid_max += io_range[i]; #else rid_max = IO_MAX - 1; #endif alloced_max = 1; break; case SYS_RES_DRQ: alloced = scp->drq_alloced; res = scp->drq; rid_max = DRQ_MAX - 1; alloced_max = 1; break; case SYS_RES_IRQ: alloced = scp->irq_alloced; res = scp->irq; rid_max = IRQ_MAX - 1; alloced_max = INTR_MAX; /* pcm and mpu may share the irq. */ break; default: return (NULL); } if (*rid > rid_max || alloced[*rid] == alloced_max) return (NULL); alloced[*rid]++; return (res[*rid]); } static int sbc_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { struct sbc_softc *scp; int *alloced, rid_max; scp = device_get_softc(bus); switch (type) { case SYS_RES_IOPORT: alloced = scp->io_alloced; rid_max = IO_MAX - 1; break; case SYS_RES_DRQ: alloced = scp->drq_alloced; rid_max = DRQ_MAX - 1; break; case SYS_RES_IRQ: alloced = scp->irq_alloced; rid_max = IRQ_MAX - 1; break; default: return (1); } if (rid > rid_max || alloced[rid] == 0) return (1); alloced[rid]--; return (0); } static int sbc_read_ivar(device_t bus, device_t dev, int index, uintptr_t * result) { struct sbc_softc *scp = device_get_softc(bus); struct sndcard_func *func = device_get_ivars(dev); switch (index) { case 0: *result = func->func; break; case 1: *result = scp->bd_ver; break; default: return ENOENT; } return 0; } static int sbc_write_ivar(device_t bus, device_t dev, int index, uintptr_t value) { switch (index) { case 0: case 1: return EINVAL; default: return (ENOENT); } } static int alloc_resource(struct sbc_softc *scp) { int i; for (i = 0 ; i < IO_MAX ; i++) { if (scp->io[i] == NULL) { #ifdef PC98 scp->io_rid[i] = i > 0 ? scp->io_rid[i - 1] + io_range[i - 1] : 0; scp->io[i] = isa_alloc_resourcev(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], sb_iat[i], io_range[i], RF_ACTIVE); if (scp->io[i] != NULL) isa_load_resourcev(scp->io[i], sb_iat[i], io_range[i]); #else scp->io_rid[i] = i; scp->io[i] = bus_alloc_resource(scp->dev, SYS_RES_IOPORT, &scp->io_rid[i], 0, ~0, io_range[i], RF_ACTIVE); #endif if (i == 0 && scp->io[i] == NULL) return (1); scp->io_alloced[i] = 0; } } for (i = 0 ; i < DRQ_MAX ; i++) { if (scp->drq[i] == NULL) { scp->drq_rid[i] = i; scp->drq[i] = bus_alloc_resource(scp->dev, SYS_RES_DRQ, &scp->drq_rid[i], 0, ~0, 1, RF_ACTIVE); if (i == 0 && scp->drq[i] == NULL) return (1); scp->drq_alloced[i] = 0; } } for (i = 0 ; i < IRQ_MAX ; i++) { if (scp->irq[i] == NULL) { scp->irq_rid[i] = i; scp->irq[i] = bus_alloc_resource(scp->dev, SYS_RES_IRQ, &scp->irq_rid[i], 0, ~0, 1, RF_ACTIVE); if (i == 0 && scp->irq[i] == NULL) return (1); scp->irq_alloced[i] = 0; } } return (0); } static int release_resource(struct sbc_softc *scp) { int i; for (i = 0 ; i < IO_MAX ; i++) { if (scp->io[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_IOPORT, scp->io_rid[i], scp->io[i]); scp->io[i] = NULL; } } for (i = 0 ; i < DRQ_MAX ; i++) { if (scp->drq[i] != NULL) { bus_release_resource(scp->dev, SYS_RES_DRQ, scp->drq_rid[i], scp->drq[i]); scp->drq[i] = NULL; } } for (i = 0 ; i < IRQ_MAX ; i++) { if (scp->irq[i] != NULL) { if (scp->ih[i] != NULL) bus_teardown_intr(scp->dev, scp->irq[i], scp->ih[i]); scp->ih[i] = NULL; bus_release_resource(scp->dev, SYS_RES_IRQ, scp->irq_rid[i], scp->irq[i]); scp->irq[i] = NULL; } } return (0); } static device_method_t sbc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, sbc_probe), DEVMETHOD(device_attach, sbc_attach), DEVMETHOD(device_detach, sbc_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_read_ivar, sbc_read_ivar), DEVMETHOD(bus_write_ivar, sbc_write_ivar), DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, sbc_alloc_resource), DEVMETHOD(bus_release_resource, sbc_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, sbc_setup_intr), DEVMETHOD(bus_teardown_intr, sbc_teardown_intr), { 0, 0 } }; static driver_t sbc_driver = { "sbc", sbc_methods, sizeof(struct sbc_softc), }; /* sbc can be attached to an isa bus. */ DRIVER_MODULE(snd_sbc, isa, sbc_driver, sbc_devclass, 0, 0); MODULE_DEPEND(snd_sbc, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_sbc, 1); - - Index: stable/4/sys/dev/sound/pci/aureal.c =================================================================== --- stable/4/sys/dev/sound/pci/aureal.c (revision 71948) +++ stable/4/sys/dev/sound/pci/aureal.c (revision 71949) @@ -1,704 +1,688 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include /* PCI IDs of supported chips */ #define AU8820_PCI_ID 0x000112eb /* channel interface */ -static void *auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int auchan_setdir(void *data, int dir); -static int auchan_setformat(void *data, u_int32_t format); -static int auchan_setspeed(void *data, u_int32_t speed); -static int auchan_setblocksize(void *data, u_int32_t blocksize); -static int auchan_trigger(void *data, int go); -static int auchan_getptr(void *data); -static pcmchan_caps *auchan_getcaps(void *data); - static u_int32_t au_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps au_playcaps = {4000, 48000, au_playfmt, 0}; static u_int32_t au_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps au_reccaps = {4000, 48000, au_recfmt, 0}; -static pcm_channel au_chantemplate = { - auchan_init, - auchan_setdir, - auchan_setformat, - auchan_setspeed, - auchan_setblocksize, - auchan_trigger, - auchan_getptr, - auchan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - /* -------------------------------------------------------------------- */ -static u_int32_t au_rdcd(void *arg, int regno); -static void au_wrcd(void *arg, int regno, u_int32_t data); - struct au_info; struct au_chinfo { struct au_info *parent; pcm_channel *channel; snd_dbuf *buffer; int dir; }; struct au_info { int unit; bus_space_tag_t st[3]; bus_space_handle_t sh[3]; bus_dma_tag_t parent_dmat; u_int32_t x[32], y[128]; char z[128]; u_int32_t routes[4], interrupts; struct au_chinfo pch; }; static int au_init(device_t dev, struct au_info *au); static void au_intr(void *); /* -------------------------------------------------------------------- */ static u_int32_t au_rd(struct au_info *au, int mapno, int regno, int size) { switch(size) { case 1: return bus_space_read_1(au->st[mapno], au->sh[mapno], regno); case 2: return bus_space_read_2(au->st[mapno], au->sh[mapno], regno); case 4: return bus_space_read_4(au->st[mapno], au->sh[mapno], regno); default: return 0xffffffff; } } static void au_wr(struct au_info *au, int mapno, int regno, u_int32_t data, int size) { switch(size) { case 1: bus_space_write_1(au->st[mapno], au->sh[mapno], regno, data); break; case 2: bus_space_write_2(au->st[mapno], au->sh[mapno], regno, data); break; case 4: bus_space_write_4(au->st[mapno], au->sh[mapno], regno, data); break; } } -static u_int32_t -au_rdcd(void *arg, int regno) +/* -------------------------------------------------------------------- */ + +static int +au_rdcd(kobj_t obj, void *arg, int regno) { struct au_info *au = (struct au_info *)arg; int i=0, j=0; regno<<=16; au_wr(au, 0, AU_REG_CODECIO, regno, 4); while (j<50) { i=au_rd(au, 0, AU_REG_CODECIO, 4); if ((i & 0x00ff0000) == (regno | 0x00800000)) break; DELAY(j * 200 + 2000); j++; } if (j==50) printf("pcm%d: codec timeout reading register %x (%x)\n", au->unit, (regno & AU_CDC_REGMASK)>>16, i); return i & AU_CDC_DATAMASK; } -static void -au_wrcd(void *arg, int regno, u_int32_t data) +static int +au_wrcd(kobj_t obj, void *arg, int regno, u_int32_t data) { struct au_info *au = (struct au_info *)arg; int i, j, tries; i=j=tries=0; do { while (j<50 && (i & AU_CDC_WROK) == 0) { i=au_rd(au, 0, AU_REG_CODECST, 4); DELAY(2000); j++; } if (j==50) printf("codec timeout during write of register %x, data %x\n", regno, data); au_wr(au, 0, AU_REG_CODECIO, (regno<<16) | AU_CDC_REGSET | data, 4); /* DELAY(20000); i=au_rdcd(au, regno); */ tries++; } while (0); /* (i != data && tries < 3); */ /* if (tries == 3) printf("giving up writing 0x%4x to codec reg %2x\n", data, regno); */ + + return 0; } +static kobj_method_t au_ac97_methods[] = { + KOBJMETHOD(ac97_read, au_rdcd), + KOBJMETHOD(ac97_write, au_wrcd), + { 0, 0 } +}; +AC97_DECLARE(au_ac97); + +/* -------------------------------------------------------------------- */ + static void au_setbit(u_int32_t *p, char bit, u_int32_t value) { p += bit >> 5; bit &= 0x1f; *p &= ~ (1 << bit); *p |= (value << bit); } static void au_addroute(struct au_info *au, int a, int b, int route) { int j = 0x1099c+(a<<2); if (au->x[a] != a+0x67) j = AU_REG_RTBASE+(au->x[a]<<2); au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xffffffff, 4); au_wr(au, 0, j, route | (b<<7), 4); au->y[route]=au->x[a]; au->x[a]=route; au->z[route]=a & 0x000000ff; au_setbit(au->routes, route, 1); } static void au_delroute(struct au_info *au, int route) { int i; int j=au->z[route]; au_setbit(au->routes, route, 0); au->z[route]=0x1f; i=au_rd(au, 0, AU_REG_RTBASE+(route<<2), 4); au_wr(au, 0, AU_REG_RTBASE+(au->y[route]<<2), i, 4); au->y[i & 0x7f]=au->y[route]; au_wr(au, 0, AU_REG_RTBASE+(route<<2), 0xfffffffe, 4); if (au->x[j] == route) au->x[j]=au->y[route]; au->y[route]=0x7f; } static void au_encodec(struct au_info *au, char channel) { au_wr(au, 0, AU_REG_CODECEN, au_rd(au, 0, AU_REG_CODECEN, 4) | (1 << (channel + 8)), 4); } static void au_clrfifo(struct au_info *au, u_int32_t c) { u_int32_t i; for (i=0; i<32; i++) au_wr(au, 0, AU_REG_FIFOBASE+(c<<7)+(i<<2), 0, 4); } static void au_setadb(struct au_info *au, u_int32_t c, u_int32_t enable) { int x; x = au_rd(au, 0, AU_REG_ADB, 4); x &= ~(1 << c); x |= (enable << c); au_wr(au, 0, AU_REG_ADB, x, 4); } static void au_prepareoutput(struct au_chinfo *ch, u_int32_t format) { struct au_info *au = ch->parent; int i, stereo = (format & AFMT_STEREO)? 1 : 0; - u_int32_t baseaddr = vtophys(ch->buffer->buf); + u_int32_t baseaddr = vtophys(sndbuf_getbuf(ch->buffer)); au_wr(au, 0, 0x1061c, 0, 4); au_wr(au, 0, 0x10620, 0, 4); au_wr(au, 0, 0x10624, 0, 4); switch(format & ~AFMT_STEREO) { case 1: i=0xb000; break; case 2: i=0xf000; break; case 8: i=0x7000; break; case 16: i=0x23000; break; default: i=0x3000; } au_wr(au, 0, 0x10200, baseaddr, 4); au_wr(au, 0, 0x10204, baseaddr+0x1000, 4); au_wr(au, 0, 0x10208, baseaddr+0x2000, 4); au_wr(au, 0, 0x1020c, baseaddr+0x3000, 4); au_wr(au, 0, 0x10400, 0xdeffffff, 4); au_wr(au, 0, 0x10404, 0xfcffffff, 4); au_wr(au, 0, 0x10580, i, 4); au_wr(au, 0, 0x10210, baseaddr, 4); au_wr(au, 0, 0x10214, baseaddr+0x1000, 4); au_wr(au, 0, 0x10218, baseaddr+0x2000, 4); au_wr(au, 0, 0x1021c, baseaddr+0x3000, 4); au_wr(au, 0, 0x10408, 0x00fff000 | 0x56000000 | 0x00000fff, 4); au_wr(au, 0, 0x1040c, 0x00fff000 | 0x74000000 | 0x00000fff, 4); au_wr(au, 0, 0x10584, i, 4); au_wr(au, 0, 0x0f800, stereo? 0x00030032 : 0x00030030, 4); au_wr(au, 0, 0x0f804, stereo? 0x00030032 : 0x00030030, 4); au_addroute(au, 0x11, 0, 0x58); au_addroute(au, 0x11, stereo? 0 : 1, 0x59); } +/* -------------------------------------------------------------------- */ /* channel interface */ static void * -auchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +auchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct au_info *au = devinfo; struct au_chinfo *ch = (dir == PCMDIR_PLAY)? &au->pch : NULL; ch->parent = au; ch->channel = c; ch->buffer = b; - ch->buffer->bufsize = AU_BUFFSIZE; - if (chn_allocbuf(ch->buffer, au->parent_dmat) == -1) return NULL; + ch->dir = dir; + if (sndbuf_alloc(ch->buffer, au->parent_dmat, AU_BUFFSIZE) == -1) return NULL; return ch; } static int -auchan_setdir(void *data, int dir) +auchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct au_chinfo *ch = data; - if (dir == PCMDIR_PLAY) { - } else { - } - ch->dir = dir; - return 0; -} -static int -auchan_setformat(void *data, u_int32_t format) -{ - struct au_chinfo *ch = data; - if (ch->dir == PCMDIR_PLAY) au_prepareoutput(ch, format); return 0; } static int -auchan_setspeed(void *data, u_int32_t speed) +auchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct au_chinfo *ch = data; if (ch->dir == PCMDIR_PLAY) { } else { } return speed; } static int -auchan_setblocksize(void *data, u_int32_t blocksize) +auchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { return blocksize; } static int -auchan_trigger(void *data, int go) +auchan_trigger(kobj_t obj, void *data, int go) { struct au_chinfo *ch = data; struct au_info *au = ch->parent; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; if (ch->dir == PCMDIR_PLAY) { au_setadb(au, 0x11, (go)? 1 : 0); if (!go) { au_wr(au, 0, 0xf800, 0, 4); au_wr(au, 0, 0xf804, 0, 4); au_delroute(au, 0x58); au_delroute(au, 0x59); } } else { } return 0; } static int -auchan_getptr(void *data) +auchan_getptr(kobj_t obj, void *data) { struct au_chinfo *ch = data; struct au_info *au = ch->parent; if (ch->dir == PCMDIR_PLAY) { return au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1); } else { return 0; } } static pcmchan_caps * -auchan_getcaps(void *data) +auchan_getcaps(kobj_t obj, void *data) { struct au_chinfo *ch = data; return (ch->dir == PCMDIR_PLAY)? &au_playcaps : &au_reccaps; } +static kobj_method_t auchan_methods[] = { + KOBJMETHOD(channel_init, auchan_init), + KOBJMETHOD(channel_setformat, auchan_setformat), + KOBJMETHOD(channel_setspeed, auchan_setspeed), + KOBJMETHOD(channel_setblocksize, auchan_setblocksize), + KOBJMETHOD(channel_trigger, auchan_trigger), + KOBJMETHOD(channel_getptr, auchan_getptr), + KOBJMETHOD(channel_getcaps, auchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(auchan); + +/* -------------------------------------------------------------------- */ /* The interrupt handler */ static void au_intr (void *p) { struct au_info *au = p; u_int32_t intsrc, i; au->interrupts++; intsrc=au_rd(au, 0, AU_REG_IRQSRC, 4); printf("pcm%d: interrupt with src %x\n", au->unit, intsrc); if (intsrc & AU_IRQ_FATAL) printf("pcm%d: fatal error irq\n", au->unit); if (intsrc & AU_IRQ_PARITY) printf("pcm%d: parity error irq\n", au->unit); if (intsrc & AU_IRQ_UNKNOWN) { (void)au_rd(au, 0, AU_REG_UNK1, 4); au_wr(au, 0, AU_REG_UNK1, 0, 4); au_wr(au, 0, AU_REG_UNK1, 0x10000, 4); } if (intsrc & AU_IRQ_PCMOUT) { i=au_rd(au, 0, AU_REG_UNK2, 4) & (AU_BUFFSIZE-1); chn_intr(au->pch.channel); (void)au_rd(au, 0, AU_REG_UNK3, 4); (void)au_rd(au, 0, AU_REG_UNK4, 4); (void)au_rd(au, 0, AU_REG_UNK5, 4); } /* don't support midi if (intsrc & AU_IRQ_MIDI) { i=au_rd(au, 0, 0x11004, 4); j=10; while (i & 0xff) { if (j-- <= 0) break; i=au_rd(au, 0, 0x11000, 4); if ((au->midi_stat & 1) && (au->midi_out)) au->midi_out(au->midi_devno, i); i=au_rd(au, 0, 0x11004); } } */ au_wr(au, 0, AU_REG_IRQSRC, intsrc & 0x7ff, 4); au_rd(au, 0, AU_REG_IRQSRC, 4); } /* -------------------------------------------------------------------- */ /* Probe and attach the card */ static int au_init(device_t dev, struct au_info *au) { u_int32_t i, j; au_wr(au, 0, AU_REG_IRQGLOB, 0xffffffff, 4); DELAY(100000); /* init codec */ /* cold reset */ for (i=0; i<32; i++) { au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4); DELAY(10000); } if (1) { au_wr(au, 0, AU_REG_CODECST, 0x8068, 4); DELAY(10000); au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4); DELAY(10000); } else { au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4); DELAY(100000); au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4); DELAY(100000); au_wr(au, 0, AU_REG_CODECST, 0x80e8, 4); DELAY(100000); au_wr(au, 0, AU_REG_CODECST, 0x80a8, 4); DELAY(100000); au_wr(au, 0, AU_REG_CODECST, 0x00a8, 4); DELAY(100000); au_wr(au, 0, AU_REG_CODECST, 0x00e8, 4); DELAY(100000); } /* init */ for (i=0; i<32; i++) { au_wr(au, 0, AU_REG_CODECCHN+(i<<2), 0, 4); DELAY(10000); } au_wr(au, 0, AU_REG_CODECST, 0xe8, 4); DELAY(10000); au_wr(au, 0, AU_REG_CODECEN, 0, 4); /* setup codec */ i=j=0; while (j<100 && (i & AU_CDC_READY)==0) { i=au_rd(au, 0, AU_REG_CODECST, 4); DELAY(1000); j++; } if (j==100) device_printf(dev, "codec not ready, status 0x%x\n", i); /* init adb */ /*au->x5c=0;*/ for (i=0; i<32; i++) au->x[i]=i+0x67; for (i=0; i<128; i++) au->y[i]=0x7f; for (i=0; i<128; i++) au->z[i]=0x1f; au_wr(au, 0, AU_REG_ADB, 0, 4); for (i=0; i<124; i++) au_wr(au, 0, AU_REG_RTBASE+(i<<2), 0xffffffff, 4); /* test */ i=au_rd(au, 0, 0x107c0, 4); if (i!=0xdeadbeef) device_printf(dev, "dma check failed: 0x%x\n", i); /* install mixer */ au_wr(au, 0, AU_REG_IRQGLOB, au_rd(au, 0, AU_REG_IRQGLOB, 4) | AU_IRQ_ENABLE, 4); /* braindead but it's what the oss/linux driver does * for (i=0; i<0x80000000; i++) au_wr(au, 0, i<<2, 0, 4); */ au->routes[0]=au->routes[1]=au->routes[2]=au->routes[3]=0; /*au->x1e4=0;*/ /* attach channel */ au_addroute(au, 0x11, 0x48, 0x02); au_addroute(au, 0x11, 0x49, 0x03); au_encodec(au, 0); au_encodec(au, 1); for (i=0; i<48; i++) au_wr(au, 0, 0xf800+(i<<2), 0x20, 4); for (i=2; i<6; i++) au_wr(au, 0, 0xf800+(i<<2), 0, 4); au_wr(au, 0, 0xf8c0, 0x0843, 4); for (i=0; i<4; i++) au_clrfifo(au, i); return (0); } static int au_testirq(struct au_info *au) { au_wr(au, 0, AU_REG_UNK1, 0x80001000, 4); au_wr(au, 0, AU_REG_IRQEN, 0x00001030, 4); au_wr(au, 0, AU_REG_IRQSRC, 0x000007ff, 4); DELAY(1000000); if (au->interrupts==0) printf("pcm%d: irq test failed\n", au->unit); /* this apparently generates an irq */ return 0; } static int au_pci_probe(device_t dev) { if (pci_get_devid(dev) == AU8820_PCI_ID) { device_set_desc(dev, "Aureal Vortex 8820"); return 0; } return ENXIO; } static int au_pci_attach(device_t dev) { u_int32_t data; struct au_info *au; int type[10]; int regid[10]; struct resource *reg[10]; int i, j, mapped = 0; int irqid; struct resource *irq = 0; void *ih = 0; struct ac97_info *codec; char status[SND_STATUSLEN]; - d = device_get_softc(dev); if ((au = malloc(sizeof(*au), M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(au, sizeof(*au)); au->unit = device_get_unit(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); j=0; /* XXX dfr: is this strictly necessary? */ for (i=0; imap[i].ln2size); printf("%s space ", (config_id->map[i].type & PCI_MAPPORT)? "io" : "memory"); printf("at 0x%x...", config_id->map[i].base); } #endif regid[j] = PCIR_MAPS + i*4; type[j] = SYS_RES_MEMORY; reg[j] = bus_alloc_resource(dev, type[j], ®id[j], 0, ~0, 1, RF_ACTIVE); if (!reg[j]) { type[j] = SYS_RES_IOPORT; reg[j] = bus_alloc_resource(dev, type[j], ®id[j], 0, ~0, 1, RF_ACTIVE); } if (reg[j]) { au->st[i] = rman_get_bustag(reg[j]); au->sh[i] = rman_get_bushandle(reg[j]); mapped++; } #if 0 if (bootverbose) printf("%s\n", mapped? "ok" : "failed"); #endif if (mapped) j++; if (j == 10) { /* XXX */ device_printf(dev, "too many resources"); goto bad; } } #if 0 if (j < config_id->nummaps) { printf("pcm%d: unable to map a required resource\n", unit); free(au, M_DEVBUF); return; } #endif au_wr(au, 0, AU_REG_IRQEN, 0, 4); irqid = 0; irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!irq || bus_setup_intr(dev, irq, INTR_TYPE_TTY, au_intr, au, &ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } if (au_testirq(au)) device_printf(dev, "irq test failed\n"); if (au_init(dev, au) == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - codec = ac97_create(dev, au, NULL, au_rdcd, au_wrcd); + codec = AC97_CREATE(dev, au, au_ac97); if (codec == NULL) goto bad; - if (mixer_init(dev, &ac97_mixer, codec) == -1) goto bad; + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/AU_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &au->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", (type[0] == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(reg[0]), rman_get_start(irq)); if (pcm_register(dev, au, 1, 1)) goto bad; /* pcm_addchan(dev, PCMDIR_REC, &au_chantemplate, au); */ - pcm_addchan(dev, PCMDIR_PLAY, &au_chantemplate, au); + pcm_addchan(dev, PCMDIR_PLAY, &auchan_class, au); pcm_setstatus(dev, status); return 0; bad: if (au) free(au, M_DEVBUF); for (i = 0; i < j; i++) bus_release_resource(dev, type[i], regid[i], reg[i]); if (ih) bus_teardown_intr(dev, irq, ih); if (irq) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); return ENXIO; } static device_method_t au_methods[] = { /* Device interface */ DEVMETHOD(device_probe, au_pci_probe), DEVMETHOD(device_attach, au_pci_attach), { 0, 0 } }; static driver_t au_driver = { "pcm", au_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_aureal, pci, au_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_aureal, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_aureal, 1); Index: stable/4/sys/dev/sound/pci/csa.c =================================================================== --- stable/4/sys/dev/sound/pci/csa.c (revision 71948) +++ stable/4/sys/dev/sound/pci/csa.c (revision 71949) @@ -1,917 +1,916 @@ /* * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * * Portions of this source are based on cwcealdr.cpp and dhwiface.cpp in * cwcealdr1.zip, the sample sources by Crystal Semiconductor. * Copyright (c) 1996-1998 Crystal Semiconductor Corp. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include -#include #include #include #include #include #include #include #include #include #include /* Here is the parameter structure per a device. */ struct csa_softc { device_t dev; /* device */ csa_res res; /* resources */ device_t pcm; /* pcm device */ driver_intr_t* pcmintr; /* pcm intr */ void *pcmintr_arg; /* pcm intr arg */ device_t midi; /* midi device */ driver_intr_t* midiintr; /* midi intr */ void *midiintr_arg; /* midi intr arg */ void *ih; /* cookie */ struct csa_bridgeinfo binfo; /* The state of this bridge. */ }; typedef struct csa_softc *sc_p; static int csa_probe(device_t dev); static int csa_attach(device_t dev); static struct resource *csa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags); static int csa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r); static int csa_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep); static int csa_teardown_intr(device_t bus, device_t child, struct resource *irq, void *cookie); static driver_intr_t csa_intr; static int csa_initialize(sc_p scp); static void csa_resetdsp(csa_res *resp); static int csa_downloadimage(csa_res *resp); static int csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len); static devclass_t csa_devclass; static int csa_probe(device_t dev) { char *s; s = NULL; switch (pci_get_devid(dev)) { case CS4610_PCI_ID: s = "Crystal Semiconductor CS4610/4611 Audio accelerator"; break; case CS4614_PCI_ID: s = "Crystal Semiconductor CS4614/4622/4624 Audio accelerator/4280 Audio controller"; break; case CS4615_PCI_ID: s = "Crystal Semiconductor CS4615 Audio accelerator"; break; } if (s != NULL) { device_set_desc(dev, s); return (0); } return (ENXIO); } static int csa_attach(device_t dev) { u_int32_t stcmd; sc_p scp; csa_res *resp; struct sndcard_func *func; scp = device_get_softc(dev); /* Fill in the softc. */ bzero(scp, sizeof(*scp)); scp->dev = dev; /* Wake up the device. */ stcmd = pci_read_config(dev, PCIR_COMMAND, 4); if ((stcmd & PCIM_CMD_MEMEN) == 0 || (stcmd & PCIM_CMD_BUSMASTEREN) == 0) { stcmd |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, 4, stcmd); } /* Allocate the resources. */ resp = &scp->res; resp->io_rid = CS461x_IO_OFFSET; resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE); if (resp->io == NULL) return (ENXIO); resp->mem_rid = CS461x_MEM_OFFSET; resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE); if (resp->mem == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); return (ENXIO); } resp->irq_rid = 0; resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (resp->irq == NULL) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); return (ENXIO); } /* Enable interrupt. */ if (bus_setup_intr(dev, resp->irq, INTR_TYPE_TTY, csa_intr, scp, &scp->ih)) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); return (ENXIO); } if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); /* Initialize the chip. */ if (csa_initialize(scp)) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); return (ENXIO); } /* Reset the Processor. */ csa_resetdsp(resp); /* Download the Processor Image to the processor. */ if (csa_downloadimage(resp)) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); return (ENXIO); } /* Attach the children. */ /* PCM Audio */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->varinfo = &scp->binfo; func->func = SCF_PCM; scp->pcm = device_add_child(dev, "pcm", -1); device_set_ivars(scp->pcm, func); /* Midi Interface */ func = malloc(sizeof(struct sndcard_func), M_DEVBUF, M_NOWAIT); if (func == NULL) return (ENOMEM); bzero(func, sizeof(*func)); func->varinfo = &scp->binfo; func->func = SCF_MIDI; scp->midi = device_add_child(dev, "midi", -1); device_set_ivars(scp->midi, func); bus_generic_attach(dev); return (0); } static struct resource * csa_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { sc_p scp; csa_res *resp; struct resource *res; scp = device_get_softc(bus); resp = &scp->res; switch (type) { case SYS_RES_IRQ: if (*rid != 0) return (NULL); res = resp->irq; break; case SYS_RES_MEMORY: switch (*rid) { case CS461x_IO_OFFSET: res = resp->io; break; case CS461x_MEM_OFFSET: res = resp->mem; break; default: return (NULL); } break; default: return (NULL); } return res; } static int csa_release_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (0); } /* * The following three functions deal with interrupt handling. * An interrupt is primarily handled by the bridge driver. * The bridge driver then determines the child devices to pass * the interrupt. Certain information of the device can be read * only once(eg the value of HISR). The bridge driver is responsible * to pass such the information to the children. */ static int csa_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp; csa_res *resp; struct sndcard_func *func; scp = device_get_softc(bus); resp = &scp->res; /* * Look at the function code of the child to determine * the appropriate hander for it. */ func = device_get_ivars(child); if (func == NULL || irq != resp->irq) return (EINVAL); switch (func->func) { case SCF_PCM: scp->pcmintr = intr; scp->pcmintr_arg = arg; break; case SCF_MIDI: scp->midiintr = intr; scp->midiintr_arg = arg; break; default: return (EINVAL); } *cookiep = scp; if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); return (0); } static int csa_teardown_intr(device_t bus, device_t child, struct resource *irq, void *cookie) { sc_p scp; csa_res *resp; struct sndcard_func *func; scp = device_get_softc(bus); resp = &scp->res; /* * Look at the function code of the child to determine * the appropriate hander for it. */ func = device_get_ivars(child); if (func == NULL || irq != resp->irq || cookie != scp) return (EINVAL); switch (func->func) { case SCF_PCM: scp->pcmintr = NULL; scp->pcmintr_arg = NULL; break; case SCF_MIDI: scp->midiintr = NULL; scp->midiintr_arg = NULL; break; default: return (EINVAL); } return (0); } /* The interrupt handler */ static void csa_intr(void *arg) { sc_p scp = arg; csa_res *resp; u_int32_t hisr; resp = &scp->res; /* Is this interrupt for us? */ hisr = csa_readio(resp, BA0_HISR); if ((hisr & ~HISR_INTENA) == 0) { /* Throw an eoi. */ csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); return; } /* * Pass the value of HISR via struct csa_bridgeinfo. * The children get access through their ivars. */ scp->binfo.hisr = hisr; /* Invoke the handlers of the children. */ if ((hisr & (HISR_VC0 | HISR_VC1)) != 0 && scp->pcmintr != NULL) scp->pcmintr(scp->pcmintr_arg); if ((hisr & HISR_MIDI) != 0 && scp->midiintr != NULL) scp->midiintr(scp->midiintr_arg); /* Throw an eoi. */ csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); } static int csa_initialize(sc_p scp) { int i; u_int32_t acsts, acisv; csa_res *resp; resp = &scp->res; /* * First, blast the clock control register to zero so that the PLL starts * out in a known state, and blast the master serial port control register * to zero so that the serial ports also start out in a known state. */ csa_writeio(resp, BA0_CLKCR1, 0); csa_writeio(resp, BA0_SERMC1, 0); /* * If we are in AC97 mode, then we must set the part to a host controlled * AC-link. Otherwise, we won't be able to bring up the link. */ #if 1 csa_writeio(resp, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_1_03); /* 1.03 codec */ #else csa_writeio(resp, BA0_SERACC, SERACC_HSP | SERACC_CODEC_TYPE_2_0); /* 2.0 codec */ #endif /* 1 */ /* * Drive the ARST# pin low for a minimum of 1uS (as defined in the AC97 * spec) and then drive it high. This is done for non AC97 modes since * there might be logic external to the CS461x that uses the ARST# line * for a reset. */ csa_writeio(resp, BA0_ACCTL, 0); DELAY(100); csa_writeio(resp, BA0_ACCTL, ACCTL_RSTN); /* * The first thing we do here is to enable sync generation. As soon * as we start receiving bit clock, we'll start producing the SYNC * signal. */ csa_writeio(resp, BA0_ACCTL, ACCTL_ESYN | ACCTL_RSTN); /* * Now wait for a short while to allow the AC97 part to start * generating bit clock (so we don't try to start the PLL without an * input clock). */ DELAY(50000); /* * Set the serial port timing configuration, so that * the clock control circuit gets its clock from the correct place. */ csa_writeio(resp, BA0_SERMC1, SERMC1_PTC_AC97); /* * Write the selected clock control setup to the hardware. Do not turn on * SWCE yet (if requested), so that the devices clocked by the output of * PLL are not clocked until the PLL is stable. */ csa_writeio(resp, BA0_PLLCC, PLLCC_LPF_1050_2780_KHZ | PLLCC_CDR_73_104_MHZ); csa_writeio(resp, BA0_PLLM, 0x3a); csa_writeio(resp, BA0_CLKCR2, CLKCR2_PDIVS_8); /* * Power up the PLL. */ csa_writeio(resp, BA0_CLKCR1, CLKCR1_PLLP); /* * Wait until the PLL has stabilized. */ DELAY(50000); /* * Turn on clocking of the core so that we can setup the serial ports. */ csa_writeio(resp, BA0_CLKCR1, csa_readio(resp, BA0_CLKCR1) | CLKCR1_SWCE); /* * Fill the serial port FIFOs with silence. */ csa_clearserialfifos(resp); /* * Set the serial port FIFO pointer to the first sample in the FIFO. */ #if notdef csa_writeio(resp, BA0_SERBSP, 0); #endif /* notdef */ /* * Write the serial port configuration to the part. The master * enable bit is not set until all other values have been written. */ csa_writeio(resp, BA0_SERC1, SERC1_SO1F_AC97 | SERC1_SO1EN); csa_writeio(resp, BA0_SERC2, SERC2_SI1F_AC97 | SERC1_SO1EN); csa_writeio(resp, BA0_SERMC1, SERMC1_PTC_AC97 | SERMC1_MSPE); /* * Wait for the codec ready signal from the AC97 codec. */ acsts = 0; for (i = 0 ; i < 1000 ; i++) { /* * First, lets wait a short while to let things settle out a bit, * and to prevent retrying the read too quickly. */ DELAY(125); /* * Read the AC97 status register to see if we've seen a CODEC READY * signal from the AC97 codec. */ acsts = csa_readio(resp, BA0_ACSTS); if ((acsts & ACSTS_CRDY) != 0) break; } /* * Make sure we sampled CODEC READY. */ if ((acsts & ACSTS_CRDY) == 0) return (ENXIO); /* * Assert the vaid frame signal so that we can start sending commands * to the AC97 codec. */ csa_writeio(resp, BA0_ACCTL, ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait until we've sampled input slots 3 and 4 as valid, meaning that * the codec is pumping ADC data across the AC-link. */ acisv = 0; for (i = 0 ; i < 1000 ; i++) { /* * First, lets wait a short while to let things settle out a bit, * and to prevent retrying the read too quickly. */ #if notdef DELAY(10000000L); /* clw */ #else DELAY(1000); #endif /* notdef */ /* * Read the input slot valid register and see if input slots 3 and * 4 are valid yet. */ acisv = csa_readio(resp, BA0_ACISV); if ((acisv & (ACISV_ISV3 | ACISV_ISV4)) == (ACISV_ISV3 | ACISV_ISV4)) break; } /* * Make sure we sampled valid input slots 3 and 4. If not, then return * an error. */ if ((acisv & (ACISV_ISV3 | ACISV_ISV4)) != (ACISV_ISV3 | ACISV_ISV4)) return (ENXIO); /* * Now, assert valid frame and the slot 3 and 4 valid bits. This will * commense the transfer of digital audio data to the AC97 codec. */ csa_writeio(resp, BA0_ACOSV, ACOSV_SLV3 | ACOSV_SLV4); /* * Power down the DAC and ADC. We will power them up (if) when we need * them. */ #if notdef csa_writeio(resp, BA0_AC97_POWERDOWN, 0x300); #endif /* notdef */ /* * Turn off the Processor by turning off the software clock enable flag in * the clock control register. */ #if notdef clkcr1 = csa_readio(resp, BA0_CLKCR1) & ~CLKCR1_SWCE; csa_writeio(resp, BA0_CLKCR1, clkcr1); #endif /* notdef */ /* * Enable interrupts on the part. */ #if notdef csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); #endif /* notdef */ return (0); } void csa_clearserialfifos(csa_res *resp) { int i, j, pwr; u_int8_t clkcr1, serbst; /* * See if the devices are powered down. If so, we must power them up first * or they will not respond. */ pwr = 1; clkcr1 = csa_readio(resp, BA0_CLKCR1); if ((clkcr1 & CLKCR1_SWCE) == 0) { csa_writeio(resp, BA0_CLKCR1, clkcr1 | CLKCR1_SWCE); pwr = 0; } /* * We want to clear out the serial port FIFOs so we don't end up playing * whatever random garbage happens to be in them. We fill the sample FIFOs * with zero (silence). */ csa_writeio(resp, BA0_SERBWP, 0); /* Fill all 256 sample FIFO locations. */ serbst = 0; for (i = 0 ; i < 256 ; i++) { /* Make sure the previous FIFO write operation has completed. */ for (j = 0 ; j < 5 ; j++) { DELAY(100); serbst = csa_readio(resp, BA0_SERBST); if ((serbst & SERBST_WBSY) == 0) break; } if ((serbst & SERBST_WBSY) != 0) { if (!pwr) csa_writeio(resp, BA0_CLKCR1, clkcr1); } /* Write the serial port FIFO index. */ csa_writeio(resp, BA0_SERBAD, i); /* Tell the serial port to load the new value into the FIFO location. */ csa_writeio(resp, BA0_SERBCM, SERBCM_WRC); } /* * Now, if we powered up the devices, then power them back down again. * This is kinda ugly, but should never happen. */ if (!pwr) csa_writeio(resp, BA0_CLKCR1, clkcr1); } static void csa_resetdsp(csa_res *resp) { int i; /* * Write the reset bit of the SP control register. */ csa_writemem(resp, BA1_SPCR, SPCR_RSTSP); /* * Write the control register. */ csa_writemem(resp, BA1_SPCR, SPCR_DRQEN); /* * Clear the trap registers. */ for (i = 0 ; i < 8 ; i++) { csa_writemem(resp, BA1_DREG, DREG_REGID_TRAP_SELECT + i); csa_writemem(resp, BA1_TWPR, 0xffff); } csa_writemem(resp, BA1_DREG, 0); /* * Set the frame timer to reflect the number of cycles per frame. */ csa_writemem(resp, BA1_FRMT, 0xadf); } static int csa_downloadimage(csa_res *resp) { int ret; u_long ul, offset; for (ul = 0, offset = 0 ; ul < INKY_MEMORY_COUNT ; ul++) { /* * DMA this block from host memory to the appropriate * memory on the CSDevice. */ ret = csa_transferimage( resp, BA1Struct.BA1Array + offset, BA1Struct.MemoryStat[ul].ulDestByteOffset, BA1Struct.MemoryStat[ul].ulSourceByteSize); if (ret) return (ret); offset += BA1Struct.MemoryStat[ul].ulSourceByteSize >> 2; } return (0); } static int csa_transferimage(csa_res *resp, u_long *src, u_long dest, u_long len) { u_long ul; /* * We do not allow DMAs from host memory to host memory (although the DMA * can do it) and we do not allow DMAs which are not a multiple of 4 bytes * in size (because that DMA can not do that). Return an error if either * of these conditions exist. */ if ((len & 0x3) != 0) return (EINVAL); /* Check the destination address that it is a multiple of 4 */ if ((dest & 0x3) != 0) return (EINVAL); /* Write the buffer out. */ for (ul = 0 ; ul < len ; ul += 4) csa_writemem(resp, dest + ul, src[ul >> 2]); return (0); } int csa_readcodec(csa_res *resp, u_long offset, u_int32_t *data) { int i; u_int32_t acsda, acctl, acsts; /* * Make sure that there is not data sitting around from a previous * uncompleted access. ACSDA = Status Data Register = 47Ch */ acsda = csa_readio(resp, BA0_ACSDA); /* * Setup the AC97 control registers on the CS461x to send the * appropriate command to the AC97 to perform the read. * ACCAD = Command Address Register = 46Ch * ACCDA = Command Data Register = 470h * ACCTL = Control Register = 460h * set DCV - will clear when process completed * set CRW - Read command * set VFRM - valid frame enabled * set ESYN - ASYNC generation enabled * set RSTN - ARST# inactive, AC97 codec not reset */ /* * Get the actual AC97 register from the offset */ csa_writeio(resp, BA0_ACCAD, offset - BA0_AC97_RESET); csa_writeio(resp, BA0_ACCDA, 0); csa_writeio(resp, BA0_ACCTL, ACCTL_DCV | ACCTL_CRW | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait for the read to occur. */ acctl = 0; for (i = 0 ; i < 10 ; i++) { /* * First, we want to wait for a short time. */ DELAY(25); /* * Now, check to see if the read has completed. * ACCTL = 460h, DCV should be reset by now and 460h = 17h */ acctl = csa_readio(resp, BA0_ACCTL); if ((acctl & ACCTL_DCV) == 0) break; } /* * Make sure the read completed. */ if ((acctl & ACCTL_DCV) != 0) return (EAGAIN); /* * Wait for the valid status bit to go active. */ acsts = 0; for (i = 0 ; i < 10 ; i++) { /* * Read the AC97 status register. * ACSTS = Status Register = 464h */ acsts = csa_readio(resp, BA0_ACSTS); /* * See if we have valid status. * VSTS - Valid Status */ if ((acsts & ACSTS_VSTS) != 0) break; /* * Wait for a short while. */ DELAY(25); } /* * Make sure we got valid status. */ if ((acsts & ACSTS_VSTS) == 0) return (EAGAIN); /* * Read the data returned from the AC97 register. * ACSDA = Status Data Register = 474h */ *data = csa_readio(resp, BA0_ACSDA); return (0); } int csa_writecodec(csa_res *resp, u_long offset, u_int32_t data) { int i; u_int32_t acctl; /* * Setup the AC97 control registers on the CS461x to send the * appropriate command to the AC97 to perform the write. * ACCAD = Command Address Register = 46Ch * ACCDA = Command Data Register = 470h * ACCTL = Control Register = 460h * set DCV - will clear when process completed * set VFRM - valid frame enabled * set ESYN - ASYNC generation enabled * set RSTN - ARST# inactive, AC97 codec not reset */ /* * Get the actual AC97 register from the offset */ csa_writeio(resp, BA0_ACCAD, offset - BA0_AC97_RESET); csa_writeio(resp, BA0_ACCDA, data); csa_writeio(resp, BA0_ACCTL, ACCTL_DCV | ACCTL_VFRM | ACCTL_ESYN | ACCTL_RSTN); /* * Wait for the write to occur. */ acctl = 0; for (i = 0 ; i < 10 ; i++) { /* * First, we want to wait for a short time. */ DELAY(25); /* * Now, check to see if the read has completed. * ACCTL = 460h, DCV should be reset by now and 460h = 17h */ acctl = csa_readio(resp, BA0_ACCTL); if ((acctl & ACCTL_DCV) == 0) break; } /* * Make sure the write completed. */ if ((acctl & ACCTL_DCV) != 0) return (EAGAIN); return (0); } u_int32_t csa_readio(csa_res *resp, u_long offset) { u_int32_t ul; if (offset < BA0_AC97_RESET) return bus_space_read_4(rman_get_bustag(resp->io), rman_get_bushandle(resp->io), offset) & 0xffffffff; else { if (csa_readcodec(resp, offset, &ul)) ul = 0; return (ul); } } void csa_writeio(csa_res *resp, u_long offset, u_int32_t data) { if (offset < BA0_AC97_RESET) bus_space_write_4(rman_get_bustag(resp->io), rman_get_bushandle(resp->io), offset, data); else csa_writecodec(resp, offset, data); } u_int32_t csa_readmem(csa_res *resp, u_long offset) { return bus_space_read_4(rman_get_bustag(resp->mem), rman_get_bushandle(resp->mem), offset) & 0xffffffff; } void csa_writemem(csa_res *resp, u_long offset, u_int32_t data) { bus_space_write_4(rman_get_bustag(resp->mem), rman_get_bushandle(resp->mem), offset, data); } static device_method_t csa_methods[] = { /* Device interface */ DEVMETHOD(device_probe, csa_probe), DEVMETHOD(device_attach, csa_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), DEVMETHOD(device_resume, bus_generic_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_alloc_resource, csa_alloc_resource), DEVMETHOD(bus_release_resource, csa_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_setup_intr, csa_setup_intr), DEVMETHOD(bus_teardown_intr, csa_teardown_intr), { 0, 0 } }; static driver_t csa_driver = { "csa", csa_methods, sizeof(struct csa_softc), }; /* * csa can be attached to a pci bus. */ DRIVER_MODULE(snd_csa, pci, csa_driver, csa_devclass, 0, 0); MODULE_DEPEND(snd_csa, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_csa, 1); Index: stable/4/sys/dev/sound/pci/csapcm.c =================================================================== --- stable/4/sys/dev/sound/pci/csapcm.c (revision 71948) +++ stable/4/sys/dev/sound/pci/csapcm.c (revision 71949) @@ -1,928 +1,913 @@ /* * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * * Portions of this source are based on cwcealdr.cpp and dhwiface.cpp in * cwcealdr1.zip, the sample sources by Crystal Semiconductor. * Copyright (c) 1996-1998 Crystal Semiconductor Corp. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include /* device private data */ struct csa_info; struct csa_chinfo { struct csa_info *parent; pcm_channel *channel; snd_dbuf *buffer; int dir; u_int32_t fmt; int dma; }; struct csa_info { csa_res res; /* resource */ void *ih; /* Interrupt cookie */ bus_dma_tag_t parent_dmat; /* DMA tag */ struct csa_bridgeinfo *binfo; /* The state of the parent. */ /* Contents of board's registers */ u_long pfie; u_long pctl; u_long cctl; struct csa_chinfo pch, rch; }; /* -------------------------------------------------------------------- */ /* prototypes */ static int csa_init(struct csa_info *); static void csa_intr(void *); static void csa_setplaysamplerate(csa_res *resp, u_long ulInRate); static void csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate); static void csa_startplaydma(struct csa_info *csa); static void csa_startcapturedma(struct csa_info *csa); static void csa_stopplaydma(struct csa_info *csa); static void csa_stopcapturedma(struct csa_info *csa); static void csa_powerupadc(csa_res *resp); static void csa_powerupdac(csa_res *resp); static int csa_startdsp(csa_res *resp); static int csa_allocres(struct csa_info *scp, device_t dev); static void csa_releaseres(struct csa_info *scp, device_t dev); -/* talk to the codec - called from ac97.c */ -static u_int32_t csa_rdcd(void *, int); -static void csa_wrcd(void *, int, u_int32_t); - -/* channel interface */ -static void *csachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int csachan_setdir(void *data, int dir); -static int csachan_setformat(void *data, u_int32_t format); -static int csachan_setspeed(void *data, u_int32_t speed); -static int csachan_setblocksize(void *data, u_int32_t blocksize); -static int csachan_trigger(void *data, int go); -static int csachan_getptr(void *data); -static pcmchan_caps *csachan_getcaps(void *data); - static u_int32_t csa_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_S16_BE, AFMT_STEREO | AFMT_S16_BE, 0 }; static pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0}; static u_int32_t csa_recfmt[] = { AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0}; -static pcm_channel csa_chantemplate = { - csachan_init, - csachan_setdir, - csachan_setformat, - csachan_setspeed, - csachan_setblocksize, - csachan_trigger, - csachan_getptr, - csachan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - /* -------------------------------------------------------------------- */ +/* ac97 codec */ -/* channel interface */ -static void * -csachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) -{ - struct csa_info *csa = devinfo; - struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch; - - ch->parent = csa; - ch->channel = c; - ch->buffer = b; - ch->buffer->bufsize = CS461x_BUFFSIZE; - if (chn_allocbuf(ch->buffer, csa->parent_dmat) == -1) return NULL; - return ch; -} - static int -csachan_setdir(void *data, int dir) +csa_rdcd(kobj_t obj, void *devinfo, int regno) { - struct csa_chinfo *ch = data; - struct csa_info *csa = ch->parent; - csa_res *resp; + u_int32_t data; + struct csa_info *csa = (struct csa_info *)devinfo; - resp = &csa->res; + if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data)) + data = 0; - if (dir == PCMDIR_PLAY) - csa_writemem(resp, BA1_PBA, vtophys(ch->buffer->buf)); - else - csa_writemem(resp, BA1_CBA, vtophys(ch->buffer->buf)); - ch->dir = dir; - return 0; + return data; } static int -csachan_setformat(void *data, u_int32_t format) +csa_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { - struct csa_chinfo *ch = data; - struct csa_info *csa = ch->parent; - u_long pdtc; - csa_res *resp; + struct csa_info *csa = (struct csa_info *)devinfo; - resp = &csa->res; + csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data); - if (ch->dir == PCMDIR_REC) - csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); - else { - csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f; - if (format & AFMT_U8 || format & AFMT_U16_LE || format & AFMT_U16_BE) - csa->pfie |= 0x8000; - if (format & AFMT_S16_BE || format & AFMT_U16_BE) - csa->pfie |= 0x4000; - if (!(format & AFMT_STEREO)) - csa->pfie |= 0x2000; - if (format & AFMT_U8 || format & AFMT_S8) - csa->pfie |= 0x1000; - csa_writemem(resp, BA1_PFIE, csa->pfie); - pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000003ff; - if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) && (format & AFMT_STEREO)) - pdtc |= 0x00f; - else if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) || (format & AFMT_STEREO)) - pdtc |= 0x007; - else - pdtc |= 0x003; - csa_writemem(resp, BA1_PDTC, pdtc); - } - ch->fmt = format; return 0; } -static int -csachan_setspeed(void *data, u_int32_t speed) -{ - struct csa_chinfo *ch = data; - struct csa_info *csa = ch->parent; - csa_res *resp; +static kobj_method_t csa_ac97_methods[] = { + KOBJMETHOD(ac97_read, csa_rdcd), + KOBJMETHOD(ac97_write, csa_wrcd), + { 0, 0 } +}; +AC97_DECLARE(csa_ac97); - resp = &csa->res; - - if (ch->dir == PCMDIR_PLAY) - csa_setplaysamplerate(resp, speed); - else if (ch->dir == PCMDIR_REC) - csa_setcapturesamplerate(resp, speed); - - /* rec/play speeds locked together - should indicate in flags */ -#if 0 - if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed; - else d->play[0].speed = speed; -#endif - return speed; /* XXX calc real speed */ -} - static void csa_setplaysamplerate(csa_res *resp, u_long ulInRate) { u_long ulTemp1, ulTemp2; u_long ulPhiIncr; u_long ulCorrectionPerGOF, ulCorrectionPerSec; u_long ulOutRate; ulOutRate = 48000; /* * Compute the values used to drive the actual sample rate conversion. * The following formulas are being computed, using inline assembly * since we need to use 64 bit arithmetic to compute the values: * * ulPhiIncr = floor((Fs,in * 2^26) / Fs,out) * ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) / * GOF_PER_SEC) * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - * GOF_PER_SEC * ulCorrectionPerGOF * * i.e. * * ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) * ulCorrectionPerGOF:ulCorrectionPerSec = * dividend:remainder(ulOther / GOF_PER_SEC) */ ulTemp1 = ulInRate << 16; ulPhiIncr = ulTemp1 / ulOutRate; ulTemp1 -= ulPhiIncr * ulOutRate; ulTemp1 <<= 10; ulPhiIncr <<= 10; ulTemp2 = ulTemp1 / ulOutRate; ulPhiIncr += ulTemp2; ulTemp1 -= ulTemp2 * ulOutRate; ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC; ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC; ulCorrectionPerSec = ulTemp1; /* * Fill in the SampleRateConverter control block. */ csa_writemem(resp, BA1_PSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF)); csa_writemem(resp, BA1_PPI, ulPhiIncr); } static void csa_setcapturesamplerate(csa_res *resp, u_long ulOutRate) { u_long ulPhiIncr, ulCoeffIncr, ulTemp1, ulTemp2; u_long ulCorrectionPerGOF, ulCorrectionPerSec, ulInitialDelay; u_long dwFrameGroupLength, dwCnt; u_long ulInRate; ulInRate = 48000; /* * We can only decimate by up to a factor of 1/9th the hardware rate. * Return an error if an attempt is made to stray outside that limit. */ if((ulOutRate * 9) < ulInRate) return; /* * We can not capture at at rate greater than the Input Rate (48000). * Return an error if an attempt is made to stray outside that limit. */ if(ulOutRate > ulInRate) return; /* * Compute the values used to drive the actual sample rate conversion. * The following formulas are being computed, using inline assembly * since we need to use 64 bit arithmetic to compute the values: * * ulCoeffIncr = -floor((Fs,out * 2^23) / Fs,in) * ulPhiIncr = floor((Fs,in * 2^26) / Fs,out) * ulCorrectionPerGOF = floor((Fs,in * 2^26 - Fs,out * ulPhiIncr) / * GOF_PER_SEC) * ulCorrectionPerSec = Fs,in * 2^26 - Fs,out * phiIncr - * GOF_PER_SEC * ulCorrectionPerGOF * ulInitialDelay = ceil((24 * Fs,in) / Fs,out) * * i.e. * * ulCoeffIncr = neg(dividend((Fs,out * 2^23) / Fs,in)) * ulPhiIncr:ulOther = dividend:remainder((Fs,in * 2^26) / Fs,out) * ulCorrectionPerGOF:ulCorrectionPerSec = * dividend:remainder(ulOther / GOF_PER_SEC) * ulInitialDelay = dividend(((24 * Fs,in) + Fs,out - 1) / Fs,out) */ ulTemp1 = ulOutRate << 16; ulCoeffIncr = ulTemp1 / ulInRate; ulTemp1 -= ulCoeffIncr * ulInRate; ulTemp1 <<= 7; ulCoeffIncr <<= 7; ulCoeffIncr += ulTemp1 / ulInRate; ulCoeffIncr ^= 0xFFFFFFFF; ulCoeffIncr++; ulTemp1 = ulInRate << 16; ulPhiIncr = ulTemp1 / ulOutRate; ulTemp1 -= ulPhiIncr * ulOutRate; ulTemp1 <<= 10; ulPhiIncr <<= 10; ulTemp2 = ulTemp1 / ulOutRate; ulPhiIncr += ulTemp2; ulTemp1 -= ulTemp2 * ulOutRate; ulCorrectionPerGOF = ulTemp1 / GOF_PER_SEC; ulTemp1 -= ulCorrectionPerGOF * GOF_PER_SEC; ulCorrectionPerSec = ulTemp1; ulInitialDelay = ((ulInRate * 24) + ulOutRate - 1) / ulOutRate; /* * Fill in the VariDecimate control block. */ csa_writemem(resp, BA1_CSRC, ((ulCorrectionPerSec << 16) & 0xFFFF0000) | (ulCorrectionPerGOF & 0xFFFF)); csa_writemem(resp, BA1_CCI, ulCoeffIncr); csa_writemem(resp, BA1_CD, (((BA1_VARIDEC_BUF_1 + (ulInitialDelay << 2)) << 16) & 0xFFFF0000) | 0x80); csa_writemem(resp, BA1_CPI, ulPhiIncr); /* * Figure out the frame group length for the write back task. Basically, * this is just the factors of 24000 (2^6*3*5^3) that are not present in * the output sample rate. */ dwFrameGroupLength = 1; for(dwCnt = 2; dwCnt <= 64; dwCnt *= 2) { if(((ulOutRate / dwCnt) * dwCnt) != ulOutRate) { dwFrameGroupLength *= 2; } } if(((ulOutRate / 3) * 3) != ulOutRate) { dwFrameGroupLength *= 3; } for(dwCnt = 5; dwCnt <= 125; dwCnt *= 5) { if(((ulOutRate / dwCnt) * dwCnt) != ulOutRate) { dwFrameGroupLength *= 5; } } /* * Fill in the WriteBack control block. */ csa_writemem(resp, BA1_CFG1, dwFrameGroupLength); csa_writemem(resp, BA1_CFG2, (0x00800000 | dwFrameGroupLength)); csa_writemem(resp, BA1_CCST, 0x0000FFFF); csa_writemem(resp, BA1_CSPB, ((65536 * ulOutRate) / 24000)); csa_writemem(resp, (BA1_CSPB + 4), 0x0000FFFF); } -static int -csachan_setblocksize(void *data, u_int32_t blocksize) -{ -#if notdef - return blocksize; -#else - struct csa_chinfo *ch = data; - return ch->buffer->bufsize / 2; -#endif /* notdef */ -} - -static int -csachan_trigger(void *data, int go) -{ - struct csa_chinfo *ch = data; - struct csa_info *csa = ch->parent; - - if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) - return 0; - - if (ch->dir == PCMDIR_PLAY) { - if (go == PCMTRIG_START) - csa_startplaydma(csa); - else - csa_stopplaydma(csa); - } else { - if (go == PCMTRIG_START) - csa_startcapturedma(csa); - else - csa_stopcapturedma(csa); - } - return 0; -} - static void csa_startplaydma(struct csa_info *csa) { csa_res *resp; u_long ul; if (!csa->pch.dma) { resp = &csa->res; ul = csa_readmem(resp, BA1_PCTL); ul &= 0x0000ffff; csa_writemem(resp, BA1_PCTL, ul | csa->pctl); csa_writemem(resp, BA1_PVOL, 0x80008000); csa->pch.dma = 1; } } static void csa_startcapturedma(struct csa_info *csa) { csa_res *resp; u_long ul; if (!csa->rch.dma) { resp = &csa->res; ul = csa_readmem(resp, BA1_CCTL); ul &= 0xffff0000; csa_writemem(resp, BA1_CCTL, ul | csa->cctl); csa_writemem(resp, BA1_CVOL, 0x80008000); csa->rch.dma = 1; } } static void csa_stopplaydma(struct csa_info *csa) { csa_res *resp; u_long ul; if (csa->pch.dma) { resp = &csa->res; ul = csa_readmem(resp, BA1_PCTL); csa->pctl = ul & 0xffff0000; csa_writemem(resp, BA1_PCTL, ul & 0x0000ffff); csa_writemem(resp, BA1_PVOL, 0xffffffff); csa->pch.dma = 0; /* * The bitwise pointer of the serial FIFO in the DSP * seems to make an error upon starting or stopping the * DSP. Clear the FIFO and correct the pointer if we * are not capturing. */ if (!csa->rch.dma) { csa_clearserialfifos(resp); csa_writeio(resp, BA0_SERBSP, 0); } } } static void csa_stopcapturedma(struct csa_info *csa) { csa_res *resp; u_long ul; if (csa->rch.dma) { resp = &csa->res; ul = csa_readmem(resp, BA1_CCTL); csa->cctl = ul & 0x0000ffff; csa_writemem(resp, BA1_CCTL, ul & 0xffff0000); csa_writemem(resp, BA1_CVOL, 0xffffffff); csa->rch.dma = 0; /* * The bitwise pointer of the serial FIFO in the DSP * seems to make an error upon starting or stopping the * DSP. Clear the FIFO and correct the pointer if we * are not playing. */ if (!csa->pch.dma) { csa_clearserialfifos(resp); csa_writeio(resp, BA0_SERBSP, 0); } } } static void csa_powerupdac(csa_res *resp) { int i; u_long ul; /* * Power on the DACs on the AC97 codec. We turn off the DAC * powerdown bit and write the new value of the power control * register. */ ul = csa_readio(resp, BA0_AC97_POWERDOWN); ul &= 0xfdff; csa_writeio(resp, BA0_AC97_POWERDOWN, ul); /* * Now, we wait until we sample a DAC ready state. */ for (i = 0 ; i < 32 ; i++) { /* * First, lets wait a short while to let things settle out a * bit, and to prevent retrying the read too quickly. */ DELAY(125); /* * Read the current state of the power control register. */ ul = csa_readio(resp, BA0_AC97_POWERDOWN); /* * If the DAC ready state bit is set, then stop waiting. */ if ((ul & 0x2) != 0) break; } /* * The DACs are now calibrated, so we can unmute the DAC output. */ csa_writeio(resp, BA0_AC97_PCM_OUT_VOLUME, 0x0808); } static void csa_powerupadc(csa_res *resp) { int i; u_long ul; /* * Power on the ADCs on the AC97 codec. We turn off the ADC * powerdown bit and write the new value of the power control * register. */ ul = csa_readio(resp, BA0_AC97_POWERDOWN); ul &= 0xfeff; csa_writeio(resp, BA0_AC97_POWERDOWN, ul); /* * Now, we wait until we sample a ADC ready state. */ for (i = 0 ; i < 32 ; i++) { /* * First, lets wait a short while to let things settle out a * bit, and to prevent retrying the read too quickly. */ DELAY(125); /* * Read the current state of the power control register. */ ul = csa_readio(resp, BA0_AC97_POWERDOWN); /* * If the ADC ready state bit is set, then stop waiting. */ if ((ul & 0x1) != 0) break; } } static int csa_startdsp(csa_res *resp) { int i; u_long ul; /* * Set the frame timer to reflect the number of cycles per frame. */ csa_writemem(resp, BA1_FRMT, 0xadf); /* * Turn on the run, run at frame, and DMA enable bits in the local copy of * the SP control register. */ csa_writemem(resp, BA1_SPCR, SPCR_RUN | SPCR_RUNFR | SPCR_DRQEN); /* * Wait until the run at frame bit resets itself in the SP control * register. */ ul = 0; for (i = 0 ; i < 25 ; i++) { /* * Wait a little bit, so we don't issue PCI reads too frequently. */ #if notdef DELAY(1000); #else DELAY(125); #endif /* notdef */ /* * Fetch the current value of the SP status register. */ ul = csa_readmem(resp, BA1_SPCR); /* * If the run at frame bit has reset, then stop waiting. */ if((ul & SPCR_RUNFR) == 0) break; } /* * If the run at frame bit never reset, then return an error. */ if((ul & SPCR_RUNFR) != 0) return (EAGAIN); return (0); } +/* -------------------------------------------------------------------- */ +/* channel interface */ + +static void * +csachan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct csa_info *csa = devinfo; + struct csa_chinfo *ch = (dir == PCMDIR_PLAY)? &csa->pch : &csa->rch; + + ch->parent = csa; + ch->channel = c; + ch->buffer = b; + if (sndbuf_alloc(ch->buffer, csa->parent_dmat, CS461x_BUFFSIZE) == -1) return NULL; + return ch; +} + static int -csachan_getptr(void *data) +csachan_setdir(kobj_t obj, void *data, int dir) { struct csa_chinfo *ch = data; struct csa_info *csa = ch->parent; csa_res *resp; + + resp = &csa->res; + + if (dir == PCMDIR_PLAY) + csa_writemem(resp, BA1_PBA, vtophys(sndbuf_getbuf(ch->buffer))); + else + csa_writemem(resp, BA1_CBA, vtophys(sndbuf_getbuf(ch->buffer))); + ch->dir = dir; + return 0; +} + +static int +csachan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct csa_chinfo *ch = data; + struct csa_info *csa = ch->parent; + u_long pdtc; + csa_res *resp; + + resp = &csa->res; + + if (ch->dir == PCMDIR_REC) + csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); + else { + csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f; + if (format & AFMT_U8 || format & AFMT_U16_LE || format & AFMT_U16_BE) + csa->pfie |= 0x8000; + if (format & AFMT_S16_BE || format & AFMT_U16_BE) + csa->pfie |= 0x4000; + if (!(format & AFMT_STEREO)) + csa->pfie |= 0x2000; + if (format & AFMT_U8 || format & AFMT_S8) + csa->pfie |= 0x1000; + csa_writemem(resp, BA1_PFIE, csa->pfie); + pdtc = csa_readmem(resp, BA1_PDTC) & ~0x000003ff; + if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) && (format & AFMT_STEREO)) + pdtc |= 0x00f; + else if ((format & AFMT_S16_BE || format & AFMT_U16_BE || format & AFMT_S16_LE || format & AFMT_U16_LE) || (format & AFMT_STEREO)) + pdtc |= 0x007; + else + pdtc |= 0x003; + csa_writemem(resp, BA1_PDTC, pdtc); + } + ch->fmt = format; + return 0; +} + +static int +csachan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct csa_chinfo *ch = data; + struct csa_info *csa = ch->parent; + csa_res *resp; + + resp = &csa->res; + + if (ch->dir == PCMDIR_PLAY) + csa_setplaysamplerate(resp, speed); + else if (ch->dir == PCMDIR_REC) + csa_setcapturesamplerate(resp, speed); + + /* rec/play speeds locked together - should indicate in flags */ +#if 0 + if (ch->direction == PCMDIR_PLAY) d->rec[0].speed = speed; + else d->play[0].speed = speed; +#endif + return speed; /* XXX calc real speed */ +} + +static int +csachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + return CS461x_BUFFSIZE / 2; +} + +static int +csachan_trigger(kobj_t obj, void *data, int go) +{ + struct csa_chinfo *ch = data; + struct csa_info *csa = ch->parent; + + if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + return 0; + + if (ch->dir == PCMDIR_PLAY) { + if (go == PCMTRIG_START) + csa_startplaydma(csa); + else + csa_stopplaydma(csa); + } else { + if (go == PCMTRIG_START) + csa_startcapturedma(csa); + else + csa_stopcapturedma(csa); + } + return 0; +} + +static int +csachan_getptr(kobj_t obj, void *data) +{ + struct csa_chinfo *ch = data; + struct csa_info *csa = ch->parent; + csa_res *resp; int ptr; resp = &csa->res; if (ch->dir == PCMDIR_PLAY) { - ptr = csa_readmem(resp, BA1_PBA) - vtophys(ch->buffer->buf); + ptr = csa_readmem(resp, BA1_PBA) - vtophys(sndbuf_getbuf(ch->buffer)); if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) ptr >>= 1; } else { - ptr = csa_readmem(resp, BA1_CBA) - vtophys(ch->buffer->buf); + ptr = csa_readmem(resp, BA1_CBA) - vtophys(sndbuf_getbuf(ch->buffer)); if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) ptr >>= 1; } return (ptr); } static pcmchan_caps * -csachan_getcaps(void *data) +csachan_getcaps(kobj_t obj, void *data) { struct csa_chinfo *ch = data; return (ch->dir == PCMDIR_PLAY)? &csa_playcaps : &csa_reccaps; } +static kobj_method_t csachan_methods[] = { + KOBJMETHOD(channel_init, csachan_init), + KOBJMETHOD(channel_setdir, csachan_setdir), + KOBJMETHOD(channel_setformat, csachan_setformat), + KOBJMETHOD(channel_setspeed, csachan_setspeed), + KOBJMETHOD(channel_setblocksize, csachan_setblocksize), + KOBJMETHOD(channel_trigger, csachan_trigger), + KOBJMETHOD(channel_getptr, csachan_getptr), + KOBJMETHOD(channel_getcaps, csachan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(csachan); + +/* -------------------------------------------------------------------- */ /* The interrupt handler */ static void csa_intr (void *p) { struct csa_info *csa = p; if ((csa->binfo->hisr & HISR_VC0) != 0) chn_intr(csa->pch.channel); if ((csa->binfo->hisr & HISR_VC1) != 0) chn_intr(csa->rch.channel); } /* -------------------------------------------------------------------- */ /* * Probe and attach the card */ static int csa_init(struct csa_info *csa) { csa_res *resp; resp = &csa->res; csa->pfie = 0; csa_stopplaydma(csa); csa_stopcapturedma(csa); /* Crank up the power on the DAC and ADC. */ csa_powerupadc(resp); csa_powerupdac(resp); csa_setplaysamplerate(resp, 8000); csa_setcapturesamplerate(resp, 8000); if (csa_startdsp(resp)) return (1); return 0; } /* Allocates resources. */ static int csa_allocres(struct csa_info *csa, device_t dev) { csa_res *resp; resp = &csa->res; if (resp->io == NULL) { resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, CS461x_IO_SIZE, RF_ACTIVE); if (resp->io == NULL) return (1); } if (resp->mem == NULL) { resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, CS461x_MEM_SIZE, RF_ACTIVE); if (resp->mem == NULL) return (1); } if (resp->irq == NULL) { resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (resp->irq == NULL) return (1); } if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/CS461x_BUFFSIZE, /*boundary*/CS461x_BUFFSIZE, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &csa->parent_dmat) != 0) return (1); return (0); } /* Releases resources. */ static void csa_releaseres(struct csa_info *csa, device_t dev) { csa_res *resp; resp = &csa->res; if (resp->irq != NULL) { if (csa->ih) bus_teardown_intr(dev, resp->irq, csa->ih); bus_release_resource(dev, SYS_RES_IRQ, resp->irq_rid, resp->irq); resp->irq = NULL; } if (resp->io != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, resp->io_rid, resp->io); resp->io = NULL; } if (resp->mem != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, resp->mem_rid, resp->mem); resp->mem = NULL; } if (csa->parent_dmat != NULL) { bus_dma_tag_destroy(csa->parent_dmat); csa->parent_dmat = NULL; } if (csa != NULL) { free(csa, M_DEVBUF); csa = NULL; } } static int pcmcsa_probe(device_t dev); static int pcmcsa_attach(device_t dev); static int pcmcsa_probe(device_t dev) { char *s; struct sndcard_func *func; /* The parent device has already been probed. */ func = device_get_ivars(dev); if (func == NULL || func->func != SCF_PCM) return (ENXIO); s = "CS461x PCM Audio"; device_set_desc(dev, s); return (0); } static int pcmcsa_attach(device_t dev) { struct csa_info *csa; csa_res *resp; int unit; char status[SND_STATUSLEN]; struct ac97_info *codec; struct sndcard_func *func; csa = malloc(sizeof(*csa), M_DEVBUF, M_NOWAIT); if (csa == NULL) return (ENOMEM); bzero(csa, sizeof(*csa)); unit = device_get_unit(dev); func = device_get_ivars(dev); csa->binfo = func->varinfo; /* * Fake the status of DMA so that the initial value of * PCTL and CCTL can be stored into csa->pctl and csa->cctl, * respectively. */ csa->pch.dma = csa->rch.dma = 1; /* Allocate the resources. */ resp = &csa->res; resp->io_rid = CS461x_IO_OFFSET; resp->mem_rid = CS461x_MEM_OFFSET; resp->irq_rid = 0; if (csa_allocres(csa, dev)) { csa_releaseres(csa, dev); return (ENXIO); } if (csa_init(csa)) { csa_releaseres(csa, dev); return (ENXIO); } - codec = ac97_create(dev, csa, NULL, csa_rdcd, csa_wrcd); + codec = AC97_CREATE(dev, csa, csa_ac97); if (codec == NULL) { csa_releaseres(csa, dev); return (ENXIO); } - if (mixer_init(dev, &ac97_mixer, codec) == -1) { + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { ac97_destroy(codec); csa_releaseres(csa, dev); return (ENXIO); } snprintf(status, SND_STATUSLEN, "at irq %ld", rman_get_start(resp->irq)); /* Enable interrupt. */ if (bus_setup_intr(dev, resp->irq, INTR_TYPE_TTY, csa_intr, csa, &csa->ih)) { ac97_destroy(codec); csa_releaseres(csa, dev); return (ENXIO); } csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); if (pcm_register(dev, csa, 1, 1)) { ac97_destroy(codec); csa_releaseres(csa, dev); return (ENXIO); } - pcm_addchan(dev, PCMDIR_REC, &csa_chantemplate, csa); - pcm_addchan(dev, PCMDIR_PLAY, &csa_chantemplate, csa); + pcm_addchan(dev, PCMDIR_REC, &csachan_class, csa); + pcm_addchan(dev, PCMDIR_PLAY, &csachan_class, csa); pcm_setstatus(dev, status); return (0); } static int pcmcsa_detach(device_t dev) { int r; struct csa_info *csa; r = pcm_unregister(dev); if (r) return r; csa = pcm_getdevinfo(dev); csa_releaseres(csa, dev); return 0; -} - -/* ac97 codec */ - -static u_int32_t -csa_rdcd(void *devinfo, int regno) -{ - u_int32_t data; - struct csa_info *csa = (struct csa_info *)devinfo; - - if (csa_readcodec(&csa->res, regno + BA0_AC97_RESET, &data)) - data = 0; - - return data; -} - -static void -csa_wrcd(void *devinfo, int regno, u_int32_t data) -{ - struct csa_info *csa = (struct csa_info *)devinfo; - - csa_writecodec(&csa->res, regno + BA0_AC97_RESET, data); } static device_method_t pcmcsa_methods[] = { /* Device interface */ DEVMETHOD(device_probe , pcmcsa_probe ), DEVMETHOD(device_attach, pcmcsa_attach), DEVMETHOD(device_detach, pcmcsa_detach), { 0, 0 }, }; static driver_t pcmcsa_driver = { "pcm", pcmcsa_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_csapcm, csa, pcmcsa_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_csapcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1); MODULE_VERSION(snd_csapcm, 1); Index: stable/4/sys/dev/sound/pci/ds1.c =================================================================== --- stable/4/sys/dev/sound/pci/ds1.c (revision 71948) +++ stable/4/sys/dev/sound/pci/ds1.c (revision 71949) @@ -1,1072 +1,1067 @@ /* * Copyright (c) 2000 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include /* -------------------------------------------------------------------- */ #define DS1_CHANS 4 #define DS1_RECPRIMARY 0 +#define DS1_IRQHZ ((48000 << 8) / 256) +#define DS1_BUFFSIZE 4096 struct pbank { volatile u_int32_t Format; volatile u_int32_t LoopDefault; volatile u_int32_t PgBase; volatile u_int32_t PgLoop; volatile u_int32_t PgLoopEnd; volatile u_int32_t PgLoopFrac; volatile u_int32_t PgDeltaEnd; volatile u_int32_t LpfKEnd; volatile u_int32_t EgGainEnd; volatile u_int32_t LchGainEnd; volatile u_int32_t RchGainEnd; volatile u_int32_t Effect1GainEnd; volatile u_int32_t Effect2GainEnd; volatile u_int32_t Effect3GainEnd; volatile u_int32_t LpfQ; volatile u_int32_t Status; volatile u_int32_t NumOfFrames; volatile u_int32_t LoopCount; volatile u_int32_t PgStart; volatile u_int32_t PgStartFrac; volatile u_int32_t PgDelta; volatile u_int32_t LpfK; volatile u_int32_t EgGain; volatile u_int32_t LchGain; volatile u_int32_t RchGain; volatile u_int32_t Effect1Gain; volatile u_int32_t Effect2Gain; volatile u_int32_t Effect3Gain; volatile u_int32_t LpfD1; volatile u_int32_t LpfD2; }; struct rbank { volatile u_int32_t PgBase; volatile u_int32_t PgLoopEnd; volatile u_int32_t PgStart; volatile u_int32_t NumOfLoops; }; struct sc_info; /* channel registers */ struct sc_pchinfo { int run, spd, dir, fmt; snd_dbuf *buffer; pcm_channel *channel; volatile struct pbank *lslot, *rslot; int lsnum, rsnum; struct sc_info *parent; }; struct sc_rchinfo { int run, spd, dir, fmt, num; snd_dbuf *buffer; pcm_channel *channel; volatile struct rbank *slot; struct sc_info *parent; }; /* device private data */ struct sc_info { device_t dev; u_int32_t type, rev; u_int32_t cd2id, ctrlbase; bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; bus_dmamap_t map; struct resource *reg, *irq; int regid, irqid; void *ih; void *regbase; u_int32_t *pbase, pbankbase, pbanksize; volatile struct pbank *pbank[2 * 64]; volatile struct rbank *rbank; int pslotfree, currbank, pchn, rchn; struct sc_pchinfo pch[DS1_CHANS]; struct sc_rchinfo rch[2]; }; struct { u_int32_t dev, subdev; char *name; u_int32_t *mcode; } ds_devs[] = { -/* Beware, things know the indexes here */ {0x00041073, 0, "Yamaha DS-1 (YMF724)", CntrlInst}, {0x000d1073, 0, "Yamaha DS-1E (YMF724F)", CntrlInst1E}, {0x00051073, 0, "Yamaha DS-1? (YMF734)", CntrlInst}, {0x00081073, 0, "Yamaha DS-1? (YMF737)", CntrlInst}, {0x00201073, 0, "Yamaha DS-1? (YMF738)", CntrlInst}, {0x00061073, 0, "Yamaha DS-1? (YMF738_TEG)", CntrlInst}, {0x000a1073, 0x00041073, "Yamaha DS-1 (YMF740)", CntrlInst}, {0x000a1073, 0x000a1073, "Yamaha DS-1 (YMF740B)", CntrlInst}, -/*8*/ {0x000a1073, 0x53328086, "Yamaha DS-1 (YMF740I)", CntrlInst}, + {0x000a1073, 0x53328086, "Yamaha DS-1 (YMF740I)", CntrlInst}, {0x000a1073, 0, "Yamaha DS-1 (YMF740?)", CntrlInst}, {0x000c1073, 0, "Yamaha DS-1E (YMF740C)", CntrlInst1E}, -/*11*/ {0x00101073, 0, "Yamaha DS-1E (YMF744)", CntrlInst1E}, + {0x00101073, 0, "Yamaha DS-1E (YMF744)", CntrlInst1E}, {0x00121073, 0, "Yamaha DS-1E (YMF754)", CntrlInst1E}, {0, 0, NULL, NULL} }; /* -------------------------------------------------------------------- */ /* * prototypes */ -/* channel interface */ -static void *ds1pchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int ds1pchan_setformat(void *data, u_int32_t format); -static int ds1pchan_setspeed(void *data, u_int32_t speed); -static int ds1pchan_setblocksize(void *data, u_int32_t blocksize); -static int ds1pchan_trigger(void *data, int go); -static int ds1pchan_getptr(void *data); -static pcmchan_caps *ds1pchan_getcaps(void *data); - -static void *ds1rchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int ds1rchan_setformat(void *data, u_int32_t format); -static int ds1rchan_setspeed(void *data, u_int32_t speed); -static int ds1rchan_setblocksize(void *data, u_int32_t blocksize); -static int ds1rchan_trigger(void *data, int go); -static int ds1rchan_getptr(void *data); -static pcmchan_caps *ds1rchan_getcaps(void *data); - -/* talk to the codec - called from ac97.c */ -static u_int32_t ds_rdcd(void *, int); -static void ds_wrcd(void *, int, u_int32_t); - /* stuff */ static int ds_init(struct sc_info *); static void ds_intr(void *); /* talk to the card */ static u_int32_t ds_rd(struct sc_info *, int, int); static void ds_wr(struct sc_info *, int, u_int32_t, int); /* -------------------------------------------------------------------- */ static u_int32_t ds_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static pcmchan_caps ds_reccaps = {4000, 48000, ds_recfmt, 0}; static u_int32_t ds_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, /* AFMT_S16_LE, */ AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps ds_playcaps = {4000, 96000, ds_playfmt, 0}; -static pcm_channel ds_pchantemplate = { - ds1pchan_init, - NULL, /* setdir */ - ds1pchan_setformat, - ds1pchan_setspeed, - ds1pchan_setblocksize, - ds1pchan_trigger, - ds1pchan_getptr, - ds1pchan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - -static pcm_channel ds_rchantemplate = { - ds1rchan_init, - NULL, /* setdir */ - ds1rchan_setformat, - ds1rchan_setspeed, - ds1rchan_setblocksize, - ds1rchan_trigger, - ds1rchan_getptr, - ds1rchan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - /* -------------------------------------------------------------------- */ /* Hardware */ static u_int32_t ds_rd(struct sc_info *sc, int regno, int size) { switch (size) { case 1: return bus_space_read_1(sc->st, sc->sh, regno); case 2: return bus_space_read_2(sc->st, sc->sh, regno); case 4: return bus_space_read_4(sc->st, sc->sh, regno); default: return 0xffffffff; } } static void ds_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { case 1: bus_space_write_1(sc->st, sc->sh, regno, data); break; case 2: bus_space_write_2(sc->st, sc->sh, regno, data); break; case 4: bus_space_write_4(sc->st, sc->sh, regno, data); break; } } static void wrl(struct sc_info *sc, u_int32_t *ptr, u_int32_t val) { *(volatile u_int32_t *)ptr = val; bus_space_barrier(sc->st, sc->sh, 0, 0, BUS_SPACE_BARRIER_WRITE); } +/* -------------------------------------------------------------------- */ /* ac97 codec */ static int ds_cdbusy(struct sc_info *sc, int sec) { int i, reg; reg = sec? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR; i = YDSXG_AC97TIMEOUT; while (i > 0) { if (!(ds_rd(sc, reg, 2) & 0x8000)) return 0; i--; } return ETIMEDOUT; } static u_int32_t -ds_initcd(void *devinfo) +ds_initcd(kobj_t obj, void *devinfo) { struct sc_info *sc = (struct sc_info *)devinfo; u_int32_t x; x = pci_read_config(sc->dev, PCIR_DSXGCTRL, 1); if (x & 0x03) { pci_write_config(sc->dev, PCIR_DSXGCTRL, x & ~0x03, 1); pci_write_config(sc->dev, PCIR_DSXGCTRL, x | 0x03, 1); pci_write_config(sc->dev, PCIR_DSXGCTRL, x & ~0x03, 1); /* * The YMF740 on some Intel motherboards requires a pretty * hefty delay after this reset for some reason... Otherwise: * "pcm0: ac97 codec init failed" * Maybe this is needed for all YMF740's? * 400ms and 500ms here seem to work, 300ms does not. * * do it for all chips -cg */ DELAY(500000); } return ds_cdbusy(sc, 0)? 0 : 1; } -static u_int32_t -ds_rdcd(void *devinfo, int regno) +static int +ds_rdcd(kobj_t obj, void *devinfo, int regno) { struct sc_info *sc = (struct sc_info *)devinfo; int sec, cid, i; u_int32_t cmd, reg; sec = regno & 0x100; regno &= 0xff; cid = sec? (sc->cd2id << 8) : 0; reg = sec? YDSXGR_SECSTATUSDATA : YDSXGR_PRISTATUSDATA; if (sec && cid == 0) return 0xffffffff; cmd = YDSXG_AC97READCMD | cid | regno; ds_wr(sc, YDSXGR_AC97CMDADR, cmd, 2); if (ds_cdbusy(sc, sec)) return 0xffffffff; if (sc->type == 11 && sc->rev < 2) for (i = 0; i < 600; i++) ds_rd(sc, reg, 2); return ds_rd(sc, reg, 2); } -static void -ds_wrcd(void *devinfo, int regno, u_int32_t data) +static int +ds_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; int sec, cid; u_int32_t cmd; sec = regno & 0x100; regno &= 0xff; cid = sec? (sc->cd2id << 8) : 0; if (sec && cid == 0) - return; + return ENXIO; cmd = YDSXG_AC97WRITECMD | cid | regno; cmd <<= 16; cmd |= data; ds_wr(sc, YDSXGR_AC97CMDDATA, cmd, 4); - ds_cdbusy(sc, sec); + return ds_cdbusy(sc, sec); } +static kobj_method_t ds_ac97_methods[] = { + KOBJMETHOD(ac97_init, ds_initcd), + KOBJMETHOD(ac97_read, ds_rdcd), + KOBJMETHOD(ac97_write, ds_wrcd), + { 0, 0 } +}; +AC97_DECLARE(ds_ac97); + +/* -------------------------------------------------------------------- */ + static void ds_enadsp(struct sc_info *sc, int on) { u_int32_t v, i; v = on? 1 : 0; if (on) { ds_wr(sc, YDSXGR_CONFIG, 0x00000001, 4); } else { if (ds_rd(sc, YDSXGR_CONFIG, 4)) ds_wr(sc, YDSXGR_CONFIG, 0x00000000, 4); i = YDSXG_WORKBITTIMEOUT; while (i > 0) { if (!(ds_rd(sc, YDSXGR_CONFIG, 4) & 0x00000002)) break; i--; } } } static volatile struct pbank * ds_allocpslot(struct sc_info *sc) { int slot; if (sc->pslotfree > 63) return NULL; slot = sc->pslotfree++; return sc->pbank[slot * 2]; } static int ds_initpbank(volatile struct pbank *pb, int ch, int b16, int stereo, u_int32_t rate, void *base, u_int32_t len) { u_int32_t lv[] = {1, 1, 0, 0, 0}; u_int32_t rv[] = {1, 0, 1, 0, 0}; u_int32_t e1[] = {0, 0, 0, 0, 0}; u_int32_t e2[] = {1, 0, 0, 1, 0}; u_int32_t e3[] = {1, 0, 0, 0, 1}; int ss, i; u_int32_t delta; struct { int rate, fK, fQ; } speedinfo[] = { { 100, 0x00570000, 0x35280000}, { 2000, 0x06aa0000, 0x34a70000}, { 8000, 0x18b20000, 0x32020000}, {11025, 0x20930000, 0x31770000}, {16000, 0x2b9a0000, 0x31390000}, {22050, 0x35a10000, 0x31c90000}, {32000, 0x3eaa0000, 0x33d00000}, /* {44100, 0x04646000, 0x370a0000}, */ {48000, 0x40000000, 0x40000000}, }; ss = b16? 1 : 0; ss += stereo? 1 : 0; delta = (65536 * rate) / 48000; i = 0; while (i < 7 && speedinfo[i].rate < rate) i++; pb->Format = stereo? 0x00010000 : 0; pb->Format |= b16? 0 : 0x80000000; pb->Format |= (stereo && (ch == 2 || ch == 4))? 0x00000001 : 0; pb->LoopDefault = 0; pb->PgBase = base? vtophys(base) : 0; pb->PgLoop = 0; pb->PgLoopEnd = len >> ss; pb->PgLoopFrac = 0; pb->Status = 0; pb->NumOfFrames = 0; pb->LoopCount = 0; pb->PgStart = 0; pb->PgStartFrac = 0; pb->PgDelta = pb->PgDeltaEnd = delta << 12; pb->LpfQ = speedinfo[i].fQ; pb->LpfK = pb->LpfKEnd = speedinfo[i].fK; pb->LpfD1 = pb->LpfD2 = 0; pb->EgGain = pb->EgGainEnd = 0x40000000; pb->LchGain = pb->LchGainEnd = lv[ch] * 0x40000000; pb->RchGain = pb->RchGainEnd = rv[ch] * 0x40000000; pb->Effect1Gain = pb->Effect1GainEnd = e1[ch] * 0x40000000; pb->Effect2Gain = pb->Effect2GainEnd = e2[ch] * 0x40000000; pb->Effect3Gain = pb->Effect3GainEnd = e3[ch] * 0x40000000; return 0; } static void ds_enapslot(struct sc_info *sc, int slot, int go) { wrl(sc, &sc->pbase[slot + 1], go? (sc->pbankbase + 2 * slot * sc->pbanksize) : 0); /* printf("pbase[%d] = 0x%x\n", slot + 1, go? (sc->pbankbase + 2 * slot * sc->pbanksize) : 0); */ } static void ds_setuppch(struct sc_pchinfo *ch) { int stereo, b16, c, sz; void *buf; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; c = stereo? 1 : 0; - buf = ch->buffer->buf; - sz = ch->buffer->bufsize; + buf = sndbuf_getbuf(ch->buffer); + sz = sndbuf_getsize(ch->buffer); ds_initpbank(ch->lslot, c, stereo, b16, ch->spd, buf, sz); ds_initpbank(ch->lslot + 1, c, stereo, b16, ch->spd, buf, sz); ds_initpbank(ch->rslot, 2, stereo, b16, ch->spd, buf, sz); ds_initpbank(ch->rslot + 1, 2, stereo, b16, ch->spd, buf, sz); } static void ds_setuprch(struct sc_rchinfo *ch) { struct sc_info *sc = ch->parent; int stereo, b16, i, sz, pri; u_int32_t x, y; void *buf; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; - buf = ch->buffer->buf; - sz = ch->buffer->bufsize; + buf = sndbuf_getbuf(ch->buffer); + sz = sndbuf_getsize(ch->buffer); pri = (ch->num == DS1_RECPRIMARY)? 1 : 0; for (i = 0; i < 2; i++) { ch->slot[i].PgBase = vtophys(buf); ch->slot[i].PgLoopEnd = sz; ch->slot[i].PgStart = 0; ch->slot[i].NumOfLoops = 0; } x = (b16? 0x00 : 0x01) | (stereo? 0x02 : 0x00); y = (48000 * 4096) / ch->spd; y--; /* printf("pri = %d, x = %d, y = %d\n", pri, x, y); */ ds_wr(sc, pri? YDSXGR_ADCFORMAT : YDSXGR_RECFORMAT, x, 4); ds_wr(sc, pri? YDSXGR_ADCSLOTSR : YDSXGR_RECSLOTSR, y, 4); } +/* -------------------------------------------------------------------- */ /* play channel interface */ static void * -ds1pchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +ds1pchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_pchinfo *ch; KASSERT(dir == PCMDIR_PLAY, ("ds1pchan_init: bad direction")); ch = &sc->pch[sc->pchn++]; ch->buffer = b; - ch->buffer->bufsize = 4096; ch->parent = sc; ch->channel = c; ch->dir = dir; ch->fmt = AFMT_U8; ch->spd = 8000; ch->run = 0; - if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, DS1_BUFFSIZE) == -1) return NULL; else { ch->lsnum = sc->pslotfree; ch->lslot = ds_allocpslot(sc); ch->rsnum = sc->pslotfree; ch->rslot = ds_allocpslot(sc); ds_setuppch(ch); return ch; } } static int -ds1pchan_setformat(void *data, u_int32_t format) +ds1pchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct sc_pchinfo *ch = data; ch->fmt = format; return 0; } static int -ds1pchan_setspeed(void *data, u_int32_t speed) +ds1pchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_pchinfo *ch = data; ch->spd = speed; return speed; } static int -ds1pchan_setblocksize(void *data, u_int32_t blocksize) +ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { + struct sc_pchinfo *ch = data; + int drate; + + /* irq rate is fixed at 187.5hz */ + drate = ch->spd * sndbuf_getbps(ch->buffer); + blocksize = (drate << 8) / DS1_IRQHZ; + sndbuf_resize(ch->buffer, DS1_BUFFSIZE / blocksize, blocksize); + return blocksize; } /* semantic note: must start at beginning of buffer */ static int -ds1pchan_trigger(void *data, int go) +ds1pchan_trigger(kobj_t obj, void *data, int go) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; int stereo; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; if (go == PCMTRIG_START) { ch->run = 1; ds_setuppch(ch); ds_enapslot(sc, ch->lsnum, 1); ds_enapslot(sc, ch->rsnum, stereo); ds_wr(sc, YDSXGR_MODE, 0x00000003, 4); } else { ch->run = 0; /* ds_setuppch(ch); */ ds_enapslot(sc, ch->lsnum, 0); ds_enapslot(sc, ch->rsnum, 0); } return 0; } static int -ds1pchan_getptr(void *data) +ds1pchan_getptr(kobj_t obj, void *data) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; volatile struct pbank *bank; int ss; u_int32_t ptr; ss = (ch->fmt & AFMT_STEREO)? 1 : 0; ss += (ch->fmt & AFMT_16BIT)? 1 : 0; bank = ch->lslot + sc->currbank; /* printf("getptr: %d\n", bank->PgStart << ss); */ ptr = bank->PgStart; ptr <<= ss; return ptr; } static pcmchan_caps * -ds1pchan_getcaps(void *data) +ds1pchan_getcaps(kobj_t obj, void *data) { return &ds_playcaps; } +static kobj_method_t ds1pchan_methods[] = { + KOBJMETHOD(channel_init, ds1pchan_init), + KOBJMETHOD(channel_setformat, ds1pchan_setformat), + KOBJMETHOD(channel_setspeed, ds1pchan_setspeed), + KOBJMETHOD(channel_setblocksize, ds1pchan_setblocksize), + KOBJMETHOD(channel_trigger, ds1pchan_trigger), + KOBJMETHOD(channel_getptr, ds1pchan_getptr), + KOBJMETHOD(channel_getcaps, ds1pchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(ds1pchan); + +/* -------------------------------------------------------------------- */ /* record channel interface */ static void * -ds1rchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +ds1rchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_rchinfo *ch; KASSERT(dir == PCMDIR_REC, ("ds1rchan_init: bad direction")); ch = &sc->rch[sc->rchn]; ch->num = sc->rchn++; ch->buffer = b; - ch->buffer->bufsize = 4096; ch->parent = sc; ch->channel = c; ch->dir = dir; ch->fmt = AFMT_U8; ch->spd = 8000; - if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 4096) == -1) return NULL; else { ch->slot = (ch->num == DS1_RECPRIMARY)? sc->rbank + 2: sc->rbank; ds_setuprch(ch); return ch; } } static int -ds1rchan_setformat(void *data, u_int32_t format) +ds1rchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct sc_rchinfo *ch = data; ch->fmt = format; return 0; } static int -ds1rchan_setspeed(void *data, u_int32_t speed) +ds1rchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_rchinfo *ch = data; ch->spd = speed; return speed; } static int -ds1rchan_setblocksize(void *data, u_int32_t blocksize) +ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { + struct sc_rchinfo *ch = data; + int drate; + + /* irq rate is fixed at 187.5hz */ + drate = ch->spd * sndbuf_getbps(ch->buffer); + blocksize = (drate << 8) / DS1_IRQHZ; + sndbuf_resize(ch->buffer, DS1_BUFFSIZE / blocksize, blocksize); + return blocksize; } /* semantic note: must start at beginning of buffer */ static int -ds1rchan_trigger(void *data, int go) +ds1rchan_trigger(kobj_t obj, void *data, int go) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; u_int32_t x; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; if (go == PCMTRIG_START) { ch->run = 1; ds_setuprch(ch); x = ds_rd(sc, YDSXGR_MAPOFREC, 4); x |= (ch->num == DS1_RECPRIMARY)? 0x02 : 0x01; ds_wr(sc, YDSXGR_MAPOFREC, x, 4); ds_wr(sc, YDSXGR_MODE, 0x00000003, 4); } else { ch->run = 0; x = ds_rd(sc, YDSXGR_MAPOFREC, 4); x &= ~((ch->num == DS1_RECPRIMARY)? 0x02 : 0x01); ds_wr(sc, YDSXGR_MAPOFREC, x, 4); } return 0; } static int -ds1rchan_getptr(void *data) +ds1rchan_getptr(kobj_t obj, void *data) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; return ch->slot[sc->currbank].PgStart; } static pcmchan_caps * -ds1rchan_getcaps(void *data) +ds1rchan_getcaps(kobj_t obj, void *data) { return &ds_reccaps; } +static kobj_method_t ds1rchan_methods[] = { + KOBJMETHOD(channel_init, ds1rchan_init), + KOBJMETHOD(channel_setformat, ds1rchan_setformat), + KOBJMETHOD(channel_setspeed, ds1rchan_setspeed), + KOBJMETHOD(channel_setblocksize, ds1rchan_setblocksize), + KOBJMETHOD(channel_trigger, ds1rchan_trigger), + KOBJMETHOD(channel_getptr, ds1rchan_getptr), + KOBJMETHOD(channel_getcaps, ds1rchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(ds1rchan); + +/* -------------------------------------------------------------------- */ /* The interrupt handler */ static void ds_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; u_int32_t i, x; i = ds_rd(sc, YDSXGR_STATUS, 4); if (i & 0x00008000) device_printf(sc->dev, "timeout irq\n"); if (i & 0x80008000) { ds_wr(sc, YDSXGR_STATUS, i & 0x80008000, 4); sc->currbank = ds_rd(sc, YDSXGR_CTRLSELECT, 4) & 0x00000001; x = 0; for (i = 0; i < DS1_CHANS; i++) { if (sc->pch[i].run) { x = 1; chn_intr(sc->pch[i].channel); } } for (i = 0; i < 2; i++) { if (sc->rch[i].run) { x = 1; chn_intr(sc->rch[i].channel); } } i = ds_rd(sc, YDSXGR_MODE, 4); if (x) ds_wr(sc, YDSXGR_MODE, i | 0x00000002, 4); } } /* -------------------------------------------------------------------- */ /* * Probe and attach the card */ static void ds_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct sc_info *sc = arg; sc->ctrlbase = error? 0 : (u_int32_t)segs->ds_addr; if (bootverbose) { printf("ds1: setmap (%lx, %lx), nseg=%d, error=%d\n", (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, nseg, error); } } static int ds_init(struct sc_info *sc) { int i; u_int32_t *ci, r, pcs, rcs, ecs, ws, memsz, cb; u_int8_t *t; void *buf; ci = ds_devs[sc->type].mcode; ds_wr(sc, YDSXGR_NATIVEDACOUTVOL, 0x00000000, 4); ds_enadsp(sc, 0); ds_wr(sc, YDSXGR_MODE, 0x00010000, 4); ds_wr(sc, YDSXGR_MODE, 0x00000000, 4); ds_wr(sc, YDSXGR_MAPOFREC, 0x00000000, 4); ds_wr(sc, YDSXGR_MAPOFEFFECT, 0x00000000, 4); ds_wr(sc, YDSXGR_PLAYCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_RECCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_EFFCTRLBASE, 0x00000000, 4); r = ds_rd(sc, YDSXGR_GLOBALCTRL, 2); ds_wr(sc, YDSXGR_GLOBALCTRL, r & ~0x0007, 2); for (i = 0; i < YDSXG_DSPLENGTH; i += 4) ds_wr(sc, YDSXGR_DSPINSTRAM + i, DspInst[i >> 2], 4); for (i = 0; i < YDSXG_CTRLLENGTH; i += 4) ds_wr(sc, YDSXGR_CTRLINSTRAM + i, ci[i >> 2], 4); ds_enadsp(sc, 1); pcs = 0; for (i = 100; i > 0; i--) { pcs = ds_rd(sc, YDSXGR_PLAYCTRLSIZE, 4) << 2; if (pcs == sizeof(struct pbank)) break; DELAY(1000); } if (pcs != sizeof(struct pbank)) { device_printf(sc->dev, "preposterous playctrlsize (%d)\n", pcs); return -1; } rcs = ds_rd(sc, YDSXGR_RECCTRLSIZE, 4) << 2; ecs = ds_rd(sc, YDSXGR_EFFCTRLSIZE, 4) << 2; ws = ds_rd(sc, YDSXGR_WORKSIZE, 4) << 2; memsz = 64 * 2 * pcs + 2 * 2 * rcs + 5 * 2 * ecs + ws; memsz += (64 + 1) * 4; if (sc->regbase == NULL) { if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &sc->map)) return -1; if (bus_dmamap_load(sc->parent_dmat, sc->map, buf, memsz, ds_setmap, sc, 0) || !sc->ctrlbase) { device_printf(sc->dev, "pcs=%d, rcs=%d, ecs=%d, ws=%d, memsz=%d\n", pcs, rcs, ecs, ws, memsz); return -1; } sc->regbase = buf; } else buf = sc->regbase; cb = 0; t = buf; ds_wr(sc, YDSXGR_WORKBASE, sc->ctrlbase + cb, 4); cb += ws; sc->pbase = (u_int32_t *)(t + cb); /* printf("pbase = %p -> 0x%x\n", sc->pbase, sc->ctrlbase + cb); */ ds_wr(sc, YDSXGR_PLAYCTRLBASE, sc->ctrlbase + cb, 4); cb += (64 + 1) * 4; sc->rbank = (struct rbank *)(t + cb); ds_wr(sc, YDSXGR_RECCTRLBASE, sc->ctrlbase + cb, 4); cb += 2 * 2 * rcs; ds_wr(sc, YDSXGR_EFFCTRLBASE, sc->ctrlbase + cb, 4); cb += 5 * 2 * ecs; sc->pbankbase = sc->ctrlbase + cb; sc->pbanksize = pcs; for (i = 0; i < 64; i++) { wrl(sc, &sc->pbase[i + 1], 0); sc->pbank[i * 2] = (struct pbank *)(t + cb); /* printf("pbank[%d] = %p -> 0x%x; ", i * 2, (struct pbank *)(t + cb), sc->ctrlbase + cb - vtophys(t + cb)); */ cb += pcs; sc->pbank[i * 2 + 1] = (struct pbank *)(t + cb); /* printf("pbank[%d] = %p -> 0x%x\n", i * 2 + 1, (struct pbank *)(t + cb), sc->ctrlbase + cb - vtophys(t + cb)); */ cb += pcs; } wrl(sc, &sc->pbase[0], DS1_CHANS * 2); sc->pchn = sc->rchn = 0; ds_wr(sc, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff, 4); ds_wr(sc, YDSXGR_NATIVEADCINVOL, 0x3fff3fff, 4); ds_wr(sc, YDSXGR_NATIVEDACINVOL, 0x3fff3fff, 4); + return 0; } static int ds_uninit(struct sc_info *sc) { ds_wr(sc, YDSXGR_NATIVEDACOUTVOL, 0x00000000, 4); ds_wr(sc, YDSXGR_NATIVEADCINVOL, 0, 4); ds_wr(sc, YDSXGR_NATIVEDACINVOL, 0, 4); ds_enadsp(sc, 0); ds_wr(sc, YDSXGR_MODE, 0x00010000, 4); ds_wr(sc, YDSXGR_MAPOFREC, 0x00000000, 4); ds_wr(sc, YDSXGR_MAPOFEFFECT, 0x00000000, 4); ds_wr(sc, YDSXGR_PLAYCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_RECCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_EFFCTRLBASE, 0x00000000, 4); ds_wr(sc, YDSXGR_GLOBALCTRL, 0, 2); bus_dmamap_unload(sc->parent_dmat, sc->map); bus_dmamem_free(sc->parent_dmat, sc->regbase, sc->map); return 0; } static int ds_finddev(u_int32_t dev, u_int32_t subdev) { int i; for (i = 0; ds_devs[i].dev; i++) { if (ds_devs[i].dev == dev && (ds_devs[i].subdev == subdev || ds_devs[i].subdev == 0)) return i; } return -1; } static int ds_pci_probe(device_t dev) { int i; u_int32_t subdev; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); i = ds_finddev(pci_get_devid(dev), subdev); if (i >= 0) { device_set_desc(dev, ds_devs[i].name); return 0; } else return ENXIO; } static int ds_pci_attach(device_t dev) { u_int32_t data; u_int32_t subdev, i; struct sc_info *sc; struct ac97_info *codec = NULL; char status[SND_STATUSLEN]; if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(sc, sizeof(*sc)); sc->dev = dev; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); sc->type = ds_finddev(pci_get_devid(dev), subdev); sc->rev = pci_get_revid(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); sc->regid = PCIR_MAPS; sc->reg = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->regid, 0, ~0, 1, RF_ACTIVE); if (!sc->reg) { device_printf(dev, "unable to map register space\n"); goto bad; } sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/65536, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } sc->regbase = NULL; if (ds_init(sc) == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - codec = ac97_create(dev, sc, ds_initcd, ds_rdcd, ds_wrcd); + codec = AC97_CREATE(dev, sc, ds_ac97); if (codec == NULL) goto bad; - mixer_init(dev, &ac97_mixer, codec); + mixer_init(dev, ac97_getmixerclass(), codec); sc->irqid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!sc->irq || bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ds_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld", rman_get_start(sc->reg), rman_get_start(sc->irq)); if (pcm_register(dev, sc, DS1_CHANS, 2)) goto bad; for (i = 0; i < DS1_CHANS; i++) - pcm_addchan(dev, PCMDIR_PLAY, &ds_pchantemplate, sc); + pcm_addchan(dev, PCMDIR_PLAY, &ds1pchan_class, sc); for (i = 0; i < 2; i++) - pcm_addchan(dev, PCMDIR_REC, &ds_rchantemplate, sc); + pcm_addchan(dev, PCMDIR_REC, &ds1rchan_class, sc); pcm_setstatus(dev, status); return 0; bad: if (codec) ac97_destroy(codec); if (sc->reg) bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat); free(sc, M_DEVBUF); return ENXIO; } static int ds_pci_resume(device_t dev) { struct sc_info *sc; sc = pcm_getdevinfo(dev); if (ds_init(sc) == -1) { device_printf(dev, "unable to reinitialize the card\n"); return ENXIO; } if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); return ENXIO; } return 0; } static int ds_pci_detach(device_t dev) { int r; struct sc_info *sc; r = pcm_unregister(dev); if (r) return r; sc = pcm_getdevinfo(dev); ds_uninit(sc); bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); bus_dma_tag_destroy(sc->parent_dmat); free(sc, M_DEVBUF); return 0; } static device_method_t ds1_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ds_pci_probe), DEVMETHOD(device_attach, ds_pci_attach), DEVMETHOD(device_detach, ds_pci_detach), DEVMETHOD(device_resume, ds_pci_resume), { 0, 0 } }; static driver_t ds1_driver = { "pcm", ds1_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_ds1, pci, ds1_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_ds1, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_ds1, 1); Index: stable/4/sys/dev/sound/pci/emu10k1.c =================================================================== --- stable/4/sys/dev/sound/pci/emu10k1.c (revision 71948) +++ stable/4/sys/dev/sound/pci/emu10k1.c (revision 71949) @@ -1,1599 +1,1606 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include /* -------------------------------------------------------------------- */ #define EMU10K1_PCI_ID 0x00021102 #define EMU_BUFFSIZE 4096 #define EMU_CHANS 4 #undef EMUDEBUG struct emu_memblk { SLIST_ENTRY(emu_memblk) link; void *buf; u_int32_t pte_start, pte_size; }; struct emu_mem { u_int8_t bmap[MAXPAGES / 8]; u_int32_t *ptb_pages; void *silent_page; SLIST_HEAD(, emu_memblk) blocks; }; struct emu_voice { int vnum; int b16:1, stereo:1, busy:1, running:1, ismaster:1; int speed; int start, end, vol; u_int32_t buf; struct emu_voice *slave; pcm_channel *channel; }; struct sc_info; /* channel registers */ struct sc_pchinfo { - int spd, fmt, run; + int spd, fmt, blksz, run; struct emu_voice *master, *slave; snd_dbuf *buffer; pcm_channel *channel; struct sc_info *parent; }; struct sc_rchinfo { - int spd, fmt, run, num; + int spd, fmt, run, blksz, num; u_int32_t idxreg, basereg, sizereg, setupreg, irqmask; snd_dbuf *buffer; pcm_channel *channel; struct sc_info *parent; }; /* device private data */ struct sc_info { device_t dev; u_int32_t type, rev; u_int32_t tos_link:1, APS:1; bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; struct resource *reg, *irq; int regtype, regid, irqid; void *ih; - int timer; + int timer, timerinterval; int pnum, rnum; struct emu_mem mem; struct emu_voice voice[64]; struct sc_pchinfo pch[EMU_CHANS]; struct sc_rchinfo rch[3]; }; /* -------------------------------------------------------------------- */ /* * prototypes */ -/* channel interface */ -static void *emupchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int emupchan_free(void *data); -static int emupchan_setdir(void *data, int dir); -static int emupchan_setformat(void *data, u_int32_t format); -static int emupchan_setspeed(void *data, u_int32_t speed); -static int emupchan_setblocksize(void *data, u_int32_t blocksize); -static int emupchan_trigger(void *data, int go); -static int emupchan_getptr(void *data); -static pcmchan_caps *emupchan_getcaps(void *data); - -/* channel interface */ -static void *emurchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int emurchan_setdir(void *data, int dir); -static int emurchan_setformat(void *data, u_int32_t format); -static int emurchan_setspeed(void *data, u_int32_t speed); -static int emurchan_setblocksize(void *data, u_int32_t blocksize); -static int emurchan_trigger(void *data, int go); -static int emurchan_getptr(void *data); -static pcmchan_caps *emurchan_getcaps(void *data); - -/* talk to the codec - called from ac97.c */ -static u_int32_t emu_rdcd(void *, int); -static void emu_wrcd(void *, int, u_int32_t); - /* stuff */ static int emu_init(struct sc_info *); static void emu_intr(void *); static void *emu_malloc(struct sc_info *sc, u_int32_t sz); static void *emu_memalloc(struct sc_info *sc, u_int32_t sz); static int emu_memfree(struct sc_info *sc, void *buf); static int emu_memstart(struct sc_info *sc, void *buf); #ifdef EMUDEBUG static void emu_vdump(struct sc_info *sc, struct emu_voice *v); #endif /* talk to the card */ static u_int32_t emu_rd(struct sc_info *, int, int); static void emu_wr(struct sc_info *, int, u_int32_t, int); /* -------------------------------------------------------------------- */ static u_int32_t emu_rfmt_ac97[] = { AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static u_int32_t emu_rfmt_mic[] = { AFMT_U8, 0 }; static u_int32_t emu_rfmt_efx[] = { AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps emu_reccaps[3] = { {8000, 48000, emu_rfmt_ac97, 0}, {8000, 8000, emu_rfmt_mic, 0}, {48000, 48000, emu_rfmt_efx, 0}, }; static u_int32_t emu_pfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; -static pcmchan_caps emu_playcaps = {8000, 48000, emu_pfmt, 0}; +static pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0}; -static pcm_channel emu_chantemplate = { - emupchan_init, - emupchan_setdir, - emupchan_setformat, - emupchan_setspeed, - emupchan_setblocksize, - emupchan_trigger, - emupchan_getptr, - emupchan_getcaps, - emupchan_free, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - -static pcm_channel emur_chantemplate = { - emurchan_init, - emurchan_setdir, - emurchan_setformat, - emurchan_setspeed, - emurchan_setblocksize, - emurchan_trigger, - emurchan_getptr, - emurchan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; /* -------------------------------------------------------------------- */ /* Hardware */ static u_int32_t emu_rd(struct sc_info *sc, int regno, int size) { switch (size) { case 1: return bus_space_read_1(sc->st, sc->sh, regno); case 2: return bus_space_read_2(sc->st, sc->sh, regno); case 4: return bus_space_read_4(sc->st, sc->sh, regno); default: return 0xffffffff; } } static void emu_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { case 1: bus_space_write_1(sc->st, sc->sh, regno, data); break; case 2: bus_space_write_2(sc->st, sc->sh, regno, data); break; case 4: bus_space_write_4(sc->st, sc->sh, regno, data); break; } } static u_int32_t emu_rdptr(struct sc_info *sc, int chn, int reg) { u_int32_t ptr, val, mask, size, offset; ptr = ((reg << 16) & PTR_ADDRESS_MASK) | (chn & PTR_CHANNELNUM_MASK); emu_wr(sc, PTR, ptr, 4); val = emu_rd(sc, DATA, 4); if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; offset = (reg >> 16) & 0x1f; mask = ((1 << size) - 1) << offset; val &= mask; val >>= offset; } return val; } static void emu_wrptr(struct sc_info *sc, int chn, int reg, u_int32_t data) { u_int32_t ptr, mask, size, offset; ptr = ((reg << 16) & PTR_ADDRESS_MASK) | (chn & PTR_CHANNELNUM_MASK); emu_wr(sc, PTR, ptr, 4); if (reg & 0xff000000) { size = (reg >> 24) & 0x3f; offset = (reg >> 16) & 0x1f; mask = ((1 << size) - 1) << offset; data <<= offset; data &= mask; data |= emu_rd(sc, DATA, 4) & ~mask; } emu_wr(sc, DATA, data, 4); } static void emu_wrefx(struct sc_info *sc, unsigned int pc, unsigned int data) { emu_wrptr(sc, 0, MICROCODEBASE + pc, data); } +/* -------------------------------------------------------------------- */ /* ac97 codec */ -static u_int32_t -emu_rdcd(void *devinfo, int regno) + +static int +emu_rdcd(kobj_t obj, void *devinfo, int regno) { struct sc_info *sc = (struct sc_info *)devinfo; emu_wr(sc, AC97ADDRESS, regno, 1); return emu_rd(sc, AC97DATA, 2); } -static void -emu_wrcd(void *devinfo, int regno, u_int32_t data) +static int +emu_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; emu_wr(sc, AC97ADDRESS, regno, 1); emu_wr(sc, AC97DATA, data, 2); + return 0; } +static kobj_method_t emu_ac97_methods[] = { + KOBJMETHOD(ac97_read, emu_rdcd), + KOBJMETHOD(ac97_write, emu_wrcd), + { 0, 0 } +}; +AC97_DECLARE(emu_ac97); + +/* -------------------------------------------------------------------- */ + #if 0 /* playback channel interrupts */ static u_int32_t emu_testint(struct sc_info *sc, char channel) { int reg = (channel & 0x20)? CLIPH : CLIPL; channel &= 0x1f; reg |= 1 << 24; reg |= channel << 16; return emu_rdptr(sc, 0, reg); } static void emu_clrint(struct sc_info *sc, char channel) { int reg = (channel & 0x20)? CLIPH : CLIPL; channel &= 0x1f; reg |= 1 << 24; reg |= channel << 16; emu_wrptr(sc, 0, reg, 1); } static void emu_enaint(struct sc_info *sc, char channel, int enable) { int reg = (channel & 0x20)? CLIEH : CLIEL; channel &= 0x1f; reg |= 1 << 24; reg |= channel << 16; emu_wrptr(sc, 0, reg, enable); } #endif /* stuff */ static int +emu_settimer(struct sc_info *sc) +{ + struct sc_pchinfo *pch; + struct sc_rchinfo *rch; + int i, tmp, rate; + + rate = 0; + for (i = 0; i < EMU_CHANS; i++) { + pch = &sc->pch[i]; + tmp = (pch->spd * sndbuf_getbps(pch->buffer)) / pch->blksz; + if (tmp > rate) + rate = tmp; + } + + for (i = 0; i < 3; i++) { + rch = &sc->rch[i]; + tmp = (rch->spd * sndbuf_getbps(rch->buffer)) / rch->blksz; + if (tmp > rate) + rate = tmp; + } + RANGE(rate, 48, 9600); + sc->timerinterval = 48000 / rate; + emu_wr(sc, TIMER, sc->timerinterval & 0x03ff, 2); + + return sc->timerinterval; +} + +static int emu_enatimer(struct sc_info *sc, int go) { u_int32_t x; if (go) { if (sc->timer++ == 0) { - emu_wr(sc, TIMER, 256, 2); x = emu_rd(sc, INTE, 4); x |= INTE_INTERVALTIMERENB; emu_wr(sc, INTE, x, 4); } } else { sc->timer = 0; x = emu_rd(sc, INTE, 4); x &= ~INTE_INTERVALTIMERENB; emu_wr(sc, INTE, x, 4); } return 0; } static void emu_enastop(struct sc_info *sc, char channel, int enable) { int reg = (channel & 0x20)? SOLEH : SOLEL; channel &= 0x1f; reg |= 1 << 24; reg |= channel << 16; emu_wrptr(sc, 0, reg, enable); } static int emu_recval(int speed) { int val; val = 0; while (val < 7 && speed < adcspeed[val]) val++; return val; } static u_int32_t emu_rate_to_pitch(u_int32_t rate) { static u_int32_t logMagTable[128] = { 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2, 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5, 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081, 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191, 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7, 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829, 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e, 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26, 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d, 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885, 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899, 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c, 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3, 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3, 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83, 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df }; static char logSlopeTable[128] = { 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58, 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53, 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f, 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b, 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47, 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41, 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e, 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c, 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39, 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37, 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35, 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34, 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32, 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f }; int i; if (rate == 0) return 0; /* Bail out if no leading "1" */ rate *= 11185; /* Scale 48000 to 0x20002380 */ for (i = 31; i > 0; i--) { if (rate & 0x80000000) { /* Detect leading "1" */ return (((u_int32_t) (i - 15) << 20) + logMagTable[0x7f & (rate >> 24)] + (0x7f & (rate >> 17)) * logSlopeTable[0x7f & (rate >> 24)]); } rate <<= 1; } return 0; /* Should never reach this point */ } static u_int32_t emu_rate_to_linearpitch(u_int32_t rate) { rate = (rate << 8) / 375; return (rate >> 1) + (rate & 1); } static struct emu_voice * emu_valloc(struct sc_info *sc) { struct emu_voice *v; int i; v = NULL; for (i = 0; i < 64 && sc->voice[i].busy; i++); if (i < 64) { v = &sc->voice[i]; v->busy = 1; } return v; } static int emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, u_int32_t sz, pcm_channel *c) { void *buf; buf = emu_memalloc(sc, sz); if (buf == NULL) return -1; - if (c != NULL) { - c->buffer.buf = buf; - c->buffer.bufsize = sz; - } + if (c != NULL) + sndbuf_setup(&c->buffer, buf, sz); m->start = emu_memstart(sc, buf) * EMUPAGESIZE; m->end = m->start + sz; m->channel = NULL; m->speed = 0; m->b16 = 0; m->stereo = 0; m->running = 0; m->ismaster = 1; m->vol = 0xff; m->buf = vtophys(buf); m->slave = s; if (s != NULL) { s->start = m->start; s->end = m->end; s->channel = NULL; s->speed = 0; s->b16 = 0; s->stereo = 0; s->running = 0; s->ismaster = 0; s->vol = m->vol; s->buf = m->buf; s->slave = NULL; } return 0; } static void emu_vsetup(struct sc_pchinfo *ch) { struct emu_voice *v = ch->master; if (ch->fmt) { v->b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; v->stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; if (v->slave != NULL) { v->slave->b16 = v->b16; v->slave->stereo = v->stereo; } } if (ch->spd) { v->speed = ch->spd; if (v->slave != NULL) v->slave->speed = v->speed; } } static void emu_vwrite(struct sc_info *sc, struct emu_voice *v) { int s; int l, r, x, y; u_int32_t sa, ea, start, val, silent_page; s = (v->stereo? 1 : 0) + (v->b16? 1 : 0); sa = v->start >> s; ea = v->end >> s; l = r = x = y = v->vol; if (v->stereo) { l = v->ismaster? l : 0; r = v->ismaster? 0 : r; } emu_wrptr(sc, v->vnum, CPF, v->stereo? CPF_STEREO_MASK : 0); val = v->stereo? 28 : 30; val *= v->b16? 1 : 2; start = sa + val; emu_wrptr(sc, v->vnum, FXRT, 0xd01c0000); emu_wrptr(sc, v->vnum, PTRX, (x << 8) | r); emu_wrptr(sc, v->vnum, DSL, ea | (y << 24)); emu_wrptr(sc, v->vnum, PSST, sa | (l << 24)); emu_wrptr(sc, v->vnum, CCCA, start | (v->b16? 0 : CCCA_8BITSELECT)); emu_wrptr(sc, v->vnum, Z1, 0); emu_wrptr(sc, v->vnum, Z2, 0); silent_page = ((u_int32_t)vtophys(sc->mem.silent_page) << 1) | MAP_PTI_MASK; emu_wrptr(sc, v->vnum, MAPA, silent_page); emu_wrptr(sc, v->vnum, MAPB, silent_page); emu_wrptr(sc, v->vnum, CVCF, CVCF_CURRENTFILTER_MASK); emu_wrptr(sc, v->vnum, VTFT, VTFT_FILTERTARGET_MASK); emu_wrptr(sc, v->vnum, ATKHLDM, 0); emu_wrptr(sc, v->vnum, DCYSUSM, DCYSUSM_DECAYTIME_MASK); emu_wrptr(sc, v->vnum, LFOVAL1, 0x8000); emu_wrptr(sc, v->vnum, LFOVAL2, 0x8000); emu_wrptr(sc, v->vnum, FMMOD, 0); emu_wrptr(sc, v->vnum, TREMFRQ, 0); emu_wrptr(sc, v->vnum, FM2FRQ2, 0); emu_wrptr(sc, v->vnum, ENVVAL, 0x8000); emu_wrptr(sc, v->vnum, ATKHLDV, ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); emu_wrptr(sc, v->vnum, ENVVOL, 0x8000); emu_wrptr(sc, v->vnum, PEFE_FILTERAMOUNT, 0x7f); emu_wrptr(sc, v->vnum, PEFE_PITCHAMOUNT, 0); if (v->slave != NULL) emu_vwrite(sc, v->slave); } static void emu_vtrigger(struct sc_info *sc, struct emu_voice *v, int go) { u_int32_t pitch_target, initial_pitch; u_int32_t cra, cs, ccis; u_int32_t sample, i; if (go) { cra = 64; cs = v->stereo? 4 : 2; ccis = v->stereo? 28 : 30; ccis *= v->b16? 1 : 2; sample = v->b16? 0x00000000 : 0x80808080; for (i = 0; i < cs; i++) emu_wrptr(sc, v->vnum, CD0 + i, sample); emu_wrptr(sc, v->vnum, CCR_CACHEINVALIDSIZE, 0); emu_wrptr(sc, v->vnum, CCR_READADDRESS, cra); emu_wrptr(sc, v->vnum, CCR_CACHEINVALIDSIZE, ccis); emu_wrptr(sc, v->vnum, IFATN, 0xff00); emu_wrptr(sc, v->vnum, VTFT, 0xffffffff); emu_wrptr(sc, v->vnum, CVCF, 0xffffffff); emu_wrptr(sc, v->vnum, DCYSUSV, 0x00007f7f); emu_enastop(sc, v->vnum, 0); pitch_target = emu_rate_to_linearpitch(v->speed); initial_pitch = emu_rate_to_pitch(v->speed) >> 8; emu_wrptr(sc, v->vnum, PTRX_PITCHTARGET, pitch_target); emu_wrptr(sc, v->vnum, CPF_CURRENTPITCH, pitch_target); emu_wrptr(sc, v->vnum, IP, initial_pitch); } else { emu_wrptr(sc, v->vnum, PTRX_PITCHTARGET, 0); emu_wrptr(sc, v->vnum, CPF_CURRENTPITCH, 0); emu_wrptr(sc, v->vnum, IFATN, 0xffff); emu_wrptr(sc, v->vnum, VTFT, 0x0000ffff); emu_wrptr(sc, v->vnum, CVCF, 0x0000ffff); emu_wrptr(sc, v->vnum, IP, 0); emu_enastop(sc, v->vnum, 1); } if (v->slave != NULL) emu_vtrigger(sc, v->slave, go); } static int emu_vpos(struct sc_info *sc, struct emu_voice *v) { int s, ptr; s = (v->b16? 1 : 0) + (v->stereo? 1 : 0); ptr = (emu_rdptr(sc, v->vnum, CCCA_CURRADDR) - (v->start >> s)) << s; return ptr & ~0x0000001f; } #ifdef EMUDEBUG static void emu_vdump(struct sc_info *sc, struct emu_voice *v) { char *regname[] = { "cpf", "ptrx", "cvcf", "vtft", "z2", "z1", "psst", "dsl", "ccca", "ccr", "clp", "fxrt", "mapa", "mapb", NULL, NULL, "envvol", "atkhldv", "dcysusv", "lfoval1", "envval", "atkhldm", "dcysusm", "lfoval2", "ip", "ifatn", "pefe", "fmmod", "tremfrq", "fmfrq2", "tempenv" }; int i, x; printf("voice number %d\n", v->vnum); for (i = 0, x = 0; i <= 0x1e; i++) { if (regname[i] == NULL) continue; printf("%s\t[%08x]", regname[i], emu_rdptr(sc, v->vnum, i)); printf("%s", (x == 2)? "\n" : "\t"); x++; if (x > 2) x = 0; } printf("\n\n"); } #endif /* channel interface */ -void * -emupchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +static void * +emupchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_pchinfo *ch; KASSERT(dir == PCMDIR_PLAY, ("emupchan_init: bad direction")); ch = &sc->pch[sc->pnum++]; ch->buffer = b; ch->parent = sc; ch->channel = c; + ch->blksz = EMU_BUFFSIZE / 2; + ch->fmt = AFMT_U8; + ch->spd = 8000; ch->master = emu_valloc(sc); ch->slave = emu_valloc(sc); if (emu_vinit(sc, ch->master, ch->slave, EMU_BUFFSIZE, ch->channel)) return NULL; else return ch; } static int -emupchan_free(void *data) +emupchan_free(kobj_t obj, void *data) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; - return emu_memfree(sc, ch->buffer->buf); + return emu_memfree(sc, sndbuf_getbuf(ch->buffer)); } static int -emupchan_setdir(void *data, int dir) +emupchan_setformat(kobj_t obj, void *data, u_int32_t format) { - return 0; -} - -static int -emupchan_setformat(void *data, u_int32_t format) -{ struct sc_pchinfo *ch = data; ch->fmt = format; return 0; } static int -emupchan_setspeed(void *data, u_int32_t speed) +emupchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_pchinfo *ch = data; ch->spd = speed; return ch->spd; } static int -emupchan_setblocksize(void *data, u_int32_t blocksize) +emupchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { + struct sc_pchinfo *ch = data; + struct sc_info *sc = ch->parent; + int irqrate, blksz; + + ch->blksz = blocksize; + emu_settimer(sc); + irqrate = 48000 / sc->timerinterval; + blksz = (ch->spd * sndbuf_getbps(ch->buffer)) / irqrate; return blocksize; } static int -emupchan_trigger(void *data, int go) +emupchan_trigger(kobj_t obj, void *data, int go) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; if (go == PCMTRIG_START) { emu_vsetup(ch); emu_vwrite(sc, ch->master); + emu_settimer(sc); emu_enatimer(sc, 1); #ifdef EMUDEBUG printf("start [%d bit, %s, %d hz]\n", ch->master->b16? 16 : 8, ch->master->stereo? "stereo" : "mono", ch->master->speed); emu_vdump(sc, ch->master); emu_vdump(sc, ch->slave); #endif } ch->run = (go == PCMTRIG_START)? 1 : 0; emu_vtrigger(sc, ch->master, ch->run); return 0; } static int -emupchan_getptr(void *data) +emupchan_getptr(kobj_t obj, void *data) { struct sc_pchinfo *ch = data; struct sc_info *sc = ch->parent; return emu_vpos(sc, ch->master); } static pcmchan_caps * -emupchan_getcaps(void *data) +emupchan_getcaps(kobj_t obj, void *data) { return &emu_playcaps; } +static kobj_method_t emupchan_methods[] = { + KOBJMETHOD(channel_init, emupchan_init), + KOBJMETHOD(channel_free, emupchan_free), + KOBJMETHOD(channel_setformat, emupchan_setformat), + KOBJMETHOD(channel_setspeed, emupchan_setspeed), + KOBJMETHOD(channel_setblocksize, emupchan_setblocksize), + KOBJMETHOD(channel_trigger, emupchan_trigger), + KOBJMETHOD(channel_getptr, emupchan_getptr), + KOBJMETHOD(channel_getcaps, emupchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(emupchan); + /* channel interface */ static void * -emurchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +emurchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_rchinfo *ch; KASSERT(dir == PCMDIR_REC, ("emurchan_init: bad direction")); ch = &sc->rch[sc->rnum]; ch->buffer = b; - ch->buffer->bufsize = EMU_BUFFSIZE; ch->parent = sc; ch->channel = c; + ch->blksz = EMU_BUFFSIZE / 2; ch->fmt = AFMT_U8; ch->spd = 8000; ch->num = sc->rnum; switch(sc->rnum) { case 0: ch->idxreg = ADCIDX; ch->basereg = ADCBA; ch->sizereg = ADCBS; ch->setupreg = ADCCR; ch->irqmask = INTE_ADCBUFENABLE; break; case 1: ch->idxreg = MICIDX; ch->basereg = MICBA; ch->sizereg = MICBS; ch->setupreg = 0; ch->irqmask = INTE_MICBUFENABLE; break; case 2: ch->idxreg = FXIDX; ch->basereg = FXBA; ch->sizereg = FXBS; ch->setupreg = FXWC; ch->irqmask = INTE_EFXBUFENABLE; break; } sc->rnum++; - if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, EMU_BUFFSIZE) == -1) return NULL; else { - emu_wrptr(sc, 0, ch->basereg, vtophys(ch->buffer->buf)); + emu_wrptr(sc, 0, ch->basereg, vtophys(sndbuf_getbuf(ch->buffer))); emu_wrptr(sc, 0, ch->sizereg, 0); /* off */ return ch; } } static int -emurchan_setdir(void *data, int dir) +emurchan_setformat(kobj_t obj, void *data, u_int32_t format) { - return 0; -} - -static int -emurchan_setformat(void *data, u_int32_t format) -{ struct sc_rchinfo *ch = data; ch->fmt = format; return 0; } static int -emurchan_setspeed(void *data, u_int32_t speed) +emurchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_rchinfo *ch = data; if (ch->num == 0) speed = adcspeed[emu_recval(speed)]; if (ch->num == 1) speed = 8000; if (ch->num == 2) speed = 48000; ch->spd = speed; return ch->spd; } static int -emurchan_setblocksize(void *data, u_int32_t blocksize) +emurchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { + struct sc_rchinfo *ch = data; + struct sc_info *sc = ch->parent; + int irqrate, blksz; + + ch->blksz = blocksize; + emu_settimer(sc); + irqrate = 48000 / sc->timerinterval; + blksz = (ch->spd * sndbuf_getbps(ch->buffer)) / irqrate; return blocksize; } /* semantic note: must start at beginning of buffer */ static int -emurchan_trigger(void *data, int go) +emurchan_trigger(kobj_t obj, void *data, int go) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; u_int32_t val; switch(go) { case PCMTRIG_START: ch->run = 1; emu_wrptr(sc, 0, ch->sizereg, ADCBS_BUFSIZE_4096); if (ch->num == 0) { val = ADCCR_LCHANENABLE; if (ch->fmt & AFMT_STEREO) val |= ADCCR_RCHANENABLE; val |= emu_recval(ch->spd); emu_wrptr(sc, 0, ch->setupreg, val); } val = emu_rd(sc, INTE, 4); val |= ch->irqmask; emu_wr(sc, INTE, val, 4); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: ch->run = 0; emu_wrptr(sc, 0, ch->sizereg, 0); if (ch->setupreg) emu_wrptr(sc, 0, ch->setupreg, 0); val = emu_rd(sc, INTE, 4); val &= ~ch->irqmask; emu_wr(sc, INTE, val, 4); break; case PCMTRIG_EMLDMAWR: case PCMTRIG_EMLDMARD: default: break; } return 0; } static int -emurchan_getptr(void *data) +emurchan_getptr(kobj_t obj, void *data) { struct sc_rchinfo *ch = data; struct sc_info *sc = ch->parent; return emu_rdptr(sc, 0, ch->idxreg) & 0x0000ffff; } static pcmchan_caps * -emurchan_getcaps(void *data) +emurchan_getcaps(kobj_t obj, void *data) { struct sc_rchinfo *ch = data; return &emu_reccaps[ch->num]; } /* The interrupt handler */ static void emu_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; u_int32_t stat, ack, i, x; while (1) { stat = emu_rd(sc, IPR, 4); if (stat == 0) break; ack = 0; /* process irq */ if (stat & IPR_INTERVALTIMER) { ack |= IPR_INTERVALTIMER; x = 0; for (i = 0; i < EMU_CHANS; i++) { if (sc->pch[i].run) { x = 1; chn_intr(sc->pch[i].channel); } } if (x == 0) emu_enatimer(sc, 0); } if (stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL)) { ack |= stat & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL); if (sc->rch[0].channel) chn_intr(sc->rch[0].channel); } if (stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL)) { ack |= stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL); if (sc->rch[1].channel) chn_intr(sc->rch[1].channel); } if (stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)) { ack |= stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL); if (sc->rch[2].channel) chn_intr(sc->rch[2].channel); } if (stat & IPR_PCIERROR) { ack |= IPR_PCIERROR; device_printf(sc->dev, "pci error\n"); /* we still get an nmi with ecc ram even if we ack this */ } if (stat & IPR_SAMPLERATETRACKER) { ack |= IPR_SAMPLERATETRACKER; device_printf(sc->dev, "sample rate tracker lock status change\n"); } if (stat & ~ack) device_printf(sc->dev, "dodgy irq: %x (harmless)\n", stat & ~ack); emu_wr(sc, IPR, stat, 4); } } +static kobj_method_t emurchan_methods[] = { + KOBJMETHOD(channel_init, emurchan_init), + KOBJMETHOD(channel_setformat, emurchan_setformat), + KOBJMETHOD(channel_setspeed, emurchan_setspeed), + KOBJMETHOD(channel_setblocksize, emurchan_setblocksize), + KOBJMETHOD(channel_trigger, emurchan_trigger), + KOBJMETHOD(channel_getptr, emurchan_getptr), + KOBJMETHOD(channel_getcaps, emurchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(emurchan); + /* -------------------------------------------------------------------- */ static void emu_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { void **phys = arg; *phys = error? 0 : (void *)segs->ds_addr; if (bootverbose) { printf("emu: setmap (%lx, %lx), nseg=%d, error=%d\n", (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, nseg, error); } } static void * emu_malloc(struct sc_info *sc, u_int32_t sz) { void *buf, *phys = 0; bus_dmamap_t map; if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) return NULL; if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, emu_setmap, &phys, 0) || !phys) return NULL; return buf; } static void emu_free(struct sc_info *sc, void *buf) { bus_dmamem_free(sc->parent_dmat, buf, NULL); } static void * emu_memalloc(struct sc_info *sc, u_int32_t sz) { u_int32_t blksz, start, idx, ofs, tmp, found; struct emu_mem *mem = &sc->mem; struct emu_memblk *blk; void *buf; blksz = sz / EMUPAGESIZE; if (sz > (blksz * EMUPAGESIZE)) blksz++; /* find a free block in the bitmap */ found = 0; start = 1; while (!found && start + blksz < MAXPAGES) { found = 1; for (idx = start; idx < start + blksz; idx++) if (mem->bmap[idx >> 3] & (1 << (idx & 7))) found = 0; if (!found) start++; } if (!found) return NULL; blk = malloc(sizeof(*blk), M_DEVBUF, M_NOWAIT); if (blk == NULL) return NULL; buf = emu_malloc(sc, sz); if (buf == NULL) { free(blk, M_DEVBUF); return NULL; } blk->buf = buf; blk->pte_start = start; blk->pte_size = blksz; /* printf("buf %p, pte_start %d, pte_size %d\n", blk->buf, blk->pte_start, blk->pte_size); */ ofs = 0; for (idx = start; idx < start + blksz; idx++) { mem->bmap[idx >> 3] |= 1 << (idx & 7); tmp = (u_int32_t)vtophys((u_int8_t *)buf + ofs); /* printf("pte[%d] -> %x phys, %x virt\n", idx, tmp, ((u_int32_t)buf) + ofs); */ mem->ptb_pages[idx] = (tmp << 1) | idx; ofs += EMUPAGESIZE; } SLIST_INSERT_HEAD(&mem->blocks, blk, link); return buf; } static int emu_memfree(struct sc_info *sc, void *buf) { u_int32_t idx, tmp; struct emu_mem *mem = &sc->mem; struct emu_memblk *blk, *i; blk = NULL; SLIST_FOREACH(i, &mem->blocks, link) { if (i->buf == buf) blk = i; } if (blk == NULL) return EINVAL; SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link); emu_free(sc, buf); tmp = (u_int32_t)vtophys(sc->mem.silent_page) << 1; for (idx = blk->pte_start; idx < blk->pte_start + blk->pte_size; idx++) { mem->bmap[idx >> 3] &= ~(1 << (idx & 7)); mem->ptb_pages[idx] = tmp | idx; } free(blk, M_DEVBUF); return 0; } static int emu_memstart(struct sc_info *sc, void *buf) { struct emu_mem *mem = &sc->mem; struct emu_memblk *blk, *i; blk = NULL; SLIST_FOREACH(i, &mem->blocks, link) { if (i->buf == buf) blk = i; } if (blk == NULL) return -EINVAL; return blk->pte_start; } static void emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, u_int32_t *pc) { emu_wrefx(sc, (*pc) * 2, (x << 10) | y); emu_wrefx(sc, (*pc) * 2 + 1, (op << 20) | (z << 10) | w); (*pc)++; } static void emu_initefx(struct sc_info *sc) { int i; u_int32_t pc = 16; for (i = 0; i < 512; i++) { emu_wrefx(sc, i * 2, 0x10040); emu_wrefx(sc, i * 2 + 1, 0x610040); } for (i = 0; i < 256; i++) emu_wrptr(sc, 0, FXGPREGBASE + i, 0); /* FX-8010 DSP Registers: FX Bus 0x000-0x00f : 16 registers Input 0x010/0x011 : AC97 Codec (l/r) 0x012/0x013 : ADC, S/PDIF (l/r) 0x014/0x015 : Mic(left), Zoom (l/r) 0x016/0x017 : APS S/PDIF?? (l/r) Output 0x020/0x021 : AC97 Output (l/r) 0x022/0x023 : TOS link out (l/r) 0x024/0x025 : ??? (l/r) 0x026/0x027 : LiveDrive Headphone (l/r) 0x028/0x029 : Rear Channel (l/r) 0x02a/0x02b : ADC Recording Buffer (l/r) Constants 0x040 - 0x044 = 0 - 4 0x045 = 0x8, 0x046 = 0x10, 0x047 = 0x20 0x048 = 0x100, 0x049 = 0x10000, 0x04a = 0x80000 0x04b = 0x10000000, 0x04c = 0x20000000, 0x04d = 0x40000000 0x04e = 0x80000000, 0x04f = 0x7fffffff Temporary Values 0x056 : Accumulator 0x058 : Noise source? 0x059 : Noise source? General Purpose Registers 0x100 - 0x1ff Tank Memory Data Registers 0x200 - 0x2ff Tank Memory Address Registers 0x300 - 0x3ff */ /* Operators: 0 : z := w + (x * y >> 31) 4 : z := w + x * y 6 : z := w + x + y */ /* Routing - this will be configurable in later version */ /* GPR[0/1] = FX * 4 + SPDIF-in */ emu_addefxop(sc, 4, 0x100, 0x12, 0, 0x44, &pc); emu_addefxop(sc, 4, 0x101, 0x13, 1, 0x44, &pc); /* GPR[0/1] += APS-input */ emu_addefxop(sc, 6, 0x100, 0x100, 0x40, sc->APS ? 0x16 : 0x40, &pc); emu_addefxop(sc, 6, 0x101, 0x101, 0x40, sc->APS ? 0x17 : 0x40, &pc); /* FrontOut (AC97) = GPR[0/1] */ emu_addefxop(sc, 6, 0x20, 0x40, 0x40, 0x100, &pc); emu_addefxop(sc, 6, 0x21, 0x40, 0x41, 0x101, &pc); /* RearOut = (GPR[0/1] * RearVolume) >> 31 */ /* RearVolume = GRP[0x10/0x11] */ emu_addefxop(sc, 0, 0x28, 0x40, 0x110, 0x100, &pc); emu_addefxop(sc, 0, 0x29, 0x40, 0x111, 0x101, &pc); /* TOS out = GPR[0/1] */ emu_addefxop(sc, 6, 0x22, 0x40, 0x40, 0x100, &pc); emu_addefxop(sc, 6, 0x23, 0x40, 0x40, 0x101, &pc); /* Mute Out2 */ emu_addefxop(sc, 6, 0x24, 0x40, 0x40, 0x40, &pc); emu_addefxop(sc, 6, 0x25, 0x40, 0x40, 0x40, &pc); /* Mute Out3 */ emu_addefxop(sc, 6, 0x26, 0x40, 0x40, 0x40, &pc); emu_addefxop(sc, 6, 0x27, 0x40, 0x40, 0x40, &pc); /* Input0 (AC97) -> Record */ emu_addefxop(sc, 6, 0x2a, 0x40, 0x40, 0x10, &pc); emu_addefxop(sc, 6, 0x2b, 0x40, 0x40, 0x11, &pc); emu_wrptr(sc, 0, DBG, 0); } /* Probe and attach the card */ static int emu_init(struct sc_info *sc) { u_int32_t spcs, ch, tmp, i; /* disable audio and lock cache */ emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE, 4); /* reset recording buffers */ emu_wrptr(sc, 0, MICBS, ADCBS_BUFSIZE_NONE); emu_wrptr(sc, 0, MICBA, 0); emu_wrptr(sc, 0, FXBS, ADCBS_BUFSIZE_NONE); emu_wrptr(sc, 0, FXBA, 0); emu_wrptr(sc, 0, ADCBS, ADCBS_BUFSIZE_NONE); emu_wrptr(sc, 0, ADCBA, 0); /* disable channel interrupt */ emu_wr(sc, INTE, INTE_INTERVALTIMERENB | INTE_SAMPLERATETRACKER | INTE_PCIERRORENABLE, 4); emu_wrptr(sc, 0, CLIEL, 0); emu_wrptr(sc, 0, CLIEH, 0); emu_wrptr(sc, 0, SOLEL, 0); emu_wrptr(sc, 0, SOLEH, 0); /* init envelope engine */ for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, DCYSUSV, ENV_OFF); emu_wrptr(sc, ch, IP, 0); emu_wrptr(sc, ch, VTFT, 0xffff); emu_wrptr(sc, ch, CVCF, 0xffff); emu_wrptr(sc, ch, PTRX, 0); emu_wrptr(sc, ch, CPF, 0); emu_wrptr(sc, ch, CCR, 0); emu_wrptr(sc, ch, PSST, 0); emu_wrptr(sc, ch, DSL, 0x10); emu_wrptr(sc, ch, CCCA, 0); emu_wrptr(sc, ch, Z1, 0); emu_wrptr(sc, ch, Z2, 0); emu_wrptr(sc, ch, FXRT, 0xd01c0000); emu_wrptr(sc, ch, ATKHLDM, 0); emu_wrptr(sc, ch, DCYSUSM, 0); emu_wrptr(sc, ch, IFATN, 0xffff); emu_wrptr(sc, ch, PEFE, 0); emu_wrptr(sc, ch, FMMOD, 0); emu_wrptr(sc, ch, TREMFRQ, 24); /* 1 Hz */ emu_wrptr(sc, ch, FM2FRQ2, 24); /* 1 Hz */ emu_wrptr(sc, ch, TEMPENV, 0); /*** these are last so OFF prevents writing ***/ emu_wrptr(sc, ch, LFOVAL2, 0); emu_wrptr(sc, ch, LFOVAL1, 0); emu_wrptr(sc, ch, ATKHLDV, 0); emu_wrptr(sc, ch, ENVVOL, 0); emu_wrptr(sc, ch, ENVVAL, 0); sc->voice[ch].vnum = ch; sc->voice[ch].slave = NULL; sc->voice[ch].busy = 0; sc->voice[ch].ismaster = 0; sc->voice[ch].running = 0; sc->voice[ch].b16 = 0; sc->voice[ch].stereo = 0; sc->voice[ch].speed = 0; sc->voice[ch].start = 0; sc->voice[ch].end = 0; sc->voice[ch].channel = NULL; } sc->pnum = sc->rnum = 0; /* * Init to 0x02109204 : * Clock accuracy = 0 (1000ppm) * Sample Rate = 2 (48kHz) * Audio Channel = 1 (Left of 2) * Source Number = 0 (Unspecified) * Generation Status = 1 (Original for Cat Code 12) * Cat Code = 12 (Digital Signal Mixer) * Mode = 0 (Mode 0) * Emphasis = 0 (None) * CP = 1 (Copyright unasserted) * AN = 0 (Audio data) * P = 0 (Consumer) */ spcs = SPCS_CLKACCY_1000PPM | SPCS_SAMPLERATE_48 | SPCS_CHANNELNUM_LEFT | SPCS_SOURCENUM_UNSPEC | SPCS_GENERATIONSTATUS | 0x00001200 | 0x00000000 | SPCS_EMPHASIS_NONE | SPCS_COPYRIGHT; emu_wrptr(sc, 0, SPCS0, spcs); emu_wrptr(sc, 0, SPCS1, spcs); emu_wrptr(sc, 0, SPCS2, spcs); emu_initefx(sc); SLIST_INIT(&sc->mem.blocks); sc->mem.ptb_pages = emu_malloc(sc, MAXPAGES * sizeof(u_int32_t)); if (sc->mem.ptb_pages == NULL) return -1; sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE); if (sc->mem.silent_page == NULL) { emu_free(sc, sc->mem.ptb_pages); return -1; } /* Clear page with silence & setup all pointers to this page */ bzero(sc->mem.silent_page, EMUPAGESIZE); tmp = (u_int32_t)vtophys(sc->mem.silent_page) << 1; for (i = 0; i < MAXPAGES; i++) sc->mem.ptb_pages[i] = tmp | i; emu_wrptr(sc, 0, PTB, vtophys(sc->mem.ptb_pages)); emu_wrptr(sc, 0, TCB, 0); /* taken from original driver */ emu_wrptr(sc, 0, TCBS, 0); /* taken from original driver */ for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, MAPA, tmp | MAP_PTI_MASK); emu_wrptr(sc, ch, MAPB, tmp | MAP_PTI_MASK); } /* emu_memalloc(sc, EMUPAGESIZE); */ /* * Hokay, now enable the AUD bit * Enable Audio = 1 * Mute Disable Audio = 0 * Lock Tank Memory = 1 * Lock Sound Memory = 0 * Auto Mute = 1 */ tmp = HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE | HCFG_AUTOMUTE; if (sc->rev >= 6) tmp |= HCFG_JOYENABLE; emu_wr(sc, HCFG, tmp, 4); /* TOSLink detection */ sc->tos_link = 0; tmp = emu_rd(sc, HCFG, 4); if (tmp & (HCFG_GPINPUT0 | HCFG_GPINPUT1)) { emu_wr(sc, HCFG, tmp | 0x800, 4); DELAY(50); if (tmp != (emu_rd(sc, HCFG, 4) & ~0x800)) { sc->tos_link = 1; emu_wr(sc, HCFG, tmp, 4); } } return 0; } static int emu_uninit(struct sc_info *sc) { u_int32_t ch; emu_wr(sc, INTE, 0, 4); for (ch = 0; ch < NUM_G; ch++) emu_wrptr(sc, ch, DCYSUSV, ENV_OFF); for (ch = 0; ch < NUM_G; ch++) { emu_wrptr(sc, ch, VTFT, 0); emu_wrptr(sc, ch, CVCF, 0); emu_wrptr(sc, ch, PTRX, 0); emu_wrptr(sc, ch, CPF, 0); } /* disable audio and lock cache */ emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE, 4); emu_wrptr(sc, 0, PTB, 0); /* reset recording buffers */ emu_wrptr(sc, 0, MICBS, ADCBS_BUFSIZE_NONE); emu_wrptr(sc, 0, MICBA, 0); emu_wrptr(sc, 0, FXBS, ADCBS_BUFSIZE_NONE); emu_wrptr(sc, 0, FXBA, 0); emu_wrptr(sc, 0, FXWC, 0); emu_wrptr(sc, 0, ADCBS, ADCBS_BUFSIZE_NONE); emu_wrptr(sc, 0, ADCBA, 0); emu_wrptr(sc, 0, TCB, 0); emu_wrptr(sc, 0, TCBS, 0); /* disable channel interrupt */ emu_wrptr(sc, 0, CLIEL, 0); emu_wrptr(sc, 0, CLIEH, 0); emu_wrptr(sc, 0, SOLEL, 0); emu_wrptr(sc, 0, SOLEH, 0); /* init envelope engine */ if (!SLIST_EMPTY(&sc->mem.blocks)) device_printf(sc->dev, "warning: memblock list not empty\n"); emu_free(sc, sc->mem.ptb_pages); emu_free(sc, sc->mem.silent_page); return 0; } static int emu_pci_probe(device_t dev) { char *s = NULL; switch (pci_get_devid(dev)) { case EMU10K1_PCI_ID: s = "Creative EMU10K1"; break; } if (s) device_set_desc(dev, s); return s? 0 : ENXIO; } static int emu_pci_attach(device_t dev) { u_int32_t data; struct sc_info *sc; struct ac97_info *codec = NULL; int i, mapped; char status[SND_STATUSLEN]; if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(sc, sizeof(*sc)); sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); mapped = 0; for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { sc->regid = PCIR_MAPS + i*4; sc->regtype = SYS_RES_MEMORY; sc->reg = bus_alloc_resource(dev, sc->regtype, &sc->regid, 0, ~0, 1, RF_ACTIVE); if (!sc->reg) { sc->regtype = SYS_RES_IOPORT; sc->reg = bus_alloc_resource(dev, sc->regtype, &sc->regid, 0, ~0, 1, RF_ACTIVE); } if (sc->reg) { sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); mapped++; } } if (mapped == 0) { device_printf(dev, "unable to map register space\n"); goto bad; } if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/1 << 31, /* can only access 0-2gb */ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/262144, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } if (emu_init(sc) == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - codec = ac97_create(dev, sc, NULL, emu_rdcd, emu_wrcd); + codec = AC97_CREATE(dev, sc, emu_ac97); if (codec == NULL) goto bad; - if (mixer_init(dev, &ac97_mixer, codec) == -1) goto bad; + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; sc->irqid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!sc->irq || bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(sc->reg), rman_get_start(sc->irq)); if (pcm_register(dev, sc, EMU_CHANS, 3)) goto bad; for (i = 0; i < EMU_CHANS; i++) - pcm_addchan(dev, PCMDIR_PLAY, &emu_chantemplate, sc); + pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc); for (i = 0; i < 3; i++) - pcm_addchan(dev, PCMDIR_REC, &emur_chantemplate, sc); + pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc); pcm_setstatus(dev, status); return 0; bad: if (codec) ac97_destroy(codec); if (sc->reg) bus_release_resource(dev, sc->regtype, sc->regid, sc->reg); if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat); free(sc, M_DEVBUF); return ENXIO; } static int emu_pci_detach(device_t dev) { int r; struct sc_info *sc; r = pcm_unregister(dev); if (r) return r; sc = pcm_getdevinfo(dev); /* shutdown chip */ emu_uninit(sc); bus_release_resource(dev, sc->regtype, sc->regid, sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); bus_dma_tag_destroy(sc->parent_dmat); free(sc, M_DEVBUF); return 0; } /* add suspend, resume */ static device_method_t emu_methods[] = { /* Device interface */ DEVMETHOD(device_probe, emu_pci_probe), DEVMETHOD(device_attach, emu_pci_attach), DEVMETHOD(device_detach, emu_pci_detach), { 0, 0 } }; static driver_t emu_driver = { "pcm", emu_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_emu10k1, pci, emu_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_emu10k1, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_emu10k1, 1); /* dummy driver to silence the joystick device */ static int emujoy_pci_probe(device_t dev) { char *s = NULL; switch (pci_get_devid(dev)) { case 0x70021102: s = "Creative EMU10K1 Joystick"; device_quiet(dev); break; } if (s) device_set_desc(dev, s); return s? 0 : ENXIO; } static int emujoy_pci_attach(device_t dev) { return 0; } static int emujoy_pci_detach(device_t dev) { return 0; } static device_method_t emujoy_methods[] = { DEVMETHOD(device_probe, emujoy_pci_probe), DEVMETHOD(device_attach, emujoy_pci_attach), DEVMETHOD(device_detach, emujoy_pci_detach), { 0, 0 } }; static driver_t emujoy_driver = { "emujoy", emujoy_methods, 8, }; static devclass_t emujoy_devclass; DRIVER_MODULE(emujoy, pci, emujoy_driver, emujoy_devclass, 0, 0); Index: stable/4/sys/dev/sound/pci/es137x.c =================================================================== --- stable/4/sys/dev/sound/pci/es137x.c (revision 71948) +++ stable/4/sys/dev/sound/pci/es137x.c (revision 71949) @@ -1,914 +1,892 @@ /* * Support the ENSONIQ AudioPCI board and Creative Labs SoundBlaster PCI * boards based on the ES1370, ES1371 and ES1373 chips. * * Copyright (c) 1999 Russell Cattelan * Copyright (c) 1999 Cameron Grant * Copyright (c) 1998 by Joachim Kuebart. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgement: * This product includes software developed by Joachim Kuebart. * * 4. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Part of this code was heavily inspired by the linux driver from * Thomas Sailer (sailer@ife.ee.ethz.ch) * Just about everything has been touched and reworked in some way but * the all the underlying sequences/timing/register values are from * Thomas' code. * */ #include #include #include #include #include #include +#include "mixer_if.h" + static int debug = 0; SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, ""); #define MEM_MAP_REG 0x14 /* PCI IDs of supported chips */ #define ES1370_PCI_ID 0x50001274 #define ES1371_PCI_ID 0x13711274 #define ES1371_PCI_ID2 0x13713274 #define ES1371_PCI_ID3 0x58801274 #define ES_BUFFSIZE 4096 /* device private data */ struct es_info; struct es_chinfo { struct es_info *parent; pcm_channel *channel; snd_dbuf *buffer; int dir, num; - u_int32_t fmt; + u_int32_t fmt, blksz, bufsz; }; struct es_info { bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; struct resource *reg, *irq; int regtype, regid, irqid; void *ih; device_t dev; int num; /* Contents of board's registers */ u_long ctrl; u_long sctrl; struct es_chinfo pch, rch; }; /* -------------------------------------------------------------------- */ /* prototypes */ static void es_intr(void *); -static void es1371_wrcodec(void *, int, u_int32_t); -static u_int32_t es1371_rdcodec(void *, int); static u_int es1371_wait_src_ready(struct es_info *); static void es1371_src_write(struct es_info *, u_short, unsigned short); static u_int es1371_adc_rate(struct es_info *, u_int, int); static u_int es1371_dac_rate(struct es_info *, u_int, int); -static int es1371_init(struct es_info *es, int); +static int es1371_init(struct es_info *, device_t); static int es1370_init(struct es_info *); static int es1370_wrcodec(struct es_info *, u_char, u_char); -/* channel interface */ -static void *eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int eschan_setdir(void *data, int dir); -static int eschan_setformat(void *data, u_int32_t format); -static int eschan1370_setspeed(void *data, u_int32_t speed); -static int eschan1371_setspeed(void *data, u_int32_t speed); -static int eschan_setblocksize(void *data, u_int32_t blocksize); -static int eschan_trigger(void *data, int go); -static int eschan_getptr(void *data); -static pcmchan_caps *eschan_getcaps(void *data); - static u_int32_t es_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps es_playcaps = {4000, 48000, es_playfmt, 0}; static u_int32_t es_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps es_reccaps = {4000, 48000, es_recfmt, 0}; -static pcm_channel es1370_chantemplate = { - eschan_init, - eschan_setdir, - eschan_setformat, - eschan1370_setspeed, - eschan_setblocksize, - eschan_trigger, - eschan_getptr, - eschan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - -static pcm_channel es1371_chantemplate = { - eschan_init, - eschan_setdir, - eschan_setformat, - eschan1371_setspeed, - eschan_setblocksize, - eschan_trigger, - eschan_getptr, - eschan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - -/* -------------------------------------------------------------------- */ - -/* The es1370 mixer interface */ - -static int es1370_mixinit(snd_mixer *m); -static int es1370_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int es1370_mixsetrecsrc(snd_mixer *m, u_int32_t src); - -static snd_mixer es1370_mixer = { - "AudioPCI 1370 mixer", - es1370_mixinit, - NULL, - es1370_mixset, - es1370_mixsetrecsrc, -}; - static const struct { unsigned volidx:4; unsigned left:4; unsigned right:4; unsigned stereo:1; unsigned recmask:13; unsigned avail:1; } mixtable[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, [SOUND_MIXER_PCM] = { 1, 0x2, 0x3, 1, 0x0400, 1 }, [SOUND_MIXER_SYNTH] = { 2, 0x4, 0x5, 1, 0x0060, 1 }, [SOUND_MIXER_CD] = { 3, 0x6, 0x7, 1, 0x0006, 1 }, [SOUND_MIXER_LINE] = { 4, 0x8, 0x9, 1, 0x0018, 1 }, [SOUND_MIXER_LINE1] = { 5, 0xa, 0xb, 1, 0x1800, 1 }, [SOUND_MIXER_LINE2] = { 6, 0xc, 0x0, 0, 0x0100, 1 }, [SOUND_MIXER_LINE3] = { 7, 0xd, 0x0, 0, 0x0200, 1 }, [SOUND_MIXER_MIC] = { 8, 0xe, 0x0, 0, 0x0001, 1 }, [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } }; +/* -------------------------------------------------------------------- */ +/* The es1370 mixer interface */ + static int es1370_mixinit(snd_mixer *m) { int i; u_int32_t v; v = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].avail) v |= (1 << i); mix_setdevs(m, v); v = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].recmask) v |= (1 << i); mix_setrecdevs(m, v); return 0; } static int es1370_mixset(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { int l, r, rl, rr; if (!mixtable[dev].avail) return -1; l = left; r = mixtable[dev].stereo? right : l; if (mixtable[dev].left == 0xf) { rl = (l < 2)? 0x80 : 7 - (l - 2) / 14; } else { rl = (l < 10)? 0x80 : 15 - (l - 10) / 6; } if (mixtable[dev].stereo) { rr = (r < 10)? 0x80 : 15 - (r - 10) / 6; es1370_wrcodec(mix_getdevinfo(m), mixtable[dev].right, rr); } es1370_wrcodec(mix_getdevinfo(m), mixtable[dev].left, rl); return l | (r << 8); } static int es1370_mixsetrecsrc(snd_mixer *m, u_int32_t src) { int i, j = 0; if (src == 0) src = 1 << SOUND_MIXER_MIC; src &= mix_getrecdevs(m); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if ((src & (1 << i)) != 0) j |= mixtable[i].recmask; es1370_wrcodec(mix_getdevinfo(m), CODEC_LIMIX1, j & 0x55); es1370_wrcodec(mix_getdevinfo(m), CODEC_RIMIX1, j & 0xaa); es1370_wrcodec(mix_getdevinfo(m), CODEC_LIMIX2, (j >> 8) & 0x17); es1370_wrcodec(mix_getdevinfo(m), CODEC_RIMIX2, (j >> 8) & 0x0f); es1370_wrcodec(mix_getdevinfo(m), CODEC_OMIX1, 0x7f); es1370_wrcodec(mix_getdevinfo(m), CODEC_OMIX2, 0x3f); return src; } +static kobj_method_t es1370_mixer_methods[] = { + KOBJMETHOD(mixer_init, es1370_mixinit), + KOBJMETHOD(mixer_set, es1370_mixset), + KOBJMETHOD(mixer_setrecsrc, es1370_mixsetrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(es1370_mixer); + +/* -------------------------------------------------------------------- */ + static int es1370_wrcodec(struct es_info *es, u_char i, u_char data) { int wait = 100; /* 100 msec timeout */ do { if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) & STAT_CSTAT) == 0) { bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC, ((u_short)i << CODEC_INDEX_SHIFT) | data); return 0; } DELAY(1000); } while (--wait); printf("pcm: es1370_wrcodec timed out\n"); return -1; } /* -------------------------------------------------------------------- */ /* channel interface */ static void * -eschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +eschan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct es_info *es = devinfo; struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch; ch->parent = es; ch->channel = c; ch->buffer = b; - ch->buffer->bufsize = ES_BUFFSIZE; + ch->bufsz = ES_BUFFSIZE; + ch->blksz = ch->bufsz / 2; ch->num = ch->parent->num++; - if (chn_allocbuf(ch->buffer, es->parent_dmat) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) == -1) return NULL; return ch; } static int -eschan_setdir(void *data, int dir) +eschan_setdir(kobj_t obj, void *data, int dir) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; if (dir == PCMDIR_PLAY) { - bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, - ES1370_REG_DAC2_FRAMEADR >> 8); - bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff, - vtophys(ch->buffer->buf)); - bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, - (ch->buffer->bufsize >> 2) - 1); + bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8); + bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMEADR & 0xff, vtophys(sndbuf_getbuf(ch->buffer))); + bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); } else { - bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, - ES1370_REG_ADC_FRAMEADR >> 8); - bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff, - vtophys(ch->buffer->buf)); - bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff, - (ch->buffer->bufsize >> 2) - 1); + bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8); + bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff, vtophys(sndbuf_getbuf(ch->buffer))); + bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); } ch->dir = dir; return 0; } static int -eschan_setformat(void *data, u_int32_t format) +eschan_setformat(kobj_t obj, void *data, u_int32_t format) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; if (ch->dir == PCMDIR_PLAY) { es->sctrl &= ~SCTRL_P2FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB; if (format & AFMT_STEREO) es->sctrl |= SCTRL_P2SMB; } else { es->sctrl &= ~SCTRL_R1FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB; if (format & AFMT_STEREO) es->sctrl |= SCTRL_R1SMB; } bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl); ch->fmt = format; return 0; } static int -eschan1370_setspeed(void *data, u_int32_t speed) +eschan1370_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; es->ctrl &= ~CTRL_PCLKDIV; es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV; bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl); /* rec/play speeds locked together - should indicate in flags */ return speed; /* XXX calc real speed */ } -int -eschan1371_setspeed(void *data, u_int32_t speed) +static int +eschan1371_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; if (ch->dir == PCMDIR_PLAY) { return es1371_dac_rate(es, speed, 3 - ch->num); /* play */ } else { return es1371_adc_rate(es, speed, 1); /* record */ } } static int -eschan_setblocksize(void *data, u_int32_t blocksize) +eschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { - return blocksize; + struct es_chinfo *ch = data; + + ch->blksz = blocksize; + ch->bufsz = ch->blksz * 2; + sndbuf_resize(ch->buffer, 2, ch->blksz); + + return ch->blksz; } static int -eschan_trigger(void *data, int go) +eschan_trigger(kobj_t obj, void *data, int go) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; - unsigned ss, cnt; + unsigned cnt; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - ss = 1; - ss <<= (ch->fmt & AFMT_STEREO)? 1 : 0; - ss <<= (ch->fmt & AFMT_16BIT)? 1 : 0; - cnt = ch->buffer->dl / ss - 1; + cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { int b = (ch->fmt & AFMT_S16_LE)? 2 : 1; es->ctrl |= CTRL_DAC2_EN; - es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | - SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | - SCTRL_P2DACSEN); + es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN); es->sctrl |= SCTRL_P2INTEN | (b << SCTRL_SH_P2ENDINC); - bus_space_write_4(es->st, es->sh, - ES1370_REG_DAC2_SCOUNT, cnt); + bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_SCOUNT, cnt); /* start at beginning of buffer */ - bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, - ES1370_REG_DAC2_FRAMECNT >> 8); - bus_space_write_4(es->st, es->sh, - ES1370_REG_DAC2_FRAMECNT & 0xff, - (ch->buffer->bufsize >> 2) - 1); + bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMECNT >> 8); + bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); } else es->ctrl &= ~CTRL_DAC2_EN; } else { if (go == PCMTRIG_START) { es->ctrl |= CTRL_ADC_EN; es->sctrl &= ~SCTRL_R1LOOPSEL; es->sctrl |= SCTRL_R1INTEN; - bus_space_write_4(es->st, es->sh, - ES1370_REG_ADC_SCOUNT, cnt); + bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_SCOUNT, cnt); /* start at beginning of buffer */ - bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, - ES1370_REG_ADC_FRAMECNT >> 8); - bus_space_write_4(es->st, es->sh, - ES1370_REG_ADC_FRAMECNT & 0xff, - (ch->buffer->bufsize >> 2) - 1); + bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMECNT >> 8); + bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); } else es->ctrl &= ~CTRL_ADC_EN; } bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl); bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl); return 0; } static int -eschan_getptr(void *data) +eschan_getptr(kobj_t obj, void *data) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; u_int32_t reg, cnt; if (ch->dir == PCMDIR_PLAY) reg = ES1370_REG_DAC2_FRAMECNT; else reg = ES1370_REG_ADC_FRAMECNT; bus_space_write_4(es->st, es->sh, ES1370_REG_MEMPAGE, reg >> 8); cnt = bus_space_read_4(es->st, es->sh, reg & 0x000000ff) >> 16; /* cnt is longwords */ return cnt << 2; } static pcmchan_caps * -eschan_getcaps(void *data) +eschan_getcaps(kobj_t obj, void *data) { struct es_chinfo *ch = data; return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps; } +static kobj_method_t eschan1370_methods[] = { + KOBJMETHOD(channel_init, eschan_init), + KOBJMETHOD(channel_setdir, eschan_setdir), + KOBJMETHOD(channel_setformat, eschan_setformat), + KOBJMETHOD(channel_setspeed, eschan1370_setspeed), + KOBJMETHOD(channel_setblocksize, eschan_setblocksize), + KOBJMETHOD(channel_trigger, eschan_trigger), + KOBJMETHOD(channel_getptr, eschan_getptr), + KOBJMETHOD(channel_getcaps, eschan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(eschan1370); + +static kobj_method_t eschan1371_methods[] = { + KOBJMETHOD(channel_init, eschan_init), + KOBJMETHOD(channel_setdir, eschan_setdir), + KOBJMETHOD(channel_setformat, eschan_setformat), + KOBJMETHOD(channel_setspeed, eschan1371_setspeed), + KOBJMETHOD(channel_setblocksize, eschan_setblocksize), + KOBJMETHOD(channel_trigger, eschan_trigger), + KOBJMETHOD(channel_getptr, eschan_getptr), + KOBJMETHOD(channel_getcaps, eschan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(eschan1371); + +/* -------------------------------------------------------------------- */ /* The interrupt handler */ static void es_intr(void *p) { struct es_info *es = p; unsigned intsrc, sctrl; intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS); if ((intsrc & STAT_INTR) == 0) return; sctrl = es->sctrl; if (intsrc & STAT_ADC) sctrl &= ~SCTRL_R1INTEN; if (intsrc & STAT_DAC1) sctrl &= ~SCTRL_P1INTEN; if (intsrc & STAT_DAC2) sctrl &= ~SCTRL_P2INTEN; bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, sctrl); bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl); if (intsrc & STAT_ADC) chn_intr(es->rch.channel); if (intsrc & STAT_DAC1); if (intsrc & STAT_DAC2) chn_intr(es->pch.channel); } /* ES1370 specific */ static int es1370_init(struct es_info *es) { es->ctrl = CTRL_CDC_EN | CTRL_SERR_DIS | (DAC2_SRTODIV(DSP_DEFAULT_SPEED) << CTRL_SH_PCLKDIV); bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl); es->sctrl = 0; bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl); es1370_wrcodec(es, CODEC_RES_PD, 3);/* No RST, PD */ es1370_wrcodec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use * {LR,B}CLK2 and run off the LRCLK2 * PLL; program DAC_SYNC=0! */ es1370_wrcodec(es, CODEC_ADSEL, 0);/* Recording source is mixer */ es1370_wrcodec(es, CODEC_MGAIN, 0);/* MIC amp is 0db */ return 0; } /* ES1371 specific */ int -es1371_init(struct es_info *es, int rev) +es1371_init(struct es_info *es, device_t dev) { int idx; + int devid = pci_get_devid(dev); + int revid = pci_get_revid(dev); if (debug > 0) printf("es_init\n"); es->num = 0; es->ctrl = 0; es->sctrl = 0; /* initialize the chips */ - if (rev == 7 || rev >= 9 || rev == 2) { + if (revid == 7 || revid >= 9 || (devid == ES1371_PCI_ID3 && revid == 2)) { #define ES1371_BINTSUMM_OFF 0x07 bus_space_write_4(es->st, es->sh, ES1371_BINTSUMM_OFF, 0x20); if (debug > 0) printf("es_init rev == 7 || rev >= 9\n"); } else { /* pre ac97 2.1 card */ bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl); if (debug > 0) printf("es_init pre ac97 2.1\n"); } bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl); bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, 0); /* AC'97 warm reset to start the bitclk */ bus_space_write_4(es->st, es->sh, ES1371_REG_LEGACY, es->ctrl | ES1371_SYNC_RES); DELAY(2000); bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->ctrl); /* Init the sample rate converter */ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, ES1371_DIS_SRC); for (idx = 0; idx < 0x80; idx++) es1371_src_write(es, idx, 0); es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_TRUNC_N, 16 << 4); es1371_src_write(es, ES_SMPREG_DAC1 + ES_SMPREG_INT_REGS, 16 << 10); es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_TRUNC_N, 16 << 4); es1371_src_write(es, ES_SMPREG_DAC2 + ES_SMPREG_INT_REGS, 16 << 10); es1371_src_write(es, ES_SMPREG_VOL_ADC, 1 << 12); es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, 1 << 12); es1371_src_write(es, ES_SMPREG_VOL_DAC1, 1 << 12); es1371_src_write(es, ES_SMPREG_VOL_DAC1 + 1, 1 << 12); es1371_src_write(es, ES_SMPREG_VOL_DAC2, 1 << 12); es1371_src_write(es, ES_SMPREG_VOL_DAC2 + 1, 1 << 12); es1371_adc_rate (es, 22050, 1); es1371_dac_rate (es, 22050, 1); es1371_dac_rate (es, 22050, 2); /* WARNING: * enabling the sample rate converter without properly programming * its parameters causes the chip to lock up (the SRC busy bit will * be stuck high, and I've found no way to rectify this other than * power cycle) */ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, 0); return (0); } -static void -es1371_wrcodec(void *s, int addr, u_int32_t data) +/* -------------------------------------------------------------------- */ + +static int +es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data) { int sl; unsigned t, x; struct es_info *es = (struct es_info*)s; if (debug > 0) printf("wrcodec addr 0x%x data 0x%x\n", addr, data); for (t = 0; t < 0x1000; t++) if (!(bus_space_read_4(es->st, es->sh,(ES1371_REG_CODEC & CODEC_WIP)))) break; sl = spltty(); /* save the current state for later */ x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE); /* enable SRC state data in SRC mux */ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, (es1371_wait_src_ready(s) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1))); /* wait for a SAFE time to write addr/data and then do it, dammit */ for (t = 0; t < 0x1000; t++) if ((bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000) break; if (debug > 2) printf("one b_s_w: 0x%x 0x%x 0x%x\n", es->sh, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK)); bus_space_write_4(es->st, es->sh,ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK)); /* restore SRC reg */ es1371_wait_src_ready(s); if (debug > 2) printf("two b_s_w: 0x%x 0x%x 0x%x\n", es->sh, ES1371_REG_SMPRATE, x); bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, x); splx(sl); + + return 0; } -static u_int32_t -es1371_rdcodec(void *s, int addr) +static int +es1371_rdcd(kobj_t obj, void *s, int addr) { int sl; unsigned t, x; struct es_info *es = (struct es_info *)s; if (debug > 0) printf("rdcodec addr 0x%x ... ", addr); for (t = 0; t < 0x1000; t++) if (!(x = bus_space_read_4(es->st, es->sh, ES1371_REG_CODEC) & CODEC_WIP)) break; if (debug > 0) printf("loop 1 t 0x%x x 0x%x ", t, x); sl = spltty(); /* save the current state for later */ x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE); /* enable SRC state data in SRC mux */ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, (es1371_wait_src_ready(s) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1))); /* wait for a SAFE time to write addr/data and then do it, dammit */ for (t = 0; t < 0x5000; t++) if ((x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE) & 0x00070000) == 0x00010000) break; if (debug > 0) printf("loop 2 t 0x%x x 0x%x ", t, x); bus_space_write_4(es->st, es->sh, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD); /* restore SRC reg */ es1371_wait_src_ready(s); bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, x); splx(sl); /* now wait for the stinkin' data (RDY) */ for (t = 0; t < 0x1000; t++) if ((x = bus_space_read_4(es->st, es->sh, ES1371_REG_CODEC)) & CODEC_RDY) break; if (debug > 0) printf("loop 3 t 0x%x 0x%x ret 0x%x\n", t, x, ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT)); return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } +static kobj_method_t es1371_ac97_methods[] = { + KOBJMETHOD(ac97_read, es1371_rdcd), + KOBJMETHOD(ac97_write, es1371_wrcd), + { 0, 0 } +}; +AC97_DECLARE(es1371_ac97); + +/* -------------------------------------------------------------------- */ + static u_int es1371_src_read(struct es_info *es, u_short reg) { unsigned int r; r = es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1); r |= ES1371_SRC_RAM_ADDRO(reg); bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE,r); return ES1371_SRC_RAM_DATAI(es1371_wait_src_ready(es)); } static void es1371_src_write(struct es_info *es, u_short reg, u_short data){ u_int r; r = es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1); r |= ES1371_SRC_RAM_ADDRO(reg) | ES1371_SRC_RAM_DATAO(data); /* printf("es1371_src_write 0x%x 0x%x\n",ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE); */ bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE); } static u_int es1371_adc_rate(struct es_info *es, u_int rate, int set) { u_int n, truncm, freq, result; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; n = rate / 3000; if ((1 << n) & ((1 << 15) | (1 << 13) | (1 << 11) | (1 << 9))) n--; truncm = (21 * n - 1) | 1; freq = ((48000UL << 15) / rate) * n; result = (48000UL << 15) / (freq / n); if (set) { if (rate >= 24000) { if (truncm > 239) truncm = 239; es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, (((239 - truncm) >> 1) << 9) | (n << 4)); } else { if (truncm > 119) truncm = 119; es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_TRUNC_N, 0x8000 | (((119 - truncm) >> 1) << 9) | (n << 4)); } es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS, (es1371_src_read(es, ES_SMPREG_ADC + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00)); es1371_src_write(es, ES_SMPREG_ADC + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); es1371_src_write(es, ES_SMPREG_VOL_ADC, n << 8); es1371_src_write(es, ES_SMPREG_VOL_ADC + 1, n << 8); } return result; } static u_int es1371_dac_rate(struct es_info *es, u_int rate, int set) { u_int freq, r, result, dac, dis; if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; freq = (rate << 15) / 3000; result = (freq * 3000) >> 15; if (set) { dac = (set == 1)? ES_SMPREG_DAC1 : ES_SMPREG_DAC2; dis = (set == 1)? ES1371_DIS_P2 : ES1371_DIS_P1; r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)); bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r); es1371_src_write(es, dac + ES_SMPREG_INT_REGS, (es1371_src_read(es, dac + ES_SMPREG_INT_REGS) & 0x00ff) | ((freq >> 5) & 0xfc00)); es1371_src_write(es, dac + ES_SMPREG_VFREQ_FRAC, freq & 0x7fff); r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | dis | ES1371_DIS_R1)); bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r); } return result; } static u_int es1371_wait_src_ready(struct es_info *es) { u_int t, r; for (t = 0; t < 500; t++) { if (!((r = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE)) & ES1371_SRC_RAM_BUSY)) return r; DELAY(1000); } printf("es1371: wait src ready timeout 0x%x [0x%x]\n", ES1371_REG_SMPRATE, r); return 0; } /* -------------------------------------------------------------------- */ /* * Probe and attach the card */ static int es_pci_probe(device_t dev) { if (pci_get_devid(dev) == ES1370_PCI_ID) { device_set_desc(dev, "AudioPCI ES1370"); return 0; } else if (pci_get_devid(dev) == ES1371_PCI_ID || pci_get_devid(dev) == ES1371_PCI_ID2 || pci_get_devid(dev) == ES1371_PCI_ID3) { device_set_desc(dev, "AudioPCI ES1371"); return 0; } return ENXIO; } static int es_pci_attach(device_t dev) { u_int32_t data; struct es_info *es = 0; int mapped; char status[SND_STATUSLEN]; struct ac97_info *codec = 0; - pcm_channel *ct = NULL; + kobj_class_t ct = NULL; if ((es = malloc(sizeof *es, M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(es, sizeof *es); es->dev = dev; mapped = 0; data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); if (mapped == 0 && (data & PCIM_CMD_MEMEN)) { es->regid = MEM_MAP_REG; es->regtype = SYS_RES_MEMORY; es->reg = bus_alloc_resource(dev, es->regtype, &es->regid, 0, ~0, 1, RF_ACTIVE); if (es->reg) { es->st = rman_get_bustag(es->reg); es->sh = rman_get_bushandle(es->reg); mapped++; } } if (mapped == 0 && (data & PCIM_CMD_PORTEN)) { es->regid = PCIR_MAPS; es->regtype = SYS_RES_IOPORT; es->reg = bus_alloc_resource(dev, es->regtype, &es->regid, 0, ~0, 1, RF_ACTIVE); if (es->reg) { es->st = rman_get_bustag(es->reg); es->sh = rman_get_bushandle(es->reg); mapped++; } } if (mapped == 0) { device_printf(dev, "unable to map register space\n"); goto bad; } if (pci_get_devid(dev) == ES1371_PCI_ID || pci_get_devid(dev) == ES1371_PCI_ID2 || pci_get_devid(dev) == ES1371_PCI_ID3) { - if(-1 == es1371_init(es, pci_get_revid(dev))) { + if(-1 == es1371_init(es, dev)) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - codec = ac97_create(dev, es, NULL, es1371_rdcodec, es1371_wrcodec); + codec = AC97_CREATE(dev, es, es1371_ac97); if (codec == NULL) goto bad; /* our init routine does everything for us */ /* set to NULL; flag mixer_init not to run the ac97_init */ /* ac97_mixer.init = NULL; */ - if (mixer_init(dev, &ac97_mixer, codec) == -1) goto bad; - ct = &es1371_chantemplate; + if (mixer_init(dev, ac97_getmixerclass(), codec)) goto bad; + ct = &eschan1371_class; } else if (pci_get_devid(dev) == ES1370_PCI_ID) { if (-1 == es1370_init(es)) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - mixer_init(dev, &es1370_mixer, es); - ct = &es1370_chantemplate; + if (mixer_init(dev, &es1370_mixer_class, es)) goto bad; + ct = &eschan1370_class; } else goto bad; es->irqid = 0; es->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &es->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!es->irq || bus_setup_intr(dev, es->irq, INTR_TYPE_TTY, es_intr, es, &es->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/ES_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &es->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } snprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", (es->regtype == SYS_RES_IOPORT)? "io" : "memory", rman_get_start(es->reg), rman_get_start(es->irq)); if (pcm_register(dev, es, 1, 1)) goto bad; pcm_addchan(dev, PCMDIR_REC, ct, es); pcm_addchan(dev, PCMDIR_PLAY, ct, es); pcm_setstatus(dev, status); return 0; bad: if (codec) ac97_destroy(codec); if (es->reg) bus_release_resource(dev, es->regtype, es->regid, es->reg); if (es->ih) bus_teardown_intr(dev, es->irq, es->ih); if (es->irq) bus_release_resource(dev, SYS_RES_IRQ, es->irqid, es->irq); if (es->parent_dmat) bus_dma_tag_destroy(es->parent_dmat); if (es) free(es, M_DEVBUF); return ENXIO; } static int es_pci_detach(device_t dev) { int r; struct es_info *es; r = pcm_unregister(dev); if (r) return r; es = pcm_getdevinfo(dev); bus_release_resource(dev, es->regtype, es->regid, es->reg); bus_teardown_intr(dev, es->irq, es->ih); bus_release_resource(dev, SYS_RES_IRQ, es->irqid, es->irq); bus_dma_tag_destroy(es->parent_dmat); free(es, M_DEVBUF); return 0; } static device_method_t es_methods[] = { /* Device interface */ DEVMETHOD(device_probe, es_pci_probe), DEVMETHOD(device_attach, es_pci_attach), DEVMETHOD(device_detach, es_pci_detach), { 0, 0 } }; static driver_t es_driver = { "pcm", es_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_es137x, pci, es_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_es137x, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_es137x, 1); Index: stable/4/sys/dev/sound/pci/fm801.c =================================================================== --- stable/4/sys/dev/sound/pci/fm801.c (revision 71948) +++ stable/4/sys/dev/sound/pci/fm801.c (revision 71949) @@ -1,728 +1,711 @@ /* * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #define PCI_VENDOR_FORTEMEDIA 0x1319 #define PCI_DEVICE_FORTEMEDIA1 0x08011319 #define PCI_DEVICE_FORTEMEDIA2 0x08021319 /* ??? have no idea what's this... */ #define FM_PCM_VOLUME 0x00 #define FM_FM_VOLUME 0x02 #define FM_I2S_VOLUME 0x04 #define FM_RECORD_SOURCE 0x06 #define FM_PLAY_CTL 0x08 #define FM_PLAY_RATE_MASK 0x0f00 #define FM_PLAY_BUF1_LAST 0x0001 #define FM_PLAY_BUF2_LAST 0x0002 #define FM_PLAY_START 0x0020 #define FM_PLAY_PAUSE 0x0040 #define FM_PLAY_STOPNOW 0x0080 #define FM_PLAY_16BIT 0x4000 #define FM_PLAY_STEREO 0x8000 #define FM_PLAY_DMALEN 0x0a #define FM_PLAY_DMABUF1 0x0c #define FM_PLAY_DMABUF2 0x10 #define FM_REC_CTL 0x14 #define FM_REC_RATE_MASK 0x0f00 #define FM_REC_BUF1_LAST 0x0001 #define FM_REC_BUF2_LAST 0x0002 #define FM_REC_START 0x0020 #define FM_REC_PAUSE 0x0040 #define FM_REC_STOPNOW 0x0080 #define FM_REC_16BIT 0x4000 #define FM_REC_STEREO 0x8000 #define FM_REC_DMALEN 0x16 #define FM_REC_DMABUF1 0x18 #define FM_REC_DMABUF2 0x1c #define FM_CODEC_CTL 0x22 #define FM_VOLUME 0x26 #define FM_VOLUME_MUTE 0x8000 #define FM_CODEC_CMD 0x2a #define FM_CODEC_CMD_READ 0x0080 #define FM_CODEC_CMD_VALID 0x0100 #define FM_CODEC_CMD_BUSY 0x0200 #define FM_CODEC_DATA 0x2c #define FM_IO_CTL 0x52 #define FM_CARD_CTL 0x54 #define FM_INTMASK 0x56 #define FM_INTMASK_PLAY 0x0001 #define FM_INTMASK_REC 0x0002 #define FM_INTMASK_VOL 0x0040 #define FM_INTMASK_MPU 0x0080 #define FM_INTSTATUS 0x5a #define FM_INTSTATUS_PLAY 0x0100 #define FM_INTSTATUS_REC 0x0200 #define FM_INTSTATUS_VOL 0x4000 #define FM_INTSTATUS_MPU 0x8000 #define FM801_BUFFSIZE 1024*4 /* Other values do not work!!! */ /* debug purposes */ #define DPRINT if(0) printf - -/* channel interface */ -static void *fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int fm801ch_setformat(void *data, u_int32_t format); -static int fm801ch_setspeed(void *data, u_int32_t speed); -static int fm801ch_setblocksize(void *data, u_int32_t blocksize); -static int fm801ch_trigger(void *data, int go); -static int fm801ch_getptr(void *data); -static pcmchan_caps *fm801ch_getcaps(void *data); /* static int fm801ch_setup(pcm_channel *c); */ static u_int32_t fmts[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps fm801ch_caps = { 4000, 48000, fmts, 0 }; -static pcm_channel fm801_chantemplate = { - fm801ch_init, - NULL, /* setdir */ - fm801ch_setformat, - fm801ch_setspeed, - fm801ch_setblocksize, - fm801ch_trigger, - fm801ch_getptr, - fm801ch_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - struct fm801_info; struct fm801_chinfo { struct fm801_info *parent; pcm_channel *channel; snd_dbuf *buffer; u_int32_t spd, dir, fmt; /* speed, direction, format */ u_int32_t shift; }; struct fm801_info { int type; bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; device_t dev; int num; u_int32_t unit; struct resource *reg, *irq; int regtype, regid, irqid; void *ih; u_int32_t play_flip, play_nextblk, play_start, play_blksize, play_fmt, play_shift, play_size; u_int32_t rec_flip, rec_nextblk, rec_start, rec_blksize, rec_fmt, rec_shift, rec_size; struct fm801_chinfo pch, rch; }; /* Bus Read / Write routines */ static u_int32_t fm801_rd(struct fm801_info *fm801, int regno, int size) { switch(size) { case 1: return (bus_space_read_1(fm801->st, fm801->sh, regno)); case 2: return (bus_space_read_2(fm801->st, fm801->sh, regno)); case 4: return (bus_space_read_4(fm801->st, fm801->sh, regno)); default: return 0xffffffff; } } static void fm801_wr(struct fm801_info *fm801, int regno, u_int32_t data, int size) { switch(size) { case 1: return bus_space_write_1(fm801->st, fm801->sh, regno, data); case 2: return bus_space_write_2(fm801->st, fm801->sh, regno, data); case 4: return bus_space_write_4(fm801->st, fm801->sh, regno, data); default: return; } } +/* -------------------------------------------------------------------- */ /* * ac97 codec routines */ #define TIMO 50 -static u_int32_t -fm801_rdcd(void *devinfo, int regno) +static int +fm801_rdcd(kobj_t obj, void *devinfo, int regno) { struct fm801_info *fm801 = (struct fm801_info *)devinfo; int i; for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { DELAY(10000); DPRINT("fm801 rdcd: 1 - DELAY\n"); } if (i >= TIMO) { printf("fm801 rdcd: codec busy\n"); return 0; } fm801_wr(fm801,FM_CODEC_CMD, regno|FM_CODEC_CMD_READ,2); for (i = 0; i < TIMO && !(fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_VALID); i++) { DELAY(10000); DPRINT("fm801 rdcd: 2 - DELAY\n"); } if (i >= TIMO) { printf("fm801 rdcd: write codec invalid\n"); return 0; } return fm801_rd(fm801,FM_CODEC_DATA,2); } -static void -fm801_wrcd(void *devinfo, int regno, u_int32_t data) +static int +fm801_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct fm801_info *fm801 = (struct fm801_info *)devinfo; int i; DPRINT("fm801_wrcd reg 0x%x val 0x%x\n",regno, data); /* if(regno == AC97_REG_RECSEL) return; */ /* Poll until codec is ready */ for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { DELAY(10000); DPRINT("fm801 rdcd: 1 - DELAY\n"); } if (i >= TIMO) { printf("fm801 wrcd: read codec busy\n"); - return; + return -1; } fm801_wr(fm801,FM_CODEC_DATA,data, 2); fm801_wr(fm801,FM_CODEC_CMD, regno,2); /* wait until codec is ready */ for (i = 0; i < TIMO && fm801_rd(fm801,FM_CODEC_CMD,2) & FM_CODEC_CMD_BUSY; i++) { DELAY(10000); DPRINT("fm801 wrcd: 2 - DELAY\n"); } if (i >= TIMO) { printf("fm801 wrcd: read codec busy\n"); - return; + return -1; } DPRINT("fm801 wrcd release reg 0x%x val 0x%x\n",regno, data); - return; + return 0; } +static kobj_method_t fm801_ac97_methods[] = { + KOBJMETHOD(ac97_read, fm801_rdcd), + KOBJMETHOD(ac97_write, fm801_wrcd), + { 0, 0 } +}; +AC97_DECLARE(fm801_ac97); + +/* -------------------------------------------------------------------- */ + /* * The interrupt handler */ static void fm801_intr(void *p) { struct fm801_info *fm801 = (struct fm801_info *)p; u_int32_t intsrc = fm801_rd(fm801, FM_INTSTATUS, 2); - struct fm801_chinfo *ch = &(fm801->pch); - snd_dbuf *b = ch->buffer; DPRINT("\nfm801_intr intsrc 0x%x ", intsrc); - DPRINT("rp %d, rl %d, fp %d fl %d, size=%d\n", - b->rp,b->rl, b->fp,b->fl, b->blksz); if(intsrc & FM_INTSTATUS_PLAY) { fm801->play_flip++; if(fm801->play_flip & 1) { fm801_wr(fm801, FM_PLAY_DMABUF1, fm801->play_start,4); } else fm801_wr(fm801, FM_PLAY_DMABUF2, fm801->play_nextblk,4); chn_intr(fm801->pch.channel); } if(intsrc & FM_INTSTATUS_REC) { fm801->rec_flip++; if(fm801->rec_flip & 1) { fm801_wr(fm801, FM_REC_DMABUF1, fm801->rec_start,4); } else fm801_wr(fm801, FM_REC_DMABUF2, fm801->rec_nextblk,4); chn_intr(fm801->rch.channel); } if ( intsrc & FM_INTSTATUS_MPU ) { /* This is a TODOish thing... */ fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_MPU,2); } if ( intsrc & FM_INTSTATUS_VOL ) { /* This is a TODOish thing... */ fm801_wr(fm801, FM_INTSTATUS, intsrc & FM_INTSTATUS_VOL,2); } DPRINT("fm801_intr clear\n\n"); fm801_wr(fm801, FM_INTSTATUS, intsrc & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC), 2); } -/* - * Init routine is taken from an original NetBSD driver - */ -static int -fm801_init(struct fm801_info *fm801) -{ - u_int32_t k1; - - /* reset codec */ - fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2); - DELAY(100000); - fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2); - DELAY(100000); - - fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2); - fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2); - fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2); - fm801_wr(fm801, 0x40,0x107f,2); /* enable legacy audio */ - - fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2); - - /* Unmask playback, record and mpu interrupts, mask the rest */ - k1 = fm801_rd((void *)fm801, FM_INTMASK,2); - fm801_wr(fm801, FM_INTMASK, - (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) | - FM_INTMASK_VOL,2); - fm801_wr(fm801, FM_INTSTATUS, - FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU | - FM_INTSTATUS_VOL,2); - - DPRINT("FM801 init Ok\n"); - return 0; -} - -static int -fm801_pci_attach(device_t dev) -{ - u_int32_t data; - struct ac97_info *codec = 0; - struct fm801_info *fm801; - int i; - int mapped = 0; - char status[SND_STATUSLEN]; - - if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - - bzero(fm801, sizeof(*fm801)); - fm801->type = pci_get_devid(dev); - - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); - - for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { - fm801->regid = PCIR_MAPS + i*4; - fm801->regtype = SYS_RES_MEMORY; - fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, - 0, ~0, 1, RF_ACTIVE); - if(!fm801->reg) - { - fm801->regtype = SYS_RES_IOPORT; - fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, - 0, ~0, 1, RF_ACTIVE); - } - - if(fm801->reg) { - fm801->st = rman_get_bustag(fm801->reg); - fm801->sh = rman_get_bushandle(fm801->reg); - mapped++; - } - } - - if (mapped == 0) { - device_printf(dev, "unable to map register space\n"); - goto oops; - } - - fm801_init(fm801); - - codec = ac97_create(dev, (void *)fm801, NULL, fm801_rdcd, fm801_wrcd); - if (codec == NULL) goto oops; - - if (mixer_init(dev, &ac97_mixer, codec) == -1) goto oops; - - fm801->irqid = 0; - fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!fm801->irq || - bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY, - fm801_intr, fm801, &fm801->ih)) { - device_printf(dev, "unable to map interrupt\n"); - goto oops; - } - - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &fm801->parent_dmat) != 0) { - device_printf(dev, "unable to create dma tag\n"); - goto oops; - } - - snprintf(status, 64, "at %s 0x%lx irq %ld", - (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", - rman_get_start(fm801->reg), rman_get_start(fm801->irq)); - -#define FM801_MAXPLAYCH 1 - if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; - pcm_addchan(dev, PCMDIR_PLAY, &fm801_chantemplate, fm801); - pcm_addchan(dev, PCMDIR_REC, &fm801_chantemplate, fm801); - pcm_setstatus(dev, status); - - return 0; - -oops: - if (codec) ac97_destroy(codec); - if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); - if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih); - if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); - if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat); - free(fm801, M_DEVBUF); - return ENXIO; -} - -static int -fm801_pci_detach(device_t dev) -{ - int r; - struct fm801_info *fm801; - - DPRINT("Forte Media FM801 detach\n"); - - r = pcm_unregister(dev); - if (r) - return r; - - fm801 = pcm_getdevinfo(dev); - bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); - bus_teardown_intr(dev, fm801->irq, fm801->ih); - bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); - bus_dma_tag_destroy(fm801->parent_dmat); - free(fm801, M_DEVBUF); - return 0; -} - -static int -fm801_pci_probe( device_t dev ) -{ - int id; - if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { - device_set_desc(dev, "Forte Media FM801 Audio Controller"); - return 0; - } -/* - if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) { - device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)"); - return ENXIO; - } -*/ - return ENXIO; -} - - - +/* -------------------------------------------------------------------- */ /* channel interface */ static void * -fm801ch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +fm801ch_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct fm801_info *fm801 = (struct fm801_info *)devinfo; struct fm801_chinfo *ch = (dir == PCMDIR_PLAY)? &fm801->pch : &fm801->rch; DPRINT("fm801ch_init, direction = %d\n", dir); ch->parent = fm801; ch->channel = c; ch->buffer = b; - ch->buffer->bufsize = FM801_BUFFSIZE; ch->dir = dir; - if( chn_allocbuf(ch->buffer, fm801->parent_dmat) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, FM801_BUFFSIZE) == -1) return NULL; return (void *)ch; } static int -fm801ch_setformat(void *data, u_int32_t format) +fm801ch_setformat(kobj_t obj, void *data, u_int32_t format) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format, (format & AFMT_STEREO)?"stereo":"mono", (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit", (format & AFMT_SIGNED)? "signed":"unsigned", (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" ); if(ch->dir == PCMDIR_PLAY) { fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0; fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; return 0; } if(ch->dir == PCMDIR_REC ) { fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0; fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; return 0; } return 0; } struct { int limit; int rate; } fm801_rates[11] = { { 6600, 5500 }, { 8750, 8000 }, { 10250, 9600 }, { 13200, 11025 }, { 17500, 16000 }, { 20500, 19200 }, { 26500, 22050 }, { 35000, 32000 }, { 41000, 38400 }, { 46000, 44100 }, { 48000, 48000 }, /* anything above -> 48000 */ }; static int -fm801ch_setspeed(void *data, u_int32_t speed) +fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; register int i; for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ; if(ch->dir == PCMDIR_PLAY) { fm801->pch.spd = fm801_rates[i].rate; fm801->play_shift = (i<<8); fm801->play_shift &= FM_PLAY_RATE_MASK; } if(ch->dir == PCMDIR_REC ) { fm801->rch.spd = fm801_rates[i].rate; fm801->rec_shift = (i<<8); fm801->rec_shift &= FM_REC_RATE_MASK; } ch->spd = fm801_rates[i].rate; return fm801_rates[i].rate; } static int -fm801ch_setblocksize(void *data, u_int32_t blocksize) +fm801ch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; if(ch->dir == PCMDIR_PLAY) { if(fm801->play_flip) return fm801->play_blksize; fm801->play_blksize = blocksize; } if(ch->dir == PCMDIR_REC) { if(fm801->rec_flip) return fm801->rec_blksize; fm801->rec_blksize = blocksize; } DPRINT("fm801ch_setblocksize %d (dir %d)\n",blocksize, ch->dir); return blocksize; } static int -fm801ch_trigger(void *data, int go) +fm801ch_trigger(kobj_t obj, void *data, int go) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; - u_int32_t baseaddr = vtophys(ch->buffer->buf); - snd_dbuf *b = ch->buffer; + u_int32_t baseaddr = vtophys(sndbuf_getbuf(ch->buffer)); u_int32_t k1; DPRINT("fm801ch_trigger go %d , ", go); - DPRINT("rp %d, rl %d, fp %d fl %d, dl %d, blksize=%d\n", - b->rp,b->rl, b->fp,b->fl, b->dl, b->blksz); if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) { return 0; } if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { fm801->play_start = baseaddr; fm801->play_nextblk = fm801->play_start + fm801->play_blksize; fm801->play_flip = 0; fm801_wr(fm801, FM_PLAY_DMALEN, fm801->play_blksize - 1, 2); fm801_wr(fm801, FM_PLAY_DMABUF1,fm801->play_start,4); fm801_wr(fm801, FM_PLAY_DMABUF2,fm801->play_nextblk,4); fm801_wr(fm801, FM_PLAY_CTL, FM_PLAY_START | FM_PLAY_STOPNOW | fm801->play_fmt | fm801->play_shift, 2 ); } else { fm801->play_flip = 0; k1 = fm801_rd(fm801, FM_PLAY_CTL,2); fm801_wr(fm801, FM_PLAY_CTL, (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) | FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST, 2 ); } } else if(ch->dir == PCMDIR_REC) { if (go == PCMTRIG_START) { fm801->rec_start = baseaddr; fm801->rec_nextblk = fm801->rec_start + fm801->rec_blksize; fm801->rec_flip = 0; fm801_wr(fm801, FM_REC_DMALEN, fm801->rec_blksize - 1, 2); fm801_wr(fm801, FM_REC_DMABUF1,fm801->rec_start,4); fm801_wr(fm801, FM_REC_DMABUF2,fm801->rec_nextblk,4); fm801_wr(fm801, FM_REC_CTL, FM_REC_START | FM_REC_STOPNOW | fm801->rec_fmt | fm801->rec_shift, 2 ); } else { fm801->rec_flip = 0; k1 = fm801_rd(fm801, FM_REC_CTL,2); fm801_wr(fm801, FM_REC_CTL, (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) | FM_REC_BUF1_LAST | FM_REC_BUF2_LAST, 2); } } return 0; } /* Almost ALSA copy */ static int -fm801ch_getptr(void *data) +fm801ch_getptr(kobj_t obj, void *data) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; int result = 0; - snd_dbuf *b = ch->buffer; if (ch->dir == PCMDIR_PLAY) { result = fm801_rd(fm801, (fm801->play_flip&1) ? FM_PLAY_DMABUF2:FM_PLAY_DMABUF1, 4) - fm801->play_start; } if (ch->dir == PCMDIR_REC) { result = fm801_rd(fm801, (fm801->rec_flip&1) ? FM_REC_DMABUF2:FM_REC_DMABUF1, 4) - fm801->rec_start; } - DPRINT("fm801ch_getptr:%d, rp %d, rl %d, fp %d fl %d\n", - result, b->rp,b->rl, b->fp,b->fl); - return result; } static pcmchan_caps * -fm801ch_getcaps(void *data) +fm801ch_getcaps(kobj_t obj, void *data) { return &fm801ch_caps; +} + +static kobj_method_t fm801ch_methods[] = { + KOBJMETHOD(channel_init, fm801ch_init), + KOBJMETHOD(channel_setformat, fm801ch_setformat), + KOBJMETHOD(channel_setspeed, fm801ch_setspeed), + KOBJMETHOD(channel_setblocksize, fm801ch_setblocksize), + KOBJMETHOD(channel_trigger, fm801ch_trigger), + KOBJMETHOD(channel_getptr, fm801ch_getptr), + KOBJMETHOD(channel_getcaps, fm801ch_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(fm801ch); + +/* -------------------------------------------------------------------- */ + +/* + * Init routine is taken from an original NetBSD driver + */ +static int +fm801_init(struct fm801_info *fm801) +{ + u_int32_t k1; + + /* reset codec */ + fm801_wr(fm801, FM_CODEC_CTL, 0x0020,2); + DELAY(100000); + fm801_wr(fm801, FM_CODEC_CTL, 0x0000,2); + DELAY(100000); + + fm801_wr(fm801, FM_PCM_VOLUME, 0x0808,2); + fm801_wr(fm801, FM_FM_VOLUME, 0x0808,2); + fm801_wr(fm801, FM_I2S_VOLUME, 0x0808,2); + fm801_wr(fm801, 0x40,0x107f,2); /* enable legacy audio */ + + fm801_wr((void *)fm801, FM_RECORD_SOURCE, 0x0000,2); + + /* Unmask playback, record and mpu interrupts, mask the rest */ + k1 = fm801_rd((void *)fm801, FM_INTMASK,2); + fm801_wr(fm801, FM_INTMASK, + (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) | + FM_INTMASK_VOL,2); + fm801_wr(fm801, FM_INTSTATUS, + FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU | + FM_INTSTATUS_VOL,2); + + DPRINT("FM801 init Ok\n"); + return 0; +} + +static int +fm801_pci_attach(device_t dev) +{ + u_int32_t data; + struct ac97_info *codec = 0; + struct fm801_info *fm801; + int i; + int mapped = 0; + char status[SND_STATUSLEN]; + + if ((fm801 = (struct fm801_info *)malloc(sizeof(*fm801),M_DEVBUF, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + + bzero(fm801, sizeof(*fm801)); + fm801->type = pci_get_devid(dev); + + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { + fm801->regid = PCIR_MAPS + i*4; + fm801->regtype = SYS_RES_MEMORY; + fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, + 0, ~0, 1, RF_ACTIVE); + if(!fm801->reg) + { + fm801->regtype = SYS_RES_IOPORT; + fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, + 0, ~0, 1, RF_ACTIVE); + } + + if(fm801->reg) { + fm801->st = rman_get_bustag(fm801->reg); + fm801->sh = rman_get_bushandle(fm801->reg); + mapped++; + } + } + + if (mapped == 0) { + device_printf(dev, "unable to map register space\n"); + goto oops; + } + + fm801_init(fm801); + + codec = AC97_CREATE(dev, fm801, fm801_ac97); + if (codec == NULL) goto oops; + + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto oops; + + fm801->irqid = 0; + fm801->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &fm801->irqid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!fm801->irq || + bus_setup_intr(dev, fm801->irq, INTR_TYPE_TTY, + fm801_intr, fm801, &fm801->ih)) { + device_printf(dev, "unable to map interrupt\n"); + goto oops; + } + + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/FM801_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, &fm801->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto oops; + } + + snprintf(status, 64, "at %s 0x%lx irq %ld", + (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", + rman_get_start(fm801->reg), rman_get_start(fm801->irq)); + +#define FM801_MAXPLAYCH 1 + if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; + pcm_addchan(dev, PCMDIR_PLAY, &fm801ch_class, fm801); + pcm_addchan(dev, PCMDIR_REC, &fm801ch_class, fm801); + pcm_setstatus(dev, status); + + return 0; + +oops: + if (codec) ac97_destroy(codec); + if (fm801->reg) bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); + if (fm801->ih) bus_teardown_intr(dev, fm801->irq, fm801->ih); + if (fm801->irq) bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); + if (fm801->parent_dmat) bus_dma_tag_destroy(fm801->parent_dmat); + free(fm801, M_DEVBUF); + return ENXIO; +} + +static int +fm801_pci_detach(device_t dev) +{ + int r; + struct fm801_info *fm801; + + DPRINT("Forte Media FM801 detach\n"); + + r = pcm_unregister(dev); + if (r) + return r; + + fm801 = pcm_getdevinfo(dev); + bus_release_resource(dev, fm801->regtype, fm801->regid, fm801->reg); + bus_teardown_intr(dev, fm801->irq, fm801->ih); + bus_release_resource(dev, SYS_RES_IRQ, fm801->irqid, fm801->irq); + bus_dma_tag_destroy(fm801->parent_dmat); + free(fm801, M_DEVBUF); + return 0; +} + +static int +fm801_pci_probe( device_t dev ) +{ + int id; + if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { + device_set_desc(dev, "Forte Media FM801 Audio Controller"); + return 0; + } +/* + if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) { + device_set_desc(dev, "Forte Media FM801 Joystick (Not Supported)"); + return ENXIO; + } +*/ + return ENXIO; } static device_method_t fm801_methods[] = { /* Device interface */ DEVMETHOD(device_probe, fm801_pci_probe), DEVMETHOD(device_attach, fm801_pci_attach), DEVMETHOD(device_detach, fm801_pci_detach), { 0, 0} }; static driver_t fm801_driver = { "pcm", fm801_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(fm801, pci, fm801_driver, pcm_devclass, 0, 0); Index: stable/4/sys/dev/sound/pci/maestro.c =================================================================== --- stable/4/sys/dev/sound/pci/maestro.c (revision 71948) +++ stable/4/sys/dev/sound/pci/maestro.c (revision 71949) @@ -1,1188 +1,1193 @@ /*- * Copyright (c) 2000 Taku YAMAMOTO * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: maestro.c,v 1.12 2000/09/06 03:32:34 taku Exp $ * $FreeBSD$ */ /* * Credits: * * Part of this code (especially in many magic numbers) was heavily inspired * by the Linux driver originally written by * Alan Cox , modified heavily by * Zach Brown . * * busdma()-ize and buffer size reduction were suggested by * Cameron Grant . * Also he showed me the way to use busdma() suite. * * Internal speaker problems on NEC VersaPro's and Dell Inspiron 7500 * were looked at by * Munehiro Matsuda , * who brought patches based on the Linux driver with some simplification. */ #include #include #include #include #include #define inline __inline /* * PCI IDs of supported chips: * * MAESTRO-1 0x01001285 * MAESTRO-2 0x1968125d * MAESTRO-2E 0x1978125d */ #define MAESTRO_1_PCI_ID 0x01001285 #define MAESTRO_2_PCI_ID 0x1968125d #define MAESTRO_2E_PCI_ID 0x1978125d #define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ #define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ #ifndef AGG_MAXPLAYCH # define AGG_MAXPLAYCH 4 #endif -#define AGG_BUFSIZ 4096 +#define AGG_BUFSIZ 0x4000 /* 0x1000, but gets underflows */ /* ----------------------------- * Data structures. */ struct agg_chinfo { struct agg_info *parent; pcm_channel *channel; snd_dbuf *buffer; bus_addr_t offset; u_int32_t blocksize; + u_int32_t speed; int dir; u_int num; u_int16_t aputype; u_int16_t wcreg_tpl; }; struct agg_info { device_t dev; struct resource *reg; int regid; bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; struct resource *irq; int irqid; void *ih; u_int8_t *stat; bus_addr_t baseaddr; struct ac97_info *codec; u_int playchns, active; struct agg_chinfo pch[AGG_MAXPLAYCH]; struct agg_chinfo rch; }; - -static u_int32_t agg_rdcodec(void *, int); -static void agg_wrcodec(void *, int, u_int32_t); - static inline void ringbus_setdest(struct agg_info*, int, int); static inline u_int16_t wp_rdreg(struct agg_info*, u_int16_t); static inline void wp_wrreg(struct agg_info*, u_int16_t, u_int16_t); static inline u_int16_t wp_rdapu(struct agg_info*, int, u_int16_t); static inline void wp_wrapu(struct agg_info*, int, u_int16_t, u_int16_t); static inline void wp_settimer(struct agg_info*, u_int); static inline void wp_starttimer(struct agg_info*); static inline void wp_stoptimer(struct agg_info*); static inline u_int16_t wc_rdreg(struct agg_info*, u_int16_t); static inline void wc_wrreg(struct agg_info*, u_int16_t, u_int16_t); static inline u_int16_t wc_rdchctl(struct agg_info*, int); static inline void wc_wrchctl(struct agg_info*, int, u_int16_t); static inline void agg_power(struct agg_info*, int); static void agg_init(struct agg_info*); -static u_int32_t agg_ac97_init(void *); static void aggch_start_dac(struct agg_chinfo*); static void aggch_stop_dac(struct agg_chinfo*); static inline void suppress_jitter(struct agg_chinfo*); static inline u_int calc_timer_freq(struct agg_chinfo*); static void set_timer(struct agg_info*); -static pcmchan_init_t aggch_init; -static pcmchan_free_t aggch_free; -static pcmchan_setformat_t aggch_setplayformat; -static pcmchan_setspeed_t aggch_setspeed; -static pcmchan_setblocksize_t aggch_setblocksize; -static pcmchan_trigger_t aggch_trigger; -static pcmchan_getptr_t aggch_getplayptr; -static pcmchan_getcaps_t aggch_getcaps; - static void agg_intr(void *); static int agg_probe(device_t); static int agg_attach(device_t); static int agg_detach(device_t); static int agg_suspend(device_t); static int agg_resume(device_t); static int agg_shutdown(device_t); static void *dma_malloc(struct agg_info*, u_int32_t, bus_addr_t*); static void dma_free(struct agg_info*, void *); /* ----------------------------- * Subsystems. */ /* Codec/Ringbus */ +/* -------------------------------------------------------------------- */ + static u_int32_t -agg_rdcodec(void *sc, int regno) +agg_ac97_init(kobj_t obj, void *sc) { struct agg_info *ess = sc; + + return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; +} + +static int +agg_rdcodec(kobj_t obj, void *sc, int regno) +{ + struct agg_info *ess = sc; unsigned t; /* We have to wait for a SAFE time to write addr/data */ for (t = 0; t < 20; t++) { if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) break; DELAY(2); /* 20.8us / 13 */ } if (t == 20) device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, CODEC_CMD_READ | regno); DELAY(21); /* AC97 cycle = 20.8usec */ /* Wait for data retrieve */ for (t = 0; t < 20; t++) { if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK) == CODEC_STAT_RW_DONE) break; DELAY(2); /* 20.8us / 13 */ } if (t == 20) /* Timed out, but perform dummy read. */ device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); return bus_space_read_2(ess->st, ess->sh, PORT_CODEC_REG); } -static void -agg_wrcodec(void *sc, int regno, u_int32_t data) +static int +agg_wrcodec(kobj_t obj, void *sc, int regno, u_int32_t data) { unsigned t; struct agg_info *ess = sc; /* We have to wait for a SAFE time to write addr/data */ for (t = 0; t < 20; t++) { if ((bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK) != CODEC_STAT_PROGLESS) break; DELAY(2); /* 20.8us / 13 */ } if (t == 20) { /* Timed out. Abort writing. */ device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); - return; + return -1; } bus_space_write_2(ess->st, ess->sh, PORT_CODEC_REG, data); bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno); + + return 0; } +static kobj_method_t agg_ac97_methods[] = { + KOBJMETHOD(ac97_init, agg_ac97_init), + KOBJMETHOD(ac97_read, agg_rdcodec), + KOBJMETHOD(ac97_write, agg_wrcodec), + { 0, 0 } +}; +AC97_DECLARE(agg_ac97); + +/* -------------------------------------------------------------------- */ + static inline void ringbus_setdest(struct agg_info *ess, int src, int dest) { u_int32_t data; data = bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL); data &= ~(0xfU << src); data |= (0xfU & dest) << src; bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); } /* Wave Processor */ static inline u_int16_t wp_rdreg(struct agg_info *ess, u_int16_t reg) { bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); return bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA); } static inline void wp_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) { bus_space_write_2(ess->st, ess->sh, PORT_DSP_INDEX, reg); bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); } static inline void apu_setindex(struct agg_info *ess, u_int16_t reg) { int t; wp_wrreg(ess, WPREG_CRAM_PTR, reg); /* Sometimes WP fails to set apu register index. */ for (t = 0; t < 1000; t++) { if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == reg) break; bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, reg); } if (t == 1000) device_printf(ess->dev, "apu_setindex() timed out.\n"); } static inline u_int16_t wp_rdapu(struct agg_info *ess, int ch, u_int16_t reg) { u_int16_t ret; apu_setindex(ess, ((unsigned)ch << 4) + reg); ret = wp_rdreg(ess, WPREG_DATA_PORT); return ret; } static inline void wp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data) { int t; apu_setindex(ess, ((unsigned)ch << 4) + reg); wp_wrreg(ess, WPREG_DATA_PORT, data); for (t = 0; t < 1000; t++) { if (bus_space_read_2(ess->st, ess->sh, PORT_DSP_DATA) == data) break; bus_space_write_2(ess->st, ess->sh, PORT_DSP_DATA, data); } if (t == 1000) device_printf(ess->dev, "wp_wrapu() timed out.\n"); } static inline void wp_settimer(struct agg_info *ess, u_int freq) { u_int clock = 48000 << 2; u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; RANGE(divide, 4, 32 << 8); for (; divide > 32 << 1; divide >>= 1) prescale++; divide = (divide + 1) >> 1; for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) prescale++; wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); wp_wrreg(ess, WPREG_TIMER_FREQ, (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); } static inline void wp_starttimer(struct agg_info *ess) { wp_wrreg(ess, WPREG_TIMER_START, 1); } static inline void wp_stoptimer(struct agg_info *ess) { wp_wrreg(ess, WPREG_TIMER_START, 0); bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); } /* WaveCache */ static inline u_int16_t wc_rdreg(struct agg_info *ess, u_int16_t reg) { bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); return bus_space_read_2(ess->st, ess->sh, PORT_WAVCACHE_DATA); } static inline void wc_wrreg(struct agg_info *ess, u_int16_t reg, u_int16_t data) { bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_INDEX, reg); bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_DATA, data); } static inline u_int16_t wc_rdchctl(struct agg_info *ess, int ch) { return wc_rdreg(ess, ch << 3); } static inline void wc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) { wc_wrreg(ess, ch << 3, data); } /* Power management */ static inline void agg_power(struct agg_info *ess, int status) { u_int8_t data; data = pci_read_config(ess->dev, CONF_PM_PTR, 1); if (pci_read_config(ess->dev, data, 1) == PPMI_CID) pci_write_config(ess->dev, data + PM_CTRL, status, 1); } /* ----------------------------- * Controller. */ static inline void agg_initcodec(struct agg_info* ess) { u_int16_t data; if (bus_space_read_4(ess->st, ess->sh, PORT_RINGBUS_CTRL) & RINGBUS_CTRL_ACLINK_ENABLED) { bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); DELAY(104); /* 20.8us * (4 + 1) */ } /* XXX - 2nd codec should be looked at. */ bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET); DELAY(2); bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED); DELAY(21); - agg_rdcodec(ess, 0); + agg_rdcodec(NULL, ess, 0); if (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK) { bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); DELAY(21); /* Try cold reset. */ device_printf(ess->dev, "will perform cold reset.\n"); data = bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR); if (pci_read_config(ess->dev, 0x58, 2) & 1) data |= 0x10; data |= 0x009 & ~bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DATA); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0xff6); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data | 0x009); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x000); DELAY(2); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); DELAY(1); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x009); DELAY(500000); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, data); DELAY(84); /* 20.8us * 4 */ bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED); DELAY(21); } } static void agg_init(struct agg_info* ess) { u_int32_t data; /* Setup PCI config registers. */ /* Disable all legacy emulations. */ data = pci_read_config(ess->dev, CONF_LEGACY, 2); data |= LEGACY_DISABLED; pci_write_config(ess->dev, CONF_LEGACY, data, 2); /* Disconnect from CHI. (Makes Dell inspiron 7500 work?) * Enable posted write. * Prefer PCI timing rather than that of ISA. * Don't swap L/R. */ data = pci_read_config(ess->dev, CONF_MAESTRO, 4); data |= MAESTRO_CHIBUS | MAESTRO_POSTEDWRITE | MAESTRO_DMA_PCITIMING; data &= ~MAESTRO_SWAP_LR; pci_write_config(ess->dev, CONF_MAESTRO, data, 4); /* Reset direct sound. */ bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET); DELAY(10000); /* XXX - too long? */ bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); DELAY(10000); - /* Enable direct sound interruption. */ + /* Enable direct sound interruption and hardware volume control. */ bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, - HOSTINT_CTRL_DSOUND_INT_ENABLED); + HOSTINT_CTRL_DSOUND_INT_ENABLED | HOSTINT_CTRL_HWVOL_ENABLED); /* Setup Wave Processor. */ /* Enable WaveCache, set DMA base address. */ wp_wrreg(ess, WPREG_WAVE_ROMRAM, WP_WAVE_VIRTUAL_ENABLED | WP_WAVE_DRAM_ENABLED); bus_space_write_2(ess->st, ess->sh, PORT_WAVCACHE_CTRL, WAVCACHE_ENABLED | WAVCACHE_WTSIZE_4MB); for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT); /* Setup Codec/Ringbus. */ agg_initcodec(ess); bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED); wp_wrreg(ess, WPREG_BASE, 0x8500); /* Parallel I/O */ ringbus_setdest(ess, RINGBUS_SRC_ADC, RINGBUS_DEST_STEREO | RINGBUS_DEST_DSOUND_IN); ringbus_setdest(ess, RINGBUS_SRC_DSOUND, RINGBUS_DEST_STEREO | RINGBUS_DEST_DAC); /* Setup ASSP. Needed for Dell Inspiron 7500? */ bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_B, 0x00); bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_A, 0x03); bus_space_write_1(ess->st, ess->sh, PORT_ASSP_CTRL_C, 0x00); /* * Setup GPIO. * There seems to be speciality with NEC systems. */ switch (pci_get_subvendor(ess->dev) | (pci_get_subdevice(ess->dev) << 16)) { case NEC_SUBID1: case NEC_SUBID2: /* Matthew Braithwaite reported that * NEC Versa LX doesn't need GPIO operation. */ bus_space_write_2(ess->st, ess->sh, PORT_GPIO_MASK, 0x9ff); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DIR, bus_space_read_2(ess->st, ess->sh, PORT_GPIO_DIR) | 0x600); bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x200); break; } } /* Channel controller. */ static void aggch_start_dac(struct agg_chinfo *ch) { u_int wpwa = APU_USE_SYSMEM | (ch->offset >> 9); u_int size = AGG_BUFSIZ >> 1; - u_int speed = ch->channel->speed; + u_int speed = ch->speed; u_int offset = ch->offset >> 1; - u_int cp = ch->buffer->rp >> 1; + u_int cp = 0; u_int16_t apuch = ch->num << 1; u_int dv; int pan = 0; switch (ch->aputype) { case APUTYPE_16BITSTEREO: wpwa >>= 1; size >>= 1; offset >>= 1; cp >>= 1; /* FALLTHROUGH */ case APUTYPE_8BITSTEREO: pan = 8; apuch++; break; case APUTYPE_8BITLINEAR: speed >>= 1; break; } dv = (((speed % 48000) << 16) + 24000) / 48000 + ((speed / 48000) << 16); do { wp_wrapu(ch->parent, apuch, APUREG_WAVESPACE, wpwa & 0xff00); wp_wrapu(ch->parent, apuch, APUREG_CURPTR, offset + cp); wp_wrapu(ch->parent, apuch, APUREG_ENDPTR, offset + size); wp_wrapu(ch->parent, apuch, APUREG_LOOPLEN, size); wp_wrapu(ch->parent, apuch, APUREG_AMPLITUDE, 0xe800); wp_wrapu(ch->parent, apuch, APUREG_POSITION, 0x8f00 | (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT) | ((PAN_FRONT + pan) << APU_PAN_SHIFT)); wp_wrapu(ch->parent, apuch, APUREG_FREQ_LOBYTE, APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); wp_wrapu(ch->parent, apuch, APUREG_FREQ_HIWORD, dv >> 8); if (ch->aputype == APUTYPE_16BITSTEREO) wpwa |= APU_STEREO >> 1; pan = -pan; } while (pan < 0 && apuch--); wc_wrchctl(ch->parent, apuch, ch->wcreg_tpl); wc_wrchctl(ch->parent, apuch + 1, ch->wcreg_tpl); wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); } static void aggch_stop_dac(struct agg_chinfo *ch) { wp_wrapu(ch->parent, (ch->num << 1), APUREG_APUTYPE, APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); wp_wrapu(ch->parent, (ch->num << 1) + 1, APUREG_APUTYPE, APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); } /* * Stereo jitter suppressor. * Sometimes playback pointers differ in stereo-paired channels. * Calling this routine within intr fixes the problem. */ static inline void suppress_jitter(struct agg_chinfo *ch) { if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) { int cp, diff, halfsize = AGG_BUFSIZ >> 2; if (ch->aputype == APUTYPE_16BITSTEREO) halfsize >>= 1; cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); diff = wp_rdapu(ch->parent, (ch->num << 1) + 1, APUREG_CURPTR); diff -= cp; if (diff >> 1 && diff > -halfsize && diff < halfsize) bus_space_write_2(ch->parent->st, ch->parent->sh, PORT_DSP_DATA, cp); } } static inline u_int calc_timer_freq(struct agg_chinfo *ch) { u_int ss = 2; if (ch->aputype == APUTYPE_16BITSTEREO) ss <<= 1; if (ch->aputype == APUTYPE_8BITLINEAR) ss >>= 1; - return (ch->channel->speed * ss + ch->blocksize - 1) / ch->blocksize; + return (ch->speed * ss) / ch->blocksize; } static void set_timer(struct agg_info *ess) { int i; u_int freq = 0; for (i = 0; i < ess->playchns; i++) if ((ess->active & (1 << i)) && (freq < calc_timer_freq(ess->pch + i))) freq = calc_timer_freq(ess->pch + i); wp_settimer(ess, freq); } /* ----------------------------- * Newpcm glue. */ -static u_int32_t -agg_ac97_init(void *sc) -{ - struct agg_info *ess = sc; - - return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; -} - static void * -aggch_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +aggch_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct agg_info *ess = devinfo; struct agg_chinfo *ch; bus_addr_t physaddr; + void *p; ch = (dir == PCMDIR_PLAY)? ess->pch + ess->playchns : &ess->rch; ch->parent = ess; ch->channel = c; ch->buffer = b; ch->num = ess->playchns; ch->dir = dir; - b->buf = dma_malloc(ess, AGG_BUFSIZ, &physaddr); - if (b->buf == NULL) + p = dma_malloc(ess, AGG_BUFSIZ, &physaddr); + if (p == NULL) return NULL; + sndbuf_setup(b, p, AGG_BUFSIZ); ch->offset = physaddr - ess->baseaddr; if (physaddr < ess->baseaddr || ch->offset > WPWA_MAXADDR) { device_printf(ess->dev, "offset %#x exceeds limit. ", ch->offset); dma_free(ess, b->buf); b->buf = NULL; return NULL; } - b->bufsize = AGG_BUFSIZ; ch->wcreg_tpl = (physaddr - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; if (dir == PCMDIR_PLAY) { ess->playchns++; if (bootverbose) device_printf(ess->dev, "pch[%d].offset = %#x\n", ch->num, ch->offset); } else if (bootverbose) device_printf(ess->dev, "rch.offset = %#x\n", ch->offset); return ch; } static int -aggch_free(void *data) +aggch_free(kobj_t obj, void *data) { struct agg_chinfo *ch = data; struct agg_info *ess = ch->parent; /* free up buffer - called after channel stopped */ - dma_free(ess, ch->buffer->buf); + dma_free(ess, sndbuf_getbuf(ch->buffer)); /* return 0 if ok */ return 0; } static int -aggch_setplayformat(void *data, u_int32_t format) +aggch_setplayformat(kobj_t obj, void *data, u_int32_t format) { struct agg_chinfo *ch = data; u_int16_t wcreg_tpl; u_int16_t aputype = APUTYPE_16BITLINEAR; wcreg_tpl = ch->wcreg_tpl & WAVCACHE_CHCTL_ADDRTAG_MASK; if (format & AFMT_STEREO) { wcreg_tpl |= WAVCACHE_CHCTL_STEREO; aputype += 1; } if (format & AFMT_U8 || format & AFMT_S8) { aputype += 2; if (format & AFMT_U8) wcreg_tpl |= WAVCACHE_CHCTL_U8; } if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) { format &= ~AFMT_BIGENDIAN & ~AFMT_U16_LE; format |= AFMT_S16_LE; } ch->wcreg_tpl = wcreg_tpl; ch->aputype = aputype; return format; } static int -aggch_setspeed(void *data, u_int32_t speed) +aggch_setspeed(kobj_t obj, void *data, u_int32_t speed) { - return speed; + struct agg_chinfo *ch = data; + + ch->speed = speed; + return ch->speed; } static int -aggch_setblocksize(void *data, u_int32_t blocksize) +aggch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { return ((struct agg_chinfo*)data)->blocksize = blocksize; } static int -aggch_trigger(void *data, int go) +aggch_trigger(kobj_t obj, void *data, int go) { struct agg_chinfo *ch = data; switch (go) { case PCMTRIG_EMLDMAWR: return 0; case PCMTRIG_START: ch->parent->active |= (1 << ch->num); if (ch->dir == PCMDIR_PLAY) aggch_start_dac(ch); #if 0 /* XXX - RECORDING */ else aggch_start_adc(ch); #endif break; case PCMTRIG_ABORT: case PCMTRIG_STOP: ch->parent->active &= ~(1 << ch->num); if (ch->dir == PCMDIR_PLAY) aggch_stop_dac(ch); #if 0 /* XXX - RECORDING */ else aggch_stop_adc(ch); #endif break; } if (ch->parent->active) { set_timer(ch->parent); wp_starttimer(ch->parent); } else wp_stoptimer(ch->parent); return 0; } static int -aggch_getplayptr(void *data) +aggch_getplayptr(kobj_t obj, void *data) { struct agg_chinfo *ch = data; u_int cp; cp = wp_rdapu(ch->parent, (ch->num << 1), APUREG_CURPTR); if (ch->aputype == APUTYPE_16BITSTEREO) cp = (0xffff << 2) & ((cp << 2) - ch->offset); else cp = (0xffff << 1) & ((cp << 1) - ch->offset); return cp; } static pcmchan_caps * -aggch_getcaps(void *data) +aggch_getcaps(kobj_t obj, void *data) { static u_int32_t playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps playcaps = {2000, 96000, playfmt, 0}; static u_int32_t recfmt[] = { AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps reccaps = {4000, 48000, recfmt, 0}; return (((struct agg_chinfo*)data)->dir == PCMDIR_PLAY)? &playcaps : &reccaps; } +static kobj_method_t aggch_methods[] = { + KOBJMETHOD(channel_init, aggch_init), + KOBJMETHOD(channel_free, aggch_free), + KOBJMETHOD(channel_setformat, aggch_setplayformat), + KOBJMETHOD(channel_setspeed, aggch_setspeed), + KOBJMETHOD(channel_setblocksize, aggch_setblocksize), + KOBJMETHOD(channel_trigger, aggch_trigger), + KOBJMETHOD(channel_getptr, aggch_getplayptr), + KOBJMETHOD(channel_getcaps, aggch_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(aggch); /* ----------------------------- * Bus space. */ static void agg_intr(void *sc) { struct agg_info* ess = sc; u_int16_t status; int i; status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT); if (!status) return; /* Acknowledge all. */ bus_space_write_2(ess->st, ess->sh, PORT_INT_STAT, 1); - bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0); -#if 0 /* XXX - HWVOL */ + bus_space_write_1(ess->st, ess->sh, PORT_HOSTINT_STAT, 0xff); + if (status & HOSTINT_STAT_HWVOL) { - u_int delta; - delta = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER) - - 0x88; - if (delta & 0x11) - mixer_set(device_get_softc(ess->dev), - SOUND_MIXER_VOLUME, 0); - else { - mixer_set(device_get_softc(ess->dev), - SOUND_MIXER_VOLUME, - mixer_get(device_get_softc(ess->dev), - SOUND_MIXER_VOLUME) - + ((delta >> 5) & 0x7) - 4 - + ((delta << 7) & 0x700) - 0x400); + u_int event; + + event = bus_space_read_1(ess->st, ess->sh, PORT_HWVOL_MASTER); + switch (event) { + case HWVOL_MUTE: + mixer_hwvol_mute(ess->dev); + break; + case HWVOL_UP: + mixer_hwvol_step(ess->dev, 1, 1); + break; + case HWVOL_DOWN: + mixer_hwvol_step(ess->dev, -1, -1); + break; + case HWVOL_NOP: + break; + default: + device_printf(ess->dev, "%s: unknown HWVOL event 0x%x\n", + device_get_nameunit(ess->dev), event); } - bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, 0x88); + bus_space_write_1(ess->st, ess->sh, PORT_HWVOL_MASTER, + HWVOL_NOP); } -#endif /* XXX - HWVOL */ for (i = 0; i < ess->playchns; i++) if (ess->active & (1 << i)) { suppress_jitter(ess->pch + i); chn_intr(ess->pch[i].channel); } #if 0 /* XXX - RECORDING */ if (ess->active & (1 << i)) chn_intr(ess->rch.channel); #endif } static void setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { bus_addr_t *phys = arg; *phys = error? 0 : segs->ds_addr; if (bootverbose) { printf("setmap (%lx, %lx), nseg=%d, error=%d\n", (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, nseg, error); } } static void * dma_malloc(struct agg_info *sc, u_int32_t sz, bus_addr_t *phys) { void *buf; bus_dmamap_t map; if (bus_dmamem_alloc(sc->parent_dmat, &buf, BUS_DMA_NOWAIT, &map)) return NULL; if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, setmap, phys, 0) || !*phys) { bus_dmamem_free(sc->parent_dmat, buf, map); return NULL; } return buf; } static void dma_free(struct agg_info *sc, void *buf) { bus_dmamem_free(sc->parent_dmat, buf, NULL); } static int agg_probe(device_t dev) { char *s = NULL; switch (pci_get_devid(dev)) { case MAESTRO_1_PCI_ID: s = "ESS Technology Maestro-1"; break; case MAESTRO_2_PCI_ID: s = "ESS Technology Maestro-2"; break; case MAESTRO_2E_PCI_ID: s = "ESS Technology Maestro-2E"; break; } if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { device_set_desc(dev, s); return 0; } return ENXIO; } static int agg_attach(device_t dev) { struct agg_info *ess = NULL; u_int32_t data; int mapped = 0; int regid = PCIR_MAPS; struct resource *reg = NULL; struct ac97_info *codec = NULL; int irqid = 0; struct resource *irq = NULL; void *ih = NULL; char status[SND_STATUSLEN]; - static pcm_channel agg_pchtpl = { - aggch_init, - NULL, /* setdir */ - aggch_setplayformat, - aggch_setspeed, - aggch_setblocksize, - aggch_trigger, - aggch_getplayptr, - aggch_getcaps, - aggch_free, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ - }; if ((ess = malloc(sizeof *ess, M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(ess, sizeof *ess); ess->dev = dev; if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/1 << WAVCACHE_BASEADDR_SHIFT, /*boundary*/WPWA_MAXADDR + 1, /*lowaddr*/MAESTRO_MAXADDR, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/AGG_BUFSIZ * 2, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &ess->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } ess->stat = dma_malloc(ess, AGG_BUFSIZ, &ess->baseaddr); if (ess->stat == NULL) { device_printf(dev, "cannot allocate status buffer\n"); goto bad; } if (bootverbose) device_printf(dev, "Maestro DMA base: %#x\n", ess->baseaddr); agg_power(ess, PPMI_D0); DELAY(100000); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); if (data & PCIM_CMD_PORTEN) { reg = bus_alloc_resource(dev, SYS_RES_IOPORT, ®id, 0, BUS_SPACE_UNRESTRICTED, 256, RF_ACTIVE); if (reg != NULL) { ess->reg = reg; ess->regid = regid; ess->st = rman_get_bustag(reg); ess->sh = rman_get_bushandle(reg); mapped++; } } if (mapped == 0) { device_printf(dev, "unable to map register space\n"); goto bad; } agg_init(ess); - if (agg_rdcodec(ess, 0) == 0x80) { + if (agg_rdcodec(NULL, ess, 0) == 0x80) { device_printf(dev, "PT101 codec detected!\n"); goto bad; } - codec = ac97_create(dev, ess, agg_ac97_init, agg_rdcodec, agg_wrcodec); + codec = AC97_CREATE(dev, ess, agg_ac97); if (codec == NULL) goto bad; - if (mixer_init(dev, &ac97_mixer, codec) == -1) + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; ess->codec = codec; irq = bus_alloc_resource(dev, SYS_RES_IRQ, &irqid, 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE | RF_SHAREABLE); if (irq == NULL || bus_setup_intr(dev, irq, INTR_TYPE_TTY, agg_intr, ess, &ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } ess->irq = irq; ess->irqid = irqid; ess->ih = ih; snprintf(status, SND_STATUSLEN, "at I/O port 0x%lx irq %ld", rman_get_start(reg), rman_get_start(irq)); if (pcm_register(dev, ess, AGG_MAXPLAYCH, 1)) goto bad; + mixer_hwvol_init(dev); for (data = 0; data < AGG_MAXPLAYCH; data++) - pcm_addchan(dev, PCMDIR_PLAY, &agg_pchtpl, ess); + pcm_addchan(dev, PCMDIR_PLAY, &aggch_class, ess); #if 0 /* XXX - RECORDING */ - pcm_addchan(dev, PCMDIR_REC, &agg_rchtpl, ess); + pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); #endif pcm_setstatus(dev, status); return 0; bad: if (codec != NULL) ac97_destroy(codec); if (ih != NULL) bus_teardown_intr(dev, irq, ih); if (irq != NULL) bus_release_resource(dev, SYS_RES_IRQ, irqid, irq); if (reg != NULL) bus_release_resource(dev, SYS_RES_IOPORT, regid, reg); if (ess != NULL) { agg_power(ess, PPMI_D3); if (ess->stat != NULL) dma_free(ess, ess->stat); if (ess->parent_dmat != NULL) bus_dma_tag_destroy(ess->parent_dmat); free(ess, M_DEVBUF); } return ENXIO; } static int agg_detach(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); int r; r = pcm_unregister(dev); if (r) return r; ess = pcm_getdevinfo(dev); dma_free(ess, ess->stat); /* Power down everything except clock and vref. */ - agg_wrcodec(ess, AC97_REG_POWER, 0xd700); + agg_wrcodec(NULL, ess, AC97_REG_POWER, 0xd700); DELAY(20); bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); agg_power(ess, PPMI_D3); bus_teardown_intr(dev, ess->irq, ess->ih); bus_release_resource(dev, SYS_RES_IRQ, ess->irqid, ess->irq); bus_release_resource(dev, SYS_RES_IOPORT, ess->regid, ess->reg); bus_dma_tag_destroy(ess->parent_dmat); free(ess, M_DEVBUF); return 0; } static int agg_suspend(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); int i, x; x = spltty(); wp_stoptimer(ess); bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); for (i = 0; i < ess->playchns; i++) aggch_stop_dac(ess->pch + i); #if 0 /* XXX - RECORDING */ aggch_stop_adc(&ess->rch); #endif splx(x); /* Power down everything except clock. */ - agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); + agg_wrcodec(NULL, ess, AC97_REG_POWER, 0xdf00); DELAY(20); bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, 0); DELAY(1); agg_power(ess, PPMI_D3); return 0; } static int agg_resume(device_t dev) { int i, x; struct agg_info *ess = pcm_getdevinfo(dev); agg_power(ess, PPMI_D0); DELAY(100000); agg_init(ess); if (mixer_reinit(dev)) { device_printf(dev, "unable to reinitialize the mixer\n"); return ENXIO; } x = spltty(); for (i = 0; i < ess->playchns; i++) if (ess->active & (1 << i)) aggch_start_dac(ess->pch + i); #if 0 /* XXX - RECORDING */ if (ess->active & (1 << i)) aggch_start_adc(&ess->rch); #endif if (ess->active) { set_timer(ess); wp_starttimer(ess); } splx(x); return 0; } static int agg_shutdown(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); int i; wp_stoptimer(ess); bus_space_write_2(ess->st, ess->sh, PORT_HOSTINT_CTRL, 0); for (i = 0; i < ess->playchns; i++) aggch_stop_dac(ess->pch + i); #if 0 /* XXX - RECORDING */ aggch_stop_adc(&ess->rch); #endif return 0; } static device_method_t agg_methods[] = { DEVMETHOD(device_probe, agg_probe), DEVMETHOD(device_attach, agg_attach), DEVMETHOD(device_detach, agg_detach), DEVMETHOD(device_suspend, agg_suspend), DEVMETHOD(device_resume, agg_resume), DEVMETHOD(device_shutdown, agg_shutdown), { 0, 0 } }; static driver_t agg_driver = { "pcm", agg_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_maestro, pci, agg_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_maestro, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_maestro, 1); Index: stable/4/sys/dev/sound/pci/maestro_reg.h =================================================================== --- stable/4/sys/dev/sound/pci/maestro_reg.h (revision 71948) +++ stable/4/sys/dev/sound/pci/maestro_reg.h (revision 71949) @@ -1,345 +1,349 @@ /*- * Copyright (c) 1999-2000 Taku YAMAMOTO * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: maestro_reg.h,v 1.10 2000/08/29 17:27:29 taku Exp $ * $FreeBSD$ */ #ifndef MAESTRO_REG_H_INCLUDED #define MAESTRO_REG_H_INCLUDED /* ----------------------------- * PCI config registers */ /* Legacy emulation */ #define CONF_LEGACY 0x40 #define LEGACY_DISABLED 0x8000 /* Chip configurations */ #define CONF_MAESTRO 0x50 #define MAESTRO_CHIBUS 0x00100000 #define MAESTRO_POSTEDWRITE 0x00000080 #define MAESTRO_DMA_PCITIMING 0x00000040 #define MAESTRO_SWAP_LR 0x00000010 /* ACPI configurations */ #define CONF_ACPI_STOPCLOCK 0x54 #define ACPI_PART_2ndC_CLOCK 15 #define ACPI_PART_CODEC_CLOCK 14 #define ACPI_PART_978 13 /* Docking station or something */ #define ACPI_PART_SPDIF 12 #define ACPI_PART_GLUE 11 /* What? */ #define ACPI_PART_DAA 10 #define ACPI_PART_PCI_IF 9 #define ACPI_PART_HW_VOL 8 #define ACPI_PART_GPIO 7 #define ACPI_PART_ASSP 6 #define ACPI_PART_SB 5 #define ACPI_PART_FM 4 #define ACPI_PART_RINGBUS 3 #define ACPI_PART_MIDI 2 #define ACPI_PART_GAME_PORT 1 #define ACPI_PART_WP 0 /* Power management */ #define CONF_PM_PTR 0x34 /* BYTE R */ #define PM_CID 0 /* BYTE R */ #define PPMI_CID 1 #define PM_CTRL 4 /* BYTE RW */ #define PPMI_D0 0 /* Full power */ #define PPMI_D1 1 /* Medium power */ #define PPMI_D2 2 /* Low power */ #define PPMI_D3 3 /* Turned off */ /* ----------------------------- * I/O ports */ /* Direct Sound Processor (aka WP) */ #define PORT_DSP_DATA 0x00 /* WORD RW */ #define PORT_DSP_INDEX 0x02 /* WORD RW */ #define PORT_INT_STAT 0x04 /* WORD RW */ #define PORT_SAMPLE_CNT 0x06 /* WORD RO */ /* WaveCache */ #define PORT_WAVCACHE_INDEX 0x10 /* WORD RW */ #define PORT_WAVCACHE_DATA 0x12 /* WORD RW */ #define WAVCACHE_PCMBAR 0x1fc #define WAVCACHE_WTBAR 0x1f0 #define WAVCACHE_BASEADDR_SHIFT 12 #define WAVCACHE_CHCTL_ADDRTAG_MASK 0xfff8 #define WAVCACHE_CHCTL_U8 0x0004 #define WAVCACHE_CHCTL_STEREO 0x0002 #define WAVCACHE_CHCTL_DECREMENTAL 0x0001 #define PORT_WAVCACHE_CTRL 0x14 /* WORD RW */ #define WAVCACHE_EXTRA_CH_ENABLED 0x0200 #define WAVCACHE_ENABLED 0x0100 #define WAVCACHE_CH_60_ENABLED 0x0080 #define WAVCACHE_WTSIZE_MASK 0x0060 #define WAVCACHE_WTSIZE_1MB 0x0000 #define WAVCACHE_WTSIZE_2MB 0x0020 #define WAVCACHE_WTSIZE_4MB 0x0040 #define WAVCACHE_WTSIZE_8MB 0x0060 #define WAVCACHE_SGC_MASK 0x000c #define WAVCACHE_SGC_DISABLED 0x0000 #define WAVCACHE_SGC_40_47 0x0004 #define WAVCACHE_SGC_32_47 0x0008 #define WAVCACHE_TESTMODE 0x0001 /* Host Interruption */ #define PORT_HOSTINT_CTRL 0x18 /* WORD RW */ #define HOSTINT_CTRL_SOFT_RESET 0x8000 #define HOSTINT_CTRL_DSOUND_RESET 0x4000 #define HOSTINT_CTRL_HW_VOL_TO_PME 0x0400 #define HOSTINT_CTRL_CLKRUN_ENABLED 0x0100 #define HOSTINT_CTRL_HWVOL_ENABLED 0x0040 #define HOSTINT_CTRL_ASSP_INT_ENABLED 0x0010 #define HOSTINT_CTRL_ISDN_INT_ENABLED 0x0008 #define HOSTINT_CTRL_DSOUND_INT_ENABLED 0x0004 #define HOSTINT_CTRL_MPU401_INT_ENABLED 0x0002 #define HOSTINT_CTRL_SB_INT_ENABLED 0x0001 #define PORT_HOSTINT_STAT 0x1a /* BYTE RW */ #define HOSTINT_STAT_HWVOL 0x40 #define HOSTINT_STAT_ASSP 0x10 #define HOSTINT_STAT_ISDN 0x08 #define HOSTINT_STAT_DSOUND 0x04 #define HOSTINT_STAT_MPU401 0x02 #define HOSTINT_STAT_SB 0x01 /* Hardware volume */ #define PORT_HWVOL_VOICE_SHADOW 0x1c /* BYTE RW */ #define PORT_HWVOL_VOICE 0x1d /* BYTE RW */ #define PORT_HWVOL_MASTER_SHADOW 0x1e /* BYTE RW */ #define PORT_HWVOL_MASTER 0x1f /* BYTE RW */ +#define HWVOL_NOP 0x88 +#define HWVOL_MUTE 0x99 +#define HWVOL_UP 0xaa +#define HWVOL_DOWN 0x66 /* CODEC */ #define PORT_CODEC_CMD 0x30 /* BYTE W */ #define CODEC_CMD_READ 0x80 #define CODEC_CMD_WRITE 0x00 #define CODEC_CMD_ADDR_MASK 0x7f #define PORT_CODEC_STAT 0x30 /* BYTE R */ #define CODEC_STAT_MASK 0x01 #define CODEC_STAT_RW_DONE 0x00 #define CODEC_STAT_PROGLESS 0x01 #define PORT_CODEC_REG 0x32 /* WORD RW */ /* Ring bus control */ #define PORT_RINGBUS_CTRL 0x34 /* DWORD RW */ #define RINGBUS_CTRL_I2S_ENABLED 0x80000000 #define RINGBUS_CTRL_RINGBUS_ENABLED 0x20000000 #define RINGBUS_CTRL_ACLINK_ENABLED 0x10000000 #define RINGBUS_CTRL_AC97_SWRESET 0x08000000 #define RINGBUS_CTRL_IODMA_PLAYBACK_ENABLED 0x04000000 #define RINGBUS_CTRL_IODMA_RECORD_ENABLED 0x02000000 #define RINGBUS_SRC_MIC 20 #define RINGBUS_SRC_I2S 16 #define RINGBUS_SRC_ADC 12 #define RINGBUS_SRC_MODEM 8 #define RINGBUS_SRC_DSOUND 4 #define RINGBUS_SRC_ASSP 0 #define RINGBUS_DEST_MONORAL 000 #define RINGBUS_DEST_STEREO 010 #define RINGBUS_DEST_NONE 0 #define RINGBUS_DEST_DAC 1 #define RINGBUS_DEST_MODEM_IN 2 #define RINGBUS_DEST_RESERVED3 3 #define RINGBUS_DEST_DSOUND_IN 4 #define RINGBUS_DEST_ASSP_IN 5 /* General Purpose I/O */ #define PORT_GPIO_DATA 0x60 /* WORD RW */ #define PORT_GPIO_MASK 0x64 /* WORD RW */ #define PORT_GPIO_DIR 0x68 /* WORD RW */ /* Application Specific Signal Processor */ #define PORT_ASSP_MEM_INDEX 0x80 /* DWORD RW */ #define PORT_ASSP_MEM_DATA 0x84 /* WORD RW */ #define PORT_ASSP_CTRL_A 0xa2 /* BYTE RW */ #define PORT_ASSP_CTRL_B 0xa4 /* BYTE RW */ #define PORT_ASSP_CTRL_C 0xa6 /* BYTE RW */ #define PORT_ASSP_HOST_WR_INDEX 0xa8 /* BYTE W */ #define PORT_ASSP_HOST_WR_DATA 0xaa /* BYTE RW */ #define PORT_ASSP_INT_STAT 0xac /* BYTE RW */ /* ----------------------------- * Wave Processor Indexed Data Registers. */ #define WPREG_DATA_PORT 0 #define WPREG_CRAM_PTR 1 #define WPREG_CRAM_DATA 2 #define WPREG_WAVE_DATA 3 #define WPREG_WAVE_PTR_LOW 4 #define WPREG_WAVE_PTR_HIGH 5 #define WPREG_TIMER_FREQ 6 #define WP_TIMER_FREQ_PRESCALE_MASK 0x00e0 /* actual - 9 */ #define WP_TIMER_FREQ_PRESCALE_SHIFT 5 #define WP_TIMER_FREQ_DIVIDE_MASK 0x001f #define WP_TIMER_FREQ_DIVIDE_SHIFT 0 #define WPREG_WAVE_ROMRAM 7 #define WP_WAVE_VIRTUAL_ENABLED 0x0400 #define WP_WAVE_8BITRAM_ENABLED 0x0200 #define WP_WAVE_DRAM_ENABLED 0x0100 #define WP_WAVE_RAMSPLIT_MASK 0x00ff #define WP_WAVE_RAMSPLIT_SHIFT 0 #define WPREG_BASE 12 #define WP_PARAOUT_BASE_MASK 0xf000 #define WP_PARAOUT_BASE_SHIFT 12 #define WP_PARAIN_BASE_MASK 0x0f00 #define WP_PARAIN_BASE_SHIFT 8 #define WP_SERIAL0_BASE_MASK 0x00f0 #define WP_SERIAL0_BASE_SHIFT 4 #define WP_SERIAL1_BASE_MASK 0x000f #define WP_SERIAL1_BASE_SHIFT 0 #define WPREG_TIMER_ENABLE 17 #define WPREG_TIMER_START 23 /* ----------------------------- * Audio Processing Unit. */ #define APUREG_APUTYPE 0 #define APU_DMA_ENABLED 0x4000 #define APU_INT_ON_LOOP 0x2000 #define APU_ENDCURVE 0x1000 #define APU_APUTYPE_MASK 0x00f0 #define APU_FILTERTYPE_MASK 0x000c #define APU_FILTERQ_MASK 0x0003 /* APU types */ #define APU_APUTYPE_SHIFT 4 #define APUTYPE_INACTIVE 0 #define APUTYPE_16BITLINEAR 1 #define APUTYPE_16BITSTEREO 2 #define APUTYPE_8BITLINEAR 3 #define APUTYPE_8BITSTEREO 4 #define APUTYPE_8BITDIFF 5 #define APUTYPE_DIGITALDELAY 6 #define APUTYPE_DUALTAP_READER 7 #define APUTYPE_CORRELATOR 8 #define APUTYPE_INPUTMIXER 9 #define APUTYPE_WAVETABLE 10 #define APUTYPE_RATECONV 11 #define APUTYPE_16BITPINGPONG 12 /* APU type 13 through 15 are reserved. */ /* Filter types */ #define APU_FILTERTYPE_SHIFT 2 #define FILTERTYPE_2POLE_LOPASS 0 #define FILTERTYPE_2POLE_BANDPASS 1 #define FILTERTYPE_2POLE_HIPASS 2 #define FILTERTYPE_1POLE_LOPASS 3 #define FILTERTYPE_1POLE_HIPASS 4 #define FILTERTYPE_PASSTHROUGH 5 /* Filter Q */ #define APU_FILTERQ_SHIFT 0 #define FILTERQ_LESSQ 0 #define FILTERQ_MOREQ 3 /* APU register 2 */ #define APUREG_FREQ_LOBYTE 2 #define APU_FREQ_LOBYTE_MASK 0xff00 #define APU_plus6dB 0x0010 /* APU register 3 */ #define APUREG_FREQ_HIWORD 3 #define APU_FREQ_HIWORD_MASK 0x0fff /* Frequency */ #define APU_FREQ_LOBYTE_SHIFT 8 #define APU_FREQ_HIWORD_SHIFT 0 #define FREQ_Hz2DIV(freq) (((u_int64_t)(freq) << 16) / 48000) /* APU register 4 */ #define APUREG_WAVESPACE 4 #define APU_STEREO 0x8000 #define APU_USE_SYSMEM 0x4000 #define APU_PCMBAR_MASK 0x6000 #define APU_64KPAGE_MASK 0xff00 /* PCM Base Address Register selection */ #define APU_PCMBAR_SHIFT 13 /* 64KW (==128KB) Page */ #define APU_64KPAGE_SHIFT 8 /* APU register 5 - 7 */ #define APUREG_CURPTR 5 #define APUREG_ENDPTR 6 #define APUREG_LOOPLEN 7 /* APU register 9 */ #define APUREG_AMPLITUDE 9 #define APU_AMPLITUDE_NOW_MASK 0xff00 #define APU_AMPLITUDE_DEST_MASK 0x00ff /* Amplitude now? */ #define APU_AMPLITUDE_NOW_SHIFT 8 /* APU register 10 */ #define APUREG_POSITION 10 #define APU_RADIUS_MASK 0x00c0 #define APU_PAN_MASK 0x003f /* Radius control. */ #define APU_RADIUS_SHIFT 6 #define RADIUS_CENTERCIRCLE 0 #define RADIUS_MIDDLE 1 #define RADIUS_OUTSIDE 2 /* Polar pan. */ #define APU_PAN_SHIFT 0 #define PAN_RIGHT 0x00 #define PAN_FRONT 0x08 #define PAN_LEFT 0x10 /* ----------------------------- * Limits. */ #define WPWA_MAX ((1 << 22) - 1) #define WPWA_MAXADDR ((1 << 23) - 1) #define MAESTRO_MAXADDR ((1 << 28) - 1) #endif /* MAESTRO_REG_H_INCLUDED */ Index: stable/4/sys/dev/sound/pci/neomagic.c =================================================================== --- stable/4/sys/dev/sound/pci/neomagic.c (revision 71948) +++ stable/4/sys/dev/sound/pci/neomagic.c (revision 71949) @@ -1,722 +1,712 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Derived from the public domain Linux driver * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include /* -------------------------------------------------------------------- */ #define NM_BUFFSIZE 16384 #define NM256AV_PCI_ID 0x800510c8 #define NM256ZX_PCI_ID 0x800610c8 struct sc_info; /* channel registers */ struct sc_chinfo { int spd, dir, fmt; snd_dbuf *buffer; pcm_channel *channel; struct sc_info *parent; }; /* device private data */ struct sc_info { device_t dev; u_int32_t type; struct resource *reg, *irq, *buf; int regid, irqid, bufid; void *ih; u_int32_t ac97_base, ac97_status, ac97_busy; u_int32_t buftop, pbuf, rbuf, cbuf, acbuf; u_int32_t playint, recint, misc1int, misc2int; u_int32_t irsz, badintr; struct sc_chinfo pch, rch; }; /* -------------------------------------------------------------------- */ /* * prototypes */ -/* channel interface */ -static void *nmchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int nmchan_free(void *data); -static int nmchan_setformat(void *data, u_int32_t format); -static int nmchan_setspeed(void *data, u_int32_t speed); -static int nmchan_setblocksize(void *data, u_int32_t blocksize); -static int nmchan_trigger(void *data, int go); -static int nmchan_getptr(void *data); -static pcmchan_caps *nmchan_getcaps(void *data); - -static int nm_waitcd(struct sc_info *sc); -/* talk to the codec - called from ac97.c */ -static u_int32_t nm_rdcd(void *, int); -static void nm_wrcd(void *, int, u_int32_t); - /* stuff */ static int nm_loadcoeff(struct sc_info *sc, int dir, int num); static int nm_setch(struct sc_chinfo *ch); static int nm_init(struct sc_info *); static void nm_intr(void *); /* talk to the card */ static u_int32_t nm_rd(struct sc_info *, int, int); static void nm_wr(struct sc_info *, int, u_int32_t, int); static u_int32_t nm_rdbuf(struct sc_info *, int, int); static void nm_wrbuf(struct sc_info *, int, u_int32_t, int); static u_int32_t badcards[] = { 0x0007103c, 0x008f1028, 0x00dd1014, }; #define NUM_BADCARDS (sizeof(badcards) / sizeof(u_int32_t)) /* The actual rates supported by the card. */ static int samplerates[9] = { 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000, 99999999 }; /* -------------------------------------------------------------------- */ static u_int32_t nm_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps nm_caps = {4000, 48000, nm_fmt, 0}; -static pcm_channel nm_chantemplate = { - nmchan_init, - NULL, /* setdir */ - nmchan_setformat, - nmchan_setspeed, - nmchan_setblocksize, - nmchan_trigger, - nmchan_getptr, - nmchan_getcaps, - nmchan_free, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - /* -------------------------------------------------------------------- */ /* Hardware */ static u_int32_t nm_rd(struct sc_info *sc, int regno, int size) { bus_space_tag_t st = rman_get_bustag(sc->reg); bus_space_handle_t sh = rman_get_bushandle(sc->reg); switch (size) { case 1: return bus_space_read_1(st, sh, regno); case 2: return bus_space_read_2(st, sh, regno); case 4: return bus_space_read_4(st, sh, regno); default: return 0xffffffff; } } static void nm_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { bus_space_tag_t st = rman_get_bustag(sc->reg); bus_space_handle_t sh = rman_get_bushandle(sc->reg); switch (size) { case 1: bus_space_write_1(st, sh, regno, data); break; case 2: bus_space_write_2(st, sh, regno, data); break; case 4: bus_space_write_4(st, sh, regno, data); break; } } static u_int32_t nm_rdbuf(struct sc_info *sc, int regno, int size) { bus_space_tag_t st = rman_get_bustag(sc->buf); bus_space_handle_t sh = rman_get_bushandle(sc->buf); switch (size) { case 1: return bus_space_read_1(st, sh, regno); case 2: return bus_space_read_2(st, sh, regno); case 4: return bus_space_read_4(st, sh, regno); default: return 0xffffffff; } } static void nm_wrbuf(struct sc_info *sc, int regno, u_int32_t data, int size) { bus_space_tag_t st = rman_get_bustag(sc->buf); bus_space_handle_t sh = rman_get_bushandle(sc->buf); switch (size) { case 1: bus_space_write_1(st, sh, regno, data); break; case 2: bus_space_write_2(st, sh, regno, data); break; case 4: bus_space_write_4(st, sh, regno, data); break; } } +/* -------------------------------------------------------------------- */ /* ac97 codec */ static int nm_waitcd(struct sc_info *sc) { int cnt = 10; while (cnt-- > 0) { if (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy) DELAY(100); else break; } return (nm_rd(sc, sc->ac97_status, 2) & sc->ac97_busy); } static u_int32_t -nm_initcd(void *devinfo) +nm_initcd(kobj_t obj, void *devinfo) { struct sc_info *sc = (struct sc_info *)devinfo; nm_wr(sc, 0x6c0, 0x01, 1); nm_wr(sc, 0x6cc, 0x87, 1); nm_wr(sc, 0x6cc, 0x80, 1); nm_wr(sc, 0x6cc, 0x00, 1); return 1; } -static u_int32_t -nm_rdcd(void *devinfo, int regno) +static int +nm_rdcd(kobj_t obj, void *devinfo, int regno) { struct sc_info *sc = (struct sc_info *)devinfo; u_int32_t x; if (!nm_waitcd(sc)) { x = nm_rd(sc, sc->ac97_base + regno, 2); DELAY(1000); return x; } else { device_printf(sc->dev, "ac97 codec not ready\n"); - return 0xffffffff; + return -1; } } -static void -nm_wrcd(void *devinfo, int regno, u_int32_t data) +static int +nm_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct sc_info *sc = (struct sc_info *)devinfo; int cnt = 3; if (!nm_waitcd(sc)) { while (cnt-- > 0) { nm_wr(sc, sc->ac97_base + regno, data, 2); if (!nm_waitcd(sc)) { DELAY(1000); - return; + return 0; } } } device_printf(sc->dev, "ac97 codec not ready\n"); + return -1; } +static kobj_method_t nm_ac97_methods[] = { + KOBJMETHOD(ac97_init, nm_initcd), + KOBJMETHOD(ac97_read, nm_rdcd), + KOBJMETHOD(ac97_write, nm_wrcd), + { 0, 0 } +}; +AC97_DECLARE(nm_ac97); + +/* -------------------------------------------------------------------- */ + static void nm_ackint(struct sc_info *sc, u_int32_t num) { if (sc->type == NM256AV_PCI_ID) { nm_wr(sc, NM_INT_REG, num << 1, 2); } else if (sc->type == NM256ZX_PCI_ID) { nm_wr(sc, NM_INT_REG, num, 4); } } static int nm_loadcoeff(struct sc_info *sc, int dir, int num) { int ofs, sz, i; u_int32_t addr; addr = (dir == PCMDIR_PLAY)? 0x01c : 0x21c; if (dir == PCMDIR_REC) num += 8; sz = coefficientSizes[num]; ofs = 0; while (num-- > 0) ofs+= coefficientSizes[num]; for (i = 0; i < sz; i++) nm_wrbuf(sc, sc->cbuf + i, coefficients[ofs + i], 1); nm_wr(sc, addr, sc->cbuf, 4); if (dir == PCMDIR_PLAY) sz--; nm_wr(sc, addr + 4, sc->cbuf + sz, 4); return 0; } static int nm_setch(struct sc_chinfo *ch) { struct sc_info *sc = ch->parent; u_int32_t base; u_int8_t x; for (x = 0; x < 8; x++) if (ch->spd < (samplerates[x] + samplerates[x + 1]) / 2) break; if (x == 8) return 1; ch->spd = samplerates[x]; nm_loadcoeff(sc, ch->dir, x); x <<= 4; x &= NM_RATE_MASK; if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; if (ch->fmt & AFMT_STEREO) x |= NM_RATE_STEREO; base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); return 0; } /* channel interface */ static void * -nmchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +nmchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_chinfo *ch; u_int32_t chnbuf; chnbuf = (dir == PCMDIR_PLAY)? sc->pbuf : sc->rbuf; ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; ch->buffer = b; - ch->buffer->bufsize = NM_BUFFSIZE; - ch->buffer->buf = (u_int8_t *)rman_get_virtual(sc->buf) + chnbuf; + sndbuf_setup(ch->buffer, (u_int8_t *)rman_get_virtual(sc->buf) + chnbuf, NM_BUFFSIZE); if (bootverbose) device_printf(sc->dev, "%s buf %p\n", (dir == PCMDIR_PLAY)? "play" : "rec", ch->buffer->buf); ch->parent = sc; ch->channel = c; ch->dir = dir; return ch; } static int -nmchan_free(void *data) +nmchan_free(kobj_t obj, void *data) { return 0; } static int -nmchan_setformat(void *data, u_int32_t format) +nmchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct sc_chinfo *ch = data; ch->fmt = format; return nm_setch(ch); } static int -nmchan_setspeed(void *data, u_int32_t speed) +nmchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct sc_chinfo *ch = data; ch->spd = speed; return nm_setch(ch)? 0 : ch->spd; } static int -nmchan_setblocksize(void *data, u_int32_t blocksize) +nmchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { return blocksize; } static int -nmchan_trigger(void *data, int go) +nmchan_trigger(kobj_t obj, void *data, int go) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; int ssz; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; if (ch->fmt & AFMT_STEREO) ssz <<= 1; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { nm_wr(sc, NM_PBUFFER_START, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_END, sc->pbuf + NM_BUFFSIZE - ssz, 4); nm_wr(sc, NM_PBUFFER_CURRP, sc->pbuf, 4); nm_wr(sc, NM_PBUFFER_WMARK, sc->pbuf + NM_BUFFSIZE / 2, 4); nm_wr(sc, NM_PLAYBACK_ENABLE_REG, NM_PLAYBACK_FREERUN | NM_PLAYBACK_ENABLE_FLAG, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, 0, 2); } else { nm_wr(sc, NM_PLAYBACK_ENABLE_REG, 0, 1); nm_wr(sc, NM_AUDIO_MUTE_REG, NM_AUDIO_MUTE_BOTH, 2); } } else { if (go == PCMTRIG_START) { nm_wr(sc, NM_RECORD_ENABLE_REG, NM_RECORD_FREERUN | NM_RECORD_ENABLE_FLAG, 1); nm_wr(sc, NM_RBUFFER_START, sc->rbuf, 4); nm_wr(sc, NM_RBUFFER_END, sc->rbuf + NM_BUFFSIZE, 4); nm_wr(sc, NM_RBUFFER_CURRP, sc->rbuf, 4); nm_wr(sc, NM_RBUFFER_WMARK, sc->rbuf + NM_BUFFSIZE / 2, 4); } else { nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); } } return 0; } static int -nmchan_getptr(void *data) +nmchan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; struct sc_info *sc = ch->parent; if (ch->dir == PCMDIR_PLAY) return nm_rd(sc, NM_PBUFFER_CURRP, 4) - sc->pbuf; else return nm_rd(sc, NM_RBUFFER_CURRP, 4) - sc->rbuf; } static pcmchan_caps * -nmchan_getcaps(void *data) +nmchan_getcaps(kobj_t obj, void *data) { return &nm_caps; } +static kobj_method_t nmchan_methods[] = { + KOBJMETHOD(channel_init, nmchan_init), + KOBJMETHOD(channel_free, nmchan_free), + KOBJMETHOD(channel_setformat, nmchan_setformat), + KOBJMETHOD(channel_setspeed, nmchan_setspeed), + KOBJMETHOD(channel_setblocksize, nmchan_setblocksize), + KOBJMETHOD(channel_trigger, nmchan_trigger), + KOBJMETHOD(channel_getptr, nmchan_getptr), + KOBJMETHOD(channel_getcaps, nmchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(nmchan); + /* The interrupt handler */ static void nm_intr(void *p) { struct sc_info *sc = (struct sc_info *)p; int status, x; status = nm_rd(sc, NM_INT_REG, sc->irsz); if (status == 0) return; if (status & sc->playint) { status &= ~sc->playint; nm_ackint(sc, sc->playint); chn_intr(sc->pch.channel); } if (status & sc->recint) { status &= ~sc->recint; nm_ackint(sc, sc->recint); chn_intr(sc->rch.channel); } if (status & sc->misc1int) { status &= ~sc->misc1int; nm_ackint(sc, sc->misc1int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x | 2, 1); device_printf(sc->dev, "misc int 1\n"); } if (status & sc->misc2int) { status &= ~sc->misc2int; nm_ackint(sc, sc->misc2int); x = nm_rd(sc, 0x400, 1); nm_wr(sc, 0x400, x & ~2, 1); device_printf(sc->dev, "misc int 2\n"); } if (status) { nm_ackint(sc, status); device_printf(sc->dev, "unknown int\n"); } } /* -------------------------------------------------------------------- */ /* * Probe and attach the card */ static int nm_init(struct sc_info *sc) { u_int32_t ofs, i; if (sc->type == NM256AV_PCI_ID) { sc->ac97_base = NM_MIXER_OFFSET; sc->ac97_status = NM_MIXER_STATUS_OFFSET; sc->ac97_busy = NM_MIXER_READY_MASK; sc->buftop = 2560 * 1024; sc->irsz = 2; sc->playint = NM_PLAYBACK_INT; sc->recint = NM_RECORD_INT; sc->misc1int = NM_MISC_INT_1; sc->misc2int = NM_MISC_INT_2; } else if (sc->type == NM256ZX_PCI_ID) { sc->ac97_base = NM_MIXER_OFFSET; sc->ac97_status = NM2_MIXER_STATUS_OFFSET; sc->ac97_busy = NM2_MIXER_READY_MASK; sc->buftop = (nm_rd(sc, 0xa0b, 2)? 6144 : 4096) * 1024; sc->irsz = 4; sc->playint = NM2_PLAYBACK_INT; sc->recint = NM2_RECORD_INT; sc->misc1int = NM2_MISC_INT_1; sc->misc2int = NM2_MISC_INT_2; } else return -1; sc->badintr = 0; ofs = sc->buftop - 0x0400; sc->buftop -= 0x1400; if ((nm_rdbuf(sc, ofs, 4) & NM_SIG_MASK) == NM_SIGNATURE) { i = nm_rdbuf(sc, ofs + 4, 4); if (i != 0 && i != 0xffffffff) sc->buftop = i; } sc->cbuf = sc->buftop - NM_MAX_COEFFICIENT; sc->rbuf = sc->cbuf - NM_BUFFSIZE; sc->pbuf = sc->rbuf - NM_BUFFSIZE; sc->acbuf = sc->pbuf - (NM_TOTAL_COEFF_COUNT * 4); nm_wr(sc, 0, 0x11, 1); nm_wr(sc, NM_RECORD_ENABLE_REG, 0, 1); nm_wr(sc, 0x214, 0, 2); return 0; } static int nm_pci_probe(device_t dev) { char *s = NULL; u_int32_t subdev, i; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); switch (pci_get_devid(dev)) { case NM256AV_PCI_ID: i = 0; while ((i < NUM_BADCARDS) && (badcards[i] != subdev)) i++; if (i == NUM_BADCARDS) s = "NeoMagic 256AV"; DEB(else) DEB(device_printf(dev, "this is a non-ac97 NM256AV, not attaching\n")); break; case NM256ZX_PCI_ID: s = "NeoMagic 256ZX"; break; } if (s) device_set_desc(dev, s); return s? 0 : ENXIO; } static int nm_pci_attach(device_t dev) { u_int32_t data; struct sc_info *sc; struct ac97_info *codec = 0; char status[SND_STATUSLEN]; if ((sc = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(sc, sizeof(*sc)); sc->dev = dev; sc->type = pci_get_devid(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); sc->bufid = PCIR_MAPS; sc->buf = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->bufid, 0, ~0, 1, RF_ACTIVE); sc->regid = PCIR_MAPS + 4; sc->reg = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->regid, 0, ~0, 1, RF_ACTIVE); if (!sc->buf || !sc->reg) { device_printf(dev, "unable to map register space\n"); goto bad; } if (nm_init(sc) == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - codec = ac97_create(dev, sc, nm_initcd, nm_rdcd, nm_wrcd); + codec = AC97_CREATE(dev, sc, nm_ac97); if (codec == NULL) goto bad; - if (mixer_init(dev, &ac97_mixer, codec) == -1) goto bad; + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; sc->irqid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!sc->irq || bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, nm_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } snprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld", rman_get_start(sc->buf), rman_get_start(sc->reg), rman_get_start(sc->irq)); if (pcm_register(dev, sc, 1, 1)) goto bad; - pcm_addchan(dev, PCMDIR_REC, &nm_chantemplate, sc); - pcm_addchan(dev, PCMDIR_PLAY, &nm_chantemplate, sc); + pcm_addchan(dev, PCMDIR_REC, &nmchan_class, sc); + pcm_addchan(dev, PCMDIR_PLAY, &nmchan_class, sc); pcm_setstatus(dev, status); return 0; bad: if (codec) ac97_destroy(codec); if (sc->buf) bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); if (sc->reg) bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); free(sc, M_DEVBUF); return ENXIO; } static int nm_pci_detach(device_t dev) { int r; struct sc_info *sc; r = pcm_unregister(dev); if (r) return r; sc = pcm_getdevinfo(dev); bus_release_resource(dev, SYS_RES_MEMORY, sc->bufid, sc->buf); bus_release_resource(dev, SYS_RES_MEMORY, sc->regid, sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); free(sc, M_DEVBUF); return 0; } static int nm_pci_resume(device_t dev) { struct sc_info *sc; sc = pcm_getdevinfo(dev); /* Reinit audio device */ if (nm_init(sc) == -1) { device_printf(dev, "unable to reinitialize the card\n"); return ENXIO; } /* Reinit mixer */ if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); return ENXIO; } return 0; } static device_method_t nm_methods[] = { /* Device interface */ DEVMETHOD(device_probe, nm_pci_probe), DEVMETHOD(device_attach, nm_pci_attach), DEVMETHOD(device_detach, nm_pci_detach), DEVMETHOD(device_resume, nm_pci_resume), { 0, 0 } }; static driver_t nm_driver = { "pcm", nm_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_neomagic, pci, nm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_neomagic, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_neomagic, 1); Index: stable/4/sys/dev/sound/pci/solo.c =================================================================== --- stable/4/sys/dev/sound/pci/solo.c (revision 71948) +++ stable/4/sys/dev/sound/pci/solo.c (revision 71949) @@ -1,1030 +1,1016 @@ /* * Copyright (c) 1999 Cameron Grant * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include +#include "mixer_if.h" + #define ESS_BUFFSIZE (16384) #define ABS(x) (((x) < 0)? -(x) : (x)) /* if defined, playback always uses the 2nd channel and full duplex works */ #undef ESS18XX_DUPLEX /* more accurate clocks and split audio1/audio2 rates */ #define ESS18XX_NEWSPEED -/* channel interface for ESS */ -static void *esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int esschan_setformat(void *data, u_int32_t format); -static int esschan_setspeed(void *data, u_int32_t speed); -static int esschan_setblocksize(void *data, u_int32_t blocksize); -static int esschan_trigger(void *data, int go); -static int esschan_getptr(void *data); -static pcmchan_caps *esschan_getcaps(void *data); - static u_int32_t ess_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static pcmchan_caps ess_playcaps = {5000, 49000, ess_playfmt, 0}; /* * Recording output is byte-swapped */ static u_int32_t ess_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_BE, AFMT_STEREO | AFMT_S16_BE, AFMT_U16_BE, AFMT_STEREO | AFMT_U16_BE, 0 }; static pcmchan_caps ess_reccaps = {5000, 49000, ess_recfmt, 0}; -static pcm_channel ess_chantemplate = { - esschan_init, - NULL, /* setdir */ - esschan_setformat, - esschan_setspeed, - esschan_setblocksize, - esschan_trigger, - esschan_getptr, - esschan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - struct ess_info; struct ess_chinfo { struct ess_info *parent; pcm_channel *channel; snd_dbuf *buffer; int dir, hwch, stopping; - u_int32_t fmt, spd; + u_int32_t fmt, spd, blksz; }; struct ess_info { struct resource *io, *sb, *vc, *mpu, *gp; /* I/O address for the board */ struct resource *irq; void *ih; bus_dma_tag_t parent_dmat; int simplex_dir, type, duplex:1, newspeed:1, dmasz[2]; struct ess_chinfo pch, rch; }; static int ess_rd(struct ess_info *sc, int reg); static void ess_wr(struct ess_info *sc, int reg, u_int8_t val); static int ess_dspready(struct ess_info *sc); static int ess_cmd(struct ess_info *sc, u_char val); static int ess_cmd1(struct ess_info *sc, u_char cmd, int val); static int ess_get_byte(struct ess_info *sc); static void ess_setmixer(struct ess_info *sc, u_int port, u_int value); static int ess_getmixer(struct ess_info *sc, u_int port); static int ess_reset_dsp(struct ess_info *sc); static int ess_write(struct ess_info *sc, u_char reg, int val); static int ess_read(struct ess_info *sc, u_char reg); static void ess_intr(void *arg); static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len); static int ess_start(struct ess_chinfo *ch); static int ess_stop(struct ess_chinfo *ch); static int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir); static int ess_dmapos(struct ess_info *sc, int ch); static int ess_dmatrigger(struct ess_info *sc, int ch, int go); -static int essmix_init(snd_mixer *m); -static int essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -static int essmix_setrecsrc(snd_mixer *m, u_int32_t src); - -static snd_mixer ess_mixer = { - "ESS mixer", - essmix_init, - NULL, - essmix_set, - essmix_setrecsrc, -}; - static devclass_t pcm_devclass; /* * Common code for the midi and pcm functions * * ess_cmd write a single byte to the CMD port. * ess_cmd1 write a CMD + 1 byte arg * ess_cmd2 write a CMD + 2 byte arg * ess_get_byte returns a single byte from the DSP data port * * ess_write is actually ess_cmd1 * ess_read access ext. regs via ess_cmd(0xc0, reg) followed by ess_get_byte */ static int port_rd(struct resource *port, int regno, int size) { bus_space_tag_t st = rman_get_bustag(port); bus_space_handle_t sh = rman_get_bushandle(port); switch (size) { case 1: return bus_space_read_1(st, sh, regno); case 2: return bus_space_read_2(st, sh, regno); case 4: return bus_space_read_4(st, sh, regno); default: return 0xffffffff; } } static void port_wr(struct resource *port, int regno, u_int32_t data, int size) { bus_space_tag_t st = rman_get_bustag(port); bus_space_handle_t sh = rman_get_bushandle(port); switch (size) { case 1: bus_space_write_1(st, sh, regno, data); break; case 2: bus_space_write_2(st, sh, regno, data); break; case 4: bus_space_write_4(st, sh, regno, data); break; } } static int ess_rd(struct ess_info *sc, int reg) { return port_rd(sc->sb, reg, 1); } static void ess_wr(struct ess_info *sc, int reg, u_int8_t val) { port_wr(sc->sb, reg, val, 1); } static int ess_dspready(struct ess_info *sc) { return ((ess_rd(sc, SBDSP_STATUS) & 0x80) == 0); } static int ess_dspwr(struct ess_info *sc, u_char val) { int i; for (i = 0; i < 1000; i++) { if (ess_dspready(sc)) { ess_wr(sc, SBDSP_CMD, val); return 1; } if (i > 10) DELAY((i > 100)? 1000 : 10); } printf("ess_dspwr(0x%02x) timed out.\n", val); return 0; } static int ess_cmd(struct ess_info *sc, u_char val) { DEB(printf("ess_cmd: %x\n", val)); return ess_dspwr(sc, val); } static int ess_cmd1(struct ess_info *sc, u_char cmd, int val) { DEB(printf("ess_cmd1: %x, %x\n", cmd, val)); if (ess_dspwr(sc, cmd)) { return ess_dspwr(sc, val & 0xff); } else return 0; } static void ess_setmixer(struct ess_info *sc, u_int port, u_int value) { u_long flags; DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);) flags = spltty(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff)); DELAY(10); splx(flags); } static int ess_getmixer(struct ess_info *sc, u_int port) { int val; u_long flags; flags = spltty(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); val = ess_rd(sc, SB_MIX_DATA); DELAY(10); splx(flags); return val; } static int ess_get_byte(struct ess_info *sc) { int i; for (i = 1000; i > 0; i--) { if (ess_rd(sc, 0xc) & 0x40) return ess_rd(sc, DSP_READ); else DELAY(20); } return -1; } static int ess_write(struct ess_info *sc, u_char reg, int val) { return ess_cmd1(sc, reg, val); } static int ess_read(struct ess_info *sc, u_char reg) { return (ess_cmd(sc, 0xc0) && ess_cmd(sc, reg))? ess_get_byte(sc) : -1; } static int ess_reset_dsp(struct ess_info *sc) { DEB(printf("ess_reset_dsp\n")); ess_wr(sc, SBDSP_RST, 3); DELAY(100); ess_wr(sc, SBDSP_RST, 0); if (ess_get_byte(sc) != 0xAA) { DEB(printf("ess_reset_dsp failed\n")); /* rman_get_start(d->io_base))); */ return ENXIO; /* Sorry */ } ess_cmd(sc, 0xc6); return 0; } static void ess_intr(void *arg) { struct ess_info *sc = (struct ess_info *)arg; int src, pirq = 0, rirq = 0; src = 0; if (ess_getmixer(sc, 0x7a) & 0x80) src |= 2; if (ess_rd(sc, 0x0c) & 0x01) src |= 1; if (src == 0) return; if (sc->duplex) { pirq = (src & sc->pch.hwch)? 1 : 0; rirq = (src & sc->rch.hwch)? 1 : 0; } else { if (sc->simplex_dir == PCMDIR_PLAY) pirq = 1; if (sc->simplex_dir == PCMDIR_REC) rirq = 1; if (!pirq && !rirq) printf("solo: IRQ neither playback nor rec!\n"); } DEB(printf("ess_intr: pirq:%d rirq:%d\n",pirq,rirq)); if (pirq) { if (sc->pch.stopping) { ess_dmatrigger(sc, sc->pch.hwch, 0); sc->pch.stopping = 0; if (sc->pch.hwch == 1) ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03); } chn_intr(sc->pch.channel); } if (rirq) { if (sc->rch.stopping) { ess_dmatrigger(sc, sc->rch.hwch, 0); sc->rch.stopping = 0; /* XXX: will this stop audio2? */ ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); } chn_intr(sc->rch.channel); } if (src & 2) ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80); if (src & 1) ess_rd(sc, DSP_DATA_AVAIL); } /* utility functions for ESS */ static u_int8_t ess_calcspeed8(int *spd) { int speed = *spd; u_int32_t t; if (speed > 22000) { t = (795500 + speed / 2) / speed; speed = (795500 + t / 2) / t; t = (256 - t) | 0x80; } else { t = (397700 + speed / 2) / speed; speed = (397700 + t / 2) / t; t = 128 - t; } *spd = speed; return t & 0x000000ff; } static u_int8_t ess_calcspeed9(int *spd) { int speed, s0, s1, use0; u_int8_t t0, t1; /* rate = source / (256 - divisor) */ /* divisor = 256 - (source / rate) */ speed = *spd; t0 = 128 - (793800 / speed); s0 = 793800 / (128 - t0); t1 = 128 - (768000 / speed); s1 = 768000 / (128 - t1); t1 |= 0x80; use0 = (ABS(speed - s0) < ABS(speed - s1))? 1 : 0; *spd = use0? s0 : s1; return use0? t0 : t1; } static u_int8_t ess_calcfilter(int spd) { int cutoff; /* cutoff = 7160000 / (256 - divisor) */ /* divisor = 256 - (7160000 / cutoff) */ cutoff = (spd * 9 * 82) / 20; return (256 - (7160000 / cutoff)); } static int ess_setupch(struct ess_info *sc, int ch, int dir, int spd, u_int32_t fmt, int len) { int play = (dir == PCMDIR_PLAY)? 1 : 0; int b16 = (fmt & AFMT_16BIT)? 1 : 0; int stereo = (fmt & AFMT_STEREO)? 1 : 0; int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE || fmt == AFMT_U16_BE)? 1 : 0; u_int8_t spdval, fmtval; DEB(printf("ess_setupch\n")); spdval = (sc->newspeed)? ess_calcspeed9(&spd) : ess_calcspeed8(&spd); sc->simplex_dir = play ? PCMDIR_PLAY : PCMDIR_REC ; if (ch == 1) { KASSERT((dir == PCMDIR_PLAY) || (dir == PCMDIR_REC), ("ess_setupch: dir1 bad")); len = -len; /* transfer length low */ ess_write(sc, 0xa4, len & 0x00ff); /* transfer length high */ ess_write(sc, 0xa5, (len & 0xff00) >> 8); /* autoinit, dma dir */ ess_write(sc, 0xb8, 0x04 | (play? 0x00 : 0x0a)); /* mono/stereo */ ess_write(sc, 0xa8, (ess_read(sc, 0xa8) & ~0x03) | (stereo? 0x01 : 0x02)); /* demand mode, 4 bytes/xfer */ ess_write(sc, 0xb9, 0x02); /* sample rate */ ess_write(sc, 0xa1, spdval); /* filter cutoff */ ess_write(sc, 0xa2, ess_calcfilter(spd)); /* setup dac/adc */ /* if (play) ess_write(sc, 0xb6, unsign? 0x80 : 0x00); */ /* mono, b16: signed, load signal */ /* ess_write(sc, 0xb7, 0x51 | (unsign? 0x00 : 0x20)); */ /* setup fifo */ ess_write(sc, 0xb7, 0x91 | (unsign? 0x00 : 0x20) | (b16? 0x04 : 0x00) | (stereo? 0x08 : 0x40)); /* irq control */ ess_write(sc, 0xb1, (ess_read(sc, 0xb1) & 0x0f) | 0x50); /* drq control */ ess_write(sc, 0xb2, (ess_read(sc, 0xb2) & 0x0f) | 0x50); } else if (ch == 2) { KASSERT(dir == PCMDIR_PLAY, ("ess_setupch: dir2 bad")); len >>= 1; len = -len; /* transfer length low */ ess_setmixer(sc, 0x74, len & 0x00ff); /* transfer length high */ ess_setmixer(sc, 0x76, (len & 0xff00) >> 8); /* autoinit, 4 bytes/req */ ess_setmixer(sc, 0x78, 0x10); fmtval = b16 | (stereo << 1) | ((!unsign) << 2); /* enable irq, set format */ ess_setmixer(sc, 0x7a, 0x40 | fmtval); if (sc->newspeed) { /* sample rate */ ess_setmixer(sc, 0x70, spdval); /* filter cutoff */ ess_setmixer(sc, 0x72, ess_calcfilter(spd)); } } return 0; } static int ess_start(struct ess_chinfo *ch) { struct ess_info *sc = ch->parent; DEB(printf("ess_start\n");); - ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->buffer->dl); + ess_setupch(sc, ch->hwch, ch->dir, ch->spd, ch->fmt, ch->blksz); ch->stopping = 0; if (ch->hwch == 1) { ess_write(sc, 0xb8, ess_read(sc, 0xb8) | 0x01); if (ch->dir == PCMDIR_PLAY) { #if 0 DELAY(100000); /* 100 ms */ #endif ess_cmd(sc, 0xd1); } } else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) | 0x03); return 0; } static int ess_stop(struct ess_chinfo *ch) { struct ess_info *sc = ch->parent; DEB(printf("ess_stop\n")); ch->stopping = 1; if (ch->hwch == 1) ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x04); else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x10); DEB(printf("done with stop\n")); return 0; } +/* -------------------------------------------------------------------- */ /* channel interface for ESS18xx */ static void * -esschan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +esschan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct ess_info *sc = devinfo; struct ess_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; DEB(printf("esschan_init\n")); ch->parent = sc; ch->channel = c; ch->buffer = b; - ch->buffer->bufsize = ESS_BUFFSIZE; ch->dir = dir; - if (chn_allocbuf(ch->buffer, sc->parent_dmat) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, ESS_BUFFSIZE) == -1) return NULL; ch->hwch = 1; if ((dir == PCMDIR_PLAY) && (sc->duplex)) ch->hwch = 2; return ch; } static int -esschan_setformat(void *data, u_int32_t format) +esschan_setformat(kobj_t obj, void *data, u_int32_t format) { struct ess_chinfo *ch = data; ch->fmt = format; return 0; } static int -esschan_setspeed(void *data, u_int32_t speed) +esschan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct ess_chinfo *ch = data; struct ess_info *sc = ch->parent; ch->spd = speed; if (sc->newspeed) ess_calcspeed9(&ch->spd); else ess_calcspeed8(&ch->spd); return ch->spd; } static int -esschan_setblocksize(void *data, u_int32_t blocksize) +esschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { - return blocksize; + struct ess_chinfo *ch = data; + + ch->blksz = blocksize; + return ch->blksz; } static int -esschan_trigger(void *data, int go) +esschan_trigger(kobj_t obj, void *data, int go) { struct ess_chinfo *ch = data; struct ess_info *sc = ch->parent; DEB(printf("esschan_trigger: %d\n",go)); if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; switch (go) { case PCMTRIG_START: - ess_dmasetup(sc, ch->hwch, vtophys(ch->buffer->buf), ch->buffer->bufsize, ch->dir); + ess_dmasetup(sc, ch->hwch, vtophys(sndbuf_getbuf(ch->buffer)), sndbuf_getsize(ch->buffer), ch->dir); ess_dmatrigger(sc, ch->hwch, 1); ess_start(ch); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: default: ess_stop(ch); break; } return 0; } static int -esschan_getptr(void *data) +esschan_getptr(kobj_t obj, void *data) { struct ess_chinfo *ch = data; struct ess_info *sc = ch->parent; return ess_dmapos(sc, ch->hwch); } static pcmchan_caps * -esschan_getcaps(void *data) +esschan_getcaps(kobj_t obj, void *data) { struct ess_chinfo *ch = data; return (ch->dir == PCMDIR_PLAY)? &ess_playcaps : &ess_reccaps; } +static kobj_method_t esschan_methods[] = { + KOBJMETHOD(channel_init, esschan_init), + KOBJMETHOD(channel_setformat, esschan_setformat), + KOBJMETHOD(channel_setspeed, esschan_setspeed), + KOBJMETHOD(channel_setblocksize, esschan_setblocksize), + KOBJMETHOD(channel_trigger, esschan_trigger), + KOBJMETHOD(channel_getptr, esschan_getptr), + KOBJMETHOD(channel_getcaps, esschan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(esschan); + /************************************************************/ static int essmix_init(snd_mixer *m) { struct ess_info *sc = mix_getdevinfo(m); mix_setrecdevs(m, SOUND_MASK_CD | SOUND_MASK_MIC | SOUND_MASK_LINE | SOUND_MASK_IMIX); mix_setdevs(m, SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_VOLUME | SOUND_MASK_LINE1); ess_setmixer(sc, 0, 0); /* reset */ return 0; } static int essmix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct ess_info *sc = mix_getdevinfo(m); int preg = 0, rreg = 0, l, r; l = (left * 15) / 100; r = (right * 15) / 100; switch (dev) { case SOUND_MIXER_SYNTH: preg = 0x36; rreg = 0x6b; break; case SOUND_MIXER_PCM: preg = 0x14; rreg = 0x7c; break; case SOUND_MIXER_LINE: preg = 0x3e; rreg = 0x6e; break; case SOUND_MIXER_MIC: preg = 0x1a; rreg = 0x68; break; case SOUND_MIXER_LINE1: preg = 0x3a; rreg = 0x6c; break; case SOUND_MIXER_CD: preg = 0x38; rreg = 0x6a; break; case SOUND_MIXER_VOLUME: l = left? (left * 63) / 100 : 64; r = right? (right * 63) / 100 : 64; ess_setmixer(sc, 0x60, l); ess_setmixer(sc, 0x62, r); left = (l == 64)? 0 : (l * 100) / 63; right = (r == 64)? 0 : (r * 100) / 63; return left | (right << 8); } if (preg) ess_setmixer(sc, preg, (l << 4) | r); if (rreg) ess_setmixer(sc, rreg, (l << 4) | r); left = (l * 100) / 15; right = (r * 100) / 15; return left | (right << 8); } static int essmix_setrecsrc(snd_mixer *m, u_int32_t src) { struct ess_info *sc = mix_getdevinfo(m); u_char recdev; switch (src) { case SOUND_MASK_CD: recdev = 0x02; break; case SOUND_MASK_LINE: recdev = 0x06; break; case SOUND_MASK_IMIX: recdev = 0x05; break; case SOUND_MASK_MIC: default: recdev = 0x00; src = SOUND_MASK_MIC; break; } ess_setmixer(sc, 0x1c, recdev); return src; } +static kobj_method_t solomixer_methods[] = { + KOBJMETHOD(mixer_init, essmix_init), + KOBJMETHOD(mixer_set, essmix_set), + KOBJMETHOD(mixer_setrecsrc, essmix_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(solomixer); + /************************************************************/ static int ess_dmasetup(struct ess_info *sc, int ch, u_int32_t base, u_int16_t cnt, int dir) { KASSERT(ch == 1 || ch == 2, ("bad ch")); sc->dmasz[ch - 1] = cnt; if (ch == 1) { port_wr(sc->vc, 0x8, 0xc4, 1); /* command */ port_wr(sc->vc, 0xd, 0xff, 1); /* reset */ port_wr(sc->vc, 0xf, 0x01, 1); /* mask */ port_wr(sc->vc, 0xb, dir == PCMDIR_PLAY? 0x58 : 0x54, 1); /* mode */ port_wr(sc->vc, 0x0, base, 4); port_wr(sc->vc, 0x4, cnt - 1, 2); } else if (ch == 2) { port_wr(sc->io, 0x6, 0x08, 1); /* autoinit */ port_wr(sc->io, 0x0, base, 4); port_wr(sc->io, 0x4, cnt, 2); } return 0; } static int ess_dmapos(struct ess_info *sc, int ch) { int p = 0, i = 0, j = 0; u_long flags; KASSERT(ch == 1 || ch == 2, ("bad ch")); flags = spltty(); if (ch == 1) { /* * During recording, this register is known to give back * garbage if it's not quiescent while being read. That's * why we spl, stop the DMA, and try over and over until * adjacent reads are "close", in the right order and not * bigger than is otherwise possible. */ ess_dmatrigger(sc, ch, 0); DELAY(20); do { DELAY(10); if (j > 1) printf("DMA count reg bogus: %04x & %04x\n", i, p); i = port_rd(sc->vc, 0x4, 2) + 1; p = port_rd(sc->vc, 0x4, 2) + 1; - } while ((p > sc->dmasz[ch -1 ] || i < p || (p - i) > 0x8) && j++ < 1000); + } while ((p > sc->dmasz[ch - 1] || i < p || (p - i) > 0x8) && j++ < 1000); ess_dmatrigger(sc, ch, 1); } else if (ch == 2) p = port_rd(sc->io, 0x4, 2); splx(flags); return sc->dmasz[ch - 1] - p; } static int ess_dmatrigger(struct ess_info *sc, int ch, int go) { KASSERT(ch == 1 || ch == 2, ("bad ch")); if (ch == 1) port_wr(sc->vc, 0xf, go? 0x00 : 0x01, 1); /* mask */ else if (ch == 2) port_wr(sc->io, 0x6, 0x08 | (go? 0x02 : 0x00), 1); /* autoinit */ return 0; } static void ess_release_resources(struct ess_info *sc, device_t dev) { /* should we bus_teardown_intr here? */ if (sc->irq) { if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); sc->irq = 0; } if (sc->io) { bus_release_resource(dev, SYS_RES_IOPORT, 0 * 4 + PCIR_MAPS, sc->io); sc->io = 0; } if (sc->sb) { bus_release_resource(dev, SYS_RES_IOPORT, 1 * 4 + PCIR_MAPS, sc->sb); sc->sb = 0; } if (sc->vc) { bus_release_resource(dev, SYS_RES_IOPORT, 2 * 4 + PCIR_MAPS, sc->vc); sc->vc = 0; } if (sc->mpu) { bus_release_resource(dev, SYS_RES_IOPORT, 3 * 4 + PCIR_MAPS, sc->mpu); sc->mpu = 0; } if (sc->gp) { bus_release_resource(dev, SYS_RES_IOPORT, 4 * 4 + PCIR_MAPS, sc->gp); sc->gp = 0; } if (sc->parent_dmat) { bus_dma_tag_destroy(sc->parent_dmat); sc->parent_dmat = 0; } free(sc, M_DEVBUF); } static int ess_alloc_resources(struct ess_info *sc, device_t dev) { int rid; rid = 0 * 4 + PCIR_MAPS; sc->io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); rid = 1 * 4 + PCIR_MAPS; sc->sb = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); rid = 2 * 4 + PCIR_MAPS; sc->vc = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); rid = 3 * 4 + PCIR_MAPS; sc->mpu = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); rid = 4 * 4 + PCIR_MAPS; sc->gp = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); rid = 0; sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); return (sc->irq && sc->io && sc->sb && sc->vc && sc->mpu && sc->gp)? 0 : ENXIO; } static int ess_probe(device_t dev) { char *s = NULL; u_int32_t subdev; subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); switch (pci_get_devid(dev)) { case 0x1969125d: if (subdev == 0x8888125d) s = "ESS Solo-1E"; else if (subdev == 0x1818125d) s = "ESS Solo-1"; else s = "ESS Solo-1 (unknown vendor)"; break; } if (s) device_set_desc(dev, s); return s? 0 : ENXIO; } #define PCI_LEGACYCONTROL 0x40 #define PCI_CONFIG 0x50 #define PCI_DDMACONTROL 0x60 static int ess_attach(device_t dev) { struct ess_info *sc; char status[SND_STATUSLEN]; u_int16_t ddma; u_int32_t data; sc = (struct ess_info *)malloc(sizeof *sc, M_DEVBUF, M_NOWAIT); if (!sc) return ENXIO; bzero(sc, sizeof *sc); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN; pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); if (ess_alloc_resources(sc, dev)) goto no; ddma = rman_get_start(sc->vc) | 1; pci_write_config(dev, PCI_LEGACYCONTROL, 0x805f, 2); pci_write_config(dev, PCI_DDMACONTROL, ddma, 2); pci_write_config(dev, PCI_CONFIG, 0, 2); if (ess_reset_dsp(sc)) goto no; - mixer_init(dev, &ess_mixer, sc); + if (mixer_init(dev, &solomixer_class, sc)) + goto no; port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ #ifdef ESS18XX_DUPLEX sc->duplex = 1; #else sc->duplex = 0; #endif #ifdef ESS18XX_NEWSPEED sc->newspeed = 1; #else sc->newspeed = 0; #endif if (sc->newspeed) ess_setmixer(sc, 0x71, 0x2a); bus_setup_intr(dev, sc->irq, INTR_TYPE_TTY, ess_intr, sc, &sc->ih); if (!sc->duplex) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/65536, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_24BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/ESS_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } snprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld", rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc), rman_get_start(sc->irq)); if (pcm_register(dev, sc, 1, 1)) goto no; - pcm_addchan(dev, PCMDIR_REC, &ess_chantemplate, sc); - pcm_addchan(dev, PCMDIR_PLAY, &ess_chantemplate, sc); + pcm_addchan(dev, PCMDIR_REC, &esschan_class, sc); + pcm_addchan(dev, PCMDIR_PLAY, &esschan_class, sc); pcm_setstatus(dev, status); return 0; no: ess_release_resources(sc, dev); return ENXIO; } static int ess_detach(device_t dev) { int r; struct ess_info *sc; r = pcm_unregister(dev); if (r) return r; sc = pcm_getdevinfo(dev); ess_release_resources(sc, dev); return 0; } static device_method_t ess_methods[] = { /* Device interface */ DEVMETHOD(device_probe, ess_probe), DEVMETHOD(device_attach, ess_attach), DEVMETHOD(device_detach, ess_detach), DEVMETHOD(device_resume, bus_generic_resume), DEVMETHOD(device_suspend, bus_generic_suspend), { 0, 0 } }; static driver_t ess_driver = { "pcm", ess_methods, sizeof(snddev_info), }; DRIVER_MODULE(snd_solo, pci, ess_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_solo, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_solo, 1); Index: stable/4/sys/dev/sound/pci/t4dwave.c =================================================================== --- stable/4/sys/dev/sound/pci/t4dwave.c (revision 71948) +++ stable/4/sys/dev/sound/pci/t4dwave.c (revision 71949) @@ -1,746 +1,824 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHERIN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include /* -------------------------------------------------------------------- */ #define TDX_PCI_ID 0x20001023 #define TNX_PCI_ID 0x20011023 -#define TR_BUFFSIZE 0xf000 +#define TR_BUFFSIZE 0x1000 #define TR_TIMEOUT_CDC 0xffff -#define TR_INTSAMPLES 0x2000 #define TR_MAXPLAYCH 4 struct tr_info; /* channel registers */ struct tr_chinfo { u_int32_t cso, alpha, fms, fmc, ec; u_int32_t lba; u_int32_t eso, delta; u_int32_t rvol, cvol; u_int32_t gvsel, pan, vol, ctrl; - int index, ss; + int index, bufhalf; snd_dbuf *buffer; pcm_channel *channel; struct tr_info *parent; }; +struct tr_rchinfo { + u_int32_t delta; + snd_dbuf *buffer; + pcm_channel *channel; + struct tr_info *parent; +}; + /* device private data */ struct tr_info { u_int32_t type; bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; struct resource *reg, *irq; int regtype, regid, irqid; void *ih; u_int32_t playchns; struct tr_chinfo chinfo[TR_MAXPLAYCH]; - struct tr_chinfo recchinfo; + struct tr_rchinfo recchinfo; }; /* -------------------------------------------------------------------- */ -/* - * prototypes - */ - -/* channel interface */ -static void *trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int trchan_setdir(void *data, int dir); -static int trchan_setformat(void *data, u_int32_t format); -static int trchan_setspeed(void *data, u_int32_t speed); -static int trchan_setblocksize(void *data, u_int32_t blocksize); -static int trchan_trigger(void *data, int go); -static int trchan_getptr(void *data); -static pcmchan_caps *trchan_getcaps(void *data); - -/* talk to the codec - called from ac97.c */ -static u_int32_t tr_rdcd(void *, int); -static void tr_wrcd(void *, int, u_int32_t); - -/* stuff */ -static int tr_init(struct tr_info *); -static void tr_intr(void *); - -/* talk to the card */ -static u_int32_t tr_rd(struct tr_info *, int, int); -static void tr_wr(struct tr_info *, int, u_int32_t, int); - -/* manipulate playback channels */ -static void tr_clrint(struct tr_info *, char); -static void tr_enaint(struct tr_info *, char, int); -static u_int32_t tr_testint(struct tr_info *, char); -static void tr_rdch(struct tr_info *, char, struct tr_chinfo *); -static void tr_wrch(struct tr_info *, char, struct tr_chinfo *); -static void tr_selch(struct tr_info *, char); -static void tr_startch(struct tr_info *, char); -static void tr_stopch(struct tr_info *, char); - -/* -------------------------------------------------------------------- */ - static u_int32_t tr_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; static u_int32_t tr_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, 0 }; static pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; -static pcm_channel tr_chantemplate = { - trchan_init, - trchan_setdir, - trchan_setformat, - trchan_setspeed, - trchan_setblocksize, - trchan_trigger, - trchan_getptr, - trchan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - /* -------------------------------------------------------------------- */ -static u_int32_t -tr_fmttobits(u_int32_t fmt) -{ - u_int32_t bits = 0; - bits |= (fmt & AFMT_STEREO)? 0x4 : 0; - bits |= (fmt & (AFMT_S8 | AFMT_S16_LE))? 0x2 : 0; - bits |= (fmt & (AFMT_S16_LE | AFMT_U16_LE))? 0x8 : 0; - return bits; -} - /* Hardware */ static u_int32_t tr_rd(struct tr_info *tr, int regno, int size) { switch(size) { case 1: return bus_space_read_1(tr->st, tr->sh, regno); case 2: return bus_space_read_2(tr->st, tr->sh, regno); case 4: return bus_space_read_4(tr->st, tr->sh, regno); default: return 0xffffffff; } } static void tr_wr(struct tr_info *tr, int regno, u_int32_t data, int size) { switch(size) { case 1: bus_space_write_1(tr->st, tr->sh, regno, data); break; case 2: bus_space_write_2(tr->st, tr->sh, regno, data); break; case 4: bus_space_write_4(tr->st, tr->sh, regno, data); break; } } +/* -------------------------------------------------------------------- */ /* ac97 codec */ -static u_int32_t -tr_rdcd(void *devinfo, int regno) +static int +tr_rdcd(kobj_t obj, void *devinfo, int regno) { struct tr_info *tr = (struct tr_info *)devinfo; int i, j, treg, trw; switch (tr->type) { case TDX_PCI_ID: treg=TDX_REG_CODECRD; trw=TDX_CDC_RWSTAT; break; case TNX_PCI_ID: treg=(regno & 0x100)? TNX_REG_CODEC2RD : TNX_REG_CODEC1RD; trw=TNX_CDC_RWSTAT; break; default: printf("!!! tr_rdcd defaulted !!!\n"); - return 0xffffffff; + return -1; } regno &= 0x7f; tr_wr(tr, treg, regno | trw, 4); j=trw; for (i=TR_TIMEOUT_CDC; (i > 0) && (j & trw); i--) j=tr_rd(tr, treg, 4); if (i == 0) printf("codec timeout during read of register %x\n", regno); return (j >> TR_CDC_DATA) & 0xffff; } -static void -tr_wrcd(void *devinfo, int regno, u_int32_t data) +static int +tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) { struct tr_info *tr = (struct tr_info *)devinfo; int i, j, treg, trw; switch (tr->type) { case TDX_PCI_ID: treg=TDX_REG_CODECWR; trw=TDX_CDC_RWSTAT; break; case TNX_PCI_ID: treg=TNX_REG_CODECWR; trw=TNX_CDC_RWSTAT | ((regno & 0x100)? TNX_CDC_SEC : 0); break; default: printf("!!! tr_wrcd defaulted !!!"); - return; + return -1; } regno &= 0x7f; #if 0 printf("tr_wrcd: reg %x was %x", regno, tr_rdcd(devinfo, regno)); #endif j=trw; for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4); tr_wr(tr, treg, (data << TR_CDC_DATA) | regno | trw, 4); #if 0 printf(" - wrote %x, now %x\n", data, tr_rdcd(devinfo, regno)); #endif if (i==0) printf("codec timeout writing %x, data %x\n", regno, data); + return (i > 0)? 0 : -1; } +static kobj_method_t tr_ac97_methods[] = { + KOBJMETHOD(ac97_read, tr_rdcd), + KOBJMETHOD(ac97_write, tr_wrcd), + { 0, 0 } +}; +AC97_DECLARE(tr_ac97); + +/* -------------------------------------------------------------------- */ /* playback channel interrupts */ +#if 0 static u_int32_t -tr_testint(struct tr_info *tr, char channel) +tr_testint(struct tr_chinfo *ch) { - return tr_rd(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA, - 4) & (1<<(channel & 0x1f)); + struct tr_info *tr = ch->parent; + int bank, chan; + + bank = (ch->index & 0x20) ? 1 : 0; + chan = ch->index & 0x1f; + return tr_rd(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 4) & (1 << chan); } +#endif static void -tr_clrint(struct tr_info *tr, char channel) +tr_clrint(struct tr_chinfo *ch) { - tr_wr(tr, (channel & 0x20)? TR_REG_ADDRINTB : TR_REG_ADDRINTA, - 1<<(channel & 0x1f), 4); + struct tr_info *tr = ch->parent; + int bank, chan; + + bank = (ch->index & 0x20) ? 1 : 0; + chan = ch->index & 0x1f; + tr_wr(tr, bank? TR_REG_ADDRINTB : TR_REG_ADDRINTA, 1 << chan, 4); } static void -tr_enaint(struct tr_info *tr, char channel, int enable) +tr_enaint(struct tr_chinfo *ch, int enable) { - u_int32_t reg = (channel & 0x20)? TR_REG_INTENB : TR_REG_INTENA; - u_int32_t i = tr_rd(tr, reg, 4); - channel &= 0x1f; - i &= ~(1 << channel); - i |= (enable? 1 : 0) << channel; - tr_clrint(tr, channel); + struct tr_info *tr = ch->parent; + u_int32_t i, reg; + int bank, chan; + + bank = (ch->index & 0x20) ? 1 : 0; + chan = ch->index & 0x1f; + reg = bank? TR_REG_INTENB : TR_REG_INTENA; + + i = tr_rd(tr, reg, 4); + i &= ~(1 << chan); + i |= (enable? 1 : 0) << chan; + + tr_clrint(ch); tr_wr(tr, reg, i, 4); } /* playback channels */ static void -tr_selch(struct tr_info *tr, char channel) +tr_selch(struct tr_chinfo *ch) { - int i=tr_rd(tr, TR_REG_CIR, 4); + struct tr_info *tr = ch->parent; + int i; + + i = tr_rd(tr, TR_REG_CIR, 4); i &= ~TR_CIR_MASK; - i |= channel & 0x3f; + i |= ch->index & 0x3f; tr_wr(tr, TR_REG_CIR, i, 4); } static void -tr_startch(struct tr_info *tr, char channel) +tr_startch(struct tr_chinfo *ch) { - tr_wr(tr, (channel & 0x20)? TR_REG_STARTB : TR_REG_STARTA, - 1<<(channel & 0x1f), 4); + struct tr_info *tr = ch->parent; + int bank, chan; + + bank = (ch->index & 0x20) ? 1 : 0; + chan = ch->index & 0x1f; + tr_wr(tr, bank? TR_REG_STARTB : TR_REG_STARTA, 1 << chan, 4); } static void -tr_stopch(struct tr_info *tr, char channel) +tr_stopch(struct tr_chinfo *ch) { - tr_wr(tr, (channel & 0x20)? TR_REG_STOPB : TR_REG_STOPA, - 1<<(channel & 0x1f), 4); + struct tr_info *tr = ch->parent; + int bank, chan; + + bank = (ch->index & 0x20) ? 1 : 0; + chan = ch->index & 0x1f; + tr_wr(tr, bank? TR_REG_STOPB : TR_REG_STOPA, 1 << chan, 4); } static void -tr_wrch(struct tr_info *tr, char channel, struct tr_chinfo *ch) +tr_wrch(struct tr_chinfo *ch) { + struct tr_info *tr = ch->parent; u_int32_t cr[TR_CHN_REGS], i; ch->gvsel &= 0x00000001; ch->fmc &= 0x00000003; ch->fms &= 0x0000000f; ch->ctrl &= 0x0000000f; ch->pan &= 0x0000007f; ch->rvol &= 0x0000007f; ch->cvol &= 0x0000007f; ch->vol &= 0x000000ff; ch->ec &= 0x00000fff; ch->alpha &= 0x00000fff; ch->delta &= 0x0000ffff; ch->lba &= 0x3fffffff; cr[1]=ch->lba; - cr[3]=(ch->rvol<<7) | (ch->cvol); - cr[4]=(ch->gvsel<<31)|(ch->pan<<24)|(ch->vol<<16)|(ch->ctrl<<12)|(ch->ec); + cr[3]=(ch->fmc<<14) | (ch->rvol<<7) | (ch->cvol); + cr[4]=(ch->gvsel<<31) | (ch->pan<<24) | (ch->vol<<16) | (ch->ctrl<<12) | (ch->ec); switch (tr->type) { case TDX_PCI_ID: ch->cso &= 0x0000ffff; ch->eso &= 0x0000ffff; cr[0]=(ch->cso<<16) | (ch->alpha<<4) | (ch->fms); cr[2]=(ch->eso<<16) | (ch->delta); - cr[3]|=0x0000c000; break; case TNX_PCI_ID: ch->cso &= 0x00ffffff; ch->eso &= 0x00ffffff; cr[0]=((ch->delta & 0xff)<<24) | (ch->cso); cr[2]=((ch->delta>>16)<<24) | (ch->eso); cr[3]|=(ch->alpha<<20) | (ch->fms<<16) | (ch->fmc<<14); break; } - tr_selch(tr, channel); + tr_selch(ch); for (i=0; iparent; u_int32_t cr[5], i; - tr_selch(tr, channel); - for (i=0; i<5; i++) cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); + + tr_selch(ch); + for (i=0; i<5; i++) + cr[i]=tr_rd(tr, TR_REG_CHNBASE+(i<<2), 4); + + ch->lba= (cr[1] & 0x3fffffff); ch->fmc= (cr[3] & 0x0000c000) >> 14; ch->rvol= (cr[3] & 0x00003f80) >> 7; ch->cvol= (cr[3] & 0x0000007f); ch->gvsel= (cr[4] & 0x80000000) >> 31; ch->pan= (cr[4] & 0x7f000000) >> 24; ch->vol= (cr[4] & 0x00ff0000) >> 16; ch->ctrl= (cr[4] & 0x0000f000) >> 12; ch->ec= (cr[4] & 0x00000fff); switch(tr->type) { case TDX_PCI_ID: ch->cso= (cr[0] & 0xffff0000) >> 16; ch->alpha= (cr[0] & 0x0000fff0) >> 4; ch->fms= (cr[0] & 0x0000000f); ch->eso= (cr[2] & 0xffff0000) >> 16; ch->delta= (cr[2] & 0x0000ffff); break; case TNX_PCI_ID: ch->cso= (cr[0] & 0x00ffffff); ch->eso= (cr[2] & 0x00ffffff); - ch->delta= ((cr[2] & 0xff000000) >> 16) | - ((cr[0] & 0xff000000) >> 24); + ch->delta= ((cr[2] & 0xff000000) >> 16) | ((cr[0] & 0xff000000) >> 24); ch->alpha= (cr[3] & 0xfff00000) >> 20; ch->fms= (cr[3] & 0x000f0000) >> 16; break; } } +static u_int32_t +tr_fmttobits(u_int32_t fmt) +{ + u_int32_t bits; + + bits = 0; + bits |= (fmt & AFMT_SIGNED)? 0x2 : 0; + bits |= (fmt & AFMT_STEREO)? 0x4 : 0; + bits |= (fmt & AFMT_16BIT)? 0x8 : 0; + + return bits; +} + +/* -------------------------------------------------------------------- */ /* channel interface */ -void * -trchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +static void * +trpchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct tr_info *tr = devinfo; struct tr_chinfo *ch; - if (dir == PCMDIR_PLAY) { - ch = &tr->chinfo[tr->playchns]; - ch->index = tr->playchns++; - } else { - ch = &tr->recchinfo; - ch->index = -1; - } + + KASSERT(dir == PCMDIR_PLAY, ("trpchan_init: bad direction")); + ch = &tr->chinfo[tr->playchns]; + ch->index = tr->playchns++; ch->buffer = b; - ch->buffer->bufsize = TR_BUFFSIZE; ch->parent = tr; ch->channel = c; - if (chn_allocbuf(ch->buffer, tr->parent_dmat) == -1) return NULL; - else return ch; + if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1) + return NULL; + + return ch; } static int -trchan_setdir(void *data, int dir) +trpchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct tr_chinfo *ch = data; - struct tr_info *tr = ch->parent; - if (dir == PCMDIR_PLAY && ch->index >= 0) { - ch->fmc = ch->fms = ch->ec = ch->alpha = 0; - ch->lba = vtophys(ch->buffer->buf); + + ch->ctrl = tr_fmttobits(format) | 0x01; + + return 0; +} + +static int +trpchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct tr_chinfo *ch = data; + + ch->delta = (speed << 12) / 48000; + return (ch->delta * 48000) >> 12; +} + +static int +trpchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct tr_chinfo *ch = data; + + sndbuf_resize(ch->buffer, 2, blocksize); + return blocksize; +} + +static int +trpchan_trigger(kobj_t obj, void *data, int go) +{ + struct tr_chinfo *ch = data; + + if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) + return 0; + + if (go == PCMTRIG_START) { + ch->fmc = 3; + ch->fms = 0; + ch->ec = 0; + ch->alpha = 0; + ch->lba = vtophys(sndbuf_getbuf(ch->buffer)); ch->cso = 0; - ch->eso = ch->buffer->bufsize - 1; - ch->rvol = ch->cvol = 0; + ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1; + ch->rvol = ch->cvol = 0x7f; ch->gvsel = 0; ch->pan = 0; ch->vol = 0; - ch->ctrl = 0x01; - ch->delta = 0; - tr_wrch(tr, ch->index, ch); - tr_enaint(tr, ch->index, 1); - } else if (dir == PCMDIR_REC && ch->index == -1) { - /* set up dma mode regs */ - u_int32_t i; - tr_wr(tr, TR_REG_DMAR15, 0, 1); - i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03; - tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1); - /* set up base address */ - tr_wr(tr, TR_REG_DMAR0, vtophys(ch->buffer->buf), 4); - /* set up buffer size */ - i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff; - tr_wr(tr, TR_REG_DMAR4, i | (ch->buffer->bufsize - 1), 4); - } else return -1; + ch->bufhalf = 0; + tr_wrch(ch); + tr_enaint(ch, 1); + tr_startch(ch); + } else + tr_stopch(ch); + return 0; } static int -trchan_setformat(void *data, u_int32_t format) +trpchan_getptr(kobj_t obj, void *data) { struct tr_chinfo *ch = data; + + tr_rdch(ch); + return ch->cso * sndbuf_getbps(ch->buffer); +} + +static pcmchan_caps * +trpchan_getcaps(kobj_t obj, void *data) +{ + return &tr_playcaps; +} + +static kobj_method_t trpchan_methods[] = { + KOBJMETHOD(channel_init, trpchan_init), + KOBJMETHOD(channel_setformat, trpchan_setformat), + KOBJMETHOD(channel_setspeed, trpchan_setspeed), + KOBJMETHOD(channel_setblocksize, trpchan_setblocksize), + KOBJMETHOD(channel_trigger, trpchan_trigger), + KOBJMETHOD(channel_getptr, trpchan_getptr), + KOBJMETHOD(channel_getcaps, trpchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(trpchan); + +/* -------------------------------------------------------------------- */ +/* rec channel interface */ + +static void * +trrchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +{ + struct tr_info *tr = devinfo; + struct tr_rchinfo *ch; + + KASSERT(dir == PCMDIR_REC, ("trrchan_init: bad direction")); + ch = &tr->recchinfo; + ch->buffer = b; + ch->parent = tr; + ch->channel = c; + if (sndbuf_alloc(ch->buffer, tr->parent_dmat, TR_BUFFSIZE) == -1) + return NULL; + + return ch; +} + +static int +trrchan_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct tr_rchinfo *ch = data; struct tr_info *tr = ch->parent; - u_int32_t bits = tr_fmttobits(format); + u_int32_t i, bits; - ch->ss = 1; - ch->ss <<= (format & AFMT_STEREO)? 1 : 0; - ch->ss <<= (format & AFMT_16BIT)? 1 : 0; - if (ch->index >= 0) { - tr_rdch(tr, ch->index, ch); - ch->eso = (ch->buffer->bufsize / ch->ss) - 1; - ch->ctrl = bits | 0x01; - tr_wrch(tr, ch->index, ch); - } else { - u_int32_t i; - /* set # of samples between interrupts */ - i = (TR_INTSAMPLES >> ((bits & 0x08)? 1 : 0)) - 1; - tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4); - /* set sample format */ - i = 0x18 | (bits << 4); - tr_wr(tr, TR_REG_SBCTRL, i, 1); - } + bits = tr_fmttobits(format); + /* set # of samples between interrupts */ + i = (sndbuf_runsz(ch->buffer) >> ((bits & 0x08)? 1 : 0)) - 1; + tr_wr(tr, TR_REG_SBBL, i | (i << 16), 4); + /* set sample format */ + i = 0x18 | (bits << 4); + tr_wr(tr, TR_REG_SBCTRL, i, 1); + return 0; + } static int -trchan_setspeed(void *data, u_int32_t speed) +trrchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { - struct tr_chinfo *ch = data; + struct tr_rchinfo *ch = data; struct tr_info *tr = ch->parent; - if (ch->index >= 0) { - tr_rdch(tr, ch->index, ch); - ch->delta = (speed << 12) / 48000; - tr_wrch(tr, ch->index, ch); - return (ch->delta * 48000) >> 12; - } else { - /* setup speed */ - ch->delta = (48000 << 12) / speed; - tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2); - return (48000 << 12) / ch->delta; - } - return 0; + /* setup speed */ + ch->delta = (48000 << 12) / speed; + tr_wr(tr, TR_REG_SBDELTA, ch->delta, 2); + + /* return closest possible speed */ + return (48000 << 12) / ch->delta; } static int -trchan_setblocksize(void *data, u_int32_t blocksize) +trrchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { - struct tr_chinfo *ch = data; - return ch->buffer->bufsize / 2; + struct tr_rchinfo *ch = data; + + sndbuf_resize(ch->buffer, 2, blocksize); + + return blocksize; } static int -trchan_trigger(void *data, int go) +trrchan_trigger(kobj_t obj, void *data, int go) { - struct tr_chinfo *ch = data; + struct tr_rchinfo *ch = data; struct tr_info *tr = ch->parent; + u_int32_t i; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - if (ch->index >= 0) { - if (go == PCMTRIG_START) { - tr_rdch(tr, ch->index, ch); - ch->cso = 0; - tr_wrch(tr, ch->index, ch); - tr_startch(tr, ch->index); - } else tr_stopch(tr, ch->index); - } else { - u_int32_t i = tr_rd(tr, TR_REG_SBCTRL, 1) & ~7; - tr_wr(tr, TR_REG_SBCTRL, i | (go == PCMTRIG_START)? 1 : 0, 1); - } + if (go == PCMTRIG_START) { + /* set up dma mode regs */ + tr_wr(tr, TR_REG_DMAR15, 0, 1); + i = tr_rd(tr, TR_REG_DMAR11, 1) & 0x03; + tr_wr(tr, TR_REG_DMAR11, i | 0x54, 1); + /* set up base address */ + tr_wr(tr, TR_REG_DMAR0, vtophys(sndbuf_getbuf(ch->buffer)), 4); + /* set up buffer size */ + i = tr_rd(tr, TR_REG_DMAR4, 4) & ~0x00ffffff; + tr_wr(tr, TR_REG_DMAR4, i | (sndbuf_runsz(ch->buffer) - 1), 4); + /* start */ + tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) | 1, 1); + } else + tr_wr(tr, TR_REG_SBCTRL, tr_rd(tr, TR_REG_SBCTRL, 1) & ~7, 1); + + /* return 0 if ok */ return 0; } static int -trchan_getptr(void *data) +trrchan_getptr(kobj_t obj, void *data) { - struct tr_chinfo *ch = data; + struct tr_rchinfo *ch = data; struct tr_info *tr = ch->parent; - if (ch->index >= 0) { - tr_rdch(tr, ch->index, ch); - return ch->cso * ch->ss; - } else return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(ch->buffer->buf); + /* return current byte offset of channel */ + return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer)); } static pcmchan_caps * -trchan_getcaps(void *data) +trrchan_getcaps(kobj_t obj, void *data) { - struct tr_chinfo *ch = data; - return (ch->index >= 0)? &tr_playcaps : &tr_reccaps; + return &tr_reccaps; } +static kobj_method_t trrchan_methods[] = { + KOBJMETHOD(channel_init, trrchan_init), + KOBJMETHOD(channel_setformat, trrchan_setformat), + KOBJMETHOD(channel_setspeed, trrchan_setspeed), + KOBJMETHOD(channel_setblocksize, trrchan_setblocksize), + KOBJMETHOD(channel_trigger, trrchan_trigger), + KOBJMETHOD(channel_getptr, trrchan_getptr), + KOBJMETHOD(channel_getcaps, trrchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(trrchan); + +/* -------------------------------------------------------------------- */ /* The interrupt handler */ static void tr_intr(void *p) { struct tr_info *tr = (struct tr_info *)p; - u_int32_t intsrc = tr_rd(tr, TR_REG_MISCINT, 4); + struct tr_chinfo *ch; + u_int32_t active, mask, bufhalf, chnum, intsrc; + int tmp; + intsrc = tr_rd(tr, TR_REG_MISCINT, 4); if (intsrc & TR_INT_ADDR) { - int i; - for (i = 0; i < tr->playchns; i++) { - if (tr_testint(tr, i)) { - chn_intr(tr->chinfo[i].channel); - tr_clrint(tr, i); - } + chnum = 0; + while (chnum < 64) { + mask = 0x00000001; + active = tr_rd(tr, (chnum < 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, 4); + bufhalf = tr_rd(tr, (chnum < 32)? TR_REG_CSPF_A : TR_REG_CSPF_B, 4); + if (active) { + do { + if (active & mask) { + tmp = (bufhalf & mask)? 1 : 0; + if (chnum < tr->playchns) { + ch = &tr->chinfo[chnum]; + /* printf("%d @ %d, ", chnum, trpchan_getptr(NULL, ch)); */ + if (ch->bufhalf != tmp) { + chn_intr(ch->channel); + ch->bufhalf = tmp; + } else + printf("same bufhalf\n"); + + } + } + chnum++; + mask <<= 1; + } while (chnum & 31); + } else + chnum += 32; + + tr_wr(tr, (chnum <= 32)? TR_REG_ADDRINTA : TR_REG_ADDRINTB, active, 4); } } if (intsrc & TR_INT_SB) { chn_intr(tr->recchinfo.channel); tr_rd(tr, TR_REG_SBR9, 1); tr_rd(tr, TR_REG_SBR10, 1); } } /* -------------------------------------------------------------------- */ /* * Probe and attach the card */ static int tr_init(struct tr_info *tr) { if (tr->type == TDX_PCI_ID) { tr_wr(tr, TDX_REG_CODECST, TDX_CDC_ON, 4); } else tr_wr(tr, TNX_REG_CODECST, TNX_CDC_ON, 4); tr_wr(tr, TR_REG_CIR, TR_CIR_MIDENA | TR_CIR_ADDRENA, 4); tr->playchns = 0; return 0; } static int tr_pci_probe(device_t dev) { if (pci_get_devid(dev) == TDX_PCI_ID) { device_set_desc(dev, "Trident 4DWave DX"); return 0; } if (pci_get_devid(dev) == TNX_PCI_ID) { device_set_desc(dev, "Trident 4DWave NX"); return 0; } return ENXIO; } static int tr_pci_attach(device_t dev) { u_int32_t data; struct tr_info *tr; struct ac97_info *codec = 0; int i; - int mapped; char status[SND_STATUSLEN]; if ((tr = malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } bzero(tr, sizeof(*tr)); tr->type = pci_get_devid(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); - mapped = 0; - /* XXX dfr: is this strictly necessary? */ - for (i = 0; (mapped == 0) && (i < PCI_MAXMAPS_0); i++) { - tr->regid = PCIR_MAPS + i*4; - tr->regtype = SYS_RES_MEMORY; - tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, - 0, ~0, 1, RF_ACTIVE); - if (!tr->reg) { - tr->regtype = SYS_RES_IOPORT; - tr->reg = bus_alloc_resource(dev, tr->regtype, - &tr->regid, 0, ~0, 1, - RF_ACTIVE); - } - if (tr->reg) { - tr->st = rman_get_bustag(tr->reg); - tr->sh = rman_get_bushandle(tr->reg); - mapped++; - } - } - - if (mapped == 0) { + tr->regid = PCIR_MAPS; + tr->regtype = SYS_RES_IOPORT; + tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE); + if (tr->reg) { + tr->st = rman_get_bustag(tr->reg); + tr->sh = rman_get_bushandle(tr->reg); + } else { device_printf(dev, "unable to map register space\n"); goto bad; } if (tr_init(tr) == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } - codec = ac97_create(dev, tr, NULL, tr_rdcd, tr_wrcd); + codec = AC97_CREATE(dev, tr, tr_ac97); if (codec == NULL) goto bad; - if (mixer_init(dev, &ac97_mixer, codec) == -1) goto bad; + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; tr->irqid = 0; tr->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &tr->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!tr->irq || bus_setup_intr(dev, tr->irq, INTR_TYPE_TTY, tr_intr, tr, &tr->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/TR_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, &tr->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } - snprintf(status, 64, "at %s 0x%lx irq %ld", - (tr->regtype == SYS_RES_IOPORT)? "io" : "memory", + snprintf(status, 64, "at io 0x%lx irq %ld", rman_get_start(tr->reg), rman_get_start(tr->irq)); if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad; - pcm_addchan(dev, PCMDIR_REC, &tr_chantemplate, tr); + pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr); for (i = 0; i < TR_MAXPLAYCH; i++) - pcm_addchan(dev, PCMDIR_PLAY, &tr_chantemplate, tr); + pcm_addchan(dev, PCMDIR_PLAY, &trpchan_class, tr); pcm_setstatus(dev, status); return 0; bad: if (codec) ac97_destroy(codec); if (tr->reg) bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); if (tr->ih) bus_teardown_intr(dev, tr->irq, tr->ih); if (tr->irq) bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); if (tr->parent_dmat) bus_dma_tag_destroy(tr->parent_dmat); free(tr, M_DEVBUF); return ENXIO; } static int tr_pci_detach(device_t dev) { int r; struct tr_info *tr; r = pcm_unregister(dev); if (r) return r; tr = pcm_getdevinfo(dev); bus_release_resource(dev, tr->regtype, tr->regid, tr->reg); bus_teardown_intr(dev, tr->irq, tr->ih); bus_release_resource(dev, SYS_RES_IRQ, tr->irqid, tr->irq); bus_dma_tag_destroy(tr->parent_dmat); free(tr, M_DEVBUF); return 0; } static device_method_t tr_methods[] = { /* Device interface */ DEVMETHOD(device_probe, tr_pci_probe), DEVMETHOD(device_attach, tr_pci_attach), DEVMETHOD(device_detach, tr_pci_detach), { 0, 0 } }; static driver_t tr_driver = { "pcm", tr_methods, sizeof(snddev_info), }; static devclass_t pcm_devclass; DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0); MODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); MODULE_VERSION(snd_t4dwave, 1); Index: stable/4/sys/dev/sound/pci/t4dwave.h =================================================================== --- stable/4/sys/dev/sound/pci/t4dwave.h (revision 71948) +++ stable/4/sys/dev/sound/pci/t4dwave.h (revision 71949) @@ -1,95 +1,97 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _T4DWAVE_REG_H #define _T4DWAVE_REG_H #define TR_REG_CIR 0xa0 #define TR_CIR_MASK 0x0000003f #define TR_CIR_ADDRENA 0x00001000 #define TR_CIR_MIDENA 0x00002000 #define TR_REG_MISCINT 0xb0 #define TR_INT_ADDR 0x00000020 #define TR_INT_SB 0x00000004 #define TR_REG_DMAR0 0x00 #define TR_REG_DMAR4 0x04 #define TR_REG_DMAR11 0x0b #define TR_REG_DMAR15 0x0f #define TR_REG_SBR4 0x14 #define TR_REG_SBR5 0x15 #define TR_SB_INTSTATUS 0x82 #define TR_REG_SBR9 0x1e #define TR_REG_SBR10 0x1f #define TR_REG_SBBL 0xc0 #define TR_REG_SBCTRL 0xc4 #define TR_REG_SBDELTA 0xac #define TR_CDC_DATA 16 #define TDX_REG_CODECWR 0x40 #define TDX_REG_CODECRD 0x44 #define TDX_CDC_RWSTAT 0x00008000 #define TDX_REG_CODECST 0x48 #define TDX_CDC_SBCTRL 0x40 #define TDX_CDC_ACTIVE 0x20 #define TDX_CDC_READY 0x10 #define TDX_CDC_ADCON 0x08 #define TDX_CDC_DACON 0x02 #define TDX_CDC_RESET 0x01 #define TDX_CDC_ON (TDX_CDC_ADCON|TDX_CDC_DACON) #define TNX_REG_CODECWR 0x44 #define TNX_REG_CODEC1RD 0x48 #define TNX_REG_CODEC2RD 0x4c #define TNX_CDC_RWSTAT 0x00000c00 #define TNX_CDC_SEC 0x00000100 #define TNX_REG_CODECST 0x40 #define TNX_CDC_READY2 0x40 #define TNX_CDC_ADC2ON 0x20 #define TNX_CDC_DAC2ON 0x10 #define TNX_CDC_READY1 0x08 #define TNX_CDC_ADC1ON 0x04 #define TNX_CDC_DAC1ON 0x02 #define TNX_CDC_RESET 0x01 #define TNX_CDC_ON (TNX_CDC_ADC1ON|TNX_CDC_DAC1ON) #define TR_REG_STARTA 0x80 #define TR_REG_STOPA 0x84 +#define TR_REG_CSPF_A 0x90 #define TR_REG_ADDRINTA 0x98 #define TR_REG_INTENA 0xa4 #define TR_REG_STARTB 0xb4 #define TR_REG_STOPB 0xb8 +#define TR_REG_CSPF_B 0xbc #define TR_REG_ADDRINTB 0xd8 #define TR_REG_INTENB 0xdc #define TR_REG_CHNBASE 0xe0 #define TR_CHN_REGS 5 #endif Index: stable/4/sys/dev/sound/pci/via82c686.c =================================================================== --- stable/4/sys/dev/sound/pci/via82c686.c (revision 71948) +++ stable/4/sys/dev/sound/pci/via82c686.c (revision 71949) @@ -1,689 +1,689 @@ /* * Copyright (c) 2000 David Jones * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #define VIA_PCI_ID 0x30581106 #define NSEGS 16 /* Number of segments in SGD table */ #define SEGS_PER_CHAN (NSEGS/2) +#define TIMEOUT 50 +#define VIA_BUFFSIZE 0x4000 + #undef DEB #define DEB(x) +/* we rely on this struct being packed to 64 bits */ +struct via_dma_op { + u_int32_t ptr; + u_int32_t flags; +#define VIA_DMAOP_EOL 0x80000000 +#define VIA_DMAOP_FLAG 0x40000000 +#define VIA_DMAOP_STOP 0x20000000 +#define VIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF) +}; + struct via_info; struct via_chinfo { struct via_info *parent; pcm_channel *channel; snd_dbuf *buffer; int dir; }; struct via_info { bus_space_tag_t st; bus_space_handle_t sh; bus_dma_tag_t parent_dmat; bus_dma_tag_t sgd_dmat; struct resource *reg, *irq; int regid, irqid; void *ih; struct via_chinfo pch, rch; struct via_dma_op *sgd_table; u_int16_t codec_caps; }; static u_int32_t via_rd(struct via_info *via, int regno, int size); static void via_wr(struct via_info *, int regno, u_int32_t data, int size); -int via_waitready_codec(struct via_info *via); -int via_waitvalid_codec(struct via_info *via); -u_int32_t via_read_codec(void *addr, int reg); -void via_write_codec(void *addr, int reg, u_int32_t val); - static void via_intr(void *); bus_dmamap_callback_t dma_cb; - -/* channel interface */ -static void *viachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int viachan_setdir(void *data, int dir); -static int viachan_setformat(void *data, u_int32_t format); -static int viachan_setspeed(void *data, u_int32_t speed); -static int viachan_setblocksize(void *data, u_int32_t blocksize); -static int viachan_trigger(void *data, int go); -static int viachan_getptr(void *data); -static pcmchan_caps *viachan_getcaps(void *data); - static u_int32_t via_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps via_playcaps = {4000, 48000, via_playfmt, 0}; static u_int32_t via_recfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; static pcmchan_caps via_reccaps = {4000, 48000, via_recfmt, 0}; -static pcm_channel via_chantemplate = { - viachan_init, - viachan_setdir, - viachan_setformat, - viachan_setspeed, - viachan_setblocksize, - viachan_trigger, - viachan_getptr, - viachan_getcaps, - NULL, /* free */ - NULL, /* nop1 */ - NULL, /* nop2 */ - NULL, /* nop3 */ - NULL, /* nop4 */ - NULL, /* nop5 */ - NULL, /* nop6 */ - NULL, /* nop7 */ -}; - - -/* - * Probe and attach the card - */ -static int -via_probe(device_t dev) -{ - if (pci_get_devid(dev) == VIA_PCI_ID) { - device_set_desc(dev, "VIA VT82C686A AC'97 Audio"); - return 0; - } - return ENXIO; -} - - -void dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) -{ -} - - -static int -via_attach(device_t dev) -{ - struct via_info *via = 0; - struct ac97_info *codec = 0; - char status[SND_STATUSLEN]; - - u_int32_t data; - - u_int16_t v; - bus_dmamap_t sgd_dma_map; - - if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT)) == NULL) { - device_printf(dev, "cannot allocate softc\n"); - return ENXIO; - } - bzero(via, sizeof *via); - - /* Get resources */ - data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); - pci_write_config(dev, PCIR_COMMAND, data, 2); - data = pci_read_config(dev, PCIR_COMMAND, 2); - - pci_write_config(dev, VIA_PCICONF_MISC, - VIA_PCICONF_ACLINKENAB | VIA_PCICONF_ACSGD | - VIA_PCICONF_ACNOTRST | VIA_PCICONF_ACVSR, 1); - - via->regid = PCIR_MAPS; - via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, - 0, ~0, 1, RF_ACTIVE); - if (!via->reg) { - device_printf(dev, "via: Cannot allocate bus resource."); - goto bad; - } - via->st = rman_get_bustag(via->reg); - via->sh = rman_get_bushandle(via->reg); - - via->irqid = 0; - via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!via->irq - || bus_setup_intr(dev, via->irq, INTR_TYPE_TTY, via_intr, via, &via->ih)){ - device_printf(dev, "unable to map interrupt\n"); - goto bad; - } - - via_wr(via, VIA_PLAY_MODE, - VIA_RPMODE_AUTOSTART | - VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); - via_wr(via, VIA_RECORD_MODE, - VIA_RPMODE_AUTOSTART | - VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); - - codec = ac97_create(dev, via, NULL, - via_read_codec, via_write_codec); - if (!codec) goto bad; - - mixer_init(dev, &ac97_mixer, codec); - - /* - * The mixer init resets the codec. So enabling VRA must be done - * afterwards. - */ - v = via_read_codec(via, AC97_REG_EXT_AUDIO_ID); - v &= (AC97_ENAB_VRA | AC97_ENAB_MICVRA); - via_write_codec(via, AC97_REG_EXT_AUDIO_STAT, v); - via->codec_caps = v; - { - v = via_read_codec(via, AC97_REG_EXT_AUDIO_STAT); - DEB(printf("init: codec stat: %d\n", v)); - } - - if (!(v & AC97_CODEC_DOES_VRA)) { - /* no VRA => can do only 48 kbps */ - via_playcaps.minspeed = 48000; - via_reccaps.minspeed = 48000; - } - - /* DMA tag for buffers */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/VIA_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &via->parent_dmat) != 0) { - device_printf(dev, "unable to create dma tag\n"); - goto bad; - } - - /* - * DMA tag for SGD table. The 686 uses scatter/gather DMA and - * requires a list in memory of work to do. We need only 16 bytes - * for this list, and it is wasteful to allocate 16K. - */ - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, - /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/NSEGS * sizeof(struct via_dma_op), - /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &via->sgd_dmat) != 0) { - device_printf(dev, "unable to create dma tag\n"); - goto bad; - } - - if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, - BUS_DMA_NOWAIT, &sgd_dma_map) == -1) goto bad; - if (bus_dmamap_load(via->sgd_dmat, sgd_dma_map, via->sgd_table, - NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) goto bad; - - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", - rman_get_start(via->reg), rman_get_start(via->irq)); - - /* Register */ - if (pcm_register(dev, via, 1, 1)) goto bad; - pcm_addchan(dev, PCMDIR_PLAY, &via_chantemplate, via); - pcm_addchan(dev, PCMDIR_REC, &via_chantemplate, via); - pcm_setstatus(dev, status); - return 0; -bad: - if (codec) ac97_destroy(codec); - if (via->reg) bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); - if (via->ih) bus_teardown_intr(dev, via->irq, via->ih); - if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); - if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); - if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); - if (via) free(via, M_DEVBUF); - return ENXIO; -} - -static int -via_detach(device_t dev) -{ - int r; - struct via_info *via = 0; - - r = pcm_unregister(dev); - if (r) - return r; - - via = pcm_getdevinfo(dev); - bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); - bus_teardown_intr(dev, via->irq, via->ih); - bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); - bus_dma_tag_destroy(via->parent_dmat); - bus_dma_tag_destroy(via->sgd_dmat); - free(via, M_DEVBUF); - return 0; -} - - -static device_method_t via_methods[] = { - DEVMETHOD(device_probe, via_probe), - DEVMETHOD(device_attach, via_attach), - DEVMETHOD(device_detach, via_detach), - { 0, 0} -}; - -static driver_t via_driver = { - "pcm", - via_methods, - sizeof(snddev_info), -}; - -static devclass_t pcm_devclass; - -DRIVER_MODULE(via, pci, via_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(via, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); -MODULE_VERSION(via, 1); - - static u_int32_t via_rd(struct via_info *via, int regno, int size) { switch (size) { case 1: return bus_space_read_1(via->st, via->sh, regno); case 2: return bus_space_read_2(via->st, via->sh, regno); case 4: return bus_space_read_4(via->st, via->sh, regno); default: return 0xFFFFFFFF; } } static void via_wr(struct via_info *via, int regno, u_int32_t data, int size) { switch (size) { case 1: bus_space_write_1(via->st, via->sh, regno, data); break; case 2: bus_space_write_2(via->st, via->sh, regno, data); break; case 4: bus_space_write_4(via->st, via->sh, regno, data); break; } } - +/* -------------------------------------------------------------------- */ /* Codec interface */ -int + +static int via_waitready_codec(struct via_info *via) { int i; /* poll until codec not busy */ for (i = 0; (i < TIMEOUT) && (via_rd(via, VIA_CODEC_CTL, 4) & VIA_CODEC_BUSY); i++) DELAY(1); if (i >= TIMEOUT) { printf("via: codec busy\n"); return 1; } return 0; } -int +static int via_waitvalid_codec(struct via_info *via) { int i; /* poll until codec valid */ for (i = 0; (i < TIMEOUT) && !(via_rd(via, VIA_CODEC_CTL, 4) & VIA_CODEC_PRIVALID); i++) DELAY(1); if (i >= TIMEOUT) { printf("via: codec invalid\n"); return 1; } return 0; } -void -via_write_codec(void *addr, int reg, u_int32_t val) +static int +via_write_codec(kobj_t obj, void *addr, int reg, u_int32_t val) { struct via_info *via = addr; - if (via_waitready_codec(via)) return; + if (via_waitready_codec(via)) return -1; via_wr(via, VIA_CODEC_CTL, VIA_CODEC_PRIVALID | VIA_CODEC_INDEX(reg) | val, 4); + + return 0; } -u_int32_t -via_read_codec(void *addr, int reg) +static int +via_read_codec(kobj_t obj, void *addr, int reg) { struct via_info *via = addr; if (via_waitready_codec(via)) return 1; via_wr(via, VIA_CODEC_CTL, VIA_CODEC_PRIVALID | VIA_CODEC_READ | VIA_CODEC_INDEX(reg),4); if (via_waitready_codec(via)) return 1; if (via_waitvalid_codec(via)) return 1; return via_rd(via, VIA_CODEC_CTL, 2); } +static kobj_method_t via_ac97_methods[] = { + KOBJMETHOD(ac97_read, via_read_codec), + KOBJMETHOD(ac97_write, via_write_codec), + { 0, 0 } +}; +AC97_DECLARE(via_ac97); +/* -------------------------------------------------------------------- */ + /* channel interface */ static void * -viachan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +viachan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { struct via_info *via = devinfo; struct via_chinfo *ch = (dir == PCMDIR_PLAY) ? &via->pch : &via->rch; ch->parent = via; ch->channel = c; ch->buffer = b; - b->bufsize = VIA_BUFFSIZE; - if (chn_allocbuf(ch->buffer, via->parent_dmat) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, via->parent_dmat, VIA_BUFFSIZE) == -1) return NULL; return ch; } static int -viachan_setdir(void *data, int dir) +viachan_setdir(kobj_t obj, void *data, int dir) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; struct via_dma_op *ado; int i, chunk_size; int phys_addr, flag; ch->dir = dir; /* * Build the scatter/gather DMA (SGD) table. * There are four slots in the table: two for play, two for record. * This creates two half-buffers, one of which is playing; the other * is feeding. */ ado = via->sgd_table; - chunk_size = ch->buffer->bufsize / SEGS_PER_CHAN; + chunk_size = sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN; if (dir == PCMDIR_REC) { ado += SEGS_PER_CHAN; } -DEB(printf("SGD table located at va %p\n", ado)); - phys_addr = vtophys(ch->buffer->buf); + DEB(printf("SGD table located at va %p\n", ado)); + phys_addr = vtophys(sndbuf_getbuf(ch->buffer)); for (i = 0; i < SEGS_PER_CHAN; i++) { ado->ptr = phys_addr; flag = (i == SEGS_PER_CHAN-1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG; ado->flags = flag | chunk_size; -DEB(printf("ado->ptr/flags = %x/%x\n", phys_addr, flag)); + DEB(printf("ado->ptr/flags = %x/%x\n", phys_addr, flag)); phys_addr += chunk_size; ado++; } return 0; } static int -viachan_setformat(void *data, u_int32_t format) +viachan_setformat(kobj_t obj, void *data, u_int32_t format) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; int mode, mode_set; mode_set = 0; if (format & AFMT_STEREO) mode_set |= VIA_RPMODE_STEREO; if (format & AFMT_S16_LE) mode_set |= VIA_RPMODE_16BIT; /* Set up for output format */ if (ch->dir == PCMDIR_PLAY) { -DEB(printf("set play format: %x\n", format)); + DEB(printf("set play format: %x\n", format)); mode = via_rd(via, VIA_PLAY_MODE, 1); mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO); mode |= mode_set; via_wr(via, VIA_PLAY_MODE, mode, 1); } else { -DEB(printf("set record format: %x\n", format)); + DEB(printf("set record format: %x\n", format)); mode = via_rd(via, VIA_RECORD_MODE, 1); mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO); mode |= mode_set; via_wr(via, VIA_RECORD_MODE, mode, 1); } return 0; } static int -viachan_setspeed(void *data, u_int32_t speed) +viachan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; /* * Basic AC'97 defines a 48 kHz sample rate only. For other rates, * upsampling is required. * * The VT82C686A does not perform upsampling, and neither do we. * If the codec supports variable-rate audio (i.e. does the upsampling * itself), then negotiate the rate with the codec. Otherwise, * return 48 kHz cuz that's all you got. */ if (ch->dir == PCMDIR_PLAY) { -DEB(printf("requested play speed: %d\n", speed)); + DEB(printf("requested play speed: %d\n", speed)); if (via->codec_caps & AC97_CODEC_DOES_VRA) { - via_write_codec(via, AC97_REG_EXT_DAC_RATE, speed); - speed = via_read_codec(via, AC97_REG_EXT_DAC_RATE); + via_write_codec(NULL, via, AC97_REG_EXT_DAC_RATE, speed); + speed = via_read_codec(NULL, via, AC97_REG_EXT_DAC_RATE); } else { -DEB(printf("VRA not supported!\n")); + DEB(printf("VRA not supported!\n")); speed = 48000; } -DEB(printf("obtained play speed: %d\n", speed)); + DEB(printf("obtained play speed: %d\n", speed)); } else { -DEB(printf("requested record speed: %d\n", speed)); + DEB(printf("requested record speed: %d\n", speed)); if (via->codec_caps & AC97_CODEC_DOES_VRA) { - via_write_codec(via, AC97_REG_EXT_ADC_RATE, speed); - speed = via_read_codec(via, AC97_REG_EXT_ADC_RATE); + via_write_codec(NULL, via, AC97_REG_EXT_ADC_RATE, speed); + speed = via_read_codec(NULL, via, AC97_REG_EXT_ADC_RATE); } else { -DEB(printf("VRA not supported!\n")); + DEB(printf("VRA not supported!\n")); speed = 48000; } -DEB(printf("obtained record speed: %d\n", speed)); + DEB(printf("obtained record speed: %d\n", speed)); } return speed; } static int -viachan_setblocksize(void *data, u_int32_t blocksize) +viachan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct via_chinfo *ch = data; - return ch->buffer->bufsize / 2; + return sndbuf_getsize(ch->buffer) / 2; } static int -viachan_trigger(void *data, int go) +viachan_trigger(kobj_t obj, void *data, int go) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; struct via_dma_op *ado; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { ado = &via->sgd_table[0]; -DEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); + DEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); via_wr(via, VIA_PLAY_DMAOPS_BASE, vtophys(ado),4); via_wr(via, VIA_PLAY_CONTROL, VIA_RPCTRL_START, 1); } else { /* Stop DMA */ via_wr(via, VIA_PLAY_CONTROL, VIA_RPCTRL_TERMINATE, 1); } } else { if (go == PCMTRIG_START) { ado = &via->sgd_table[SEGS_PER_CHAN]; -DEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); + DEB(printf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); via_wr(via, VIA_RECORD_DMAOPS_BASE, vtophys(ado),4); via_wr(via, VIA_RECORD_CONTROL, VIA_RPCTRL_START, 1); } else { /* Stop DMA */ via_wr(via, VIA_RECORD_CONTROL, VIA_RPCTRL_TERMINATE, 1); } } DEB(printf("viachan_trigger: go=%d\n", go)); return 0; } static int -viachan_getptr(void *data) +viachan_getptr(kobj_t obj, void *data) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; struct via_dma_op *ado; int ptr, base, len, seg; int base1; if (ch->dir == PCMDIR_PLAY) { ado = &via->sgd_table[0]; base1 = via_rd(via, VIA_PLAY_DMAOPS_BASE, 4); len = via_rd(via, VIA_PLAY_DMAOPS_COUNT, 4); base = via_rd(via, VIA_PLAY_DMAOPS_BASE, 4); if (base != base1) { /* Avoid race hazzard */ len = via_rd(via, VIA_PLAY_DMAOPS_COUNT, 4); } -DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base)); + DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base)); /* Base points to SGD segment to do, one past current */ /* Determine how many segments have been done */ seg = (base - vtophys(ado)) / sizeof(struct via_dma_op); if (seg == 0) seg = SEGS_PER_CHAN; /* Now work out offset: seg less count */ - ptr = seg * ch->buffer->bufsize / SEGS_PER_CHAN - len; -DEB(printf("return ptr=%d\n", ptr)); + ptr = seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN - len; + DEB(printf("return ptr=%d\n", ptr)); return ptr; } else { base1 = via_rd(via, VIA_RECORD_DMAOPS_BASE, 4); ado = &via->sgd_table[SEGS_PER_CHAN]; len = via_rd(via, VIA_RECORD_DMAOPS_COUNT, 4); base = via_rd(via, VIA_RECORD_DMAOPS_BASE, 4); if (base != base1) { /* Avoid race hazzard */ len = via_rd(via, VIA_RECORD_DMAOPS_COUNT, 4); } -DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base)); + DEB(printf("viachan_getptr: len / base = %x / %x\n", len, base)); /* Base points to next block to do, one past current */ /* Determine how many segments have been done */ seg = (base - vtophys(ado)) / sizeof(struct via_dma_op); if (seg == 0) seg = SEGS_PER_CHAN; /* Now work out offset: seg less count */ - ptr = seg * ch->buffer->bufsize / SEGS_PER_CHAN - len; + ptr = seg * sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN - len; /* DMA appears to operate on memory 'lines' of 32 bytes */ /* so don't return any part line - it isn't in RAM yet */ ptr = ptr & ~0x1f; -DEB(printf("return ptr=%d\n", ptr)); + DEB(printf("return ptr=%d\n", ptr)); return ptr; } return 0; } static pcmchan_caps * -viachan_getcaps(void *data) +viachan_getcaps(kobj_t obj, void *data) { struct via_chinfo *ch = data; return (ch->dir == PCMDIR_PLAY) ? &via_playcaps : &via_reccaps; } +static kobj_method_t viachan_methods[] = { + KOBJMETHOD(channel_init, viachan_init), + KOBJMETHOD(channel_setdir, viachan_setdir), + KOBJMETHOD(channel_setformat, viachan_setformat), + KOBJMETHOD(channel_setspeed, viachan_setspeed), + KOBJMETHOD(channel_setblocksize, viachan_setblocksize), + KOBJMETHOD(channel_trigger, viachan_trigger), + KOBJMETHOD(channel_getptr, viachan_getptr), + KOBJMETHOD(channel_getcaps, viachan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(viachan); + +/* -------------------------------------------------------------------- */ + static void via_intr(void *p) { struct via_info *via = p; int st; -DEB(printf("viachan_intr\n")); + /* DEB(printf("viachan_intr\n")); */ /* Read channel */ st = via_rd(via, VIA_PLAY_STAT, 1); if (st & VIA_RPSTAT_INTR) { via_wr(via, VIA_PLAY_STAT, VIA_RPSTAT_INTR, 1); chn_intr(via->pch.channel); } /* Write channel */ st = via_rd(via, VIA_RECORD_STAT, 1); if (st & VIA_RPSTAT_INTR) { via_wr(via, VIA_RECORD_STAT, VIA_RPSTAT_INTR, 1); chn_intr(via->rch.channel); } } + +/* + * Probe and attach the card + */ +static int +via_probe(device_t dev) +{ + if (pci_get_devid(dev) == VIA_PCI_ID) { + device_set_desc(dev, "VIA VT82C686A"); + return 0; + } + return ENXIO; +} + + +void dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) +{ +} + + +static int +via_attach(device_t dev) +{ + struct via_info *via = 0; + struct ac97_info *codec = 0; + char status[SND_STATUSLEN]; + + u_int32_t data; + + u_int16_t v; + bus_dmamap_t sgd_dma_map; + + if ((via = malloc(sizeof *via, M_DEVBUF, M_NOWAIT)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + bzero(via, sizeof *via); + + /* Get resources */ + data = pci_read_config(dev, PCIR_COMMAND, 2); + data |= (PCIM_CMD_PORTEN | PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, data, 2); + data = pci_read_config(dev, PCIR_COMMAND, 2); + + pci_write_config(dev, VIA_PCICONF_MISC, + VIA_PCICONF_ACLINKENAB | VIA_PCICONF_ACSGD | + VIA_PCICONF_ACNOTRST | VIA_PCICONF_ACVSR, 1); + + via->regid = PCIR_MAPS; + via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, + 0, ~0, 1, RF_ACTIVE); + if (!via->reg) { + device_printf(dev, "cannot allocate bus resource."); + goto bad; + } + via->st = rman_get_bustag(via->reg); + via->sh = rman_get_bushandle(via->reg); + + via->irqid = 0; + via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, + 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + if (!via->irq + || bus_setup_intr(dev, via->irq, INTR_TYPE_TTY, via_intr, via, &via->ih)){ + device_printf(dev, "unable to map interrupt\n"); + goto bad; + } + + via_wr(via, VIA_PLAY_MODE, + VIA_RPMODE_AUTOSTART | + VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); + via_wr(via, VIA_RECORD_MODE, + VIA_RPMODE_AUTOSTART | + VIA_RPMODE_INTR_FLAG | VIA_RPMODE_INTR_EOL, 1); + + codec = AC97_CREATE(dev, via, via_ac97); + if (!codec) goto bad; + + mixer_init(dev, ac97_getmixerclass(), codec); + + /* + * The mixer init resets the codec. So enabling VRA must be done + * afterwards. + */ + v = via_read_codec(NULL, via, AC97_REG_EXT_AUDIO_ID); + v &= (AC97_ENAB_VRA | AC97_ENAB_MICVRA); + via_write_codec(NULL, via, AC97_REG_EXT_AUDIO_STAT, v); + via->codec_caps = v; + { + v = via_read_codec(NULL, via, AC97_REG_EXT_AUDIO_STAT); + DEB(printf("init: codec stat: %d\n", v)); + } + + if (!(v & AC97_CODEC_DOES_VRA)) { + /* no VRA => can do only 48 kbps */ + via_playcaps.minspeed = 48000; + via_reccaps.minspeed = 48000; + } + + /* DMA tag for buffers */ + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/VIA_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, &via->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } + + /* + * DMA tag for SGD table. The 686 uses scatter/gather DMA and + * requires a list in memory of work to do. We need only 16 bytes + * for this list, and it is wasteful to allocate 16K. + */ + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/NSEGS * sizeof(struct via_dma_op), + /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, &via->sgd_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } + + if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, + BUS_DMA_NOWAIT, &sgd_dma_map) == -1) goto bad; + if (bus_dmamap_load(via->sgd_dmat, sgd_dma_map, via->sgd_table, + NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) goto bad; + + snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", + rman_get_start(via->reg), rman_get_start(via->irq)); + + /* Register */ + if (pcm_register(dev, via, 1, 1)) goto bad; + pcm_addchan(dev, PCMDIR_PLAY, &viachan_class, via); + pcm_addchan(dev, PCMDIR_REC, &viachan_class, via); + pcm_setstatus(dev, status); + return 0; +bad: + if (codec) ac97_destroy(codec); + if (via->reg) bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); + if (via->ih) bus_teardown_intr(dev, via->irq, via->ih); + if (via->irq) bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); + if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); + if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); + if (via) free(via, M_DEVBUF); + return ENXIO; +} + +static int +via_detach(device_t dev) +{ + int r; + struct via_info *via = 0; + + r = pcm_unregister(dev); + if (r) + return r; + + via = pcm_getdevinfo(dev); + bus_release_resource(dev, SYS_RES_IOPORT, via->regid, via->reg); + bus_teardown_intr(dev, via->irq, via->ih); + bus_release_resource(dev, SYS_RES_IRQ, via->irqid, via->irq); + bus_dma_tag_destroy(via->parent_dmat); + bus_dma_tag_destroy(via->sgd_dmat); + free(via, M_DEVBUF); + return 0; +} + + +static device_method_t via_methods[] = { + DEVMETHOD(device_probe, via_probe), + DEVMETHOD(device_attach, via_attach), + DEVMETHOD(device_detach, via_detach), + { 0, 0} +}; + +static driver_t via_driver = { + "pcm", + via_methods, + sizeof(snddev_info), +}; + +static devclass_t pcm_devclass; + +DRIVER_MODULE(via, pci, via_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(via, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_VERSION(via, 1); Index: stable/4/sys/dev/sound/pci/via82c686.h =================================================================== --- stable/4/sys/dev/sound/pci/via82c686.h (revision 71948) +++ stable/4/sys/dev/sound/pci/via82c686.h (revision 71949) @@ -1,108 +1,96 @@ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Tyler C. Sarna. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _VIA_H #define _VIA_H /* * VIA Technologies VT82C686A Southbridge Audio Driver * * Documentation links: * * ftp://ftp.alsa-project.org/pub/manuals/via/686a.pdf * ftp://ftp.alsa-project.org/pub/manuals/general/ac97r21.pdf * ftp://ftp.alsa-project.org/pub/manuals/ad/AD1881_0.pdf (example AC'97 codec) */ -struct via_dma_op { - u_int32_t ptr; - u_int32_t flags; -#define VIA_DMAOP_EOL 0x80000000 -#define VIA_DMAOP_FLAG 0x40000000 -#define VIA_DMAOP_STOP 0x20000000 -#define VIA_DMAOP_COUNT(x) ((x)&0x00FFFFFF) -}; - #define VIA_PCICONF_MISC 0x41 #define VIA_PCICONF_ACLINKENAB 0x80 /* ac link enab */ #define VIA_PCICONF_ACNOTRST 0x40 /* ~(ac reset) */ #define VIA_PCICONF_ACSYNC 0x20 /* ac sync */ #define VIA_PCICONF_ACVSR 0x08 /* var. samp. rate */ #define VIA_PCICONF_ACSGD 0x04 /* SGD enab */ #define VIA_PCICONF_ACFM 0x02 /* FM enab */ #define VIA_PCICONF_ACSB 0x01 /* SB enab */ #define VIA_PCICONF_FUNC_EN 0x42 #define VIA_PLAY_STAT 0x00 #define VIA_RECORD_STAT 0x10 #define VIA_RPSTAT_INTR 0x03 #define VIA_PLAY_CONTROL 0x01 #define VIA_RECORD_CONTROL 0x11 #define VIA_RPCTRL_START 0x80 #define VIA_RPCTRL_TERMINATE 0x40 #define VIA_PLAY_MODE 0x02 #define VIA_RECORD_MODE 0x12 #define VIA_RPMODE_INTR_FLAG 0x01 #define VIA_RPMODE_INTR_EOL 0x02 #define VIA_RPMODE_STEREO 0x10 #define VIA_RPMODE_16BIT 0x20 #define VIA_RPMODE_AUTOSTART 0x80 #define VIA_PLAY_DMAOPS_BASE 0x04 #define VIA_RECORD_DMAOPS_BASE 0x14 #define VIA_PLAY_DMAOPS_COUNT 0x0C #define VIA_RECORD_DMAOPS_COUNT 0x1C #define VIA_CODEC_CTL 0x80 #define VIA_CODEC_READ 0x00800000 #define VIA_CODEC_BUSY 0x01000000 #define VIA_CODEC_PRIVALID 0x02000000 #define VIA_CODEC_INDEX(x) ((x)<<16) #define AC97_REG_EXT_AUDIO_ID 0x28 #define AC97_CODEC_DOES_VRA 0x0001 #define AC97_CODEC_DOES_MICVRA 0x0008 #define AC97_REG_EXT_AUDIO_STAT 0x2A #define AC97_ENAB_VRA 0x0001 #define AC97_ENAB_MICVRA 0x0008 #define AC97_REG_EXT_DAC_RATE 0x2C #define AC97_REG_EXT_ADC_RATE 0x32 - -#define TIMEOUT 50 -#define VIA_BUFFSIZE 0x4000 #endif /* _VIA_H */ Index: stable/4/sys/dev/sound/pcm/ac97.c =================================================================== --- stable/4/sys/dev/sound/pcm/ac97.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/ac97.c (revision 71949) @@ -1,478 +1,527 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include -struct ac97mixtable_entry { - int reg:8; - unsigned bits:4; - unsigned ofs:4; - unsigned stereo:1; - unsigned mute:1; - unsigned recidx:4; - unsigned mask:1; - unsigned enable:1; -}; +#include "mixer_if.h" -struct ac97_info { - device_t dev; - ac97_init *init; - ac97_read *read; - ac97_write *write; - void *devinfo; - char *name; - char rev; - unsigned count, caps, se, extcaps, extid, extstat, noext:1; - struct ac97mixtable_entry mix[32]; -}; +MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); struct ac97_codecid { u_int32_t id, noext:1; char *name; }; static const struct ac97mixtable_entry ac97mixtable_default[32] = { [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, [SOUND_MIXER_MONITOR] = { AC97_MIX_PHONES, 5, 0, 1, 1, 0, 0, 0 }, [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } }; static struct ac97_codecid ac97codecid[] = { { 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" }, { 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" }, { 0x43525900, 0, "Cirrus Logic CS4297" }, { 0x83847600, 0, "SigmaTel STAC????" }, { 0x83847604, 0, "SigmaTel STAC9701/3/4/5" }, { 0x83847605, 0, "SigmaTel STAC9704" }, { 0x83847608, 0, "SigmaTel STAC9708" }, { 0x83847609, 0, "SigmaTel STAC9721" }, { 0, 0, NULL } }; static char *ac97enhancement[] = { "no 3D Stereo Enhancement", "Analog Devices Phat Stereo", "Creative Stereo Enhancement", "National Semi 3D Stereo Enhancement", "Yamaha Ymersion", "BBE 3D Stereo Enhancement", "Crystal Semi 3D Stereo Enhancement", "Qsound QXpander", "Spatializer 3D Stereo Enhancement", "SRS 3D Stereo Enhancement", "Platform Tech 3D Stereo Enhancement", "AKM 3D Audio", "Aureal Stereo Enhancement", "Aztech 3D Enhancement", "Binaura 3D Audio Enhancement", "ESS Technology Stereo Enhancement", "Harman International VMAx", "Nvidea 3D Stereo Enhancement", "Philips Incredible Sound", "Texas Instruments 3D Stereo Enhancement", "VLSI Technology 3D Stereo Enhancement", "TriTech 3D Stereo Enhancement", "Realtek 3D Stereo Enhancement", "Samsung 3D Stereo Enhancement", "Wolfson Microelectronics 3D Enhancement", "Delta Integration 3D Enhancement", "SigmaTel 3D Enhancement", "Reserved 27", "Rockwell 3D Stereo Enhancement", "Reserved 29", "Reserved 30", "Reserved 31" }; static char *ac97feature[] = { "mic channel", "reserved", "tone", "simulated stereo", "headphone", "bass boost", "18 bit DAC", "20 bit DAC", "18 bit ADC", "20 bit ADC" }; static char *ac97extfeature[] = { "variable rate PCM", "double rate PCM", "reserved 1", "variable rate mic", "reserved 2", "reserved 3", "center DAC", "surround DAC", "LFE DAC", "AMAP", "reserved 4", "reserved 5", "reserved 6", "reserved 7", }; static u_int16_t rdcd(struct ac97_info *codec, int reg) { - return codec->read(codec->devinfo, reg); + return AC97_READ(codec->methods, codec->devinfo, reg); } static void wrcd(struct ac97_info *codec, int reg, u_int16_t val) { - codec->write(codec->devinfo, reg, val); + AC97_WRITE(codec->methods, codec->devinfo, reg, val); } int ac97_setrate(struct ac97_info *codec, int which, int rate) { u_int16_t v; switch(which) { case AC97_REGEXT_FDACRATE: case AC97_REGEXT_SDACRATE: case AC97_REGEXT_LDACRATE: case AC97_REGEXT_LADCRATE: case AC97_REGEXT_MADCRATE: break; default: return -1; } if (rate != 0) { v = rate; if (codec->extstat & AC97_EXTCAP_DRA) v >>= 1; wrcd(codec, which, v); } v = rdcd(codec, which); if (codec->extstat & AC97_EXTCAP_DRA) v <<= 1; return v; } int ac97_setextmode(struct ac97_info *codec, u_int16_t mode) { mode &= AC97_EXTCAPS; if ((mode & ~codec->extcaps) != 0) return -1; wrcd(codec, AC97_REGEXT_STAT, mode); codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; return (mode == codec->extstat)? 0 : -1; } +u_int16_t +ac97_getextmode(struct ac97_info *codec) +{ + return codec->extstat; +} + +u_int16_t +ac97_getextcaps(struct ac97_info *codec) +{ + return codec->extcaps; +} + static int ac97_setrecsrc(struct ac97_info *codec, int channel) { struct ac97mixtable_entry *e = &codec->mix[channel]; if (e->recidx > 0) { int val = e->recidx - 1; val |= val << 8; wrcd(codec, AC97_REG_RECSEL, val); return 0; } else return -1; } static int ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) { struct ac97mixtable_entry *e = &codec->mix[channel]; if (e->reg && e->enable && e->bits) { int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; if (!e->stereo) right = left; if (e->reg > 0) { left = 100 - left; right = 100 - right; } max = (1 << e->bits) - 1; left = (left * max) / 100; right = (right * max) / 100; val = (left << 8) | right; left = (left * 100) / max; right = (right * 100) / max; if (e->reg > 0) { left = 100 - left; right = 100 - right; } if (!e->stereo) { val &= max; val <<= e->ofs; if (e->mask) { int cur = rdcd(codec, e->reg); val |= cur & ~(max << e->ofs); } } if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE; wrcd(codec, reg, val); return left | (right << 8); } else { /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ return -1; } } #if 0 static int ac97_getmixer(struct ac97_info *codec, int channel) { struct ac97mixtable_entry *e = &codec->mix[channel]; if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { int max, val, volume; max = (1 << e->bits) - 1; val = rdcd(code, e->reg); if (val == AC97_MUTE && e->mute == 1) volume = 0; else { if (e->stereo == 0) val >>= e->ofs; val &= max; volume = (val * 100) / max; if (e->reg > 0) volume = 100 - volume; } return volume; } else return -1; } #endif static unsigned ac97_initmixer(struct ac97_info *codec) { unsigned i, j, k, old; u_int32_t id; for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i]; - if (codec->init) { - codec->count = codec->init(codec->devinfo); - if (codec->count == 0) { - device_printf(codec->dev, "ac97 codec init failed\n"); - return ENODEV; - } - } else - codec->count = 1; + codec->count = AC97_INIT(codec->methods, codec->devinfo); + if (codec->count == 0) { + device_printf(codec->dev, "ac97 codec init failed\n"); + return ENODEV; + } + wrcd(codec, AC97_REG_POWER, 0); wrcd(codec, AC97_REG_RESET, 0); DELAY(100000); i = rdcd(codec, AC97_REG_RESET); codec->caps = i & 0x03ff; codec->se = (i & 0x7c00) >> 10; id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); codec->rev = id & 0x000000ff; if (id == 0 || id == 0xffffffff) { device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); return ENODEV; } codec->noext = 0; codec->name = NULL; for (i = 0; ac97codecid[i].id; i++) { if (ac97codecid[i].id == id) { codec->name = ac97codecid[i].name; codec->noext = ac97codecid[i].noext; } } - if (codec->noext) { - codec->extcaps = 0; - codec->extid = 0; - codec->extstat = 0; - } else { + codec->extcaps = 0; + codec->extid = 0; + codec->extstat = 0; + if (!codec->noext) { i = rdcd(codec, AC97_REGEXT_ID); - codec->extcaps = i & 0x3fff; - codec->extid = (i & 0xc000) >> 14; - codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; + if (i != 0xffff) { + codec->extcaps = i & 0x3fff; + codec->extid = (i & 0xc000) >> 14; + codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; + } } for (i = 0; i < 32; i++) { k = codec->noext? codec->mix[i].enable : 1; if (k && (codec->mix[i].reg > 0)) { old = rdcd(codec, codec->mix[i].reg); wrcd(codec, codec->mix[i].reg, 0x3f); j = rdcd(codec, codec->mix[i].reg); wrcd(codec, codec->mix[i].reg, old); - codec->mix[i].enable = j? 1 : 0; + codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; for (k = 1; j & (1 << k); k++); codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; } /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ } if (bootverbose) { device_printf(codec->dev, "ac97 codec id 0x%08x", id); if (codec->name) printf(" (%s)", codec->name); printf("\n"); device_printf(codec->dev, "ac97 codec features "); for (i = j = 0; i < 10; i++) if (codec->caps & (1 << i)) printf("%s%s", j++? ", " : "", ac97feature[i]); printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); if (codec->extcaps != 0 || codec->extid) { device_printf(codec->dev, "ac97 %s codec", codec->extid? "secondary" : "primary"); if (codec->extcaps) printf(" extended features "); for (i = j = 0; i < 14; i++) if (codec->extcaps & (1 << i)) printf("%s%s", j++? ", " : "", ac97extfeature[i]); printf("\n"); } } if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) device_printf(codec->dev, "ac97 codec reports dac not ready\n"); return 0; } +static unsigned +ac97_reinitmixer(struct ac97_info *codec) +{ + unsigned i; + + codec->count = AC97_INIT(codec->methods, codec->devinfo); + if (codec->count == 0) { + device_printf(codec->dev, "ac97 codec init failed\n"); + return ENODEV; + } + + wrcd(codec, AC97_REG_POWER, 0); + wrcd(codec, AC97_REG_RESET, 0); + DELAY(100000); + i = rdcd(codec, AC97_REG_RESET); + + if (!codec->noext) { + wrcd(codec, AC97_REGEXT_STAT, codec->extstat); + if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) + device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", + codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); + } + + if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) + device_printf(codec->dev, "ac97 codec reports dac not ready\n"); + return 0; +} + struct ac97_info * -ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr) +ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { struct ac97_info *codec; - codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); - if (codec != NULL) { - codec->dev = dev; - codec->init = init; - codec->read = rd; - codec->write = wr; - codec->devinfo = devinfo; + codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); + if (codec == NULL) + return NULL; + + codec->methods = kobj_create(cls, M_AC97, M_WAITOK); + if (codec->methods == NULL) { + free(codec, M_AC97); + return NULL; } + + codec->dev = dev; + codec->devinfo = devinfo; return codec; } void ac97_destroy(struct ac97_info *codec) { - free(codec, M_DEVBUF); + if (codec->methods != NULL) + kobj_delete(codec->methods, M_AC97); + free(codec, M_AC97); } +/* -------------------------------------------------------------------- */ + static int ac97mix_init(snd_mixer *m) { struct ac97_info *codec = mix_getdevinfo(m); u_int32_t i, mask; if (codec == NULL) return -1; if (ac97_initmixer(codec)) return -1; mask = 0; for (i = 0; i < 32; i++) mask |= codec->mix[i].enable? 1 << i : 0; mix_setdevs(m, mask); mask = 0; for (i = 0; i < 32; i++) mask |= codec->mix[i].recidx? 1 << i : 0; mix_setrecdevs(m, mask); return 0; } static int ac97mix_uninit(snd_mixer *m) { struct ac97_info *codec = mix_getdevinfo(m); if (codec == NULL) return -1; /* if (ac97_uninitmixer(codec)) return -1; */ ac97_destroy(codec); return 0; } static int +ac97mix_reinit(snd_mixer *m) +{ + struct ac97_info *codec = mix_getdevinfo(m); + + if (codec == NULL) + return -1; + return ac97_reinitmixer(codec); +} + +static int ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) { struct ac97_info *codec = mix_getdevinfo(m); if (codec == NULL) return -1; return ac97_setmixer(codec, dev, left, right); } static int ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) { int i; struct ac97_info *codec = mix_getdevinfo(m); if (codec == NULL) return -1; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if ((src & (1 << i)) != 0) break; return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; } -snd_mixer ac97_mixer = { - "AC97 mixer", - ac97mix_init, - ac97mix_uninit, - ac97mix_set, - ac97mix_setrecsrc, +static kobj_method_t ac97mixer_methods[] = { + KOBJMETHOD(mixer_init, ac97mix_init), + KOBJMETHOD(mixer_uninit, ac97mix_uninit), + KOBJMETHOD(mixer_reinit, ac97mix_reinit), + KOBJMETHOD(mixer_set, ac97mix_set), + KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), + { 0, 0 } }; +MIXER_DECLARE(ac97mixer); + +/* -------------------------------------------------------------------- */ + +kobj_class_t +ac97_getmixerclass(void) +{ + return &ac97mixer_class; +} + Index: stable/4/sys/dev/sound/pcm/ac97.h =================================================================== --- stable/4/sys/dev/sound/pcm/ac97.h (revision 71948) +++ stable/4/sys/dev/sound/pcm/ac97.h (revision 71949) @@ -1,78 +1,100 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #define AC97_MUTE 0x8000 #define AC97_REG_RESET 0x00 #define AC97_MIX_MASTER 0x02 #define AC97_MIX_PHONES 0x04 #define AC97_MIX_MONO 0x06 #define AC97_MIX_TONE 0x08 #define AC97_MIX_BEEP 0x0a #define AC97_MIX_PHONE 0x0c #define AC97_MIX_MIC 0x0e #define AC97_MIX_LINE 0x10 #define AC97_MIX_CD 0x12 #define AC97_MIX_VIDEO 0x14 #define AC97_MIX_AUX 0x16 #define AC97_MIX_PCM 0x18 #define AC97_REG_RECSEL 0x1a #define AC97_MIX_RGAIN 0x1c #define AC97_MIX_MGAIN 0x1e #define AC97_REG_GEN 0x20 #define AC97_REG_3D 0x22 #define AC97_REG_POWER 0x26 #define AC97_REGEXT_ID 0x28 #define AC97_EXTCAP_VRA (1 << 0) #define AC97_EXTCAP_DRA (1 << 1) #define AC97_EXTCAP_VRM (1 << 3) #define AC97_EXTCAPS (AC97_EXTCAP_VRA | AC97_EXTCAP_DRA | AC97_EXTCAP_VRM) #define AC97_REGEXT_STAT 0x2a #define AC97_REGEXT_FDACRATE 0x2c #define AC97_REGEXT_SDACRATE 0x2e #define AC97_REGEXT_LDACRATE 0x30 #define AC97_REGEXT_LADCRATE 0x32 #define AC97_REGEXT_MADCRATE 0x34 #define AC97_MIXEXT_CLFE 0x36 #define AC97_MIXEXT_SURROUND 0x38 #define AC97_REG_ID1 0x7c #define AC97_REG_ID2 0x7e -typedef u_int32_t (ac97_init)(void *devinfo); -typedef u_int32_t (ac97_read)(void *devinfo, int regno); -typedef void (ac97_write)(void *devinfo, int regno, u_int32_t data); +struct ac97mixtable_entry { + int reg:8; + unsigned bits:4; + unsigned ofs:4; + unsigned stereo:1; + unsigned mute:1; + unsigned recidx:4; + unsigned mask:1; + unsigned enable:1; +}; -extern snd_mixer ac97_mixer; -struct ac97_info; +#define AC97_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj)) +#define AC97_CREATE(dev, devinfo, cls) ac97_create(dev, devinfo, &cls ## _class) -struct ac97_info *ac97_create(device_t dev, void *devinfo, ac97_init *init, - ac97_read *rd, ac97_write *wr); +struct ac97_info { + kobj_t methods; + device_t dev; + void *devinfo; + char *name; + char rev; + unsigned count, caps, se, extcaps, extid, extstat, noext:1; + struct ac97mixtable_entry mix[32]; +}; + +#include "ac97_if.h" + +extern kobj_class_t ac97_getmixerclass(void); + +struct ac97_info *ac97_create(device_t dev, void *devinfo, kobj_class_t cls); void ac97_destroy(struct ac97_info *codec); int ac97_setrate(struct ac97_info *codec, int which, int rate); int ac97_setextmode(struct ac97_info *codec, u_int16_t mode); +u_int16_t ac97_getextmode(struct ac97_info *codec); +u_int16_t ac97_getextcaps(struct ac97_info *codec); Index: stable/4/sys/dev/sound/pcm/ac97_if.m =================================================================== --- stable/4/sys/dev/sound/pcm/ac97_if.m (revision 71948) +++ stable/4/sys/dev/sound/pcm/ac97_if.m (revision 71949) @@ -1,60 +1,61 @@ +# KOBJ # # Copyright (c) 2000 Cameron Grant # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # #include INTERFACE ac97; CODE { static u_int32_t ac97_noinit(kobj_t obj, void *devinfo) { return 1; } }; METHOD u_int32_t init { kobj_t obj; void *devinfo; } DEFAULT ac97_noinit; METHOD int read { kobj_t obj; void *devinfo; int regno; }; METHOD int write { kobj_t obj; void *devinfo; int regno; u_int32_t data; }; Index: stable/4/sys/dev/sound/pcm/channel.c =================================================================== --- stable/4/sys/dev/sound/pcm/channel.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/channel.c (revision 71949) @@ -1,1458 +1,1371 @@ /* * Copyright (c) 1999 Cameron Grant * Portions Copyright by Luigi Rizzo - 1997-99 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include +#include "feeder_if.h" + +MALLOC_DEFINE(M_CHANNEL, "channel", "pcm channel"); #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ #define DMA_ALIGN_THRESHOLD 4 #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) -#define ISA_DMA(b) (((b)->chan >= 0 && (b)->chan != 4 && (b)->chan < 8)) #define CANCHANGE(c) (!(c)->buffer.dl) #define ROUND(x) ((x) & DMA_ALIGN_MASK) + /* #define DEB(x) x */ -static void buf_clear(snd_dbuf *b, u_int32_t fmt, int length); + static void chn_dmaupdate(pcm_channel *c); static void chn_wrintr(pcm_channel *c); static void chn_rdintr(pcm_channel *c); -static u_int32_t chn_start(pcm_channel *c); static int chn_buildfeeder(pcm_channel *c); /* * SOUND OUTPUT We use a circular buffer to store samples directed to the DAC. The buffer is split into two variable-size regions, each identified by an offset in the buffer (rp,fp) and a length (rl,fl): 0 rp,rl fp,fl bufsize |__________>____________>________| FREE d READY w FREE READY: data written from the process and ready to be sent to the DAC; FREE: free part of the buffer. Both regions can wrap around the end of the buffer. At initialization, READY is empty, FREE takes all the available space, and dma is idle. dl contains the length of the current DMA transfer, dl=0 means that the dma is idle. The two boundaries (rp,fp) in the buffers are advanced by DMA [d] and write() [w] operations. The first portion of the READY region is used for DMA transfers. The transfer is started at rp and with chunks of length dl. During DMA operations, dsp_wr_dmaupdate() updates rp, rl and fl tracking the ISA DMA engine as the transfer makes progress. When a new block is written, fp advances and rl,fl are updated accordingly. The code works as follows: the user write routine dsp_write_body() fills up the READY region with new data (reclaiming space from the FREE region) and starts the write DMA engine if inactive. When a DMA transfer is complete, an interrupt causes dsp_wrintr() to be called which extends the FREE region and possibly starts the next transfer. In some cases, the code tries to track the current status of DMA operations by calling dsp_wr_dmaupdate() which changes rp, rl and fl. The system tries to make all DMA transfers use the same size, play_blocksize or rec_blocksize. The size is either selected by the user, or computed by the system to correspond to about .25s of audio. The blocksize must be within a range which is currently: min(5ms, 40 bytes) ... 1/2 buffer size. When there aren't enough data (write) or space (read), a transfer is started with a reduced size. To reduce problems in case of overruns, the routine which fills up the buffer should initialize (e.g. by repeating the last value) a reasonably long area after the last block so that no noise is produced on overruns. * */ /* XXX this is broken: in the event a bounce buffer is used, data never * gets copied in or out of the real buffer. fix requires mods to isa_dma.c * and possibly fixes to other autodma mode clients */ -static void -chn_isadmabounce(pcm_channel *c) -{ - if (ISA_DMA(&c->buffer)) { - /* tell isa_dma to bounce data in/out */ - } else KASSERT(1, ("chn_isadmabounce called on invalid channel")); -} - static int chn_polltrigger(pcm_channel *c) { snd_dbuf *bs = &c->buffer2nd; unsigned lim = (c->flags & CHN_F_HAS_SIZE)? bs->blksz : 0; int trig = 0; if (c->flags & CHN_F_MAPPED) trig = ((bs->int_count > bs->prev_int_count) || bs->prev_int_count == 0); else trig = (((c->direction == PCMDIR_PLAY)? bs->fl : bs->rl) > lim); return trig; } static int chn_pollreset(pcm_channel *c) { snd_dbuf *bs = &c->buffer2nd; if (c->flags & CHN_F_MAPPED) bs->prev_int_count = bs->int_count; return 1; } /* * chn_dmadone() updates pointers and wakes up any process waiting * on a select(). Must be called at spltty(). */ static void chn_dmadone(pcm_channel *c) { snd_dbuf *b = &c->buffer; if (c->direction == PCMDIR_PLAY) chn_checkunderflow(c); else chn_dmaupdate(c); if (ISA_DMA(b)) - chn_isadmabounce(c); /* sync bounce buffer */ + sndbuf_isadmabounce(b); /* sync bounce buffer */ b->int_count++; } /* * chn_dmawakeup() wakes up any process sleeping. Separated from * chn_dmadone() so that wakeup occurs only when feed from a * secondary buffer to a DMA buffer takes place. Must be called * at spltty(). */ static void chn_dmawakeup(pcm_channel *c) { snd_dbuf *b = &c->buffer; wakeup(b); } /* * chn_dmaupdate() tracks the status of a dma transfer, * updating pointers. It must be called at spltty(). * * NOTE: when we are using auto dma in the device, rl might become * negative. */ DEB (static int chn_updatecount=0); static void chn_dmaupdate(pcm_channel *c) { snd_dbuf *b = &c->buffer; int delta, hwptr; DEB (int b_rl=b->rl; int b_fl=b->fl; int b_rp=b->rp; int b_fp=b->fp); hwptr = chn_getptr(c); delta = (b->bufsize + hwptr - b->hp) % b->bufsize; if (delta >= ((b->bufsize * 15) / 16)) { - /* if (!(c->flags & (CHN_F_CLOSING | CHN_F_ABORTING))) device_printf(c->parent->dev, "hwptr went backwards %d -> %d\n", b->hp, hwptr); - */ } if (c->direction == PCMDIR_PLAY) { delta = (b->bufsize + hwptr - b->rp) % b->bufsize; b->rp = hwptr; b->rl -= delta; b->fl += delta; if (b->rl < 0) { DEB(printf("OUCH!(%d) rl %d(%d) delta %d bufsize %d hwptr %d rp %d(%d)\n", chn_updatecount++, b->rl, b_rl, delta, b->bufsize, hwptr, b->rp, b_rp)); } } else { delta = (b->bufsize + hwptr - b->fp) % b->bufsize; b->fp = hwptr; b->rl += delta; b->fl -= delta; if (b->fl < 0) { DEB(printf("OUCH!(%d) fl %d(%d) delta %d bufsize %d hwptr %d fp %d(%d)\n", chn_updatecount++, b->fl, b_fl, delta, b->bufsize, hwptr, b->fp, b_fp)); } } b->hp = hwptr; b->total += delta; } /* * Check channel for underflow occured. Reset DMA buffer in case of * underflow, so that new data can go into the buffer. It must be * called at spltty(). */ void chn_checkunderflow(pcm_channel *c) { snd_dbuf *b = &c->buffer; if (b->underflow) { DEB(printf("Clear underflow condition\n")); /* * The DMA keeps running even after underflow occurs. * Hence the value returned by chn_getptr() here soon * gets a lag when we get back to chn_write(). Although * there are no easy and precise methods to figure out * the lag, a quarter of b->bufsize would be a fair * choice, provided that a DMA interrupt generates upon * each transfer of a half b->bufsize. */ b->rp = chn_getptr(c); b->fp = (b->rp + b->bufsize / 4) % b->bufsize; b->rl = b->bufsize / 4; b->fl = b->bufsize - b->rl; b->underflow = 0; } else { chn_dmaupdate(c); } } /* * Feeds new data to the write dma buffer. Can be called in the bottom half. * Hence must be called at spltty. */ int chn_wrfeed(pcm_channel *c) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; int a, l, lacc; /* ensure we always have a whole number of samples */ a = (1 << c->align) - 1; lacc = 0; if (c->flags & CHN_F_MAPPED) { bs->rl = min(b->blksz, b->fl); bs->fl = 0; a = 0; } - /* - printf("b: [rl: %d, rp %d, fl %d, fp %d]; bs: [rl: %d, rp %d, fl %d, fp %d]\n", - b->rl, b->rp, b->fl, b->fp, bs->rl, bs->rp, bs->fl, bs->fp); - */ + DEB(if (c->flags & CHN_F_CLOSING) + printf("b: [rl: %d, rp %d, fl %d, fp %d]; bs: [rl: %d, rp %d, fl %d, fp %d]\n", + b->rl, b->rp, b->fl, b->fp, bs->rl, bs->rp, bs->fl, bs->fp)); /* Don't allow write unaligned data */ while (bs->rl > a && b->fl > a) { /* ensure we always have a whole number of samples */ l = min(min(bs->rl, bs->bufsize - bs->rp), min(b->fl, b->bufsize - b->fp)) & ~a; if (l == 0) return lacc; /* Move the samples, update the markers and pointers. */ bcopy(bs->buf + bs->rp, b->buf + b->fp, l); bs->fl += l; bs->rl -= l; bs->rp = (bs->rp + l) % bs->bufsize; b->rl += l; b->fl -= l; b->fp = (b->fp + l) % b->bufsize; /* Clear the new space in the secondary buffer. */ - buf_clear(bs, bs->fmt, l); + sndbuf_clear(bs, l); /* Accumulate the total bytes of the moved samples. */ lacc += l; /* A feed to the DMA buffer is equivalent to an interrupt. */ bs->total += l; if (c->flags & CHN_F_MAPPED) { if (bs->total - bs->prev_total >= bs->blksz) { bs->prev_total = bs->total; bs->int_count++; c->blocks++; } } else bs->int_count++; if (bs->sel.si_pid && chn_polltrigger(c)) selwakeup(&bs->sel); } return lacc; } /* Feeds new data to the secondary write buffer. */ static int chn_wrfeed2nd(pcm_channel *c, struct uio *buf) { snd_dbuf *bs = &c->buffer2nd; int l, w, wacc, hl; u_int8_t hackbuf[64]; /* The DMA buffer may have some space. */ while (chn_wrfeed(c) > 0); /* ensure we always have a whole number of samples */ wacc = 0; hl = 0; while (buf->uio_resid > 0 && bs->fl > 64) { /* * The size of the data to move here does not have to be * aligned. We take care of it upon moving the data to a * DMA buffer. */ l = min(bs->fl, bs->bufsize - bs->fp); /* Move the samples, update the markers and pointers. */ if (l < 64) { - w = c->feeder->feed(c->feeder, c, hackbuf, 64, buf); + w = FEEDER_FEED(c->feeder, c, hackbuf, 64, buf); l = min(w, bs->bufsize - bs->fp); bcopy(hackbuf, bs->buf + bs->fp, l); if (w > l) bcopy(hackbuf + l, bs->buf, w - l); } else - w = c->feeder->feed(c->feeder, c, bs->buf + bs->fp, l, buf); + w = FEEDER_FEED(c->feeder, c, bs->buf + bs->fp, l, buf); if (w == 0) panic("no feed"); bs->rl += w; bs->fl -= w; bs->fp = (bs->fp + w) % bs->bufsize; /* Accumulate the total bytes of the moved samples. */ wacc += w; /* If any pcm data gets moved, push it to the DMA buffer. */ if (w > 0) while (chn_wrfeed(c) > 0); } return wacc; } /* * Write interrupt routine. Can be called from other places (e.g. * to start a paused transfer), but with interrupts disabled. */ static void chn_wrintr(pcm_channel *c) { snd_dbuf *b = &c->buffer; if (b->underflow && !(c->flags & CHN_F_MAPPED)) { /* printf("underflow return\n"); */ return; /* nothing new happened */ } if (b->dl) chn_dmadone(c); /* * start another dma operation only if have ready data in the buffer, * there is no pending abort, have a full-duplex device, or have a * half duplex device and there is no pending op on the other side. * * Force transfers to be aligned to a boundary of 4, which is * needed when doing stereo and 16-bit. */ /* Check underflow and update the pointers. */ chn_checkunderflow(c); /* * Fill up the DMA buffer, followed by waking up the top half. * If some of the pcm data in uio are still left, the top half * goes to sleep by itself. */ if (c->flags & CHN_F_MAPPED) chn_wrfeed(c); else { while (chn_wrfeed(c) > 0); - buf_clear(b, b->fmt, b->fl); + sndbuf_clear(b, b->fl); } chn_dmawakeup(c); if (c->flags & CHN_F_TRIGGERED) { chn_dmaupdate(c); /* * check if we need to reprogram the DMA on the sound card. * This happens if the size has changed from zero */ if (b->dl == 0) { /* Start DMA operation */ b->dl = b->blksz; /* record new transfer size */ chn_trigger(c, PCMTRIG_START); } /* * Emulate writing by DMA, i.e. transfer the pcm data from * the emulated-DMA buffer to the device itself. */ chn_trigger(c, PCMTRIG_EMLDMAWR); if (b->rl < b->dl) { DEB(printf("near underflow (%d < %d), %d\n", b->rl, b->dl, b->fl)); /* * we are near to underflow condition, so to prevent * audio 'clicks' clear next b->fl bytes */ - buf_clear(b, b->fmt, b->fl); + sndbuf_clear(b, b->fl); if (b->rl < DMA_ALIGN_THRESHOLD) b->underflow = 1; } } else { /* cannot start a new dma transfer */ DEB(printf("underflow, flags 0x%08x rp %d rl %d\n", c->flags, b->rp, b->rl)); if (b->dl) { /* DMA was active */ b->underflow = 1; /* set underflow flag */ - buf_clear(b, b->fmt, b->bufsize); + sndbuf_clear(b, b->bufsize); } } } /* * user write routine * * advance the boundary between READY and FREE, fill the space with * uiomove(), and possibly start DMA. Do the above until the transfer * is complete. * * To minimize latency in case a pending DMA transfer is about to end, * we do the transfer in pieces of increasing sizes, extending the * READY area at every checkpoint. In the (necessary) assumption that * memory bandwidth is larger than the rate at which the dma consumes * data, we reduce the latency to something proportional to the length * of the first piece, while keeping the overhead low and being able * to feed the DMA with large blocks. */ int chn_write(pcm_channel *c, struct uio *buf) { int ret = 0, timeout, res, newsize, count; long s; snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; if (c->flags & CHN_F_WRITING) { /* This shouldn't happen and is actually silly * - will never wake up, just timeout; why not sleep on b? */ tsleep(&s, PZERO, "pcmwrW", hz); return EBUSY; } c->flags |= CHN_F_WRITING; c->flags &= ~CHN_F_ABORTING; s = spltty(); /* * XXX Certain applications attempt to write larger size * of pcm data than c->blocksize2nd without blocking, * resulting partial write. Expand the block size so that * the write operation avoids blocking. */ if ((c->flags & CHN_F_NBIO) && buf->uio_resid > bs->blksz) { DEB(printf("pcm warning: broken app, nbio and tried to write %d bytes with fragsz %d\n", buf->uio_resid, bs->blksz)); newsize = 16; while (newsize < min(buf->uio_resid, CHN_2NDBUFMAXSIZE / 2)) newsize <<= 1; chn_setblocksize(c, bs->blkcnt, newsize); DEB(printf("pcm warning: frags reset to %d x %d\n", bs->blkcnt, bs->blksz)); } /* * Fill up the secondary and DMA buffer. * chn_wrfeed*() takes care of the alignment. */ /* Check for underflow before writing into the buffers. */ chn_checkunderflow(c); while (chn_wrfeed2nd(c, buf) > 0); if ((c->flags & CHN_F_NBIO) && (buf->uio_resid > 0)) ret = EAGAIN; /* Start playing if not yet. */ if (!b->dl) - chn_start(c); + chn_start(c, 0); if (ret == 0) { count = hz; /* Wait until all samples are played in blocking mode. */ while ((buf->uio_resid > 0) && (count > 0)) { /* Check for underflow before writing into the buffers. */ chn_checkunderflow(c); /* Fill up the buffers with new pcm data. */ res = buf->uio_resid; while (chn_wrfeed2nd(c, buf) > 0); if (buf->uio_resid < res) count = hz; else count--; /* Have we finished to feed the secondary buffer? */ if (buf->uio_resid == 0) break; /* Wait for new free space to write new pcm samples. */ /* splx(s); */ timeout = 1; /*(buf->uio_resid >= b->dl)? hz / 20 : 1; */ ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout); /* s = spltty(); */ /* if (ret == EINTR) chn_abort(c); */ if (ret == EINTR || ret == ERESTART) break; } if (count == 0) { c->flags |= CHN_F_DEAD; device_printf(c->parent->dev, "play interrupt timeout, channel dead\n"); } } else ret = 0; c->flags &= ~CHN_F_WRITING; splx(s); return ret; } /* * SOUND INPUT * The input part is similar to the output one, with a circular buffer split in two regions, and boundaries advancing because of read() calls [r] or dma operation [d]. At initialization, as for the write routine, READY is empty, and FREE takes all the space. 0 rp,rl fp,fl bufsize |__________>____________>________| FREE r READY d FREE Operation is as follows: upon user read (dsp_read_body()) a DMA read is started if not already active (marked by b->dl > 0), then as soon as data are available in the READY region they are transferred to the user buffer, thus advancing the boundary between FREE and READY. Upon interrupts, caused by a completion of a DMA transfer, the READY region is extended and possibly a new transfer is started. When necessary, dsp_rd_dmaupdate() is called to advance fp (and update rl,fl accordingly). Upon user reads, rp is advanced and rl,fl are updated accordingly. The rules to choose the size of the new DMA area are similar to the other case, with a preferred constant transfer size equal to rec_blocksize, and fallback to smaller sizes if no space is available. */ static int chn_rddump(pcm_channel *c, int cnt) { snd_dbuf *b = &c->buffer; int maxover, ss; ss = 1; ss <<= (b->fmt & AFMT_STEREO)? 1 : 0; ss <<= (b->fmt & AFMT_16BIT)? 1 : 0; maxover = c->speed * ss; b->overrun += cnt; if (b->overrun > maxover) { device_printf(c->parent->dev, "record overrun, dumping %d bytes\n", b->overrun); b->overrun = 0; } b->rl -= cnt; b->fl += cnt; b->rp = (b->rp + cnt) % b->bufsize; return cnt; } /* * Feed new data from the read buffer. Can be called in the bottom half. * Hence must be called at spltty. */ int chn_rdfeed(pcm_channel *c) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; int l, lacc; /* printf("b: [rl: %d, rp %d, fl %d, fp %d]; bs: [rl: %d, rp %d, fl %d, fp %d]\n", b->rl, b->rp, b->fl, b->fp, bs->rl, bs->rp, bs->fl, bs->fp); */ /* ensure we always have a whole number of samples */ lacc = 0; while (bs->fl >= DMA_ALIGN_THRESHOLD && b->rl >= DMA_ALIGN_THRESHOLD) { l = min(min(bs->fl, bs->bufsize - bs->fp), min(b->rl, b->bufsize - b->rp)) & DMA_ALIGN_MASK; /* Move the samples, update the markers and pointers. */ bcopy(b->buf + b->rp, bs->buf + bs->fp, l); bs->fl -= l; bs->rl += l; bs->fp = (bs->fp + l) % bs->bufsize; b->rl -= l; b->fl += l; b->rp = (b->rp + l) % b->bufsize; /* Accumulate the total bytes of the moved samples. */ lacc += l; /* A feed from the DMA buffer is equivalent to an interrupt. */ bs->int_count++; if (bs->sel.si_pid && chn_polltrigger(c)) selwakeup(&bs->sel); } return lacc; } /* Feeds new data from the secondary read buffer. */ static int chn_rdfeed2nd(pcm_channel *c, struct uio *buf) { snd_dbuf *bs = &c->buffer2nd; int l, w, wacc; /* ensure we always have a whole number of samples */ wacc = 0; while ((buf->uio_resid > 0) && (bs->rl > 0)) { /* The DMA buffer may have pcm data. */ /* while (chn_rdfeed(c) > 0); */ /* * The size of the data to move here does not have to be * aligned. We take care of it upon moving the data to a * DMA buffer. */ l = min(bs->rl, bs->bufsize - bs->rp); /* Move the samples, update the markers and pointers. */ - w = c->feeder->feed(c->feeder, c, bs->buf + bs->rp, l, buf); + w = FEEDER_FEED(c->feeder, c, bs->buf + bs->rp, l, buf); if (w == 0) panic("no feed"); bs->fl += w; bs->rl -= w; bs->rp = (bs->rp + w) % bs->bufsize; /* Clear the new space in the secondary buffer. */ - buf_clear(bs, bs->fmt, l); + sndbuf_clear(bs, l); /* Accumulate the total bytes of the moved samples. */ bs->total += w; wacc += w; } return wacc; } /* read interrupt routine. Must be called with interrupts blocked. */ static void chn_rdintr(pcm_channel *c) { snd_dbuf *b = &c->buffer; if (b->dl) chn_dmadone(c); DEB(printf("rdintr: start dl %d, rp:rl %d:%d, fp:fl %d:%d\n", b->dl, b->rp, b->rl, b->fp, b->fl)); /* Update the pointers. */ chn_dmaupdate(c); /* * Suck up the DMA buffer, followed by waking up the top half. * If some of the pcm data in the secondary buffer are still left, * the top half goes to sleep by itself. */ while(chn_rdfeed(c) > 0); chn_dmawakeup(c); if (b->fl < b->dl) { DEB(printf("near overflow (%d < %d), %d\n", b->fl, b->dl, b->rl)); chn_rddump(c, b->blksz - b->fl); } if (c->flags & CHN_F_TRIGGERED) { /* * check if we need to reprogram the DMA on the sound card. * This happens if the size has changed from zero */ if (b->dl == 0) { /* Start DMA operation */ b->dl = b->blksz; /* record new transfer size */ chn_trigger(c, PCMTRIG_START); } /* * Emulate writing by DMA, i.e. transfer the pcm data from * the emulated-DMA buffer to the device itself. */ chn_trigger(c, PCMTRIG_EMLDMARD); } else { if (b->dl) { /* was active */ b->dl = 0; chn_trigger(c, PCMTRIG_STOP); chn_dmaupdate(c); } } } /* * body of user-read routine * * Start DMA if not active; wait for READY not empty. * Transfer data from READY region using uiomove(), advance boundary * between FREE and READY. Repeat until transfer is complete. * * To avoid excessive latency in freeing up space for the DMA * engine, transfers are done in blocks of increasing size, so that * the latency is proportional to the size of the smallest block, but * we have a low overhead and are able to feed the dma engine with * large blocks. * * NOTE: in the current version, read will not return more than * blocksize bytes at once (unless more are already available), to * avoid that requests using very large buffers block for too long. */ int chn_read(pcm_channel *c, struct uio *buf) { int ret = 0, timeout, limit, res, count; long s; snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; if (c->flags & CHN_F_READING) { /* This shouldn't happen and is actually silly */ tsleep(&s, PZERO, "pcmrdR", hz); return (EBUSY); } s = spltty(); /* Store the initial size in the uio. */ res = buf->uio_resid; c->flags |= CHN_F_READING; c->flags &= ~CHN_F_ABORTING; /* suck up the DMA and secondary buffers. */ while (chn_rdfeed2nd(c, buf) > 0); if (buf->uio_resid == 0) goto skip; limit = res - b->blksz; if (limit < 0) limit = 0; /* Start capturing if not yet. */ if ((!bs->rl || !b->rl) && !b->dl) - chn_start(c); + chn_start(c, 0); if (!(c->flags & CHN_F_NBIO)) { count = hz; /* Wait until all samples are captured. */ while ((buf->uio_resid > 0) && (count > 0)) { /* Suck up the DMA and secondary buffers. */ chn_dmaupdate(c); res = buf->uio_resid; while (chn_rdfeed(c) > 0); while (chn_rdfeed2nd(c, buf) > 0); if (buf->uio_resid < res) count = hz; else count--; /* Have we finished to feed the uio? */ if (buf->uio_resid == 0) break; /* Wait for new pcm samples. */ /* splx(s); */ timeout = (buf->uio_resid - limit >= b->dl)? hz / 20 : 1; ret = tsleep(b, PRIBIO | PCATCH, "pcmrd", 1); /* s = spltty(); */ /* if (ret == EINTR) chn_abort(c); */ if (ret == EINTR || ret == ERESTART) break; } if (count == 0) { c->flags |= CHN_F_DEAD; device_printf(c->parent->dev, "record interrupt timeout, channel dead\n"); } } else { /* If no pcm data was read on nonblocking, return EAGAIN. */ if (buf->uio_resid == res) ret = EAGAIN; } skip: c->flags &= ~CHN_F_READING; splx(s); return ret; } void chn_intr(pcm_channel *c) { if (c->flags & CHN_F_INIT) chn_reinit(c); if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c); } u_int32_t -chn_start(pcm_channel *c) +chn_start(pcm_channel *c, int force) { u_int32_t r, s; snd_dbuf *b = &c->buffer; r = 0; s = spltty(); - if (b->dl == 0 && !(c->flags & (CHN_F_MAPPED | CHN_F_NOTRIGGER))) { + if (b->dl == 0 && !(c->flags & CHN_F_NOTRIGGER)) { if (c->direction == PCMDIR_PLAY) { - /* Fill up the DMA buffer. */ - while (chn_wrfeed(c) > 0); - if (b->rl >= b->blksz) + if (!(c->flags & CHN_F_MAPPED)) + while (chn_wrfeed(c) > 0); /* Fill up the DMA buffer. */ + if (force || (b->rl >= b->blksz)) r = CHN_F_TRIGGERED; } else { - /* Suck up the DMA buffer. */ - while (chn_rdfeed(c) > 0); - if (b->fl >= b->blksz) + if (!(c->flags & CHN_F_MAPPED)) + while (chn_rdfeed(c) > 0); /* Suck up the DMA buffer. */ + if (force || (b->fl >= b->blksz)) r = CHN_F_TRIGGERED; } c->flags |= r; chn_intr(c); } splx(s); return r; } -static void -chn_dma_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) -{ - snd_dbuf *b = (snd_dbuf *)arg; - - if (bootverbose) { - printf("pcm: setmap %lx, %lx; ", (unsigned long)segs->ds_addr, - (unsigned long)segs->ds_len); - printf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf)); - } -} - -/* - * Allocate memory for DMA buffer. If the device do not perform DMA transfer, - * the drvier can call malloc(9) by its own. - */ -int -chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat) -{ - b->parent_dmat = parent_dmat; - if (bus_dmamem_alloc(b->parent_dmat, (void **)&b->buf, - BUS_DMA_NOWAIT, &b->dmamap)) return -1; - if (bus_dmamap_load(b->parent_dmat, b->dmamap, b->buf, - b->bufsize, chn_dma_setmap, b, 0)) return -1; - return 0; -} - void -chn_freebuf(snd_dbuf *b) -{ - bus_dmamem_free(b->parent_dmat, b->buf, b->dmamap); -} - -static void -buf_clear(snd_dbuf *b, u_int32_t fmt, int length) -{ - int i; - u_int16_t data, *p; - - if (length == 0) - return; - - if (fmt & AFMT_SIGNED) - data = 0x00; - else - data = 0x80; - - if (fmt & AFMT_16BIT) - data <<= 8; - else - data |= data << 8; - - if (fmt & AFMT_BIGENDIAN) - data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00); - - i = b->fp; - p = (u_int16_t *)(b->buf + b->fp); - while (length > 1) { - *p++ = data; - length -= 2; - i += 2; - if (i >= b->bufsize) { - p = (u_int16_t *)b->buf; - i = 0; - } - } - if (length == 1) - *(b->buf + i) = data & 0xff; -} - -void chn_resetbuf(pcm_channel *c) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; c->blocks = 0; - b->rp = b->fp = 0; - b->dl = b->rl = 0; - b->fl = b->bufsize; - b->prev_total = b->total = 0; - b->prev_int_count = b->int_count = 0; - b->underflow = 0; - if (b->buf && b->bufsize > 0) - buf_clear(b, b->fmt, b->bufsize); - - bs->rp = bs->fp = 0; - bs->dl = bs->rl = 0; - bs->fl = bs->bufsize; - bs->prev_total = bs->total = 0; - bs->prev_int_count = bs->int_count = 0; - bs->underflow = 0; - if (bs->buf && bs->bufsize > 0) - buf_clear(bs, bs->fmt, bs->bufsize); + sndbuf_reset(b); + sndbuf_reset(bs); } -void -buf_isadma(snd_dbuf *b, int go) -{ - if (ISA_DMA(b)) { - switch (go) { - case PCMTRIG_START: - DEB(printf("buf 0x%p ISA DMA started\n", b)); - isa_dmastart(b->dir | ISADMA_RAW, b->buf, - b->bufsize, b->chan); - break; - case PCMTRIG_STOP: - case PCMTRIG_ABORT: - DEB(printf("buf 0x%p ISA DMA stopped\n", b)); - isa_dmastop(b->chan); - isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, - b->chan); - break; - } - } else KASSERT(1, ("buf_isadma called on invalid channel")); -} - -int -buf_isadmaptr(snd_dbuf *b) -{ - if (ISA_DMA(b)) { - int i = b->dl? isa_dmastatus(b->chan) : b->bufsize; - if (i < 0) - i = 0; - return b->bufsize - i; - } else KASSERT(1, ("buf_isadmaptr called on invalid channel")); - return -1; -} - /* * chn_sync waits until the space in the given channel goes above * a threshold. The threshold is checked against fl or rl respectively. * Assume that the condition can become true, do not check here... */ int chn_sync(pcm_channel *c, int threshold) { u_long s, rdy; int ret; snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; for (;;) { s = spltty(); chn_checkunderflow(c); while (chn_wrfeed(c) > 0); rdy = (c->direction == PCMDIR_PLAY)? bs->fl : bs->rl; if (rdy <= threshold) { ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1); splx(s); if (ret == ERESTART || ret == EINTR) { DEB(printf("chn_sync: tsleep returns %d\n", ret)); return -1; } } else break; } splx(s); return 0; } int chn_poll(pcm_channel *c, int ev, struct proc *p) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; u_long s; int ret; s = spltty(); if (!(c->flags & CHN_F_MAPPED)) { if (c->direction == PCMDIR_PLAY) { /* Fill up the DMA buffer. */ chn_checkunderflow(c); while (chn_wrfeed(c) > 0); } else { /* Suck up the DMA buffer. */ chn_dmaupdate(c); while (chn_rdfeed(c) > 0); } if (!b->dl) - chn_start(c); + chn_start(c, 1); } ret = 0; if (chn_polltrigger(c) && chn_pollreset(c)) ret = ev; else selrecord(p, &bs->sel); splx(s); return ret; } /* * chn_abort is a non-blocking function which aborts a pending * DMA transfer and flushes the buffers. * It returns the number of bytes that have not been transferred. */ int chn_abort(pcm_channel *c) { int missing = 0, cnt = 0; snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; if (!b->dl) return 0; c->flags |= CHN_F_ABORTING; c->flags &= ~CHN_F_TRIGGERED; cnt = 10; while (!b->underflow && (cnt-- > 0)) tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmabr", hz / 50); chn_trigger(c, PCMTRIG_ABORT); b->dl = 0; chn_dmaupdate(c); missing = bs->rl + b->rl; return missing; } /* * this routine tries to flush the dma transfer. It is called * on a close. We immediately abort any read DMA * operation, and then wait for the play buffer to drain. */ int chn_flush(pcm_channel *c) { int ret, count, s, resid, resid_p; snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; DEB(printf("chn_flush c->flags 0x%08x\n", c->flags)); + if (!b->dl) + return 0; + c->flags |= CHN_F_CLOSING; if (c->direction == PCMDIR_REC) chn_abort(c); - else if (b->dl) { - resid_p = resid = b->rl + bs->rl; + else { + resid = b->rl + bs->rl; + resid_p = resid; count = 10; while ((count > 0) && (resid > 0) && !b->underflow) { /* still pending output data. */ ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz / 10); if (ret == EINTR || ret == ERESTART) { DEB(printf("chn_flush: tsleep returns %d\n", ret)); return ret; } s = spltty(); chn_dmaupdate(c); splx(s); - DEB(printf("chn_flush: now rl = %d, fl = %d\n", b->rl, b->fl)); + DEB(printf("chn_flush: now rl = %d, fl = %d, resid = %d\n", b->rl, b->fl, resid)); resid = b->rl + bs->rl; if (resid >= resid_p) count--; resid_p = resid; } if (count == 0) DEB(printf("chn_flush: timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n", b->rl, c->flags)); - if (c->direction == PCMDIR_PLAY && b->dl) + if (b->dl) chn_abort(c); } c->flags &= ~CHN_F_CLOSING; return 0; } int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) { int i; for (i = 0; fmtlist[i]; i++) if (fmt == fmtlist[i]) return 1; - return 0; } int chn_reset(pcm_channel *c, u_int32_t fmt) { - int hwspd, r; + int hwspd, r = 0; chn_abort(c); c->flags &= CHN_F_RESET; - r = chn_setblocksize(c, CHN_2NDBUFBLKNUM, CHN_2NDBUFBLKSIZE); - if (r) - return r; + CHANNEL_RESET(c->methods, c->devinfo); if (fmt) { hwspd = DSP_DEFAULT_SPEED; RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); c->speed = hwspd; r = chn_setformat(c, fmt); if (r == 0) r = chn_setspeed(c, hwspd); if (r == 0) r = chn_setvolume(c, 100, 100); } + r = chn_setblocksize(c, 0, 0); + if (r) + return r; chn_resetbuf(c); + CHANNEL_RESETDONE(c->methods, c->devinfo); /* c->flags |= CHN_F_INIT; */ return 0; } int chn_reinit(pcm_channel *c) { if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) { chn_setformat(c, c->format); chn_setspeed(c, c->speed); chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff); c->flags &= ~CHN_F_INIT; return 1; } return 0; } int chn_init(pcm_channel *c, void *devinfo, int dir) { - snd_dbuf *bs = &c->buffer2nd; + struct feeder_class *fc; + snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; /* Initialize the hardware and DMA buffer first. */ - c->feeder = malloc(sizeof(*(c->feeder)), M_DEVBUF, M_NOWAIT); - *(c->feeder) = *feeder_getroot(); - c->feederdesc = malloc(sizeof(*(c->feeder)), M_DEVBUF, M_NOWAIT); - c->feederdesc->type = FEEDER_ROOT; - c->feederdesc->in = 0; - c->feederdesc->out = 0; - c->feederdesc->flags = 0; - c->feederdesc->idx = 0; - c->feeder->desc = c->feederdesc; - c->feeder->source = NULL; + c->feeder = NULL; + fc = feeder_getclass(NULL); + if (fc == NULL) + return EINVAL; + if (chn_addfeeder(c, fc, NULL)) + return EINVAL; c->flags = 0; c->feederflags = 0; c->buffer.chan = -1; - c->devinfo = c->init(devinfo, &c->buffer, c, dir); - if (c->devinfo == NULL || c->buffer.bufsize == 0) - return 1; + c->devinfo = CHANNEL_INIT(c->methods, devinfo, &c->buffer, c, dir); + if (c->devinfo == NULL) + return ENODEV; + if (c->buffer.bufsize == 0) + return ENOMEM; chn_setdir(c, dir); /* And the secondary buffer. */ bs->buf = NULL; + sndbuf_setfmt(b, AFMT_U8); + sndbuf_setfmt(bs, AFMT_U8); bs->bufsize = 0; return 0; } int chn_kill(pcm_channel *c) { if (c->flags & CHN_F_TRIGGERED) chn_trigger(c, PCMTRIG_ABORT); while (chn_removefeeder(c) == 0); - free(c->feeder->desc, M_DEVBUF); - free(c->feeder, M_DEVBUF); - if (c->free) - c->free(c->devinfo); - else - chn_freebuf(&c->buffer); + if (CHANNEL_FREE(c->methods, c->devinfo)) + sndbuf_free(&c->buffer); c->flags |= CHN_F_DEAD; return 0; } int chn_setdir(pcm_channel *c, int dir) { int r; c->direction = dir; - r = c->setdir? c->setdir(c->devinfo, c->direction) : 0; + r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); if (!r && ISA_DMA(&c->buffer)) c->buffer.dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ; return r; } int chn_setvolume(pcm_channel *c, int left, int right) { /* could add a feeder for volume changing if channel returns -1 */ if (CANCHANGE(c)) { c->volume = (left << 8) | right; return 0; } c->volume = (left << 8) | right; c->flags |= CHN_F_INIT; return 0; } int chn_setspeed(pcm_channel *c, int speed) { pcm_feeder *f; - int r, hwspd, delta; + snd_dbuf *b = &c->buffer; + snd_dbuf *bs = &c->buffer2nd; + int r, delta; DEB(printf("want speed %d, ", speed)); if (speed <= 0) return EINVAL; if (CANCHANGE(c)) { c->speed = speed; - hwspd = speed; - RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - DEB(printf("try speed %d, ", hwspd)); - hwspd = c->setspeed(c->devinfo, hwspd); - DEB(printf("got speed %d, ", hwspd)); - delta = hwspd - speed; + b->spd = speed; + bs->spd = speed; + RANGE(b->spd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); + DEB(printf("try speed %d, ", b->spd)); + b->spd = CHANNEL_SETSPEED(c->methods, c->devinfo, b->spd); + DEB(printf("got speed %d, ", b->spd)); + + delta = b->spd - bs->spd; if (delta < 0) delta = -delta; + c->feederflags &= ~(1 << FEEDER_RATE); if (delta > 500) c->feederflags |= 1 << FEEDER_RATE; else - speed = hwspd; + bs->spd = b->spd; + r = chn_buildfeeder(c); DEB(printf("r = %d\n", r)); if (r) return r; + + r = chn_setblocksize(c, 0, 0); + if (r) + return r; + if (!(c->feederflags & (1 << FEEDER_RATE))) return 0; + f = chn_findfeeder(c, FEEDER_RATE); DEB(printf("feedrate = %p\n", f)); if (f == NULL) return EINVAL; - r = feeder_set(f, FEEDRATE_SRC, speed); - DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", speed, r)); + + r = FEEDER_SET(f, FEEDRATE_SRC, bs->spd); + DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", bs->spd, r)); if (r) return r; - r = feeder_set(f, FEEDRATE_DST, hwspd); - DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", hwspd, r)); + + r = FEEDER_SET(f, FEEDRATE_DST, b->spd); + DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", b->spd, r)); if (r) return r; + return 0; } c->speed = speed; c->flags |= CHN_F_INIT; return 0; } int chn_setformat(pcm_channel *c, u_int32_t fmt) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; int r; - u_int32_t hwfmt; + if (CANCHANGE(c)) { DEB(printf("want format %d\n", fmt)); c->format = fmt; - c->feederdesc->out = c->format; hwfmt = c->format; c->feederflags &= ~(1 << FEEDER_FMT); if (!fmtvalid(hwfmt, chn_getcaps(c)->fmtlist)) c->feederflags |= 1 << FEEDER_FMT; r = chn_buildfeeder(c); if (r) return r; hwfmt = c->feeder->desc->out; - b->fmt = hwfmt; - bs->fmt = hwfmt; + sndbuf_setfmt(b, hwfmt); + sndbuf_setfmt(bs, hwfmt); chn_resetbuf(c); - c->setformat(c->devinfo, hwfmt); + CHANNEL_SETFORMAT(c->methods, c->devinfo, hwfmt); return chn_setspeed(c, c->speed); } c->format = fmt; c->flags |= CHN_F_INIT; return 0; } int chn_setblocksize(pcm_channel *c, int blkcnt, int blksz) { snd_dbuf *b = &c->buffer; snd_dbuf *bs = &c->buffer2nd; - int s, ss, bufsz; + int s, bufsz, irqhz, tmp; - if (bs->blkcnt == blkcnt && bs->blksz == blksz) - return 0; - if (c->flags & CHN_F_MAPPED) { - DEB(printf("chn_setblocksize: can't work on mapped channel")); + if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) return EINVAL; - } - c->flags &= ~CHN_F_HAS_SIZE; - ss = 1; - ss <<= (bs->fmt & AFMT_STEREO)? 1 : 0; - ss <<= (bs->fmt & AFMT_16BIT)? 1 : 0; + if (blksz == 0 || blksz == -1) { + if (blksz == -1) + c->flags &= ~CHN_F_HAS_SIZE; + if (!(c->flags & CHN_F_HAS_SIZE)) { + blksz = (bs->bps * bs->spd) / CHN_DEFAULT_HZ; + tmp = 32; + while (tmp <= blksz) + tmp <<= 1; + tmp >>= 1; + blksz = tmp; - if (blksz >= 2) + RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); + RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); + } else { + blksz = bs->blksz; + blkcnt = bs->blkcnt; + } + } else { + if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) + return EINVAL; c->flags |= CHN_F_HAS_SIZE; - /* let us specify blksz without setting CHN_F_HAS_SIZE */ - if (blksz < 0) - blksz = -blksz; - /* default to blksz = ~0.25s */ - if (blksz < 16) - blksz = (ss * c->speed) >> 2; - if (blksz > CHN_2NDBUFMAXSIZE / 2) - blksz = CHN_2NDBUFMAXSIZE / 2; - if (blkcnt < 2) - blkcnt = 2; + } - if (blkcnt * blksz > CHN_2NDBUFMAXSIZE) - blkcnt = CHN_2NDBUFMAXSIZE / blksz; bufsz = blkcnt * blksz; s = spltty(); + if (bs->buf != NULL) free(bs->buf, M_DEVBUF); bs->buf = malloc(bufsz, M_DEVBUF, M_WAITOK); if (bs->buf == NULL) { splx(s); - DEB(printf("chn_setblocksize: out of memory.")); + DEB(printf("chn_setblocksize: out of memory\n")); return ENOSPC; } + bs->bufsize = bufsz; - bs->rl = bs->rp = bs->fp = 0; - bs->fl = bs->bufsize; - buf_clear(bs, bs->fmt, bs->bufsize); bs->blkcnt = blkcnt; bs->blksz = blksz; - RANGE(blksz, 16, b->bufsize / 2); - b->blksz = c->setblocksize(c->devinfo, blksz); + + /* adjust for different hw format/speed */ + irqhz = (bs->bps * bs->spd) / bs->blksz; + RANGE(irqhz, 16, 512); + + b->blksz = (b->bps * b->spd) / irqhz; + + /* round down to 2^x */ + blksz = 32; + while (blksz <= b->blksz) + blksz <<= 1; + blksz >>= 1; + + /* round down to fit hw buffer size */ + RANGE(blksz, 16, b->maxsize / 2); + + b->blksz = CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz); + + chn_resetbuf(c); splx(s); return 0; } int chn_trigger(pcm_channel *c, int go) { - return c->trigger(c->devinfo, go); + return CHANNEL_TRIGGER(c->methods, c->devinfo, go); } int chn_getptr(pcm_channel *c) { int hwptr; int a = (1 << c->align) - 1; snd_dbuf *b = &c->buffer; - hwptr = b->dl? c->getptr(c->devinfo) : 0; + hwptr = b->dl? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; /* don't allow unaligned values in the hwa ptr */ hwptr &= ~a ; /* Apply channel align mask */ hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ return hwptr; } pcmchan_caps * chn_getcaps(pcm_channel *c) { - return c->getcaps(c->devinfo); + return CHANNEL_GETCAPS(c->methods, c->devinfo); } u_int32_t chn_getformats(pcm_channel *c) { u_int32_t *fmtlist, fmts; int i; fmtlist = chn_getcaps(c)->fmtlist; fmts = 0; for (i = 0; fmtlist[i]; i++) fmts |= fmtlist[i]; return fmts; } static int chn_buildfeeder(pcm_channel *c) { - pcm_feeder *f; + struct feeder_class *fc; struct pcm_feederdesc desc; u_int32_t tmp[2], src, dst, type, flags; while (chn_removefeeder(c) == 0); + KASSERT((c->feeder == NULL), ("feeder chain not empty")); c->align = 0; + fc = feeder_getclass(NULL); + if (fc == NULL) + return EINVAL; + if (chn_addfeeder(c, fc, NULL)) + return EINVAL; + c->feeder->desc->out = c->format; + flags = c->feederflags; src = c->feeder->desc->out; if ((c->flags & CHN_F_MAPPED) && (flags != 0)) return EINVAL; DEB(printf("not mapped, flags %x, ", flags)); for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { if (flags & (1 << type)) { desc.type = type; desc.in = 0; desc.out = 0; desc.flags = 0; DEB(printf("find feeder type %d, ", type)); - f = feeder_get(&desc); - DEB(printf("got %p\n", f)); - if (f == NULL) + fc = feeder_getclass(&desc); + DEB(printf("got %p\n", fc)); + if (fc == NULL) return EINVAL; - dst = f->desc->in; + dst = fc->desc->in; if (src != dst) { DEB(printf("build fmtchain from %x to %x: ", src, dst)); tmp[0] = dst; tmp[1] = 0; if (chn_fmtchain(c, tmp) == 0) return EINVAL; DEB(printf("ok\n")); } - if (chn_addfeeder(c, f)) + if (chn_addfeeder(c, fc, fc->desc)) return EINVAL; - src = f->desc->out; - DEB(printf("added feeder %p, output %x\n", f, src)); + src = fc->desc->out; + DEB(printf("added feeder %p, output %x\n", fc, src)); dst = 0; flags &= ~(1 << type); } } if (!fmtvalid(src, chn_getcaps(c)->fmtlist)) { if (chn_fmtchain(c, chn_getcaps(c)->fmtlist) == 0) return EINVAL; DEB(printf("built fmtchain from %x to %x\n", src, c->feeder->desc->out)); flags &= ~(1 << FEEDER_FMT); } return 0; } Index: stable/4/sys/dev/sound/pcm/channel.h =================================================================== --- stable/4/sys/dev/sound/pcm/channel.h (revision 71948) +++ stable/4/sys/dev/sound/pcm/channel.h (revision 71949) @@ -1,100 +1,102 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ +#include "channel_if.h" + int chn_reinit(pcm_channel *c); int chn_write(pcm_channel *c, struct uio *buf); int chn_read(pcm_channel *c, struct uio *buf); +u_int32_t chn_start(pcm_channel *c, int force); int chn_sync(pcm_channel *c, int threshold); int chn_flush(pcm_channel *c); int chn_poll(pcm_channel *c, int ev, struct proc *p); int chn_init(pcm_channel *c, void *devinfo, int dir); int chn_kill(pcm_channel *c); int chn_setdir(pcm_channel *c, int dir); int chn_reset(pcm_channel *c, u_int32_t fmt); int chn_setvolume(pcm_channel *c, int left, int right); int chn_setspeed(pcm_channel *c, int speed); int chn_setformat(pcm_channel *c, u_int32_t fmt); int chn_setblocksize(pcm_channel *c, int blkcnt, int blksz); int chn_trigger(pcm_channel *c, int go); int chn_getptr(pcm_channel *c); pcmchan_caps *chn_getcaps(pcm_channel *c); u_int32_t chn_getformats(pcm_channel *c); -int chn_allocbuf(snd_dbuf *b, bus_dma_tag_t parent_dmat); -void chn_freebuf(snd_dbuf *b); void chn_resetbuf(pcm_channel *c); void chn_intr(pcm_channel *c); void chn_checkunderflow(pcm_channel *c); int chn_wrfeed(pcm_channel *c); int chn_rdfeed(pcm_channel *c); int chn_abort(pcm_channel *c); int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); -void buf_isadma(snd_dbuf *b, int go); -int buf_isadmaptr(snd_dbuf *b); - #define PCMDIR_PLAY 1 #define PCMDIR_REC -1 #define PCMTRIG_START 1 #define PCMTRIG_EMLDMAWR 2 #define PCMTRIG_EMLDMARD 3 #define PCMTRIG_STOP 0 #define PCMTRIG_ABORT -1 #define CHN_F_READING 0x00000001 /* have a pending read */ #define CHN_F_WRITING 0x00000002 /* have a pending write */ #define CHN_F_CLOSING 0x00000004 /* a pending close */ #define CHN_F_ABORTING 0x00000008 /* a pending abort */ #define CHN_F_PENDING_IO (CHN_F_READING | CHN_F_WRITING) #define CHN_F_RUNNING 0x00000010 /* dma is running */ #define CHN_F_TRIGGERED 0x00000020 #define CHN_F_NOTRIGGER 0x00000040 #define CHN_F_BUSY 0x00001000 /* has been opened */ #define CHN_F_HAS_SIZE 0x00002000 /* user set block size */ #define CHN_F_NBIO 0x00004000 /* do non-blocking i/o */ #define CHN_F_INIT 0x00008000 /* changed parameters. need init */ #define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */ #define CHN_F_DEAD 0x00020000 +#define CHN_F_BADSETTING 0x00040000 - #define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD) /* * This should be large enough to hold all pcm data between * tsleeps in chn_{read,write} at the highest sample rate. * (which is usually 48kHz * 16bit * stereo = 192000 bytes/sec) */ #define CHN_2NDBUFBLKSIZE (2 * 1024) /* The total number of blocks per secondary buffer. */ #define CHN_2NDBUFBLKNUM (32) /* The size of a whole secondary buffer. */ #define CHN_2NDBUFMAXSIZE (131072) + +#define CHN_DEFAULT_HZ 50 + +#define CHANNEL_DECLARE(name) static DEFINE_CLASS(name, name ## _methods, sizeof(struct kobj)) Index: stable/4/sys/dev/sound/pcm/channel_if.m =================================================================== --- stable/4/sys/dev/sound/pcm/channel_if.m (revision 71948) +++ stable/4/sys/dev/sound/pcm/channel_if.m (revision 71949) @@ -1,123 +1,124 @@ +# KOBJ # # Copyright (c) 2000 Cameron Grant # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # #include INTERFACE channel; CODE { static int channel_nosetdir(kobj_t obj, void *data, int dir) { return 0; } static int channel_noreset(kobj_t obj, void *data) { return 0; } static int channel_noresetdone(kobj_t obj, void *data) { return 0; } static int channel_nofree(kobj_t obj, void *data) { return 1; } }; METHOD void* init { kobj_t obj; void *devinfo; snd_dbuf *b; pcm_channel *c; int dir; }; METHOD int free { kobj_t obj; void *data; } DEFAULT channel_nofree; METHOD int reset { kobj_t obj; void *data; } DEFAULT channel_noreset; METHOD int resetdone { kobj_t obj; void *data; } DEFAULT channel_noresetdone; METHOD int setdir { kobj_t obj; void *data; int dir; } DEFAULT channel_nosetdir; METHOD u_int32_t setformat { kobj_t obj; void *data; u_int32_t format; }; METHOD u_int32_t setspeed { kobj_t obj; void *data; u_int32_t speed; }; METHOD u_int32_t setblocksize { kobj_t obj; void *data; u_int32_t blocksize; }; METHOD int trigger { kobj_t obj; void *data; int go; }; METHOD u_int32_t getptr { kobj_t obj; void *data; }; METHOD pcmchan_caps* getcaps { kobj_t obj; void *data; }; Index: stable/4/sys/dev/sound/pcm/datatypes.h =================================================================== --- stable/4/sys/dev/sound/pcm/datatypes.h (revision 71948) +++ stable/4/sys/dev/sound/pcm/datatypes.h (revision 71949) @@ -1,191 +1,166 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ typedef struct _snd_mixer snd_mixer; typedef struct _snd_dbuf snd_dbuf; typedef struct _snddev_info snddev_info; typedef struct _pcmchan_caps pcmchan_caps; typedef struct _pcm_feeder pcm_feeder; typedef struct _pcm_channel pcm_channel; -typedef int (mix_set_t)(snd_mixer *m, unsigned dev, unsigned left, unsigned right); -typedef int (mix_recsrc_t)(snd_mixer *m, u_int32_t src); -typedef int (mix_init_t)(snd_mixer *m); -typedef int (mix_uninit_t)(snd_mixer *m); +/*****************************************************************************/ struct _snd_mixer { - char name[64]; - mix_init_t *init; - mix_uninit_t *uninit; - mix_set_t *set; - mix_recsrc_t *setrecsrc; - + KOBJ_FIELDS; + const char *name; void *devinfo; int busy; + int hwvol_muted; + int hwvol_mixer; + int hwvol_step; + u_int32_t hwvol_mute_level; u_int32_t devs; u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; }; +/*****************************************************************************/ + /* * descriptor of a dma buffer. See dmabuf.c for documentation. * (rp,rl) and (fp,fl) identify the READY and FREE regions of the * buffer. dl contains the length used for dma transfer, dl>0 also * means that the channel is busy and there is a DMA transfer in progress. */ struct _snd_dbuf { u_int8_t *buf; - int bufsize; + int bufsize, maxsize; volatile int dl; /* transfer size */ volatile int rp, fp; /* pointers to the ready and free area */ volatile int rl, fl; /* lenght of ready and free areas. */ volatile int hp; volatile u_int32_t int_count, prev_int_count; volatile u_int32_t total, prev_total; int chan, dir; /* dma channel */ - int fmt, blksz, blkcnt; + int fmt, spd, bps; + int blksz, blkcnt; int underflow, overrun; + u_int32_t flags; bus_dmamap_t dmamap; - bus_dma_tag_t parent_dmat; + bus_dma_tag_t dmatag; struct selinfo sel; }; +#define SNDBUF_F_ISADMA 0x00000001 -typedef int (pcmfeed_init_t)(pcm_feeder *feeder); -typedef int (pcmfeed_free_t)(pcm_feeder *feeder); -typedef int (pcmfeed_feed_t)(pcm_feeder *feeder, pcm_channel *c, u_int8_t *buffer, - u_int32_t count, struct uio *stream); -typedef int (pcmfeed_set_t)(pcm_feeder *feeder, int what, int value); +/*****************************************************************************/ struct pcm_feederdesc { u_int32_t type; u_int32_t in, out; u_int32_t flags; int idx; }; struct _pcm_feeder { - char name[16]; + KOBJ_FIELDS; int align; struct pcm_feederdesc *desc; - pcmfeed_init_t *init; - pcmfeed_free_t *free; - pcmfeed_set_t *set; - pcmfeed_feed_t *feed; void *data; pcm_feeder *source; }; +/*****************************************************************************/ + struct _pcmchan_caps { u_int32_t minspeed, maxspeed; u_int32_t *fmtlist; u_int32_t caps; }; -typedef void *(pcmchan_init_t)(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -typedef int (pcmchan_setdir_t)(void *data, int dir); -typedef int (pcmchan_setformat_t)(void *data, u_int32_t format); -typedef int (pcmchan_setspeed_t)(void *data, u_int32_t speed); -typedef int (pcmchan_setblocksize_t)(void *data, u_int32_t blocksize); -typedef int (pcmchan_trigger_t)(void *data, int go); -typedef int (pcmchan_getptr_t)(void *data); -typedef int (pcmchan_free_t)(void *data); -typedef pcmchan_caps *(pcmchan_getcaps_t)(void *data); - struct _pcm_channel { - pcmchan_init_t *init; - pcmchan_setdir_t *setdir; - pcmchan_setformat_t *setformat; - pcmchan_setspeed_t *setspeed; - pcmchan_setblocksize_t *setblocksize; - pcmchan_trigger_t *trigger; - pcmchan_getptr_t *getptr; - pcmchan_getcaps_t *getcaps; - pcmchan_free_t *free; - void *nop1; - void *nop2; - void *nop3; - void *nop4; - void *nop5; - void *nop6; - void *nop7; + kobj_t methods; pcm_feeder *feeder; - struct pcm_feederdesc *feederdesc; u_int32_t align; int volume; u_int32_t speed; u_int32_t format; u_int32_t flags; u_int32_t feederflags; u_int32_t blocks; int direction; snd_dbuf buffer, buffer2nd; snddev_info *parent; void *devinfo; }; -typedef void (pcm_swap_t)(void *data, int dir); +/*****************************************************************************/ + #define SND_STATUSLEN 64 /* descriptor of audio device */ struct _snddev_info { pcm_channel *play, *rec, **aplay, **arec, fakechan; int *ref; unsigned playcount, reccount, chancount, maxchans; - snd_mixer mixer; + snd_mixer *mixer; u_long magic; unsigned flags; void *devinfo; - pcm_swap_t *swap; device_t dev; char status[SND_STATUSLEN]; + /* sysctl stuff is only used in 5.x */ + struct sysctl_ctx_list sysctl_tree; + struct sysctl_oid *sysctl_tree_top; }; +/*****************************************************************************/ + /* mixer description structure and macros - these should go away, - * only sb.[ch] and mss.[ch] use them + * only mss.[ch] use them */ struct mixer_def { u_int regno:7; u_int polarity:1; /* 1 means reversed */ u_int bitoffs:4; u_int nbits:4; }; typedef struct mixer_def mixer_ent; typedef struct mixer_def mixer_tab[32][2]; #define MIX_ENT(name, reg_l, pol_l, pos_l, len_l, reg_r, pol_r, pos_r, len_r) \ {{reg_l, pol_l, pos_l, len_l}, {reg_r, pol_r, pos_r, len_r}} #define PMIX_ENT(name, reg_l, pos_l, len_l, reg_r, pos_r, len_r) \ {{reg_l, 0, pos_l, len_l}, {reg_r, 0, pos_r, len_r}} #define MIX_NONE(name) MIX_ENT(name, 0,0,0,0, 0,0,0,0) Index: stable/4/sys/dev/sound/pcm/dsp.c =================================================================== --- stable/4/sys/dev/sound/pcm/dsp.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/dsp.c (revision 71949) @@ -1,652 +1,661 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #define OLDPCM_IOCTL static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch); static pcm_channel * allocchn(snddev_info *d, int direction) { pcm_channel *chns = (direction == PCMDIR_PLAY)? d->play : d->rec; int i, cnt = (direction == PCMDIR_PLAY)? d->playcount : d->reccount; for (i = 0; i < cnt; i++) { if (!(chns[i].flags & (CHN_F_BUSY | CHN_F_DEAD))) { chns[i].flags |= CHN_F_BUSY; return &chns[i]; } } return NULL; } static int getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch) { KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ ("getchns: read and write both prioritised")); if ((d->flags & SD_F_SIMPLEX) && (d->flags & SD_F_PRIO_SET)) { *rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan; *wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan; d->fakechan.flags |= CHN_F_BUSY; } else { *rdch = d->arec[chan]; *wrch = d->aplay[chan]; } return 0; } static void setchns(snddev_info *d, int chan) { KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ ("getchns: read and write both prioritised")); d->flags |= SD_F_DIR_SET; - if (d->swap) d->swap(d->devinfo, (d->flags & SD_F_PRIO_WR)? PCMDIR_PLAY : PCMDIR_REC); } int dsp_open(snddev_info *d, int chan, int oflags, int devtype) { pcm_channel *rdch, *wrch; u_int32_t fmt; if (chan >= d->chancount) return ENODEV; if ((d->flags & SD_F_SIMPLEX) && (d->ref[chan] > 0)) return EBUSY; rdch = d->arec[chan]; wrch = d->aplay[chan]; if (oflags & FREAD) { if (rdch == NULL) { rdch = allocchn(d, PCMDIR_REC); if (!rdch) return EBUSY; } else return EBUSY; } if (oflags & FWRITE) { if (wrch == NULL) { wrch = allocchn(d, PCMDIR_PLAY); if (!wrch) { if (rdch && (oflags & FREAD)) rdch->flags &= ~CHN_F_BUSY; return EBUSY; } } else return EBUSY; } d->aplay[chan] = wrch; d->arec[chan] = rdch; d->ref[chan]++; switch (devtype) { case SND_DEV_DSP16: fmt = AFMT_S16_LE; break; case SND_DEV_DSP: fmt = AFMT_U8; break; case SND_DEV_AUDIO: fmt = AFMT_MU_LAW; break; case SND_DEV_NORESET: fmt = 0; break; default: return ENXIO; } if (rdch && (oflags & FREAD)) { chn_reset(rdch, fmt); if (oflags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; } if (wrch && (oflags & FWRITE)) { chn_reset(wrch, fmt); if (oflags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; } return 0; } int dsp_close(snddev_info *d, int chan, int devtype) { pcm_channel *rdch, *wrch; d->ref[chan] = 0; #if 0 /* enable this if/when every close() is propagated here */ if (d->ref[chan]) return 0; #endif d->flags &= ~SD_F_TRANSIENT; rdch = d->arec[chan]; wrch = d->aplay[chan]; if (rdch) { chn_abort(rdch); - rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED); + rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(rdch, 0); } if (wrch) { chn_flush(wrch); - wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED); + wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); chn_reset(wrch, 0); } d->aplay[chan] = NULL; d->arec[chan] = NULL; return 0; } int dsp_read(snddev_info *d, int chan, struct uio *buf, int flag) { pcm_channel *rdch, *wrch; if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); getchns(d, chan, &rdch, &wrch); KASSERT(rdch, ("dsp_read: nonexistant channel")); KASSERT(rdch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel")); if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL; if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING; return chn_read(rdch, buf); } int dsp_write(snddev_info *d, int chan, struct uio *buf, int flag) { pcm_channel *rdch, *wrch; if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR; if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan); getchns(d, chan, &rdch, &wrch); KASSERT(wrch, ("dsp_write: nonexistant channel")); KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel")); if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) return EINVAL; if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING; return chn_write(wrch, buf); } int dsp_ioctl(snddev_info *d, int chan, u_long cmd, caddr_t arg) { int ret = 0, *arg_i = (int *)arg; u_long s; pcm_channel *wrch = NULL, *rdch = NULL; rdch = d->arec[chan]; wrch = d->aplay[chan]; if (rdch && (rdch->flags & CHN_F_DEAD)) rdch = NULL; if (wrch && (wrch->flags & CHN_F_DEAD)) wrch = NULL; if (!(rdch || wrch)) return EINVAL; /* * all routines are called with int. blocked. Make sure that * ints are re-enabled when calling slow or blocking functions! */ s = spltty(); switch(cmd) { #ifdef OLDPCM_IOCTL /* * we start with the new ioctl interface. */ case AIONWRITE: /* how many bytes can write ? */ if (wrch && wrch->buffer.dl) while (chn_wrfeed(wrch) > 0); *arg_i = wrch? wrch->buffer2nd.fl : 0; break; case AIOSSIZE: /* set the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; if (wrch) chn_setblocksize(wrch, 2, p->play_size); if (rdch) chn_setblocksize(rdch, 2, p->rec_size); } /* FALLTHROUGH */ case AIOGSIZE: /* get the current blocksize */ { struct snd_size *p = (struct snd_size *)arg; - if (wrch) p->play_size = wrch->buffer2nd.blksz; - if (rdch) p->rec_size = rdch->buffer2nd.blksz; + if (wrch) + p->play_size = wrch->buffer2nd.blksz; + if (rdch) + p->rec_size = rdch->buffer2nd.blksz; } break; case AIOSFMT: { snd_chan_param *p = (snd_chan_param *)arg; if (wrch) { chn_setformat(wrch, p->play_format); chn_setspeed(wrch, p->play_rate); } if (rdch) { chn_setformat(rdch, p->rec_format); chn_setspeed(rdch, p->rec_rate); } } /* FALLTHROUGH */ case AIOGFMT: { snd_chan_param *p = (snd_chan_param *)arg; p->play_rate = wrch? wrch->speed : 0; p->rec_rate = rdch? rdch->speed : 0; p->play_format = wrch? wrch->format : 0; p->rec_format = rdch? rdch->format : 0; } break; case AIOGCAP: /* get capabilities */ { snd_capabilities *p = (snd_capabilities *)arg; pcmchan_caps *pcaps = NULL, *rcaps = NULL; if (rdch) rcaps = chn_getcaps(rdch); if (wrch) pcaps = chn_getcaps(wrch); p->rate_min = max(rcaps? rcaps->minspeed : 0, pcaps? pcaps->minspeed : 0); p->rate_max = min(rcaps? rcaps->maxspeed : 1000000, pcaps? pcaps->maxspeed : 1000000); p->bufsize = min(rdch? rdch->buffer2nd.bufsize : 1000000, wrch? wrch->buffer2nd.bufsize : 1000000); /* XXX bad on sb16 */ p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & (wrch? chn_getformats(wrch) : 0xffffffff); if (rdch && wrch) p->formats |= (d->flags & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; p->mixers = 1; /* default: one mixer */ - p->inputs = d->mixer.devs; + p->inputs = mix_getdevs(d->mixer); p->left = p->right = 100; } break; case AIOSTOP: - if (*arg_i == AIOSYNC_PLAY && wrch) *arg_i = chn_abort(wrch); - else if (*arg_i == AIOSYNC_CAPTURE && rdch) *arg_i = chn_abort(rdch); + if (*arg_i == AIOSYNC_PLAY && wrch) + *arg_i = chn_abort(wrch); + else if (*arg_i == AIOSYNC_CAPTURE && rdch) + *arg_i = chn_abort(rdch); else { printf("AIOSTOP: bad channel 0x%x\n", *arg_i); *arg_i = 0; } break; case AIOSYNC: printf("AIOSYNC chan 0x%03lx pos %lu unimplemented\n", ((snd_sync_parm *)arg)->chan, ((snd_sync_parm *)arg)->pos); break; #endif /* * here follow the standard ioctls (filio.h etc.) */ case FIONREAD: /* get # bytes to read */ if (rdch && rdch->buffer.dl) while (chn_rdfeed(rdch) > 0); *arg_i = rdch? rdch->buffer2nd.rl : 0; break; case FIOASYNC: /*set/clear async i/o */ DEB( printf("FIOASYNC\n") ; ) break; case SNDCTL_DSP_NONBLOCK: case FIONBIO: /* set/clear non-blocking i/o */ if (rdch) rdch->flags &= ~CHN_F_NBIO; if (wrch) wrch->flags &= ~CHN_F_NBIO; if (*arg_i) { if (rdch) rdch->flags |= CHN_F_NBIO; if (wrch) wrch->flags |= CHN_F_NBIO; } break; /* * Finally, here is the linux-compatible ioctl interface */ #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: if (wrch) *arg_i = wrch->buffer2nd.blksz; else if (rdch) *arg_i = rdch->buffer2nd.blksz; else *arg_i = 0; break ; case SNDCTL_DSP_SETBLKSIZE: RANGE(*arg_i, 16, 65536); if (wrch) chn_setblocksize(wrch, 2, *arg_i); if (rdch) chn_setblocksize(rdch, 2, *arg_i); break; case SNDCTL_DSP_RESET: DEB(printf("dsp reset\n")); splx(s); if (wrch) chn_abort(wrch); if (rdch) chn_abort(rdch); break; case SNDCTL_DSP_SYNC: DEB(printf("dsp sync\n")); splx(s); if (wrch) chn_sync(wrch, wrch->buffer2nd.bufsize - 4); break; case SNDCTL_DSP_SPEED: splx(s); if (wrch) ret = chn_setspeed(wrch, *arg_i); if (rdch && ret == 0) ret = chn_setspeed(rdch, *arg_i); /* fallthru */ case SOUND_PCM_READ_RATE: *arg_i = wrch? wrch->speed : rdch->speed; break; case SNDCTL_DSP_STEREO: splx(s); if (wrch) ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | ((*arg_i)? AFMT_STEREO : 0)); if (rdch && ret == 0) ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | ((*arg_i)? AFMT_STEREO : 0)); *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 1 : 0; break; case SOUND_PCM_WRITE_CHANNELS: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ splx(s); if (*arg_i == 1 || *arg_i == 2) { if (wrch) ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | ((*arg_i == 2)? AFMT_STEREO : 0)); if (rdch && ret == 0) ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | ((*arg_i == 2)? AFMT_STEREO : 0)); *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1; } else *arg_i = 0; break; case SOUND_PCM_READ_CHANNELS: *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1; break; case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch); break ; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ splx(s); if ((*arg_i != AFMT_QUERY)) { if (wrch) ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); if (rdch && ret == 0) ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); } - *arg_i = (wrch? wrch->format: rdch->format) & ~AFMT_STEREO; + *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO; break; case SNDCTL_DSP_SUBDIVIDE: /* XXX watch out, this is RW! */ printf("SNDCTL_DSP_SUBDIVIDE unimplemented\n"); break; case SNDCTL_DSP_SETFRAGMENT: DEB(printf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); { pcm_channel *c = wrch? wrch : rdch; u_int32_t fragln = (*arg_i) & 0x0000ffff; u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; u_int32_t fragsz; RANGE(fragln, 4, 16); fragsz = 1 << fragln; if (maxfrags == 0) maxfrags = CHN_2NDBUFMAXSIZE / fragsz; if (maxfrags < 2) { ret = EINVAL; break; } if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) maxfrags = CHN_2NDBUFMAXSIZE / fragsz; DEB(printf("SNDCTL_DSP_SETFRAGMENT %d frags, %d sz\n", maxfrags, fragsz)); if (rdch) ret = chn_setblocksize(rdch, maxfrags, fragsz); if (wrch && ret == 0) ret = chn_setblocksize(wrch, maxfrags, fragsz); fragsz = c->buffer2nd.blksz; fragln = 0; while (fragsz > 1) { fragln++; fragsz >>= 1; } *arg_i = (c->buffer2nd.blkcnt << 16) | fragln; } break; case SNDCTL_DSP_GETISPACE: /* XXX Space for reading? Makes no sense... */ /* return the size of data available in the input queue */ { audio_buf_info *a = (audio_buf_info *)arg; if (rdch) { snd_dbuf *b = &rdch->buffer; snd_dbuf *bs = &rdch->buffer2nd; if (b->dl && !(rdch->flags & CHN_F_MAPPED)) /* * Suck up the secondary and DMA buffer. * chn_rdfeed*() takes care of the alignment. */ while (chn_rdfeed(rdch) > 0); a->bytes = bs->rl; a->fragments = a->bytes / rdch->buffer2nd.blksz; a->fragstotal = rdch->buffer2nd.blkcnt; a->fragsize = rdch->buffer2nd.blksz; } } break; case SNDCTL_DSP_GETOSPACE: /* return space available in the output queue */ { audio_buf_info *a = (audio_buf_info *)arg; if (wrch) { snd_dbuf *b = &wrch->buffer; snd_dbuf *bs = &wrch->buffer2nd; if (b->dl && !(wrch->flags & CHN_F_MAPPED)) { /* * Fill up the secondary and DMA buffer. * chn_wrfeed*() takes care of the alignment. * Check for underflow before writing into the buffers. */ chn_checkunderflow(wrch); while (chn_wrfeed(wrch) > 0); } a->bytes = bs->fl; a->fragments = a->bytes / wrch->buffer2nd.blksz; a->fragstotal = wrch->buffer2nd.blkcnt; a->fragsize = wrch->buffer2nd.blksz; } } break; case SNDCTL_DSP_GETIPTR: { count_info *a = (count_info *)arg; if (rdch) { snd_dbuf *b = &rdch->buffer; snd_dbuf *bs = &rdch->buffer2nd; if (b->dl && !(rdch->flags & CHN_F_MAPPED)) /* * Suck up the secondary and DMA buffer. * chn_rdfeed*() takes care of the alignment. */ while (chn_rdfeed(rdch) > 0); a->bytes = bs->total; a->blocks = rdch->blocks; a->ptr = bs->rp; rdch->blocks = 0; } else ret = EINVAL; } break; case SNDCTL_DSP_GETOPTR: { count_info *a = (count_info *)arg; if (wrch) { snd_dbuf *b = &wrch->buffer; snd_dbuf *bs = &wrch->buffer2nd; if (b->dl && !(wrch->flags & CHN_F_MAPPED)) { /* * Fill up the secondary and DMA buffer. * chn_wrfeed*() takes care of the alignment. * Check for underflow before writing into the buffers. */ chn_checkunderflow(wrch); while (chn_wrfeed(wrch) > 0); } a->bytes = bs->total; a->blocks = wrch->blocks; a->ptr = bs->rp; wrch->blocks = 0; } else ret = EINVAL; } break; case SNDCTL_DSP_GETCAPS: *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER; if (rdch && wrch && !(d->flags & SD_F_SIMPLEX)) *arg_i |= DSP_CAP_DUPLEX; break; case SOUND_PCM_READ_BITS: *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_16BIT)? 16 : 8; break; case SNDCTL_DSP_SETTRIGGER: if (rdch) { rdch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); if (*arg_i & PCM_ENABLE_INPUT) rdch->flags |= CHN_F_TRIGGERED; else rdch->flags |= CHN_F_NOTRIGGER; chn_intr(rdch); } if (wrch) { wrch->flags &= ~(CHN_F_TRIGGERED | CHN_F_NOTRIGGER); if (*arg_i & PCM_ENABLE_OUTPUT) wrch->flags |= CHN_F_TRIGGERED; else wrch->flags |= CHN_F_NOTRIGGER; chn_intr(wrch); } break; case SNDCTL_DSP_GETTRIGGER: *arg_i = 0; if (wrch && wrch->flags & CHN_F_TRIGGERED) *arg_i |= PCM_ENABLE_OUTPUT; if (rdch && rdch->flags & CHN_F_TRIGGERED) *arg_i |= PCM_ENABLE_INPUT; break; case SNDCTL_DSP_GETODELAY: if (wrch) { snd_dbuf *b = &wrch->buffer; snd_dbuf *bs = &wrch->buffer2nd; if (b->dl) { chn_checkunderflow(wrch); if (!(wrch->flags & CHN_F_MAPPED)) while (chn_wrfeed(wrch) > 0); } *arg_i = b->rl + bs->rl; } else ret = EINVAL; break; + case SNDCTL_DSP_POST: + if (wrch) { + wrch->flags &= ~CHN_F_NOTRIGGER; + chn_start(wrch, 1); + } + break; + case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: /* undocumented */ - case SNDCTL_DSP_POST: case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: /* dunno what these do, don't sound important */ default: DEB(printf("default ioctl chan%d fn 0x%08lx fail\n", chan, cmd)); ret = EINVAL; break; } splx(s); return ret; } int dsp_poll(snddev_info *d, int chan, int events, struct proc *p) { int ret = 0, e; pcm_channel *wrch = NULL, *rdch = NULL; getchns(d, chan, &rdch, &wrch); e = events & (POLLOUT | POLLWRNORM); if (wrch && e) ret |= chn_poll(wrch, e, p); e = events & (POLLIN | POLLRDNORM); if (rdch && e) ret |= chn_poll(rdch, e, p); return ret; } int dsp_mmap(snddev_info *d, int chan, vm_offset_t offset, int nprot) { pcm_channel *wrch = NULL, *rdch = NULL, *c = NULL; getchns(d, chan, &rdch, &wrch); /* XXX this is broken by line 204 of vm/device_pager.c, so force write buffer */ if (1 || (wrch && (nprot & PROT_WRITE))) c = wrch; else if (rdch && (nprot & PROT_READ)) c = rdch; if (c && (c->format == c->buffer.fmt)) { c->flags |= CHN_F_MAPPED; return atop(vtophys(c->buffer2nd.buf + offset)); } else return -1; } Index: stable/4/sys/dev/sound/pcm/fake.c =================================================================== --- stable/4/sys/dev/sound/pcm/fake.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/fake.c (revision 71949) @@ -1,125 +1,120 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include -/* channel interface */ -static void *fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir); -static int fkchan_setdir(void *data, int dir); -static int fkchan_setformat(void *data, u_int32_t format); -static int fkchan_setspeed(void *data, u_int32_t speed); -static int fkchan_setblocksize(void *data, u_int32_t blocksize); -static int fkchan_trigger(void *data, int go); -static int fkchan_getptr(void *data); -static pcmchan_caps *fkchan_getcaps(void *data); - static u_int32_t fk_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, AFMT_U16_LE, AFMT_STEREO | AFMT_U16_LE, AFMT_S16_BE, AFMT_STEREO | AFMT_S16_BE, AFMT_U16_BE, AFMT_STEREO | AFMT_U16_BE, 0 }; -static pcmchan_caps fk_caps = {4000, 48000, fk_fmt, 0}; +static pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0}; -static pcm_channel fk_chantemplate = { - fkchan_init, - fkchan_setdir, - fkchan_setformat, - fkchan_setspeed, - fkchan_setblocksize, - fkchan_trigger, - fkchan_getptr, - fkchan_getcaps, -}; - /* channel interface */ static void * -fkchan_init(void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) +fkchan_init(kobj_t obj, void *devinfo, snd_dbuf *b, pcm_channel *c, int dir) { b->bufsize = 16384; b->buf = malloc(b->bufsize, M_DEVBUF, M_NOWAIT); return (void *)0xbabef00d; } static int -fkchan_setdir(void *data, int dir) +fkchan_setformat(kobj_t obj, void *data, u_int32_t format) { return 0; } static int -fkchan_setformat(void *data, u_int32_t format) +fkchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { - return 0; -} - -static int -fkchan_setspeed(void *data, u_int32_t speed) -{ return speed; } static int -fkchan_setblocksize(void *data, u_int32_t blocksize) +fkchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { return blocksize; } static int -fkchan_trigger(void *data, int go) +fkchan_trigger(kobj_t obj, void *data, int go) { return 0; } static int -fkchan_getptr(void *data) +fkchan_getptr(kobj_t obj, void *data) { return 0; } static pcmchan_caps * -fkchan_getcaps(void *data) +fkchan_getcaps(kobj_t obj, void *data) { return &fk_caps; } +static kobj_method_t fkchan_methods[] = { + KOBJMETHOD(channel_init, fkchan_init), + KOBJMETHOD(channel_setformat, fkchan_setformat), + KOBJMETHOD(channel_setspeed, fkchan_setspeed), + KOBJMETHOD(channel_setblocksize, fkchan_setblocksize), + KOBJMETHOD(channel_trigger, fkchan_trigger), + KOBJMETHOD(channel_getptr, fkchan_getptr), + KOBJMETHOD(channel_getcaps, fkchan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(fkchan); + int fkchan_setup(pcm_channel *c) { - *c = fk_chantemplate; + c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK); return 0; } + +int +fkchan_kill(pcm_channel *c) +{ + kobj_delete(c->methods, M_DEVBUF); + c->methods = NULL; + return 0; +} + + Index: stable/4/sys/dev/sound/pcm/feeder.c =================================================================== --- stable/4/sys/dev/sound/pcm/feeder.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/feeder.c (revision 71949) @@ -1,317 +1,333 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include +#include "feeder_if.h" + +MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); + #define MAXFEEDERS 256 #undef FEEDER_DEBUG struct feedertab_entry { SLIST_ENTRY(feedertab_entry) link; - pcm_feeder *feeder; + struct feeder_class *feederclass; struct pcm_feederdesc *desc; int idx; }; static SLIST_HEAD(, feedertab_entry) feedertab; /*****************************************************************************/ void feeder_register(void *p) { - pcm_feeder *f = p; + struct feeder_class *fc = p; struct feedertab_entry *fte; static int feedercnt = 0; int i; if (feedercnt == 0) { - if (f->desc) - panic("FIRST FEEDER NOT ROOT: %s\n", f->name); + if (fc->desc) + panic("FIRST FEEDER NOT ROOT: %s\n", fc->name); SLIST_INIT(&feedertab); - fte = malloc(sizeof(*fte), M_DEVBUF, M_NOWAIT); - fte->feeder = f; + fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); + fte->feederclass = fc; fte->desc = NULL; fte->idx = feedercnt; SLIST_INSERT_HEAD(&feedertab, fte, link); feedercnt++; return; } /* printf("installing feeder: %s\n", f->name); */ i = 0; - while ((feedercnt < MAXFEEDERS) && (f->desc[i].type > 0)) { - fte = malloc(sizeof(*fte), M_DEVBUF, M_NOWAIT); - fte->feeder = f; - fte->desc = &f->desc[i]; + while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { + fte = malloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); + fte->feederclass = fc; + fte->desc = &fc->desc[i]; fte->idx = feedercnt; fte->desc->idx = feedercnt; SLIST_INSERT_HEAD(&feedertab, fte, link); i++; } feedercnt++; if (feedercnt >= MAXFEEDERS) printf("MAXFEEDERS exceeded\n"); } static int cmpdesc(struct pcm_feederdesc *n, struct pcm_feederdesc *m) { return ((n->type == m->type) && ((n->in == 0) || (n->in == m->in)) && ((n->out == 0) || (n->out == m->out)) && (n->flags == m->flags)); } -pcm_feeder * -feeder_get(struct pcm_feederdesc *desc) +static void +feeder_destroy(pcm_feeder *f) { - struct feedertab_entry *fte; + FEEDER_FREE(f); + free(f->desc, M_FEEDER); + kobj_delete((kobj_t)f, M_FEEDER); +} - SLIST_FOREACH(fte, &feedertab, link) { - if ((fte->desc != NULL) && cmpdesc(desc, fte->desc)) - return fte->feeder; +static pcm_feeder * +feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) +{ + pcm_feeder *f; + int err; + + f = (pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO); + f->align = fc->align; + f->desc = malloc(sizeof(*(f->desc)), M_FEEDER, M_WAITOK | M_ZERO); + if (desc) + *(f->desc) = *desc; + else { + f->desc->type = FEEDER_ROOT; + f->desc->in = 0; + f->desc->out = 0; + f->desc->flags = 0; + f->desc->idx = 0; } - return NULL; + f->data = fc->data; + f->source = NULL; + err = FEEDER_INIT(f); + if (err) { + feeder_destroy(f); + return NULL; + } else + return f; } -pcm_feeder * -feeder_getroot() +struct feeder_class * +feeder_getclass(struct pcm_feederdesc *desc) { struct feedertab_entry *fte; SLIST_FOREACH(fte, &feedertab, link) { - if (fte->desc == NULL) - return fte->feeder; + if ((desc == NULL) && (fte->desc == NULL)) + return fte->feederclass; + if ((fte->desc != NULL) && (desc != NULL) && cmpdesc(desc, fte->desc)) + return fte->feederclass; } return NULL; } int -feeder_set(pcm_feeder *feeder, int what, int value) +chn_addfeeder(pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc) { - if (feeder->set) - return feeder->set(feeder, what, value); - else - return -1; -} - -int -chn_addfeeder(pcm_channel *c, pcm_feeder *f) -{ pcm_feeder *nf; - struct pcm_feederdesc *nfdesc; - nf = malloc(sizeof(*nf), M_DEVBUF, M_NOWAIT); - nfdesc = malloc(sizeof(*nfdesc), M_DEVBUF, M_NOWAIT); - *nfdesc = *(f->desc); - *nf = *f; - nf->desc = nfdesc; + nf = feeder_create(fc, desc); + if (nf == NULL) + return -1; + nf->source = c->feeder; - if (nf->init) - nf->init(nf); + if (nf->align > 0) c->align += nf->align; else if (nf->align < 0 && c->align < -nf->align) c->align = -nf->align; c->feeder = nf; return 0; } int chn_removefeeder(pcm_channel *c) { pcm_feeder *f; - if (c->feeder->source == NULL) + if (c->feeder == NULL) return -1; - f = c->feeder->source; - if (c->feeder->free) - c->feeder->free(c->feeder); - free(c->feeder->desc, M_DEVBUF); - free(c->feeder, M_DEVBUF); - c->feeder = f; + f = c->feeder; + c->feeder = c->feeder->source; + feeder_destroy(f); return 0; } pcm_feeder * chn_findfeeder(pcm_channel *c, u_int32_t type) { pcm_feeder *f; f = c->feeder; while (f != NULL) { if (f->desc->type == type) return f; f = f->source; } return NULL; } static int chainok(pcm_feeder *test, pcm_feeder *stop) { u_int32_t visited[MAXFEEDERS / 32]; u_int32_t idx, mask; bzero(visited, sizeof(visited)); while (test && (test != stop)) { idx = test->desc->idx; if (idx < 0) panic("bad idx %d", idx); if (idx >= MAXFEEDERS) panic("bad idx %d", idx); mask = 1 << (idx & 31); idx >>= 5; if (visited[idx] & mask) return 0; visited[idx] |= mask; test = test->source; } return 1; } static pcm_feeder * feeder_fmtchain(u_int32_t *to, pcm_feeder *source, pcm_feeder *stop, int maxdepth) { struct feedertab_entry *fte; pcm_feeder *try, *ret; - struct pcm_feederdesc *trydesc; + struct pcm_feederdesc trydesc; /* printf("trying %s...\n", source->name); */ if (fmtvalid(source->desc->out, to)) { /* printf("got it\n"); */ return source; } if (maxdepth < 0) return NULL; - try = malloc(sizeof(*try), M_DEVBUF, M_NOWAIT); - trydesc = malloc(sizeof(*trydesc), M_DEVBUF, M_NOWAIT); - trydesc->type = FEEDER_FMT; - trydesc->in = source->desc->out; - trydesc->out = 0; - trydesc->flags = 0; - trydesc->idx = -1; + trydesc.type = FEEDER_FMT; + trydesc.in = source->desc->out; + trydesc.out = 0; + trydesc.flags = 0; + trydesc.idx = -1; SLIST_FOREACH(fte, &feedertab, link) { if ((fte->desc) && (fte->desc->in == source->desc->out)) { - *try = *(fte->feeder); + trydesc.out = fte->desc->out; + trydesc.idx = fte->idx; + try = feeder_create(fte->feederclass, &trydesc); + if (try == NULL) + return NULL; try->source = source; - try->desc = trydesc; - trydesc->out = fte->desc->out; - trydesc->idx = fte->idx; ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; if (ret != NULL) return ret; + feeder_destroy(try); } } - free(try, M_DEVBUF); - free(trydesc, M_DEVBUF); /* printf("giving up %s...\n", source->name); */ return NULL; } u_int32_t chn_fmtchain(pcm_channel *c, u_int32_t *to) { pcm_feeder *try, *stop; int max; stop = c->feeder; try = NULL; max = 0; while (try == NULL && max < 8) { try = feeder_fmtchain(to, c->feeder, stop, max); max++; } if (try == NULL) return 0; c->feeder = try; c->align = 0; #ifdef FEEDER_DEBUG printf("chain: "); #endif while (try && (try != stop)) { #ifdef FEEDER_DEBUG printf("%s [%d]", try->name, try->desc->idx); if (try->source) printf(" -> "); #endif - if (try->init) - try->init(try); if (try->align > 0) c->align += try->align; else if (try->align < 0 && c->align < -try->align) c->align = -try->align; try = try->source; } #ifdef FEEDER_DEBUG printf("%s [%d]\n", try->name, try->desc->idx); #endif return c->feeder->desc->out; } /*****************************************************************************/ static int feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream) { int ret, s; KASSERT(count, ("feed_root: count == 0")); count &= ~((1 << ch->align) - 1); KASSERT(count, ("feed_root: aligned count == 0 (align = %d)", ch->align)); s = spltty(); count = min(count, stream->uio_resid); if (count) { ret = uiomove(buffer, count, stream); - KASSERT(ret == 0, ("feed_root: uiomove failed")); + KASSERT(ret == 0, ("feed_root: uiomove failed (%d)", ret)); } splx(s); return count; } -static pcm_feeder feeder_root = { - "root", - 0, - NULL, - NULL, - NULL, - NULL, - feed_root, + +static kobj_method_t feeder_root_methods[] = { + KOBJMETHOD(feeder_feed, feed_root), + { 0, 0 } }; -SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root); +static struct feeder_class feeder_root_class = { + name: "feeder_root", + methods: feeder_root_methods, + size: sizeof(pcm_feeder), + align: 0, + desc: NULL, + data: NULL, +}; +SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); + Index: stable/4/sys/dev/sound/pcm/feeder.h =================================================================== --- stable/4/sys/dev/sound/pcm/feeder.h (revision 71948) +++ stable/4/sys/dev/sound/pcm/feeder.h (revision 71949) @@ -1,51 +1,65 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ +struct feeder_class { + KOBJ_CLASS_FIELDS; + int align; + struct pcm_feederdesc *desc; + void *data; +}; + void feeder_register(void *p); -pcm_feeder *feeder_get(struct pcm_feederdesc *desc); -pcm_feeder *feeder_getroot(void); -int feeder_set(pcm_feeder *feeder, int what, int value); +struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc); u_int32_t chn_fmtchain(pcm_channel *c, u_int32_t *to); -int chn_addfeeder(pcm_channel *c, pcm_feeder *f); +int chn_addfeeder(pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc); int chn_removefeeder(pcm_channel *c); pcm_feeder *chn_findfeeder(pcm_channel *c, u_int32_t type); -#define FEEDER_DECLARE(feeder) SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder) +#define FEEDER_DECLARE(feeder, palign, pdata) \ +static struct feeder_class feeder ## _class = { \ + name: #feeder, \ + methods: feeder ## _methods, \ + size: sizeof(pcm_feeder), \ + align: palign, \ + desc: feeder ## _desc, \ + data: pdata, \ +}; \ +SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class); #define FEEDER_ROOT 1 #define FEEDER_FMT 2 #define FEEDER_RATE 3 #define FEEDER_FILTER 4 #define FEEDER_VOLUME 5 #define FEEDER_LAST FEEDER_VOLUME #define FEEDRATE_SRC 1 #define FEEDRATE_DST 2 Index: stable/4/sys/dev/sound/pcm/feeder_fmt.c =================================================================== --- stable/4/sys/dev/sound/pcm/feeder_fmt.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/feeder_fmt.c (revision 71949) @@ -1,496 +1,450 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include +#include "feeder_if.h" + +MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder"); + #define FEEDBUFSZ 8192 static unsigned char ulaw_to_u8[] = { 3, 7, 11, 15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59, 63, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 113, 114, 114, 115, 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, 120, 121, 121, 121, 122, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 253, 249, 245, 241, 237, 233, 229, 225, 221, 217, 213, 209, 205, 201, 197, 193, 190, 188, 186, 184, 182, 180, 178, 176, 174, 172, 170, 168, 166, 164, 162, 160, 158, 157, 156, 155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, 143, 142, 142, 141, 141, 140, 140, 139, 139, 138, 138, 137, 137, 136, 136, 135, 135, 135, 134, 134, 134, 134, 133, 133, 133, 133, 132, 132, 132, 132, 131, 131, 131, 131, 131, 131, 130, 130, 130, 130, 130, 130, 130, 130, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 129, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, }; static unsigned char u8_to_ulaw[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 49, 51, 53, 55, 57, 59, 61, 63, 66, 70, 74, 78, 84, 92, 104, 254, 231, 219, 211, 205, 201, 197, 193, 190, 188, 186, 184, 182, 180, 178, 176, 175, 174, 173, 172, 171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 160, 159, 159, 158, 158, 157, 157, 156, 156, 155, 155, 154, 154, 153, 153, 152, 152, 151, 151, 150, 150, 149, 149, 148, 148, 147, 147, 146, 146, 145, 145, 144, 144, 143, 143, 143, 143, 142, 142, 142, 142, 141, 141, 141, 141, 140, 140, 140, 140, 139, 139, 139, 139, 138, 138, 138, 138, 137, 137, 137, 137, 136, 136, 136, 136, 135, 135, 135, 135, 134, 134, 134, 134, 133, 133, 133, 133, 132, 132, 132, 132, 131, 131, 131, 131, 130, 130, 130, 130, 129, 129, 129, 129, 128, 128, 128, 128, }; /*****************************************************************************/ static int feed_8to16le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { int i, j, k; - k = f->source->feed(f->source, c, b, count / 2, stream); + k = FEEDER_FEED(f->source, c, b, count / 2, stream); j = k - 1; i = j * 2 + 1; while (i > 0 && j >= 0) { b[i--] = b[j--]; b[i--] = 0; } return k * 2; } -static struct pcm_feederdesc desc_8to16le[] = { +static struct pcm_feederdesc feeder_8to16le_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_8to16le = { - "8to16le", - 0, - desc_8to16le, - NULL, - NULL, - NULL, - feed_8to16le, +static kobj_method_t feeder_8to16le_methods[] = { + KOBJMETHOD(feeder_feed, feed_8to16le), + { 0, 0 } }; -FEEDER_DECLARE(feeder_8to16le); +FEEDER_DECLARE(feeder_8to16le, 0, NULL); /*****************************************************************************/ static int feed_16to8_init(pcm_feeder *f) { - f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); - return (f->data == NULL); + f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_WAITOK | M_ZERO); + return (f->data == NULL)? 0 : ENOMEM; } static int feed_16to8_free(pcm_feeder *f) { - if (f->data) free(f->data, M_DEVBUF); + if (f->data) + free(f->data, M_FMTFEEDER); f->data = NULL; return 0; } static int feed_16leto8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { u_int32_t i = 0, toget = count * 2; int j = 1, k; - k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); + k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); while (j < k) { b[i++] = ((u_int8_t *)f->data)[j]; j += 2; } return i; } -static struct pcm_feederdesc desc_16leto8[] = { +static struct pcm_feederdesc feeder_16leto8_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0}, {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0}, {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_16leto8 = { - "16leto8", - 1, - desc_16leto8, - feed_16to8_init, - feed_16to8_free, - NULL, - feed_16leto8, +static kobj_method_t feeder_16leto8_methods[] = { + KOBJMETHOD(feeder_init, feed_16to8_init), + KOBJMETHOD(feeder_free, feed_16to8_free), + KOBJMETHOD(feeder_feed, feed_16leto8), + { 0, 0 } }; -FEEDER_DECLARE(feeder_16leto8); +FEEDER_DECLARE(feeder_16leto8, 1, NULL); /*****************************************************************************/ static int feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { - int i, j, k = f->source->feed(f->source, c, b, count / 2, stream); + int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, stream); j = k - 1; i = j * 2 + 1; while (i > 0 && j >= 0) { b[i--] = b[j]; b[i--] = b[j]; j--; } return k * 2; } -static struct pcm_feederdesc desc_monotostereo8[] = { +static struct pcm_feederdesc feeder_monotostereo8_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_U8 | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S8, AFMT_S8 | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_monotostereo8 = { - "monotostereo8", - 0, - desc_monotostereo8, - NULL, - NULL, - NULL, - feed_monotostereo8, +static kobj_method_t feeder_monotostereo8_methods[] = { + KOBJMETHOD(feeder_feed, feed_monotostereo8), + { 0, 0 } }; -FEEDER_DECLARE(feeder_monotostereo8); +FEEDER_DECLARE(feeder_monotostereo8, 0, NULL); /*****************************************************************************/ static int feed_monotostereo16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { - int i, j, k = f->source->feed(f->source, c, b, count / 2, stream); + int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, stream); u_int8_t x, y; j = k - 1; i = j * 2 + 1; while (i > 3 && j >= 1) { x = b[j--]; y = b[j--]; b[i--] = x; b[i--] = y; b[i--] = x; b[i--] = y; } return k * 2; } -static struct pcm_feederdesc desc_monotostereo16[] = { +static struct pcm_feederdesc feeder_monotostereo16_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_monotostereo16 = { - "monotostereo16", - 0, - desc_monotostereo16, - NULL, - NULL, - NULL, - feed_monotostereo16, +static kobj_method_t feeder_monotostereo16_methods[] = { + KOBJMETHOD(feeder_feed, feed_monotostereo16), + { 0, 0 } }; -FEEDER_DECLARE(feeder_monotostereo16); +FEEDER_DECLARE(feeder_monotostereo16, 0, NULL); /*****************************************************************************/ static int feed_stereotomono8_init(pcm_feeder *f) { - f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); - return (f->data == NULL); + f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_WAITOK | M_ZERO); + return (f->data == NULL)? 0 : ENOMEM; } static int feed_stereotomono8_free(pcm_feeder *f) { - if (f->data) free(f->data, M_DEVBUF); + if (f->data) + free(f->data, M_FMTFEEDER); f->data = NULL; return 0; } static int feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { u_int32_t i = 0, toget = count * 2; int j = 0, k; - k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); + k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); while (j < k) { b[i++] = ((u_int8_t *)f->data)[j]; j += 2; } return i; } -static struct pcm_feederdesc desc_stereotomono8[] = { +static struct pcm_feederdesc feeder_stereotomono8_desc[] = { {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U8, 0}, {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S8, 0}, {0}, }; -static pcm_feeder feeder_stereotomono8 = { - "stereotomono8", - 1, - desc_stereotomono8, - feed_stereotomono8_init, - feed_stereotomono8_free, - NULL, - feed_stereotomono8, +static kobj_method_t feeder_stereotomono8_methods[] = { + KOBJMETHOD(feeder_init, feed_stereotomono8_init), + KOBJMETHOD(feeder_free, feed_stereotomono8_free), + KOBJMETHOD(feeder_feed, feed_stereotomono8), + { 0, 0 } }; -FEEDER_DECLARE(feeder_stereotomono8); +FEEDER_DECLARE(feeder_stereotomono8, 1, NULL); /*****************************************************************************/ static int feed_stereotomono16_init(pcm_feeder *f) { - f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); - return (f->data == NULL); + f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_WAITOK | M_ZERO); + return (f->data == NULL)? 0 : ENOMEM; } static int feed_stereotomono16_free(pcm_feeder *f) { - if (f->data) free(f->data, M_DEVBUF); + if (f->data) + free(f->data, M_FMTFEEDER); f->data = NULL; return 0; } static int feed_stereotomono16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { u_int32_t i = 0, toget = count * 2; int j = 0, k; - k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); + k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); while (j < k) { b[i++] = ((u_int8_t *)f->data)[j]; b[i++] = ((u_int8_t *)f->data)[j + 1]; j += 4; } return i; } -static struct pcm_feederdesc desc_stereotomono16[] = { +static struct pcm_feederdesc feeder_stereotomono16_desc[] = { {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE, 0}, {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE, 0}, {0}, }; -static pcm_feeder feeder_stereotomono16 = { - "stereotomono16", - 1, - desc_stereotomono16, - feed_stereotomono16_init, - feed_stereotomono16_free, - NULL, - feed_stereotomono16, +static kobj_method_t feeder_stereotomono16_methods[] = { + KOBJMETHOD(feeder_init, feed_stereotomono16_init), + KOBJMETHOD(feeder_free, feed_stereotomono16_free), + KOBJMETHOD(feeder_feed, feed_stereotomono16), + { 0, 0 } }; -FEEDER_DECLARE(feeder_stereotomono16); +FEEDER_DECLARE(feeder_stereotomono16, 1, NULL); /*****************************************************************************/ static int feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { u_int8_t t; - int i = 0, j = f->source->feed(f->source, c, b, count, stream); + int i = 0, j = FEEDER_FEED(f->source, c, b, count, stream); while (i < j) { t = b[i]; b[i] = b[i + 1]; b[i + 1] = t; i += 2; } return i; } -static struct pcm_feederdesc desc_endian[] = { +static struct pcm_feederdesc feeder_endian_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0}, {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0}, {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_endian = { - "endian", - -1, - desc_endian, - NULL, - NULL, - NULL, - feed_endian, +static kobj_method_t feeder_endian_methods[] = { + KOBJMETHOD(feeder_feed, feed_endian), + { 0, 0 } }; -FEEDER_DECLARE(feeder_endian); +FEEDER_DECLARE(feeder_endian, 0, NULL); /*****************************************************************************/ static int feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { - int i = 0, j = f->source->feed(f->source, c, b, count, stream); + int i = 0, j = FEEDER_FEED(f->source, c, b, count, stream); int ssz = (int)f->data, ofs = ssz - 1; while (i < j) { b[i + ofs] ^= 0x80; i += ssz; } return i; } -static struct pcm_feederdesc desc_sign8[] = { +static struct pcm_feederdesc feeder_sign8_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_S8, 0}, {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S8, AFMT_U8, 0}, {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_sign8 = { - "sign8", - 0, - desc_sign8, - NULL, - NULL, - NULL, - feed_sign, - (void *)1, +static kobj_method_t feeder_sign8_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign), + { 0, 0 } }; -FEEDER_DECLARE(feeder_sign8); +FEEDER_DECLARE(feeder_sign8, 0, (void *)1); -static struct pcm_feederdesc desc_sign16le[] = { +static struct pcm_feederdesc feeder_sign16le_desc[] = { {FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0}, {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, {FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0}, {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_sign16le = { - "sign16le", - -1, - desc_sign16le, - NULL, - NULL, - NULL, - feed_sign, - (void *)2, +static kobj_method_t feeder_sign16le_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign), + { 0, 0 } }; -FEEDER_DECLARE(feeder_sign16le); +FEEDER_DECLARE(feeder_sign16le, -1, (void *)2); /*****************************************************************************/ static int feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) { - int i = 0, j = f->source->feed(f->source, c, b, count, stream); + int i = 0, j = FEEDER_FEED(f->source, c, b, count, stream); while (i < j) { b[i] = ((u_int8_t *)f->data)[b[i]]; i++; } return i; } -static struct pcm_feederdesc desc_ulawtou8[] = { +static struct pcm_feederdesc feeder_ulawtou8_desc[] = { {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0}, {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_ulawtou8 = { - "ulawtou8", - 0, - desc_ulawtou8, - NULL, - NULL, - NULL, - feed_table, - ulaw_to_u8, +static kobj_method_t feeder_ulawtou8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table), + { 0, 0 } }; -FEEDER_DECLARE(feeder_ulawtou8); +FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_u8); -static struct pcm_feederdesc desc_u8toulaw[] = { +static struct pcm_feederdesc feeder_u8toulaw_desc[] = { {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0}, {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0}, {0}, }; -static pcm_feeder feeder_u8toulaw = { - "u8toulaw", - 0, - desc_u8toulaw, - NULL, - NULL, - NULL, - feed_table, - u8_to_ulaw, +static kobj_method_t feeder_u8toulaw_methods[] = { + KOBJMETHOD(feeder_feed, feed_table), + { 0, 0 } }; -FEEDER_DECLARE(feeder_u8toulaw); +FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw); Index: stable/4/sys/dev/sound/pcm/feeder_if.m =================================================================== --- stable/4/sys/dev/sound/pcm/feeder_if.m (revision 71948) +++ stable/4/sys/dev/sound/pcm/feeder_if.m (revision 71949) @@ -1,71 +1,72 @@ +# KOBJ # # Copyright (c) 2000 Cameron Grant # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # #include INTERFACE feeder; CODE { static int feeder_noinit(pcm_feeder* feeder) { return 0; } static int feeder_nofree(pcm_feeder* feeder) { return 0; } }; METHOD int init { pcm_feeder* feeder; } DEFAULT feeder_noinit; METHOD int free { pcm_feeder* feeder; } DEFAULT feeder_nofree; METHOD int set { pcm_feeder* feeder; int what; int value; }; METHOD int feed { pcm_feeder* feeder; pcm_channel* c; u_int8_t* buffer; u_int32_t count; struct uio* stream; }; Index: stable/4/sys/dev/sound/pcm/mixer.c =================================================================== --- stable/4/sys/dev/sound/pcm/mixer.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/mixer.c (revision 71949) @@ -1,247 +1,386 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include +#include "mixer_if.h" + +MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); + static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_VOLUME] = 75, [SOUND_MIXER_BASS] = 50, [SOUND_MIXER_TREBLE] = 50, [SOUND_MIXER_SYNTH] = 75, [SOUND_MIXER_PCM] = 75, [SOUND_MIXER_SPEAKER] = 75, [SOUND_MIXER_LINE] = 75, [SOUND_MIXER_MIC] = 0, [SOUND_MIXER_CD] = 75, [SOUND_MIXER_LINE1] = 75, [SOUND_MIXER_VIDEO] = 75, [SOUND_MIXER_RECLEV] = 0, [SOUND_MIXER_OGAIN] = 50, }; +static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; + static int -mixer_set(snddev_info *d, unsigned dev, unsigned lev) +mixer_lookup(char *devname) { - if (d == NULL || d->mixer.set == NULL) return -1; - if ((dev < SOUND_MIXER_NRDEVICES) && (d->mixer.devs & (1 << dev))) { - unsigned l = min((lev & 0x00ff), 100); - unsigned r = min(((lev & 0xff00) >> 8), 100); - int v = d->mixer.set(&d->mixer, dev, l, r); - if (v >= 0) d->mixer.level[dev] = l | (r << 8); - return 0; - } else return -1; + int i; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + if (strncmp(devname, snd_mixernames[i], + strlen(snd_mixernames[i])) == 0) + return i; + return -1; } static int -mixer_get(snddev_info *d, int dev) +mixer_set(snd_mixer *mixer, unsigned dev, unsigned lev) { - if (d == NULL) return -1; - if (dev < SOUND_MIXER_NRDEVICES && (d->mixer.devs & (1 << dev))) - return d->mixer.level[dev]; + unsigned l, r; + int v; + + if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev)))) + return -1; + + l = min((lev & 0x00ff), 100); + r = min(((lev & 0xff00) >> 8), 100); + + v = MIXER_SET(mixer, dev, l, r); + if (v < 0) + return -1; + + mixer->level[dev] = l | (r << 8); + return 0; +} + +static int +mixer_get(snd_mixer *mixer, int dev) +{ + if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev))) + return mixer->level[dev]; else return -1; } static int -mixer_setrecsrc(snddev_info *d, u_int32_t src) +mixer_setrecsrc(snd_mixer *mixer, u_int32_t src) { - if (d == NULL || d->mixer.setrecsrc == NULL) return -1; - src &= d->mixer.recdevs; - if (src == 0) src = SOUND_MASK_MIC; - d->mixer.recsrc = d->mixer.setrecsrc(&d->mixer, src); + src &= mixer->recdevs; + if (src == 0) + src = SOUND_MASK_MIC; + mixer->recsrc = MIXER_SETRECSRC(mixer, src); return 0; } static int -mixer_getrecsrc(snddev_info *d) +mixer_getrecsrc(snd_mixer *mixer) { - if (d == NULL) return -1; - return d->mixer.recsrc; + return mixer->recsrc; } +void +mix_setdevs(snd_mixer *m, u_int32_t v) +{ + m->devs = v; +} + +void +mix_setrecdevs(snd_mixer *m, u_int32_t v) +{ + m->recdevs = v; +} + +u_int32_t +mix_getdevs(snd_mixer *m) +{ + return m->devs; +} + +u_int32_t +mix_getrecdevs(snd_mixer *m) +{ + return m->recdevs; +} + +void * +mix_getdevinfo(snd_mixer *m) +{ + return m->devinfo; +} + int -mixer_init(device_t dev, snd_mixer *m, void *devinfo) +mixer_busy(snd_mixer *m, int busy) { - snddev_info *d = device_get_softc(dev); - if (d == NULL) return -1; - d->mixer = *m; - d->mixer.devinfo = devinfo; - bzero(&d->mixer.level, sizeof d->mixer.level); - if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) { - int i; - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { - u_int16_t v = snd_mixerdefaults[i]; - mixer_set(d, i, v | (v << 8)); - } - mixer_setrecsrc(d, SOUND_MASK_MIC); - return 0; - } else return -1; + m->busy = busy; + return 0; } int +mixer_isbusy(snd_mixer *m) +{ + return m->busy; +} + +int +mixer_init(device_t dev, kobj_class_t cls, void *devinfo) +{ + snddev_info *d; + snd_mixer *m; + u_int16_t v; + int i; + + d = device_get_softc(dev); + m = (snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); + + m->name = cls->name; + m->devinfo = devinfo; + + if (MIXER_INIT(m)) + goto bad; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + v = snd_mixerdefaults[i]; + mixer_set(m, i, v | (v << 8)); + } + + mixer_setrecsrc(m, SOUND_MASK_MIC); + + d->mixer = m; + + return 0; + +bad: kobj_delete((kobj_t)m, M_MIXER); + return -1; +} + +int mixer_uninit(device_t dev) { int i; - snddev_info *d = device_get_softc(dev); - if (d == NULL) return -1; + snddev_info *d; + snd_mixer *m; + + d = device_get_softc(dev); + m = d->mixer; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - mixer_set(d, i, 0); - mixer_setrecsrc(d, SOUND_MASK_MIC); - if (d->mixer.uninit != NULL) d->mixer.uninit(&d->mixer); + mixer_set(m, i, 0); + + mixer_setrecsrc(m, SOUND_MASK_MIC); + + MIXER_UNINIT(m); + + kobj_delete((kobj_t)m, M_MIXER); + d->mixer = NULL; + return 0; } int mixer_reinit(device_t dev) { int i; - snddev_info *d = device_get_softc(dev); - if (d == NULL) return -1; - if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) { - for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) - mixer_set(d, i, d->mixer.level[i]); - mixer_setrecsrc(d, d->mixer.recsrc); - return 0; - } else return -1; + snddev_info *d; + snd_mixer *m; + + d = device_get_softc(dev); + m = d->mixer; + + i = MIXER_REINIT(m); + if (i) + return i; + + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) + mixer_set(m, i, m->level[i]); + + mixer_setrecsrc(m, m->recsrc); + + return 0; } int mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg) { int ret, *arg_i = (int *)arg; + int v = -1, j = cmd & 0xff; + snd_mixer *m; - if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { - int j = cmd & 0xff; + m = d->mixer; - if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(d, *arg_i); - else ret = mixer_set(d, j, *arg_i); + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + if (j == SOUND_MIXER_RECSRC) + ret = mixer_setrecsrc(m, *arg_i); + else + ret = mixer_set(m, j, *arg_i); return (ret == 0)? 0 : ENXIO; } if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { - int v = -1, j = cmd & 0xff; - switch (j) { case SOUND_MIXER_DEVMASK: case SOUND_MIXER_CAPS: case SOUND_MIXER_STEREODEVS: - v = d->mixer.devs; + v = mix_getdevs(m); break; case SOUND_MIXER_RECMASK: - v = d->mixer.recdevs; + v = mix_getrecdevs(m); break; case SOUND_MIXER_RECSRC: - v = mixer_getrecsrc(d); + v = mixer_getrecsrc(m); break; default: - v = mixer_get(d, j); + v = mixer_get(m, j); } *arg_i = v; return (v != -1)? 0 : ENXIO; } return ENXIO; } -int -mixer_busy(snddev_info *d, int busy) +static int +sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) { - if (d == NULL) return -1; - d->mixer.busy = busy; - return 0; + char devname[32]; + int error, dev; + snd_mixer *m; + + m = oidp->oid_arg1; + strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); + error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); + if (error == 0 && req->newptr != NULL) { + dev = mixer_lookup(devname); + if (dev == -1) + return EINVAL; + else if (dev != m->hwvol_mixer) { + m->hwvol_mixer = dev; + m->hwvol_muted = 0; + } + } + return error; } int -mixer_isbusy(snddev_info *d) +mixer_hwvol_init(device_t dev) { - if (d == NULL) return -1; - return d->mixer.busy; -} + snddev_info *d; + snd_mixer *m; -void -mix_setdevs(snd_mixer *m, u_int32_t v) -{ - m->devs = v; + d = device_get_softc(dev); + m = d->mixer; + m->hwvol_mixer = SOUND_MIXER_VOLUME; + m->hwvol_step = 5; + SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), + OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); + SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top), + OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, + sysctl_hw_snd_hwvol_mixer, "A", "") + return 0; } void -mix_setrecdevs(snd_mixer *m, u_int32_t v) +mixer_hwvol_mute(device_t dev) { - m->recdevs = v; -} + snddev_info *d; + snd_mixer *m; -u_int32_t -mix_getdevs(snd_mixer *m) -{ - return m->devs; + d = device_get_softc(dev); + m = d->mixer; + if (m->hwvol_muted) { + m->hwvol_muted = 0; + mixer_set(m, m->hwvol_mixer, m->hwvol_mute_level); + } else { + m->hwvol_muted++; + m->hwvol_mute_level = mixer_get(m, m->hwvol_mixer); + mixer_set(m, m->hwvol_mixer, 0); + } } -u_int32_t -mix_getrecdevs(snd_mixer *m) +void +mixer_hwvol_step(device_t dev, int left_step, int right_step) { - return m->recdevs; -} + snddev_info *d; + snd_mixer *m; + int level, left, right; -void * -mix_getdevinfo(snd_mixer *m) -{ - return m->devinfo; + d = device_get_softc(dev); + m = d->mixer; + if (m->hwvol_muted) { + m->hwvol_muted = 0; + level = m->hwvol_mute_level; + } else + level = mixer_get(m, m->hwvol_mixer); + if (level != -1) { + left = level & 0xff; + right = level >> 8; + left += left_step * m->hwvol_step; + if (left < 0) + left = 0; + right += right_step * m->hwvol_step; + if (right < 0) + right = 0; + mixer_set(m, m->hwvol_mixer, left | right << 8); + } } /* * The various mixers use a variety of bitmasks etc. The Voxware * driver had a very nice technique to describe a mixer and interface * to it. A table defines, for each channel, which register, bits, * offset, polarity to use. This procedure creates the new value * using the table and the old value. */ void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval) { u_char mask; int shift; DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x " "r %d p %d bit %d off %d\n", dev, chn, newval, *regval, (*t)[dev][chn].regno, (*t)[dev][chn].polarity, (*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) ); if ( (*t)[dev][chn].polarity == 1) /* reverse */ newval = 100 - newval ; mask = (1 << (*t)[dev][chn].nbits) - 1; newval = (int) ((newval * mask) + 50) / 100; /* Scale it */ shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/; *regval &= ~(mask << shift); /* Filter out the previous value */ *regval |= (newval & mask) << shift; /* Set the new value */ } Index: stable/4/sys/dev/sound/pcm/mixer.h =================================================================== --- stable/4/sys/dev/sound/pcm/mixer.h (revision 71948) +++ stable/4/sys/dev/sound/pcm/mixer.h (revision 71949) @@ -1,42 +1,48 @@ /* * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ -extern int mixer_init(device_t dev, snd_mixer *m, void *devinfo); +extern int mixer_init(device_t dev, kobj_class_t cls, void *devinfo); extern int mixer_uninit(device_t dev); extern int mixer_reinit(device_t dev); extern int mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg); -extern int mixer_busy(snddev_info *d, int busy); -extern int mixer_isbusy(snddev_info *d); +extern int mixer_busy(snd_mixer *m, int busy); +extern int mixer_isbusy(snd_mixer *m); +int mixer_hwvol_init(device_t dev); +void mixer_hwvol_mute(device_t dev); +void mixer_hwvol_step(device_t dev, int left_step, int right_step); + extern void change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval); void mix_setdevs(snd_mixer *m, u_int32_t v); void mix_setrecdevs(snd_mixer *m, u_int32_t v); u_int32_t mix_getdevs(snd_mixer *m); u_int32_t mix_getrecdevs(snd_mixer *m); void *mix_getdevinfo(snd_mixer *m); + +#define MIXER_DECLARE(name) DEFINE_CLASS(name, name ## _methods, sizeof(snd_mixer)) Index: stable/4/sys/dev/sound/pcm/mixer_if.m =================================================================== --- stable/4/sys/dev/sound/pcm/mixer_if.m (revision 71948) +++ stable/4/sys/dev/sound/pcm/mixer_if.m (revision 71949) @@ -1,68 +1,69 @@ +# KOBJ # # Copyright (c) 2000 Cameron Grant # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # #include INTERFACE mixer; CODE { static int mixer_noreinit(snd_mixer *m) { return 0; } }; METHOD int init { snd_mixer *m; }; METHOD int reinit { snd_mixer *m; } DEFAULT mixer_noreinit; METHOD int uninit { snd_mixer *m; }; METHOD int set { snd_mixer *m; unsigned dev; unsigned left; unsigned right; }; METHOD u_int32_t setrecsrc { snd_mixer *m; u_int32_t src }; Index: stable/4/sys/dev/sound/pcm/sound.c =================================================================== --- stable/4/sys/dev/sound/pcm/sound.c (revision 71948) +++ stable/4/sys/dev/sound/pcm/sound.c (revision 71949) @@ -1,669 +1,684 @@ /* * Copyright (c) 1999 Cameron Grant * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include static dev_t status_dev = 0; static int status_isopen = 0; static int status_init(char *buf, int size); static int status_read(struct uio *buf); static d_open_t sndopen; static d_close_t sndclose; static d_ioctl_t sndioctl; static d_read_t sndread; static d_write_t sndwrite; static d_mmap_t sndmmap; static d_poll_t sndpoll; #define CDEV_MAJOR 30 static struct cdevsw snd_cdevsw = { /* open */ sndopen, /* close */ sndclose, /* read */ sndread, /* write */ sndwrite, /* ioctl */ sndioctl, /* poll */ sndpoll, /* mmap */ sndmmap, /* strategy */ nostrategy, /* name */ "snd", /* maj */ CDEV_MAJOR, /* dump */ nodump, /* psize */ nopsize, /* flags */ 0, /* bmaj */ -1 }; /* PROPOSAL: each unit needs: status, mixer, dsp, dspW, audio, sequencer, midi-in, seq2, sndproc = 9 devices dspW and audio are deprecated. dsp needs min 64 channels, will give it 256 minor = (unit << 20) + (dev << 16) + channel currently minor = (channel << 16) + (unit << 4) + dev nomenclature: /dev/pcmX/dsp.(0..255) /dev/pcmX/dspW /dev/pcmX/audio /dev/pcmX/status /dev/pcmX/mixer [etc.] */ #define PCMMINOR(x) (minor(x)) #define PCMCHAN(x) ((PCMMINOR(x) & 0x00ff0000) >> 16) #define PCMUNIT(x) ((PCMMINOR(x) & 0x000000f0) >> 4) #define PCMDEV(x) (PCMMINOR(x) & 0x0000000f) #define PCMMKMINOR(u, d, c) ((((c) & 0xff) << 16) | (((u) & 0x0f) << 4) | ((d) & 0x0f)) static devclass_t pcm_devclass; #if __FreeBSD_version > 500000 #define USING_DEVFS #endif #ifdef USING_DEVFS int snd_unit; -TUNABLE_INT_DECL("hw.sndunit", 0, snd_unit); +TUNABLE_INT_DECL("hw.snd.unit", 0, snd_unit); static void pcm_makelinks(void *dummy) { int unit; dev_t pdev; static dev_t dsp = 0, dspW = 0, audio = 0, mixer = 0; if (pcm_devclass == NULL || devfs_present == 0) return; if (dsp) { destroy_dev(dsp); dsp = 0; } if (dspW) { destroy_dev(dspW); dspW = 0; } if (audio) { destroy_dev(audio); audio = 0; } if (mixer) { destroy_dev(mixer); mixer = 0; } unit = snd_unit; if (unit < 0 || unit > devclass_get_maxunit(pcm_devclass)) return; if (devclass_get_softc(pcm_devclass, unit) == NULL) return; pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, 0)); dsp = make_dev_alias(pdev, "dsp"); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, 0)); dspW = make_dev_alias(pdev, "dspW"); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); audio = make_dev_alias(pdev, "audio"); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); mixer = make_dev_alias(pdev, "mixer"); } +SYSCTL_NODE(_hw, OID_AUTO, snd, CTLFLAG_RD, 0, "Sound driver"); + static int sysctl_hw_sndunit(SYSCTL_HANDLER_ARGS) { int error, unit; unit = snd_unit; error = sysctl_handle_int(oidp, &unit, sizeof(unit), req); if (error == 0 && req->newptr != NULL) { snd_unit = unit; pcm_makelinks(NULL); } return (error); } -SYSCTL_PROC(_hw, OID_AUTO, sndunit, CTLTYPE_INT | CTLFLAG_RW, +SYSCTL_PROC(_hw_snd, OID_AUTO, unit, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_sndunit, "I", ""); #endif int -pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo) +pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) { int unit = device_get_unit(dev), idx; snddev_info *d = device_get_softc(dev); pcm_channel *chns, *ch; char *dirs; + int err; dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); idx = ((dir == PCMDIR_PLAY)? d->playcount++ : d->reccount++); if (chns == NULL) { device_printf(dev, "bad channel add (%s:%d)\n", dirs, idx); return 1; } ch = &chns[idx]; - *ch = *templ; + ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK); ch->parent = d; - if (chn_init(ch, devinfo, dir)) { - device_printf(dev, "chn_init() for (%s:%d) failed\n", dirs, idx); + err = chn_init(ch, devinfo, dir); + if (err) { + device_printf(dev, "chn_init() for (%s:%d) failed: err = %d\n", dirs, idx, err); return 1; } make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount), UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, d->chancount); make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount), UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, d->chancount); make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount), UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, d->chancount); /* XXX SND_DEV_NORESET? */ d->chancount++; #ifdef USING_DEVFS - if (d->chancount == d->maxchans) + if (d->chancount == 1) pcm_makelinks(NULL); #endif return 0; } static int pcm_killchan(device_t dev, int dir) { int unit = device_get_unit(dev), idx; snddev_info *d = device_get_softc(dev); pcm_channel *chns, *ch; char *dirs; dev_t pdev; dirs = ((dir == PCMDIR_PLAY)? "play" : "record"); chns = ((dir == PCMDIR_PLAY)? d->play : d->rec); idx = ((dir == PCMDIR_PLAY)? --d->playcount : --d->reccount); if (chns == NULL || idx < 0) { device_printf(dev, "bad channel kill (%s:%d)\n", dirs, idx); return 1; } ch = &chns[idx]; if (chn_kill(ch)) { device_printf(dev, "chn_kill() for (%s:%d) failed\n", dirs, idx); return 1; } + kobj_delete(ch->methods, M_DEVBUF); + ch->methods = NULL; d->chancount--; pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP, d->chancount)); destroy_dev(pdev); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_DSP16, d->chancount)); destroy_dev(pdev); pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_AUDIO, d->chancount)); destroy_dev(pdev); return 0; } int pcm_setstatus(device_t dev, char *str) { snddev_info *d = device_get_softc(dev); strncpy(d->status, str, SND_STATUSLEN); return 0; } u_int32_t pcm_getflags(device_t dev) { snddev_info *d = device_get_softc(dev); return d->flags; } void pcm_setflags(device_t dev, u_int32_t val) { snddev_info *d = device_get_softc(dev); d->flags = val; } void * pcm_getdevinfo(device_t dev) { snddev_info *d = device_get_softc(dev); return d->devinfo; } -void -pcm_setswap(device_t dev, pcm_swap_t *swap) -{ - snddev_info *d = device_get_softc(dev); - d->swap = swap; -} - /* This is the generic init routine */ int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { int sz, unit = device_get_unit(dev); snddev_info *d = device_get_softc(dev); if (!pcm_devclass) { pcm_devclass = device_get_devclass(dev); status_dev = make_dev(&snd_cdevsw, PCMMKMINOR(0, SND_DEV_STATUS, 0), UID_ROOT, GID_WHEEL, 0444, "sndstat"); } make_dev(&snd_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); d->dev = dev; d->devinfo = devinfo; d->chancount = d->playcount = d->reccount = 0; d->maxchans = numplay + numrec; sz = (numplay + numrec) * sizeof(pcm_channel *); if (sz > 0) { d->aplay = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); if (!d->aplay) goto no; bzero(d->aplay, sz); d->arec = (pcm_channel **)malloc(sz, M_DEVBUF, M_NOWAIT); if (!d->arec) goto no; bzero(d->arec, sz); sz = (numplay + numrec) * sizeof(int); d->ref = (int *)malloc(sz, M_DEVBUF, M_NOWAIT); if (!d->ref) goto no; bzero(d->ref, sz); } if (numplay > 0) { d->play = (pcm_channel *)malloc(numplay * sizeof(pcm_channel), M_DEVBUF, M_NOWAIT); if (!d->play) goto no; bzero(d->play, numplay * sizeof(pcm_channel)); } else d->play = NULL; if (numrec > 0) { d->rec = (pcm_channel *)malloc(numrec * sizeof(pcm_channel), M_DEVBUF, M_NOWAIT); if (!d->rec) goto no; bzero(d->rec, numrec * sizeof(pcm_channel)); } else d->rec = NULL; +#ifdef USING_DEVFS + sysctl_ctx_init(&d->sysctl_tree); + d->sysctl_tree_top = SYSCTL_ADD_NODE(&d->sysctl_tree, + SYSCTL_STATIC_CHILDREN(_hw_snd), OID_AUTO, + device_get_nameunit(dev), CTLFLAG_RD, 0, ""); + if (d->sysctl_tree_top == NULL) { + sysctl_ctx_free(&d->sysctl_tree); + goto no; + } +#endif + if (numplay == 0 || numrec == 0) d->flags |= SD_F_SIMPLEX; fkchan_setup(&d->fakechan); chn_init(&d->fakechan, NULL, 0); d->magic = MAGIC(unit); /* debugging... */ - d->swap = NULL; return 0; no: if (d->aplay) free(d->aplay, M_DEVBUF); if (d->play) free(d->play, M_DEVBUF); if (d->arec) free(d->arec, M_DEVBUF); if (d->rec) free(d->rec, M_DEVBUF); if (d->ref) free(d->ref, M_DEVBUF); return ENXIO; } int pcm_unregister(device_t dev) { int r, i, unit = device_get_unit(dev); snddev_info *d = device_get_softc(dev); dev_t pdev; + sysctl_remove_oid(d->sysctl_tree_top, 1, 1); + d->sysctl_tree_top = NULL; + sysctl_ctx_free(&d->sysctl_tree); + r = 0; for (i = 0; i < d->chancount; i++) if (d->ref[i]) r = EBUSY; if (r) { device_printf(dev, "unregister: channel busy"); return r; } - if (mixer_isbusy(d)) { + if (mixer_isbusy(d->mixer)) { device_printf(dev, "unregister: mixer busy"); return EBUSY; } pdev = makedev(CDEV_MAJOR, PCMMKMINOR(unit, SND_DEV_CTL, 0)); destroy_dev(pdev); mixer_uninit(dev); while (d->playcount > 0) pcm_killchan(dev, PCMDIR_PLAY); while (d->reccount > 0) pcm_killchan(dev, PCMDIR_REC); d->magic = 0; if (d->aplay) free(d->aplay, M_DEVBUF); if (d->play) free(d->play, M_DEVBUF); if (d->arec) free(d->arec, M_DEVBUF); if (d->rec) free(d->rec, M_DEVBUF); if (d->ref) free(d->ref, M_DEVBUF); + fkchan_kill(&d->fakechan); + #ifdef USING_DEVFS pcm_makelinks(NULL); #endif return 0; } /* * a small utility function which, given a device number, returns * a pointer to the associated snddev_info struct, and sets the unit * number. */ static snddev_info * get_snddev_info(dev_t i_dev, int *unit, int *dev, int *chan) { snddev_info *sc; int u, d, c; u = PCMUNIT(i_dev); d = PCMDEV(i_dev); c = PCMCHAN(i_dev); if (u > devclass_get_maxunit(pcm_devclass)) u = -1; if (unit) *unit = u; if (dev) *dev = d; if (chan) *chan = c; if (u < 0) return NULL; sc = devclass_get_softc(pcm_devclass, u); if (sc == NULL || sc->magic == 0) return NULL; switch(d) { case SND_DEV_CTL: /* /dev/mixer handled by pcm */ case SND_DEV_STATUS: /* /dev/sndstat handled by pcm */ case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return sc; case SND_DEV_SEQ: /* XXX when enabled... */ case SND_DEV_SEQ2: case SND_DEV_MIDIN: case SND_DEV_SNDPROC: /* /dev/sndproc handled by pcm */ default: printf("unsupported subdevice %d\n", d); return NULL; } } static int sndopen(dev_t i_dev, int flags, int mode, struct proc *p) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("open snd%d subdev %d flags 0x%08x mode 0x%08x\n", unit, dev, flags, mode)); switch(dev) { case SND_DEV_STATUS: if (status_isopen) return EBUSY; status_isopen = 1; return 0; case SND_DEV_CTL: - return d? mixer_busy(d, 1) : ENXIO; + return d? mixer_busy(d->mixer, 1) : ENXIO; case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_NORESET: return d? dsp_open(d, chan, flags, dev) : ENXIO; default: return ENXIO; } } static int sndclose(dev_t i_dev, int flags, int mode, struct proc *p) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("close snd%d subdev %d\n", unit, dev)); switch(dev) { /* only those for which close makes sense */ case SND_DEV_STATUS: if (!status_isopen) return EBADF; status_isopen = 0; return 0; case SND_DEV_CTL: - return d? mixer_busy(d, 0) : ENXIO; + return d? mixer_busy(d->mixer, 0) : ENXIO; case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return d? dsp_close(d, chan, dev) : ENXIO; default: return ENXIO; } } static int sndread(dev_t i_dev, struct uio *buf, int flag) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("read snd%d subdev %d flag 0x%08x\n", unit, dev, flag)); switch(dev) { case SND_DEV_STATUS: return status_isopen? status_read(buf) : EBADF; case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return d? dsp_read(d, chan, buf, flag) : EBADF; default: return ENXIO; } } static int sndwrite(dev_t i_dev, struct uio *buf, int flag) { int dev, unit, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("write snd%d subdev %d flag 0x%08x\n", unit, dev & 0xf, flag)); switch(dev) { /* only writeable devices */ case SND_DEV_DSP: case SND_DEV_DSP16: case SND_DEV_AUDIO: return d? dsp_write(d, chan, buf, flag) : EBADF; default: return EPERM; /* for non-writeable devices ; */ } } static int sndioctl(dev_t i_dev, u_long cmd, caddr_t arg, int mode, struct proc * p) { int dev, chan; snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); if (d == NULL) return ENXIO; switch(dev) { case SND_DEV_CTL: return mixer_ioctl(d, cmd, arg); case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: if (IOCGROUP(cmd) == 'M') return mixer_ioctl(d, cmd, arg); else return dsp_ioctl(d, chan, cmd, arg); default: return ENXIO; } } static int sndpoll(dev_t i_dev, int events, struct proc *p) { int dev, chan; snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan); DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events)); if (d == NULL) return ENXIO; switch(dev) { case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return dsp_poll(d, chan, events, p); default: return (events & (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)) | POLLHUP; } } /* * The mmap interface allows access to the play and read buffer, * plus the device descriptor. * The various blocks are accessible at the following offsets: * * 0x00000000 ( 0 ) : write buffer ; * 0x01000000 (16 MB) : read buffer ; * 0x02000000 (32 MB) : device descriptor (dangerous!) * * WARNING: the mmap routines assume memory areas are aligned. This * is true (probably) for the dma buffers, but likely false for the * device descriptor. As a consequence, we do not know where it is * located in the requested area. */ static int sndmmap(dev_t i_dev, vm_offset_t offset, int nprot) { int unit, dev, chan; snddev_info *d = get_snddev_info(i_dev, &unit, &dev, &chan); DEB(printf("sndmmap d 0x%p dev 0x%04x ofs 0x%08x nprot 0x%08x\n", d, dev, offset, nprot)); if (d == NULL || nprot & PROT_EXEC) return -1; /* forbidden */ switch(dev) { case SND_DEV_AUDIO: case SND_DEV_DSP: case SND_DEV_DSP16: return dsp_mmap(d, chan, offset, nprot); default: return -1; } } static int status_init(char *buf, int size) { int i; device_t dev; snddev_info *d; snprintf(buf, size, "FreeBSD Audio Driver (newpcm) %s %s\n" "Installed devices:\n", __DATE__, __TIME__); for (i = 0; i <= devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (!d) continue; dev = devclass_get_device(pcm_devclass, i); if (1) { snprintf(buf + strlen(buf), size - strlen(buf), "pcm%d: <%s> %s", i, device_get_desc(dev), d->status); if (d->chancount > 0) snprintf(buf + strlen(buf), size - strlen(buf), " (%dp/%dr channels%s)\n", d->playcount, d->reccount, (!(d->flags & SD_F_SIMPLEX))? " duplex" : ""); else snprintf(buf + strlen(buf), size - strlen(buf), " (mixer only)\n"); } } return strlen(buf); } static int status_read(struct uio *buf) { static char status_buf[4096]; static int bufptr = 0, buflen = 0; int l; if (status_isopen == 1) { status_isopen++; bufptr = 0; buflen = status_init(status_buf, sizeof status_buf); } l = min(buf->uio_resid, buflen - bufptr); bufptr += l; return (l > 0)? uiomove(status_buf + bufptr - l, l, buf) : 0; } static int sndpcm_modevent(module_t mod, int type, void *data) { switch (type) { case MOD_LOAD: break; case MOD_UNLOAD: if (status_isopen) return EBUSY; if (status_dev) destroy_dev(status_dev); status_dev = 0; break; default: break; } return 0; } static moduledata_t sndpcm_mod = { "snd_pcm", sndpcm_modevent, NULL }; DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); MODULE_VERSION(snd_pcm, PCM_MODVER); Index: stable/4/sys/dev/sound/pcm/sound.h =================================================================== --- stable/4/sys/dev/sound/pcm/sound.h (revision 71948) +++ stable/4/sys/dev/sound/pcm/sound.h (revision 71949) @@ -1,180 +1,186 @@ /* * Copyright (c) 1999 Cameron Grant * Copyright by Hannu Savolainen 1995 * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * first, include kernel header files. */ #ifndef _OS_H_ #define _OS_H_ #ifdef _KERNEL #include #include #include #include #include #include #include #include #include /* for DATA_SET */ #include #include #include #include #include #include #include #include #if __FreeBSD_version > 500000 #include #endif #include #include /* for DELAY */ #include #include #include #include #include #include +#include #include +#include #include #include #else struct isa_device { int dummy; }; #define d_open_t void #define d_close_t void #define d_read_t void #define d_write_t void #define d_ioctl_t void #define d_select_t void #endif /* _KERNEL */ #endif /* _OS_H_ */ #include #include +#include #include #include #include #ifndef ISADMA_WRITE #define ISADMA_WRITE B_WRITE #define ISADMA_READ B_READ #define ISADMA_RAW B_RAW #endif #define PCM_MODVER 1 #define PCM_MINVER 1 #define PCM_PREFVER PCM_MODVER #define PCM_MAXVER 1 #define MAGIC(unit) (0xa4d10de0 + unit) #define SD_F_SIMPLEX 0x00000001 #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 #define SD_F_PRIO_SET (SD_F_PRIO_RD | SD_F_PRIO_WR) #define SD_F_DIR_SET 0x40000000 #define SD_F_TRANSIENT 0xf0000000 /* many variables should be reduced to a range. Here define a macro */ #define RANGE(var, low, high) (var) = \ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) /* make figuring out what a format is easier. got AFMT_STEREO already */ #define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) #define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE) +#define AFMT_8BIT (AFMT_U8 | AFMT_S8) #define AFMT_SIGNED (AFMT_S16_LE | AFMT_S16_BE | AFMT_S8) #define AFMT_BIGENDIAN (AFMT_S16_BE | AFMT_U16_BE) int fkchan_setup(pcm_channel *c); +int fkchan_kill(pcm_channel *c); /* * Minor numbers for the sound driver. * * Unfortunately Creative called the codec chip of SB as a DSP. For this * reason the /dev/dsp is reserved for digitized audio use. There is a * device for true DSP processors but it will be called something else. * In v3.0 it's /dev/sndproc but this could be a temporary solution. */ #define SND_DEV_CTL 0 /* Control port /dev/mixer */ #define SND_DEV_SEQ 1 /* Sequencer /dev/sequencer */ #define SND_DEV_MIDIN 2 /* Raw midi access */ #define SND_DEV_DSP 3 /* Digitized voice /dev/dsp */ #define SND_DEV_AUDIO 4 /* Sparc compatible /dev/audio */ #define SND_DEV_DSP16 5 /* Like /dev/dsp but 16 bits/sample */ #define SND_DEV_STATUS 6 /* /dev/sndstat */ /* #7 not in use now. */ #define SND_DEV_SEQ2 8 /* /dev/sequencer, level 2 interface */ #define SND_DEV_SNDPROC 9 /* /dev/sndproc for programmable devices */ #define SND_DEV_PSS SND_DEV_SNDPROC /* ? */ #define SND_DEV_NORESET 10 #define DSP_DEFAULT_SPEED 8000 #define ON 1 #define OFF 0 #ifdef _KERNEL /* * some macros for debugging purposes * DDB/DEB to enable/disable debugging stuff * BVDDB to enable debugging when bootverbose */ #define DDB(x) x /* XXX */ #define BVDDB(x) if (bootverbose) x #ifndef DEB #define DEB(x) #endif -int pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo); +SYSCTL_DECL(_hw_snd); + +int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo); int pcm_register(device_t dev, void *devinfo, int numplay, int numrec); int pcm_unregister(device_t dev); int pcm_setstatus(device_t dev, char *str); u_int32_t pcm_getflags(device_t dev); void pcm_setflags(device_t dev, u_int32_t val); void *pcm_getdevinfo(device_t dev); -void pcm_setswap(device_t dev, pcm_swap_t *swap); #endif /* _KERNEL */ /* usage of flags in device config entry (config file) */ #define DV_F_DRQ_MASK 0x00000007 /* mask for secondary drq */ #define DV_F_DUAL_DMA 0x00000010 /* set to use secondary dma channel */ /* ought to be made obsolete */ #define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */ #define DV_F_DEV_SHIFT 8 /* force device type/class */