From 558a398b19696474173810daa238fb5df32df27b Mon Sep 17 00:00:00 2001 From: Simon Schubert Date: Thu, 4 Jan 2007 21:47:03 +0000 Subject: [PATCH] Say hello to a sound system update from FreeBSD. This includes the long awaited Intel High Definition Audio (HDA) a.k.a. Azalia support. The generic sound support module has been renamed to sound.ko and the "everything included" module is called snd_driver.ko now. Apart from that, everything should continue working as normal, just better. --- share/man/man4/Makefile | 33 +- share/man/man4/pcm.4 | 460 +- share/man/man4/snd_ad1816.4 | 78 + share/man/man4/snd_als4000.4 | 71 + share/man/man4/snd_atiixp.4 | 100 + share/man/man4/snd_cmi.4 | 77 + share/man/man4/snd_cs4281.4 | 71 + share/man/man4/snd_csa.4 | 99 + share/man/man4/snd_ds1.4 | 73 + share/man/man4/snd_emu10k1.4 | 83 + share/man/man4/snd_emu10kx.4 | 218 + share/man/man4/snd_envy24.4 | 83 + share/man/man4/snd_envy24ht.4 | 100 + share/man/man4/snd_es137x.4 | 118 + share/man/man4/snd_ess.4 | 73 + share/man/man4/snd_fm801.4 | 79 + share/man/man4/snd_gusc.4 | 86 + share/man/man4/snd_hda.4 | 195 + share/man/man4/snd_ich.4 | 106 + share/man/man4/snd_maestro.4 | 79 + share/man/man4/snd_maestro3.4 | 89 + share/man/man4/snd_mss.4 | 113 + share/man/man4/snd_neomagic.4 | 75 + share/man/man4/snd_sbc.4 | 121 + share/man/man4/snd_solo.4 | 67 + share/man/man4/snd_spicds.4 | 88 + share/man/man4/snd_t4dwave.4 | 78 + share/man/man4/snd_uaudio.4 | 109 + share/man/man4/snd_via8233.4 | 105 + share/man/man4/snd_via82c686.4 | 72 + share/man/man4/snd_vibes.4 | 72 + sys/conf/files | 126 +- sys/config/LINT | 27 +- sys/dev/sound/Makefile | 6 +- sys/dev/sound/driver.c | 39 +- sys/dev/sound/driver/Makefile | 17 +- sys/dev/sound/driver/Makefile.inc | 6 +- sys/dev/sound/driver/ad1816/Makefile | 18 +- sys/dev/sound/driver/als4000/Makefile | 13 +- sys/dev/sound/driver/atiixp/Makefile | 12 + sys/dev/sound/driver/au88x0/Makefile | 12 + sys/dev/sound/driver/audiocs/Makefile | 13 + sys/dev/sound/driver/cmi/Makefile | 18 +- sys/dev/sound/driver/cs4281/Makefile | 18 +- sys/dev/sound/driver/csa/Makefile | 18 +- sys/dev/sound/driver/driver/Makefile | 37 + sys/dev/sound/driver/ds1/Makefile | 18 +- sys/dev/sound/driver/emu10k1/Makefile | 27 +- sys/dev/sound/driver/es137x/Makefile | 18 +- sys/dev/sound/driver/ess/Makefile | 18 +- sys/dev/sound/driver/fm801/Makefile | 18 +- sys/dev/sound/driver/hda/Makefile | 12 + sys/dev/sound/driver/ich/Makefile | 9 +- sys/dev/sound/driver/maestro/Makefile | 19 +- sys/dev/sound/driver/maestro3/Makefile | 19 +- sys/dev/sound/driver/mss/Makefile | 18 +- sys/dev/sound/driver/neomagic/Makefile | 18 +- sys/dev/sound/driver/sb16/Makefile | 7 +- sys/dev/sound/driver/sb8/Makefile | 7 +- sys/dev/sound/driver/sbc/Makefile | 18 +- sys/dev/sound/driver/solo/Makefile | 18 +- sys/dev/sound/driver/t4dwave/Makefile | 18 +- sys/dev/sound/driver/uaudio/Makefile | 18 +- sys/dev/sound/driver/via8233/Makefile | 18 +- sys/dev/sound/driver/via82c686/Makefile | 18 +- sys/dev/sound/driver/vibes/Makefile | 18 +- sys/dev/sound/isa/ad1816.c | 69 +- sys/dev/sound/isa/ad1816.h | 6 +- sys/dev/sound/isa/es1888.c | 64 - sys/dev/sound/isa/ess.c | 74 +- sys/dev/sound/isa/gusc.c | 34 +- sys/dev/sound/isa/mss.c | 143 +- sys/dev/sound/isa/mss.h | 68 +- sys/dev/sound/isa/sb.h | 5 +- sys/dev/sound/isa/sb16.c | 147 +- sys/dev/sound/isa/sb8.c | 62 +- sys/dev/sound/isa/sbc.c | 106 +- sys/dev/sound/isa/sndbuf_dma.c | 106 + sys/dev/sound/pci/als4000.c | 94 +- sys/dev/sound/pci/als4000.h | 6 +- sys/dev/sound/pci/atiixp.c | 1133 ++++ sys/dev/sound/pci/atiixp.h | 204 + sys/dev/sound/pci/au88x0.c | 734 +++ sys/dev/sound/pci/au88x0.h | 179 + sys/dev/sound/pci/aureal.c | 44 +- sys/dev/sound/pci/aureal.h | 8 +- sys/dev/sound/pci/cmi.c | 93 +- sys/dev/sound/pci/cmireg.h | 6 +- sys/dev/sound/pci/cs4281.c | 33 +- sys/dev/sound/pci/cs4281.h | 6 +- sys/dev/sound/pci/csa.c | 77 +- sys/dev/sound/pci/csapcm.c | 246 +- sys/dev/sound/pci/csareg.h | 22 +- sys/dev/sound/pci/csavar.h | 5 +- sys/dev/sound/pci/ds1-fw.h | 7 +- sys/dev/sound/pci/ds1.c | 87 +- sys/dev/sound/pci/ds1.h | 4 +- sys/dev/sound/pci/emu10k1.c | 475 +- sys/dev/sound/pci/es137x.c | 1189 +++- sys/dev/sound/pci/es137x.h | 17 +- sys/dev/sound/pci/fm801.c | 138 +- sys/dev/sound/pci/gnu/csaimg.h | 7 +- sys/dev/sound/pci/gnu/emu10k1-ac97.h | 294 + .../pci/gnu/{emu10k1.h => emu10k1-alsa.h} | 1024 ++-- sys/dev/sound/pci/gnu/emu10k1.h | 372 +- sys/dev/sound/pci/gnu/maestro3_dsp.h | 6 +- sys/dev/sound/pci/gnu/maestro3_reg.h | 6 +- sys/dev/sound/pci/hda/hda_reg.h | 1227 +++++ sys/dev/sound/pci/hda/hdac.c | 4883 +++++++++++++++++ sys/dev/sound/pci/hda/hdac.h | 70 + sys/dev/sound/pci/hda/hdac_private.h | 333 ++ sys/dev/sound/pci/hda/hdac_reg.h | 267 + sys/dev/sound/pci/ich.c | 397 +- sys/dev/sound/pci/ich.h | 6 +- sys/dev/sound/pci/maestro.c | 1842 +++++-- sys/dev/sound/pci/maestro3.c | 337 +- sys/dev/sound/pci/maestro_reg.h | 63 +- sys/dev/sound/pci/neomagic-coeff.h | 10 +- sys/dev/sound/pci/neomagic.c | 51 +- sys/dev/sound/pci/neomagic.h | 8 +- sys/dev/sound/pci/solo.c | 210 +- sys/dev/sound/pci/t4dwave.c | 49 +- sys/dev/sound/pci/t4dwave.h | 8 +- sys/dev/sound/pci/via8233.c | 708 ++- sys/dev/sound/pci/via8233.h | 39 +- sys/dev/sound/pci/via82c686.c | 106 +- sys/dev/sound/pci/via82c686.h | 4 +- sys/dev/sound/pci/vibes.c | 30 +- sys/dev/sound/pci/vibes.h | 6 +- sys/dev/sound/pcm/Makefile | 40 +- sys/dev/sound/pcm/ac97.c | 193 +- sys/dev/sound/pcm/ac97.h | 8 +- sys/dev/sound/pcm/ac97_if.m | 6 +- sys/dev/sound/pcm/ac97_patch.c | 18 +- sys/dev/sound/pcm/ac97_patch.h | 7 +- sys/dev/sound/pcm/buffer.c | 268 +- sys/dev/sound/pcm/buffer.h | 53 +- sys/dev/sound/pcm/channel.c | 570 +- sys/dev/sound/pcm/channel.h | 26 +- sys/dev/sound/pcm/channel_if.m | 5 +- sys/dev/sound/pcm/dsp.c | 785 +-- sys/dev/sound/pcm/dsp.h | 13 +- sys/dev/sound/pcm/fake.c | 35 +- sys/dev/sound/pcm/feeder.c | 185 +- sys/dev/sound/pcm/feeder.h | 25 +- sys/dev/sound/pcm/feeder_fmt.c | 1290 +++-- sys/dev/sound/pcm/feeder_if.m | 7 +- sys/dev/sound/pcm/feeder_rate.c | 1002 ++-- sys/dev/sound/pcm/feeder_volume.c | 82 + sys/dev/sound/pcm/mixer.c | 166 +- sys/dev/sound/pcm/mixer.h | 10 +- sys/dev/sound/pcm/mixer_if.m | 8 +- sys/dev/sound/pcm/sndstat.c | 146 +- sys/dev/sound/pcm/sound.c | 847 ++- sys/dev/sound/pcm/sound.h | 130 +- sys/dev/sound/pcm/vchan.c | 381 +- sys/dev/sound/pcm/vchan.h | 8 +- sys/dev/sound/snd/Makefile | 17 - sys/dev/sound/usb/Makefile | 28 - sys/dev/sound/usb/uaudio.c | 3647 ++++++++---- sys/dev/sound/usb/uaudio.h | 24 +- sys/dev/sound/usb/uaudio_pcm.c | 186 +- sys/dev/sound/usb/uaudioreg.h | 40 +- sys/sys/bus.h | 37 +- sys/sys/selinfo.h | 4 +- sys/sys/soundcard.h | 62 +- sys/tools/emu10k1-mkalsa.sh | 22 + 167 files changed, 26141 insertions(+), 6279 deletions(-) create mode 100644 share/man/man4/snd_ad1816.4 create mode 100644 share/man/man4/snd_als4000.4 create mode 100644 share/man/man4/snd_atiixp.4 create mode 100644 share/man/man4/snd_cmi.4 create mode 100644 share/man/man4/snd_cs4281.4 create mode 100644 share/man/man4/snd_csa.4 create mode 100644 share/man/man4/snd_ds1.4 create mode 100644 share/man/man4/snd_emu10k1.4 create mode 100644 share/man/man4/snd_emu10kx.4 create mode 100644 share/man/man4/snd_envy24.4 create mode 100644 share/man/man4/snd_envy24ht.4 create mode 100644 share/man/man4/snd_es137x.4 create mode 100644 share/man/man4/snd_ess.4 create mode 100644 share/man/man4/snd_fm801.4 create mode 100644 share/man/man4/snd_gusc.4 create mode 100644 share/man/man4/snd_hda.4 create mode 100644 share/man/man4/snd_ich.4 create mode 100644 share/man/man4/snd_maestro.4 create mode 100644 share/man/man4/snd_maestro3.4 create mode 100644 share/man/man4/snd_mss.4 create mode 100644 share/man/man4/snd_neomagic.4 create mode 100644 share/man/man4/snd_sbc.4 create mode 100644 share/man/man4/snd_solo.4 create mode 100644 share/man/man4/snd_spicds.4 create mode 100644 share/man/man4/snd_t4dwave.4 create mode 100644 share/man/man4/snd_uaudio.4 create mode 100644 share/man/man4/snd_via8233.4 create mode 100644 share/man/man4/snd_via82c686.4 create mode 100644 share/man/man4/snd_vibes.4 create mode 100644 sys/dev/sound/driver/atiixp/Makefile create mode 100644 sys/dev/sound/driver/au88x0/Makefile create mode 100644 sys/dev/sound/driver/audiocs/Makefile create mode 100644 sys/dev/sound/driver/driver/Makefile create mode 100644 sys/dev/sound/driver/hda/Makefile delete mode 100644 sys/dev/sound/isa/es1888.c create mode 100644 sys/dev/sound/isa/sndbuf_dma.c create mode 100644 sys/dev/sound/pci/atiixp.c create mode 100644 sys/dev/sound/pci/atiixp.h create mode 100644 sys/dev/sound/pci/au88x0.c create mode 100644 sys/dev/sound/pci/au88x0.h create mode 100644 sys/dev/sound/pci/gnu/emu10k1-ac97.h copy sys/dev/sound/pci/gnu/{emu10k1.h => emu10k1-alsa.h} (55%) create mode 100644 sys/dev/sound/pci/hda/hda_reg.h create mode 100644 sys/dev/sound/pci/hda/hdac.c create mode 100644 sys/dev/sound/pci/hda/hdac.h create mode 100644 sys/dev/sound/pci/hda/hdac_private.h create mode 100644 sys/dev/sound/pci/hda/hdac_reg.h create mode 100644 sys/dev/sound/pcm/feeder_volume.c delete mode 100644 sys/dev/sound/snd/Makefile delete mode 100644 sys/dev/sound/usb/Makefile create mode 100644 sys/tools/emu10k1-mkalsa.sh diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 9c9b670d03..eff1daf8ad 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -1,6 +1,6 @@ # @(#)Makefile 8.1 (Berkeley) 6/18/93 # $FreeBSD: src/share/man/man4/Makefile,v 1.83.2.66 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/share/man/man4/Makefile,v 1.49 2006/12/14 16:21:34 swildner Exp $ +# $DragonFly: src/share/man/man4/Makefile,v 1.50 2007/01/04 21:47:00 corecode Exp $ MAN= aac.4 \ acpi.4 \ @@ -204,6 +204,35 @@ MAN= aac.4 \ smbus.4 \ smp.4 \ sn.4 \ + snd_ad1816.4 \ + snd_als4000.4 \ + snd_atiixp.4 \ + snd_cmi.4 \ + snd_cs4281.4 \ + snd_csa.4 \ + snd_ds1.4 \ + snd_emu10k1.4 \ + snd_emu10kx.4 \ + snd_envy24.4 \ + snd_envy24ht.4 \ + snd_es137x.4 \ + snd_ess.4 \ + snd_fm801.4 \ + snd_gusc.4 \ + snd_hda.4 \ + snd_ich.4 \ + snd_maestro.4 \ + snd_maestro3.4 \ + snd_mss.4 \ + snd_neomagic.4 \ + snd_sbc.4 \ + snd_solo.4 \ + snd_spicds.4 \ + snd_t4dwave.4 \ + snd_uaudio.4 \ + snd_via8233.4 \ + snd_via82c686.4 \ + snd_vibes.4 \ snp.4 \ spic.4 \ splash.4 \ @@ -284,7 +313,7 @@ MLINKS+=lp.4 plip.4 MLINKS+=mem.4 kmem.4 MLINKS+=netintro.4 networking.4 MLINKS+=pccbb.4 cbb.4 -MLINKS+=pcm.4 snd.4 +MLINKS+=pcm.4 sound.4 MLINKS+=scsi.4 CAM.4 MLINKS+=scsi.4 SCSI.4 MLINKS+=scsi.4 cam.4 diff --git a/share/man/man4/pcm.4 b/share/man/man4/pcm.4 index c266ef54eb..8303bbd04f 100644 --- a/share/man/man4/pcm.4 +++ b/share/man/man4/pcm.4 @@ -23,178 +23,384 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.\" $FreeBSD: src/share/man/man4/pcm.4,v 1.12.2.11 2002/12/12 19:53:32 trhodes Exp $ -.\" $DragonFly: src/share/man/man4/pcm.4,v 1.5 2006/05/26 19:39:39 swildner Exp $ +.\" $FreeBSD: src/share/man/man4/pcm.4,v 1.50 2006/11/29 17:07:02 joel Exp $ +.\" $DragonFly: src/share/man/man4/pcm.4,v 1.6 2007/01/04 21:47:00 corecode Exp $ .\" -.Dd June 3, 1998 -.Dt PCM 4 +.Dd November 29, 2006 +.Dt SOUND 4 .Os .Sh NAME +.Nm sound , .Nm pcm , .Nm snd -.Nd FreeBSD PCM audio device driver +.Nd +.Fx +PCM audio device infrastructure .Sh SYNOPSIS -For a card with bridge driver support, and a PnP card: +To compile this driver into the kernel, place the following line in your +kernel configuration file: +.Bd -ragged -offset indent .Cd "device pcm" +.Ed .Pp -For a card without bridge driver support, and a non-PnP card: +For non-PnP sound cards: +.Bd -literal -offset indent .Cd "device pcm0 at isa? port? irq 5 drq 1 flags 0x15" +.Ed .Sh DESCRIPTION The .Nm -driver provides support for PCM audio play and capture. -This driver -also supports various PCI and WSS/MSS compatible ISA sound cards, and -AC97 mixer. -True full duplex operation is available on most cards. +driver provides support for +.Tn PCM +audio play and capture. +This driver also supports various +.Tn PCI , +.Tn ISA , +.Tn WSS/MSS +compatible +sound cards, AC97 mixer and High Definition Audio. +Once the +.Nm +driver attaches, supported devices provide audio record and +playback channels. +The +.Dx +sound system provides dynamic mixing +.Dq VCHAN +and rate conversion +.Dq soft formats . +True full duplex operation is available on most sound cards. .Pp -If your sound card is supported by a bridge driver, +If the sound card is supported by a bridge driver, the .Nm -driver works -in conjunction with the bridge driver. +driver works in conjunction with the bridge driver. .Pp Apart from the usual parameters, the flags field is used to specify -the secondary DMA channel (generally used for capture in full duplex -cards). Flags are set to 0 for cards not using a secondary DMA +the secondary +.Tn DMA +channel (generally used for capture in full duplex cards). +Flags are set to 0 for cards not using a secondary +.Tn DMA channel, or to 0x10 + C to specify channel C. .Pp -The driver works best with WSS/MSS cards, which have a very clean -architecture and an orthogonal set of features. -They also happen to be -among the cheapest audio cards on the market. -.Pp The driver does its best to recognize the installed hardware and drive -it correctly, so that you don't have to give too many details in the -kernel config files. -For PCI and ISA PnP cards this is actually easy +it correctly so the user is not required to add specific settings to +the kernel config file. +For +.Tn PCI +and +.Tn ISA +.Tn PnP +cards this is actually easy since they identify themselves. -For legacy ISA cards, the driver looks -for MSS cards at addresses 0x530 and 0x604 (obviously, unless overridden -in the kernel config file by specifying an address). -.Sh IOCTL -The driver supports most of the Voxware ioctls(), and most -applications work unmodified (including popular mpeg players and linux -binaries). A few -differences exist (the most important one is the ability to use -memory-mapped access to the audio buffers). As a consequence, some -applications may need to be recompiled with a slightly modified -audio module. +For legacy +.Tn ISA +cards, the driver looks for +.Tn MSS +cards at addresses 0x530 and 0x604 (unless overridden +in the kernel config file ) . +.Ss Boot Variables +In general, the module +.Pa snd_foo +corresponds to +.Cd "device snd_foo" +and can be +loaded by the boot +.Xr loader 8 +via +.Xr loader.conf 5 +or from the command line using the +.Xr kldload 8 +utility. +Options which can be specified in +.Pa /boot/loader.conf +include: +.Bl -tag -width ".Va snd_emu10k1_load" -offset indent +.It Va snd_driver_load +.Pq Dq Li NO +If set to +.Dq Li YES , +this option loads all available drivers. +.It Va snd_emu10k1_load +.Pq Dq Li NO +If set to +.Dq Li YES , +only the SoundBlaster 5.1 driver and dependent modules will be loaded. +.It Va snd_foo_load +.Pq Dq Li NO +If set to +.Dq Li YES , +load driver for card/chipset foo. +.El +.Ss VCHANs +Each device can optionally support more playback channels +than physical hardware provides by using +.Dq virtual channels +or +.Tn VCHANs . +.Tn VCHAN +options can be configured via the +.Xr sysctl 8 +interface but can only be manipulated while the device is inactive. +.Ss Runtime Configuration +There are a number of +.Xr sysctl 8 +variables available. +.Va hw.snd.* +tunables are global settings and +.Va dev.pcm.* +are device specific. +.Bl -tag -width ".Va hw.snd.report_soft_formats" -offset indent +.It Va hw.snd.latency_profile +Define sets of buffering latency conversion tables for the +.Va hw.snd.latency +tunable. +A value of 0 will use a low and aggressive latency profile which can result +in possible underruns if the application cannot keep up with a rapid irq +rate, especially during high workload. +The default value is 1, which is considered a moderate/safe latency profile. +.It Va hw.snd.latency +Configure the buffering latency. +Only affects applications that do not explicitly request +blocksize / fragments. +This tunable provides finer granularity than the +.Va hw.snd.latency_profile +tunable. +Possible values range between 0 (lowest latency) and 10 (highest latency). +.It Va hw.snd.report_soft_formats +Controls the internal format conversion if it is +available transparently to the application software. +When disabled or not available, the application will +only be able to select formats the device natively supports. +.It Va hw.snd.feeder_rate_round +Sample rate rounding threshold, to avoid large prime division at the +cost of accuracy. +All requested sample rates will be rounded to the nearest threshold value. +Possible values range between 0 (disabled) and 500. +Default is 25. +.It Va hw.snd.feeder_rate_max +Maximum allowable sample rate. +.It Va hw.snd.feeder_rate_min +Minimum allowable sample rate. +.It Va hw.snd.verbose +Level of verbosity for the +.Pa /dev/sndstat +device. +Higher values include more output and the highest level, +four, should be used when reporting problems. +Other options include: +.Bl -tag -width 2n +.It 0 +Installed devices and their allocated bus resources. +.It 1 +The number of playback, record, virtual channels, and +flags per device. +.It 2 +Channel information per device including the channel's +current format, speed, and pseudo device statistics such as +buffer overruns and buffer underruns. +.It 3 +File names and versions of the currently loaded sound modules. +.It 4 +Various messages intended for debugging. +.El +.It Va hw.snd.maxautovchans +Global +.Tn VCHAN +setting that only affects devices with only one playback channel available. +The sound system will dynamically create up this many +.Tn VCHANs . +Set to +.Dq 0 +if no +.Tn VCHANS +are desired. +Maximum value is 255. +.It Va hw.snd.default_unit +Default sound card for systems with multiple sound cards. +When using +.Xr devfs 5 , +the default device for +.Pa /dev/dsp . +Equivalent to a symlink from +.Pa /dev/dsp +to +.Pa /dev/dsp Ns Va ${hw.snd.default_unit} . +.It Va dev.pcm.%d.vchans +The current number of +.Tn VCHANs +allocated per device. +This can be set to preallocate a certain number of +.Tn VCHANs . +Setting this value to +.Dq 0 +will disable +.Tn VCHANs +for this device. +.It Va dev.pcm.%d.vchanrate +Sample rate speed for +.Tn VCHAN +mixing. +All playback paths will be converted to this sample rate before the mixing +process begins. +.It Va dev.pcm.%d.vchanformat +Format for +.Tn VCHAN +mixing. +All playback paths will be converted to this format before the mixing +process begins. +.It Va dev.pcm.%d.polling +Experimental polling mode support where the driver operates by querying the +device state on each tick using a +.Xr callout 9 +mechanism. +Disabled by default and currently only available for a few device drivers. +.El +.Ss Recording Channels +On devices that have more than one recording source (ie: mic and line), +there is a corresponding +.Pa /dev/dsp%d.r%d +device. +.Ss Statistics +Channel statistics are only kept while the device is open. +So with situations involving overruns and underruns, consider the output +while the errant application is open and running. +.Ss IOCTL Support +The driver supports most of the +.Tn OSS +.Fn ioctl +functions, and most applications work unmodified. +A few differences exist, while memory mapped playback is +supported natively and in +.Tn Linux +emulation, memory mapped recording is +not due to +.Tn VM +system design. +As a consequence, some applications may need to be recompiled +with a slightly modified audio module. See .In sys/soundcard.h -for a complete list of the supported ioctls. -.Sh SUPPORTED CARDS -Below we include a list of supported codecs/cards. -If your sound card -is not listed here, it may be supported by a bridge driver. -.Bl -tag -width 2m -.It CS4237, CS4236, CS4232, CS4231 (ISA) -All these cards work perfectly in full duplex using the MSS mode. -This chipset is used, among others, on the A/Open AW35 and AW32, on -some Intel motherboards, and (the CS4231) on some non-PnP cards. -.Pp -The CS4232 is reported as buggy in the Voxware documentation but -I am not sure if this is true. -On one of my Intel motherboards, -capture does not work simply because the capture DMA channel is -not wired to the ISA DMA controller. -.It Yamaha OPL-SAx (ISA) -Works perfectly in all modes. -This chip is used in several PnP cards, -but also (in non-PnP mode) on motherboards and laptops (e.g. the -Toshiba Libretto). -.It OPTi931 (ISA) -The chip is buggy, but the driver has many workarounds to make it work -in full duplex because for some time these were the only full duplex -cards I could find. u-law formats uses U8 format internally because of -a bug in the chip. -.It Trident 4DWave DX/NX (PCI) -.It ENSONIQ AudioPCI ES1370/1371 (PCI) -Creative Labs SoundBlaster PCI is supported as well. -.It ESS Solo-1/1E (PCI) -.It NeoMagic 256AV/ZX (PCI) -.El +for a complete list of the supported +.Fn ioctl +functions. .Sh FILES -The following commonly used symbolic links to real device nodes -should be present: +The +.Nm +drivers may create the following +device nodes: .Pp -.Bl -tag -width /dev/sequencer -compact -.It Pa /dev/audio -Sparc-compatible audio device -.It Pa /dev/dsp -Digitized voice device -.It Pa /dev/dspW +.Bl -tag -width ".Pa /dev/audio%d.%d" -compact +.It Pa /dev/audio%d.%d +Sparc-compatible audio device. +.It Pa /dev/dsp%d.%d +Digitized voice device. +.It Pa /dev/dspW%d.%d Like .Pa /dev/dsp , -but 16 bits per sample -.It Pa /dev/midi -Raw midi access device -.It Pa /dev/mixer -Control port mixer device -.It Pa /dev/music -Level 2 sequencer interface -.It Pa /dev/sequencer -Sequencer device -.It Pa /dev/pss -Programmable device interface +but 16 bits per sample. +.It Pa /dev/dsp%d.p%d +Playback channel. +.It Pa /dev/dsp%d.r%d +Record channel. +.It Pa /dev/dsp%d.v%d +Virtual channel. +.It Pa /dev/sndstat +Current +.Nm +status, including all channels and drivers. .El .Pp -Each symbolic link refers to a device node of the same name, -but with a unit number appended. -The unit number for each device matches the unit number of the -device probed at boot time. -Device probe messages can be examined with the +The first number in the device node +represents the unit number of the +.Nm +device. +All +.Nm +devices are listed +in +.Pa /dev/sndstat . +Additional messages are sometimes recorded when the +device is probed and attached, these messages can be viewed with the .Xr dmesg 8 utility. -.Pp -All the appropriate device nodes and symbolic links -for the -.Ql pcm0 -device can be created with the following commands: -.Bd -literal -offset indent -cd /dev; sh MAKEDEV snd0 -.Ed -.Pp -Similarly, the device nodes and symbolic links for the -.Ql pcm1 -device would be created as follows: -.Bd -literal -offset indent -cd /dev; sh MAKEDEV snd1 -.Ed -.Pp -Since the -.Pa /dev/MAKEDEV -utility creates symbolic links that will be used by -many utilities by default, -the device nodes and symbolic links for the -preferred audio device in systems with multiple audio devices -should be created last. -.Sh DIAGNOSTICS AND TROUBLESHOOTING -.Bl -tag -width 2m -.It ac97: dac not ready -AC97 codec is not likely to be accompanied with the sound card. +.Sh DIAGNOSTICS +.Bl -diag +.It pcm%d:play:%d:dsp%d.p%d: play interrupt timeout, channel dead +The hardware does not generate interrupts to serve incoming (play) +or outgoing (record) data. .It unsupported subdevice XX A device node is not created properly. .El .Sh SEE ALSO -.Xr csa 4 , -.Xr gusc 4 , -.Xr sbc 4 +.Xr snd_ad1816 4 , +.Xr snd_als4000 4 , +.Xr snd_atiixp 4 , +.Xr snd_audiocs 4 , +.Xr snd_cmi 4 , +.Xr snd_cs4281 4 , +.Xr snd_csa 4 , +.Xr snd_ds1 4 , +.Xr snd_emu10k1 4 , +.Xr snd_emu10kx 4 , +.Xr snd_envy24 4 , +.Xr snd_envy24ht 4 , +.Xr snd_es137x 4 , +.Xr snd_ess 4 , +.Xr snd_fm801 4 , +.Xr snd_gusc 4 , +.Xr snd_hda 4 , +.Xr snd_ich 4 , +.Xr snd_maestro 4 , +.Xr snd_maestro3 4 , +.Xr snd_mss 4 , +.Xr snd_neomagic 4 , +.Xr snd_sbc 4 , +.Xr snd_solo 4 , +.Xr snd_spicds 4 , +.Xr snd_t4dwave 4 , +.Xr snd_uaudio 4 , +.Xr snd_via8233 4 , +.Xr snd_via82c686 4 , +.Xr snd_vibes 4 , +.Xr devfs 5 , +.Xr loader.conf 5 , +.Xr dmesg 8 , +.Xr kldload 8 , +.Xr sysctl 8 +.Rs +.%T "The OSS API" +.%O "http://www.opensound.com/pguide/oss.pdf" +.Re .Sh HISTORY The .Nm device driver first appeared in -.Fx 2.2.6 , +.Fx 2.2.6 +as +.Nm pcm , +written by +.An Luigi Rizzo . +It was later rewritten in -.Fx 4.0 . +.Fx 4.0 +by +.An Cameron Grant . +The API evolved from the VOXWARE +standard which later became OSS standard. .Sh AUTHORS +.An -nosplit .An Luigi Rizzo Aq luigi@iet.unipi.it initially wrote the -.Nm +.Nm pcm device driver and this manual page. .An Cameron Grant Aq gandalf@vilnya.demon.co.uk -totally revised the device driver. +later revised the device driver for +.Fx 4.0 . .An Seigo Tanimura Aq tanimura@r.dl.itc.u-tokyo.ac.jp revised this manual page. +It was then rewritten for +.Fx 5.2 . .Sh BUGS -Some features of your cards (e.g. global volume control) might not +Some features of your sound card (e.g., global volume control) might not be supported on all devices. diff --git a/share/man/man4/snd_ad1816.4 b/share/man/man4/snd_ad1816.4 new file mode 100644 index 0000000000..72802c2714 --- /dev/null +++ b/share/man/man4/snd_ad1816.4 @@ -0,0 +1,78 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_ad1816.4,v 1.6 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_ad1816.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_AD1816 4 +.Os +.Sh NAME +.Nm snd_ad1816 +.Nd "Analog Devices AD1816 ISA bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_ad1816" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_ad1816_load="YES" +.Ed +.Pp +Non-PnP cards require a setting in the kernel config file: +.Bd -ragged -offset indent +.Cd "device pcm0 at isa? port? irq 5 drq 1 flags 0x15" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the AD1816 sound card. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Analog Devices AD1816 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +.An "Cameron Grant" Aq cg@FreeBSD.org +.An "Luigi Rizzo" Aq luigi@FreeBSD.org +.An "Hannu Savolainen" diff --git a/share/man/man4/snd_als4000.4 b/share/man/man4/snd_als4000.4 new file mode 100644 index 0000000000..4a36009af2 --- /dev/null +++ b/share/man/man4/snd_als4000.4 @@ -0,0 +1,71 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_als4000.4,v 1.7 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_als4000.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_ALS4000 4 +.Os +.Sh NAME +.Nm snd_als4000 +.Nd "Avance Logic ALS4000 PCI bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_als4000" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_als4000_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the ALS4000 sound card. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Avance Logic ALS4000 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.4 . +.Sh AUTHORS +.An "Orion Hodson" Aq oho@acm.org diff --git a/share/man/man4/snd_atiixp.4 b/share/man/man4/snd_atiixp.4 new file mode 100644 index 0000000000..e4247193f7 --- /dev/null +++ b/share/man/man4/snd_atiixp.4 @@ -0,0 +1,100 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_atiixp.4,v 1.4 2006/11/29 17:07:02 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_atiixp.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd November 29, 2006 +.Dt SND_ATIIXP 4 +.Os +.Sh NAME +.Nm snd_atiixp +.Nd "ATI IXP bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_atiixp" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_atiixp_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach to ATI IXP audio devices. +This driver supports 16bit playback and recording, and 32bit native playback +and recording. +.Ss Runtime Configuration +The following +.Xr sysctl 8 +variables are available in addition to those available to all +.Xr sound 4 +devices: +.Bl -tag -width ".Va dev.pcm.%d.polling" -offset indent +.It Va dev.pcm.%d.polling +Experimental polling mode, where the driver operates by querying the device +state on each tick using +.Xr callout 9 . +Polling is disabled by default. +Do not enable it unless you are facing weird interrupt problems or if the +device cannot generate interrupts at all. +.Sh HARDWARE +The +.Nm +driver supports the following audio chipsets: +.Pp +.Bl -bullet -compact +.It +ATI IXP 200 +.It +ATI IXP 300 +.It +ATI IXP 400 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 6.1 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . +.Sh BUGS +The +.Nm +driver +does not support S/PDIF, but implementing it should be fairly easy if the +right hardware is available. +.Pp +32bit native recording is broken on some hardware. diff --git a/share/man/man4/snd_cmi.4 b/share/man/man4/snd_cmi.4 new file mode 100644 index 0000000000..45264f0512 --- /dev/null +++ b/share/man/man4/snd_cmi.4 @@ -0,0 +1,77 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_cmi.4,v 1.7 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_cmi.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_CMI 4 +.Os +.Sh NAME +.Nm snd_cmi +.Nd "CMedia CMI8338/CMI8738 PCI bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_cmi" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_cmi_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the CMedia CMI8338/CMI8738 audio cards. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +CMedia CMI8338A +.It +CMedia CMI8338B +.It +CMedia CMI8738 +.It +CMedia CMI8738B +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.3 . +.Sh AUTHORS +.An "Orion Hodson" Aq O.Hodson@cs.ucl.ac.uk diff --git a/share/man/man4/snd_cs4281.4 b/share/man/man4/snd_cs4281.4 new file mode 100644 index 0000000000..283184eb10 --- /dev/null +++ b/share/man/man4/snd_cs4281.4 @@ -0,0 +1,71 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_cs4281.4,v 1.6 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_cs4281.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_CS4281 4 +.Os +.Sh NAME +.Nm snd_cs4281 +.Nd "Crystal Semiconductor CS4281 PCI bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_cs4281" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_cs4281_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the CS4281 sound card. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Crystal Semiconductor CS4281 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.3 . +.Sh AUTHORS +.An "Orion Hodson" Aq O.Hodson@cs.ucl.ac.uk diff --git a/share/man/man4/snd_csa.4 b/share/man/man4/snd_csa.4 new file mode 100644 index 0000000000..dee25fdb76 --- /dev/null +++ b/share/man/man4/snd_csa.4 @@ -0,0 +1,99 @@ +.\" +.\" 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: src/share/man/man4/snd_csa.4,v 1.16 2006/06/18 17:53:04 brueffer Exp $ +.\" $DragonFly: src/share/man/man4/snd_csa.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_CSA 4 +.Os +.Sh NAME +.Nm snd_csa +.Nd Crystal Semiconductor CS461x/462x/4280 PCI bridge device driver +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_csa" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_csa_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to Crystal Semiconductor CS461x/462x/4280 based sound cards. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Crystal Semiconductor CS4280 +.It +Crystal Semiconductor CS4610 +.It +Crystal Semiconductor CS4611 +.It +Crystal Semiconductor CS4614 +.It +Crystal Semiconductor CS4615 +.It +Crystal Semiconductor CS4622 +.It +Crystal Semiconductor CS4624 +.It +Crystal Semiconductor CS4630 +.It +Genius Soundmaker 128 Value +.It +Hercules Game Theatre XP +.It +Turtle Beach Santa Cruz +.El +.Pp +Some onboard CS4610 chips are accompanied by the CS423x ISA codec +instead of the CS4297 AC97 codec. +Such configurations are not +supported by the +.Nm +driver yet. +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +.An Seigo Tanimura Aq tanimura@r.dl.itc.u-tokyo.ac.jp diff --git a/share/man/man4/snd_ds1.4 b/share/man/man4/snd_ds1.4 new file mode 100644 index 0000000000..5dab181571 --- /dev/null +++ b/share/man/man4/snd_ds1.4 @@ -0,0 +1,73 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_ds1.4,v 1.6 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_ds1.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_DS1 4 +.Os +.Sh NAME +.Nm snd_ds1 +.Nd "Yamaha DS-1 PCI bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_ds1" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_ds1_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the Yamaha DS-1 sound card. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Yamaha DS-1 +.It +Yamaha DS-1E +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.1 . +.Sh AUTHORS +.An "Cameron Grant" Aq cg@FreeBSD.org diff --git a/share/man/man4/snd_emu10k1.4 b/share/man/man4/snd_emu10k1.4 new file mode 100644 index 0000000000..0dc0be0a8e --- /dev/null +++ b/share/man/man4/snd_emu10k1.4 @@ -0,0 +1,83 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_emu10k1.4,v 1.8 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_emu10k1.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_EMU10K1 4 +.Os +.Sh NAME +.Nm snd_emu10k1 +.Nd "SoundBlaster Live! and Audigy PCI bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_emu10k1" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_emu10k1_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the SoundBlaster Live!\& and Audigy audio cards. +.Pp +Digital output is supported by default. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Creative SoundBlaster Live!\& (EMU10K1 Chipset) +.It +Creative SoundBlaster Audigy (EMU10K2 Chipset) +.It +Creative SoundBlaster Audigy 2 (EMU10K2 Chipset) +.It +Creative SoundBlaster Audigy 2 (EMU10K3 Chipset) +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.1 . +.Sh AUTHORS +.An "David O'Brien" Aq obrien@FreeBSD.org +.An "Orlando Bassotto" Aq orlando.bassotto@ieo-research.it +.An "Cameron Grant" Aq cg@FreeBSD.org +.Sh BUGS +Fancy features like DD5.1 output are not supported. diff --git a/share/man/man4/snd_emu10kx.4 b/share/man/man4/snd_emu10kx.4 new file mode 100644 index 0000000000..71263fb016 --- /dev/null +++ b/share/man/man4/snd_emu10kx.4 @@ -0,0 +1,218 @@ +.\" +.\" Copyright (c) 2003,2006 Yuriy Tsibizov, +.\" 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: snd_emu10kx.4,v 1.19 2006/06/07 11:18:57 chibis Exp $ +.\" $FreeBSD: src/share/man/man4/snd_emu10kx.4,v 1.4 2006/12/14 16:40:57 mpp Exp $ +.\" $DragonFly: src/share/man/man4/Attic/snd_emu10kx.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd July 15, 2006 +.Dt SND_EMU10KX 4 +.Os +.Sh NAME +.Nm snd_emu10kx +.Nd Creative SoundBlaster Live! and Audigy sound cards device driver +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_emu10kx" +.Pp +For additional options: +.Cd "options EMU10KX_MULTICHANNEL" +.Cd "options EMU10KX_DEBUG" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_emu10kx_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the Creative sound cards based on EMU10K1, CA0100, CA0101, CA0102 +and CA0108 DSPs. +.Pp +The +.Nm +sound cards have a PCM part, which is accessible through one to five +.Xr pcm 4 +devices (see +.Sx MULTICHANNEL PLAYBACK +for details), and MPU401-compatible MIDI I/O controller, which is accessible +through the midi device. +Wave table synthesizer is not supported. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Creative Sound Blaster Live!\& (EMU10K1 Chipset). +Both PCM and MIDI interfaces are available. +.It +Creative Sound Blaster Audigy (CA0100 and CA0101 Chipset). +PCM and two MIDI interfaces available. +.It +Creative Sound Blaster Audigy 2 and Creative Sound Blaster Audigy 4 (CA0102 +Chipset). +PCM support is limited to 48kHz/16 bit stereo (192kHz/24 bit part +of this chipset is not supported). +.It +Creative Sound Blaster Audigy 2 Value (CA0108 Chipset). +PCM support is limited +to 48kHz/16 bit stereo (192kHz/24 bit part of this chipset is not supported). +There is no MIDI support for this card. +.El +.Pp +The +.Nm +driver does +.Em not +support the following sound cards (although they are named +similar to some supported ones): +.Pp +.Bl -bullet -compact +.It +Creative Sound Blaster Live!\& 24-Bit, identified by +.Fx +as +.Qq Li "emu10k1x Soundblaster Live! 5.1" . +.It +Creative Sound Blaster Audigy LS / ES, identified by +.Fx +as +.Qq Li "CA0106-DAT Audigy LS" . +.It +All other cards with -DAT chipsets. +.El +.Sh MULTICHANNEL PLAYBACK +It is possible to build this driver with multichannel playback capabilities. +If you enable the +.Dv EMU10KX_MULTICHANNEL +option in your kernel configuration (or +build it as a module) you will get up to five DSP devices, one for each sound +card output. +Only +.Dq FRONT +output can play and record sound from external +sources (like line or S/PDIF inputs). +.Sh OSS MIXER CONTROLS +These are controls available through the standard OSS programming interface. +You can use +.Xr mixer 8 +to change them. +.Pp +On EMU10K1-based cards the OSS mixer directly controls the AC97 codec on card. +On newer cards the OSS mixer controls some parameters of the AC97 codec and +some DSP-based mixer controls. +.Bl -inset +.It Qq vol +mixer control is overall sound volume. +.It Qq pcm +mixer control is PCM playback volume. +It controls only front output +volume in multichannel mode and all output volume in single channel mode. +.It Qq rec +mixer control acts very different on EMU10K1 and other cards. +On EMU10K1 cards it controls the AC97 codec recording level. +On non-EMU10K1 cards +it controls the amount of AC97 "stereo mix" entering the DSP. +AC97 recording level and AC97 recording source are fixed +on CA0100, CA0101, CA0102 and CA0108 cards. +AC97 recording level are always set to +maximum and recording source is always +.Dq Li "stereo mix" . +.El +.Pp +Other OSS mixer controls do not work. +.Sh PRIVATE DEVICE CONTROLS +You can control most of EMU10Kx operation and configuration parameters through +.Va dev.emu10kx. Ns Aq Ar X +sysctls. +These +.Xr sysctl 8 +values are temporary and should not be relied +upon. +.Sh DRIVER CONFIGURATION +.Ss Kernel Configuration Options +The following kernel configuration options control the +.Nm +driver. +.Bl -tag -width ".Dv EMU10KX_MULTICHANNEL" +.It Dv EMU10KX_MULTICHANNEL +This option enables +.Sx MULTICHANNEL PLAYBACK +for all instances of the +.Nm +driver. +.It Dv EMU10KX_DEBUG +This option enables additional debug messages. +.El +.Sh FILES +.Bl -tag -width ".Pa /dev/emu10kx?" -compact +.It Pa /dev/emu10kx? +.Nm +management interface +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The PCM part of the driver is based on the +.Xr snd_emu10k1 4 +SB Live!\& driver by +.An "Cameron Grant" . +The MIDI interface is based on the +.Xr snd_emu10k1 4 +MIDI interface code by +.An "Mathew Kanner" . +The +.Nm +device driver and this manual page were written by +.An Yuriy Tsibizov . +.Sh BUGS +8kHz/8bit/mono recording does not work. +8kHz recording was removed from the driver capabilities. +.Pp +The driver does not detect lost S/PDIF signal and produces noise when S/PDIF +is not connected and S/PDIF volume is not zero. +.Pp +The PCM driver cannot detect the presence of Live!Drive or AudigyDrive +breakout boxes +and tries to use them (and list their connectors in the mixer). +.Pp +The MIDI driver cannot detect the presence of Live!Drive or AudigyDrive +breakout boxes and tries to enable the IR receiver on them anyway. diff --git a/share/man/man4/snd_envy24.4 b/share/man/man4/snd_envy24.4 new file mode 100644 index 0000000000..88fdd75c05 --- /dev/null +++ b/share/man/man4/snd_envy24.4 @@ -0,0 +1,83 @@ +.\" Copyright (c) 2006 Alexander Leidinger +.\" 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: src/share/man/man4/snd_envy24.4,v 1.3 2006/09/30 17:19:22 netchild Exp $ +.\" $DragonFly: src/share/man/man4/snd_envy24.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd September 30, 2006 +.Dt SND_ENVY24 4 +.Os +.Sh NAME +.Nm snd_envy24 +.Nd "VIA Envy24 and compatible bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_envy24" +.Cd "device snd_spicds" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_envy24_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to VIA Envy24 (ICE1724 or VT1724 chipset) and compatible audio +devices. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +M-Audio Audiophile 2496 +.It +M-Audio Delta Dio 2496 +.It +Terratec DMX 6fire +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Katsurajima Naoto . +This manual page was written by +.An Alexander Leidinger Aq netchild@FreeBSD.org . diff --git a/share/man/man4/snd_envy24ht.4 b/share/man/man4/snd_envy24ht.4 new file mode 100644 index 0000000000..2687169e33 --- /dev/null +++ b/share/man/man4/snd_envy24ht.4 @@ -0,0 +1,100 @@ +.\" Copyright (c) 2006 Alexander Leidinger +.\" 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: src/share/man/man4/snd_envy24ht.4,v 1.1 2006/09/30 18:04:57 netchild Exp $ +.\" $DragonFly: src/share/man/man4/snd_envy24ht.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd September 30, 2006 +.Dt SND_ENVY24HT 4 +.Os +.Sh NAME +.Nm snd_envy24ht +.Nd "VIA Envy24HT and compatible bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_envy24ht" +.Cd "device snd_spicds" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_envy24ht_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to VIA Envy24HT (ICE1724 or VT1724 chipset) and compatible audio +devices. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +Audiotrak Prodigy 7.1 +.It +Audiotrak Prodigy 7.1 LT +.It +M-Audio Audiophile 192 +.It +M-Audio Revolution 5.1 +.It +M-Audio Revolution 7.1 +.It +Terratec Aureon 5.1 Sky +.It +Terratec Aureon 7.1 Space +.It +Terratec Aureon 7.1 Universe +.It +Terratec PHASE 22 +.It +Terratec PHASE 28 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Konstantin Dimitrov +based upon the +.Xr snd_envy24 4 +driver. +This manual page was written by +.An Alexander Leidinger Aq netchild@FreeBSD.org . diff --git a/share/man/man4/snd_es137x.4 b/share/man/man4/snd_es137x.4 new file mode 100644 index 0000000000..66d1b5f1d1 --- /dev/null +++ b/share/man/man4/snd_es137x.4 @@ -0,0 +1,118 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_es137x.4,v 1.9 2006/11/29 17:07:02 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_es137x.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd November 29, 2006 +.Dt SND_ES137X 4 +.Os +.Sh NAME +.Nm snd_es137x +.Nd "Ensoniq AudioPCI ES137x bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_es137x" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_es137x_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the Ensoniq 137x audio cards. +.Ss Runtime Configuration +The following +.Xr sysctl 8 +variables are available in addition to those available to all +.Xr sound 4 +devices: +.Bl -tag -width ".Va hw.snd.pcm%d.latency_timer" -offset indent +.It Va hw.snd.pcm%d.latency_timer +Controls the PCI latency timer setting. +Increasing this value will solve most popping and crackling issues +(especially on VIA motherboards). +.It Va hw.snd.pcm%d.spdif_enabled +Enables S/PDIF output on the primary playback channel. +This +.Xr sysctl 8 +variable is available only if the device is known to support S/PDIF output. +.It Va dev.pcm.%d.polling +Experimental polling mode, where the driver operates by querying the device +state on each tick using +.Xr callout 9 . +Polling is disabled by default. +Do not enable it unless you are facing weird interrupt problems or if the +device cannot generate interrupts at all. +.El +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Creative CT5880-A +.It +Creative CT5880-C +.It +Creative CT5880-D +.It +Creative CT5880-E +.It +Creative SB AudioPCI CT4730 +.It +Ensoniq AudioPCI ES1370 +.It +Ensoniq AudioPCI ES1371-A +.It +Ensoniq AudioPCI ES1371-B +.It +Ensoniq AudioPCI ES1373-A +.It +Ensoniq AudioPCI ES1373-B +.It +Ensoniq AudioPCI ES1373-8 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +.An "Russell Cattelan" Aq cattelan@thebarn.com +.An "Cameron Grant" Aq cg@FreeBSD.org +.An "Joachim Kuebart" +.An "Jonathan Noack" Aq noackjr@alumni.rice.edu diff --git a/share/man/man4/snd_ess.4 b/share/man/man4/snd_ess.4 new file mode 100644 index 0000000000..3ecebd69e0 --- /dev/null +++ b/share/man/man4/snd_ess.4 @@ -0,0 +1,73 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_ess.4,v 1.7 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_ess.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_ESS 4 +.Os +.Sh NAME +.Nm snd_ess +.Nd "Ensoniq ESS ISA PnP/non-PnP bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_ess" +.Cd "device snd_sbc" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_ess_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the ESS ISA sound cards. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Ensoniq ESS ISA PnP/non-PnP +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.1 . +.Sh AUTHORS +.An "Cameron Grant" Aq cg@FreeBSD.org +.An "Luigi Rizzo" Aq luigi@FreeBSD.org diff --git a/share/man/man4/snd_fm801.4 b/share/man/man4/snd_fm801.4 new file mode 100644 index 0000000000..19ad3d4ba9 --- /dev/null +++ b/share/man/man4/snd_fm801.4 @@ -0,0 +1,79 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_fm801.4,v 1.3 2005/12/01 12:58:50 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_fm801.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 1, 2005 +.Dt SND_FM801 4 +.Os +.Sh NAME +.Nm snd_fm801 +.Nd "Forte Media FM801 bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_fm801" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_fm801_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach audio devices based on the Forte Media FM801 chipset. +This is a common chipset found in various parts used by OEM manufacturers. +.Sh HARDWARE +The +.Nm +driver supports audio devices based on the following chipset: +.Pp +.Bl -bullet -compact +.It +Forte Media FM801 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.2 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . +.Sh BUGS +The Forte Media FM801 chipset is a sort of PCI bridge, not an actual +sound controller, making it possible to have soundless support. +One problem is that both chipsets, with and without sound support, use the +same PCI ID. +This makes it impossible to determine which one is installed. diff --git a/share/man/man4/snd_gusc.4 b/share/man/man4/snd_gusc.4 new file mode 100644 index 0000000000..530473069b --- /dev/null +++ b/share/man/man4/snd_gusc.4 @@ -0,0 +1,86 @@ +.\" +.\" 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: src/share/man/man4/snd_gusc.4,v 1.14 2006/06/18 17:53:04 brueffer Exp $ +.\" $DragonFly: src/share/man/man4/snd_gusc.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_GUSC 4 +.Os +.Sh NAME +.Nm snd_gusc +.Nd Gravis UltraSound ISA bridge device driver +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_gusc" +.Ed +.Pp +Non-PnP cards require a setting in the kernel config file: +.Bd -literal -offset indent +.Cd "device pcm0 at isa? port? irq 5 drq 1 flags 0x15" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to Gravis UltraSound sound cards. +.Pp +The value of flags specifies the secondary DMA channel. +If the secondary +DMA channel is C, set the flags to (C | 0x10). +For a sound card without the +secondary DMA channel, the flags should be set to zero. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Gravis UltraSound MAX +.It +Gravis UltraSound PnP +.El +.Sh DIAGNOSTICS +.Bl -diag +.It xxx: gus pcm not attached, out of memory +There are not enough memory to drive the device. +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +.An Ville-Pertti Keinonen Aq will@iki.fi +.An Seigo Tanimura Aq tanimura@r.dl.itc.u-tokyo.ac.jp +.Sh BUGS +Recording pcm sound data is not supported yet. diff --git a/share/man/man4/snd_hda.4 b/share/man/man4/snd_hda.4 new file mode 100644 index 0000000000..7b2de8c768 --- /dev/null +++ b/share/man/man4/snd_hda.4 @@ -0,0 +1,195 @@ +.\" Copyright (c) 2006 Joel Dahl +.\" 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: src/share/man/man4/snd_hda.4,v 1.5 2006/12/17 16:48:26 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_hda.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 17, 2006 +.Dt SND_HDA 4 +.Os +.Sh NAME +.Nm snd_hda +.Nd "Intel High Definition Audio bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_hda" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_hda_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge device driver allows the generic audio driver, +.Xr sound 4 , +to attach to Intel High Definition Audio devices. +The +.Nm +driver supports hardware that conforms with revision 1.0 of the Intel High +Definition Audio specification and tries to behave much like the Microsoft +Universal Audio Architecture (UAA) draft (revision 0.7b) for handling audio +devices. +HDA acts like a primary bus, similar to +.Xr miibus 4 , +for handling various child buses such as audio, modem and HDMI (High Definition +Multimedia Interface). +Only audio is implemented in the +.Nm +driver. +.Pp +The High Definition (HD) Audio specification was developed by Intel as the +logical successor of the old AC'97 specification and has several advantages, +such as higher bandwidth which allows more channels and more detailed formats, +support for several logical audio devices, and general purpose DMA channels. +.Pp +The HDA specification defines the register-level interface, physical link +characteristics, codec programming models, and codec architectural components. +This specification is intended for both device driver developers and hardware +component designers. +.Ss Runtime Configuration +The following +.Xr sysctl 8 +variables are available in addition to those available to all +.Xr sound 4 +devices: +.Bl -tag -width ".Va dev.pcm.%d.polling" -offset indent +.It Va dev.pcm.%d.polling +Experimental polling mode, where the driver operates by querying the device +state on each tick using +.Xr callout 9 . +Polling is disabled by default. +Do not enable it unless you are facing weird interrupt problems or if the +device cannot generate interrupts at all. +.El +.Sh HARDWARE +The +.Nm +driver supports the following audio chipsets: +.Pp +.Bl -bullet -compact +.It +ATI SB450 +.It +ATI SB600 +.It +Intel 631x/632xESB +.It +Intel 82801F +.It +Intel 82801G +.It +Intel 82801H +.It +nVidia MCP51 +.It +nVidia MCP55 +.It +nVidia MCP61A +.It +nVidia MCP61B +.It +nVidia MCP65A +.It +nVidia MCP65B +.It +SiS 966 +.It +VIA VT8251/8237A +.El +.Pp +Generic Audio chipsets compatible with the Intel HDA specification should work, +but have not been verified yet. +The following codecs have been verified to work: +.Pp +.Bl -bullet -compact +.It +Analog Device AD1981HD +.It +Analog Device AD1983 +.It +Analog Device AD1986A +.It +CMedia CMI9880 +.It +Conexant Venice +.It +Conexant Waikiki +.It +Realtek ALC260 +.It +Realtek ALC861 +.It +Realtek ALC880 +.It +Realtek ALC882 +.It +Realtek ALC883 +.It +Realtek ALC888 +.It +Sigmatel STAC9220 +.It +Sigmatel STAC9220D/9223D +.It +Sigmatel STAC9221 +.It +Sigmatel STAC9221D +.It +Sigmatel STAC9227 +.El +.Sh SEE ALSO +.Xr sound 4 , +.Xr loader.conf 5 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Stephane E. Potvin Aq sepotvin@videotron.ca +and +.An Ariff Abdullah Aq ariff@FreeBSD.org . +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . +.Sh BUGS +There are a couple of missing features, such as support for Digital +S/PDIF and multichannel output. +.Pp +A few Hardware/OEM vendors tend to screw up BIOS settings, thus +rendering the +.Nm +driver useless, which usually results in a state where the +.Nm +driver seems to attach and work, but without any sound. diff --git a/share/man/man4/snd_ich.4 b/share/man/man4/snd_ich.4 new file mode 100644 index 0000000000..8362991c08 --- /dev/null +++ b/share/man/man4/snd_ich.4 @@ -0,0 +1,106 @@ +.\" Copyright (c) 2004 Jorge Mario G. Mazo +.\" 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: src/share/man/man4/snd_ich.4,v 1.9 2006/06/18 17:53:04 brueffer Exp $ +.\" $DragonFly: src/share/man/man4/snd_ich.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_ICH 4 +.Os +.Sh NAME +.Nm snd_ich +.Nd "Intel ICH PCI and compatible bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_ich" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_ich_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to Intel ICH and compatible audio devices. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +AMD 768 +.It +AMD 8111 +.It +Intel 443MX +.It +Intel ICH +.It +Intel ICH revision 1 +.It +Intel ICH2 +.It +Intel ICH3 +.It +Intel ICH4 +.It +Intel ICH5 +.It +Intel ICH6 +.It +Intel ICH7 +.It +NVIDIA nForce +.It +NVIDIA nForce2 +.It +NVIDIA nForce2 400 +.It +NVIDIA nForce3 +.It +NVIDIA nForce3 250 +.It +NVIDIA nForce4 +.It +SiS 7012 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.2 . +.Sh AUTHORS +This manual page was written by +.An Jorge Mario G. Mazo Aq jgutie11@eafit.edu.co . diff --git a/share/man/man4/snd_maestro.4 b/share/man/man4/snd_maestro.4 new file mode 100644 index 0000000000..3f6ac2eb24 --- /dev/null +++ b/share/man/man4/snd_maestro.4 @@ -0,0 +1,79 @@ +.\" Copyright (c) 2004 Jorge Mario G. Mazo +.\" 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: src/share/man/man4/snd_maestro.4,v 1.3 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_maestro.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_MAESTRO 4 +.Os +.Sh NAME +.Nm snd_maestro +.Nd "ESS Maestro bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_maestro" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_maestro_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to ESS Maestro based sound chips. +This chipset is very popular in notebook computers, but it is +also very used in a wide selection of cheap OEM sound cards. +.Sh HARDWARE +The +.Nm +driver supports the following PCI sound cards: +.Pp +.Bl -bullet -compact +.It +ESS Technology Maestro-1 +.It +ESS Technology Maestro-2 +.It +ESS Technology Maestro-2E +.El +.Sh SEE ALSO +.Xr snd_maestro3 4 , +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.2 . +.Sh AUTHORS +This manual page was written by +.An Jorge Mario G. Mazo Aq jgutie11@eafit.edu.co . diff --git a/share/man/man4/snd_maestro3.4 b/share/man/man4/snd_maestro3.4 new file mode 100644 index 0000000000..fb8729dd2b --- /dev/null +++ b/share/man/man4/snd_maestro3.4 @@ -0,0 +1,89 @@ +.\" Copyright (c) 2001 Scott Long +.\" 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: src/share/man/man4/snd_maestro3.4,v 1.9 2006/01/09 12:51:45 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_maestro3.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_MAESTRO3 4 +.Os +.Sh NAME +.Nm snd_maestro3 +.Nd "ESS Maestro3/Allegro-1 bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_maestro3" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_maestro3_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for the ESS Maestro3 and Allegro-1 sound chips +under the PCM framework. +These chips are mostly found in laptop computers and feature an AC97 mixer, +a multi-channel sample rate converter that can mix up to four digital audio +streams in hardware, recording support, and external volume control buttons. +.Pp +The firmware for the sound processor is licensed under the GNU Public +License, and thus this driver is not included in the default +.Pa GENERIC +kernel. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +ESS Technology Allegro-1 +.It +ESS Technology Maestro3 +.El +.Sh DIAGNOSTICS +The hardware volume control buttons can be connected to two different pin +sets (GPIO or GD) on the chip, depending on the manufacturer. +The driver has no way of determining this configuration, so a hint may be +used to override the default guess. +The default setting for hardware volume control assumes that GD pins +are wired to control the hardware volume. +.Sh SEE ALSO +.Xr sound 4 , +.Xr loader.conf 5 +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 4.3 . +.Sh AUTHORS +.An Scott Long Aq scottl@FreeBSD.org +.An Darrel Anderson Aq anderson@cs.duke.edu diff --git a/share/man/man4/snd_mss.4 b/share/man/man4/snd_mss.4 new file mode 100644 index 0000000000..420a4659b8 --- /dev/null +++ b/share/man/man4/snd_mss.4 @@ -0,0 +1,113 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_mss.4,v 1.3 2005/12/01 12:58:50 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_mss.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 1, 2005 +.Dt SND_MSS 4 +.Os +.Sh NAME +.Nm snd_mss +.Nd "Microsoft Sound System ISA PnP/non-PnP bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_mss" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_mss_load="YES" +.Ed +.Pp +Non-PnP cards require a setting in the kernel config file: +.Bd -literal -offset indent +.Cd "device pcm0 at isa? port? irq 5 drq 1 flags 0x15" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach to the supported audio devices. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +AD1845 +.It +AD1848 +.It +Aztech 2320 +.It +CMedia CMI8330 +.It +Crystal Semiconductor CS4231 +.It +Crystal Semiconductor CS4232 +.It +Crystal Semiconductor CS4234 +.It +Crystal Semiconductor CS4235 +.It +Crystal Semiconductor CS4236 +.It +Crystal Semiconductor CS4237 +.It +ENSONIQ SoundscapeVIVO ENS4081 +.It +NeoMagic 256AV (non-AC97) +.It +OPTi 924 +.It +OPTi 925 +.It +OPTi 930 +.It +OPTi 931 +.It +OPTi 933 +.It +Yamaha OPL-SA2 +.It +Yamaha OPL-SA3 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 2.2.6 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . diff --git a/share/man/man4/snd_neomagic.4 b/share/man/man4/snd_neomagic.4 new file mode 100644 index 0000000000..06ed47b7aa --- /dev/null +++ b/share/man/man4/snd_neomagic.4 @@ -0,0 +1,75 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_neomagic.4,v 1.5 2005/12/01 12:58:50 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_neomagic.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 1, 2005 +.Dt SND_NEOMAGIC 4 +.Os +.Sh NAME +.Nm snd_neomagic +.Nd "NeoMagic 256AV/ZX bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_neomagic" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_neomagic_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach to the NeoMagic 256AV/ZX audio devices. +These chips are mostly found in laptop computers. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +NeoMagic 256AV +.It +NeoMagic 256ZX +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . diff --git a/share/man/man4/snd_sbc.4 b/share/man/man4/snd_sbc.4 new file mode 100644 index 0000000000..837f407909 --- /dev/null +++ b/share/man/man4/snd_sbc.4 @@ -0,0 +1,121 @@ +.\" +.\" 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: src/share/man/man4/snd_sbc.4,v 1.17 2006/06/18 17:53:04 brueffer Exp $ +.\" $DragonFly: src/share/man/man4/snd_sbc.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_SBC 4 +.Os +.Sh NAME +.Nm snd_sbc +.Nd Creative Sound Blaster ISA and compatible bridge device driver +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_sbc" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_sbc_load="YES" +.Ed +.Pp +Non-PnP cards require a setting in the kernel config file: +.Bd -literal -offset indent +.Cd "device pcm0 at isa? port? irq 5 drq 1 flags 0x15" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to Creative Sound Blaster ISA compatible audio cards. +.Pp +The value of flags specifies the secondary DMA channel. +If the secondary +DMA channel is C, set the flags to (C | 0x10). +For a sound card without the +secondary DMA channel, the flags should be set to zero. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +Avance Asound 110 +.It +Avance Logic ALS100+ +.It +Avance Logic ALS120 +.It +Creative SB16 +.It +Creative SB32 +.It +Creative AWE64 +.It +Creative AWE64 Gold +.It +Creative ViBRA16C +.It +Creative ViBRA16X +.It +ESS ES1681 +.It +ESS ES1688 +.It +ESS ES1868 +.It +ESS ES1869 +.It +ESS ES1878 +.It +ESS ES1879 +.It +ESS ES1888 +.El +.Sh DIAGNOSTICS +.Bl -diag +.It sb_dspwr(XX) timed out. +A command to the DSP has timed out. +Check the I/O port configuration. +.It bad irq XX (5/7/9/10 valid) +The IRQ given to the driver is not valid. +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +.An Seigo Tanimura Aq tanimura@r.dl.itc.u-tokyo.ac.jp diff --git a/share/man/man4/snd_solo.4 b/share/man/man4/snd_solo.4 new file mode 100644 index 0000000000..0640198d2c --- /dev/null +++ b/share/man/man4/snd_solo.4 @@ -0,0 +1,67 @@ +.\" Copyright (c) 2004 Atte Peltomaki +.\" 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: src/share/man/man4/snd_solo.4,v 1.5 2005/11/28 18:47:00 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_solo.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd November 28, 2005 +.Dt SND_SOLO 4 +.Os +.Sh NAME +.Nm snd_solo +.Nd "ESS Solo-1/1E PCI bridge device driver" +.Sh SYNOPSIS +.Cd "device pcm" +.Cd "device snd_solo" +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver +.Xr sound 4 +to attach to the ESS Solo-1x PCI cards. +.Sh HARDWARE +The +.Nm +driver supports the following sound cards: +.Pp +.Bl -bullet -compact +.It +ESS Solo-1 (ES1938 Chipset) +.It +ESS Solo-1E (ES1946 Chipset) +.El +.Pp +Note that older ESS ISA cards with ES18xx chipset are supported via +.Xr snd_ess 4 +and/or +.Xr snd_sbc 4 . +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.1 . +.Sh AUTHORS +.An "Cameron Grant" Aq cg@FreeBSD.org diff --git a/share/man/man4/snd_spicds.4 b/share/man/man4/snd_spicds.4 new file mode 100644 index 0000000000..eddcd563a4 --- /dev/null +++ b/share/man/man4/snd_spicds.4 @@ -0,0 +1,88 @@ +.\" Copyright (c) 2006 Alexander Leidinger +.\" 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: src/share/man/man4/snd_spicds.4,v 1.2 2006/11/13 08:56:42 ru Exp $ +.\" $DragonFly: src/share/man/man4/snd_spicds.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd September 30, 2006 +.Dt SND_SPICDS 4 +.Os +.Sh NAME +.Nm snd_spicds +.Nd "I2S SPI audio codec driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device snd_spicds" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_spicds_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +I2S codec driver is used by several sound drivers for soundcards which use +the supported codec chips. +.Sh HARDWARE +The +.Nm +driver supports the following codecs: +.Pp +.Bl -bullet -compact +.It +AK4358 +.It +AK4381 +.It +AK4524 +.It +AK4528 +.It +WM8770 +.El +.Sh SEE ALSO +.Xr snd_envy24 4 , +.Xr snd_envy24ht 4 , +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +driver was written by +.An Konstantin Dimitrov +based upon the +.Xr snd_envy24 4 +driver. +This manual page was written by +.An Alexander Leidinger Aq netchild@FreeBSD.org . diff --git a/share/man/man4/snd_t4dwave.4 b/share/man/man4/snd_t4dwave.4 new file mode 100644 index 0000000000..51c5eb0aa7 --- /dev/null +++ b/share/man/man4/snd_t4dwave.4 @@ -0,0 +1,78 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_t4dwave.4,v 1.3 2005/12/01 12:58:50 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_t4dwave.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 1, 2005 +.Dt SND_T4DWAVE 4 +.Os +.Sh NAME +.Nm snd_t4dwave +.Nd "Trident 4DWave bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_t4dwave" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_t4dwave_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach to Trident 4DWave audio devices. +.Sh HARDWARE +The +.Nm +driver supports the following audio devices: +.Pp +.Bl -bullet -compact +.It +Acer Labs M5451 +.It +SIS 7018 +.It +Trident 4DWave DX +.It +Trident 4DWave NX +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.0 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . diff --git a/share/man/man4/snd_uaudio.4 b/share/man/man4/snd_uaudio.4 new file mode 100644 index 0000000000..15f20741d9 --- /dev/null +++ b/share/man/man4/snd_uaudio.4 @@ -0,0 +1,109 @@ +.\" $NetBSD: uaudio.4,v 1.15 2002/02/12 19:53:57 jdolecek Exp $ +.\" +.\" Copyright (c) 1999 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Lennart Augustsson. +.\" +.\" 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: src/share/man/man4/snd_uaudio.4,v 1.6 2005/12/15 20:25:41 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_uaudio.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 15, 2005 +.Dt SND_UAUDIO 4 +.Os +.Sh NAME +.Nm snd_uaudio +.Nd USB audio device driver +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device usb" +.Cd "device snd_uaudio" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_uaudio_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for +.Tn USB +audio class devices. +.Pp +A +.Tn USB +audio device consists of a number of components: +input terminals (e.g.\& USB digital input), output terminals (e.g.\& +speakers), and a number of units in between (e.g.\& volume control). +.Pp +Refer to the +.Ql USB Audio Class Specification +for more information. +.Sh SEE ALSO +.Xr sound 4 , +.Xr usb 4 +.Rs +.%T "USB Audio Class Specifications" +.%O http://www.usb.org/developers/devclass_docs/ +.Re +.Sh HISTORY +The +.Nm +driver first appeared in +.Fx 4.7 . +.Sh AUTHORS +This manual page was adopted from +.Nx 1.6 +and modified for +.Fx +by +.An Hiten Pandya Aq hmp@FreeBSD.org . +.Sh BUGS +The +.Tn PCM +framework in +.Fx , +as of this writing, does not handle device un-registrations in a properly +abstracted manner, i.e., a detach request is refused by the +.Tn PCM +framework if the device is in use. +For +.Tn USB +and supposedly other detach-able busses, it is necessary to allow the +device un-registration to complete successfully, otherwise the driver +leaves wild pointers to invalid data structures and thus leading to a panic. diff --git a/share/man/man4/snd_via8233.4 b/share/man/man4/snd_via8233.4 new file mode 100644 index 0000000000..3fe1b2c1cb --- /dev/null +++ b/share/man/man4/snd_via8233.4 @@ -0,0 +1,105 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_via8233.4,v 1.6 2006/11/29 17:07:02 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_via8233.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd November 29, 2006 +.Dt SND_VIA8233 4 +.Os +.Sh NAME +.Nm snd_via8233 +.Nd "VIA Technologies VT8233 bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_via8233" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_via8233_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach to the VIA VT8233 audio devices. +These audio chipsets are integrated in the southbridge on many VIA based +motherboards. +.Ss Runtime Configuration +The following +.Xr sysctl 8 +variables are available in addition to those available to all +.Xr sound 4 +devices: +.Bl -tag -width ".Va dev.pcm.%d.polling" -offset indent +.It Va dev.pcm.%d.polling +Experimental polling mode, where the driver operates by querying the device +state on each tick using +.Xr callout 9 . +Polling is disabled by default. +Do not enable it unless you are facing weird interrupt problems or if the +device cannot generate interrupts at all. +.Sh HARDWARE +The +.Nm +driver supports the following audio chipsets: +.Pp +.Bl -bullet -compact +.It +VIA VT8233 +.It +VIA VT8233A +.It +VIA VT8233C +.It +VIA VT8235 +.It +VIA VT8237 +.It +VIA VT8251 +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.7 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . +.Sh BUGS +The +.Nm +driver +does not support S/PDIF. +There is partial support in the code, so implementing it should be fairly +easy if the right hardware is available. diff --git a/share/man/man4/snd_via82c686.4 b/share/man/man4/snd_via82c686.4 new file mode 100644 index 0000000000..e0314703e9 --- /dev/null +++ b/share/man/man4/snd_via82c686.4 @@ -0,0 +1,72 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_via82c686.4,v 1.3 2005/12/01 12:58:50 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_via82c686.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 1, 2005 +.Dt SND_VIA82C686 4 +.Os +.Sh NAME +.Nm snd_via82c686 +.Nd "VIA Technologies 82C686A bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_via82c686" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_via82c686_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach audio devices based on the VIA 82C686A chipset. +.Sh HARDWARE +The +.Nm +driver supports audio devices based on the following chipset: +.Pp +.Bl -bullet -compact +.It +VIA 82C686A +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.2 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . diff --git a/share/man/man4/snd_vibes.4 b/share/man/man4/snd_vibes.4 new file mode 100644 index 0000000000..3443ef0b5f --- /dev/null +++ b/share/man/man4/snd_vibes.4 @@ -0,0 +1,72 @@ +.\" Copyright (c) 2005 Joel Dahl +.\" 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: src/share/man/man4/snd_vibes.4,v 1.3 2005/12/01 12:58:51 joel Exp $ +.\" $DragonFly: src/share/man/man4/snd_vibes.4,v 1.1 2007/01/04 21:47:00 corecode Exp $ +.\" +.Dd December 1, 2005 +.Dt SND_VIBES 4 +.Os +.Sh NAME +.Nm snd_vibes +.Nd "S3 SonicVibes bridge device driver" +.Sh SYNOPSIS +To compile this driver into the kernel, place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device pcm" +.Cd "device snd_vibes" +.Ed +.Pp +Alternatively, to load the driver as a module at boot time, place the +following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +snd_vibes_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +bridge driver allows the generic audio driver, +.Xr sound 4 , +to attach audio devices based on the S3 SonicVibes chipset. +.Sh HARDWARE +The +.Nm +driver supports audio devices based on the following chipset: +.Pp +.Bl -bullet -compact +.It +S3 SonicVibes +.El +.Sh SEE ALSO +.Xr sound 4 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 4.4 . +.Sh AUTHORS +This manual page was written by +.An Joel Dahl Aq joel@FreeBSD.org . diff --git a/sys/conf/files b/sys/conf/files index d54e8a944c..fe3d07fb2b 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,5 +1,5 @@ # $FreeBSD: src/sys/conf/files,v 1.340.2.137 2003/06/04 17:10:30 sam Exp $ -# $DragonFly: src/sys/conf/files,v 1.147 2006/12/17 20:07:27 dillon Exp $ +# $DragonFly: src/sys/conf/files,v 1.148 2007/01/04 21:47:00 corecode Exp $ # # The long compile-with and dependency lines are required because of # limitations in config: backslash-newline doesn't work in strings, and @@ -31,6 +31,11 @@ aic79xx_{seq.h,reg.h,reg_print.c} optional ahd pci \ aic79xx_reg_print.o optional ahd pci ahd_reg_pretty_print \ compile-with "${NORMAL_C}" \ no-implicit-rule local +emu10k1-alsa%diked.h optional snd_emu10k1 pci \ + dependency "$S/tools/emu10k1-mkalsa.sh $S/dev/sound/pci/gnu/emu10k1-alsa.h" \ + compile-with "CC=${CC} AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/dev/sound/pci/gnu/emu10k1-alsa.h emu10k1-alsa%diked.h" \ + no-obj no-implicit-rule before-depend \ + clean "emu10k1-alsa%diked.h" kern/device_if.m standard kern/bus_if.m standard bus/cam/cam.c optional scbus @@ -1198,82 +1203,57 @@ dev/misc/orm/orm.c optional isa bus/isa/pnp.c optional isa bus/isa/pnpparse.c optional isa # -dev/sound/isa/ad1816.c optional snd_ad1816 -dev/sound/isa/ad1816.c optional snd isa -dev/sound/isa/es1888.c optional snd_ess -dev/sound/isa/es1888.c optional snd isa -dev/sound/isa/ess.c optional snd_ess -dev/sound/isa/ess.c optional snd isa -dev/sound/isa/gusc.c optional snd_mss -dev/sound/isa/gusc.c optional snd isa -dev/sound/isa/mss.c optional snd_mss -dev/sound/isa/mss.c optional snd isa -dev/sound/isa/sb16.c optional snd_sb16 -dev/sound/isa/sb16.c optional snd isa -dev/sound/isa/sb8.c optional snd_sb8 -dev/sound/isa/sb8.c optional snd isa -dev/sound/isa/sbc.c optional snd_sbc -dev/sound/isa/sbc.c optional snd isa -# -dev/sound/pci/als4000.c optional snd_als4000 -dev/sound/pci/als4000.c optional snd pci -dev/sound/pci/cmi.c optional snd_cmi -dev/sound/pci/cmi.c optional snd pci -dev/sound/pci/cs4281.c optional snd_cs4281 -dev/sound/pci/cs4281.c optional snd pci -dev/sound/pci/csa.c optional snd_csa -dev/sound/pci/csa.c optional pcm pci -dev/sound/pci/csapcm.c optional snd_csa -dev/sound/pci/csapcm.c optional snd pci -dev/sound/pci/ds1.c optional snd_sa1 -dev/sound/pci/ds1.c optional snd pci -dev/sound/pci/emu10k1.c optional snd_emu10k1 -dev/sound/pci/emu10k1.c optional snd pci -dev/sound/pci/es137x.c optional snd_es137x -dev/sound/pci/es137x.c optional snd pci -dev/sound/pci/fm801.c optional snd_fm801 -dev/sound/pci/fm801.c optional snd pci -dev/sound/pci/ich.c optional snd_ich -dev/sound/pci/ich.c optional snd pci -dev/sound/pci/maestro.c optional snd_maestro -dev/sound/pci/maestro.c optional snd pci -dev/sound/pci/maestro3.c optional snd_maestro3 -dev/sound/pci/maestro3.c optional snd pci -dev/sound/pci/neomagic.c optional snd_neomagic -dev/sound/pci/neomagic.c optional snd pci -dev/sound/pci/solo.c optional snd_solo -dev/sound/pci/solo.c optional snd pci -dev/sound/pci/t4dwave.c optional snd_t4dwave -dev/sound/pci/t4dwave.c optional snd pci -dev/sound/pci/via8233.c optional snd_via8233 -dev/sound/pci/via8233.c optional snd pci -dev/sound/pci/via82c686.c optional snd_via82c686 -dev/sound/pci/via82c686.c optional snd pci -dev/sound/pci/vibes.c optional snd_vibes -dev/sound/pci/vibes.c optional snd pci -dev/sound/usb/uaudio.c optional snd_uaudio -dev/sound/usb/uaudio.c optional snd usb -dev/sound/usb/uaudio_pcm.c optional snd_uaudio -dev/sound/usb/uaudio_pcm.c optional snd usb -# +dev/sound/isa/ad1816.c optional snd_ad1816 isa +dev/sound/isa/es1888.c optional snd_ess isa +dev/sound/isa/ess.c optional snd_ess isa +dev/sound/isa/gusc.c optional snd_gusc isa +dev/sound/isa/mss.c optional snd_mss isa +dev/sound/isa/sb16.c optional snd_sb16 isa +dev/sound/isa/sb8.c optional snd_sb8 isa +dev/sound/isa/sbc.c optional snd_sbc isa +dev/sound/isa/sndbuf_dma.c optional pcm isa +dev/sound/pci/als4000.c optional snd_als4000 pci +dev/sound/pci/atiixp.c optional snd_atiixp pci +#dev/sound/pci/au88x0.c optional snd_au88x0 pci +dev/sound/pci/cmi.c optional snd_cmi pci +dev/sound/pci/cs4281.c optional snd_cs4281 pci +dev/sound/pci/csa.c optional snd_csa pci +dev/sound/pci/csapcm.c optional snd_csa pci +dev/sound/pci/ds1.c optional snd_ds1 pci +dev/sound/pci/emu10k1.c optional snd_emu10k1 pci \ + dependency "emu10k1-alsa%diked.h" +dev/sound/pci/es137x.c optional snd_es137x pci +dev/sound/pci/fm801.c optional snd_fm801 pci +dev/sound/pci/ich.c optional snd_ich pci +dev/sound/pci/maestro.c optional snd_maestro pci +dev/sound/pci/maestro3.c optional snd_maestro3 pci +dev/sound/pci/neomagic.c optional snd_neomagic pci +dev/sound/pci/solo.c optional snd_solo pci +dev/sound/pci/t4dwave.c optional snd_t4dwave pci +dev/sound/pci/via8233.c optional snd_via8233 pci +dev/sound/pci/via82c686.c optional snd_via82c686 pci +dev/sound/pci/vibes.c optional snd_vibes pci +dev/sound/pcm/ac97.c optional pcm dev/sound/pcm/ac97_if.m optional pcm +dev/sound/pcm/ac97_patch.c optional pcm +dev/sound/pcm/buffer.c optional pcm +dev/sound/pcm/channel.c optional pcm dev/sound/pcm/channel_if.m 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/feeder_if.m optional pcm +dev/sound/pcm/feeder_rate.c optional pcm +dev/sound/pcm/feeder_volume.c optional pcm +dev/sound/pcm/mixer.c optional pcm dev/sound/pcm/mixer_if.m optional pcm -# -dev/sound/pcm/ac97.c optional pcm -dev/sound/pcm/ac97_patch.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/feeder_rate.c optional pcm -dev/sound/pcm/mixer.c optional pcm -dev/sound/pcm/sndstat.c optional pcm -dev/sound/pcm/sound.c optional pcm -dev/sound/pcm/vchan.c optional pcm +dev/sound/pcm/sndstat.c optional pcm +dev/sound/pcm/sound.c optional pcm +dev/sound/pcm/vchan.c optional pcm +#dev/sound/usb/upcm.c optional snd_upcm usb +dev/sound/usb/uaudio.c optional snd_uaudio usb +dev/sound/usb/uaudio_pcm.c optional snd_uaudio usb # # These files in libkern/ are those needed by all architectures. Some # of the files in libkern/ are only needed on some architectures, e.g., diff --git a/sys/config/LINT b/sys/config/LINT index 588376e630..6a0b457795 100644 --- a/sys/config/LINT +++ b/sys/config/LINT @@ -3,7 +3,7 @@ # as much of the source tree as it can. # # $FreeBSD: src/sys/i386/conf/LINT,v 1.749.2.144 2003/06/04 17:56:59 sam Exp $ -# $DragonFly: src/sys/config/LINT,v 1.99 2007/01/03 13:24:13 swildner Exp $ +# $DragonFly: src/sys/config/LINT,v 1.100 2007/01/04 21:47:00 corecode Exp $ # # NB: You probably don't want to try running a kernel built from this # file. Instead, you should start from GENERIC, and add options from @@ -1471,12 +1471,31 @@ options NATM #native ATM # flags to be the ``read dma channel''. # -# Basic PCM support, needed for all sound card: +# Basic sound card support: device pcm # For PnP/PCI sound cards: -device snd +device "snd_als4000" +device "snd_atiixp" +device "snd_cmi" +device "snd_cs4281" +device "snd_csa" +device "snd_ds1" +device "snd_emu10k1" +device "snd_es137x" +device "snd_fm801" +device "snd_ich" +device "snd_maestro" +device "snd_maestro3" +device "snd_neomagic" +device "snd_solo" +device "snd_t4dwave" +device "snd_via8233" +device "snd_via82c686" +device "snd_vibes" # For non-pnp sound cards: -device snd0 at isa? irq 10 drq 1 flags 0x0 +device pcm0 at isa? irq 10 drq 1 flags 0x0 +# Usb +device "snd_uaudio" # # Miscellaneous hardware: diff --git a/sys/dev/sound/Makefile b/sys/dev/sound/Makefile index cd9be2518c..a903dc4d24 100644 --- a/sys/dev/sound/Makefile +++ b/sys/dev/sound/Makefile @@ -1,8 +1,6 @@ # $FreeBSD: src/sys/modules/sound/Makefile,v 1.1.2.2 2001/02/27 04:47:48 cg Exp $ -# $DragonFly: src/sys/dev/sound/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ +# $DragonFly: src/sys/dev/sound/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $ -SUBDIR = -SUBDIR += pcm -SUBDIR += driver snd +SUBDIR = pcm driver .include diff --git a/sys/dev/sound/driver.c b/sys/dev/sound/driver.c index e470aada8f..c49858566a 100644 --- a/sys/dev/sound/driver.c +++ b/sys/dev/sound/driver.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Cameron Grant * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/driver.c,v 1.7.2.1 2001/02/27 04:47:47 cg Exp $ - * $DragonFly: src/sys/dev/sound/driver.c,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/driver.c,v 1.13.2.2 2006/01/24 18:56:11 joel Exp $ + * $DragonFly: src/sys/dev/sound/driver.c,v 1.3 2007/01/04 21:47:00 corecode Exp $ */ #include @@ -39,6 +39,7 @@ snd_modevent(module_t mod, int type, void *data) case MOD_UNLOAD: break; default: + return (EOPNOTSUPP); break; } return 0; @@ -49,5 +50,35 @@ static moduledata_t snd_mod = { snd_modevent, NULL }; -DECLARE_MODULE(snd, snd_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); +DECLARE_MODULE(snd_driver, snd_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); +MODULE_VERSION(snd_driver, 1); +MODULE_DEPEND(snd_driver, snd_ad1816, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_als4000, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_atiixp, 1, 1, 1); +/* MODULE_DEPEND(snd_driver, snd_aureal, 1, 1, 1); */ +MODULE_DEPEND(snd_driver, snd_cmi, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_cs4281, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_csa, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_csapcm, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_ds1, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_emu10k1, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_es137x, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_es1888, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_ess, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_fm801, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_gusc, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_hda, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_ich, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_maestro, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_maestro3, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_mss, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_neomagic, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_sb16, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_sb8, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_sbc, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_solo, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_t4dwave, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_via8233, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_via82c686, 1, 1, 1); +MODULE_DEPEND(snd_driver, snd_vibes, 1, 1, 1); diff --git a/sys/dev/sound/driver/Makefile b/sys/dev/sound/driver/Makefile index 7d945bbae9..af2333cc53 100644 --- a/sys/dev/sound/driver/Makefile +++ b/sys/dev/sound/driver/Makefile @@ -1,8 +1,15 @@ -# $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.5.2.12 2002/08/24 08:34:20 nsayer Exp $ -# $DragonFly: src/sys/dev/sound/driver/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ +# $FreeBSD: src/sys/modules/sound/driver/Makefile,v 1.16.2.1 2005/12/30 19:55:54 netchild Exp $ +# $DragonFly: src/sys/dev/sound/driver/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $ -SUBDIR = ad1816 als4000 cmi cs4281 csa ds1 emu10k1 es137x ess fm801 ich -SUBDIR += maestro maestro3 mss neomagic sb16 sb8 sbc solo t4dwave -SUBDIR += uaudio via8233 via82c686 vibes +.if ${MACHINE_ARCH} == "sparc64" +SUBDIR = audiocs es137x +.else +SUBDIR = als4000 ad1816 atiixp cmi cs4281 csa ds1 emu10k1 es137x ess +SUBDIR += fm801 ich maestro maestro3 mss neomagic sb16 sb8 sbc solo +SUBDIR += t4dwave via8233 via82c686 vibes +SUBDIR += hda +SUBDIR += driver +SUBDIR += uaudio +.endif .include diff --git a/sys/dev/sound/driver/Makefile.inc b/sys/dev/sound/driver/Makefile.inc index d1f9528d49..b92aa03632 100644 --- a/sys/dev/sound/driver/Makefile.inc +++ b/sys/dev/sound/driver/Makefile.inc @@ -1,6 +1,6 @@ -# $FreeBSD: src/sys/modules/sound/driver/Makefile.inc,v 1.1.2.3 2001/02/24 21:49:05 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/Makefile.inc,v 1.4 2004/01/28 19:27:01 dillon Exp $ +# $FreeBSD: src/sys/modules/sound/driver/Makefile.inc,v 1.3 2001/04/08 21:50:41 obrien Exp $ +# $DragonFly: src/sys/dev/sound/driver/Makefile.inc,v 1.5 2007/01/04 21:47:00 corecode Exp $ -SRCS += ac97_if.h channel_if.h feeder_if.h mixer_if.h +SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h .include "../Makefile.inc" diff --git a/sys/dev/sound/driver/ad1816/Makefile b/sys/dev/sound/driver/ad1816/Makefile index a82b2838ef..31b66580ff 100644 --- a/sys/dev/sound/driver/ad1816/Makefile +++ b/sys/dev/sound/driver/ad1816/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/ad1816/Makefile,v 1.1.2.2 2001/04/07 16:48:50 peter Exp $ -# $DragonFly: src/sys/dev/sound/driver/ad1816/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/isa -KMOD = snd_ad1816 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += ad1816.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/ad1816/Makefile,v 1.2 2001/01/06 14:00:15 obrien Exp $ +# $DragonFly: src/sys/dev/sound/driver/ad1816/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/isa + +KMOD= snd_ad1816 +SRCS= device_if.h bus_if.h isa_if.h pci_if.h +SRCS+= ad1816.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/als4000/Makefile b/sys/dev/sound/driver/als4000/Makefile index 287bbdaf28..27182317b3 100644 --- a/sys/dev/sound/driver/als4000/Makefile +++ b/sys/dev/sound/driver/als4000/Makefile @@ -1,11 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/als4000/Makefile,v 1.1.2.2 2001/08/01 03:41:06 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/als4000/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ +# $FreeBSD: src/sys/modules/sound/driver/als4000/Makefile,v 1.2 2003/02/07 13:56:31 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/als4000/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $ .PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD= snd_als4000 -SRCS= device_if.h bus_if.h isa_if.h pci_if.h -SRCS+= als4000.c -KMODDEPS = snd_pcm +KMOD= snd_als4000 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= als4000.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/atiixp/Makefile b/sys/dev/sound/driver/atiixp/Makefile new file mode 100644 index 0000000000..79d5efb94b --- /dev/null +++ b/sys/dev/sound/driver/atiixp/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD: src/sys/modules/sound/driver/atiixp/Makefile,v 1.1.2.1 2005/12/30 19:55:55 netchild Exp $ +# $DragonFly: src/sys/dev/sound/driver/atiixp/Makefile,v 1.1 2007/01/04 21:47:00 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_atiixp +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= atiixp.c + +KMODDEPS= sound + +.include diff --git a/sys/dev/sound/driver/au88x0/Makefile b/sys/dev/sound/driver/au88x0/Makefile new file mode 100644 index 0000000000..273028d1cf --- /dev/null +++ b/sys/dev/sound/driver/au88x0/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD: src/sys/modules/sound/driver/au88x0/Makefile,v 1.1 2003/06/01 11:58:46 des Exp $ +# $DragonFly: src/sys/dev/sound/driver/au88x0/Makefile,v 1.1 2007/01/04 21:47:00 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_au88x0 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= au88x0.c + +KMODDEPS=sound + +.include diff --git a/sys/dev/sound/driver/audiocs/Makefile b/sys/dev/sound/driver/audiocs/Makefile new file mode 100644 index 0000000000..c095812a10 --- /dev/null +++ b/sys/dev/sound/driver/audiocs/Makefile @@ -0,0 +1,13 @@ +# $FreeBSD: src/sys/modules/sound/driver/audiocs/Makefile,v 1.1 2004/10/25 10:29:57 yongari Exp $ +# $DragonFly: src/sys/dev/sound/driver/audiocs/Makefile,v 1.1 2007/01/04 21:47:00 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/sbus + +KMOD= snd_audiocs +SRCS= device_if.h bus_if.h ofw_bus_if.h +SRCS+= channel_if.h feeder_if.h mixer_if.h +SRCS+= cs4231.c + +KMODDEPS=sound + +.include diff --git a/sys/dev/sound/driver/cmi/Makefile b/sys/dev/sound/driver/cmi/Makefile index 738fd351a3..fd58814df6 100644 --- a/sys/dev/sound/driver/cmi/Makefile +++ b/sys/dev/sound/driver/cmi/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/cmi/Makefile,v 1.1.2.1 2001/02/27 03:42:29 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/cmi/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_cmi -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += cmi.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/cmi/Makefile,v 1.3 2003/02/07 13:56:31 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/cmi/Makefile,v 1.3 2007/01/04 21:47:00 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_cmi +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= cmi.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/cs4281/Makefile b/sys/dev/sound/driver/cs4281/Makefile index d322a8fbfa..439794f001 100644 --- a/sys/dev/sound/driver/cs4281/Makefile +++ b/sys/dev/sound/driver/cs4281/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/cs4281/Makefile,v 1.1.2.1 2001/02/27 03:57:52 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/cs4281/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_cs4281 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += cs4281.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/cs4281/Makefile,v 1.3 2003/02/07 13:56:31 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/cs4281/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_cs4281 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= cs4281.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/csa/Makefile b/sys/dev/sound/driver/csa/Makefile index c82b1dcdcb..0c8b7426b6 100644 --- a/sys/dev/sound/driver/csa/Makefile +++ b/sys/dev/sound/driver/csa/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/csa/Makefile,v 1.1.2.2 2001/02/27 04:31:27 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/csa/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_csa -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += csa.c csapcm.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/csa/Makefile,v 1.4 2003/02/07 13:56:31 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/csa/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_csa +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= csa.c csapcm.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/driver/Makefile b/sys/dev/sound/driver/driver/Makefile new file mode 100644 index 0000000000..5d9bf503f5 --- /dev/null +++ b/sys/dev/sound/driver/driver/Makefile @@ -0,0 +1,37 @@ +# $FreeBSD: src/sys/modules/sound/driver/driver/Makefile,v 1.4 2003/02/07 15:05:37 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/driver/Makefile,v 1.1 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound + +KMOD= snd_driver +SRCS= device_if.h bus_if.h +SRCS+= driver.c + +KMODDEPS= snd_ad1816 +KMODDEPS+= snd_als4000 +KMODDEPS+= snd_atiixp +#KMODDEPS+= snd_aureal +KMODDEPS+= snd_cmi +KMODDEPS+= snd_cs4281 +KMODDEPS+= snd_csa +KMODDEPS+= snd_ds1 +KMODDEPS+= snd_emu10k1 +KMODDEPS+= snd_es137x +KMODDEPS+= snd_ess +KMODDEPS+= snd_fm801 +KMODDEPS+= snd_hda +KMODDEPS+= snd_ich +KMODDEPS+= snd_maestro +KMODDEPS+= snd_maestro3 +KMODDEPS+= snd_mss +KMODDEPS+= snd_neomagic +KMODDEPS+= snd_sb16 +KMODDEPS+= snd_sb8 +KMODDEPS+= snd_sbc +KMODDEPS+= snd_solo +KMODDEPS+= snd_t4dwave +KMODDEPS+= snd_via8233 +KMODDEPS+= snd_via82c686 +KMODDEPS+= snd_vibes + +.include diff --git a/sys/dev/sound/driver/ds1/Makefile b/sys/dev/sound/driver/ds1/Makefile index d374fcad33..74fbb05f46 100644 --- a/sys/dev/sound/driver/ds1/Makefile +++ b/sys/dev/sound/driver/ds1/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/ds1/Makefile,v 1.1.2.1 2000/09/23 19:15:02 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/ds1/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_ds1 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += ds1.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/ds1/Makefile,v 1.3 2003/02/07 13:56:31 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/ds1/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_ds1 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= ds1.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/emu10k1/Makefile b/sys/dev/sound/driver/emu10k1/Makefile index ee4d739eba..71492ac2b1 100644 --- a/sys/dev/sound/driver/emu10k1/Makefile +++ b/sys/dev/sound/driver/emu10k1/Makefile @@ -1,10 +1,21 @@ -# $FreeBSD: src/sys/modules/sound/driver/emu10k1/Makefile,v 1.1.2.2 2001/04/07 16:48:51 peter Exp $ -# $DragonFly: src/sys/dev/sound/driver/emu10k1/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_emu10k1 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += emu10k1.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/emu10k1/Makefile,v 1.4 2004/01/11 10:30:56 obrien Exp $ +# $DragonFly: src/sys/dev/sound/driver/emu10k1/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../pci \ + ${.CURDIR}/../../pci/gnu + +KMOD= snd_emu10k1 +SRCS= device_if.h bus_if.h pci_if.h emu10k1-alsa%diked.h +SRCS+= emu10k1.c + +KMODDEPS= sound + +CLEANFILES+= emu10k1-alsa%diked.h + +emu10k1-alsa%diked.h: emu10k1-alsa.h + grep -v '#include' ${.OODATE} | $(CC) -E -D__KERNEL__ -dM - \ + | awk -F"[ (]" '/define/ \ + { print "#ifndef " $$2 ; print ; print "#endif" }' \ + >${.TARGET} .include diff --git a/sys/dev/sound/driver/es137x/Makefile b/sys/dev/sound/driver/es137x/Makefile index ec473fe964..ba6d25ec2d 100644 --- a/sys/dev/sound/driver/es137x/Makefile +++ b/sys/dev/sound/driver/es137x/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/es137x/Makefile,v 1.1.2.1 2000/09/23 19:15:02 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/es137x/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_es137x -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += es137x.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/es137x/Makefile,v 1.3 2003/02/07 13:56:32 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/es137x/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_es137x +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= es137x.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/ess/Makefile b/sys/dev/sound/driver/ess/Makefile index b80ed3625c..a706e90b43 100644 --- a/sys/dev/sound/driver/ess/Makefile +++ b/sys/dev/sound/driver/ess/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/ess/Makefile,v 1.1.2.2 2001/02/27 04:31:28 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/ess/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/isa -KMOD = snd_ess -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += ess.c es1888.c -KMODDEPS = snd_pcm snd_sbc +# $FreeBSD: src/sys/modules/sound/driver/ess/Makefile,v 1.3 2002/01/23 03:32:36 cg Exp $ +# $DragonFly: src/sys/dev/sound/driver/ess/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/isa + +KMOD= snd_ess +SRCS= device_if.h bus_if.h isa_if.h pci_if.h +SRCS+= ess.c + +KMODDEPS= sound snd_sbc .include diff --git a/sys/dev/sound/driver/fm801/Makefile b/sys/dev/sound/driver/fm801/Makefile index 958c4733d4..26443d566e 100644 --- a/sys/dev/sound/driver/fm801/Makefile +++ b/sys/dev/sound/driver/fm801/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/fm801/Makefile,v 1.1.2.1 2000/10/05 05:41:40 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/fm801/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_fm801 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += fm801.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/fm801/Makefile,v 1.3 2003/02/07 13:56:32 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/fm801/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_fm801 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= fm801.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/hda/Makefile b/sys/dev/sound/driver/hda/Makefile new file mode 100644 index 0000000000..bf8114178e --- /dev/null +++ b/sys/dev/sound/driver/hda/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD: src/sys/modules/sound/driver/hda/Makefile,v 1.1 2006/10/01 11:13:00 ariff Exp $ +# $DragonFly: src/sys/dev/sound/driver/hda/Makefile,v 1.1 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci/hda + +KMOD= snd_hda +SRCS= device_if.h bus_if.h pci_if.h channel_if.h mixer_if.h isa_if.h +SRCS+= hdac.c hdac_private.h hdac_reg.h hda_reg.h hdac.h + +KMODDEPS= sound + +.include diff --git a/sys/dev/sound/driver/ich/Makefile b/sys/dev/sound/driver/ich/Makefile index 79ed3f29d0..02d9bd46c0 100644 --- a/sys/dev/sound/driver/ich/Makefile +++ b/sys/dev/sound/driver/ich/Makefile @@ -1,10 +1,11 @@ -# $FreeBSD: src/sys/modules/sound/driver/ich/Makefile,v 1.1.2.1 2001/08/01 05:37:30 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/ich/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ +# $FreeBSD: src/sys/modules/sound/driver/ich/Makefile,v 1.2 2003/02/07 13:56:32 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/ich/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ .PATH: ${.CURDIR}/../../../../dev/sound/pci KMOD = snd_ich -SRCS = device_if.h bus_if.h isa_if.h pci_if.h +SRCS = device_if.h bus_if.h pci_if.h SRCS += ich.c -KMODDEPS = snd_pcm + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/maestro/Makefile b/sys/dev/sound/driver/maestro/Makefile index 9b83c57a94..b8fdf7b160 100644 --- a/sys/dev/sound/driver/maestro/Makefile +++ b/sys/dev/sound/driver/maestro/Makefile @@ -1,10 +1,13 @@ -# $FreeBSD: src/sys/modules/sound/driver/maestro/Makefile,v 1.1.2.1 2000/10/05 05:44:06 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/maestro/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_maestro -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += maestro.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/maestro/Makefile,v 1.4 2005/01/26 16:29:07 imp Exp $ +# $DragonFly: src/sys/dev/sound/driver/maestro/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_maestro +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= maestro.c + +KMODDEPS= sound +WERROR= .include diff --git a/sys/dev/sound/driver/maestro3/Makefile b/sys/dev/sound/driver/maestro3/Makefile index 042993c51f..fb9dd7d25e 100644 --- a/sys/dev/sound/driver/maestro3/Makefile +++ b/sys/dev/sound/driver/maestro3/Makefile @@ -1,13 +1,14 @@ -# $FreeBSD: src/sys/modules/sound/driver/maestro3/Makefile,v 1.1.2.2 2001/03/04 08:19:02 scottl Exp $ -# $DragonFly: src/sys/dev/sound/driver/maestro3/Makefile,v 1.3 2003/08/15 08:32:31 dillon Exp $ -.PATH: ${.CURDIR}/../../pci ${.CURDIR}/../../pci/gnu -KMOD = snd_maestro3 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += maestro3.c -CFLAGS += -Wall +# $FreeBSD: src/sys/modules/sound/driver/maestro3/Makefile,v 1.3 2003/02/07 13:56:32 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/maestro3/Makefile,v 1.4 2007/01/04 21:47:01 corecode Exp $ -CFLAGS += -DM3_DEBUG_LEVEL=-1 +.PATH: ${.CURDIR}/../../pci -KMODDEPS = snd_pcm +KMOD= snd_maestro3 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= maestro3.c + +KMODDEPS= sound + +CFLAGS+= -Wall -DM3_DEBUG_LEVEL=-1 .include diff --git a/sys/dev/sound/driver/mss/Makefile b/sys/dev/sound/driver/mss/Makefile index 90e1f8fd8a..da2dd8c80a 100644 --- a/sys/dev/sound/driver/mss/Makefile +++ b/sys/dev/sound/driver/mss/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/mss/Makefile,v 1.1.2.4 2002/12/01 09:01:08 nyan Exp $ -# $DragonFly: src/sys/dev/sound/driver/mss/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/isa -KMOD = snd_mss -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += mss.c gusc.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/mss/Makefile,v 1.5 2002/11/06 13:46:59 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/mss/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/isa + +KMOD= snd_mss +SRCS= device_if.h bus_if.h isa_if.h pci_if.h +SRCS+= mss.c gusc.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/neomagic/Makefile b/sys/dev/sound/driver/neomagic/Makefile index e93615e77e..3618dab450 100644 --- a/sys/dev/sound/driver/neomagic/Makefile +++ b/sys/dev/sound/driver/neomagic/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/neomagic/Makefile,v 1.1.2.1 2000/09/23 19:15:05 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/neomagic/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_neomagic -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += neomagic.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/neomagic/Makefile,v 1.3 2003/02/07 13:56:32 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/neomagic/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_neomagic +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= neomagic.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/sb16/Makefile b/sys/dev/sound/driver/sb16/Makefile index 0b86ba760c..a36ad50878 100644 --- a/sys/dev/sound/driver/sb16/Makefile +++ b/sys/dev/sound/driver/sb16/Makefile @@ -1,11 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/sb16/Makefile,v 1.2.2.1 2001/02/03 02:09:24 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/sb16/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ +# $FreeBSD: src/sys/modules/sound/driver/sb16/Makefile,v 1.2 2001/01/06 14:00:20 obrien Exp $ +# $DragonFly: src/sys/dev/sound/driver/sb16/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ .PATH: ${.CURDIR}/../../../../dev/sound/isa KMOD= snd_sb16 SRCS= device_if.h bus_if.h isa_if.h pci_if.h SRCS+= sb16.c -KMODDEPS = snd_pcm snd_sbc + +KMODDEPS= sound snd_sbc .include diff --git a/sys/dev/sound/driver/sb8/Makefile b/sys/dev/sound/driver/sb8/Makefile index 0371314a47..1382c3a891 100644 --- a/sys/dev/sound/driver/sb8/Makefile +++ b/sys/dev/sound/driver/sb8/Makefile @@ -1,11 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/sb8/Makefile,v 1.2.2.1 2001/02/03 02:09:24 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/sb8/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ +# $FreeBSD: src/sys/modules/sound/driver/sb8/Makefile,v 1.2 2001/01/06 14:00:20 obrien Exp $ +# $DragonFly: src/sys/dev/sound/driver/sb8/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ .PATH: ${.CURDIR}/../../../../dev/sound/isa KMOD= snd_sb8 SRCS= device_if.h bus_if.h isa_if.h pci_if.h SRCS+= sb8.c -KMODDEPS = snd_pcm snd_sbc + +KMODDEPS= sound snd_sbc .include diff --git a/sys/dev/sound/driver/sbc/Makefile b/sys/dev/sound/driver/sbc/Makefile index 0111f595c3..7e741338e9 100644 --- a/sys/dev/sound/driver/sbc/Makefile +++ b/sys/dev/sound/driver/sbc/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/sbc/Makefile,v 1.1.2.3 2002/12/01 09:01:08 nyan Exp $ -# $DragonFly: src/sys/dev/sound/driver/sbc/Makefile,v 1.3 2004/01/05 20:23:53 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/isa -KMOD = snd_sbc -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += sbc.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/sbc/Makefile,v 1.4 2002/11/06 13:46:59 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/sbc/Makefile,v 1.4 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/isa + +KMOD= snd_sbc +SRCS= device_if.h bus_if.h isa_if.h pci_if.h +SRCS+= sbc.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/solo/Makefile b/sys/dev/sound/driver/solo/Makefile index ec8d28ca55..f8b7007069 100644 --- a/sys/dev/sound/driver/solo/Makefile +++ b/sys/dev/sound/driver/solo/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/solo/Makefile,v 1.1.2.1 2000/09/23 19:15:08 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/solo/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_solo -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += solo.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/solo/Makefile,v 1.3 2003/02/07 13:56:32 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/solo/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_solo +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= solo.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/t4dwave/Makefile b/sys/dev/sound/driver/t4dwave/Makefile index 8ccd158c18..4188145044 100644 --- a/sys/dev/sound/driver/t4dwave/Makefile +++ b/sys/dev/sound/driver/t4dwave/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/t4dwave/Makefile,v 1.1.2.1 2000/09/23 19:15:08 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/t4dwave/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_t4dwave -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += t4dwave.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/t4dwave/Makefile,v 1.3 2003/02/07 13:56:33 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/t4dwave/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_t4dwave +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= t4dwave.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/uaudio/Makefile b/sys/dev/sound/driver/uaudio/Makefile index e2f1a788c1..2b034bc54f 100644 --- a/sys/dev/sound/driver/uaudio/Makefile +++ b/sys/dev/sound/driver/uaudio/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/uaudio/Makefile,v 1.1.2.1 2002/08/24 08:06:13 nsayer Exp $ -# $DragonFly: src/sys/dev/sound/driver/uaudio/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/usb -KMOD = snd_uaudio -SRCS = device_if.h bus_if.h isa_if.h opt_usb.h -SRCS += uaudio.c uaudio_pcm.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/uaudio/Makefile,v 1.3 2004/12/29 08:50:35 imp Exp $ +# $DragonFly: src/sys/dev/sound/driver/uaudio/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/usb + +KMOD= snd_uaudio +SRCS= device_if.h bus_if.h opt_usb.h +SRCS+= uaudio.c uaudio_pcm.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/via8233/Makefile b/sys/dev/sound/driver/via8233/Makefile index 9d9b298f8b..48194e930b 100644 --- a/sys/dev/sound/driver/via8233/Makefile +++ b/sys/dev/sound/driver/via8233/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/via8233/Makefile,v 1.1.2.1 2002/08/22 17:32:49 orion Exp $ -# $DragonFly: src/sys/dev/sound/driver/via8233/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_via8233 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += via8233.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/via8233/Makefile,v 1.2 2003/02/07 13:56:33 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/via8233/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_via8233 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= via8233.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/via82c686/Makefile b/sys/dev/sound/driver/via82c686/Makefile index bbe9807d18..7c0d1a7abf 100644 --- a/sys/dev/sound/driver/via82c686/Makefile +++ b/sys/dev/sound/driver/via82c686/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/via82c686/Makefile,v 1.1.2.1 2000/10/05 05:45:55 cg Exp $ -# $DragonFly: src/sys/dev/sound/driver/via82c686/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_via82c686 -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += via82c686.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/via82c686/Makefile,v 1.3 2003/02/07 13:56:33 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/via82c686/Makefile,v 1.3 2007/01/04 21:47:01 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_via82c686 +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= via82c686.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/driver/vibes/Makefile b/sys/dev/sound/driver/vibes/Makefile index b56c5c98cc..fe1ffdb1da 100644 --- a/sys/dev/sound/driver/vibes/Makefile +++ b/sys/dev/sound/driver/vibes/Makefile @@ -1,10 +1,12 @@ -# $FreeBSD: src/sys/modules/sound/driver/vibes/Makefile,v 1.3.2.1 2001/04/23 22:58:26 orion Exp $ -# $DragonFly: src/sys/dev/sound/driver/vibes/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../../dev/sound/pci -KMOD = snd_vibes -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += vibes.c -KMODDEPS = snd_pcm +# $FreeBSD: src/sys/modules/sound/driver/vibes/Makefile,v 1.4 2003/02/07 13:56:33 nyan Exp $ +# $DragonFly: src/sys/dev/sound/driver/vibes/Makefile,v 1.3 2007/01/04 21:47:02 corecode Exp $ + +.PATH: ${.CURDIR}/../../../../dev/sound/pci + +KMOD= snd_vibes +SRCS= device_if.h bus_if.h pci_if.h +SRCS+= vibes.c + +KMODDEPS= sound .include diff --git a/sys/dev/sound/isa/ad1816.c b/sys/dev/sound/isa/ad1816.c index 0a65f2be87..ee0728fe89 100644 --- a/sys/dev/sound/isa/ad1816.c +++ b/sys/dev/sound/isa/ad1816.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 * All rights reserved. @@ -25,16 +25,18 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/ad1816.c,v 1.7.2.9 2002/12/24 21:17:41 semenu Exp $ - * $DragonFly: src/sys/dev/sound/isa/ad1816.c,v 1.6 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/ad1816.c,v 1.37.2.2 2006/04/04 17:23:24 ariff Exp $ + * $DragonFly: src/sys/dev/sound/isa/ad1816.c,v 1.7 2007/01/04 21:47:02 corecode Exp $ */ #include #include +#include + #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/ad1816.c,v 1.6 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/ad1816.c,v 1.7 2007/01/04 21:47:02 corecode Exp $"); struct ad1816_info; @@ -56,7 +58,7 @@ struct ad1816_info { int drq2_rid; void *ih; bus_dma_tag_t parent_dmat; - void *lock; + struct spinlock *lock; unsigned int bufsize; struct ad1816_chinfo pch, rch; @@ -139,12 +141,16 @@ ad1816_intr(void *arg) } /* check for capture interupt */ if (sndbuf_runsz(ad1816->rch.buffer) && (c & AD1816_INTRCI)) { + ad1816_unlock(ad1816); chn_intr(ad1816->rch.channel); + ad1816_lock(ad1816); served |= AD1816_INTRCI; /* cp served */ } /* check for playback interupt */ if (sndbuf_runsz(ad1816->pch.buffer) && (c & AD1816_INTRPI)) { + ad1816_unlock(ad1816); chn_intr(ad1816->pch.channel); + ad1816_lock(ad1816); served |= AD1816_INTRPI; /* pb served */ } if (served == 0) { @@ -315,7 +321,8 @@ ad1816chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channe ch->parent = ad1816; ch->channel = c; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, ad1816->bufsize) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, ad1816->bufsize) != 0) + return NULL; return ch; } @@ -325,7 +332,7 @@ ad1816chan_setdir(kobj_t obj, void *data, int dir) struct ad1816_chinfo *ch = data; struct ad1816_info *ad1816 = ch->parent; - sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); + sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); ch->dir = dir; return 0; } @@ -371,8 +378,11 @@ ad1816chan_setformat(kobj_t obj, void *data, u_int32_t format) if (format & AFMT_STEREO) fmt |= AD1816_STEREO; io_wr(ad1816, reg, fmt); ad1816_unlock(ad1816); - - return (0); +#if 0 + return format; +#else + return 0; +#endif } static int @@ -407,7 +417,7 @@ ad1816chan_trigger(kobj_t obj, void *data, int go) if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - sndbuf_isadma(ch->buffer, go); + sndbuf_dma(ch->buffer, go); wr = (ch->dir == PCMDIR_PLAY); reg = wr? AD1816_PLAY : AD1816_CAPT; ad1816_lock(ad1816); @@ -453,7 +463,7 @@ static int ad1816chan_getptr(kobj_t obj, void *data) { struct ad1816_chinfo *ch = data; - return sndbuf_isadmaptr(ch->buffer); + return sndbuf_dmaptr(ch->buffer); } static struct pcmchan_caps * @@ -516,17 +526,17 @@ 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); + ad1816->io_base = bus_alloc_resource_any(dev, + SYS_RES_IOPORT, &ad1816->io_rid, RF_ACTIVE); if (!ad1816->irq) - ad1816->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &ad1816->irq_rid, - 0, ~0, 1, RF_ACTIVE); + ad1816->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &ad1816->irq_rid, RF_ACTIVE); if (!ad1816->drq1) - ad1816->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq1_rid, - 0, ~0, 1, RF_ACTIVE); + ad1816->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &ad1816->drq1_rid, RF_ACTIVE); if (!ad1816->drq2) - ad1816->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &ad1816->drq2_rid, - 0, ~0, 1, RF_ACTIVE); + ad1816->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &ad1816->drq2_rid, RF_ACTIVE); if (!ad1816->io_base || !ad1816->drq1 || !ad1816->irq) ok = 0; @@ -573,11 +583,14 @@ ad1816_probe(device_t dev) case 0x80719304: /* ADS7180 */ s = "AD1816"; break; + case 0x50719304: /* ADS7150 */ + s = "AD1815"; + break; } if (s) { device_set_desc(dev, s); - return 0; + return BUS_PROBE_DEFAULT; } return ENXIO; } @@ -602,15 +615,15 @@ ad1816_attach(device_t dev) ad1816_init(ad1816, dev); if (mixer_init(dev, &ad1816mixer_class, ad1816)) goto no; - snd_setup_intr(dev, ad1816->irq, INTR_MPSAFE, ad1816_intr, ad1816, - &ad1816->ih, NULL); + snd_setup_intr(dev, ad1816->irq, 0, 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*/ad1816->bufsize, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &ad1816->parent_dmat) != 0) { + /*flags*/0, + &ad1816->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } @@ -619,12 +632,13 @@ ad1816_attach(device_t dev) else status2[0] = '\0'; - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u", + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", rman_get_start(ad1816->io_base), rman_get_start(ad1816->irq), rman_get_start(ad1816->drq1), status2, - ad1816->bufsize); + ad1816->bufsize, + PCM_KLDSTRING(snd_ad1816)); if (pcm_register(dev, ad1816, 1, 1)) goto no; pcm_addchan(dev, PCMDIR_REC, &ad1816chan_class, ad1816); @@ -670,7 +684,8 @@ static driver_t ad1816_driver = { }; DRIVER_MODULE(snd_ad1816, isa, ad1816_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_ad1816, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +DRIVER_MODULE(snd_ad1816, acpi, ad1816_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_ad1816, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_ad1816, 1); diff --git a/sys/dev/sound/isa/ad1816.h b/sys/dev/sound/isa/ad1816.h index bdcfead7ae..b1f8529c39 100644 --- a/sys/dev/sound/isa/ad1816.h +++ b/sys/dev/sound/isa/ad1816.h @@ -1,11 +1,11 @@ -/* +/*- * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) * * This file contains information and macro definitions for * the ad1816 chip * - * $FreeBSD: src/sys/dev/sound/isa/ad1816.h,v 1.1.2.2 2002/04/22 15:49:30 cg Exp $ - * $DragonFly: src/sys/dev/sound/isa/ad1816.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/isa/ad1816.h,v 1.2 2005/01/06 01:43:17 imp Exp $ + * $DragonFly: src/sys/dev/sound/isa/ad1816.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ /* AD1816 register macros */ diff --git a/sys/dev/sound/isa/es1888.c b/sys/dev/sound/isa/es1888.c deleted file mode 100644 index ca90aef39b..0000000000 --- a/sys/dev/sound/isa/es1888.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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: src/sys/dev/sound/isa/es1888.c,v 1.5.2.5 2002/04/22 15:49:30 cg Exp $ - * $DragonFly: src/sys/dev/sound/isa/Attic/es1888.c,v 1.5 2006/08/25 22:37:08 swildner Exp $ - */ - -#include -#include - -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/Attic/es1888.c,v 1.5 2006/08/25 22:37:08 swildner Exp $"); - -static int -es1888_identify(driver_t *driver, device_t parent) -{ - /* - * We do not suppot rescans - */ - if (device_get_state(parent) == DS_ATTACHED) - return (0); - - return (ENXIO); -} - -static device_method_t es1888_methods[] = { - /* Device interface */ - DEVMETHOD(device_identify, es1888_identify), - - { 0, 0 } -}; - -static driver_t es1888_driver = { - "pcm", - es1888_methods, - 1, /* no softc */ -}; - -DRIVER_MODULE(snd_es1888, isa, es1888_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_es1888, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); -MODULE_VERSION(snd_es1888, 1); - - diff --git a/sys/dev/sound/isa/ess.c b/sys/dev/sound/isa/ess.c index 8eb5162837..f0a2fe5c26 100644 --- a/sys/dev/sound/isa/ess.c +++ b/sys/dev/sound/isa/ess.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, @@ -28,8 +28,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/ess.c,v 1.3.2.8 2002/12/24 21:17:41 semenu Exp $ - * $DragonFly: src/sys/dev/sound/isa/ess.c,v 1.6 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/ess.c,v 1.34.2.2 2006/01/19 01:17:00 ariff Exp $ + * $DragonFly: src/sys/dev/sound/isa/ess.c,v 1.7 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -37,9 +37,11 @@ #include #include +#include + #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/ess.c,v 1.6 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/ess.c,v 1.7 2007/01/04 21:47:02 corecode Exp $"); #define ESS_BUFFSIZE (4096) #define ABS(x) (((x) < 0)? -(x) : (x)) @@ -62,7 +64,7 @@ static u_int32_t ess_pfmt[] = { 0 }; -static struct pcmchan_caps ess_playcaps = {5000, 49000, ess_pfmt, 0}; +static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_pfmt, 0}; static u_int32_t ess_rfmt[] = { AFMT_U8, @@ -76,7 +78,7 @@ static u_int32_t ess_rfmt[] = { 0 }; -static struct pcmchan_caps ess_reccaps = {5000, 49000, ess_rfmt, 0}; +static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_rfmt, 0}; struct ess_info; @@ -317,24 +319,20 @@ ess_alloc_resources(struct ess_info *sc, device_t dev) rid = 0; if (!sc->io_base) - sc->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, - &rid, 0, ~0, 1, - RF_ACTIVE); + sc->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &rid, RF_ACTIVE); rid = 0; if (!sc->irq) - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, - &rid, 0, ~0, 1, - RF_ACTIVE); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &rid, RF_ACTIVE); rid = 0; if (!sc->drq1) - sc->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, - &rid, 0, ~0, 1, - RF_ACTIVE); + sc->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &rid, RF_ACTIVE); rid = 1; if (!sc->drq2) - sc->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, - &rid, 0, ~0, 1, - RF_ACTIVE); + sc->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &rid, RF_ACTIVE); if (sc->io_base && sc->drq1 && sc->irq) { isa_dma_acquire(rman_get_start(sc->drq1)); @@ -366,11 +364,14 @@ ess_intr(void *arg) rirq = (src & sc->rch.hwch)? 1 : 0; if (pirq) { - if (sc->pch.run) + if (sc->pch.run) { + ess_unlock(sc); chn_intr(sc->pch.channel); + ess_lock(sc); + } if (sc->pch.stopping) { sc->pch.run = 0; - sndbuf_isadma(sc->pch.buffer, PCMTRIG_STOP); + sndbuf_dma(sc->pch.buffer, PCMTRIG_STOP); sc->pch.stopping = 0; if (sc->pch.hwch == 1) ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); @@ -380,11 +381,14 @@ ess_intr(void *arg) } if (rirq) { - if (sc->rch.run) + if (sc->rch.run) { + ess_unlock(sc); chn_intr(sc->rch.channel); + ess_lock(sc); + } if (sc->rch.stopping) { sc->rch.run = 0; - sndbuf_isadma(sc->rch.buffer, PCMTRIG_STOP); + sndbuf_dma(sc->rch.buffer, PCMTRIG_STOP); sc->rch.stopping = 0; /* XXX: will this stop audio2? */ ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); @@ -562,13 +566,13 @@ esschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->parent = sc; ch->channel = c; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsize) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsize) != 0) return NULL; ch->dir = dir; ch->hwch = 1; if ((dir == PCMDIR_PLAY) && (sc->duplex)) ch->hwch = 2; - sndbuf_isadmasetup(ch->buffer, (ch->hwch == 1)? sc->drq1 : sc->drq2); + sndbuf_dmasetup(ch->buffer, (ch->hwch == 1)? sc->drq1 : sc->drq2); return ch; } @@ -615,7 +619,7 @@ esschan_trigger(kobj_t obj, void *data, int go) switch (go) { case PCMTRIG_START: ch->run = 1; - sndbuf_isadma(ch->buffer, go); + sndbuf_dma(ch->buffer, go); ess_start(ch); break; @@ -633,7 +637,7 @@ esschan_getptr(kobj_t obj, void *data) { struct ess_chinfo *ch = data; - return sndbuf_isadmaptr(ch->buffer); + return sndbuf_dmaptr(ch->buffer); } static struct pcmchan_caps * @@ -846,7 +850,7 @@ ess_attach(device_t dev) if (sc->newspeed) ess_setmixer(sc, 0x71, 0x22); - snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ess_intr, sc, &sc->ih, NULL); + snd_setup_intr(dev, sc->irq, 0, ess_intr, sc, &sc->ih); if (!sc->duplex) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); @@ -856,7 +860,8 @@ ess_attach(device_t dev) /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsize, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { + /*flags*/0, + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } @@ -866,9 +871,10 @@ ess_attach(device_t dev) else buf[0] = '\0'; - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u", + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", rman_get_start(sc->io_base), rman_get_start(sc->irq), - rman_get_start(sc->drq1), buf, sc->bufsize); + rman_get_start(sc->drq1), buf, sc->bufsize, + PCM_KLDSTRING(snd_ess)); if (pcm_register(dev, sc, 1, 1)) goto no; @@ -935,7 +941,7 @@ static driver_t ess_driver = { }; DRIVER_MODULE(snd_ess, sbc, ess_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_ess, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_ess, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_DEPEND(snd_ess, snd_sbc, 1, 1, 1); MODULE_VERSION(snd_ess, 1); @@ -967,7 +973,7 @@ esscontrol_attach(device_t dev) int rid, i, x; rid = 0; - io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); x = 0; for (i = 0; i < 0x100; i++) { port_wr(io, 0, i); @@ -1007,4 +1013,4 @@ static driver_t esscontrol_driver = { }; DRIVER_MODULE(esscontrol, isa, esscontrol_driver, esscontrol_devclass, 0, 0); - +DRIVER_MODULE(esscontrol, acpi, esscontrol_driver, esscontrol_devclass, 0, 0); diff --git a/sys/dev/sound/isa/gusc.c b/sys/dev/sound/isa/gusc.c index 05fda48e37..b8f121f9ba 100644 --- a/sys/dev/sound/isa/gusc.c +++ b/sys/dev/sound/isa/gusc.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1999 Seigo Tanimura * Copyright (c) 1999 Ville-Pertti Keinonen * All rights reserved. @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/gusc.c,v 1.5.2.6 2002/04/22 15:49:30 cg Exp $ - * $DragonFly: src/sys/dev/sound/isa/gusc.c,v 1.10 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/gusc.c,v 1.16 2005/01/06 01:43:17 imp Exp $ + * $DragonFly: src/sys/dev/sound/isa/gusc.c,v 1.11 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -44,7 +44,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/gusc.c,v 1.10 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/gusc.c,v 1.11 2007/01/04 21:47:02 corecode Exp $"); #define LOGICALID_NOPNP 0 #define LOGICALID_PCM 0x0000561e @@ -317,9 +317,8 @@ gusc_attach(device_t dev) return (ENXIO); } - if (scp->irq != NULL) { - bus_setup_intr(dev, scp->irq, 0, gusc_intr, scp, &ih, NULL); - } + if (scp->irq != NULL) + bus_setup_intr(dev, scp->irq, INTR_TYPE_AV, gusc_intr, scp, &ih, NULL); bus_generic_attach(dev); return (0); @@ -422,8 +421,7 @@ gusc_release_resource(device_t bus, device_t child, int type, int rid, static int gusc_setup_intr(device_t dev, device_t child, struct resource *irq, - int flags, driver_intr_t *intr, void *arg, - void **cookiep, lwkt_serialize_t serializer) + int flags, driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp = (sc_p)device_get_softc(dev); devclass_t devclass; @@ -439,7 +437,7 @@ gusc_setup_intr(device_t dev, device_t child, struct resource *irq, return 0; } return bus_generic_setup_intr(dev, child, irq, flags, intr, - arg, cookiep, serializer); + arg, cookiep, NULL); } static device_t @@ -505,8 +503,10 @@ alloc_resource(sc_p scp) } 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); + scp->irq = + bus_alloc_resource_any(scp->dev, SYS_RES_IRQ, + &scp->irq_rid, + RF_ACTIVE|RF_SHAREABLE); if (scp->irq == NULL) return (1); scp->irq_alloced = 0; @@ -515,8 +515,11 @@ alloc_resource(sc_p scp) 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); + scp->drq[i] = + bus_alloc_resource_any( + scp->dev, SYS_RES_DRQ, + &scp->drq_rid[i], + 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], @@ -652,7 +655,8 @@ static driver_t gusc_driver = { * 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); +DRIVER_MODULE(snd_gusc, acpi, gusc_driver, gusc_devclass, 0, 0); +MODULE_DEPEND(snd_gusc, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_gusc, 1); diff --git a/sys/dev/sound/isa/mss.c b/sys/dev/sound/isa/mss.c index fcf49f6c10..b35e2bb1e5 100644 --- a/sys/dev/sound/isa/mss.c +++ b/sys/dev/sound/isa/mss.c @@ -1,6 +1,6 @@ -/* +/*- * Copyright (c) 2001 George Reid - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 1999 Cameron Grant * Copyright Luigi Rizzo, 1997,1998 * Copyright by Hannu Savolainen 1994, 1995 * All rights reserved. @@ -26,23 +26,24 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/mss.c,v 1.48.2.11 2002/12/24 21:17:41 semenu Exp $ - * $DragonFly: src/sys/dev/sound/isa/mss.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/mss.c,v 1.95.2.3 2006/04/04 17:30:59 ariff Exp $ + * $DragonFly: src/sys/dev/sound/isa/mss.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/mss.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/mss.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); /* board-specific include files */ #include #include #include +#include + #include "mixer_if.h" #define MSS_DEFAULT_BUFSZ (4096) -#define abs(x) (((x) < 0) ? -(x) : (x)) #define MSS_INDEXED_REGS 0x20 #define OPL_INDEXED_REGS 0x19 @@ -69,7 +70,7 @@ struct mss_info { int drq2_rid; void *ih; bus_dma_tag_t parent_dmat; - void *lock; + struct spinlock *lock; char mss_indexed_regs[MSS_INDEXED_REGS]; char opl_indexed_regs[OPL_INDEXED_REGS]; @@ -94,7 +95,9 @@ static driver_intr_t mss_intr; /* prototypes for local functions */ static int mss_detect(device_t dev, struct mss_info *mss); +#ifndef PC98 static int opti_detect(device_t dev, struct mss_info *mss); +#endif static char *ymf_test(device_t dev, struct mss_info *mss); static void ad_unmute(struct mss_info *mss); @@ -113,7 +116,9 @@ static void ad_leave_MCE(struct mss_info *mss); /* OPTi-specific functions */ static void opti_write(struct mss_info *mss, u_char reg, u_char data); +#ifndef PC98 static u_char opti_read(struct mss_info *mss, u_char reg); +#endif static int opti_init(device_t dev, struct mss_info *mss); /* io primitives */ @@ -161,6 +166,7 @@ static struct pcmchan_caps opti931_caps = {4000, 48000, opti931_fmt, 0}; #define MD_AD1848 0x91 #define MD_AD1845 0x92 #define MD_CS42XX 0xA1 +#define MD_CS423X 0xA2 #define MD_OPTI930 0xB0 #define MD_OPTI931 0xB1 #define MD_OPTI925 0xB2 @@ -315,20 +321,23 @@ mss_alloc_resources(struct mss_info *mss, device_t dev) { 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); + mss->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &mss->io_rid, RF_ACTIVE); if (!mss->irq) - mss->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &mss->irq_rid, - 0, ~0, 1, RF_ACTIVE); + mss->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &mss->irq_rid, RF_ACTIVE); if (!mss->drq1) - mss->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &mss->drq1_rid, - 0, ~0, 1, RF_ACTIVE); + mss->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &mss->drq1_rid, + 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); + mss->conf_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &mss->conf_rid, + 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); + mss->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &mss->drq2_rid, + RF_ACTIVE); if (!mss->io_base || !mss->drq1 || !mss->irq) ok = 0; if (mss->conf_rid >= 0 && !mss->conf_base) ok = 0; @@ -705,8 +714,8 @@ mss_init(struct mss_info *mss, device_t dev) /* end of reset */ rid = 0; - alt = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, - 0, ~0, 1, RF_ACTIVE); + alt = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, + RF_ACTIVE); if (alt == NULL) { kprintf("XXX couldn't init GUS PnP/MAX\n"); break; @@ -792,11 +801,15 @@ mss_intr(void *arg) c &= ~served; if (sndbuf_runsz(mss->pch.buffer) && (c & 0x10)) { served |= 0x10; + mss_unlock(mss); chn_intr(mss->pch.channel); + mss_lock(mss); } if (sndbuf_runsz(mss->rch.buffer) && (c & 0x20)) { served |= 0x20; + mss_unlock(mss); chn_intr(mss->rch.channel); + mss_lock(mss); } /* now ack the interrupt */ if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */ @@ -962,11 +975,14 @@ mss_speed(struct mss_chinfo *ch, int speed) {8000, 5512, 16000, 11025, 27429, 18900, 32000, 22050, -1, 37800, -1, 44100, 48000, 33075, 9600, 6615}; +#define abs(i) (i < 0 ? -i : i) for (i = 1; i < 16; i++) if (speeds[i] > 0 && abs(speed-speeds[i]) < abs(speed-speeds[sel])) sel = i; +#undef abs speed = speeds[sel]; ad_write(mss, 8, (ad_read(mss, 8) & 0xf0) | sel); + ad_wait_init(mss, 10000); } ad_leave_MCE(mss); @@ -1006,7 +1022,11 @@ mss_format(struct mss_chinfo *ch, u_int32_t format) 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_wait_init(mss, 10000); + if (ad_read(mss, 12) & 0x40) { /* mode2? */ + ad_write(mss, 28, arg); /* capture mode */ + ad_wait_init(mss, 10000); + } ad_leave_MCE(mss); return format; } @@ -1101,15 +1121,23 @@ opti931_intr(void *arg) if (reason & 1) { DEB(kprintf("one more try...\n");) if (--loops) goto again; - else DDB(kprintf("intr, but mc11 not set\n");) + else BVDDB(kprintf("intr, but mc11 not set\n");) } if (loops == 0) BVDDB(kprintf("intr, nothing in mcir11 0x%02x\n", mc11)); mss_unlock(mss); 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); + if (sndbuf_runsz(mss->rch.buffer) && (mc11 & 8)) { + mss_unlock(mss); + chn_intr(mss->rch.channel); + mss_lock(mss); + } + if (sndbuf_runsz(mss->pch.buffer) && (mc11 & 4)) { + mss_unlock(mss); + chn_intr(mss->pch.channel); + mss_lock(mss); + } opti_wr(mss, 11, ~mc11); /* ack */ if (--loops) goto again; mss_unlock(mss); @@ -1128,8 +1156,9 @@ msschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, mss->parent_dmat, mss->bufsize) == -1) return NULL; - sndbuf_isadmasetup(ch->buffer, (dir == PCMDIR_PLAY)? mss->drq1 : mss->drq2); + if (sndbuf_alloc(ch->buffer, mss->parent_dmat, mss->bufsize) != 0) + return NULL; + sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? mss->drq1 : mss->drq2); return ch; } @@ -1179,7 +1208,7 @@ msschan_trigger(kobj_t obj, void *data, int go) if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - sndbuf_isadma(ch->buffer, go); + sndbuf_dma(ch->buffer, go); mss_lock(mss); mss_trigger(ch, go); mss_unlock(mss); @@ -1190,7 +1219,7 @@ static int msschan_getptr(kobj_t obj, void *data) { struct mss_chinfo *ch = data; - return sndbuf_isadmaptr(ch->buffer); + return sndbuf_dmaptr(ch->buffer); } static struct pcmchan_caps * @@ -1347,6 +1376,7 @@ mss_detect(device_t dev, struct mss_info *mss) name = "AD1848"; mss->bd_id = MD_AD1848; /* AD1848 or CS4248 */ +#ifndef PC98 if (opti_detect(dev, mss)) { switch (mss->bd_id) { case MD_OPTI924: @@ -1359,6 +1389,7 @@ mss_detect(device_t dev, struct mss_info *mss) kprintf("Found OPTi device %s\n", name); if (opti_init(dev, mss) == 0) goto gotit; } +#endif /* * Check that the I/O address is in use. @@ -1374,7 +1405,7 @@ mss_detect(device_t dev, struct mss_info *mss) if ((tmp = io_rd(mss, MSS_INDEX)) & MSS_IDXBUSY) DELAY(10000); else break; - if (i >= 10) { /* Not a AD1848 */ + if (i >= 10) { /* Not an AD1848 */ BVDDB(kprintf("mss_detect, busy still set (0x%02x)\n", tmp)); goto no; } @@ -1565,6 +1596,7 @@ no: return ENXIO; } +#ifndef PC98 static int opti_detect(device_t dev, struct mss_info *mss) { @@ -1610,6 +1642,7 @@ opti_detect(device_t dev, struct mss_info *mss) } return 0; } +#endif static char * ymf_test(device_t dev, struct mss_info *mss) @@ -1644,6 +1677,10 @@ ymf_test(device_t dev, struct mss_info *mss) 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; } @@ -1667,16 +1704,23 @@ mss_doattach(device_t dev, struct mss_info *mss) 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[pdma] == -1) goto no; bits |= pdma_bits[pdma]; @@ -1693,10 +1737,10 @@ mss_doattach(device_t dev, struct mss_info *mss) mixer_init(dev, (mss->bd_id == MD_YM0020)? &ymmix_mixer_class : &mssmix_mixer_class, mss); switch (mss->bd_id) { case MD_OPTI931: - snd_setup_intr(dev, mss->irq, INTR_MPSAFE, opti931_intr, mss, &mss->ih, NULL); + snd_setup_intr(dev, mss->irq, 0, opti931_intr, mss, &mss->ih); break; default: - snd_setup_intr(dev, mss->irq, INTR_MPSAFE, mss_intr, mss, &mss->ih, NULL); + snd_setup_intr(dev, mss->irq, 0, mss_intr, mss, &mss->ih); } if (pdma == rdma) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); @@ -1705,8 +1749,8 @@ mss_doattach(device_t dev, struct mss_info *mss) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/mss->bufsize, /*nsegments*/1, - /*maxsegz*/0x3ffff, - /*flags*/0, &mss->parent_dmat) != 0) { + /*maxsegz*/0x3ffff, /*flags*/0, + &mss->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } @@ -1794,8 +1838,7 @@ mss_resume(device_t dev) mss = pcm_getdevinfo(dev); - if (mss->bd_id == MD_YM0020) - { + if(mss->bd_id == MD_YM0020 || mss->bd_id == MD_CS423X) { /* This works on a Toshiba Libretto 100CT. */ for (i = 0; i < MSS_INDEXED_REGS; i++) ad_write(mss, i, mss->mss_indexed_regs[i]); @@ -1803,6 +1846,15 @@ mss_resume(device_t dev) conf_wr(mss, i, mss->opl_indexed_regs[i]); mss_intr(mss); } + + if (mss->bd_id == MD_CS423X) { + /* Needed on IBM Thinkpad 600E */ + mss_lock(mss); + mss_format(&mss->pch, mss->pch.channel->format); + mss_speed(&mss->pch, mss->pch.channel->speed); + mss_unlock(mss); + } + return 0; } @@ -1824,7 +1876,7 @@ mss_suspend(device_t dev) mss = pcm_getdevinfo(dev); - if(mss->bd_id == MD_YM0020) + if(mss->bd_id == MD_YM0020 || mss->bd_id == MD_CS423X) { /* this stops playback. */ conf_wr(mss, 0x12, 0x0c); @@ -1855,7 +1907,7 @@ static driver_t mss_driver = { }; DRIVER_MODULE(snd_mss, isa, mss_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_mss, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_mss, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_mss, 1); static int @@ -1866,8 +1918,7 @@ azt2320_mss_mode(struct mss_info *mss, device_t dev) rid = 0; ret = -1; - sbport = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, - 0, ~0, 1, RF_ACTIVE); + sbport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (sbport) { for (i = 0; i < 1000; i++) { if ((port_rd(sbport, SBDSP_STATUS) & 0x80)) @@ -1941,6 +1992,7 @@ pnpmss_attach(device_t dev) case 0x0000630e: /* CSC0000 */ case 0x0001630e: /* CSC0100 */ mss->bd_flags |= BD_F_MSS_OFFSET; + mss->bd_id = MD_CS423X; break; case 0x2100a865: /* YHM0021 */ @@ -1974,8 +2026,10 @@ pnpmss_attach(device_t dev) mss->conf_rid = 3; mss->bd_id = MD_OPTI924; mss->bd_flags |= BD_F_924PNP; - if(opti_init(dev, mss) != 0) + if(opti_init(dev, mss) != 0) { + kfree(mss, M_DEVBUF); return ENXIO; + } break; case 0x1022b839: /* NMX2210 */ @@ -1984,8 +2038,10 @@ pnpmss_attach(device_t dev) case 0x01005407: /* AZT0001 */ /* put into MSS mode first (snatched from NetBSD) */ - if (azt2320_mss_mode(mss, dev) == -1) + if (azt2320_mss_mode(mss, dev) == -1) { + kfree(mss, M_DEVBUF); return ENXIO; + } mss->bd_flags |= BD_F_MSS_OFFSET; mss->io_rid = 2; @@ -2124,6 +2180,7 @@ opti_write(struct mss_info *mss, u_char reg, u_char val) } } +#ifndef PC98 u_char opti_read(struct mss_info *mss, u_char reg) { @@ -2147,6 +2204,7 @@ opti_read(struct mss_info *mss, u_char reg) } return -1; } +#endif static device_method_t pnpmss_methods[] = { /* Device interface */ @@ -2166,7 +2224,8 @@ static driver_t pnpmss_driver = { }; DRIVER_MODULE(snd_pnpmss, isa, pnpmss_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_pnpmss, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +DRIVER_MODULE(snd_pnpmss, acpi, pnpmss_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_pnpmss, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_pnpmss, 1); static int @@ -2250,7 +2309,7 @@ static driver_t guspcm_driver = { }; DRIVER_MODULE(snd_guspcm, gusc, guspcm_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_guspcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_guspcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_guspcm, 1); diff --git a/sys/dev/sound/isa/mss.h b/sys/dev/sound/isa/mss.h index de91131351..ca586c341e 100644 --- a/sys/dev/sound/isa/mss.h +++ b/sys/dev/sound/isa/mss.h @@ -1,4 +1,4 @@ -/* +/*- * file: mss.h * * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) @@ -8,6 +8,35 @@ * */ +/*- + * 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: src/sys/dev/sound/isa/mss.h,v 1.12 2005/01/06 01:43:17 imp Exp $ + * $DragonFly: src/sys/dev/sound/isa/mss.h,v 1.4 2007/01/04 21:47:02 corecode Exp $ + */ + /* * @@ -191,12 +220,20 @@ 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 */ +MIX_ENT(SOUND_MIXER_SYNTH, 4, 1, 0, 5, 5, 1, 0, 5), +#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 */ +MIX_ENT(SOUND_MIXER_CD, 2, 1, 0, 5, 3, 1, 0, 5), +#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), @@ -270,35 +307,6 @@ MIX_NONE(SOUND_MIXER_LINE3), 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: src/sys/dev/sound/isa/mss.h,v 1.7.2.6 2002/04/22 15:49:31 cg Exp $ - * $DragonFly: src/sys/dev/sound/isa/mss.h,v 1.3 2005/09/01 00:18:24 swildner Exp $ - */ - /* * Register definitions for the Yamaha OPL3-SA[23x]. */ diff --git a/sys/dev/sound/isa/sb.h b/sys/dev/sound/isa/sb.h index 0cb70692c9..35c29f8817 100644 --- a/sys/dev/sound/isa/sb.h +++ b/sys/dev/sound/isa/sb.h @@ -1,7 +1,7 @@ /* * file: sbcard.h - * $FreeBSD: src/sys/dev/sound/isa/sb.h,v 1.12.2.3 2002/04/22 15:49:31 cg Exp $ - * $DragonFly: src/sys/dev/sound/isa/sb.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/isa/sb.h,v 1.15 2004/05/13 11:32:54 truckman Exp $ + * $DragonFly: src/sys/dev/sound/isa/sb.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef SB_H @@ -9,6 +9,7 @@ struct sbc_softc; void sbc_lock(struct sbc_softc *); +void sbc_lockassert(struct sbc_softc *); void sbc_unlock(struct sbc_softc *); /* diff --git a/sys/dev/sound/isa/sb16.c b/sys/dev/sound/isa/sb16.c index 73031a22c2..1858a7e1ac 100644 --- a/sys/dev/sound/isa/sb16.c +++ b/sys/dev/sound/isa/sb16.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, @@ -28,8 +28,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/sb16.c,v 1.64.2.7 2002/12/24 21:17:42 semenu Exp $ - * $DragonFly: src/sys/dev/sound/isa/sb16.c,v 1.7 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/sb16.c,v 1.90.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/isa/sb16.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -37,9 +37,11 @@ #include #include +#include + #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sb16.c,v 1.7 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sb16.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); #define SB16_BUFFSIZE 4096 #define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) @@ -124,6 +126,12 @@ sb_lock(struct sb_info *sb) { sbc_lock(device_get_softc(sb->parent_dev)); } +static void +sb_lockassert(struct sb_info *sb) { + + sbc_lockassert(device_get_softc(sb->parent_dev)); +} + static void sb_unlock(struct sb_info *sb) { @@ -167,7 +175,7 @@ sb_dspwr(struct sb_info *sb, u_char val) return 1; } } -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 +#if __FreeBSD_version > 500000 if (curthread->td_intr_nesting_level == 0) kprintf("sb_dspwr(0x%02x) timed out.\n", val); #endif @@ -204,7 +212,7 @@ sb_cmd2(struct sb_info *sb, u_char cmd, int val) #if 0 kprintf("sb_cmd2: %x, %x\n", cmd, val); #endif - sb_lock(sb); + sb_lockassert(sb); r = 0; if (sb_dspwr(sb, cmd)) { if (sb_dspwr(sb, val & 0xff)) { @@ -213,7 +221,6 @@ sb_cmd2(struct sb_info *sb, u_char cmd, int val) } } } - sb_unlock(sb); return r; } @@ -238,12 +245,11 @@ sb_getmixer(struct sb_info *sb, u_int port) { int val; - sb_lock(sb); + sb_lockassert(sb); sb_wr(sb, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); val = sb_rd(sb, SB_MIX_DATA); DELAY(10); - sb_unlock(sb); return val; } @@ -267,12 +273,11 @@ sb_reset_dsp(struct sb_info *sb) { u_char b; - sb_lock(sb); + sb_lockassert(sb); sb_wr(sb, SBDSP_RST, 3); DELAY(100); sb_wr(sb, SBDSP_RST, 0); b = sb_get_byte(sb); - sb_unlock(sb); if (b != 0xAA) { DEB(kprintf("sb_reset_dsp 0x%lx failed\n", rman_get_start(sb->io_base))); @@ -326,6 +331,19 @@ sb16mix_init(struct snd_mixer *m) return 0; } +static int +rel2abs_volume(int x, int max) +{ + int temp; + + temp = ((x * max) + 50) / 100; + if (temp > max) + temp = max; + else if (temp < 0) + temp = 0; + return (temp); +} + static int sb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { @@ -336,8 +354,8 @@ sb16mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) e = &sb16_mixtab[dev]; max = (1 << e->bits) - 1; - left = (left * max) / 100; - right = (right * max) / 100; + left = rel2abs_volume(left, max); + right = rel2abs_volume(right, max); sb_setmixer(sb, e->reg, left << e->ofs); if (e->stereo) @@ -355,23 +373,32 @@ static int sb16mix_setrecsrc(struct snd_mixer *m, u_int32_t src) { struct sb_info *sb = mix_getdevinfo(m); - u_char recdev; + u_char recdev_l, recdev_r; - recdev = 0; - if (src & SOUND_MASK_MIC) - recdev |= 0x01; /* mono mic */ + recdev_l = 0; + recdev_r = 0; + if (src & SOUND_MASK_MIC) { + recdev_l |= 0x01; /* mono mic */ + recdev_r |= 0x01; + } - if (src & SOUND_MASK_CD) - recdev |= 0x06; /* l+r cd */ + if (src & SOUND_MASK_CD) { + recdev_l |= 0x04; /* l cd */ + recdev_r |= 0x02; /* r cd */ + } - if (src & SOUND_MASK_LINE) - recdev |= 0x18; /* l+r line */ + if (src & SOUND_MASK_LINE) { + recdev_l |= 0x10; /* l line */ + recdev_r |= 0x08; /* r line */ + } - if (src & SOUND_MASK_SYNTH) - recdev |= 0x60; /* l+r midi */ + if (src & SOUND_MASK_SYNTH) { + recdev_l |= 0x40; /* l midi */ + recdev_r |= 0x20; /* r midi */ + } - sb_setmixer(sb, SB16_IMASK_L, recdev); - sb_setmixer(sb, SB16_IMASK_R, recdev); + sb_setmixer(sb, SB16_IMASK_L, recdev_l); + sb_setmixer(sb, SB16_IMASK_R, recdev_r); /* Switch on/off FM tuner source */ if (src & SOUND_MASK_LINE1) @@ -441,19 +468,23 @@ sb16_alloc_resources(struct sb_info *sb, device_t dev) rid = 0; if (!sb->io_base) - sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + sb->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &rid, RF_ACTIVE); rid = 0; if (!sb->irq) - sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE); + sb->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); rid = 0; if (!sb->drq1) - sb->drq1 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE); + sb->drq1 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, + RF_ACTIVE); rid = 1; if (!sb->drq2) - sb->drq2 = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE); + sb->drq2 = bus_alloc_resource_any(dev, SYS_RES_DRQ, &rid, + RF_ACTIVE); if (sb->io_base && sb->drq1 && sb->irq) { isa_dma_acquire(rman_get_start(sb->drq1)); @@ -475,7 +506,7 @@ static void sb_intr(void *arg) { struct sb_info *sb = (struct sb_info *)arg; - int reason = 3, c; + int reason, c; /* * The Vibra16X has separate flags for 8 and 16 bit transfers, but @@ -543,43 +574,44 @@ sb_setup(struct sb_info *sb) sb_lock(sb); if (sb->bd_flags & BD_F_DMARUN) - sndbuf_isadma(sb->pch.buffer, PCMTRIG_STOP); + sndbuf_dma(sb->pch.buffer, PCMTRIG_STOP); if (sb->bd_flags & BD_F_DMARUN2) - sndbuf_isadma(sb->rch.buffer, PCMTRIG_STOP); + sndbuf_dma(sb->rch.buffer, PCMTRIG_STOP); sb->bd_flags &= ~(BD_F_DMARUN | BD_F_DMARUN2); sb_reset_dsp(sb); if (sb->bd_flags & BD_F_SB16X) { + /* full-duplex doesn't work! */ pprio = sb->pch.run? 1 : 0; - sndbuf_isadmasetup(sb->pch.buffer, pprio? sb->drq1 : NULL); + sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2); sb->pch.dch = pprio? 1 : 0; - sndbuf_isadmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1); + sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1); sb->rch.dch = pprio? 2 : 1; } else { if (sb->pch.run && sb->rch.run) { pprio = (sb->rch.fmt & AFMT_16BIT)? 0 : 1; - sndbuf_isadmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1); + sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq2 : sb->drq1); sb->pch.dch = pprio? 2 : 1; - sndbuf_isadmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2); + sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq1 : sb->drq2); sb->rch.dch = pprio? 1 : 2; } else { if (sb->pch.run) { - sndbuf_isadmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); + sndbuf_dmasetup(sb->pch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); sb->pch.dch = (sb->pch.fmt & AFMT_16BIT)? 2 : 1; - sndbuf_isadmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); + sndbuf_dmasetup(sb->rch.buffer, (sb->pch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); sb->rch.dch = (sb->pch.fmt & AFMT_16BIT)? 1 : 2; } else if (sb->rch.run) { - sndbuf_isadmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); + sndbuf_dmasetup(sb->pch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq1 : sb->drq2); sb->pch.dch = (sb->rch.fmt & AFMT_16BIT)? 1 : 2; - sndbuf_isadmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); + sndbuf_dmasetup(sb->rch.buffer, (sb->rch.fmt & AFMT_16BIT)? sb->drq2 : sb->drq1); sb->rch.dch = (sb->rch.fmt & AFMT_16BIT)? 2 : 1; } } } - sndbuf_isadmasetdir(sb->pch.buffer, PCMDIR_PLAY); - sndbuf_isadmasetdir(sb->rch.buffer, PCMDIR_REC); + sndbuf_dmasetdir(sb->pch.buffer, PCMDIR_PLAY); + sndbuf_dmasetdir(sb->rch.buffer, PCMDIR_REC); /* kprintf("setup: [pch = %d, pfmt = %d, pgo = %d] [rch = %d, rfmt = %d, rgo = %d]\n", @@ -607,7 +639,7 @@ sb_setup(struct sb_info *sb) v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; sb_cmd2(sb, v, l); - sndbuf_isadma(ch->buffer, PCMTRIG_START); + sndbuf_dma(ch->buffer, PCMTRIG_START); sb->bd_flags |= BD_F_DMARUN; } @@ -632,7 +664,7 @@ sb_setup(struct sb_info *sb) v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; sb_cmd2(sb, v, l); - sndbuf_isadma(ch->buffer, PCMTRIG_START); + sndbuf_dma(ch->buffer, PCMTRIG_START); sb->bd_flags |= BD_F_DMARUN2; } sb_unlock(sb); @@ -652,7 +684,7 @@ sb16chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) == -1) + if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) != 0) return NULL; return ch; @@ -713,7 +745,7 @@ sb16chan_getptr(kobj_t obj, void *data) { struct sb_chinfo *ch = data; - return sndbuf_isadmaptr(ch->buffer); + return sndbuf_dmaptr(ch->buffer); } static struct pcmchan_caps * @@ -794,13 +826,17 @@ sb16_attach(device_t dev) sb->bd_flags = (ver & 0xffff0000) >> 16; sb->bufsize = pcm_getbuffersize(dev, 4096, SB16_BUFFSIZE, 65536); - if (sb16_alloc_resources(sb, dev)) + if (sb16_alloc_resources(sb, dev)) goto no; - if (sb_reset_dsp(sb)) + sb_lock(sb); + if (sb_reset_dsp(sb)) { + sb_unlock(sb); goto no; + } + sb_unlock(sb); if (mixer_init(dev, &sb16mix_mixer_class, sb)) goto no; - if (snd_setup_intr(dev, sb->irq, INTR_MPSAFE, sb_intr, sb, &sb->ih, NULL)) + if (snd_setup_intr(dev, sb->irq, 0, sb_intr, sb, &sb->ih)) goto no; if (sb->bd_flags & BD_F_SB16X) @@ -813,8 +849,8 @@ sb16_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sb->bufsize, /*nsegments*/1, - /*maxsegz*/0x3ffff, - /*flags*/0, &sb->parent_dmat) != 0) { + /*maxsegz*/0x3ffff, /*flags*/0, + &sb->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } @@ -824,9 +860,10 @@ sb16_attach(device_t dev) else status2[0] = '\0'; - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %ud", + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %u %s", rman_get_start(sb->io_base), rman_get_start(sb->irq), - rman_get_start(sb->drq1), status2, sb->bufsize); + rman_get_start(sb->drq1), status2, sb->bufsize, + PCM_KLDSTRING(snd_sb16)); if (pcm_register(dev, sb, 1, 1)) goto no; @@ -873,6 +910,6 @@ static driver_t sb16_driver = { }; DRIVER_MODULE(snd_sb16, sbc, sb16_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_sb16, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_sb16, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_DEPEND(snd_sb16, snd_sbc, 1, 1, 1); MODULE_VERSION(snd_sb16, 1); diff --git a/sys/dev/sound/isa/sb8.c b/sys/dev/sound/isa/sb8.c index 62ddeb27e6..ff319f9000 100644 --- a/sys/dev/sound/isa/sb8.c +++ b/sys/dev/sound/isa/sb8.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, @@ -28,8 +28,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/sb8.c,v 1.62.2.5 2002/12/24 21:17:42 semenu Exp $ - * $DragonFly: src/sys/dev/sound/isa/sb8.c,v 1.6 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/sb8.c,v 1.79.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/isa/sb8.c,v 1.7 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -37,9 +37,11 @@ #include #include +#include + #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sb8.c,v 1.6 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sb8.c,v 1.7 2007/01/04 21:47:02 corecode Exp $"); #define SB_DEFAULT_BUFSZ 4096 @@ -287,13 +289,16 @@ sb_alloc_resources(struct sb_info *sb, device_t dev) rid = 0; if (!sb->io_base) - sb->io_base = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + sb->io_base = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &rid, RF_ACTIVE); rid = 0; if (!sb->irq) - sb->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE); + sb->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &rid, RF_ACTIVE); rid = 0; if (!sb->drq) - sb->drq = bus_alloc_resource(dev, SYS_RES_DRQ, &rid, 0, ~0, 1, RF_ACTIVE); + sb->drq = bus_alloc_resource_any(dev, SYS_RES_DRQ, + &rid, RF_ACTIVE); if (sb->io_base && sb->drq && sb->irq) { isa_dma_acquire(rman_get_start(sb->drq)); @@ -473,11 +478,17 @@ sb_intr(void *arg) struct sb_info *sb = (struct sb_info *)arg; sb_lock(sb); - if (sndbuf_runsz(sb->pch.buffer) > 0) + if (sndbuf_runsz(sb->pch.buffer) > 0) { + sb_unlock(sb); chn_intr(sb->pch.channel); + sb_lock(sb); + } - if (sndbuf_runsz(sb->rch.buffer) > 0) + if (sndbuf_runsz(sb->rch.buffer) > 0) { + sb_unlock(sb); chn_intr(sb->rch.channel); + sb_lock(sb); + } sb_rd(sb, DSP_DATA_AVAIL); /* int ack */ sb_unlock(sb); @@ -562,8 +573,16 @@ sb_stop(struct sb_chinfo *ch) sb_lock(sb); if (sb->bd_flags & BD_F_HISPEED) sb_reset_dsp(sb); - else + else { +#if 0 + /* + * NOTE: DSP_CMD_DMAEXIT_8 does not work with old + * soundblaster. + */ sb_cmd(sb, DSP_CMD_DMAEXIT_8); +#endif + sb_reset_dsp(sb); + } if (play) sb_cmd(sb, DSP_CMD_SPKOFF); /* speaker off */ @@ -583,9 +602,9 @@ sbchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c ch->channel = c; ch->dir = dir; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) == -1) + if (sndbuf_alloc(ch->buffer, sb->parent_dmat, sb->bufsize) != 0) return NULL; - sndbuf_isadmasetup(ch->buffer, sb->drq); + sndbuf_dmasetup(ch->buffer, sb->drq); return ch; } @@ -624,7 +643,7 @@ sbchan_trigger(kobj_t obj, void *data, int go) if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; - sndbuf_isadma(ch->buffer, go); + sndbuf_dma(ch->buffer, go); if (go == PCMTRIG_START) sb_start(ch); else @@ -637,7 +656,7 @@ sbchan_getptr(kobj_t obj, void *data) { struct sb_chinfo *ch = data; - return sndbuf_isadmaptr(ch->buffer); + return sndbuf_dmaptr(ch->buffer); } static struct pcmchan_caps * @@ -714,7 +733,7 @@ sb_attach(device_t dev) goto no; if (mixer_init(dev, (sb->bd_id < 0x300)? &sbmix_mixer_class : &sbpromix_mixer_class, sb)) goto no; - if (snd_setup_intr(dev, sb->irq, INTR_MPSAFE, sb_intr, sb, &sb->ih, NULL)) + if (snd_setup_intr(dev, sb->irq, 0, sb_intr, sb, &sb->ih)) goto no; pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); @@ -724,14 +743,15 @@ sb_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sb->bufsize, /*nsegments*/1, - /*maxsegz*/0x3ffff, - /*flags*/0, &sb->parent_dmat) != 0) { + /*maxsegz*/0x3ffff, /*flags*/0, + &sb->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld bufsz %u", - rman_get_start(sb->io_base), rman_get_start(sb->irq), rman_get_start(sb->drq), sb->bufsize); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld bufsz %u %s", + rman_get_start(sb->io_base), rman_get_start(sb->irq), + rman_get_start(sb->drq), sb->bufsize, PCM_KLDSTRING(snd_sb8)); if (pcm_register(dev, sb, 1, 1)) goto no; @@ -778,7 +798,7 @@ static driver_t sb_driver = { }; DRIVER_MODULE(snd_sb8, sbc, sb_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_sb8, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_sb8, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_DEPEND(snd_sb8, snd_sbc, 1, 1, 1); MODULE_VERSION(snd_sb8, 1); diff --git a/sys/dev/sound/isa/sbc.c b/sys/dev/sound/isa/sbc.c index e853dbcbc8..6b7057a290 100644 --- a/sys/dev/sound/isa/sbc.c +++ b/sys/dev/sound/isa/sbc.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * @@ -23,15 +23,17 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/isa/sbc.c,v 1.19.2.12 2002/12/24 21:17:42 semenu Exp $ - * $DragonFly: src/sys/dev/sound/isa/sbc.c,v 1.8 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/isa/sbc.c,v 1.44.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/isa/sbc.c,v 1.9 2007/01/04 21:47:02 corecode Exp $ */ #include #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sbc.c,v 1.8 2006/12/22 23:26:25 swildner Exp $"); +#include + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sbc.c,v 1.9 2007/01/04 21:47:02 corecode Exp $"); #define IO_MAX 3 #define IRQ_MAX 1 @@ -67,7 +69,7 @@ struct sbc_softc { void *ih[IRQ_MAX]; - void *lock; + struct spinlock *lock; u_int32_t bd_ver; }; @@ -82,7 +84,7 @@ 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, lwkt_serialize_t serializer); + void **cookiep); static int sbc_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie); @@ -93,6 +95,20 @@ 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); @@ -118,6 +134,12 @@ sbc_lock(struct sbc_softc *scp) snd_mtxlock(scp->lock); } +void +sbc_lockassert(struct sbc_softc *scp) +{ + snd_mtxassert(scp->lock); +} + void sbc_unlock(struct sbc_softc *scp) { @@ -171,12 +193,12 @@ sb_cmd(struct resource *io, u_char val) static void sb_setmixer(struct resource *io, u_int port, u_int value) { - crit_enter(); + crit_enter(); 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); - crit_exit(); + crit_exit(); } static u_int @@ -238,6 +260,7 @@ static struct isa_pnp_id sbc_ids[] = { {0x81167316, "ESS ES1681"}, /* ESS1681 */ {0x02017316, "ESS ES1688"}, /* ESS1688 */ + {0x68097316, "ESS ES1688"}, /* ESS1688 */ {0x68187316, "ESS ES1868"}, /* ESS1868 */ {0x03007316, "ESS ES1869"}, /* ESS1869 */ {0x69187316, "ESS ES1869"}, /* ESS1869 */ @@ -268,9 +291,17 @@ sbc_probe(device_t dev) 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; @@ -362,6 +393,20 @@ sbc_attach(device_t dev) /* 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; @@ -372,6 +417,7 @@ sbc_attach(device_t dev) } 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) kprintf(", %d", dh); @@ -392,7 +438,7 @@ sbc_attach(device_t dev) err = "setup_intr"; for (i = 0; i < IRQ_MAX; i++) { scp->ihl[i].parent = scp; - if (snd_setup_intr(dev, scp->irq[i], INTR_MPSAFE, sbc_intr, &scp->ihl[i], &scp->ih[i], NULL)) + if (snd_setup_intr(dev, scp->irq[i], 0, sbc_intr, &scp->ihl[i], &scp->ih[i])) goto bad; } @@ -459,14 +505,12 @@ sbc_intr(void *p) static int sbc_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *intr, void *arg, - void **cookiep, lwkt_serialize_t serializer) + void **cookiep) { struct sbc_softc *scp = device_get_softc(dev); struct sbc_ihl *ihl = NULL; int i, ret; - KKASSERT(serializer == NULL); /* not yet supported */ - sbc_lock(scp); i = 0; while (i < IRQ_MAX) { @@ -523,13 +567,22 @@ sbc_alloc_resource(device_t bus, device_t child, int type, int *rid, 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: @@ -630,9 +683,23 @@ alloc_resource(struct sbc_softc *scp) 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; @@ -641,8 +708,10 @@ alloc_resource(struct sbc_softc *scp) 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); + scp->drq[i] = bus_alloc_resource_any(scp->dev, + SYS_RES_DRQ, + &scp->drq_rid[i], + RF_ACTIVE); if (i == 0 && scp->drq[i] == NULL) return (1); scp->drq_alloced[i] = 0; @@ -651,8 +720,10 @@ alloc_resource(struct sbc_softc *scp) 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); + scp->irq[i] = bus_alloc_resource_any(scp->dev, + SYS_RES_IRQ, + &scp->irq_rid[i], + RF_ACTIVE); if (i == 0 && scp->irq[i] == NULL) return (1); scp->irq_alloced[i] = 0; @@ -721,5 +792,6 @@ static driver_t sbc_driver = { /* 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); +DRIVER_MODULE(snd_sbc, acpi, sbc_driver, sbc_devclass, 0, 0); +MODULE_DEPEND(snd_sbc, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_sbc, 1); diff --git a/sys/dev/sound/isa/sndbuf_dma.c b/sys/dev/sound/isa/sndbuf_dma.c new file mode 100644 index 0000000000..5eae8f06d0 --- /dev/null +++ b/sys/dev/sound/isa/sndbuf_dma.c @@ -0,0 +1,106 @@ +/*- + * 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: src/sys/dev/sound/isa/sndbuf_dma.c,v 1.3 2005/01/06 01:43:17 imp Exp $ + * $DragonFly: src/sys/dev/sound/isa/sndbuf_dma.c,v 1.1 2007/01/04 21:47:02 corecode Exp $ + */ + +#include + +#include + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/isa/sndbuf_dma.c,v 1.1 2007/01/04 21:47:02 corecode Exp $"); + +int +sndbuf_dmasetup(struct snd_dbuf *b, struct resource *drq) +{ + /* should do isa_dma_acquire/isa_dma_release here */ + if (drq == NULL) { + b->dmachan = -1; + } else { + sndbuf_setflags(b, SNDBUF_F_DMA, 1); + b->dmachan = rman_get_start(drq); + } + return 0; +} + +int +sndbuf_dmasetdir(struct snd_dbuf *b, int dir) +{ + KASSERT(b, ("sndbuf_dmasetdir called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_DMA, ("sndbuf_dmasetdir called on non-ISA buffer")); + + b->dir = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ; + return 0; +} + +void +sndbuf_dma(struct snd_dbuf *b, int go) +{ + KASSERT(b, ("sndbuf_dma called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_DMA, ("sndbuf_dma called on non-ISA buffer")); + + switch (go) { + case PCMTRIG_START: + /* isa_dmainit(b->chan, size); */ + isa_dmastart(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->dmachan); + break; + + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + isa_dmastop(b->dmachan); + isa_dmadone(b->dir | ISADMA_RAW, b->buf, b->bufsize, b->dmachan); + break; + } + + DEB(printf("buf 0x%p ISA DMA %s, channel %d\n", + b, + (go == PCMTRIG_START)? "started" : "stopped", + b->dmachan)); +} + +int +sndbuf_dmaptr(struct snd_dbuf *b) +{ + int i; + + KASSERT(b, ("sndbuf_dmaptr called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_DMA, ("sndbuf_dmaptr called on non-ISA buffer")); + + if (!sndbuf_runsz(b)) + return 0; + i = isa_dmastatus(b->dmachan); + KASSERT(i >= 0, ("isa_dmastatus returned %d", i)); + return b->bufsize - i; +} + +void +sndbuf_dmabounce(struct snd_dbuf *b) +{ + KASSERT(b, ("sndbuf_dmabounce called with b == NULL")); + KASSERT(sndbuf_getflags(b) & SNDBUF_F_DMA, ("sndbuf_dmabounce called on non-ISA buffer")); + + /* tell isa_dma to bounce data in/out */ +} diff --git a/sys/dev/sound/pci/als4000.c b/sys/dev/sound/pci/als4000.c index 76bc8740bd..4283bd722a 100644 --- a/sys/dev/sound/pci/als4000.c +++ b/sys/dev/sound/pci/als4000.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2001 Orion Hodson * All rights reserved. * @@ -23,14 +23,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/als4000.c,v 1.2.2.5 2002/04/22 15:49:31 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/als4000.c,v 1.18.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ /* * als4000.c - driver for the Avance Logic ALS 4000 chipset. * - * The ALS4000 is a effectively an SB16 with a PCI interface. + * The ALS4000 is effectively an SB16 with a PCI interface. * * This driver derives from ALS4000a.PDF, Bart Hartgers alsa driver, and * SB16 register descriptions. @@ -45,7 +45,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/als4000.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); /* Debugging macro's */ #undef DEB @@ -78,6 +78,7 @@ struct sc_info { struct resource *reg, *irq; int regid, irqid; void *ih; + struct spinlock *lock; unsigned int bufsz; struct sc_chinfo pch, rch; @@ -93,7 +94,11 @@ static u_int32_t als_format[] = { 0 }; -static struct pcmchan_caps als_caps = { 4000, 48000, als_format, 0 }; +/* + * I don't believe this rotten soundcard can do 48k, really, + * trust me. + */ +static struct pcmchan_caps als_caps = { 4000, 44100, als_format, 0 }; /* ------------------------------------------------------------------------- */ /* Register Utilities */ @@ -202,6 +207,7 @@ alschan_init(kobj_t obj, void *devinfo, struct sc_info *sc = devinfo; struct sc_chinfo *ch; + snd_mtxlock(sc->lock); if (dir == PCMDIR_PLAY) { ch = &sc->pch; ch->gcr_fifo_status = ALS_GCR_FIFO0_STATUS; @@ -216,9 +222,11 @@ alschan_init(kobj_t obj, void *devinfo, ch->format = AFMT_U8; ch->speed = DSP_DEFAULT_SPEED; ch->buffer = b; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) { + snd_mtxunlock(sc->lock); + + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) return NULL; - } + return ch; } @@ -266,9 +274,12 @@ static int alschan_getptr(kobj_t obj, void *data) { struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; int32_t pos, sz; + snd_mtxlock(sc->lock); pos = als_gcr_rd(ch->parent, ch->gcr_fifo_status) & 0xffff; + snd_mtxunlock(sc->lock); sz = sndbuf_getsize(ch->buffer); return (2 * sz - pos - 1) % sz; } @@ -337,7 +348,7 @@ als_playback_start(struct sc_chinfo *ch) struct sc_info *sc = ch->parent; u_int32_t buf, bufsz, count, dma_prog; - buf = vtophys(sndbuf_getbuf(ch->buffer)); + buf = sndbuf_getbufaddr(ch->buffer); bufsz = sndbuf_getsize(ch->buffer); count = bufsz / 2; if (ch->format & AFMT_16BIT) @@ -381,7 +392,9 @@ static int alspchan_trigger(kobj_t obj, void *data, int go) { struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + snd_mtxlock(sc->lock); switch(go) { case PCMTRIG_START: als_playback_start(ch); @@ -390,6 +403,7 @@ alspchan_trigger(kobj_t obj, void *data, int go) als_playback_stop(ch); break; } + snd_mtxunlock(sc->lock); return 0; } @@ -431,7 +445,7 @@ als_capture_start(struct sc_chinfo *ch) struct sc_info *sc = ch->parent; u_int32_t buf, bufsz, count, dma_prog; - buf = vtophys(sndbuf_getbuf(ch->buffer)); + buf = sndbuf_getbufaddr(ch->buffer); bufsz = sndbuf_getsize(ch->buffer); count = bufsz / 2; if (ch->format & AFMT_16BIT) @@ -471,7 +485,9 @@ static int alsrchan_trigger(kobj_t obj, void *data, int go) { struct sc_chinfo *ch = data; + struct sc_info *sc = ch->parent; + snd_mtxlock(sc->lock); switch(go) { case PCMTRIG_START: als_capture_start(ch); @@ -480,6 +496,7 @@ alsrchan_trigger(kobj_t obj, void *data, int go) als_capture_stop(ch); break; } + snd_mtxunlock(sc->lock); return 0; } @@ -581,8 +598,13 @@ alsmix_setrecsrc(struct snd_mixer *m, u_int32_t src) for (i = l = r = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (src & (1 << i)) { - l |= amt[i].iselect; - r |= amt[i].iselect << 1; + if (amt[i].iselect == 1) { /* microphone */ + l |= amt[i].iselect; + r |= amt[i].iselect; + } else { + l |= amt[i].iselect; + r |= amt[i].iselect >> 1; + } } } @@ -608,13 +630,20 @@ als_intr(void *p) struct sc_info *sc = (struct sc_info *)p; u_int8_t intr, sb_status; + snd_mtxlock(sc->lock); intr = als_intr_rd(sc); - if (intr & 0x80) + if (intr & 0x80) { + snd_mtxunlock(sc->lock); chn_intr(sc->pch.channel); + snd_mtxlock(sc->lock); + } - if (intr & 0x40) + if (intr & 0x40) { + snd_mtxunlock(sc->lock); chn_intr(sc->rch.channel); + snd_mtxlock(sc->lock); + } /* ACK interrupt in PCI core */ als_intr_wr(sc, intr); @@ -630,6 +659,8 @@ als_intr(void *p) als_ack_read(sc, ALS_MIDI_DATA); if (sb_status & ALS_IRQ_CR1E) als_ack_read(sc, ALS_CR1E_ACK_PORT); + + snd_mtxunlock(sc->lock); return; } @@ -687,7 +718,7 @@ als_pci_probe(device_t dev) { if (pci_get_devid(dev) == ALS_PCI_ID0) { device_set_desc(dev, "Avance Logic ALS4000"); - return 0; + return BUS_PROBE_DEFAULT; } return ENXIO; } @@ -711,12 +742,16 @@ als_resource_free(device_t dev, struct sc_info *sc) bus_dma_tag_destroy(sc->parent_dmat); sc->parent_dmat = 0; } + if (sc->lock) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } } static int als_resource_grab(device_t dev, struct sc_info *sc) { - sc->regid = PCIR_MAPS; + sc->regid = PCIR_BAR(0); sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, 0, ~0, ALS_CONFIG_SPACE_BYTES, RF_ACTIVE); if (sc->reg == 0) { @@ -726,14 +761,15 @@ als_resource_grab(device_t dev, struct sc_info *sc) sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, 0, ~0, 1, - RF_ACTIVE | RF_SHAREABLE); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); if (sc->irq == 0) { device_printf(dev, "unable to allocate interrupt\n"); goto bad; } - if (bus_setup_intr(dev, sc->irq, 0, als_intr, sc, &sc->ih, NULL)) { + if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr, + sc, &sc->ih)) { device_printf(dev, "unable to setup interrupt\n"); goto bad; } @@ -747,7 +783,8 @@ als_resource_grab(device_t dev, struct sc_info *sc) /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { + /*flags*/0, + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -769,6 +806,7 @@ als_pci_attach(device_t dev) return ENXIO; } + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); sc->dev = dev; data = pci_read_config(dev, PCIR_COMMAND, 2); @@ -779,7 +817,7 @@ als_pci_attach(device_t dev) * ALS4000 is entirely controlled by the pci powerstate. We * could attempt finer grained control by setting GCR6.31. */ -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 +#if __FreeBSD_version > 500000 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { /* Reset the power state. */ device_printf(dev, "chip is in D%d power mode " @@ -819,8 +857,8 @@ als_pci_attach(device_t dev) pcm_addchan(dev, PCMDIR_PLAY, &alspchan_class, sc); pcm_addchan(dev, PCMDIR_REC, &alsrchan_class, sc); - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", - rman_get_start(sc->reg), rman_get_start(sc->irq)); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_als4000)); pcm_setstatus(dev, status); return 0; @@ -852,9 +890,11 @@ als_pci_suspend(device_t dev) { struct sc_info *sc = pcm_getdevinfo(dev); + snd_mtxlock(sc->lock); sc->pch.dma_was_active = als_playback_stop(&sc->pch); sc->rch.dma_was_active = als_capture_stop(&sc->rch); als_uninit(sc); + snd_mtxunlock(sc->lock); return 0; } @@ -863,13 +903,17 @@ als_pci_resume(device_t dev) { struct sc_info *sc = pcm_getdevinfo(dev); + + snd_mtxlock(sc->lock); if (als_init(sc) != 0) { device_printf(dev, "unable to reinitialize the card\n"); + snd_mtxunlock(sc->lock); return ENXIO; } if (mixer_reinit(dev) != 0) { device_printf(dev, "unable to reinitialize the mixer\n"); + snd_mtxunlock(sc->lock); return ENXIO; } @@ -880,6 +924,8 @@ als_pci_resume(device_t dev) if (sc->rch.dma_was_active) { als_capture_start(&sc->rch); } + snd_mtxunlock(sc->lock); + return 0; } @@ -900,5 +946,5 @@ static driver_t als_driver = { }; DRIVER_MODULE(snd_als4000, pci, als_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_als4000, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_als4000, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_als4000, 1); diff --git a/sys/dev/sound/pci/als4000.h b/sys/dev/sound/pci/als4000.h index d79428ee05..c2f474d717 100644 --- a/sys/dev/sound/pci/als4000.h +++ b/sys/dev/sound/pci/als4000.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2001 Orion Hodson * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/als4000.h,v 1.1.2.3 2002/04/22 15:49:31 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/als4000.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/als4000.h,v 1.3 2005/01/06 01:43:18 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/als4000.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #define ALS_PCI_ID0 0x40004005 diff --git a/sys/dev/sound/pci/atiixp.c b/sys/dev/sound/pci/atiixp.c new file mode 100644 index 0000000000..5d134aad8b --- /dev/null +++ b/sys/dev/sound/pci/atiixp.c @@ -0,0 +1,1133 @@ +/*- + * Copyright (c) 2005 Ariff Abdullah + * 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: src/sys/dev/sound/pci/atiixp.c,v 1.2.2.4 2006/04/04 17:24:33 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.1 2007/01/04 21:47:02 corecode Exp $ + */ + +/* + * FreeBSD pcm driver for ATI IXP 150/200/250/300 AC97 controllers + * + * Features + * * 16bit playback / recording + * * 32bit native playback - yay! + * * 32bit native recording (seems broken on few hardwares) + * + * Issues / TODO: + * * SPDIF + * * Support for more than 2 channels. + * * VRA ? VRM ? DRA ? + * * 32bit native recording seems broken on few hardwares, most + * probably because of incomplete VRA/DRA cleanup. + * + * + * Thanks goes to: + * + * Shaharil @ SCAN Associates whom relentlessly providing me the + * mind blowing Acer Ferrari 4002 WLMi with this ATI IXP hardware. + * + * Reinoud Zandijk (auixp), which this driver is + * largely based upon although large part of it has been reworked. His + * driver is the primary reference and pretty much well documented. + * + * Takashi Iwai (ALSA snd-atiixp), for register definitions and some + * random ninja hackery. + */ + +#include +#include + +#include +#include +#include +#include + +#include + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/atiixp.c,v 1.1 2007/01/04 21:47:02 corecode Exp $"); + +struct atiixp_dma_op { + volatile uint32_t addr; + volatile uint16_t status; + volatile uint16_t size; + volatile uint32_t next; +}; + +struct atiixp_info; + +struct atiixp_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct atiixp_info *parent; + struct atiixp_dma_op *sgd_table; + bus_addr_t sgd_addr; + uint32_t enable_bit, flush_bit, linkptr_bit, dma_dt_cur_bit; + uint32_t dma_segs; + uint32_t fmt; + int caps_32bit, dir, active; +}; + +struct atiixp_info { + device_t dev; + + bus_space_tag_t st; + bus_space_handle_t sh; + bus_dma_tag_t parent_dmat; + bus_dma_tag_t sgd_dmat; + bus_dmamap_t sgd_dmamap; + bus_addr_t sgd_addr; + + struct resource *reg, *irq; + int regtype, regid, irqid; + void *ih; + struct ac97_info *codec; + + struct atiixp_chinfo pch; + struct atiixp_chinfo rch; + struct atiixp_dma_op *sgd_table; + struct intr_config_hook delayed_attach; + + uint32_t bufsz; + uint32_t codec_not_ready_bits, codec_idx, codec_found; + uint32_t dma_segs; + int registered_channels; + + struct spinlock *lock; +}; + +#define atiixp_rd(_sc, _reg) \ + bus_space_read_4((_sc)->st, (_sc)->sh, _reg) +#define atiixp_wr(_sc, _reg, _val) \ + bus_space_write_4((_sc)->st, (_sc)->sh, _reg, _val) + +#define atiixp_lock(_sc) snd_mtxlock((_sc)->lock) +#define atiixp_unlock(_sc) snd_mtxunlock((_sc)->lock) +#define atiixp_assert(_sc) snd_mtxassert((_sc)->lock) + +static uint32_t atiixp_fmt_32bit[] = { + AFMT_STEREO | AFMT_S16_LE, + AFMT_STEREO | AFMT_S32_LE, + 0 +}; + +static uint32_t atiixp_fmt[] = { + AFMT_STEREO | AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps atiixp_caps_32bit = { + ATI_IXP_BASE_RATE, + ATI_IXP_BASE_RATE, + atiixp_fmt_32bit, 0 +}; + +static struct pcmchan_caps atiixp_caps = { + ATI_IXP_BASE_RATE, + ATI_IXP_BASE_RATE, + atiixp_fmt, 0 +}; + +static const struct { + uint16_t vendor; + uint16_t devid; + char *desc; +} atiixp_hw[] = { + { ATI_VENDOR_ID, ATI_IXP_200_ID, "ATI IXP 200" }, + { ATI_VENDOR_ID, ATI_IXP_300_ID, "ATI IXP 300" }, + { ATI_VENDOR_ID, ATI_IXP_400_ID, "ATI IXP 400" }, +}; + +static void atiixp_enable_interrupts(struct atiixp_info *); +static void atiixp_disable_interrupts(struct atiixp_info *); +static void atiixp_reset_aclink(struct atiixp_info *); +static void atiixp_flush_dma(struct atiixp_info *, struct atiixp_chinfo *); +static void atiixp_enable_dma(struct atiixp_info *, struct atiixp_chinfo *); +static void atiixp_disable_dma(struct atiixp_info *, struct atiixp_chinfo *); + +static int atiixp_waitready_codec(struct atiixp_info *); +static int atiixp_rdcd(kobj_t, void *, int); +static int atiixp_wrcd(kobj_t, void *, int, uint32_t); + +static void *atiixp_chan_init(kobj_t, void *, struct snd_dbuf *, + struct pcm_channel *, int); +static int atiixp_chan_setformat(kobj_t, void *, uint32_t); +static int atiixp_chan_setspeed(kobj_t, void *, uint32_t); +static int atiixp_chan_setblocksize(kobj_t, void *, uint32_t); +static void atiixp_buildsgdt(struct atiixp_chinfo *); +static int atiixp_chan_trigger(kobj_t, void *, int); +static int atiixp_chan_getptr(kobj_t, void *); +static struct pcmchan_caps *atiixp_chan_getcaps(kobj_t, void *); + +static void atiixp_intr(void *); +static void atiixp_dma_cb(void *, bus_dma_segment_t *, int, int); +static void atiixp_chip_pre_init(struct atiixp_info *); +static void atiixp_chip_post_init(void *); +static void atiixp_release_resource(struct atiixp_info *); +static int atiixp_pci_probe(device_t); +static int atiixp_pci_attach(device_t); +static int atiixp_pci_detach(device_t); +static int atiixp_pci_suspend(device_t); +static int atiixp_pci_resume(device_t); + +/* + * ATI IXP helper functions + */ +static void +atiixp_enable_interrupts(struct atiixp_info *sc) +{ + uint32_t value; + + /* clear all pending */ + atiixp_wr(sc, ATI_REG_ISR, 0xffffffff); + + /* enable all relevant interrupt sources we can handle */ + value = atiixp_rd(sc, ATI_REG_IER); + + value |= ATI_REG_IER_IO_STATUS_EN; + + /* + * Disable / ignore internal xrun/spdf interrupt flags + * since it doesn't interest us (for now). + */ +#if 0 + value |= ATI_REG_IER_IN_XRUN_EN; + value |= ATI_REG_IER_OUT_XRUN_EN; + + value |= ATI_REG_IER_SPDF_XRUN_EN; + value |= ATI_REG_IER_SPDF_STATUS_EN; +#endif + + atiixp_wr(sc, ATI_REG_IER, value); +} + +static void +atiixp_disable_interrupts(struct atiixp_info *sc) +{ + /* disable all interrupt sources */ + atiixp_wr(sc, ATI_REG_IER, 0); + + /* clear all pending */ + atiixp_wr(sc, ATI_REG_ISR, 0xffffffff); +} + +static void +atiixp_reset_aclink(struct atiixp_info *sc) +{ + uint32_t value, timeout; + + /* if power is down, power it up */ + value = atiixp_rd(sc, ATI_REG_CMD); + if (value & ATI_REG_CMD_POWERDOWN) { + /* explicitly enable power */ + value &= ~ATI_REG_CMD_POWERDOWN; + atiixp_wr(sc, ATI_REG_CMD, value); + + /* have to wait at least 10 usec for it to initialise */ + DELAY(20); + }; + + /* perform a soft reset */ + value = atiixp_rd(sc, ATI_REG_CMD); + value |= ATI_REG_CMD_AC_SOFT_RESET; + atiixp_wr(sc, ATI_REG_CMD, value); + + /* need to read the CMD reg and wait aprox. 10 usec to init */ + value = atiixp_rd(sc, ATI_REG_CMD); + DELAY(20); + + /* clear soft reset flag again */ + value = atiixp_rd(sc, ATI_REG_CMD); + value &= ~ATI_REG_CMD_AC_SOFT_RESET; + atiixp_wr(sc, ATI_REG_CMD, value); + + /* check if the ac-link is working; reset device otherwise */ + timeout = 10; + value = atiixp_rd(sc, ATI_REG_CMD); + while (!(value & ATI_REG_CMD_ACLINK_ACTIVE) + && --timeout) { +#if 0 + device_printf(sc->dev, "not up; resetting aclink hardware\n"); +#endif + + /* dip aclink reset but keep the acsync */ + value &= ~ATI_REG_CMD_AC_RESET; + value |= ATI_REG_CMD_AC_SYNC; + atiixp_wr(sc, ATI_REG_CMD, value); + + /* need to read CMD again and wait again (clocking in issue?) */ + value = atiixp_rd(sc, ATI_REG_CMD); + DELAY(20); + + /* assert aclink reset again */ + value = atiixp_rd(sc, ATI_REG_CMD); + value |= ATI_REG_CMD_AC_RESET; + atiixp_wr(sc, ATI_REG_CMD, value); + + /* check if its active now */ + value = atiixp_rd(sc, ATI_REG_CMD); + }; + + if (timeout == 0) + device_printf(sc->dev, "giving up aclink reset\n"); +#if 0 + if (timeout != 10) + device_printf(sc->dev, "aclink hardware reset successful\n"); +#endif + + /* assert reset and sync for safety */ + value = atiixp_rd(sc, ATI_REG_CMD); + value |= ATI_REG_CMD_AC_SYNC | ATI_REG_CMD_AC_RESET; + atiixp_wr(sc, ATI_REG_CMD, value); +} + +static void +atiixp_flush_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +{ + atiixp_wr(sc, ATI_REG_FIFO_FLUSH, ch->flush_bit); +} + +static void +atiixp_enable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +{ + uint32_t value; + + value = atiixp_rd(sc, ATI_REG_CMD); + if (!(value & ch->enable_bit)) { + value |= ch->enable_bit; + atiixp_wr(sc, ATI_REG_CMD, value); + } +} + +static void +atiixp_disable_dma(struct atiixp_info *sc, struct atiixp_chinfo *ch) +{ + uint32_t value; + + value = atiixp_rd(sc, ATI_REG_CMD); + if (value & ch->enable_bit) { + value &= ~ch->enable_bit; + atiixp_wr(sc, ATI_REG_CMD, value); + } +} + +/* + * AC97 interface + */ +static int +atiixp_waitready_codec(struct atiixp_info *sc) +{ + int timeout = 500; + + do { + if ((atiixp_rd(sc, ATI_REG_PHYS_OUT_ADDR) & + ATI_REG_PHYS_OUT_ADDR_EN) == 0) + return 0; + DELAY(1); + } while (timeout--); + + return -1; +} + +static int +atiixp_rdcd(kobj_t obj, void *devinfo, int reg) +{ + struct atiixp_info *sc = devinfo; + uint32_t data; + int timeout; + + if (atiixp_waitready_codec(sc)) + return -1; + + data = (reg << ATI_REG_PHYS_OUT_ADDR_SHIFT) | + ATI_REG_PHYS_OUT_ADDR_EN | + ATI_REG_PHYS_OUT_RW | sc->codec_idx; + + atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data); + + if (atiixp_waitready_codec(sc)) + return -1; + + timeout = 500; + do { + data = atiixp_rd(sc, ATI_REG_PHYS_IN_ADDR); + if (data & ATI_REG_PHYS_IN_READ_FLAG) + return data >> ATI_REG_PHYS_IN_DATA_SHIFT; + DELAY(1); + } while (timeout--); + + if (reg < 0x7c) + device_printf(sc->dev, "codec read timeout! (reg 0x%x)\n", reg); + + return -1; +} + +static int +atiixp_wrcd(kobj_t obj, void *devinfo, int reg, uint32_t data) +{ + struct atiixp_info *sc = devinfo; + + if (atiixp_waitready_codec(sc)) + return -1; + + data = (data << ATI_REG_PHYS_OUT_DATA_SHIFT) | + (((uint32_t)reg) << ATI_REG_PHYS_OUT_ADDR_SHIFT) | + ATI_REG_PHYS_OUT_ADDR_EN | sc->codec_idx; + + atiixp_wr(sc, ATI_REG_PHYS_OUT_ADDR, data); + + return 0; +} + +static kobj_method_t atiixp_ac97_methods[] = { + KOBJMETHOD(ac97_read, atiixp_rdcd), + KOBJMETHOD(ac97_write, atiixp_wrcd), + { 0, 0 } +}; +AC97_DECLARE(atiixp_ac97); + +/* + * Playback / Record channel interface + */ +static void * +atiixp_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct atiixp_info *sc = devinfo; + struct atiixp_chinfo *ch; + int num; + + atiixp_lock(sc); + + if (dir == PCMDIR_PLAY) { + ch = &sc->pch; + ch->linkptr_bit = ATI_REG_OUT_DMA_LINKPTR; + ch->enable_bit = ATI_REG_CMD_OUT_DMA_EN | ATI_REG_CMD_SEND_EN; + ch->flush_bit = ATI_REG_FIFO_OUT_FLUSH; + ch->dma_dt_cur_bit = ATI_REG_OUT_DMA_DT_CUR; + /* Native 32bit playback working properly */ + ch->caps_32bit = 1; + } else { + ch = &sc->rch; + ch->linkptr_bit = ATI_REG_IN_DMA_LINKPTR; + ch->enable_bit = ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_RECEIVE_EN; + ch->flush_bit = ATI_REG_FIFO_IN_FLUSH; + ch->dma_dt_cur_bit = ATI_REG_IN_DMA_DT_CUR; + /* XXX Native 32bit recording appear to be broken */ + ch->caps_32bit = 1; + } + + ch->buffer = b; + ch->parent = sc; + ch->channel = c; + ch->dir = dir; + ch->dma_segs = sc->dma_segs; + + atiixp_unlock(sc); + + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1) + return NULL; + + atiixp_lock(sc); + num = sc->registered_channels++; + ch->sgd_table = &sc->sgd_table[num * ch->dma_segs]; + ch->sgd_addr = sc->sgd_addr + + (num * ch->dma_segs * sizeof(struct atiixp_dma_op)); + atiixp_disable_dma(sc, ch); + atiixp_unlock(sc); + + return ch; +} + +static int +atiixp_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct atiixp_chinfo *ch = data; + struct atiixp_info *sc = ch->parent; + uint32_t value; + + atiixp_lock(sc); + if (ch->dir == PCMDIR_REC) { + value = atiixp_rd(sc, ATI_REG_CMD); + value &= ~ATI_REG_CMD_INTERLEAVE_IN; + if ((format & AFMT_32BIT) == 0) + value |= ATI_REG_CMD_INTERLEAVE_IN; + atiixp_wr(sc, ATI_REG_CMD, value); + } else { + value = atiixp_rd(sc, ATI_REG_OUT_DMA_SLOT); + value &= ~ATI_REG_OUT_DMA_SLOT_MASK; + /* We do not have support for more than 2 channels, _yet_. */ + value |= ATI_REG_OUT_DMA_SLOT_BIT(3) | + ATI_REG_OUT_DMA_SLOT_BIT(4); + value |= 0x04 << ATI_REG_OUT_DMA_THRESHOLD_SHIFT; + atiixp_wr(sc, ATI_REG_OUT_DMA_SLOT, value); + value = atiixp_rd(sc, ATI_REG_CMD); + value &= ~ATI_REG_CMD_INTERLEAVE_OUT; + if ((format & AFMT_32BIT) == 0) + value |= ATI_REG_CMD_INTERLEAVE_OUT; + atiixp_wr(sc, ATI_REG_CMD, value); + value = atiixp_rd(sc, ATI_REG_6CH_REORDER); + value &= ~ATI_REG_6CH_REORDER_EN; + atiixp_wr(sc, ATI_REG_6CH_REORDER, value); + } + ch->fmt = format; + atiixp_unlock(sc); + + return 0; +} + +static int +atiixp_chan_setspeed(kobj_t obj, void *data, uint32_t spd) +{ + /* XXX We're supposed to do VRA/DRA processing right here */ + return ATI_IXP_BASE_RATE; +} + +static int +atiixp_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct atiixp_chinfo *ch = data; + struct atiixp_info *sc = ch->parent; + + if (blksz > (sc->bufsz / ch->dma_segs)) + blksz = sc->bufsz / ch->dma_segs; + + sndbuf_resize(ch->buffer, ch->dma_segs, blksz); + + return sndbuf_getblksz(ch->buffer); +} + +static void +atiixp_buildsgdt(struct atiixp_chinfo *ch) +{ + uint32_t addr, blksz; + int i; + + addr = sndbuf_getbufaddr(ch->buffer); + blksz = sndbuf_getblksz(ch->buffer); + + for (i = 0; i < ch->dma_segs; i++) { + ch->sgd_table[i].addr = htole32(addr + (i * blksz)); + ch->sgd_table[i].status = htole16(0); + ch->sgd_table[i].size = htole16(blksz >> 2); + ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr + + (((i + 1) % ch->dma_segs) * + sizeof(struct atiixp_dma_op))); + } +} + +static int +atiixp_chan_trigger(kobj_t obj, void *data, int go) +{ + struct atiixp_chinfo *ch = data; + struct atiixp_info *sc = ch->parent; + uint32_t value; + + atiixp_lock(sc); + + switch (go) { + case PCMTRIG_START: + atiixp_flush_dma(sc, ch); + atiixp_buildsgdt(ch); + atiixp_wr(sc, ch->linkptr_bit, 0); + atiixp_enable_dma(sc, ch); + atiixp_wr(sc, ch->linkptr_bit, + (uint32_t)ch->sgd_addr | ATI_REG_LINKPTR_EN); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + atiixp_disable_dma(sc, ch); + atiixp_flush_dma(sc, ch); + break; + default: + atiixp_unlock(sc); + return 0; + break; + } + + /* Update bus busy status */ + value = atiixp_rd(sc, ATI_REG_IER); + if (atiixp_rd(sc, ATI_REG_CMD) & ( + ATI_REG_CMD_SEND_EN | ATI_REG_CMD_RECEIVE_EN | + ATI_REG_CMD_SPDF_OUT_EN)) + value |= ATI_REG_IER_SET_BUS_BUSY; + else + value &= ~ATI_REG_IER_SET_BUS_BUSY; + atiixp_wr(sc, ATI_REG_IER, value); + + atiixp_unlock(sc); + + return 0; +} + +static int +atiixp_chan_getptr(kobj_t obj, void *data) +{ + struct atiixp_chinfo *ch = data; + struct atiixp_info *sc = ch->parent; + uint32_t addr, align, retry, sz; + volatile uint32_t ptr; + + addr = sndbuf_getbufaddr(ch->buffer); + align = (ch->fmt & AFMT_32BIT) ? 7 : 3; + retry = 100; + sz = sndbuf_getblksz(ch->buffer) * ch->dma_segs; + + atiixp_lock(sc); + do { + ptr = atiixp_rd(sc, ch->dma_dt_cur_bit); + if (ptr < addr) + continue; + ptr -= addr; + if (ptr < sz && !(ptr & align)) + break; + } while (--retry); + atiixp_unlock(sc); + +#if 0 + if (retry != 100) { + device_printf(sc->dev, + "%saligned hwptr: dir=PCMDIR_%s ptr=%u fmt=0x%08x retry=%d\n", + (ptr & align) ? "un" : "", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr, + ch->fmt, 100 - retry); + } +#endif + + return (retry > 0) ? ptr : 0; +} + +static struct pcmchan_caps * +atiixp_chan_getcaps(kobj_t obj, void *data) +{ + struct atiixp_chinfo *ch = data; + + if (ch->caps_32bit) + return &atiixp_caps_32bit; + return &atiixp_caps; +} + +static kobj_method_t atiixp_chan_methods[] = { + KOBJMETHOD(channel_init, atiixp_chan_init), + KOBJMETHOD(channel_setformat, atiixp_chan_setformat), + KOBJMETHOD(channel_setspeed, atiixp_chan_setspeed), + KOBJMETHOD(channel_setblocksize, atiixp_chan_setblocksize), + KOBJMETHOD(channel_trigger, atiixp_chan_trigger), + KOBJMETHOD(channel_getptr, atiixp_chan_getptr), + KOBJMETHOD(channel_getcaps, atiixp_chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(atiixp_chan); + +/* + * PCI driver interface + */ +static void +atiixp_intr(void *p) +{ + struct atiixp_info *sc = p; + uint32_t status, enable, detected_codecs; + + atiixp_lock(sc); + status = atiixp_rd(sc, ATI_REG_ISR); + + if (status == 0) { + atiixp_unlock(sc); + return; + } + + if ((status & ATI_REG_ISR_IN_STATUS) && sc->rch.channel) { + atiixp_unlock(sc); + chn_intr(sc->rch.channel); + atiixp_lock(sc); + } + if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) { + atiixp_unlock(sc); + chn_intr(sc->pch.channel); + atiixp_lock(sc); + } + +#if 0 + if (status & ATI_REG_ISR_IN_XRUN) { + device_printf(sc->dev, + "Recieve IN XRUN interrupt\n"); + } + if (status & ATI_REG_ISR_OUT_XRUN) { + device_printf(sc->dev, + "Recieve OUT XRUN interrupt\n"); + } +#endif + + if (status & CODEC_CHECK_BITS) { + /* mark missing codecs as not ready */ + detected_codecs = status & CODEC_CHECK_BITS; + sc->codec_not_ready_bits |= detected_codecs; + + /* disable detected interupt sources */ + enable = atiixp_rd(sc, ATI_REG_IER); + enable &= ~detected_codecs; + atiixp_wr(sc, ATI_REG_IER, enable); + } + + /* acknowledge */ + atiixp_wr(sc, ATI_REG_ISR, status); + atiixp_unlock(sc); +} + +static void +atiixp_dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) +{ + struct atiixp_info *sc = (struct atiixp_info *)p; + sc->sgd_addr = bds->ds_addr; +} + +static void +atiixp_chip_pre_init(struct atiixp_info *sc) +{ + uint32_t value; + + atiixp_lock(sc); + + /* disable interrupts */ + atiixp_disable_interrupts(sc); + + /* clear all DMA enables (preserving rest of settings) */ + value = atiixp_rd(sc, ATI_REG_CMD); + value &= ~(ATI_REG_CMD_IN_DMA_EN | ATI_REG_CMD_OUT_DMA_EN | + ATI_REG_CMD_SPDF_OUT_EN ); + atiixp_wr(sc, ATI_REG_CMD, value); + + /* reset aclink */ + atiixp_reset_aclink(sc); + + sc->codec_not_ready_bits = 0; + + /* enable all codecs to interrupt as well as the new frame interrupt */ + atiixp_wr(sc, ATI_REG_IER, CODEC_CHECK_BITS); + + atiixp_unlock(sc); +} + +static void +atiixp_chip_post_init(void *arg) +{ + struct atiixp_info *sc = (struct atiixp_info *)arg; + uint32_t subdev; + int i, timeout, found; + char status[SND_STATUSLEN]; + + atiixp_lock(sc); + + if (sc->delayed_attach.ich_func) { + config_intrhook_disestablish(&sc->delayed_attach); + sc->delayed_attach.ich_func = NULL; + } + + /* wait for the interrupts to happen */ + timeout = 100; + while (--timeout) { + msleep(sc, sc->lock, 0, "ixpslp", 1); + if (sc->codec_not_ready_bits) + break; + } + + atiixp_disable_interrupts(sc); + + if (timeout == 0) { + device_printf(sc->dev, + "WARNING: timeout during codec detection; " + "codecs might be present but haven't interrupted\n"); + atiixp_unlock(sc); + goto postinitbad; + } + + found = 0; + + /* + * ATI IXP can have upto 3 codecs, but single codec should be + * suffice for now. + */ + if (!(sc->codec_not_ready_bits & + ATI_REG_ISR_CODEC0_NOT_READY)) { + /* codec 0 present */ + sc->codec_found++; + sc->codec_idx = 0; + found++; + } + + if (!(sc->codec_not_ready_bits & + ATI_REG_ISR_CODEC1_NOT_READY)) { + /* codec 1 present */ + sc->codec_found++; + } + + if (!(sc->codec_not_ready_bits & + ATI_REG_ISR_CODEC2_NOT_READY)) { + /* codec 2 present */ + sc->codec_found++; + } + + atiixp_unlock(sc); + + if (found == 0) + goto postinitbad; + + /* create/init mixer */ + sc->codec = AC97_CREATE(sc->dev, sc, atiixp_ac97); + if (sc->codec == NULL) + goto postinitbad; + + subdev = (pci_get_subdevice(sc->dev) << 16) | pci_get_subvendor(sc->dev); + switch (subdev) { + case 0x2043161f: /* Maxselect x710s - http://maxselect.ru/ */ + ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV); + break; + default: + break; + } + + mixer_init(sc->dev, ac97_getmixerclass(), sc->codec); + + if (pcm_register(sc->dev, sc, ATI_IXP_NPCHAN, ATI_IXP_NRCHAN)) + goto postinitbad; + + for (i = 0; i < ATI_IXP_NPCHAN; i++) + pcm_addchan(sc->dev, PCMDIR_PLAY, &atiixp_chan_class, sc); + for (i = 0; i < ATI_IXP_NRCHAN; i++) + pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc); + + ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", + rman_get_start(sc->reg), rman_get_start(sc->irq), + PCM_KLDSTRING(snd_atiixp)); + + pcm_setstatus(sc->dev, status); + + atiixp_lock(sc); + atiixp_enable_interrupts(sc); + atiixp_unlock(sc); + + return; + +postinitbad: + atiixp_release_resource(sc); +} + +static void +atiixp_release_resource(struct atiixp_info *sc) +{ + if (sc == NULL) + return; + if (sc->codec) { + ac97_destroy(sc->codec); + sc->codec = NULL; + } + if (sc->ih) { + bus_teardown_intr(sc->dev, sc->irq, sc->ih); + sc->ih = 0; + } + if (sc->reg) { + bus_release_resource(sc->dev, sc->regtype, sc->regid, sc->reg); + sc->reg = 0; + } + if (sc->irq) { + bus_release_resource(sc->dev, SYS_RES_IRQ, sc->irqid, sc->irq); + sc->irq = 0; + } + if (sc->parent_dmat) { + bus_dma_tag_destroy(sc->parent_dmat); + sc->parent_dmat = 0; + } + if (sc->sgd_dmamap) { + bus_dmamap_unload(sc->sgd_dmat, sc->sgd_dmamap); + sc->sgd_dmamap = 0; + } + if (sc->sgd_dmat) { + bus_dma_tag_destroy(sc->sgd_dmat); + sc->sgd_dmat = 0; + } + if (sc->lock) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } +} + +static int +atiixp_pci_probe(device_t dev) +{ + int i; + uint16_t devid, vendor; + + vendor = pci_get_vendor(dev); + devid = pci_get_device(dev); + for (i = 0; i < sizeof(atiixp_hw)/sizeof(atiixp_hw[0]); i++) { + if (vendor == atiixp_hw[i].vendor && + devid == atiixp_hw[i].devid) { + device_set_desc(dev, atiixp_hw[i].desc); + return BUS_PROBE_DEFAULT; + } + } + + return ENXIO; +} + +static int +atiixp_pci_attach(device_t dev) +{ + struct atiixp_info *sc; + int i; + + if ((sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return ENXIO; + } + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + sc->dev = dev; + /* + * Default DMA segments per playback / recording channel + */ + sc->dma_segs = ATI_IXP_DMA_CHSEGS; + + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + pci_enable_busmaster(dev); + + sc->regid = PCIR_BAR(0); + sc->regtype = SYS_RES_MEMORY; + sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid, + RF_ACTIVE); + + if (!sc->reg) { + device_printf(dev, "unable to allocate register space\n"); + goto bad; + } + + sc->st = rman_get_bustag(sc->reg); + sc->sh = rman_get_bushandle(sc->reg); + + sc->bufsz = pcm_getbuffersize(dev, 4096, ATI_IXP_DEFAULT_BUFSZ, 65536); + + sc->irqid = 0; + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(dev, sc->irq, INTR_MPSAFE, + atiixp_intr, sc, &sc->ih)) { + device_printf(dev, "unable to map interrupt\n"); + goto bad; + } + + /* + * Let the user choose the best DMA segments. + */ + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "dma_segs", + &i) == 0) { + if (i < ATI_IXP_DMA_CHSEGS_MIN) + i = ATI_IXP_DMA_CHSEGS_MIN; + if (i > ATI_IXP_DMA_CHSEGS_MAX) + i = ATI_IXP_DMA_CHSEGS_MAX; + sc->dma_segs = i; + } + + /* + * round the value to the nearest ^2 + */ + i = 0; + while (sc->dma_segs >> i) + i++; + sc->dma_segs = 1 << (i - 1); + if (sc->dma_segs < ATI_IXP_DMA_CHSEGS_MIN) + sc->dma_segs = ATI_IXP_DMA_CHSEGS_MIN; + else if (sc->dma_segs > ATI_IXP_DMA_CHSEGS_MAX) + sc->dma_segs = ATI_IXP_DMA_CHSEGS_MAX; + + /* + * DMA tag for scatter-gather buffers and link pointers + */ + if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/sc->bufsz, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, + &sc->parent_dmat) != 0) { + device_printf(dev, "unable to create dma tag\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*/sc->dma_segs * ATI_IXP_NCHANS * + sizeof(struct atiixp_dma_op), + /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, + &sc->sgd_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + goto bad; + } + + if (bus_dmamem_alloc(sc->sgd_dmat, (void **)&sc->sgd_table, + BUS_DMA_NOWAIT, &sc->sgd_dmamap) == -1) + goto bad; + + if (bus_dmamap_load(sc->sgd_dmat, sc->sgd_dmamap, sc->sgd_table, + sc->dma_segs * ATI_IXP_NCHANS * + sizeof(struct atiixp_dma_op), + atiixp_dma_cb, sc, 0)) + goto bad; + + + atiixp_chip_pre_init(sc); + + sc->delayed_attach.ich_func = atiixp_chip_post_init; + sc->delayed_attach.ich_arg = sc; + if (cold == 0 || + config_intrhook_establish(&sc->delayed_attach) != 0) { + sc->delayed_attach.ich_func = NULL; + atiixp_chip_post_init(sc); + } + + return 0; + +bad: + atiixp_release_resource(sc); + return ENXIO; +} + +static int +atiixp_pci_detach(device_t dev) +{ + int r; + struct atiixp_info *sc; + + sc = pcm_getdevinfo(dev); + if (sc != NULL) { + if (sc->codec != NULL) { + r = pcm_unregister(dev); + if (r) + return r; + } + sc->codec = NULL; + atiixp_disable_interrupts(sc); + atiixp_release_resource(sc); + kfree(sc, M_DEVBUF); + } + return 0; +} + +static int +atiixp_pci_suspend(device_t dev) +{ + struct atiixp_info *sc = pcm_getdevinfo(dev); + uint32_t value; + + /* quickly disable interrupts and save channels active state */ + atiixp_lock(sc); + atiixp_disable_interrupts(sc); + value = atiixp_rd(sc, ATI_REG_CMD); + sc->pch.active = (value & ATI_REG_CMD_SEND_EN) ? 1 : 0; + sc->rch.active = (value & ATI_REG_CMD_RECEIVE_EN) ? 1 : 0; + atiixp_unlock(sc); + + /* stop everything */ + if (sc->pch.channel && sc->pch.active) + atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_STOP); + if (sc->rch.channel && sc->rch.active) + atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_STOP); + + /* power down aclink and pci bus */ + atiixp_lock(sc); + value = atiixp_rd(sc, ATI_REG_CMD); + value |= ATI_REG_CMD_POWERDOWN | ATI_REG_CMD_AC_RESET; + atiixp_wr(sc, ATI_REG_CMD, ATI_REG_CMD_POWERDOWN); + pci_set_powerstate(dev, PCI_POWERSTATE_D3); + atiixp_unlock(sc); + + return 0; +} + +static int +atiixp_pci_resume(device_t dev) +{ + struct atiixp_info *sc = pcm_getdevinfo(dev); + + atiixp_lock(sc); + /* power up pci bus */ + pci_set_powerstate(dev, PCI_POWERSTATE_D0); + pci_enable_io(dev, SYS_RES_MEMORY); + pci_enable_busmaster(dev); + /* reset / power up aclink */ + atiixp_reset_aclink(sc); + atiixp_unlock(sc); + + if (mixer_reinit(dev) == -1) { + device_printf(dev, "unable to reinitialize the mixer\n"); + return ENXIO; + } + + /* + * Resume channel activities. Reset channel format regardless + * of its previous state. + */ + if (sc->pch.channel) { + if (sc->pch.fmt) + atiixp_chan_setformat(NULL, &sc->pch, sc->pch.fmt); + if (sc->pch.active) + atiixp_chan_trigger(NULL, &sc->pch, PCMTRIG_START); + } + if (sc->rch.channel) { + if (sc->rch.fmt) + atiixp_chan_setformat(NULL, &sc->rch, sc->rch.fmt); + if (sc->rch.active) + atiixp_chan_trigger(NULL, &sc->rch, PCMTRIG_START); + } + + /* enable interrupts */ + atiixp_lock(sc); + atiixp_enable_interrupts(sc); + atiixp_unlock(sc); + + return 0; +} + +static device_method_t atiixp_methods[] = { + DEVMETHOD(device_probe, atiixp_pci_probe), + DEVMETHOD(device_attach, atiixp_pci_attach), + DEVMETHOD(device_detach, atiixp_pci_detach), + DEVMETHOD(device_suspend, atiixp_pci_suspend), + DEVMETHOD(device_resume, atiixp_pci_resume), + { 0, 0 } +}; + +static driver_t atiixp_driver = { + "pcm", + atiixp_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_atiixp, pci, atiixp_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_atiixp, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_atiixp, 1); diff --git a/sys/dev/sound/pci/atiixp.h b/sys/dev/sound/pci/atiixp.h new file mode 100644 index 0000000000..7d75c87319 --- /dev/null +++ b/sys/dev/sound/pci/atiixp.h @@ -0,0 +1,204 @@ +/*- + * Copyright (c) 2005 Ariff Abdullah + * 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: src/sys/dev/sound/pci/atiixp.h,v 1.1.2.2 2006/03/02 00:09:30 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/atiixp.h,v 1.1 2007/01/04 21:47:02 corecode Exp $ + */ + +#ifndef _ATIIXP_H_ +#define _ATIIXP_H_ + +/* + * Constants, pretty much FreeBSD specific. + */ + +/* Number of playback / recording channel */ +#define ATI_IXP_NPCHAN 1 +#define ATI_IXP_NRCHAN 1 +#define ATI_IXP_NCHANS (ATI_IXP_NPCHAN + ATI_IXP_NRCHAN) + +/* + * Maximum segments/descriptors is 256, but 2 for + * each channel should be more than enough for us. + */ +#define ATI_IXP_DMA_CHSEGS 2 +#define ATI_IXP_DMA_CHSEGS_MIN 2 +#define ATI_IXP_DMA_CHSEGS_MAX 256 + +#define ATI_IXP_DEFAULT_BUFSZ (1 << 13) /* 8192 */ + +#define ATI_VENDOR_ID 0x1002 /* ATI Technologies */ +#define ATI_IXP_200_ID 0x4341 +#define ATI_IXP_300_ID 0x4361 +#define ATI_IXP_400_ID 0x4370 + +#define ATI_IXP_BASE_RATE 48000 + +/* + * Register definitions for ATI IXP + * + * References: ALSA snd-atiixp.c , OpenBSD/NetBSD auixp-*.h + */ + +#define ATI_IXP_CODECS 3 + +#define ATI_REG_ISR 0x00 /* interrupt source */ +#define ATI_REG_ISR_IN_XRUN (1U<<0) +#define ATI_REG_ISR_IN_STATUS (1U<<1) +#define ATI_REG_ISR_OUT_XRUN (1U<<2) +#define ATI_REG_ISR_OUT_STATUS (1U<<3) +#define ATI_REG_ISR_SPDF_XRUN (1U<<4) +#define ATI_REG_ISR_SPDF_STATUS (1U<<5) +#define ATI_REG_ISR_PHYS_INTR (1U<<8) +#define ATI_REG_ISR_PHYS_MISMATCH (1U<<9) +#define ATI_REG_ISR_CODEC0_NOT_READY (1U<<10) +#define ATI_REG_ISR_CODEC1_NOT_READY (1U<<11) +#define ATI_REG_ISR_CODEC2_NOT_READY (1U<<12) +#define ATI_REG_ISR_NEW_FRAME (1U<<13) + +#define ATI_REG_IER 0x04 /* interrupt enable */ +#define ATI_REG_IER_IN_XRUN_EN (1U<<0) +#define ATI_REG_IER_IO_STATUS_EN (1U<<1) +#define ATI_REG_IER_OUT_XRUN_EN (1U<<2) +#define ATI_REG_IER_OUT_XRUN_COND (1U<<3) +#define ATI_REG_IER_SPDF_XRUN_EN (1U<<4) +#define ATI_REG_IER_SPDF_STATUS_EN (1U<<5) +#define ATI_REG_IER_PHYS_INTR_EN (1U<<8) +#define ATI_REG_IER_PHYS_MISMATCH_EN (1U<<9) +#define ATI_REG_IER_CODEC0_INTR_EN (1U<<10) +#define ATI_REG_IER_CODEC1_INTR_EN (1U<<11) +#define ATI_REG_IER_CODEC2_INTR_EN (1U<<12) +#define ATI_REG_IER_NEW_FRAME_EN (1U<<13) /* (RO) */ +#define ATI_REG_IER_SET_BUS_BUSY (1U<<14) /* (WO) audio is running */ + +#define ATI_REG_CMD 0x08 /* command */ +#define ATI_REG_CMD_POWERDOWN (1U<<0) +#define ATI_REG_CMD_RECEIVE_EN (1U<<1) +#define ATI_REG_CMD_SEND_EN (1U<<2) +#define ATI_REG_CMD_STATUS_MEM (1U<<3) +#define ATI_REG_CMD_SPDF_OUT_EN (1U<<4) +#define ATI_REG_CMD_SPDF_STATUS_MEM (1U<<5) +#define ATI_REG_CMD_SPDF_THRESHOLD (3U<<6) +#define ATI_REG_CMD_SPDF_THRESHOLD_SHIFT 6 +#define ATI_REG_CMD_IN_DMA_EN (1U<<8) +#define ATI_REG_CMD_OUT_DMA_EN (1U<<9) +#define ATI_REG_CMD_SPDF_DMA_EN (1U<<10) +#define ATI_REG_CMD_SPDF_OUT_STOPPED (1U<<11) +#define ATI_REG_CMD_SPDF_CONFIG_MASK (7U<<12) +#define ATI_REG_CMD_SPDF_CONFIG_34 (1U<<12) +#define ATI_REG_CMD_SPDF_CONFIG_78 (2U<<12) +#define ATI_REG_CMD_SPDF_CONFIG_69 (3U<<12) +#define ATI_REG_CMD_SPDF_CONFIG_01 (4U<<12) +#define ATI_REG_CMD_INTERLEAVE_SPDF (1U<<16) +#define ATI_REG_CMD_AUDIO_PRESENT (1U<<20) +#define ATI_REG_CMD_INTERLEAVE_IN (1U<<21) +#define ATI_REG_CMD_INTERLEAVE_OUT (1U<<22) +#define ATI_REG_CMD_LOOPBACK_EN (1U<<23) +#define ATI_REG_CMD_PACKED_DIS (1U<<24) +#define ATI_REG_CMD_BURST_EN (1U<<25) +#define ATI_REG_CMD_PANIC_EN (1U<<26) +#define ATI_REG_CMD_MODEM_PRESENT (1U<<27) +#define ATI_REG_CMD_ACLINK_ACTIVE (1U<<28) +#define ATI_REG_CMD_AC_SOFT_RESET (1U<<29) +#define ATI_REG_CMD_AC_SYNC (1U<<30) +#define ATI_REG_CMD_AC_RESET (1U<<31) + +#define ATI_REG_PHYS_OUT_ADDR 0x0c +#define ATI_REG_PHYS_OUT_CODEC_MASK (3U<<0) +#define ATI_REG_PHYS_OUT_RW (1U<<2) +#define ATI_REG_PHYS_OUT_ADDR_EN (1U<<8) +#define ATI_REG_PHYS_OUT_ADDR_SHIFT 9 +#define ATI_REG_PHYS_OUT_DATA_SHIFT 16 + +#define ATI_REG_PHYS_IN_ADDR 0x10 +#define ATI_REG_PHYS_IN_READ_FLAG (1U<<8) +#define ATI_REG_PHYS_IN_ADDR_SHIFT 9 +#define ATI_REG_PHYS_IN_DATA_SHIFT 16 + +#define ATI_REG_SLOTREQ 0x14 + +#define ATI_REG_COUNTER 0x18 +#define ATI_REG_COUNTER_SLOT (3U<<0) /* slot # */ +#define ATI_REG_COUNTER_BITCLOCK (31U<<8) + +#define ATI_REG_IN_FIFO_THRESHOLD 0x1c + +#define ATI_REG_IN_DMA_LINKPTR 0x20 +#define ATI_REG_IN_DMA_DT_START 0x24 /* RO */ +#define ATI_REG_IN_DMA_DT_NEXT 0x28 /* RO */ +#define ATI_REG_IN_DMA_DT_CUR 0x2c /* RO */ +#define ATI_REG_IN_DMA_DT_SIZE 0x30 + +#define ATI_REG_OUT_DMA_SLOT 0x34 +#define ATI_REG_OUT_DMA_SLOT_BIT(x) (1U << ((x) - 3)) +#define ATI_REG_OUT_DMA_SLOT_MASK 0x1ff +#define ATI_REG_OUT_DMA_THRESHOLD_MASK 0xf800 +#define ATI_REG_OUT_DMA_THRESHOLD_SHIFT 11 + +#define ATI_REG_OUT_DMA_LINKPTR 0x38 +#define ATI_REG_OUT_DMA_DT_START 0x3c /* RO */ +#define ATI_REG_OUT_DMA_DT_NEXT 0x40 /* RO */ +#define ATI_REG_OUT_DMA_DT_CUR 0x44 /* RO */ +#define ATI_REG_OUT_DMA_DT_SIZE 0x48 + +#define ATI_REG_SPDF_CMD 0x4c +#define ATI_REG_SPDF_CMD_LFSR (1U<<4) +#define ATI_REG_SPDF_CMD_SINGLE_CH (1U<<5) +#define ATI_REG_SPDF_CMD_LFSR_ACC (0xff<<8) /* RO */ + +#define ATI_REG_SPDF_DMA_LINKPTR 0x50 +#define ATI_REG_SPDF_DMA_DT_START 0x54 /* RO */ +#define ATI_REG_SPDF_DMA_DT_NEXT 0x58 /* RO */ +#define ATI_REG_SPDF_DMA_DT_CUR 0x5c /* RO */ +#define ATI_REG_SPDF_DMA_DT_SIZE 0x60 + +#define ATI_REG_MODEM_MIRROR 0x7c +#define ATI_REG_AUDIO_MIRROR 0x80 + +#define ATI_REG_6CH_REORDER 0x84 /* reorder slots for 6ch */ +#define ATI_REG_6CH_REORDER_EN (1U<<0) /* 3,4,7,8,6,9 -> 3,4,6,9,7,8 */ + +#define ATI_REG_FIFO_FLUSH 0x88 +#define ATI_REG_FIFO_OUT_FLUSH (1U<<0) +#define ATI_REG_FIFO_IN_FLUSH (1U<<1) + +/* LINKPTR */ +#define ATI_REG_LINKPTR_EN (1U<<0) + +/* [INT|OUT|SPDIF]_DMA_DT_SIZE */ +#define ATI_REG_DMA_DT_SIZE (0xffffU<<0) +#define ATI_REG_DMA_FIFO_USED (0x1fU<<16) +#define ATI_REG_DMA_FIFO_FREE (0x1fU<<21) +#define ATI_REG_DMA_STATE (7U<<26) + +#define ATI_MAX_DESCRIPTORS 256 /* max number of descriptor packets */ + +/* codec detection constant indicating the interrupt flags */ +#define ALL_CODECS_NOT_READY \ + (ATI_REG_ISR_CODEC0_NOT_READY | ATI_REG_ISR_CODEC1_NOT_READY |\ + ATI_REG_ISR_CODEC2_NOT_READY) +#define CODEC_CHECK_BITS (ALL_CODECS_NOT_READY|ATI_REG_ISR_NEW_FRAME) + +#endif diff --git a/sys/dev/sound/pci/au88x0.c b/sys/dev/sound/pci/au88x0.c new file mode 100644 index 0000000000..2ca4ce8fab --- /dev/null +++ b/sys/dev/sound/pci/au88x0.c @@ -0,0 +1,734 @@ +/*- + * Copyright (c) 2003 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. 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 BY THE AUTHOR ``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: src/sys/dev/sound/pci/au88x0.c,v 1.10 2005/03/01 08:58:05 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/au88x0.c,v 1.1 2007/01/04 21:47:02 corecode Exp $ + */ + +#include +#include +#include + +#include + +#include +#include + + +/***************************************************************************\ + * * + * SUPPORTED CHIPSETS * + * * +\***************************************************************************/ + +static struct au88x0_chipset au88x0_chipsets[] = { + { + .auc_name = "Aureal Vortex (8820)", + .auc_pci_id = 0x000112eb, + + .auc_control = 0x1280c, + + .auc_irq_source = 0x12800, + .auc_irq_mask = 0x12804, + .auc_irq_control = 0x12808, + .auc_irq_status = 0x1199c, + + .auc_dma_control = 0x1060c, + + .auc_fifo_size = 0x20, + .auc_wt_fifos = 32, + .auc_wt_fifo_base = 0x0e800, + .auc_wt_fifo_ctl = 0x0f800, + .auc_wt_dma_ctl = 0x10500, + .auc_adb_fifos = 16, + .auc_adb_fifo_base = 0x0e000, + .auc_adb_fifo_ctl = 0x0f840, + .auc_adb_dma_ctl = 0x10580, + + .auc_adb_route_base = 0x10800, + .auc_adb_route_bits = 7, + .auc_adb_codec_in = 0x48, + .auc_adb_codec_out = 0x58, + }, + { + .auc_name = "Aureal Vortex 2 (8830)", + .auc_pci_id = 0x000212eb, + + .auc_control = 0x2a00c, + + .auc_irq_source = 0x2a000, + .auc_irq_mask = 0x2a004, + .auc_irq_control = 0x2a008, + .auc_irq_status = 0x2919c, + + .auc_dma_control = 0x27ae8, + + .auc_fifo_size = 0x40, + .auc_wt_fifos = 64, + .auc_wt_fifo_base = 0x10000, + .auc_wt_fifo_ctl = 0x16000, + .auc_wt_dma_ctl = 0x27900, + .auc_adb_fifos = 32, + .auc_adb_fifo_base = 0x14000, + .auc_adb_fifo_ctl = 0x16100, + .auc_adb_dma_ctl = 0x27a00, + + .auc_adb_route_base = 0x28000, + .auc_adb_route_bits = 8, + .auc_adb_codec_in = 0x70, + .auc_adb_codec_out = 0x88, + }, + { + .auc_name = "Aureal Vortex Advantage (8810)", + .auc_pci_id = 0x000312eb, + + .auc_control = 0x2a00c, + + .auc_irq_source = 0x2a000, + .auc_irq_mask = 0x2a004, + .auc_irq_control = 0x2a008, + .auc_irq_status = 0x2919c, + + .auc_dma_control = 0x27ae8, + + .auc_fifo_size = 0x20, + .auc_wt_fifos = 32, + .auc_wt_fifo_base = 0x10000, + .auc_wt_fifo_ctl = 0x16000, + .auc_wt_dma_ctl = 0x27fd8, + .auc_adb_fifos = 16, + .auc_adb_fifo_base = 0x14000, + .auc_adb_fifo_ctl = 0x16100, + .auc_adb_dma_ctl = 0x27180, + + .auc_adb_route_base = 0x28000, + .auc_adb_route_bits = 8, + .auc_adb_codec_in = 0x70, + .auc_adb_codec_out = 0x88, + }, + { + .auc_pci_id = 0, + } +}; + + +/***************************************************************************\ + * * + * FORMATS AND CAPABILITIES * + * * +\***************************************************************************/ + +static u_int32_t au88x0_formats[] = { + AFMT_U8, + AFMT_STEREO | AFMT_U8, + AFMT_S16_LE, + AFMT_STEREO | AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps au88x0_capabilities = { + 4000, /* minimum sample rate */ + 48000, /* maximum sample rate */ + au88x0_formats, /* supported formats */ + 0 /* no particular capabilities */ +}; + + +/***************************************************************************\ + * * + * CODEC INTERFACE * + * * +\***************************************************************************/ + +/* + * Read from the au88x0 register space + */ +#if 1 +/* all our writes are 32-bit */ +#define au88x0_read(aui, reg, n) \ + bus_space_read_4((aui)->aui_spct, (aui)->aui_spch, (reg)) +#define au88x0_write(aui, reg, data, n) \ + bus_space_write_4((aui)->aui_spct, (aui)->aui_spch, (reg), (data)) +#else +static uint32_t +au88x0_read(struct au88x0_info *aui, int reg, int size) +{ + uint32_t data; + + switch (size) { + case 1: + data = bus_space_read_1(aui->aui_spct, aui->aui_spch, reg); + break; + case 2: + data = bus_space_read_2(aui->aui_spct, aui->aui_spch, reg); + break; + case 4: + data = bus_space_read_4(aui->aui_spct, aui->aui_spch, reg); + break; + default: + panic("unsupported read size %d", size); + } + return (data); +} + +/* + * Write to the au88x0 register space + */ +static void +au88x0_write(struct au88x0_info *aui, int reg, uint32_t data, int size) +{ + + switch (size) { + case 1: + bus_space_write_1(aui->aui_spct, aui->aui_spch, reg, data); + break; + case 2: + bus_space_write_2(aui->aui_spct, aui->aui_spch, reg, data); + break; + case 4: + bus_space_write_4(aui->aui_spct, aui->aui_spch, reg, data); + break; + default: + panic("unsupported write size %d", size); + } +} +#endif + +/* + * Reset and initialize the codec + */ +static void +au88x0_codec_init(struct au88x0_info *aui) +{ + uint32_t data; + int i; + + /* wave that chicken */ + au88x0_write(aui, AU88X0_CODEC_CONTROL, 0x8068, 4); + DELAY(AU88X0_SETTLE_DELAY); + au88x0_write(aui, AU88X0_CODEC_CONTROL, 0x00e8, 4); + DELAY(1000); + for (i = 0; i < 32; ++i) { + au88x0_write(aui, AU88X0_CODEC_CHANNEL + i * 4, 0, 4); + DELAY(AU88X0_SETTLE_DELAY); + } + au88x0_write(aui, AU88X0_CODEC_CONTROL, 0x00e8, 4); + DELAY(AU88X0_SETTLE_DELAY); + + /* enable both codec channels */ + data = au88x0_read(aui, AU88X0_CODEC_ENABLE, 4); + data |= (1 << (8 + 0)) | (1 << (8 + 1)); + au88x0_write(aui, AU88X0_CODEC_ENABLE, data, 4); + DELAY(AU88X0_SETTLE_DELAY); +} + +/* + * Wait for the codec to get ready to accept a register write + * Should be called at spltty + */ +static int +au88x0_codec_wait(struct au88x0_info *aui) +{ + uint32_t data; + int i; + + for (i = 0; i < AU88X0_RETRY_COUNT; ++i) { + data = au88x0_read(aui, AU88X0_CODEC_CONTROL, 4); + if (data & AU88X0_CDCTL_WROK) + return (0); + DELAY(AU88X0_SETTLE_DELAY); + } + device_printf(aui->aui_dev, "timeout while waiting for codec\n"); + return (-1); +} + +/* + * Read from the ac97 codec + */ +static int +au88x0_codec_read(kobj_t obj, void *arg, int reg) +{ + struct au88x0_info *aui = arg; + uint32_t data; + int sl; + + sl = spltty(); + au88x0_codec_wait(aui); + au88x0_write(aui, AU88X0_CODEC_IO, AU88X0_CDIO_READ(reg), 4); + DELAY(1000); + data = au88x0_read(aui, AU88X0_CODEC_IO, 4); + splx(sl); + data &= AU88X0_CDIO_DATA_MASK; + data >>= AU88X0_CDIO_DATA_SHIFT; + return (data); +} + +/* + * Write to the ac97 codec + */ +static int +au88x0_codec_write(kobj_t obj, void *arg, int reg, uint32_t data) +{ + struct au88x0_info *aui = arg; + int sl; + + sl = spltty(); + au88x0_codec_wait(aui); + au88x0_write(aui, AU88X0_CODEC_IO, AU88X0_CDIO_WRITE(reg, data), 4); + splx(sl); + return 0; +} + +/* + * Codec interface glue + */ +static kobj_method_t au88x0_ac97_methods[] = { + KOBJMETHOD(ac97_read, au88x0_codec_read), + KOBJMETHOD(ac97_write, au88x0_codec_write), + { 0, 0 } +}; +AC97_DECLARE(au88x0_ac97); + +#define au88x0_channel(aui, dir) \ + &(aui)->aui_chan[((dir) == PCMDIR_PLAY) ? 0 : 1] + + +/***************************************************************************\ + * * + * CHANNEL INTERFACE * + * * +\***************************************************************************/ + +/* + * Initialize a PCM channel + */ +static void * +au88x0_chan_init(kobj_t obj, void *arg, + struct snd_dbuf *buf, struct pcm_channel *chan, int dir) +{ + struct au88x0_info *aui = arg; + struct au88x0_chan_info *auci = au88x0_channel(aui, dir); + + if (sndbuf_alloc(buf, aui->aui_dmat, aui->aui_bufsize) != 0) + return (NULL); + auci->auci_aui = aui; + auci->auci_pcmchan = chan; + auci->auci_buf = buf; + auci->auci_dir = dir; + return (auci); +} + +/* + * Set the data format for a PCM channel + */ +static int +au88x0_chan_setformat(kobj_t obj, void *arg, u_int32_t format) +{ + + /* XXX */ + return (ENXIO); +} + +/* + * Set the sample rate for a PCM channel + */ +static int +au88x0_chan_setspeed(kobj_t obj, void *arg, u_int32_t speed) +{ + + /* XXX */ + return (speed); +} + +/* + * Set the block size for a PCM channel + */ +static int +au88x0_chan_setblocksize(kobj_t obj, void *arg, u_int32_t blocksize) +{ + + /* XXX */ + return (blocksize); +} + +/* + * Initiate a data transfer + */ +static int +au88x0_chan_trigger(kobj_t obj, void *arg, int trigger) +{ + struct au88x0_chan_info *auci = arg; + + (void)auci; + switch (trigger) { + case PCMTRIG_START: + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + break; + } + return (0); +} + +/* + * + */ +static int +au88x0_chan_getptr(kobj_t obj, void *arg) +{ + + /* XXX */ + return (0); +} + +/* + * Return the capabilities of a PCM channel + */ +static struct pcmchan_caps * +au88x0_chan_getcaps(kobj_t obj, void *arg) +{ + + return (&au88x0_capabilities); +} + +/* + * Channel interface glue + */ +static kobj_method_t au88x0_chan_methods[] = { + KOBJMETHOD(channel_init, au88x0_chan_init), + KOBJMETHOD(channel_setformat, au88x0_chan_setformat), + KOBJMETHOD(channel_setspeed, au88x0_chan_setspeed), + KOBJMETHOD(channel_setblocksize, au88x0_chan_setblocksize), + KOBJMETHOD(channel_trigger, au88x0_chan_trigger), + KOBJMETHOD(channel_getptr, au88x0_chan_getptr), + KOBJMETHOD(channel_getcaps, au88x0_chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(au88x0_chan); + + +/***************************************************************************\ + * * + * INTERRUPT HANDLER * + * * +\***************************************************************************/ + +static void +au88x0_intr(void *arg) +{ + struct au88x0_info *aui = arg; + struct au88x0_chipset *auc = aui->aui_chipset; + int pending, source; + + pending = au88x0_read(aui, auc->auc_irq_control, 4); + if ((pending & AU88X0_IRQ_PENDING_BIT) == 0) + return; + source = au88x0_read(aui, auc->auc_irq_source, 4); + if (source & AU88X0_IRQ_FATAL_ERR) + device_printf(aui->aui_dev, + "fatal error interrupt received\n"); + if (source & AU88X0_IRQ_PARITY_ERR) + device_printf(aui->aui_dev, + "parity error interrupt received\n"); + /* XXX handle the others... */ + + /* acknowledge the interrupts we just handled */ + au88x0_write(aui, auc->auc_irq_source, source, 4); + au88x0_read(aui, auc->auc_irq_source, 4); +} + + +/***************************************************************************\ + * * + * INITIALIZATION * + * * +\***************************************************************************/ + +/* + * Reset and initialize the ADB and WT FIFOs + * + * - need to find out what the magic values 0x42000 and 0x2000 mean. + */ +static void +au88x0_fifo_init(struct au88x0_info *aui) +{ + struct au88x0_chipset *auc = aui->aui_chipset; + int i; + + /* reset, then clear the ADB FIFOs */ + for (i = 0; i < auc->auc_adb_fifos; ++i) + au88x0_write(aui, auc->auc_adb_fifo_ctl + i * 4, 0x42000, 4); + for (i = 0; i < auc->auc_adb_fifos * auc->auc_fifo_size; ++i) + au88x0_write(aui, auc->auc_adb_fifo_base + i * 4, 0, 4); + + /* reset, then clear the WT FIFOs */ + for (i = 0; i < auc->auc_wt_fifos; ++i) + au88x0_write(aui, auc->auc_wt_fifo_ctl + i * 4, 0x42000, 4); + for (i = 0; i < auc->auc_wt_fifos * auc->auc_fifo_size; ++i) + au88x0_write(aui, auc->auc_wt_fifo_base + i * 4, 0, 4); +} + +/* + * Hardware initialization + */ +static void +au88x0_init(struct au88x0_info *aui) +{ + struct au88x0_chipset *auc = aui->aui_chipset; + + /* reset the chip */ + au88x0_write(aui, auc->auc_control, 0xffffffff, 4); + DELAY(10000); + + /* clear all interrupts */ + au88x0_write(aui, auc->auc_irq_source, 0xffffffff, 4); + au88x0_read(aui, auc->auc_irq_source, 4); + au88x0_read(aui, auc->auc_irq_status, 4); + + /* initialize the codec */ + au88x0_codec_init(aui); + + /* initialize the fifos */ + au88x0_fifo_init(aui); + + /* initialize the DMA engine */ + /* XXX chicken-waving! */ + au88x0_write(aui, auc->auc_dma_control, 0x1380000, 4); +} + +/* + * Construct and set status string + */ +static void +au88x0_set_status(device_t dev) +{ + char status[SND_STATUSLEN]; + struct au88x0_info *aui; + + aui = pcm_getdevinfo(dev); + snprintf(status, sizeof status, "at %s 0x%lx irq %ld %s", + (aui->aui_regtype == SYS_RES_IOPORT)? "io" : "memory", + rman_get_start(aui->aui_reg), rman_get_start(aui->aui_irq),PCM_KLDSTRING(snd_au88x0)); + pcm_setstatus(dev, status); +} + + +/***************************************************************************\ + * * + * PCI INTERFACE * + * * +\***************************************************************************/ + +/* + * Probe + */ +static int +au88x0_pci_probe(device_t dev) +{ + struct au88x0_chipset *auc; + uint32_t pci_id; + + pci_id = pci_get_devid(dev); + for (auc = au88x0_chipsets; auc->auc_pci_id; ++auc) { + if (auc->auc_pci_id == pci_id) { + device_set_desc(dev, auc->auc_name); + return BUS_PROBE_DEFAULT; + } + } + return (ENXIO); +} + +/* + * Attach + */ +static int +au88x0_pci_attach(device_t dev) +{ + struct au88x0_chipset *auc; + struct au88x0_info *aui = NULL; + uint32_t config; + int error; + + if ((aui = kmalloc(sizeof *aui, M_DEVBUF, M_NOWAIT|M_ZERO)) == NULL) { + device_printf(dev, "failed to allocate softc\n"); + return (ENXIO); + } + aui->aui_dev = dev; + + /* Model-specific parameters */ + aui->aui_model = pci_get_devid(dev); + for (auc = au88x0_chipsets; auc->auc_pci_id; ++auc) + if (auc->auc_pci_id == aui->aui_model) + aui->aui_chipset = auc; + if (aui->aui_chipset == NULL) + panic("%s() called for non-au88x0 device", __func__); + + /* enable pio, mmio, bus-mastering dma */ + config = pci_read_config(dev, PCIR_COMMAND, 2); + config |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + pci_write_config(dev, PCIR_COMMAND, config, 2); + + /* register mapping */ + config = pci_read_config(dev, PCIR_COMMAND, 2); + if (config & PCIM_CMD_MEMEN) { + /* try memory-mapped I/O */ + aui->aui_regid = PCIR_BAR(0); + aui->aui_regtype = SYS_RES_MEMORY; + aui->aui_reg = bus_alloc_resource_any(dev, aui->aui_regtype, + &aui->aui_regid, RF_ACTIVE); + } + if (aui->aui_reg == NULL && (config & PCIM_CMD_PORTEN)) { + /* fall back on port I/O */ + aui->aui_regid = PCIR_BAR(0); + aui->aui_regtype = SYS_RES_IOPORT; + aui->aui_reg = bus_alloc_resource_any(dev, aui->aui_regtype, + &aui->aui_regid, RF_ACTIVE); + } + if (aui->aui_reg == NULL) { + /* both mmio and pio failed... */ + device_printf(dev, "failed to map registers\n"); + goto failed; + } + aui->aui_spct = rman_get_bustag(aui->aui_reg); + aui->aui_spch = rman_get_bushandle(aui->aui_reg); + + /* IRQ mapping */ + aui->aui_irqid = 0; + aui->aui_irqtype = SYS_RES_IRQ; + aui->aui_irq = bus_alloc_resource_any(dev, aui->aui_irqtype, + &aui->aui_irqid, RF_ACTIVE | RF_SHAREABLE); + if (aui->aui_irq == 0) { + device_printf(dev, "failed to map IRQ\n"); + goto failed; + } + + /* install interrupt handler */ + error = snd_setup_intr(dev, aui->aui_irq, 0, au88x0_intr, + aui, &aui->aui_irqh); + if (error != 0) { + device_printf(dev, "failed to install interrupt handler\n"); + goto failed; + } + + /* DMA mapping */ + aui->aui_bufsize = pcm_getbuffersize(dev, AU88X0_BUFSIZE_MIN, + AU88X0_BUFSIZE_DFLT, AU88X0_BUFSIZE_MAX); + error = bus_dma_tag_create(NULL, + 2, 0, /* 16-bit alignment, no boundary */ + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, /* restrict to 4GB */ + NULL, NULL, /* no filter */ + aui->aui_bufsize, 1, aui->aui_bufsize, + 0, busdma_lock_mutex, &Giant, &aui->aui_dmat); + if (error != 0) { + device_printf(dev, "failed to create DMA tag\n"); + goto failed; + } + + /* initialize the hardware */ + au88x0_init(aui); + + /* initialize the ac97 codec and mixer */ + if ((aui->aui_ac97i = AC97_CREATE(dev, aui, au88x0_ac97)) == NULL) { + device_printf(dev, "failed to initialize ac97 codec\n"); + goto failed; + } + if (mixer_init(dev, ac97_getmixerclass(), aui->aui_ac97i) != 0) { + device_printf(dev, "failed to initialize ac97 mixer\n"); + goto failed; + } + + /* register with the pcm driver */ + if (pcm_register(dev, aui, 0, 0)) + goto failed; + pcm_addchan(dev, PCMDIR_PLAY, &au88x0_chan_class, aui); +#if 0 + pcm_addchan(dev, PCMDIR_REC, &au88x0_chan_class, aui); +#endif + au88x0_set_status(dev); + + return (0); +failed: + if (aui->aui_ac97i != NULL) + ac97_destroy(aui->aui_ac97i); + if (aui->aui_dmat) + bus_dma_tag_destroy(aui->aui_dmat); + if (aui->aui_irqh != NULL) + bus_teardown_intr(dev, aui->aui_irq, aui->aui_irqh); + if (aui->aui_irq) + bus_release_resource(dev, aui->aui_irqtype, + aui->aui_irqid, aui->aui_irq); + if (aui->aui_reg) + bus_release_resource(dev, aui->aui_regtype, + aui->aui_regid, aui->aui_reg); + kfree(aui, M_DEVBUF); + return (ENXIO); +} + +/* + * Detach + */ +static int +au88x0_pci_detach(device_t dev) +{ + struct au88x0_info *aui; + int error; + + aui = pcm_getdevinfo(dev); + if ((error = pcm_unregister(dev)) != 0) + return (error); + + /* release resources in reverse order */ + bus_dma_tag_destroy(aui->aui_dmat); + bus_teardown_intr(dev, aui->aui_irq, aui->aui_irqh); + bus_release_resource(dev, aui->aui_irqtype, + aui->aui_irqid, aui->aui_irq); + bus_release_resource(dev, aui->aui_regtype, + aui->aui_regid, aui->aui_reg); + kfree(aui, M_DEVBUF); + + return (0); +} + +/* + * Driver glue + */ +static device_method_t au88x0_methods[] = { + DEVMETHOD(device_probe, au88x0_pci_probe), + DEVMETHOD(device_attach, au88x0_pci_attach), + DEVMETHOD(device_detach, au88x0_pci_detach), + { 0, 0 } +}; + +static driver_t au88x0_driver = { + "pcm", + au88x0_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_au88x0, pci, au88x0_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_au88x0, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_au88x0, 1); diff --git a/sys/dev/sound/pci/au88x0.h b/sys/dev/sound/pci/au88x0.h new file mode 100644 index 0000000000..7a446a6949 --- /dev/null +++ b/sys/dev/sound/pci/au88x0.h @@ -0,0 +1,179 @@ +/*- + * Copyright (c) 2003 Dag-Erling Coïdan Smørgrav + * 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 + * in this position and unchanged. + * 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. 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 BY THE AUTHOR ``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: src/sys/dev/sound/pci/au88x0.h,v 1.2 2003/10/12 11:33:39 des Exp $ + * $DragonFly: src/sys/dev/sound/pci/au88x0.h,v 1.1 2007/01/04 21:47:02 corecode Exp $ + */ + +#ifndef _AU88X0_H_INCLUDED +#define _AU88X0_H_INCLUDED + +/* + * Chipset parameters + */ +struct au88x0_chipset { + const char *auc_name; + uint32_t auc_pci_id; + + /* General control register */ + uint32_t auc_control; +#define AU88X0_CTL_MIDI_ENABLE 0x0001 +#define AU88X0_CTL_GAME_ENABLE 0x0008 +#define AU88X0_CTL_IRQ_ENABLE 0x4000 + + /* IRQ control register */ + uint32_t auc_irq_source; +#define AU88X0_IRQ_FATAL_ERR 0x0001 +#define AU88X0_IRQ_PARITY_ERR 0x0002 +#define AU88X0_IRQ_REG_ERR 0x0004 +#define AU88X0_IRQ_FIFO_ERR 0x0008 +#define AU88X0_IRQ_DMA_ERR 0x0010 +#define AU88X0_IRQ_PCMOUT 0x0020 +#define AU88X0_IRQ_TIMER 0x1000 +#define AU88X0_IRQ_MIDI 0x2000 +#define AU88X0_IRQ_MODEM 0x4000 + uint32_t auc_irq_mask; + uint32_t auc_irq_control; +#define AU88X0_IRQ_PENDING_BIT 0x0001 + uint32_t auc_irq_status; + + /* DMA control registers */ + uint32_t auc_dma_control; + + /* FIFOs */ + int auc_fifo_size; + int auc_wt_fifos; + uint32_t auc_wt_fifo_base; + uint32_t auc_wt_fifo_ctl; + uint32_t auc_wt_dma_ctl; + int auc_adb_fifos; + uint32_t auc_adb_fifo_base; + uint32_t auc_adb_fifo_ctl; + uint32_t auc_adb_dma_ctl; + + /* Routing */ + uint32_t auc_adb_route_base; + int auc_adb_route_bits; + int auc_adb_codec_in; + int auc_adb_codec_out; +}; + +/* + * Channel information + */ +struct au88x0_chan_info { + struct au88x0_info *auci_aui; + struct pcm_channel *auci_pcmchan; + struct snd_dbuf *auci_buf; + int auci_dir; +}; + +/* + * Device information + */ +struct au88x0_info { + /* the device we're associated with */ + device_t aui_dev; + uint32_t aui_model; + struct au88x0_chipset *aui_chipset; + + /* parameters */ + bus_size_t aui_bufsize; + int aui_wt_fifos; + int aui_wt_fifo_ctl; + int aui_adb_fifos; + int aui_adb_fifo_ctl; + int aui_fifo_size; + uint32_t aui_chanbase; + + /* bus_space tag and handle */ + bus_space_tag_t aui_spct; + bus_space_handle_t aui_spch; + + /* register space */ + int aui_regtype; + int aui_regid; + struct resource *aui_reg; + + /* irq */ + int aui_irqtype; + int aui_irqid; + struct resource *aui_irq; + void *aui_irqh; + + /* dma */ + bus_dma_tag_t aui_dmat; + + /* codec */ + struct ac97_info *aui_ac97i; + + /* channels */ + struct au88x0_chan_info aui_chan[2]; +}; + +/* + * Common parameters + */ +#define AU88X0_SETTLE_DELAY 1000 +#define AU88X0_RETRY_COUNT 10 +#define AU88X0_BUFSIZE_MIN 0x1000 +#define AU88X0_BUFSIZE_DFLT 0x4000 +#define AU88X0_BUFSIZE_MAX 0x4000 + +/* + * Codec control registers + * + * AU88X0_CODEC_CHANNEL array of 32 32-bit words + * + * AU88X0_CODEC_CONTROL control register + * + * bit 16 ready + * + * AU88X0_CODEC_IO I/O register + * + * bits 0-15 contents of codec register + * bits 16-22 address of codec register + * bit 23 0 for read, 1 for write + */ +#define AU88X0_CODEC_CHANNEL 0x29080 +#define AU88X0_CODEC_CONTROL 0x29184 +#define AU88X0_CDCTL_WROK 0x00000100 +#define AU88X0_CODEC_IO 0x29188 +#define AU88X0_CDIO_DATA_SHIFT 0 +#define AU88X0_CDIO_DATA_MASK 0x0000ffff +#define AU88X0_CDIO_ADDR_SHIFT 16 +#define AU88X0_CDIO_ADDR_MASK 0x007f0000 +#define AU88X0_CDIO_RDBIT 0x00000000 +#define AU88X0_CDIO_WRBIT 0x00800000 +#define AU88X0_CDIO_READ(a) (AU88X0_CDIO_RDBIT | \ + (((a) << AU88X0_CDIO_ADDR_SHIFT) & AU88X0_CDIO_ADDR_MASK)) +#define AU88X0_CDIO_WRITE(a, d) (AU88X0_CDIO_WRBIT | \ + (((a) << AU88X0_CDIO_ADDR_SHIFT) & AU88X0_CDIO_ADDR_MASK) | \ + (((d) << AU88X0_CDIO_DATA_SHIFT) & AU88X0_CDIO_DATA_MASK)) +#define AU88X0_CODEC_ENABLE 0x29190 + +#endif diff --git a/sys/dev/sound/pci/aureal.c b/sys/dev/sound/pci/aureal.c index efb5a6cc70..a7ac37e10c 100644 --- a/sys/dev/sound/pci/aureal.c +++ b/sys/dev/sound/pci/aureal.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/aureal.c,v 1.8.2.7 2002/04/22 15:49:31 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/aureal.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/aureal.c,v 1.32 2005/03/01 08:58:05 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/aureal.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -34,7 +34,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/aureal.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/aureal.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); /* PCI IDs of supported chips */ #define AU8820_PCI_ID 0x000112eb @@ -76,7 +76,7 @@ struct au_info { bus_space_handle_t sh[3]; bus_dma_tag_t parent_dmat; - void *lock; + struct spinlock *lock; u_int32_t x[32], y[128]; char z[128]; @@ -246,7 +246,7 @@ 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(sndbuf_getbuf(ch->buffer)); + u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer); au_wr(au, 0, 0x1061c, 0, 4); au_wr(au, 0, 0x10620, 0, 4); @@ -306,7 +306,8 @@ auchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, au->parent_dmat, AU_BUFFSIZE) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, au->parent_dmat, AU_BUFFSIZE) != 0) + return NULL; return ch; } @@ -539,7 +540,7 @@ au_pci_probe(device_t dev) { if (pci_get_devid(dev) == AU8820_PCI_ID) { device_set_desc(dev, "Aureal Vortex 8820"); - return 0; + return BUS_PROBE_DEFAULT; } return ENXIO; @@ -585,14 +586,14 @@ au_pci_attach(device_t dev) kprintf("at 0x%x...", config_id->map[i].base); } #endif - regid[j] = PCIR_MAPS + i*4; + regid[j] = PCIR_BAR(i); type[j] = SYS_RES_MEMORY; - reg[j] = bus_alloc_resource(dev, type[j], ®id[j], - 0, ~0, 1, RF_ACTIVE); + reg[j] = bus_alloc_resource_any(dev, type[j], ®id[j], + 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); + reg[j] = bus_alloc_resource_any(dev, type[j], + ®id[j], RF_ACTIVE); } if (reg[j]) { au->st[i] = rman_get_bustag(reg[j]); @@ -621,9 +622,9 @@ au_pci_attach(device_t dev) 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 || snd_setup_intr(dev, irq, 0, au_intr, au, &ih, NULL)) { + irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!irq || snd_setup_intr(dev, irq, 0, au_intr, au, &ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -644,14 +645,15 @@ au_pci_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/AU_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &au->parent_dmat) != 0) { + /*flags*/0, /*lockfunc*/busdma_lock_mutex, + /*lockarg*/&Giant, &au->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } - ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", + ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", (type[0] == SYS_RES_IOPORT)? "io" : "memory", - rman_get_start(reg[0]), rman_get_start(irq)); + rman_get_start(reg[0]), rman_get_start(irq),PCM_KLDSTRING(snd_aureal)); if (pcm_register(dev, au, 1, 1)) goto bad; /* pcm_addchan(dev, PCMDIR_REC, &au_chantemplate, au); */ @@ -684,5 +686,5 @@ static driver_t au_driver = { }; DRIVER_MODULE(snd_aureal, pci, au_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_aureal, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_aureal, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_aureal, 1); diff --git a/sys/dev/sound/pci/aureal.h b/sys/dev/sound/pci/aureal.h index 50ebe5ae28..21a2bac0a0 100644 --- a/sys/dev/sound/pci/aureal.h +++ b/sys/dev/sound/pci/aureal.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/aureal.h,v 1.2.2.2 2002/04/22 15:49:31 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/aureal.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/aureal.h,v 1.4 2005/01/06 01:43:18 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/aureal.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _AU8820_REG_H diff --git a/sys/dev/sound/pci/cmi.c b/sys/dev/sound/pci/cmi.c index 4d851aeb78..a2c8421f39 100644 --- a/sys/dev/sound/pci/cmi.c +++ b/sys/dev/sound/pci/cmi.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Orion Hodson * All rights reserved. * @@ -27,7 +27,7 @@ * Much of register handling is based on NetBSD CMI8x38 audio driver * by Takuya Shiozaki . Chen-Li Tien * clarified points regarding the DMA related - * registers and the 8738 mixer devices. His Linux was driver a also + * registers and the 8738 mixer devices. His Linux driver was also a * useful reference point. * * TODO: MIDI @@ -39,8 +39,8 @@ * differences visible in register dumps between times that work and * those that don't. * - * $FreeBSD: src/sys/dev/sound/pci/cmi.c,v 1.1.2.8 2002/08/27 00:17:34 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/cmi.c,v 1.8 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/cmi.c,v 1.32.2.2 2006/01/24 18:54:22 joel Exp $ + * $DragonFly: src/sys/dev/sound/pci/cmi.c,v 1.9 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -54,7 +54,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/cmi.c,v 1.8 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/cmi.c,v 1.9 2007/01/04 21:47:02 corecode Exp $"); /* Supported chip ID's */ #define CMI8338A_PCI_ID 0x010013f6 @@ -110,7 +110,7 @@ struct sc_info { struct resource *reg, *irq; int regid, irqid; void *ih; - void *lock; + struct spinlock *lock; int spdif_enabled; unsigned int bufsz; @@ -242,7 +242,7 @@ cmi_dma_prog(struct sc_info *sc, struct sc_chinfo *ch, u_int32_t base) { u_int32_t s, i, sz; - ch->phys_buf = vtophys(sndbuf_getbuf(ch->buffer)); + ch->phys_buf = sndbuf_getbufaddr(ch->buffer); cmi_wr(sc, base, ch->phys_buf, 4); sz = (u_int32_t)sndbuf_getsize(ch->buffer); @@ -519,41 +519,41 @@ cmi_intr(void *data) { struct sc_info *sc = data; u_int32_t intrstat; + u_int32_t toclear; snd_mtxlock(sc->lock); intrstat = cmi_rd(sc, CMPCI_REG_INTR_STATUS, 4); - if ((intrstat & CMPCI_REG_ANY_INTR) == 0) { - goto out; - } + if ((intrstat & CMPCI_REG_ANY_INTR) != 0) { - /* Disable interrupts */ - if (intrstat & CMPCI_REG_CH0_INTR) { - cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); - } + toclear = 0; + if (intrstat & CMPCI_REG_CH0_INTR) { + toclear |= CMPCI_REG_CH0_INTR_ENABLE; + /* cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); */ + } - if (intrstat & CMPCI_REG_CH1_INTR) { - cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); - } + if (intrstat & CMPCI_REG_CH1_INTR) { + toclear |= CMPCI_REG_CH1_INTR_ENABLE; + /* cmi_clr4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); */ + } - /* Signal interrupts to channel */ - if (intrstat & CMPCI_REG_CH0_INTR) { - chn_intr(sc->pch.channel); - } + if (toclear) { + cmi_clr4(sc, CMPCI_REG_INTR_CTRL, toclear); + snd_mtxunlock(sc->lock); - if (intrstat & CMPCI_REG_CH1_INTR) { - chn_intr(sc->rch.channel); - } + /* Signal interrupts to channel */ + if (intrstat & CMPCI_REG_CH0_INTR) { + chn_intr(sc->pch.channel); + } - /* Enable interrupts */ - if (intrstat & CMPCI_REG_CH0_INTR) { - cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH0_INTR_ENABLE); - } + if (intrstat & CMPCI_REG_CH1_INTR) { + chn_intr(sc->rch.channel); + } - if (intrstat & CMPCI_REG_CH1_INTR) { - cmi_set4(sc, CMPCI_REG_INTR_CTRL, CMPCI_REG_CH1_INTR_ENABLE); - } + snd_mtxlock(sc->lock); + cmi_set4(sc, CMPCI_REG_INTR_CTRL, toclear); -out: + } + } snd_mtxunlock(sc->lock); return; } @@ -815,16 +815,16 @@ cmi_probe(device_t dev) switch(pci_get_devid(dev)) { case CMI8338A_PCI_ID: device_set_desc(dev, "CMedia CMI8338A"); - return 0; + return BUS_PROBE_DEFAULT; case CMI8338B_PCI_ID: device_set_desc(dev, "CMedia CMI8338B"); - return 0; + return BUS_PROBE_DEFAULT; case CMI8738_PCI_ID: device_set_desc(dev, "CMedia CMI8738"); - return 0; + return BUS_PROBE_DEFAULT; case CMI8738B_PCI_ID: device_set_desc(dev, "CMedia CMI8738B"); - return 0; + return BUS_PROBE_DEFAULT; default: return ENXIO; } @@ -833,12 +833,10 @@ cmi_probe(device_t dev) static int cmi_attach(device_t dev) { - struct snddev_info *d; struct sc_info *sc; u_int32_t data; char status[SND_STATUSLEN]; - d = device_get_softc(dev); sc = kmalloc(sizeof(struct sc_info), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc == NULL) { device_printf(dev, "cannot allocate softc\n"); @@ -852,9 +850,9 @@ cmi_attach(device_t dev) data = pci_read_config(dev, PCIR_COMMAND, 2); sc->dev = dev; - sc->regid = PCIR_MAPS; - sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->regid, - 0, BUS_SPACE_UNRESTRICTED, 1, RF_ACTIVE); + sc->regid = PCIR_BAR(0); + sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &sc->regid, + RF_ACTIVE); if (!sc->reg) { device_printf(dev, "cmi_attach: Cannot allocate bus resource\n"); goto bad; @@ -863,10 +861,10 @@ cmi_attach(device_t dev) sc->sh = rman_get_bushandle(sc->reg); sc->irqid = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); if (!sc->irq || - snd_setup_intr(dev, sc->irq, INTR_MPSAFE, cmi_intr, sc, &sc->ih, NULL)) { + snd_setup_intr(dev, sc->irq, INTR_MPSAFE, cmi_intr, sc, &sc->ih)) { device_printf(dev, "cmi_attach: Unable to map interrupt\n"); goto bad; } @@ -899,8 +897,8 @@ cmi_attach(device_t dev) pcm_addchan(dev, PCMDIR_PLAY, &cmichan_class, sc); pcm_addchan(dev, PCMDIR_REC, &cmichan_class, sc); - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", - rman_get_start(sc->reg), rman_get_start(sc->irq)); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_cmi)); pcm_setstatus(dev, status); DEB(kprintf("cmi_attach: succeeded\n")); @@ -1008,7 +1006,6 @@ static driver_t cmi_driver = { PCM_SOFTC_SIZE }; -DECLARE_DUMMY_MODULE(snd_cmi); DRIVER_MODULE(snd_cmi, pci, cmi_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_cmi, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_cmi, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_cmi, 1); diff --git a/sys/dev/sound/pci/cmireg.h b/sys/dev/sound/pci/cmireg.h index b5922e41ce..b455dd33b5 100644 --- a/sys/dev/sound/pci/cmireg.h +++ b/sys/dev/sound/pci/cmireg.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * @@ -26,8 +26,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/cmireg.h,v 1.1.2.4 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/cmireg.h,v 1.3 2006/10/23 21:50:32 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/cmireg.h,v 1.3 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/cmireg.h,v 1.4 2007/01/04 21:47:02 corecode Exp $ */ /* C-Media CMI8x38 Audio Chip Support */ diff --git a/sys/dev/sound/pci/cs4281.c b/sys/dev/sound/pci/cs4281.c index 0271c97e6d..7ccd7f64ff 100644 --- a/sys/dev/sound/pci/cs4281.c +++ b/sys/dev/sound/pci/cs4281.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Orion Hodson * All rights reserved. * @@ -28,8 +28,8 @@ * woller (twoller@crystal.cirrus.com). Shingo Watanabe (nabe@nabechan.org) * contributed towards power management. * - * $FreeBSD: src/sys/dev/sound/pci/cs4281.c,v 1.2.2.8 2002/08/27 00:25:55 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/cs4281.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/cs4281.c,v 1.22 2005/03/01 08:58:05 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/cs4281.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -40,7 +40,7 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/cs4281.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/cs4281.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); #define CS4281_DEFAULT_BUFSZ 16384 @@ -490,7 +490,7 @@ adcdac_prog(struct sc_chinfo *ch) if (!ch->dma_setup) { go = adcdac_go(ch, 0); cs4281_wr(sc, CS4281PCI_DBA(ch->dma_chan), - vtophys(sndbuf_getbuf(ch->buffer))); + sndbuf_getbufaddr(ch->buffer)); cs4281_wr(sc, CS4281PCI_DBC(ch->dma_chan), sndbuf_getsize(ch->buffer) / ch->bps - 1); ch->dma_setup = 1; @@ -745,7 +745,7 @@ cs4281_pci_probe(device_t dev) if (s) device_set_desc(dev, s); - return s ? 0 : ENXIO; + return s ? BUS_PROBE_DEFAULT : ENXIO; } static int @@ -768,7 +768,7 @@ cs4281_pci_attach(device_t dev) data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 +#if __FreeBSD_version > 500000 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { /* Reset the power state. */ device_printf(dev, "chip is in D%d power mode " @@ -788,7 +788,7 @@ cs4281_pci_attach(device_t dev) } #endif - sc->regid = PCIR_MAPS; + sc->regid = PCIR_BAR(0); sc->regtype = SYS_RES_MEMORY; sc->reg = bus_alloc_resource(dev, sc->regtype, &sc->regid, 0, ~0, CS4281PCI_BA0_SIZE, RF_ACTIVE); @@ -804,7 +804,7 @@ cs4281_pci_attach(device_t dev) sc->st = rman_get_bustag(sc->reg); sc->sh = rman_get_bushandle(sc->reg); - sc->memid = PCIR_MAPS + 4; + sc->memid = PCIR_BAR(1); sc->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->memid, 0, ~0, CS4281PCI_BA1_SIZE, RF_ACTIVE); if (sc->mem == NULL) { @@ -813,14 +813,14 @@ cs4281_pci_attach(device_t dev) } sc->irqid = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); if (!sc->irq) { device_printf(dev, "unable to allocate interrupt\n"); goto bad; } - if (snd_setup_intr(dev, sc->irq, 0, cs4281_intr, sc, &sc->ih, NULL)) { + if (snd_setup_intr(dev, sc->irq, 0, cs4281_intr, sc, &sc->ih)) { device_printf(dev, "unable to setup interrupt\n"); goto bad; } @@ -833,7 +833,8 @@ cs4281_pci_attach(device_t dev) /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { + /*flags*/0, + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -860,9 +861,9 @@ cs4281_pci_attach(device_t dev) pcm_addchan(dev, PCMDIR_PLAY, &cs4281chan_class, sc); pcm_addchan(dev, PCMDIR_REC, &cs4281chan_class, sc); - ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", + ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", - rman_get_start(sc->reg), rman_get_start(sc->irq)); + rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_cs4281)); pcm_setstatus(dev, status); return 0; @@ -978,5 +979,5 @@ static driver_t cs4281_driver = { }; DRIVER_MODULE(snd_cs4281, pci, cs4281_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_cs4281, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_cs4281, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_cs4281, 1); diff --git a/sys/dev/sound/pci/cs4281.h b/sys/dev/sound/pci/cs4281.h index 88c630134b..75349800bb 100644 --- a/sys/dev/sound/pci/cs4281.h +++ b/sys/dev/sound/pci/cs4281.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Orion Hodson * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/cs4281.h,v 1.2.2.4 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/cs4281.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/cs4281.h,v 1.4 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/cs4281.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _CS4281_H_ diff --git a/sys/dev/sound/pci/csa.c b/sys/dev/sound/pci/csa.c index 39629924ed..c670c0be09 100644 --- a/sys/dev/sound/pci/csa.c +++ b/sys/dev/sound/pci/csa.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * @@ -27,8 +27,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/csa.c,v 1.8.2.12 2002/10/05 19:53:18 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/csa.c,v 1.7 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/csa.c,v 1.33.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/csa.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -48,9 +48,9 @@ #include #include -#include "gnu/csaimg.h" +#include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/csa.c,v 1.7 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/csa.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); /* This is the pci device id. */ #define CS4610_PCI_ID 0x60011013 @@ -84,13 +84,11 @@ 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, lwkt_serialize_t serializer); + 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 devclass_t csa_devclass; @@ -229,7 +227,7 @@ csa_probe(device_t dev) card = csa_findcard(dev); if (card) { device_set_desc(dev, card->name); - return 0; + return BUS_PROBE_DEFAULT; } return ENXIO; } @@ -261,21 +259,24 @@ csa_attach(device_t dev) scp->card = csa_findsubcard(dev); scp->binfo.card = scp->card; kprintf("csa: card is %s\n", scp->card->name); - resp->io_rid = PCIR_MAPS; - resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, 1, RF_ACTIVE); + resp->io_rid = PCIR_BAR(0); + resp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &resp->io_rid, RF_ACTIVE); if (resp->io == NULL) return (ENXIO); - resp->mem_rid = PCIR_MAPS + 4; - resp->mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->mem_rid, 0, ~0, 1, RF_ACTIVE); + resp->mem_rid = PCIR_BAR(1); + resp->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &resp->mem_rid, RF_ACTIVE); if (resp->mem == NULL) goto err_io; resp->irq_rid = 0; - resp->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &resp->irq_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + resp->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &resp->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (resp->irq == NULL) goto err_mem; /* Enable interrupt. */ - if (snd_setup_intr(dev, resp->irq, INTR_MPSAFE, csa_intr, scp, &scp->ih, NULL)) + if (snd_setup_intr(dev, resp->irq, 0, csa_intr, scp, &scp->ih)) goto err_intr; #if 0 if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) @@ -363,6 +364,29 @@ csa_detach(device_t dev) return bus_generic_detach(dev); } +static int +csa_resume(device_t dev) +{ + csa_res *resp; + sc_p scp; + + scp = device_get_softc(dev); + resp = &scp->res; + + /* Initialize the chip. */ + if (csa_initialize(scp)) + return (ENXIO); + + /* Reset the Processor. */ + csa_resetdsp(resp); + + /* Download the Processor Image to the processor. */ + if (csa_downloadimage(resp)) + return (ENXIO); + + return (bus_generic_resume(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) @@ -381,10 +405,10 @@ csa_alloc_resource(device_t bus, device_t child, int type, int *rid, break; case SYS_RES_MEMORY: switch (*rid) { - case PCIR_MAPS: + case PCIR_BAR(0): res = resp->io; break; - case PCIR_MAPS + 4: + case PCIR_BAR(1): res = resp->mem; break; default: @@ -417,8 +441,7 @@ csa_release_resource(device_t bus, device_t child, int type, int rid, static int csa_setup_intr(device_t bus, device_t child, struct resource *irq, int flags, - driver_intr_t *intr, void *arg, - void **cookiep, lwkt_serialize_t serializer) + driver_intr_t *intr, void *arg, void **cookiep) { sc_p scp; csa_res *resp; @@ -427,8 +450,6 @@ csa_setup_intr(device_t bus, device_t child, scp = device_get_softc(bus); resp = &scp->res; - KKASSERT(serializer == NULL); /* not yet supported */ - /* * Look at the function code of the child to determine * the appropriate hander for it. @@ -625,7 +646,7 @@ csa_initialize(sc_p scp) /* * Set the serial port FIFO pointer to the first sample in the FIFO. */ -#if notdef +#ifdef notdef csa_writeio(resp, BA0_SERBSP, 0); #endif /* notdef */ @@ -679,7 +700,7 @@ csa_initialize(sc_p scp) * First, lets wait a short while to let things settle out a bit, * and to prevent retrying the read too quickly. */ -#if notdef +#ifdef notdef DELAY(10000000L); /* clw */ #else DELAY(1000); @@ -709,7 +730,7 @@ csa_initialize(sc_p scp) * Power down the DAC and ADC. We will power them up (if) when we need * them. */ -#if notdef +#ifdef notdef csa_writeio(resp, BA0_AC97_POWERDOWN, 0x300); #endif /* notdef */ @@ -717,7 +738,7 @@ csa_initialize(sc_p scp) * Turn off the Processor by turning off the software clock enable flag in * the clock control register. */ -#if notdef +#ifdef notdef clkcr1 = csa_readio(resp, BA0_CLKCR1) & ~CLKCR1_SWCE; csa_writeio(resp, BA0_CLKCR1, clkcr1); #endif /* notdef */ @@ -783,7 +804,7 @@ csa_clearserialfifos(csa_res *resp) csa_writeio(resp, BA0_CLKCR1, clkcr1); } -static void +void csa_resetdsp(csa_res *resp) { int i; @@ -1029,7 +1050,7 @@ static device_method_t csa_methods[] = { DEVMETHOD(device_detach, csa_detach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_resume, csa_resume), /* Bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), @@ -1053,5 +1074,5 @@ static driver_t csa_driver = { * 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_DEPEND(snd_csa, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_csa, 1); diff --git a/sys/dev/sound/pci/csapcm.c b/sys/dev/sound/pci/csapcm.c index 746788e9d2..7441848c35 100644 --- a/sys/dev/sound/pci/csapcm.c +++ b/sys/dev/sound/pci/csapcm.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1999 Seigo Tanimura * All rights reserved. * @@ -27,8 +27,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/csapcm.c,v 1.8.2.7 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/csapcm.c,v 1.6 2006/12/20 18:14:40 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/csapcm.c,v 1.34.2.2 2006/04/04 17:32:48 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/csapcm.c,v 1.7 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -41,7 +41,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/csapcm.c,v 1.6 2006/12/20 18:14:40 dillon Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/csapcm.c,v 1.7 2007/01/04 21:47:02 corecode Exp $"); /* Buffer size on dma transfer. Fixed for CS416x. */ #define CS461x_BUFFSIZE (4 * 1024) @@ -73,6 +73,9 @@ struct csa_info { u_long pctl; u_long cctl; struct csa_chinfo pch, rch; + u_int32_t ac97[CS461x_AC97_NUMBER_RESTORE_REGS]; + u_int32_t ac97_powerdown; + u_int32_t ac97_general_purpose; }; /* -------------------------------------------------------------------- */ @@ -87,8 +90,11 @@ 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 int csa_startdsp(csa_res *resp); +static int csa_stopdsp(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); +static void csa_ac97_suspend(struct csa_info *csa); +static void csa_ac97_resume(struct csa_info *csa); static u_int32_t csa_playfmt[] = { AFMT_U8, @@ -115,16 +121,16 @@ static struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0}; static int csa_active(struct csa_info *csa, int run) { - int old, go; + int old; old = csa->active; csa->active += run; - if ((csa->active == 0 && old == 1) || (csa->active == 1 && old == 0)) { - go = csa->active; - if (csa->card->active) - return csa->card->active(go); - } + if ((csa->active > 1) || (csa->active < -1)) + csa->active = 0; + if (csa->card->active) + return (csa->card->active(!(csa->active && old))); + return 0; } @@ -457,6 +463,18 @@ csa_startdsp(csa_res *resp) return (0); } +static int +csa_stopdsp(csa_res *resp) +{ + /* + * Turn off the run, run at frame, and DMA enable bits in + * the local copy of the SP control register. + */ + csa_writemem(resp, BA1_SPCR, 0); + + return (0); +} + static int csa_setupchan(struct csa_chinfo *ch) { @@ -466,7 +484,7 @@ csa_setupchan(struct csa_chinfo *ch) if (ch->dir == PCMDIR_PLAY) { /* direction */ - csa_writemem(resp, BA1_PBA, vtophys(sndbuf_getbuf(ch->buffer))); + csa_writemem(resp, BA1_PBA, sndbuf_getbufaddr(ch->buffer)); /* format */ csa->pfie = csa_readmem(resp, BA1_PFIE) & ~0x0000f03f; @@ -495,7 +513,7 @@ csa_setupchan(struct csa_chinfo *ch) csa_setplaysamplerate(resp, ch->spd); } else if (ch->dir == PCMDIR_REC) { /* direction */ - csa_writemem(resp, BA1_CBA, vtophys(sndbuf_getbuf(ch->buffer))); + csa_writemem(resp, BA1_CBA, sndbuf_getbufaddr(ch->buffer)); /* format */ csa_writemem(resp, BA1_CIE, (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); @@ -519,7 +537,8 @@ csachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, csa->parent_dmat, CS461x_BUFFSIZE) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, csa->parent_dmat, CS461x_BUFFSIZE) != 0) + return NULL; return ch; } @@ -584,11 +603,11 @@ csachan_getptr(kobj_t obj, void *data) resp = &csa->res; if (ch->dir == PCMDIR_PLAY) { - ptr = csa_readmem(resp, BA1_PBA) - vtophys(sndbuf_getbuf(ch->buffer)); + ptr = csa_readmem(resp, BA1_PBA) - sndbuf_getbufaddr(ch->buffer); if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) ptr >>= 1; } else { - ptr = csa_readmem(resp, BA1_CBA) - vtophys(sndbuf_getbuf(ch->buffer)); + ptr = csa_readmem(resp, BA1_CBA) - sndbuf_getbufaddr(ch->buffer); if ((ch->fmt & AFMT_U8) != 0 || (ch->fmt & AFMT_S8) != 0) ptr >>= 1; } @@ -651,6 +670,14 @@ csa_init(struct csa_info *csa) /* Crank up the power on the DAC and ADC. */ csa_setplaysamplerate(resp, 8000); csa_setcapturesamplerate(resp, 8000); + /* Set defaults */ + csa_writeio(resp, BA0_EGPIODR, EGPIODR_GPOE0); + csa_writeio(resp, BA0_EGPIOPTR, EGPIOPTR_GPPT0); + /* Power up amplifier */ + csa_writeio(resp, BA0_EGPIODR, csa_readio(resp, BA0_EGPIODR) | + EGPIODR_GPOE2); + csa_writeio(resp, BA0_EGPIOPTR, csa_readio(resp, BA0_EGPIOPTR) | + EGPIOPTR_GPPT2); return 0; } @@ -663,17 +690,20 @@ csa_allocres(struct csa_info *csa, device_t dev) resp = &csa->res; if (resp->io == NULL) { - resp->io = bus_alloc_resource(dev, SYS_RES_MEMORY, &resp->io_rid, 0, ~0, 1, RF_ACTIVE); + resp->io = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &resp->io_rid, 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, 1, RF_ACTIVE); + resp->mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &resp->mem_rid, 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); + resp->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &resp->irq_rid, RF_ACTIVE | RF_SHAREABLE); if (resp->irq == NULL) return (1); } @@ -682,7 +712,8 @@ csa_allocres(struct csa_info *csa, device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/CS461x_BUFFSIZE, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &csa->parent_dmat) != 0) + /*flags*/0, + &csa->parent_dmat) != 0) return (1); return (0); @@ -694,6 +725,8 @@ csa_releaseres(struct csa_info *csa, device_t dev) { csa_res *resp; + KASSERT(csa != NULL, ("called with bogus resource structure")); + resp = &csa->res; if (resp->irq != NULL) { if (csa->ih) @@ -713,10 +746,8 @@ csa_releaseres(struct csa_info *csa, device_t dev) bus_dma_tag_destroy(csa->parent_dmat); csa->parent_dmat = NULL; } - if (csa != NULL) { - kfree(csa, M_DEVBUF); - csa = NULL; - } + + kfree(csa, M_DEVBUF); } static int @@ -764,8 +795,8 @@ pcmcsa_attach(device_t dev) /* Allocate the resources. */ resp = &csa->res; - resp->io_rid = PCIR_MAPS; - resp->mem_rid = PCIR_MAPS + 4; + resp->io_rid = PCIR_BAR(0); + resp->mem_rid = PCIR_BAR(1); resp->irq_rid = 0; if (csa_allocres(csa, dev)) { csa_releaseres(csa, dev); @@ -790,10 +821,11 @@ pcmcsa_attach(device_t dev) return (ENXIO); } - ksnprintf(status, SND_STATUSLEN, "at irq %ld", rman_get_start(resp->irq)); + ksnprintf(status, SND_STATUSLEN, "at irq %ld %s", + rman_get_start(resp->irq),PCM_KLDSTRING(snd_csa)); /* Enable interrupt. */ - if (snd_setup_intr(dev, resp->irq, INTR_MPSAFE, csa_intr, csa, &csa->ih, NULL)) { + if (snd_setup_intr(dev, resp->irq, 0, csa_intr, csa, &csa->ih)) { ac97_destroy(codec); csa_releaseres(csa, dev); return (ENXIO); @@ -830,11 +862,169 @@ pcmcsa_detach(device_t dev) return 0; } +static void +csa_ac97_suspend(struct csa_info *csa) +{ + int count, i; + uint32_t tmp; + + for (count = 0x2, i=0; + (count <= CS461x_AC97_HIGHESTREGTORESTORE) && + (i < CS461x_AC97_NUMBER_RESTORE_REGS); + count += 2, i++) + csa_readcodec(&csa->res, BA0_AC97_RESET + count, &csa->ac97[i]); + + /* mute the outputs */ + csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME, 0x8000); + csa_writecodec(&csa->res, BA0_AC97_HEADPHONE_VOLUME, 0x8000); + csa_writecodec(&csa->res, BA0_AC97_MASTER_VOLUME_MONO, 0x8000); + csa_writecodec(&csa->res, BA0_AC97_PCM_OUT_VOLUME, 0x8000); + /* save the registers that cause pops */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &csa->ac97_powerdown); + csa_readcodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, + &csa->ac97_general_purpose); + + /* + * And power down everything on the AC97 codec. Well, for now, + * only power down the DAC/ADC and MIXER VREFON components. + * trouble with removing VREF. + */ + + /* MIXVON */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, + tmp | CS_AC97_POWER_CONTROL_MIXVON); + /* ADC */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, + tmp | CS_AC97_POWER_CONTROL_ADC); + /* DAC */ + csa_readcodec(&csa->res, BA0_AC97_POWERDOWN, &tmp); + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, + tmp | CS_AC97_POWER_CONTROL_DAC); +} + +static void +csa_ac97_resume(struct csa_info *csa) +{ + int count, i; + + /* + * First, we restore the state of the general purpose register. This + * contains the mic select (mic1 or mic2) and if we restore this after + * we restore the mic volume/boost state and mic2 was selected at + * suspend time, we will end up with a brief period of time where mic1 + * is selected with the volume/boost settings for mic2, causing + * acoustic feedback. So we restore the general purpose register + * first, thereby getting the correct mic selected before we restore + * the mic volume/boost. + */ + csa_writecodec(&csa->res, BA0_AC97_GENERAL_PURPOSE, + csa->ac97_general_purpose); + /* + * Now, while the outputs are still muted, restore the state of power + * on the AC97 part. + */ + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, csa->ac97_powerdown); + /* + * Restore just the first set of registers, from register number + * 0x02 to the register number that ulHighestRegToRestore specifies. + */ + for (count = 0x2, i=0; + (count <= CS461x_AC97_HIGHESTREGTORESTORE) && + (i < CS461x_AC97_NUMBER_RESTORE_REGS); + count += 2, i++) + csa_writecodec(&csa->res, BA0_AC97_RESET + count, csa->ac97[i]); +} + +static int +pcmcsa_suspend(device_t dev) +{ + struct csa_info *csa; + csa_res *resp; + + csa = pcm_getdevinfo(dev); + resp = &csa->res; + + csa_active(csa, 1); + + /* playback interrupt disable */ + csa_writemem(resp, BA1_PFIE, + (csa_readmem(resp, BA1_PFIE) & ~0x0000f03f) | 0x00000010); + /* capture interrupt disable */ + csa_writemem(resp, BA1_CIE, + (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000011); + csa_stopplaydma(csa); + csa_stopcapturedma(csa); + + csa_ac97_suspend(csa); + + csa_resetdsp(resp); + + csa_stopdsp(resp); + /* + * Power down the DAC and ADC. For now leave the other areas on. + */ + csa_writecodec(&csa->res, BA0_AC97_POWERDOWN, 0x300); + /* + * Power down the PLL. + */ + csa_writemem(resp, BA0_CLKCR1, 0); + /* + * Turn off the Processor by turning off the software clock + * enable flag in the clock control register. + */ + csa_writemem(resp, BA0_CLKCR1, + csa_readmem(resp, BA0_CLKCR1) & ~CLKCR1_SWCE); + + csa_active(csa, -1); + + return 0; +} + +static int +pcmcsa_resume(device_t dev) +{ + struct csa_info *csa; + csa_res *resp; + + csa = pcm_getdevinfo(dev); + resp = &csa->res; + + csa_active(csa, 1); + + /* cs_hardware_init */ + csa_stopplaydma(csa); + csa_stopcapturedma(csa); + csa_ac97_resume(csa); + if (csa_startdsp(resp)) + return (ENXIO); + /* Enable interrupts on the part. */ + if ((csa_readio(resp, BA0_HISR) & HISR_INTENA) == 0) + csa_writeio(resp, BA0_HICR, HICR_IEV | HICR_CHGM); + /* playback interrupt enable */ + csa_writemem(resp, BA1_PFIE, csa_readmem(resp, BA1_PFIE) & ~0x0000f03f); + /* capture interrupt enable */ + csa_writemem(resp, BA1_CIE, + (csa_readmem(resp, BA1_CIE) & ~0x0000003f) | 0x00000001); + /* cs_restart_part */ + csa_setupchan(&csa->pch); + csa_startplaydma(csa); + csa_setupchan(&csa->rch); + csa_startcapturedma(csa); + + csa_active(csa, -1); + + return 0; +} + static device_method_t pcmcsa_methods[] = { /* Device interface */ DEVMETHOD(device_probe , pcmcsa_probe ), DEVMETHOD(device_attach, pcmcsa_attach), DEVMETHOD(device_detach, pcmcsa_detach), + DEVMETHOD(device_suspend, pcmcsa_suspend), + DEVMETHOD(device_resume, pcmcsa_resume), { 0, 0 }, }; @@ -846,6 +1036,6 @@ static driver_t pcmcsa_driver = { }; 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, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_DEPEND(snd_csapcm, snd_csa, 1, 1, 1); MODULE_VERSION(snd_csapcm, 1); diff --git a/sys/dev/sound/pci/csareg.h b/sys/dev/sound/pci/csareg.h index 37bcd5f020..ff2badbb7a 100644 --- a/sys/dev/sound/pci/csareg.h +++ b/sys/dev/sound/pci/csareg.h @@ -27,8 +27,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/csareg.h,v 1.2.2.2 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/csareg.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/csareg.h,v 1.4 2005/06/27 07:43:57 glebius Exp $ + * $DragonFly: src/sys/dev/sound/pci/csareg.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _CSA_REG_H @@ -1932,6 +1932,24 @@ #define BA1_CCST 0x13c /* BA1_CAPTURE_CONSTANT_REG */ #define BA1_CSPB 0x340 /* BA1_CAPTURE_SPB_ADDRESS */ +/* PM state definitions */ +#define CS461x_AC97_HIGHESTREGTORESTORE 0x26 +#define CS461x_AC97_NUMBER_RESTORE_REGS (CS461x_AC97_HIGHESTREGTORESTORE/2-1) + +#define CS_POWER_DAC 0x0001 +#define CS_POWER_ADC 0x0002 +#define CS_POWER_MIXVON 0x0004 +#define CS_POWER_MIXVOFF 0x0008 +#define CS_AC97_POWER_CONTROL_ON 0xf000 /* always on bits (inverted) */ +#define CS_AC97_POWER_CONTROL_ADC 0x0100 +#define CS_AC97_POWER_CONTROL_DAC 0x0200 +#define CS_AC97_POWER_CONTROL_MIXVON 0x0400 +#define CS_AC97_POWER_CONTROL_MIXVOFF 0x0800 +#define CS_AC97_POWER_CONTROL_ADC_ON 0x0001 +#define CS_AC97_POWER_CONTROL_DAC_ON 0x0002 +#define CS_AC97_POWER_CONTROL_MIXVON_ON 0x0004 +#define CS_AC97_POWER_CONTROL_MIXVOFF_ON 0x0008 + /* The following struct holds the initialization array. */ /* diff --git a/sys/dev/sound/pci/csavar.h b/sys/dev/sound/pci/csavar.h index 825447fb94..d589a4d8e5 100644 --- a/sys/dev/sound/pci/csavar.h +++ b/sys/dev/sound/pci/csavar.h @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/csavar.h,v 1.2.2.2 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/csavar.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/csavar.h,v 1.5 2005/06/27 07:43:57 glebius Exp $ + * $DragonFly: src/sys/dev/sound/pci/csavar.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _CSA_VAR_H @@ -67,4 +67,5 @@ void csa_writeio(csa_res *resp, u_long offset, u_int32_t data); u_int32_t csa_readmem(csa_res *resp, u_long offset); void csa_writemem(csa_res *resp, u_long offset, u_int32_t data); +void csa_resetdsp(csa_res *resp); #endif /* _CSA_VAR_H */ diff --git a/sys/dev/sound/pci/ds1-fw.h b/sys/dev/sound/pci/ds1-fw.h index c16e270bad..d9e54e4817 100644 --- a/sys/dev/sound/pci/ds1-fw.h +++ b/sys/dev/sound/pci/ds1-fw.h @@ -1,4 +1,5 @@ -/* ============================================================================= +/*- + * ============================================================================= * Copyright (c) 1997-1999 Yamaha Corporation. All Rights Reserved. * * Title: @@ -29,8 +30,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/ds1-fw.h,v 1.1.2.4 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/ds1-fw.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/ds1-fw.h,v 1.4 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/ds1-fw.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _HWMCODE_ #define _HWMCODE_ diff --git a/sys/dev/sound/pci/ds1.c b/sys/dev/sound/pci/ds1.c index cfea2d7471..985437ff5f 100644 --- a/sys/dev/sound/pci/ds1.c +++ b/sys/dev/sound/pci/ds1.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Cameron Grant * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/ds1.c,v 1.8.2.9 2003/04/28 03:59:03 simokawa Exp $ - * $DragonFly: src/sys/dev/sound/pci/ds1.c,v 1.7 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/ds1.c,v 1.43.2.1 2006/01/18 01:05:34 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/ds1.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -36,7 +36,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ds1.c,v 1.7 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ds1.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); /* -------------------------------------------------------------------- */ @@ -119,7 +119,7 @@ struct sc_info { struct resource *reg, *irq; int regid, irqid; void *ih; - void *lock; + struct spinlock *lock; void *regbase; u_int32_t *pbase, pbankbase, pbanksize; @@ -364,7 +364,7 @@ ds_allocpslot(struct sc_info *sc) } static int -ds_initpbank(volatile struct pbank *pb, int ch, int b16, int stereo, u_int32_t rate, void *base, u_int32_t len) +ds_initpbank(volatile struct pbank *pb, int ch, int b16, int stereo, u_int32_t rate, bus_addr_t base, u_int32_t len) { u_int32_t lv[] = {1, 1, 0, 0, 0}; u_int32_t rv[] = {1, 0, 1, 0, 0}; @@ -399,7 +399,7 @@ ds_initpbank(volatile struct pbank *pb, int ch, int b16, int stereo, u_int32_t r pb->Format |= b16? 0 : 0x80000000; pb->Format |= (stereo && (ch == 2 || ch == 4))? 0x00000001 : 0; pb->LoopDefault = 0; - pb->PgBase = base? vtophys(base) : 0; + pb->PgBase = base? base : 0; pb->PgLoop = 0; pb->PgLoopEnd = len >> ss; pb->PgLoopFrac = 0; @@ -433,18 +433,18 @@ static void ds_setuppch(struct sc_pchinfo *ch) { int stereo, b16, c, sz; - void *buf; + bus_addr_t addr; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; c = stereo? 1 : 0; - buf = sndbuf_getbuf(ch->buffer); + addr = sndbuf_getbufaddr(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); + ds_initpbank(ch->lslot, c, stereo, b16, ch->spd, addr, sz); + ds_initpbank(ch->lslot + 1, c, stereo, b16, ch->spd, addr, sz); + ds_initpbank(ch->rslot, 2, stereo, b16, ch->spd, addr, sz); + ds_initpbank(ch->rslot + 1, 2, stereo, b16, ch->spd, addr, sz); } static void @@ -453,16 +453,16 @@ 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; + bus_addr_t addr; stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; - buf = sndbuf_getbuf(ch->buffer); + addr = sndbuf_getbufaddr(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].PgBase = addr; ch->slot[i].PgLoopEnd = sz; ch->slot[i].PgStart = 0; ch->slot[i].NumOfLoops = 0; @@ -493,7 +493,7 @@ ds1pchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->fmt = AFMT_U8; ch->spd = 8000; ch->run = 0; - if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) == -1) + if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) != 0) return NULL; else { ch->lsnum = sc->pslotfree; @@ -529,12 +529,13 @@ static int ds1pchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_pchinfo *ch = data; + struct sc_info *sc = ch->parent; 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); + blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4); + sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize); return blocksize; } @@ -623,7 +624,7 @@ ds1rchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->dir = dir; ch->fmt = AFMT_U8; ch->spd = 8000; - if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) == -1) + if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, sc->bufsz) != 0) return NULL; else { ch->slot = (ch->num == DS1_RECPRIMARY)? sc->rbank + 2: sc->rbank; @@ -656,12 +657,13 @@ static int ds1rchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct sc_rchinfo *ch = data; + struct sc_info *sc = ch->parent; 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); + blocksize = roundup2((drate << 8) / DS1_IRQHZ, 4); + sndbuf_resize(ch->buffer, sc->bufsz / blocksize, blocksize); return blocksize; } @@ -744,13 +746,17 @@ ds_intr(void *p) for (i = 0; i < DS1_CHANS; i++) { if (sc->pch[i].run) { x = 1; + snd_mtxunlock(sc->lock); chn_intr(sc->pch[i].channel); + snd_mtxlock(sc->lock); } } for (i = 0; i < 2; i++) { if (sc->rch[i].run) { x = 1; + snd_mtxunlock(sc->lock); chn_intr(sc->rch[i].channel); + snd_mtxlock(sc->lock); } } i = ds_rd(sc, YDSXGR_MODE, 4); @@ -831,7 +837,8 @@ ds_init(struct sc_info *sc) if (sc->regbase == NULL) { if (bus_dma_tag_create(NULL, 2, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, - NULL, NULL, memsz, 1, memsz, 0, &sc->control_dmat)) + NULL, NULL, memsz, 1, memsz, 0, + &sc->control_dmat)) return -1; if (bus_dmamem_alloc(sc->control_dmat, &buf, BUS_DMA_NOWAIT, &sc->map)) return -1; @@ -923,7 +930,7 @@ ds_pci_probe(device_t dev) i = ds_finddev(pci_get_devid(dev), subdev); if (i >= 0) { device_set_desc(dev, ds_devs[i].name); - return 0; + return BUS_PROBE_DEFAULT; } else return ENXIO; } @@ -953,9 +960,9 @@ ds_pci_attach(device_t dev) 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); + sc->regid = PCIR_BAR(0); + sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regid, + RF_ACTIVE); if (!sc->reg) { device_printf(dev, "unable to map register space\n"); goto bad; @@ -971,7 +978,8 @@ ds_pci_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &sc->buffer_dmat) != 0) { + /*flags*/0, + &sc->buffer_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -985,18 +993,29 @@ ds_pci_attach(device_t dev) codec = AC97_CREATE(dev, sc, ds_ac97); if (codec == NULL) goto bad; + /* + * Turn on inverted external amplifier sense flags for few + * 'special' boards. + */ + switch (subdev) { + case 0x81171033: /* NEC ValueStar (VT550/0) */ + ac97_setflags(codec, ac97_getflags(codec) | AC97_F_EAPD_INV); + break; + default: + break; + } 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 || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ds_intr, sc, &sc->ih, NULL)) { + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ds_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } - ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld", - rman_get_start(sc->reg), rman_get_start(sc->irq)); + ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", + rman_get_start(sc->reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_ds1)); if (pcm_register(dev, sc, DS1_CHANS, 2)) goto bad; @@ -1083,5 +1102,5 @@ static driver_t ds1_driver = { }; DRIVER_MODULE(snd_ds1, pci, ds1_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_ds1, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_ds1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_ds1, 1); diff --git a/sys/dev/sound/pci/ds1.h b/sys/dev/sound/pci/ds1.h index 3093e686cf..d0cb0deca2 100644 --- a/sys/dev/sound/pci/ds1.h +++ b/sys/dev/sound/pci/ds1.h @@ -5,8 +5,8 @@ * author : Taichi Sugiyama * create Data : 28/Sep/99 * ======================================================================= - * $FreeBSD: src/sys/dev/sound/pci/ds1.h,v 1.1.2.4 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/ds1.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/ds1.h,v 1.1 2000/05/31 03:21:36 cg Exp $ + * $DragonFly: src/sys/dev/sound/pci/ds1.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ diff --git a/sys/dev/sound/pci/emu10k1.c b/sys/dev/sound/pci/emu10k1.c index d9de8b656f..2d639229cf 100644 --- a/sys/dev/sound/pci/emu10k1.c +++ b/sys/dev/sound/pci/emu10k1.c @@ -1,5 +1,7 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 2004 David O'Brien + * Copyright (c) 2003 Orlando Bassotto + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,40 +25,65 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/emu10k1.c,v 1.6.2.9 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/emu10k1.c,v 1.11 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/emu10k1.c,v 1.55.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/emu10k1.c,v 1.12 2007/01/04 21:47:02 corecode Exp $ */ #include #include -#include "gnu/emu10k1.h" +#include +#include "emu10k1-alsa%diked.h" #include #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/emu10k1.c,v 1.11 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/emu10k1.c,v 1.12 2007/01/04 21:47:02 corecode Exp $"); /* -------------------------------------------------------------------- */ -#define EMU10K1_PCI_ID 0x00021102 -#define EMU10K2_PCI_ID 0x00041102 -#define EMU10K1X_PCI_ID 0x00061102 -#define EMU_DEFAULT_BUFSZ 4096 -#define EMU_MAX_CHANS 8 -#undef EMUDEBUG +#define NUM_G 64 /* use all channels */ +#define WAVEOUT_MAXBUFSIZE 32768 +#define EMUPAGESIZE 4096 /* don't change */ +#define EMUMAXPAGES (WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE) +#define EMU10K1_PCI_ID 0x00021102 /* 1102 => Creative Labs Vendor ID */ +#define EMU10K2_PCI_ID 0x00041102 +#define EMU10K3_PCI_ID 0x00081102 +#define EMU_DEFAULT_BUFSZ 4096 +#define EMU_MAX_CHANS 8 +#define EMU_CHANS 4 + +#define MAXREQVOICES 8 +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_FXSENDS 4 + +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 + +#define ENABLE 0xffffffff +#define DISABLE 0x00000000 +#define ENV_ON DCYSUSV_CHANNELENABLE_MASK +#define ENV_OFF 0x00 /* XXX: should this be 1? */ + +#define A_IOCFG_GPOUT_A 0x40 /* Analog Output */ +#define A_IOCFG_GPOUT_D 0x04 /* Digital Output */ +#define A_IOCFG_GPOUT_AD (A_IOCFG_GPOUT_A|A_IOCFG_GPOUT_D) /* A_IOCFG_GPOUT0 */ struct emu_memblk { SLIST_ENTRY(emu_memblk) link; void *buf; + bus_addr_t buf_addr; u_int32_t pte_start, pte_size; }; struct emu_mem { - u_int8_t bmap[MAXPAGES / 8]; + u_int8_t bmap[EMUMAXPAGES / 8]; u_int32_t *ptb_pages; void *silent_page; - SLIST_HEAD(, emu_memblk) blocks; + bus_addr_t silent_page_addr; + bus_addr_t ptb_pages_addr; + SLIST_HEAD(, emu_memblk) blocks; }; struct emu_voice { @@ -93,7 +120,7 @@ struct sc_rchinfo { /* device private data */ struct sc_info { device_t dev; - u_int32_t type, rev; + u_int32_t type, rev; u_int32_t tos_link:1, APS:1, audigy:1, audigy2:1; u_int32_t addrmask; /* wider if audigy */ @@ -103,7 +130,7 @@ struct sc_info { struct resource *reg, *irq; void *ih; - void *lock; + struct spinlock *lock; unsigned int bufsz; int timer, timerinterval; @@ -124,8 +151,8 @@ struct sc_info { /* 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 void *emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr); +static void *emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr); static int emu_memfree(struct sc_info *sc, void *buf); static int emu_memstart(struct sc_info *sc, void *buf); #ifdef EMUDEBUG @@ -172,7 +199,9 @@ static struct pcmchan_caps emu_playcaps = {4000, 48000, emu_pfmt, 0}; static int adcspeed[8] = {48000, 44100, 32000, 24000, 22050, 16000, 11025, 8000}; /* audigy supports 12kHz. */ -static int audigy_adcspeed[9] = {48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000}; +static int audigy_adcspeed[9] = { + 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000 +}; /* -------------------------------------------------------------------- */ /* Hardware */ @@ -267,15 +296,15 @@ 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), + KOBJMETHOD(ac97_read, emu_rdcd), + KOBJMETHOD(ac97_write, emu_wrcd), { 0, 0 } }; AC97_DECLARE(emu_ac97); @@ -293,7 +322,8 @@ emu_settimer(struct sc_info *sc) for (i = 0; i < sc->nchans; i++) { pch = &sc->pch[i]; if (pch->buffer) { - tmp = (pch->spd * sndbuf_getbps(pch->buffer)) / pch->blksz; + tmp = (pch->spd * sndbuf_getbps(pch->buffer)) + / pch->blksz; if (tmp > rate) rate = tmp; } @@ -302,7 +332,8 @@ emu_settimer(struct sc_info *sc) for (i = 0; i < 3; i++) { rch = &sc->rch[i]; if (rch->buffer) { - tmp = (rch->spd * sndbuf_getbps(rch->buffer)) / rch->blksz; + tmp = (rch->spd * sndbuf_getbps(rch->buffer)) + / rch->blksz; if (tmp > rate) rate = tmp; } @@ -336,7 +367,7 @@ emu_enatimer(struct sc_info *sc, int go) static void emu_enastop(struct sc_info *sc, char channel, int enable) { - int reg = (channel & 0x20)? SOLEH : SOLEL; + int reg = (channel & 0x20) ? SOLEH : SOLEL; channel &= 0x1f; reg |= 1 << 24; reg |= channel << 16; @@ -410,9 +441,9 @@ emu_rate_to_pitch(u_int32_t rate) 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)]); + logMagTable[0x7f & (rate >> 24)] + + (0x7f & (rate >> 17)) * + logSlopeTable[0x7f & (rate >> 24)]); } rate <<= 1; } @@ -447,8 +478,9 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, u_int32_t sz, struct snd_dbuf *b) { void *buf; + bus_addr_t tmp_addr; - buf = emu_memalloc(sc, sz); + buf = emu_memalloc(sc, sz, &tmp_addr); if (buf == NULL) return -1; if (b != NULL) @@ -462,19 +494,15 @@ emu_vinit(struct sc_info *sc, struct emu_voice *m, struct emu_voice *s, m->running = 0; m->ismaster = 1; m->vol = 0xff; - m->buf = vtophys(buf); + m->buf = tmp_addr; m->slave = s; if (sc->audigy) { - m->fxrt1 = A_FXBUS_PCM_LEFT | A_FXBUS_PCM_RIGHT << 8 | - A_FXBUS_PCM_LEFT_REAR << 16 | - A_FXBUS_PCM_RIGHT_REAR << 24; - m->fxrt2 = A_FXBUS_PCM_CENTER | A_FXBUS_PCM_LFE << 8 | - A_FXBUS_MIDI_CHORUS << 16 | - A_FXBUS_MIDI_REVERB << 24; + m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 8 | + FXBUS_PCM_LEFT << 16 | FXBUS_MIDI_REVERB << 24; + m->fxrt2 = 0x3f3f3f3f; /* No effects on second route */ } else { - m->fxrt1 = FXBUS_PCM_LEFT | FXBUS_PCM_RIGHT << 4 | - FXBUS_MIDI_CHORUS << 8 | - FXBUS_MIDI_REVERB << 12; + m->fxrt1 = FXBUS_MIDI_CHORUS | FXBUS_PCM_RIGHT << 4 | + FXBUS_PCM_LEFT << 8 | FXBUS_MIDI_REVERB << 12; m->fxrt2 = 0; } @@ -502,8 +530,8 @@ 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; + 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; @@ -523,20 +551,20 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) int l, r, x, y; u_int32_t sa, ea, start, val, silent_page; - s = (v->stereo? 1 : 0) + (v->b16? 1 : 0); + 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; + 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; + 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; if (sc->audigy) { @@ -550,12 +578,13 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) 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, 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; + silent_page = ((u_int32_t)(sc->mem.silent_page_addr) << 1) + | MAP_PTI_MASK; emu_wrptr(sc, v->vnum, MAPA, silent_page); emu_wrptr(sc, v->vnum, MAPB, silent_page); @@ -570,7 +599,8 @@ emu_vwrite(struct sc_info *sc, struct emu_voice *v) 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, ATKHLDV, + ATKHLDV_HOLDTIME_MASK | ATKHLDV_ATTACKTIME_MASK); emu_wrptr(sc, v->vnum, ENVVOL, 0x8000); emu_wrptr(sc, v->vnum, PEFE_FILTERAMOUNT, 0x7f); @@ -589,10 +619,10 @@ emu_vtrigger(struct sc_info *sc, struct emu_voice *v, int go) if (go) { cra = 64; - cs = v->stereo? 4 : 2; - ccis = v->stereo? 28 : 30; - ccis *= v->b16? 1 : 2; - sample = v->b16? 0x00000000 : 0x80808080; + 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); @@ -629,7 +659,7 @@ emu_vpos(struct sc_info *sc, struct emu_voice *v) { int s, ptr; - s = (v->b16? 1 : 0) + (v->stereo? 1 : 0); + s = (v->b16 ? 1 : 0) + (v->stereo ? 1 : 0); ptr = (emu_rdptr(sc, v->vnum, CCCA_CURRADDR) - (v->start >> s)) << s; return ptr & ~0x0000001f; } @@ -638,16 +668,20 @@ emu_vpos(struct sc_info *sc, struct emu_voice *v) 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" }; - char *regname2[] = { "mudata1", "mustat1", "mudata2", "mustat2", - "fxwc1", "fxwc2", "spdrate", NULL, NULL, - NULL, NULL, NULL, "fxrt2", "sndamnt", "fxrt1", - NULL, NULL }; + 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" + }; + char *regname2[] = { + "mudata1", "mustat1", "mudata2", "mustat2", + "fxwc1", "fxwc2", "spdrate", NULL, NULL, + NULL, NULL, NULL, "fxrt2", "sndamnt", "fxrt1", + NULL, NULL + }; int i, x; kprintf("voice number %d\n", v->vnum); @@ -655,7 +689,7 @@ emu_vdump(struct sc_info *sc, struct emu_voice *v) if (regname[i] == NULL) continue; kprintf("%s\t[%08x]", regname[i], emu_rdptr(sc, v->vnum, i)); - kprintf("%s", (x == 2)? "\n" : "\t"); + kprintf("%s", (x == 2) ? "\n" : "\t"); x++; if (x > 2) x = 0; @@ -666,21 +700,22 @@ emu_vdump(struct sc_info *sc, struct emu_voice *v) for (i = 0; i <= 0xe; i++) { if (regname2[i] == NULL) continue; - kprintf("%s\t[%08x]", regname2[i], emu_rdptr(sc, v->vnum, i + 0x70)); + kprintf("%s\t[%08x]", regname2[i], + emu_rdptr(sc, v->vnum, i + 0x70)); kprintf("%s", (x == 2)? "\n" : "\t"); x++; if (x > 2) x = 0; } } - kprintf("\n\n"); } #endif /* channel interface */ static void * -emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_pchinfo *ch; @@ -697,8 +732,9 @@ emupchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel snd_mtxlock(sc->lock); ch->master = emu_valloc(sc); ch->slave = emu_valloc(sc); - r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer))? NULL : ch; snd_mtxunlock(sc->lock); + r = (emu_vinit(sc, ch->master, ch->slave, sc->bufsz, ch->buffer)) + ? NULL : ch; return r; } @@ -768,14 +804,14 @@ emupchan_trigger(kobj_t obj, void *data, int go) emu_enatimer(sc, 1); #ifdef EMUDEBUG kprintf("start [%d bit, %s, %d hz]\n", - ch->master->b16? 16 : 8, - ch->master->stereo? "stereo" : "mono", + 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; + ch->run = (go == PCMTRIG_START) ? 1 : 0; emu_vtrigger(sc, ch->master, ch->run); snd_mtxunlock(sc->lock); return 0; @@ -802,21 +838,22 @@ emupchan_getcaps(kobj_t obj, void *data) } 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), + 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(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct sc_info *sc = devinfo; struct sc_rchinfo *ch; @@ -856,11 +893,11 @@ emurchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel break; } sc->rnum++; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) return NULL; else { snd_mtxlock(sc->lock); - emu_wrptr(sc, 0, ch->basereg, vtophys(sndbuf_getbuf(ch->buffer))); + emu_wrptr(sc, 0, ch->basereg, sndbuf_getbufaddr(ch->buffer)); emu_wrptr(sc, 0, ch->sizereg, 0); /* off */ snd_mtxunlock(sc->lock); return ch; @@ -1014,13 +1051,13 @@ emurchan_getcaps(kobj_t obj, void *data) } 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), + 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); @@ -1028,11 +1065,12 @@ CHANNEL_DECLARE(emurchan); /* -------------------------------------------------------------------- */ /* The interrupt handler */ static void -emu_intr(void *p) +emu_intr(void *data) { - struct sc_info *sc = (struct sc_info *)p; + struct sc_info *sc = data; u_int32_t stat, ack, i, x; + snd_mtxlock(sc->lock); while (1) { stat = emu_rd(sc, IPR, 4); if (stat == 0) @@ -1040,35 +1078,18 @@ emu_intr(void *p) ack = 0; /* process irq */ - if (stat & IPR_INTERVALTIMER) { + if (stat & IPR_INTERVALTIMER) ack |= IPR_INTERVALTIMER; - x = 0; - for (i = 0; i < sc->nchans; 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)) { + 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_EFXBUFFULL | IPR_EFXBUFHALFFULL)) { + + if (stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)) ack |= stat & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL); - if (sc->rch[1].channel) - chn_intr(sc->rch[1].channel); - } - if (stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL)) { + + if (stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL)) ack |= stat & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL); - 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"); @@ -1076,14 +1097,51 @@ emu_intr(void *p) } if (stat & IPR_SAMPLERATETRACKER) { ack |= IPR_SAMPLERATETRACKER; - /* device_printf(sc->dev, "sample rate tracker lock status change\n"); */ +#ifdef EMUDEBUG + device_printf(sc->dev, + "sample rate tracker lock status change\n"); +#endif } if (stat & ~ack) - device_printf(sc->dev, "dodgy irq: %x (harmless)\n", stat & ~ack); + device_printf(sc->dev, "dodgy irq: %x (harmless)\n", + stat & ~ack); emu_wr(sc, IPR, stat, 4); + + if (ack) { + snd_mtxunlock(sc->lock); + + if (ack & IPR_INTERVALTIMER) { + x = 0; + for (i = 0; i < sc->nchans; i++) { + if (sc->pch[i].run) { + x = 1; + chn_intr(sc->pch[i].channel); + } + } + if (x == 0) + emu_enatimer(sc, 0); + } + + + if (ack & (IPR_ADCBUFFULL | IPR_ADCBUFHALFFULL)) { + if (sc->rch[0].channel) + chn_intr(sc->rch[0].channel); + } + if (ack & (IPR_EFXBUFFULL | IPR_EFXBUFHALFFULL)) { + if (sc->rch[1].channel) + chn_intr(sc->rch[1].channel); + } + if (ack & (IPR_MICBUFFULL | IPR_MICBUFHALFFULL)) { + if (sc->rch[2].channel) + chn_intr(sc->rch[2].channel); + } + + snd_mtxlock(sc->lock); + } } + snd_mtxunlock(sc->lock); } /* -------------------------------------------------------------------- */ @@ -1091,27 +1149,28 @@ emu_intr(void *p) static void emu_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { - void **phys = arg; + bus_addr_t *phys = arg; - *phys = error? 0 : (void *)segs->ds_addr; + *phys = error ? 0 : (bus_addr_t)segs->ds_addr; if (bootverbose) { kprintf("emu: setmap (%lx, %lx), nseg=%d, error=%d\n", - (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, - nseg, error); + (unsigned long)segs->ds_addr, (unsigned long)segs->ds_len, + nseg, error); } } static void * -emu_malloc(struct sc_info *sc, u_int32_t sz) +emu_malloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr) { - void *buf, *phys = 0; + void *buf; bus_dmamap_t map; + *addr = 0; 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) + if (bus_dmamap_load(sc->parent_dmat, map, buf, sz, emu_setmap, addr, 0) + || !*addr) return NULL; return buf; } @@ -1123,7 +1182,7 @@ emu_free(struct sc_info *sc, void *buf) } static void * -emu_memalloc(struct sc_info *sc, u_int32_t sz) +emu_memalloc(struct sc_info *sc, u_int32_t sz, bus_addr_t *addr) { u_int32_t blksz, start, idx, ofs, tmp, found; struct emu_mem *mem = &sc->mem; @@ -1133,10 +1192,10 @@ emu_memalloc(struct sc_info *sc, u_int32_t sz) blksz = sz / EMUPAGESIZE; if (sz > (blksz * EMUPAGESIZE)) blksz++; - /* find a free block in the bitmap */ + /* find a kfree block in the bitmap */ found = 0; start = 1; - while (!found && start + blksz < MAXPAGES) { + while (!found && start + blksz < EMUMAXPAGES) { found = 1; for (idx = start; idx < start + blksz; idx++) if (mem->bmap[idx >> 3] & (1 << (idx & 7))) @@ -1149,7 +1208,8 @@ emu_memalloc(struct sc_info *sc, u_int32_t sz) blk = kmalloc(sizeof(*blk), M_DEVBUF, M_NOWAIT); if (blk == NULL) return NULL; - buf = emu_malloc(sc, sz); + buf = emu_malloc(sc, sz, &blk->buf_addr); + *addr = blk->buf_addr; if (buf == NULL) { kfree(blk, M_DEVBUF); return NULL; @@ -1157,12 +1217,18 @@ emu_memalloc(struct sc_info *sc, u_int32_t sz) blk->buf = buf; blk->pte_start = start; blk->pte_size = blksz; - /* kprintf("buf %p, pte_start %d, pte_size %d\n", blk->buf, blk->pte_start, blk->pte_size); */ +#ifdef EMUDEBUG + kprintf("buf %p, pte_start %d, pte_size %d\n", blk->buf, + blk->pte_start, blk->pte_size); +#endif 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); - /* kprintf("pte[%d] -> %x phys, %x virt\n", idx, tmp, ((u_int32_t)buf) + ofs); */ + tmp = (u_int32_t)(u_long)((u_int8_t *)blk->buf_addr + ofs); +#ifdef EMUDEBUG + kprintf("pte[%d] -> %x phys, %x virt\n", idx, tmp, + ((u_int32_t)buf) + ofs); +#endif mem->ptb_pages[idx] = (tmp << 1) | idx; ofs += EMUPAGESIZE; } @@ -1186,7 +1252,7 @@ emu_memfree(struct sc_info *sc, void *buf) return EINVAL; SLIST_REMOVE(&mem->blocks, blk, emu_memblk, link); emu_free(sc, buf); - tmp = (u_int32_t)vtophys(sc->mem.silent_page) << 1; + tmp = (u_int32_t)(sc->mem.silent_page_addr) << 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; @@ -1212,7 +1278,8 @@ emu_memstart(struct sc_info *sc, void *buf) } static void -emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, u_int32_t *pc) +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); @@ -1220,7 +1287,8 @@ emu_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, u_int32_t * } static void -audigy_addefxop(struct sc_info *sc, int op, int z, int w, int x, int y, u_int32_t *pc) +audigy_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 << 12) | y); emu_wrefx(sc, (*pc) * 2 + 1, (op << 24) | (z << 12) | w); @@ -1247,7 +1315,7 @@ audigy_initefx(struct sc_info *sc) /* Audigy 2 (EMU10K2) DSP Registers: FX Bus - 0x000-0x00f : 16 registers (?) + 0x000-0x00f : 16 registers (???) Input 0x040/0x041 : AC97 Codec (l/r) 0x042/0x043 : ADC, S/PDIF (l/r) @@ -1276,9 +1344,9 @@ audigy_initefx(struct sc_info *sc) 0x0cb = 0x10000000, 0x0cc = 0x20000000, 0x0cd = 0x40000000 0x0ce = 0x80000000, 0x0cf = 0x7fffffff, 0x0d0 = 0xffffffff 0x0d1 = 0xfffffffe, 0x0d2 = 0xc0000000, 0x0d3 = 0x41fbbcdc - 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (?) + 0x0d4 = 0x5a7ef9db, 0x0d5 = 0x00100000, 0x0dc = 0x00000001 (???) Temporary Values - 0x0d6 : Accumulator (?) + 0x0d6 : Accumulator (???) 0x0d7 : Condition Register 0x0d8 : Noise source 0x0d9 : Noise source @@ -1292,15 +1360,15 @@ audigy_initefx(struct sc_info *sc) /* AC97Output[l/r] = FXBus PCM[l/r] */ audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_L), A_C_00000000, - A_C_00000000, A_FXBUS(A_FXBUS_PCM_LEFT), &pc); + A_C_00000000, A_FXBUS(FXBUS_PCM_LEFT), &pc); audigy_addefxop(sc, iACC3, A_EXTOUT(A_EXTOUT_AC97_R), A_C_00000000, - A_C_00000000, A_FXBUS(A_FXBUS_PCM_RIGHT), &pc); + A_C_00000000, A_FXBUS(FXBUS_PCM_RIGHT), &pc); /* GPR[0/1] = RCA S/PDIF[l/r] -- Master volume */ audigy_addefxop(sc, iACC3, A_GPR(0), A_C_00000000, - A_C_00000000, A_EXTIN(A_EXTIN_RCA_SPDIF_L), &pc); + A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_L), &pc); audigy_addefxop(sc, iACC3, A_GPR(1), A_C_00000000, - A_C_00000000, A_EXTIN(A_EXTIN_RCA_SPDIF_R), &pc); + A_C_00000000, A_EXTIN(EXTIN_COAX_SPDIF_R), &pc); /* GPR[2] = GPR[0] (Left) / 2 + GPR[1] (Right) / 2 -- Central volume */ audigy_addefxop(sc, iINTERP, A_GPR(2), A_GPR(1), @@ -1437,7 +1505,7 @@ emu_initefx(struct sc_info *sc) 0x200 - 0x2ff Tank Memory Address Registers 0x300 - 0x3ff - */ + */ /* Routing - this will be configurable in later version */ @@ -1523,11 +1591,15 @@ emu_init(struct sc_info *sc) { u_int32_t spcs, ch, tmp, i; - /* enable additional AC97 slots */ - emu_wrptr(sc, 0, AC97SLOT, AC97SLOT_CNTR | AC97SLOT_LFE); + if (sc->audigy) { + /* enable additional AC97 slots */ + emu_wrptr(sc, 0, AC97SLOT, AC97SLOT_CNTR | AC97SLOT_LFE); + } - /* disable audio and lock cache */ - emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE, 4); + /* disable audio and lock cache */ + emu_wr(sc, HCFG, + HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, + 4); /* reset recording buffers */ emu_wrptr(sc, 0, MICBS, ADCBS_BUFSIZE_NONE); @@ -1538,7 +1610,9 @@ emu_init(struct sc_info *sc) emu_wrptr(sc, 0, ADCBA, 0); /* disable channel interrupt */ - emu_wr(sc, INTE, INTE_INTERVALTIMERENB | INTE_SAMPLERATETRACKER | INTE_PCIERRORENABLE, 4); + 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); @@ -1606,8 +1680,8 @@ emu_init(struct sc_info *sc) sc->voice[ch].start = 0; sc->voice[ch].end = 0; sc->voice[ch].channel = NULL; - } - sc->pnum = sc->rnum = 0; + } + sc->pnum = sc->rnum = 0; /* * Init to 0x02109204 : @@ -1624,16 +1698,16 @@ emu_init(struct sc_info *sc) * 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; + 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); if (!sc->audigy) emu_initefx(sc); - else if (sc->audigy2) { /* Audigy 2 */ + else if (sc->audigy2) { /* Audigy 2 */ /* from ALSA initialization code: */ /* Hack for Alice3 to work independent of haP16V driver */ @@ -1653,22 +1727,24 @@ emu_init(struct sc_info *sc) } SLIST_INIT(&sc->mem.blocks); - sc->mem.ptb_pages = emu_malloc(sc, MAXPAGES * sizeof(u_int32_t)); + sc->mem.ptb_pages = emu_malloc(sc, EMUMAXPAGES * sizeof(u_int32_t), + &sc->mem.ptb_pages_addr); if (sc->mem.ptb_pages == NULL) return -1; - sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE); + sc->mem.silent_page = emu_malloc(sc, EMUPAGESIZE, + &sc->mem.silent_page_addr); 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++) + tmp = (u_int32_t)(sc->mem.silent_page_addr) << 1; + for (i = 0; i < EMUMAXPAGES; i++) sc->mem.ptb_pages[i] = tmp | i; - emu_wrptr(sc, 0, PTB, vtophys(sc->mem.ptb_pages)); + emu_wrptr(sc, 0, PTB, (sc->mem.ptb_pages_addr)); emu_wrptr(sc, 0, TCB, 0); /* taken from original driver */ emu_wrptr(sc, 0, TCBS, 0); /* taken from original driver */ @@ -1700,11 +1776,12 @@ emu_init(struct sc_info *sc) * Lock Sound Memory = 0 * Auto Mute = 1 */ + if (sc->audigy) { tmp = HCFG_AUTOMUTE | HCFG_JOYENABLE; - if (sc->audigy2) /* Audigy 2 */ + if (sc->audigy2) /* Audigy 2 */ tmp = HCFG_AUDIOENABLE | HCFG_AC3ENABLE_CDSPDIF | - HCFG_AC3ENABLE_GPSPDIF; + HCFG_AC3ENABLE_GPSPDIF; emu_wr(sc, HCFG, tmp, 4); audigy_initefx(sc); @@ -1713,17 +1790,21 @@ emu_init(struct sc_info *sc) /* enable audio and disable both audio/digital outputs */ emu_wr(sc, HCFG, emu_rd(sc, HCFG, 4) | HCFG_AUDIOENABLE, 4); - emu_wr(sc, A_IOCFG, emu_rd(sc, A_IOCFG, 4) & ~A_IOCFG_GPOUT_AD, 4); - if (sc->audigy2) { /* Audigy 2 */ - /* Unmute Analog. Set GPO6 to 1 for Apollo. - * This has to be done after init Alice3 I2SOut beyond 48kHz. + emu_wr(sc, A_IOCFG, emu_rd(sc, A_IOCFG, 4) & ~A_IOCFG_GPOUT_AD, + 4); + if (sc->audigy2) { /* Audigy 2 */ + /* Unmute Analog. + * Set GPO6 to 1 for Apollo. This has to be done after + * init Alice3 I2SOut beyond 48kHz. * So, sequence is important. */ - emu_wr(sc, A_IOCFG, emu_rd(sc, A_IOCFG, 4) | A_IOCFG_GPOUT_A, 4); + emu_wr(sc, A_IOCFG, + emu_rd(sc, A_IOCFG, 4) | A_IOCFG_GPOUT_A, 4); } } else { /* EMU10K1 initialization code */ - tmp = HCFG_AUDIOENABLE | HCFG_AUTOMUTE | HCFG_LOCKTANKCACHE; + tmp = HCFG_AUDIOENABLE | HCFG_LOCKTANKCACHE_MASK + | HCFG_AUTOMUTE; if (sc->rev >= 6) tmp |= HCFG_JOYENABLE; @@ -1760,13 +1841,14 @@ emu_uninit(struct sc_info *sc) emu_wrptr(sc, ch, CPF, 0); } - if (sc->audigy) { - /* stop fx processor */ + if (sc->audigy) { /* stop fx processor */ emu_wrptr(sc, 0, A_DBG, A_DBG_SINGLE_STEP); } /* disable audio and lock cache */ - emu_wr(sc, HCFG, HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE | HCFG_MUTEBUTTONENABLE, 4); + emu_wr(sc, HCFG, + HCFG_LOCKSOUNDCACHE | HCFG_LOCKTANKCACHE_MASK | HCFG_MUTEBUTTONENABLE, + 4); emu_wrptr(sc, 0, PTB, 0); /* reset recording buffers */ @@ -1786,7 +1868,6 @@ emu_uninit(struct sc_info *sc) 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"); @@ -1805,10 +1886,6 @@ emu_pci_probe(device_t dev) case EMU10K1_PCI_ID: s = "Creative EMU10K1"; break; - - case EMU10K1X_PCI_ID: - s = "Creative SB Live! (Dell OEM)"; - break; case EMU10K2_PCI_ID: if (pci_get_revid(dev) == 0x04) @@ -1817,12 +1894,16 @@ emu_pci_probe(device_t dev) s = "Creative Audigy (EMU10K2)"; break; + case EMU10K3_PCI_ID: + s = "Creative Audigy 2 (EMU10K3)"; + break; + default: return ENXIO; } device_set_desc(dev, s); - return 0; + return BUS_PROBE_DEFAULT; } static int @@ -1843,7 +1924,7 @@ emu_pci_attach(device_t dev) sc->dev = dev; sc->type = pci_get_devid(dev); sc->rev = pci_get_revid(dev); - sc->audigy = (sc->type == EMU10K2_PCI_ID); + sc->audigy = sc->type == EMU10K2_PCI_ID || sc->type == EMU10K3_PCI_ID; sc->audigy2 = (sc->audigy && sc->rev == 0x04); sc->nchans = sc->audigy ? 8 : 4; sc->addrmask = sc->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK; @@ -1853,8 +1934,8 @@ emu_pci_attach(device_t dev) pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); - i = PCIR_MAPS; - sc->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &i, 0, ~0, 1, RF_ACTIVE); + i = PCIR_BAR(0); + sc->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &i, RF_ACTIVE); if (sc->reg == NULL) { device_printf(dev, "unable to map register space\n"); goto bad; @@ -1869,7 +1950,8 @@ emu_pci_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { + /*flags*/0, + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -1881,22 +1963,26 @@ emu_pci_attach(device_t dev) codec = AC97_CREATE(dev, sc, emu_ac97); if (codec == NULL) goto bad; - gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL)? 1 : 0; + gotmic = (ac97_getcaps(codec) & AC97_CAP_MICCHANNEL) ? 1 : 0; if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) goto bad; i = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &i, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih, NULL)) { + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || + snd_setup_intr(dev, sc->irq, INTR_MPSAFE, emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", rman_get_start(sc->reg), rman_get_start(sc->irq)); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + rman_get_start(sc->reg), rman_get_start(sc->irq), + PCM_KLDSTRING(snd_emu10k1)); - if (pcm_register(dev, sc, sc->nchans, gotmic? 3 : 2)) goto bad; + if (pcm_register(dev, sc, sc->nchans, gotmic ? 3 : 2)) goto bad; for (i = 0; i < sc->nchans; i++) pcm_addchan(dev, PCMDIR_PLAY, &emupchan_class, sc); - for (i = 0; i < (gotmic? 3 : 2); i++) + for (i = 0; i < (gotmic ? 3 : 2); i++) pcm_addchan(dev, PCMDIR_REC, &emurchan_class, sc); pcm_setstatus(dev, status); @@ -1905,7 +1991,7 @@ emu_pci_attach(device_t dev) bad: if (codec) ac97_destroy(codec); - if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS, sc->reg); + if (sc->reg) bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat); @@ -1928,7 +2014,7 @@ emu_pci_detach(device_t dev) /* shutdown chip */ emu_uninit(sc); - bus_release_resource(dev, SYS_RES_IOPORT, PCIR_MAPS, sc->reg); + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->reg); bus_teardown_intr(dev, sc->irq, sc->ih); bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); bus_dma_tag_destroy(sc->parent_dmat); @@ -1955,7 +2041,8 @@ static driver_t emu_driver = { }; DRIVER_MODULE(snd_emu10k1, pci, emu_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_emu10k1, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +DRIVER_MODULE(snd_emu10k1, cardbus, emu_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_emu10k1, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_emu10k1, 1); /* dummy driver to silence the joystick device */ @@ -1973,12 +2060,10 @@ emujoy_pci_probe(device_t dev) s = "Creative EMU10K2 Joystick"; device_quiet(dev); break; - default: - return ENXIO; } - device_set_desc(dev, s); - return -1000; + if (s) device_set_desc(dev, s); + return s ? -1000 : ENXIO; } static int diff --git a/sys/dev/sound/pci/es137x.c b/sys/dev/sound/pci/es137x.c index 7010a5aae3..070b1953ba 100644 --- a/sys/dev/sound/pci/es137x.c +++ b/sys/dev/sound/pci/es137x.c @@ -1,9 +1,9 @@ -/* +/*- * 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) 1999 Cameron Grant * Copyright (c) 1998 by Joachim Kuebart. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,8 +38,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/es137x.c,v 1.13.2.10 2002/05/07 17:02:25 greid Exp $ - * $DragonFly: src/sys/dev/sound/pci/es137x.c,v 1.8 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/es137x.c,v 1.55.2.2 2006/01/16 02:08:56 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/es137x.c,v 1.9 2007/01/04 21:47:02 corecode Exp $ */ /* @@ -62,7 +62,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/es137x.c,v 1.8 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/es137x.c,v 1.9 2007/01/04 21:47:02 corecode Exp $"); static int debug = 0; SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, ""); @@ -74,6 +74,7 @@ SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, ""); #define ES1371_PCI_ID 0x13711274 #define ES1371_PCI_ID2 0x13713274 #define CT5880_PCI_ID 0x58801274 +#define CT4730_PCI_ID 0x89381102 #define ES1371REV_ES1371_A 0x02 #define ES1371REV_ES1371_B 0x09 @@ -88,8 +89,19 @@ SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, ""); #define CT5880REV_CT5880_D 0x03 #define CT5880REV_CT5880_E 0x04 +#define CT4730REV_CT4730_A 0x00 + #define ES_DEFAULT_BUFSZ 4096 +/* 2 DAC for playback, 1 ADC for record */ +#define ES_DAC1 0 +#define ES_DAC2 1 +#define ES_ADC 2 +#define ES_NCHANS 3 + +#define ES1370_DAC1_MINSPEED 5512 +#define ES1370_DAC1_MAXSPEED 44100 + /* device private data */ struct es_info; @@ -97,10 +109,73 @@ struct es_chinfo { struct es_info *parent; struct pcm_channel *channel; struct snd_dbuf *buffer; - int dir, num; + struct pcmchan_caps caps; + int dir, num, index; u_int32_t fmt, blksz, bufsz; }; +/* + * 32bit Ensoniq Configuration (es->escfg). + * ---------------------------------------- + * + * +-------+--------+------+------+---------+--------+---------+---------+ + * len | 16 | 1 | 1 | 1 | 2 | 2 | 1 | 8 | + * +-------+--------+------+------+---------+--------+---------+---------+ + * | fixed | single | | | | | is | general | + * | rate | pcm | DACx | DACy | numplay | numrec | es1370? | purpose | + * | | mixer | | | | | | | + * +-------+--------+------+------+---------+--------+---------+---------+ + */ +#define ES_FIXED_RATE(cfgv) \ + (((cfgv) & 0xffff0000) >> 16) +#define ES_SET_FIXED_RATE(cfgv, nv) \ + (((cfgv) & ~0xffff0000) | (((nv) & 0xffff) << 16)) +#define ES_SINGLE_PCM_MIX(cfgv) \ + (((cfgv) & 0x8000) >> 15) +#define ES_SET_SINGLE_PCM_MIX(cfgv, nv) \ + (((cfgv) & ~0x8000) | (((nv) ? 1 : 0) << 15)) +#define ES_DAC_FIRST(cfgv) \ + (((cfgv) & 0x4000) >> 14) +#define ES_SET_DAC_FIRST(cfgv, nv) \ + (((cfgv) & ~0x4000) | (((nv) & 0x1) << 14)) +#define ES_DAC_SECOND(cfgv) \ + (((cfgv) & 0x2000) >> 13) +#define ES_SET_DAC_SECOND(cfgv, nv) \ + (((cfgv) & ~0x2000) | (((nv) & 0x1) << 13)) +#define ES_NUMPLAY(cfgv) \ + (((cfgv) & 0x1800) >> 11) +#define ES_SET_NUMPLAY(cfgv, nv) \ + (((cfgv) & ~0x1800) | (((nv) & 0x3) << 11)) +#define ES_NUMREC(cfgv) \ + (((cfgv) & 0x600) >> 9) +#define ES_SET_NUMREC(cfgv, nv) \ + (((cfgv) & ~0x600) | (((nv) & 0x3) << 9)) +#define ES_IS_ES1370(cfgv) \ + (((cfgv) & 0x100) >> 8) +#define ES_SET_IS_ES1370(cfgv, nv) \ + (((cfgv) & ~0x100) | (((nv) ? 1 : 0) << 8)) +#define ES_GP(cfgv) \ + ((cfgv) & 0xff) +#define ES_SET_GP(cfgv, nv) \ + (((cfgv) & ~0xff) | ((nv) & 0xff)) + +#define ES_DAC1_ENABLED(cfgv) \ + (ES_NUMPLAY(cfgv) > 1 || \ + (ES_NUMPLAY(cfgv) == 1 && ES_DAC_FIRST(cfgv) == ES_DAC1)) +#define ES_DAC2_ENABLED(cfgv) \ + (ES_NUMPLAY(cfgv) > 1 || \ + (ES_NUMPLAY(cfgv) == 1 && ES_DAC_FIRST(cfgv) == ES_DAC2)) + +/* + * DAC 1/2 configuration through kernel hint - hint.pcm..dac="val" + * + * 0 = Enable both DACs - Default + * 1 = Enable single DAC (DAC1) + * 2 = Enable single DAC (DAC2) + * 3 = Enable both DACs, swap position (DAC2 comes first instead of DAC1) + */ +#define ES_DEFAULT_DAC_CFG 2 + struct es_info { bus_space_tag_t st; bus_space_handle_t sh; @@ -115,41 +190,35 @@ struct es_info { unsigned int bufsz; /* Contents of board's registers */ - u_long ctrl; - u_long sctrl; - struct es_chinfo pch, rch; + uint32_t ctrl; + uint32_t sctrl; + uint32_t escfg; + struct es_chinfo ch[ES_NCHANS]; + struct spinlock *lock; }; -/* -------------------------------------------------------------------- */ +#define ES_LOCK(sc) snd_mtxlock((sc)->lock) +#define ES_UNLOCK(sc) snd_mtxunlock((sc)->lock) +#define ES_LOCK_ASSERT(sc) snd_mtxassert((sc)->lock) /* prototypes */ static void es_intr(void *); - -static u_int es1371_wait_src_ready(struct es_info *); +static uint32_t 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 *, device_t); +static int es1371_init(struct es_info *); static int es1370_init(struct es_info *); static int es1370_wrcodec(struct es_info *, u_char, u_char); -static u_int32_t es_playfmt[] = { +static u_int32_t es_fmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; -static struct 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 struct pcmchan_caps es_reccaps = {4000, 48000, es_recfmt, 0}; +static struct pcmchan_caps es_caps = {4000, 48000, es_fmt, 0}; static const struct { unsigned volidx:4; @@ -159,7 +228,7 @@ static const struct { unsigned recmask:13; unsigned avail:1; } mixtable[SOUND_MIXER_NRDEVICES] = { - [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x0000, 1 }, + [SOUND_MIXER_VOLUME] = { 0, 0x0, 0x1, 1, 0x1f7f, 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 }, @@ -171,22 +240,67 @@ static const struct { [SOUND_MIXER_OGAIN] = { 9, 0xf, 0x0, 0, 0x0000, 1 } }; +static __inline u_int32_t +es_rd(struct es_info *es, int regno, int size) +{ + switch (size) { + case 1: + return bus_space_read_1(es->st, es->sh, regno); + case 2: + return bus_space_read_2(es->st, es->sh, regno); + case 4: + return bus_space_read_4(es->st, es->sh, regno); + default: + return 0xFFFFFFFF; + } +} + +static __inline void +es_wr(struct es_info *es, int regno, u_int32_t data, int size) +{ + + switch (size) { + case 1: + bus_space_write_1(es->st, es->sh, regno, data); + break; + case 2: + bus_space_write_2(es->st, es->sh, regno, data); + break; + case 4: + bus_space_write_4(es->st, es->sh, regno, data); + break; + } +} + /* -------------------------------------------------------------------- */ /* The es1370 mixer interface */ static int es1370_mixinit(struct snd_mixer *m) { + struct es_info *es; int i; u_int32_t v; + es = mix_getdevinfo(m); v = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].avail) v |= (1 << i); + /* + * Each DAC1/2 for ES1370 can be controlled independently + * DAC1 = controlled by synth + * DAC2 = controlled by pcm + * This is indeed can confuse user if DAC1 become primary playback + * channel. Try to be smart and combine both if necessary. + */ + if (ES_SINGLE_PCM_MIX(es->escfg)) + v &= ~(1 << SOUND_MIXER_SYNTH); mix_setdevs(m, v); v = 0; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) if (mixtable[i].recmask) v |= (1 << i); + if (ES_SINGLE_PCM_MIX(es->escfg)) /* ditto */ + v &= ~(1 << SOUND_MIXER_SYNTH); mix_setrecdevs(m, v); return 0; } @@ -194,7 +308,8 @@ es1370_mixinit(struct snd_mixer *m) static int es1370_mixset(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) { - int l, r, rl, rr; + struct es_info *es; + int l, r, rl, rr, set_dac1; if (!mixtable[dev].avail) return -1; l = left; @@ -204,30 +319,53 @@ es1370_mixset(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) } else { rl = (l < 10)? 0x80 : 15 - (l - 10) / 6; } + es = mix_getdevinfo(m); + ES_LOCK(es); + if (dev == SOUND_MIXER_PCM && (ES_SINGLE_PCM_MIX(es->escfg)) && + ES_DAC1_ENABLED(es->escfg)) { + set_dac1 = 1; + } else { + set_dac1 = 0; + } if (mixtable[dev].stereo) { rr = (r < 10)? 0x80 : 15 - (r - 10) / 6; - es1370_wrcodec(mix_getdevinfo(m), mixtable[dev].right, rr); + es1370_wrcodec(es, mixtable[dev].right, rr); + if (set_dac1 && mixtable[SOUND_MIXER_SYNTH].stereo) + es1370_wrcodec(es, mixtable[SOUND_MIXER_SYNTH].right, rr); } - es1370_wrcodec(mix_getdevinfo(m), mixtable[dev].left, rl); + es1370_wrcodec(es, mixtable[dev].left, rl); + if (set_dac1) + es1370_wrcodec(es, mixtable[SOUND_MIXER_SYNTH].left, rl); + ES_UNLOCK(es); + return l | (r << 8); } static int es1370_mixsetrecsrc(struct snd_mixer *m, u_int32_t src) { + struct es_info *es; int i, j = 0; + es = mix_getdevinfo(m); 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); + ES_LOCK(es); + if ((src & (1 << SOUND_MIXER_PCM)) && ES_SINGLE_PCM_MIX(es->escfg) && + ES_DAC1_ENABLED(es->escfg)) { + j |= mixtable[SOUND_MIXER_SYNTH].recmask; + } + es1370_wrcodec(es, CODEC_LIMIX1, j & 0x55); + es1370_wrcodec(es, CODEC_RIMIX1, j & 0xaa); + es1370_wrcodec(es, CODEC_LIMIX2, (j >> 8) & 0x17); + es1370_wrcodec(es, CODEC_RIMIX2, (j >> 8) & 0x0f); + es1370_wrcodec(es, CODEC_OMIX1, 0x7f); + es1370_wrcodec(es, CODEC_OMIX2, 0x3f); + ES_UNLOCK(es); + return src; } @@ -244,18 +382,20 @@ MIXER_DECLARE(es1370_mixer); static int es1370_wrcodec(struct es_info *es, u_char i, u_char data) { - int wait = 100; /* 100 msec timeout */ + u_int t; - do { - if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) & + ES_LOCK_ASSERT(es); + + for (t = 0; t < 0x1000; t++) { + if ((es_rd(es, ES1370_REG_STATUS, 4) & STAT_CSTAT) == 0) { - bus_space_write_2(es->st, es->sh, ES1370_REG_CODEC, - ((u_short)i << CODEC_INDEX_SHIFT) | data); + es_wr(es, ES1370_REG_CODEC, + ((u_short)i << CODEC_INDEX_SHIFT) | data, 2); return 0; } - DELAY(1000); - } while (--wait); - kprintf("pcm: es1370_wrcodec timed out\n"); + DELAY(1); + } + device_printf(es->dev, "%s: timed out\n", __func__); return -1; } @@ -266,35 +406,82 @@ static void * eschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct es_info *es = devinfo; - struct es_chinfo *ch = (dir == PCMDIR_PLAY)? &es->pch : &es->rch; + struct es_chinfo *ch; + uint32_t index; + + ES_LOCK(es); + if (dir == PCMDIR_PLAY) { + index = ES_GP(es->escfg); + es->escfg = ES_SET_GP(es->escfg, index + 1); + if (index == 0) { + index = ES_DAC_FIRST(es->escfg); + } else if (index == 1) { + index = ES_DAC_SECOND(es->escfg); + } else { + device_printf(es->dev, "Invalid ES_GP index: %d\n", index); + ES_UNLOCK(es); + return NULL; + } + if (!(index == ES_DAC1 || index == ES_DAC2)) { + device_printf(es->dev, "Unknown DAC: %d\n", + index + 1); + ES_UNLOCK(es); + return NULL; + } + if (es->ch[index].channel != NULL) { + device_printf(es->dev, "DAC%d already initialized!\n", + index + 1); + ES_UNLOCK(es); + return NULL; + } + } else + index = ES_ADC; + + ch = &es->ch[index]; + ch->index = index; + ch->num = es->num++; + ch->caps = es_caps; + if (ES_IS_ES1370(es->escfg)) { + if (ch->index == ES_DAC1) { + ch->caps.maxspeed = ES1370_DAC1_MAXSPEED; + ch->caps.minspeed = ES1370_DAC1_MINSPEED; + } else { + uint32_t fixed_rate = ES_FIXED_RATE(es->escfg); + if (!(fixed_rate < es_caps.minspeed || + fixed_rate > es_caps.maxspeed)) { + ch->caps.maxspeed = fixed_rate; + ch->caps.minspeed = fixed_rate; + } + } + } ch->parent = es; ch->channel = c; ch->buffer = b; ch->bufsz = es->bufsz; ch->blksz = ch->bufsz / 2; - ch->num = ch->parent->num++; - if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) == -1) return NULL; - return ch; -} - -static int -eschan_setdir(kobj_t obj, void *data, int dir) -{ - struct es_chinfo *ch = data; - struct es_info *es = ch->parent; - + ch->dir = dir; + ES_UNLOCK(es); + if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) != 0) + return NULL; + ES_LOCK(es); 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(sndbuf_getbuf(ch->buffer))); - bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); + if (ch->index == ES_DAC1) { + es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC1_FRAMEADR >> 8, 1); + es_wr(es, ES1370_REG_DAC1_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4); + es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + } else { + es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMEADR >> 8, 1); + es_wr(es, ES1370_REG_DAC2_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4); + es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + } } 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(sndbuf_getbuf(ch->buffer))); - bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); + es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8, 1); + es_wr(es, ES1370_REG_ADC_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer), 4); + es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); } - ch->dir = dir; - return 0; + ES_UNLOCK(es); + return ch; } static int @@ -303,16 +490,24 @@ eschan_setformat(kobj_t obj, void *data, u_int32_t format) struct es_chinfo *ch = data; struct es_info *es = ch->parent; + ES_LOCK(es); 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; + if (ch->index == ES_DAC1) { + es->sctrl &= ~SCTRL_P1FMT; + if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P1SEB; + if (format & AFMT_STEREO) es->sctrl |= SCTRL_P1SMB; + } else { + 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); + es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); + ES_UNLOCK(es); ch->fmt = format; return 0; } @@ -323,11 +518,41 @@ 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 */ + /* Fixed rate , do nothing. */ + if (ch->caps.minspeed == ch->caps.maxspeed) + return ch->caps.maxspeed; + if (speed < ch->caps.minspeed) + speed = ch->caps.minspeed; + if (speed > ch->caps.maxspeed) + speed = ch->caps.maxspeed; + ES_LOCK(es); + if (ch->index == ES_DAC1) { + /* + * DAC1 does not support continuous rate settings. + * Pick the nearest and use it since FEEDER_RATE will + * do the the proper conversion for us. + */ + es->ctrl &= ~CTRL_WTSRSEL; + if (speed < 8268) { + speed = 5512; + es->ctrl |= 0 << CTRL_SH_WTSRSEL; + } else if (speed < 16537) { + speed = 11025; + es->ctrl |= 1 << CTRL_SH_WTSRSEL; + } else if (speed < 33075) { + speed = 22050; + es->ctrl |= 2 << CTRL_SH_WTSRSEL; + } else { + speed = 44100; + es->ctrl |= 3 << CTRL_SH_WTSRSEL; + } + } else { + es->ctrl &= ~CTRL_PCLKDIV; + es->ctrl |= DAC2_SRTODIV(speed) << CTRL_SH_PCLKDIV; + } + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + ES_UNLOCK(es); + return speed; } static int @@ -335,23 +560,41 @@ eschan1371_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; + uint32_t i; + int delta; - if (ch->dir == PCMDIR_PLAY) { - return es1371_dac_rate(es, speed, 3 - ch->num); /* play */ - } else { - return es1371_adc_rate(es, speed, 1); /* record */ - } + ES_LOCK(es); + if (ch->dir == PCMDIR_PLAY) + i = es1371_dac_rate(es, speed, ch->index); /* play */ + else + i = es1371_adc_rate(es, speed, ch->index); /* record */ + ES_UNLOCK(es); + delta = (speed > i) ? speed - i : i - speed; + if (delta < 2) + return speed; + return i; } static int eschan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { + struct es_info *es; struct es_chinfo *ch = data; + uint32_t oblksz, obufsz; + int error; + oblksz = ch->blksz; + obufsz = ch->bufsz; ch->blksz = blocksize; ch->bufsz = ch->blksz * 2; - sndbuf_resize(ch->buffer, 2, ch->blksz); - + error = sndbuf_resize(ch->buffer, 2, ch->blksz); + if (error != 0) { + ch->blksz = oblksz; + ch->bufsz = obufsz; + es = ch->parent; + device_printf(es->dev, "unable to set block size, blksz = %d, " + "error = %d", blocksize, error); + } return ch->blksz; } @@ -360,37 +603,52 @@ eschan_trigger(kobj_t obj, void *data, int go) { struct es_chinfo *ch = data; struct es_info *es = ch->parent; - unsigned cnt; + uint32_t cnt, b = 0; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1; - + if (ch->fmt & AFMT_16BIT) + b |= 0x02; + if (ch->fmt & AFMT_STEREO) + b |= 0x01; + ES_LOCK(es); 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_P2INTEN | (b << SCTRL_SH_P2ENDINC); - 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->bufsz >> 2) - 1); - } else es->ctrl &= ~CTRL_DAC2_EN; + if (ch->index == ES_DAC1) { + es->ctrl |= CTRL_DAC1_EN; + es->sctrl &= ~(SCTRL_P1LOOPSEL | SCTRL_P1PAUSE | SCTRL_P1SCTRLD); + es->sctrl |= SCTRL_P1INTEN | b; + es_wr(es, ES1370_REG_DAC1_SCOUNT, cnt, 4); + /* start at beginning of buffer */ + es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC1_FRAMECNT >> 8, 4); + es_wr(es, ES1370_REG_DAC1_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + } else { + es->ctrl |= CTRL_DAC2_EN; + es->sctrl &= ~(SCTRL_P2ENDINC | SCTRL_P2STINC | SCTRL_P2LOOPSEL | SCTRL_P2PAUSE | SCTRL_P2DACSEN); + es->sctrl |= SCTRL_P2INTEN | (b << 2) | + (((b & 2) ? : 1) << SCTRL_SH_P2ENDINC); + es_wr(es, ES1370_REG_DAC2_SCOUNT, cnt, 4); + /* start at beginning of buffer */ + es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_DAC2_FRAMECNT >> 8, 4); + es_wr(es, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); + } + } else es->ctrl &= ~(ch->index == ES_DAC1 ? CTRL_DAC1_EN : 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); + es->sctrl |= SCTRL_R1INTEN | (b << 4); + es_wr(es, ES1370_REG_ADC_SCOUNT, cnt, 4); /* 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->bufsz >> 2) - 1); + es_wr(es, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMECNT >> 8, 4); + es_wr(es, ES1370_REG_ADC_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1, 4); } 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); + es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + ES_UNLOCK(es); return 0; } @@ -401,13 +659,17 @@ eschan_getptr(kobj_t obj, void *data) struct es_info *es = ch->parent; u_int32_t reg, cnt; - if (ch->dir == PCMDIR_PLAY) - reg = ES1370_REG_DAC2_FRAMECNT; - else + if (ch->dir == PCMDIR_PLAY) { + if (ch->index == ES_DAC1) + reg = ES1370_REG_DAC1_FRAMECNT; + else + 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; + ES_LOCK(es); + es_wr(es, ES1370_REG_MEMPAGE, reg >> 8, 4); + cnt = es_rd(es, reg & 0x000000ff, 4) >> 16; + ES_UNLOCK(es); /* cnt is longwords */ return cnt << 2; } @@ -416,12 +678,12 @@ static struct pcmchan_caps * eschan_getcaps(kobj_t obj, void *data) { struct es_chinfo *ch = data; - return (ch->dir == PCMDIR_PLAY)? &es_playcaps : &es_reccaps; + + return &ch->caps; } 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), @@ -434,7 +696,6 @@ 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), @@ -451,34 +712,78 @@ static void es_intr(void *p) { struct es_info *es = p; - unsigned intsrc, sctrl; + uint32_t intsrc, sctrl; - intsrc = bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS); - if ((intsrc & STAT_INTR) == 0) return; + ES_LOCK(es); + intsrc = es_rd(es, ES1370_REG_STATUS, 4); + if ((intsrc & STAT_INTR) == 0) { + ES_UNLOCK(es); + 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); + es_wr(es, ES1370_REG_SERIAL_CONTROL, sctrl, 4); + es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); + ES_UNLOCK(es); - if (intsrc & STAT_ADC) chn_intr(es->rch.channel); - if (intsrc & STAT_DAC1); - if (intsrc & STAT_DAC2) chn_intr(es->pch.channel); + if (intsrc & STAT_ADC) chn_intr(es->ch[ES_ADC].channel); + if (intsrc & STAT_DAC1) chn_intr(es->ch[ES_DAC1].channel); + if (intsrc & STAT_DAC2) chn_intr(es->ch[ES_DAC2].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); + uint32_t fixed_rate; + int r, single_pcm; + + /* ES1370 default to fixed rate operation */ + if (resource_int_value(device_get_name(es->dev), + device_get_unit(es->dev), "fixed_rate", &r) == 0) { + fixed_rate = r; + if (fixed_rate) { + if (fixed_rate < es_caps.minspeed) + fixed_rate = es_caps.minspeed; + if (fixed_rate > es_caps.maxspeed) + fixed_rate = es_caps.maxspeed; + } + } else + fixed_rate = es_caps.maxspeed; + + if (resource_int_value(device_get_name(es->dev), + device_get_unit(es->dev), "single_pcm_mixer", &r) == 0) + single_pcm = (r) ? 1 : 0; + else + single_pcm = 1; + + ES_LOCK(es); + if (ES_NUMPLAY(es->escfg) == 1) + single_pcm = 1; + /* This is ES1370 */ + es->escfg = ES_SET_IS_ES1370(es->escfg, 1); + if (fixed_rate) { + es->escfg = ES_SET_FIXED_RATE(es->escfg, fixed_rate); + } else { + es->escfg = ES_SET_FIXED_RATE(es->escfg, 0); + fixed_rate = DSP_DEFAULT_SPEED; + } + if (single_pcm) { + es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1); + } else { + es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0); + } + es->ctrl = CTRL_CDC_EN | CTRL_JYSTK_EN | CTRL_SERR_DIS | + (DAC2_SRTODIV(fixed_rate) << CTRL_SH_PCLKDIV); + es->ctrl |= 3 << CTRL_SH_WTSRSEL; + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); es->sctrl = 0; - bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->sctrl); + es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); es1370_wrcodec(es, CODEC_RES_PD, 3);/* No RST, PD */ es1370_wrcodec(es, CODEC_CSEL, 0); /* CODEC ADC and CODEC DAC use @@ -486,44 +791,65 @@ es1370_init(struct es_info *es) * 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 */ + ES_UNLOCK(es); return 0; } /* ES1371 specific */ int -es1371_init(struct es_info *es, device_t dev) +es1371_init(struct es_info *es) { + uint32_t cssr, devid, revid, subdev; int idx; - int devid = pci_get_devid(dev); - int revid = pci_get_revid(dev); - - if (debug > 0) kprintf("es_init\n"); + ES_LOCK(es); + /* This is NOT ES1370 */ + es->escfg = ES_SET_IS_ES1370(es->escfg, 0); es->num = 0; - es->ctrl = 0; es->sctrl = 0; + cssr = 0; + devid = pci_get_devid(es->dev); + revid = pci_get_revid(es->dev); + subdev = (pci_get_subdevice(es->dev) << 16) | pci_get_subvendor(es->dev); + /* + * Joyport blacklist. Either we're facing with broken hardware + * or because this hardware need special (unknown) initialization + * procedures. + */ + switch (subdev) { + case 0x20001274: /* old Ensoniq */ + es->ctrl = 0; + break; + default: + es->ctrl = CTRL_JYSTK_EN; + break; + } + if (devid == CT4730_PCI_ID) { + /* XXX amplifier hack? */ + es->ctrl |= (1 << 16); + } /* initialize the chips */ + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); + es_wr(es, ES1371_REG_LEGACY, 0, 4); if ((devid == ES1371_PCI_ID && revid == ES1371REV_ES1373_8) || (devid == ES1371_PCI_ID && revid == ES1371REV_CT5880_A) || (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_C) || (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_D) || (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_E)) { - bus_space_write_4(es->st, es->sh, ES1370_REG_STATUS, 0x20000000); + cssr = 1 << 29; + es_wr(es, ES1370_REG_STATUS, cssr, 4); DELAY(20000); - if (debug > 0) device_printf(dev, "ac97 2.1 enabled\n"); - } else { /* pre ac97 2.1 card */ - bus_space_write_4(es->st, es->sh, ES1370_REG_CONTROL, es->ctrl); - if (debug > 0) device_printf(dev, "ac97 pre-2.1 enabled\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); + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + es_wr(es, ES1371_REG_LEGACY, ES1371_SYNC_RES, 4); DELAY(2000); - bus_space_write_4(es->st, es->sh, ES1370_REG_SERIAL_CONTROL, es->ctrl); + es_wr(es, ES1370_REG_CONTROL, es->sctrl, 4); + es1371_wait_src_ready(es); /* Init the sample rate converter */ - bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, ES1371_DIS_SRC); + es_wr(es, ES1371_REG_SMPRATE, ES1371_DIS_SRC, 4); 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); @@ -536,16 +862,21 @@ es1371_init(struct es_info *es, device_t dev) 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); + es1371_adc_rate(es, 22050, ES_ADC); + es1371_dac_rate(es, 22050, ES_DAC1); + es1371_dac_rate(es, 22050, ES_DAC2); /* 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); + es1371_wait_src_ready(es); + es_wr(es, ES1371_REG_SMPRATE, 0, 4); + /* try to reset codec directly */ + es_wr(es, ES1371_REG_CODEC, 0, 4); + es_wr(es, ES1370_REG_STATUS, cssr, 4); + ES_UNLOCK(es); return (0); } @@ -555,42 +886,34 @@ es1371_init(struct es_info *es, device_t dev) static int es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data) { - unsigned t, x; + uint32_t t, x, orig; struct es_info *es = (struct es_info*)s; - if (debug > 0) kprintf("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)))) + if (!es_rd(es, ES1371_REG_CODEC & CODEC_WIP, 4)) break; - crit_enter(); /* save the current state for later */ - x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE); + x = orig = es_rd(es, ES1371_REG_SMPRATE, 4); /* 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))); + es_wr(es, ES1371_REG_SMPRATE, + (x & + (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) | + 0x00010000, 4); + /* busy wait */ + for (t = 0; t < 0x1000; t++) + if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000) + break; /* 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) + if ((es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000) break; - if (debug > 2) - kprintf("one b_s_w: 0x%lx 0x%x 0x%x\n", - rman_get_start(es->reg), 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, + es_wr(es, ES1371_REG_CODEC, ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | - ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK)); + ((data << CODEC_PODAT_SHIFT) & CODEC_PODAT_MASK), 4); /* restore SRC reg */ es1371_wait_src_ready(s); - if (debug > 2) - kprintf("two b_s_w: 0x%lx 0x%x 0x%x\n", - rman_get_start(es->reg), ES1371_REG_SMPRATE, x); - bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, x); - crit_exit(); + es_wr(es, ES1371_REG_SMPRATE, orig, 4); return 0; } @@ -598,43 +921,42 @@ es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data) static int es1371_rdcd(kobj_t obj, void *s, int addr) { - unsigned t, x = 0; + uint32_t t, x, orig; struct es_info *es = (struct es_info *)s; - if (debug > 0) kprintf("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)) + if (!(x = es_rd(es, ES1371_REG_CODEC, 4) & CODEC_WIP)) break; - if (debug > 0) kprintf("loop 1 t 0x%x x 0x%x ", t, x); - - crit_enter(); /* save the current state for later */ - x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE); + x = orig = es_rd(es, ES1371_REG_SMPRATE, 4); /* 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))); + es_wr(es, ES1371_REG_SMPRATE, + (x & + (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)) | + 0x00010000, 4); + /* busy wait */ + for (t = 0; t < 0x1000; t++) + if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00000000) + break; /* 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) + for (t = 0; t < 0x1000; t++) + if ((x = es_rd(es, ES1371_REG_SMPRATE, 4) & 0x00870000) == 0x00010000) break; - if (debug > 0) kprintf("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); + + es_wr(es, ES1371_REG_CODEC, + ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | + CODEC_PORD, 4); /* restore SRC reg */ es1371_wait_src_ready(s); - bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, x); - - crit_exit(); + es_wr(es, ES1371_REG_SMPRATE, orig, 4); /* 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) + if ((x = es_rd(es, ES1371_REG_CODEC, 4)) & CODEC_RDY) break; - if (debug > 0) kprintf("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); } @@ -650,24 +972,24 @@ AC97_DECLARE(es1371_ac97); static u_int es1371_src_read(struct es_info *es, u_short reg) { - unsigned int r; + uint32_t 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); + es_wr(es, ES1371_REG_SMPRATE, r, 4); 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; +es1371_src_write(struct es_info *es, u_short reg, u_short data) +{ + uint32_t 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); - /* kprintf("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); + es_wr(es, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE, 4); } static u_int @@ -675,6 +997,8 @@ es1371_adc_rate(struct es_info *es, u_int rate, int set) { u_int n, truncm, freq, result; + ES_LOCK_ASSERT(es); + if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; n = rate / 3000; @@ -708,36 +1032,37 @@ es1371_dac_rate(struct es_info *es, u_int rate, int set) { u_int freq, r, result, dac, dis; + ES_LOCK_ASSERT(es); + if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; - freq = (rate << 15) / 3000; + freq = ((rate << 15) + 1500) / 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); - } + + dac = (set == ES_DAC1) ? ES_SMPREG_DAC1 : ES_SMPREG_DAC2; + dis = (set == ES_DAC1) ? ES1371_DIS_P2 : ES1371_DIS_P1; + r = (es1371_wait_src_ready(es) & (ES1371_DIS_SRC | ES1371_DIS_P1 | ES1371_DIS_P2 | ES1371_DIS_R1)); + es_wr(es, ES1371_REG_SMPRATE, r, 4); + 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)); + es_wr(es, ES1371_REG_SMPRATE, r, 4); return result; } -static u_int +static uint32_t es1371_wait_src_ready(struct es_info *es) { - u_int t, r; + uint32_t 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)) + for (t = 0; t < 0x1000; t++) { + if (!((r = es_rd(es, ES1371_REG_SMPRATE, 4)) & ES1371_SRC_RAM_BUSY)) return r; - DELAY(1000); + DELAY(1); } - kprintf("es1371: wait src ready timeout 0x%x [0x%x]\n", ES1371_REG_SMPRATE, r); + device_printf(es->dev, "%s: timed out 0x%x [0x%x]\n", __func__, + ES1371_REG_SMPRATE, r); return 0; } @@ -753,63 +1078,74 @@ es_pci_probe(device_t dev) switch(pci_get_devid(dev)) { case ES1370_PCI_ID: device_set_desc(dev, "AudioPCI ES1370"); - return 0; + return BUS_PROBE_DEFAULT; case ES1371_PCI_ID: switch(pci_get_revid(dev)) { case ES1371REV_ES1371_A: device_set_desc(dev, "AudioPCI ES1371-A"); - return 0; + return BUS_PROBE_DEFAULT; case ES1371REV_ES1371_B: device_set_desc(dev, "AudioPCI ES1371-B"); - return 0; + return BUS_PROBE_DEFAULT; case ES1371REV_ES1373_A: device_set_desc(dev, "AudioPCI ES1373-A"); - return 0; + return BUS_PROBE_DEFAULT; case ES1371REV_ES1373_B: device_set_desc(dev, "AudioPCI ES1373-B"); - return 0; + return BUS_PROBE_DEFAULT; case ES1371REV_ES1373_8: device_set_desc(dev, "AudioPCI ES1373-8"); - return 0; + return BUS_PROBE_DEFAULT; case ES1371REV_CT5880_A: device_set_desc(dev, "Creative CT5880-A"); - return 0; + return BUS_PROBE_DEFAULT; default: device_set_desc(dev, "AudioPCI ES1371-?"); device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return 0; + return BUS_PROBE_DEFAULT; } case ES1371_PCI_ID2: device_set_desc(dev, "Strange AudioPCI ES1371-? (vid=3274)"); device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return 0; + return BUS_PROBE_DEFAULT; + + case CT4730_PCI_ID: + switch(pci_get_revid(dev)) { + case CT4730REV_CT4730_A: + device_set_desc(dev, "Creative SB AudioPCI CT4730/EV1938"); + return BUS_PROBE_DEFAULT; + default: + device_set_desc(dev, "Creative SB AudioPCI CT4730-?"); + device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); + return BUS_PROBE_DEFAULT; + } case CT5880_PCI_ID: switch(pci_get_revid(dev)) { case CT5880REV_CT5880_C: device_set_desc(dev, "Creative CT5880-C"); - return 0; + return BUS_PROBE_DEFAULT; case CT5880REV_CT5880_D: device_set_desc(dev, "Creative CT5880-D"); - return 0; + return BUS_PROBE_DEFAULT; case CT5880REV_CT5880_E: device_set_desc(dev, "Creative CT5880-E"); - return 0; + return BUS_PROBE_DEFAULT; default: device_set_desc(dev, "Creative CT5880-?"); device_printf(dev, "unknown revision %d -- please report to cg@freebsd.org\n", pci_get_revid(dev)); - return 0; + return BUS_PROBE_DEFAULT; } default: @@ -817,83 +1153,389 @@ es_pci_probe(device_t dev) } } +#ifdef SND_DYNSYSCTL +static int +sysctl_es137x_spdif_enable(SYSCTL_HANDLER_ARGS) +{ + struct es_info *es; + device_t dev; + uint32_t r; + int err, new_en; + + dev = oidp->oid_arg1; + es = pcm_getdevinfo(dev); + ES_LOCK(es); + r = es_rd(es, ES1370_REG_STATUS, 4); + ES_UNLOCK(es); + new_en = (r & ENABLE_SPDIF) ? 1 : 0; + err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req); + + if (err || req->newptr == NULL) + return (err); + if (new_en < 0 || new_en > 1) + return (EINVAL); + + ES_LOCK(es); + if (new_en) { + r |= ENABLE_SPDIF; + es->ctrl |= SPDIFEN_B; + es->ctrl |= RECEN_B; + } else { + r &= ~ENABLE_SPDIF; + es->ctrl &= ~SPDIFEN_B; + es->ctrl &= ~RECEN_B; + } + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + es_wr(es, ES1370_REG_STATUS, r, 4); + ES_UNLOCK(es); + + return (0); +} + +#if 0 +static int +sysctl_es137x_latency_timer(SYSCTL_HANDLER_ARGS) +{ + struct es_info *es; + device_t dev; + uint32_t val; + int err; + + dev = oidp->oid_arg1; + es = pcm_getdevinfo(dev); + ES_LOCK(es); + val = pci_read_config(dev, PCIR_LATTIMER, 1); + ES_UNLOCK(es); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err || req->newptr == NULL) + return (err); + if (val > 255) + return (EINVAL); + + ES_LOCK(es); + pci_write_config(dev, PCIR_LATTIMER, val, 1); + ES_UNLOCK(es); + + return (0); +} + +static int +sysctl_es137x_fixed_rate(SYSCTL_HANDLER_ARGS) +{ + struct es_info *es; + device_t dev; + uint32_t val; + int err; + + dev = oidp->oid_arg1; + es = pcm_getdevinfo(dev); + ES_LOCK(es); + val = ES_FIXED_RATE(es->escfg); + if (val < es_caps.minspeed) + val = 0; + ES_UNLOCK(es); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err || req->newptr == NULL) + return (err); + if (val != 0 && (val < es_caps.minspeed || val > es_caps.maxspeed)) + return (EINVAL); + + ES_LOCK(es); + if (es->ctrl & (CTRL_DAC2_EN|CTRL_ADC_EN)) { + ES_UNLOCK(es); + return (EBUSY); + } + if (val) { + if (val != ES_FIXED_RATE(es->escfg)) { + es->escfg = ES_SET_FIXED_RATE(es->escfg, val); + es->ch[ES_DAC2].caps.maxspeed = val; + es->ch[ES_DAC2].caps.minspeed = val; + es->ch[ES_ADC].caps.maxspeed = val; + es->ch[ES_ADC].caps.minspeed = val; + es->ctrl &= ~CTRL_PCLKDIV; + es->ctrl |= DAC2_SRTODIV(val) << CTRL_SH_PCLKDIV; + es_wr(es, ES1370_REG_CONTROL, es->ctrl, 4); + } + } else { + es->escfg = ES_SET_FIXED_RATE(es->escfg, 0); + es->ch[ES_DAC2].caps = es_caps; + es->ch[ES_ADC].caps = es_caps; + } + ES_UNLOCK(es); + + return (0); +} + +static int +sysctl_es137x_single_pcm_mixer(SYSCTL_HANDLER_ARGS) +{ + struct es_info *es; + struct snddev_info *d; + struct snd_mixer *m; + struct cdev *i_dev; + device_t dev; + uint32_t val, set; + int recsrc, level, err; + + dev = oidp->oid_arg1; + d = device_get_softc(dev); + if (d == NULL || d->mixer_dev == NULL || d->mixer_dev->si_drv1 == NULL) + return (EINVAL); + es = d->devinfo; + if (es == NULL) + return (EINVAL); + ES_LOCK(es); + set = ES_SINGLE_PCM_MIX(es->escfg); + val = set; + ES_UNLOCK(es); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err || req->newptr == NULL) + return (err); + if (!(val == 0 || val == 1)) + return (EINVAL); + if (val == set) + return (0); + i_dev = d->mixer_dev; + if (mixer_ioctl(i_dev, 0, (caddr_t)&recsrc, 0, NULL) != EBADF) + return (EBUSY); + err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_PCM), + (caddr_t)&level, -1, NULL); + if (!err) + err = mixer_ioctl(i_dev, MIXER_READ(SOUND_MIXER_RECSRC), + (caddr_t)&recsrc, -1, NULL); + if (err) + return (err); + if (level < 0) + return (EINVAL); + + ES_LOCK(es); + if (es->ctrl & (CTRL_ADC_EN | CTRL_DAC1_EN | CTRL_DAC2_EN)) { + ES_UNLOCK(es); + return (EBUSY); + } + if (val) { + es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 1); + } else { + es->escfg = ES_SET_SINGLE_PCM_MIX(es->escfg, 0); + } + ES_UNLOCK(es); + m = i_dev->si_drv1; + if (!val) { + mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) | + (1 << SOUND_MIXER_SYNTH)); + mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) | + (1 << SOUND_MIXER_SYNTH)); + err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH), + (caddr_t)&level, -1, NULL); + } else { + err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_SYNTH), + (caddr_t)&level, -1, NULL); + mix_setdevs(m, mix_getdevs(d->mixer_dev->si_drv1) & + ~(1 << SOUND_MIXER_SYNTH)); + mix_setrecdevs(m, mix_getrecdevs(d->mixer_dev->si_drv1) & + ~(1 << SOUND_MIXER_SYNTH)); + } + if (!err) { + level = recsrc; + if (recsrc & (1 << SOUND_MIXER_PCM)) + recsrc |= 1 << SOUND_MIXER_SYNTH; + else if (recsrc & (1 << SOUND_MIXER_SYNTH)) + recsrc |= 1 << SOUND_MIXER_PCM; + if (level != recsrc) + err = mixer_ioctl(i_dev, MIXER_WRITE(SOUND_MIXER_RECSRC), + (caddr_t)&recsrc, -1, NULL); + } + return (err); +} +#endif +#endif /* SND_DYNSYSCTL */ + +static void +es_init_sysctls(device_t dev) +{ +#ifdef SND_DYNSYSCTL + struct es_info *es; + int r, devid, revid; + + devid = pci_get_devid(dev); + revid = pci_get_revid(dev); + es = pcm_getdevinfo(dev); + if ((devid == ES1371_PCI_ID && revid == ES1371REV_ES1373_8) || + (devid == ES1371_PCI_ID && revid == ES1371REV_CT5880_A) || + (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_C) || + (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_D) || + (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_E)) { + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "spdif_enabled", + CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_es137x_spdif_enable, "I", + "Enable S/PDIF output on primary playback channel"); +#if 0 + } else if (devid == ES1370_PCI_ID) { + /* + * Enable fixed rate sysctl if both DAC2 / ADC enabled. + */ + if (es->ch[ES_DAC2].channel != NULL && es->ch[ES_ADC].channel != NULL) { + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "fixed_rate", + CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_es137x_fixed_rate, "I", + "Enable fixed rate playback/recording"); + } + /* + * Enable single pcm mixer sysctl if both DAC1/2 enabled. + */ + if (es->ch[ES_DAC1].channel != NULL && es->ch[ES_DAC2].channel != NULL) { + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "single_pcm_mixer", + CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_es137x_single_pcm_mixer, "I", + "Single PCM mixer controller for both DAC1/DAC2"); + } +#endif + } + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "latency_timer", &r) == 0 && + !(r < 0 || r > 255)) + pci_write_config(dev, PCIR_LATTIMER, r, 1); +#if 0 + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "latency_timer", + CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_es137x_latency_timer, "I", + "PCI Latency Timer configuration"); +#endif +#endif /* SND_DYNSYSCTL */ +} + static int es_pci_attach(device_t dev) { u_int32_t data; - struct es_info *es = 0; - int mapped; + struct es_info *es = NULL; + int mapped, i, numplay, dac_cfg; char status[SND_STATUSLEN]; - struct ac97_info *codec = 0; + struct ac97_info *codec = NULL; kobj_class_t ct = NULL; + uint32_t devid; if ((es = kmalloc(sizeof *es, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } - + es->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); es->dev = dev; + es->escfg = 0; mapped = 0; + + pci_enable_busmaster(dev); data = pci_read_config(dev, PCIR_COMMAND, 2); - data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); + data |= (PCIM_CMD_PORTEN|PCIM_CMD_MEMEN); 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); + es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid, + RF_ACTIVE); + if (es->reg) mapped++; - } } if (mapped == 0 && (data & PCIM_CMD_PORTEN)) { - es->regid = PCIR_MAPS; + es->regid = PCIR_BAR(0); 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); + es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid, + RF_ACTIVE); + if (es->reg) mapped++; - } } if (mapped == 0) { device_printf(dev, "unable to map register space\n"); goto bad; } + es->st = rman_get_bustag(es->reg); + es->sh = rman_get_bushandle(es->reg); es->bufsz = pcm_getbuffersize(dev, 4096, ES_DEFAULT_BUFSZ, 65536); - if (pci_get_devid(dev) == ES1371_PCI_ID || - pci_get_devid(dev) == ES1371_PCI_ID2 || - pci_get_devid(dev) == CT5880_PCI_ID) { - if(-1 == es1371_init(es, dev)) { - device_printf(dev, "unable to initialize the card\n"); - goto bad; - } + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "dac", &dac_cfg) == 0) { + if (dac_cfg < 0 || dac_cfg > 3) + dac_cfg = ES_DEFAULT_DAC_CFG; + } else + dac_cfg = ES_DEFAULT_DAC_CFG; + + switch (dac_cfg) { + case 0: /* Enable all DAC: DAC1, DAC2 */ + numplay = 2; + es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC1); + es->escfg = ES_SET_DAC_SECOND(es->escfg, ES_DAC2); + break; + case 1: /* Only DAC1 */ + numplay = 1; + es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC1); + break; + case 3: /* Enable all DAC / swap position: DAC2, DAC1 */ + numplay = 2; + es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC2); + es->escfg = ES_SET_DAC_SECOND(es->escfg, ES_DAC1); + break; + case 2: /* Only DAC2 */ + default: + numplay = 1; + es->escfg = ES_SET_DAC_FIRST(es->escfg, ES_DAC2); + break; + } + es->escfg = ES_SET_NUMPLAY(es->escfg, numplay); + es->escfg = ES_SET_NUMREC(es->escfg, 1); + + devid = pci_get_devid(dev); + switch (devid) { + case ES1371_PCI_ID: + case ES1371_PCI_ID2: + case CT5880_PCI_ID: + case CT4730_PCI_ID: + es1371_init(es); codec = AC97_CREATE(dev, es, es1371_ac97); - if (codec == NULL) goto bad; + 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_getmixerclass(), codec)) goto bad; + 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"); + break; + case ES1370_PCI_ID: + es1370_init(es); + /* + * Disable fixed rate operation if DAC2 disabled. + * This is a special case for es1370 only, where the + * speed of both ADC and DAC2 locked together. + */ + if (!ES_DAC2_ENABLED(es->escfg)) { + es->escfg = ES_SET_FIXED_RATE(es->escfg, 0); + } + if (mixer_init(dev, &es1370_mixer_class, es)) goto bad; - } - if (mixer_init(dev, &es1370_mixer_class, es)) goto bad; ct = &eschan1370_class; - } else goto bad; + break; + default: + goto bad; + /* NOTREACHED */ + } es->irqid = 0; - es->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &es->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!es->irq || snd_setup_intr(dev, es->irq, 0, es_intr, es, &es->ih, NULL)) { + es->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &es->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!es->irq || snd_setup_intr(dev, es->irq, INTR_MPSAFE, es_intr, es, &es->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -903,28 +1545,41 @@ es_pci_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/es->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &es->parent_dmat) != 0) { + /*flags*/0, + &es->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } - ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld", + ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", (es->regtype == SYS_RES_IOPORT)? "io" : "memory", - rman_get_start(es->reg), rman_get_start(es->irq)); + rman_get_start(es->reg), rman_get_start(es->irq),PCM_KLDSTRING(snd_es137x)); - if (pcm_register(dev, es, 1, 1)) goto bad; + if (pcm_register(dev, es, numplay, 1)) + goto bad; + for (i = 0; i < numplay; i++) + pcm_addchan(dev, PCMDIR_PLAY, ct, es); pcm_addchan(dev, PCMDIR_REC, ct, es); - pcm_addchan(dev, PCMDIR_PLAY, ct, es); + es_init_sysctls(dev); pcm_setstatus(dev, status); - + es->escfg = ES_SET_GP(es->escfg, 0); + if (numplay == 1) { + device_printf(dev, "\n", + ES_DAC_FIRST(es->escfg) + 1); + } else if (numplay == 2) { + device_printf(dev, "\n", + ES_DAC_FIRST(es->escfg) + 1, + ES_DAC_SECOND(es->escfg) + 1); + } return 0; bad: - if (codec) ac97_destroy(codec); - if (es->reg) bus_release_resource(dev, es->regtype, es->regid, es->reg); + if (es->parent_dmat) bus_dma_tag_destroy(es->parent_dmat); 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 (codec) ac97_destroy(codec); + if (es->reg) bus_release_resource(dev, es->regtype, es->regid, es->reg); + if (es->lock) snd_mtxfree(es->lock); if (es) kfree(es, M_DEVBUF); return ENXIO; } @@ -936,14 +1591,14 @@ es_pci_detach(device_t dev) struct es_info *es; r = pcm_unregister(dev); - if (r) - return r; + 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_release_resource(dev, es->regtype, es->regid, es->reg); bus_dma_tag_destroy(es->parent_dmat); + snd_mtxfree(es->lock); kfree(es, M_DEVBUF); return 0; @@ -965,5 +1620,5 @@ static driver_t es_driver = { }; DRIVER_MODULE(snd_es137x, pci, es_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_es137x, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_es137x, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_es137x, 1); diff --git a/sys/dev/sound/pci/es137x.h b/sys/dev/sound/pci/es137x.h index a3748050e1..18f7141759 100644 --- a/sys/dev/sound/pci/es137x.h +++ b/sys/dev/sound/pci/es137x.h @@ -1,4 +1,4 @@ -/* +/*- * This supports the ENSONIQ AudioPCI board based on the ES1370. * * Copyright (c) 1998 Joachim Kuebart @@ -18,8 +18,8 @@ * 4. Modifications may be freely made to this file if the above conditions * are met. * - * $FreeBSD: src/sys/dev/sound/pci/es137x.h,v 1.3.2.5 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/es137x.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/es137x.h,v 1.5.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/es137x.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _ES1370_REG_H @@ -167,6 +167,17 @@ #define ES1371_SRC_RAM_DATAO(o) (((o)&0xffff)<<0) /* current value of the sample rate converter */ #define ES1371_SRC_RAM_DATAI(i) (((i)>>0)&0xffff) /* current value of the sample rate converter */ +/* + * S/PDIF specific + */ + +/* Use ES1370_REG_CONTROL */ +#define RECEN_B 0x08000000 /* Used to control mixing of analog with digital data */ +#define SPDIFEN_B 0x04000000 /* Reset to switch digital output mux to "THRU" mode */ +/* Use ES1370_REG_STATUS */ +#define ENABLE_SPDIF 0x00040000 /* Used to enable the S/PDIF circuitry */ +#define TEST_SPDIF 0x00020000 /* Used to put the S/PDIF module in "test mode" */ + /* * Sample rate converter addresses */ diff --git a/sys/dev/sound/pci/fm801.c b/sys/dev/sound/pci/fm801.c index 0ed5c17460..a4e98004f7 100644 --- a/sys/dev/sound/pci/fm801.c +++ b/sys/dev/sound/pci/fm801.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Dmitry Dicky diwil@dataart.com * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/fm801.c,v 1.3.2.8 2002/12/24 21:17:42 semenu Exp $ - * $DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/fm801.c,v 1.27.2.1 2006/01/10 01:01:24 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -32,7 +32,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/fm801.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); #define PCI_VENDOR_FORTEMEDIA 0x1319 #define PCI_DEVICE_FORTEMEDIA1 0x08011319 @@ -117,33 +117,33 @@ static u_int32_t fmts[] = { }; static struct pcmchan_caps fm801ch_caps = { - 4000, 48000, + 5500, 48000, fmts, 0 }; struct fm801_info; struct fm801_chinfo { - struct fm801_info *parent; - struct pcm_channel *channel; - struct snd_dbuf *buffer; - u_int32_t spd, dir, fmt; /* speed, direction, format */ + struct fm801_info *parent; + struct pcm_channel *channel; + struct 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; + 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; + device_t dev; + int num; + u_int32_t unit; - struct resource *reg, *irq; - int regtype, regid, irqid; - void *ih; + struct resource *reg, *irq; + int regtype, regid, irqid; + void *ih; u_int32_t play_flip, play_nextblk, @@ -161,7 +161,7 @@ struct fm801_info { rec_shift, rec_size; - unsigned int bufsz; + unsigned int bufsz; struct fm801_chinfo pch, rch; @@ -337,7 +337,8 @@ fm801ch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, fm801->bufsz) == -1) return NULL; + if (sndbuf_alloc(ch->buffer, fm801->parent_dmat, fm801->bufsz) != 0) + return NULL; return (void *)ch; } @@ -391,7 +392,7 @@ fm801ch_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct fm801_chinfo *ch = data; struct fm801_info *fm801 = ch->parent; - int i; + register int i; for (i = 0; i < 10 && fm801_rates[i].limit <= speed; i++) ; @@ -439,7 +440,7 @@ 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(sndbuf_getbuf(ch->buffer)); + u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer); u_int32_t k1; DPRINT("fm801ch_trigger go %d , ", go); @@ -590,15 +591,17 @@ fm801_pci_attach(device_t dev) 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->regid = PCIR_BAR(i); fm801->regtype = SYS_RES_MEMORY; - fm801->reg = bus_alloc_resource(dev, fm801->regtype, &fm801->regid, - 0, ~0, 1, RF_ACTIVE); + fm801->reg = bus_alloc_resource_any(dev, fm801->regtype, + &fm801->regid, 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); + fm801->reg = bus_alloc_resource_any(dev, + fm801->regtype, + &fm801->regid, + RF_ACTIVE); } if(fm801->reg) { @@ -623,9 +626,9 @@ fm801_pci_attach(device_t dev) 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 || snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih, NULL)) { + fm801->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &fm801->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!fm801->irq || snd_setup_intr(dev, fm801->irq, 0, fm801_intr, fm801, &fm801->ih)) { device_printf(dev, "unable to map interrupt\n"); goto oops; } @@ -635,14 +638,15 @@ fm801_pci_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/fm801->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &fm801->parent_dmat) != 0) { + /*flags*/0, + &fm801->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto oops; } - ksnprintf(status, 64, "at %s 0x%lx irq %ld", + ksnprintf(status, 64, "at %s 0x%lx irq %ld %s", (fm801->regtype == SYS_RES_IOPORT)? "io" : "memory", - rman_get_start(fm801->reg), rman_get_start(fm801->irq)); + rman_get_start(fm801->reg), rman_get_start(fm801->irq),PCM_KLDSTRING(snd_fm801)); #define FM801_MAXPLAYCH 1 if (pcm_register(dev, fm801, FM801_MAXPLAYCH, 1)) goto oops; @@ -700,63 +704,11 @@ fm801_pci_detach(device_t dev) static int fm801_pci_probe( device_t dev ) { - u_int32_t data; - int id, regtype, regid, result; - struct resource *reg; - bus_space_tag_t st; - bus_space_handle_t sh; - - result = ENXIO; - - if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { - 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); - - regid = PCIR_MAPS; - regtype = SYS_RES_IOPORT; - reg = bus_alloc_resource(dev, regtype, ®id, 0, ~0, 1, - RF_ACTIVE); - - if (reg == NULL) - return ENXIO; - - st = rman_get_bustag(reg); - sh = rman_get_bushandle(reg); - /* - * XXX: quick check that device actually has sound capabilities. - * The problem is that some cards built around FM801 chip only - * have radio tuner onboard, but no sound capabilities. There - * is no "official" way to quickly check this, because all - * IDs are exactly the same. The only difference is 0x28 - * device control register, described in FM801 specification - * as "SRC/Mixer Test Control/DFC Status", but without - * any more detailed explanation. According to specs, and - * available sample cards (SF256-PCP-R and SF256-PCS-R) its - * power-on value should be `0', while on AC97-less tuner - * card (SF64-PCR) it was 0x80. - */ - /* - * HACK (on top of the hack above?) - * Unfortunately, some fully-sound-capable cards, like the - * TerraTec 512i, return 0x80. I don't know of any other - * differences to detect between the cards. As I have one - * of these cards but not an SF64-PCR, I'd prefer to have - * the card work, as would others with the same card. - * Therefore, not knowing better, disable this check. - */ -#ifdef I_HAVE_SF64_PCR - if (bus_space_read_1(st, sh, 0x28) == 0) { -#endif - device_set_desc(dev, - "Forte Media FM801 Audio Controller"); - result = 0; -#ifdef I_HAVE_SF64_PCR - } -#endif + int id; - bus_release_resource(dev, regtype, regid, reg); + if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA1 ) { + device_set_desc(dev, "Forte Media FM801 Audio Controller"); + return BUS_PROBE_DEFAULT; } /* if ((id = pci_get_devid(dev)) == PCI_DEVICE_FORTEMEDIA2 ) { @@ -764,7 +716,7 @@ fm801_pci_probe( device_t dev ) return ENXIO; } */ - return (result); + return ENXIO; } static struct resource * @@ -775,7 +727,7 @@ fm801_alloc_resource(device_t bus, device_t child, int type, int *rid, fm801 = pcm_getdevinfo(bus); - if (type == SYS_RES_IOPORT && *rid == PCIR_MAPS) + if (type == SYS_RES_IOPORT && *rid == PCIR_BAR(0)) return (fm801->reg); return (NULL); @@ -813,5 +765,5 @@ static driver_t fm801_driver = { }; DRIVER_MODULE(snd_fm801, pci, fm801_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_fm801, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_fm801, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_fm801, 1); diff --git a/sys/dev/sound/pci/gnu/csaimg.h b/sys/dev/sound/pci/gnu/csaimg.h index f5ad3fcf4b..8a2afff310 100644 --- a/sys/dev/sound/pci/gnu/csaimg.h +++ b/sys/dev/sound/pci/gnu/csaimg.h @@ -1,4 +1,5 @@ -/**************************************************************************** +/*- + **************************************************************************** * "CWCIMAGE.H"-- For CS46XX. Ver 1.04 * Copyright 1998-2001 (c) Cirrus Logic Corp. * Version 1.04 @@ -18,8 +19,8 @@ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, * USA. * - * $FreeBSD: src/sys/gnu/dev/sound/pci/csaimg.h,v 1.1.2.1 2001/08/01 03:41:09 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/gnu/csaimg.h,v 1.2 2003/06/17 04:28:33 dillon Exp $ + * $FreeBSD: src/sys/gnu/dev/sound/pci/csaimg.h,v 1.2 2005/01/06 18:27:30 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/gnu/csaimg.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ * ***************************************************************************/ diff --git a/sys/dev/sound/pci/gnu/emu10k1-ac97.h b/sys/dev/sound/pci/gnu/emu10k1-ac97.h new file mode 100644 index 0000000000..65b1be74c9 --- /dev/null +++ b/sys/dev/sound/pci/gnu/emu10k1-ac97.h @@ -0,0 +1,294 @@ +/* $FreeBSD: src/sys/gnu/dev/sound/pci/emu10k1-ac97.h,v 1.2 2004/01/09 05:08:32 obrien Exp $ */ +/* $DragonFly: src/sys/dev/sound/pci/gnu/emu10k1-ac97.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ */ + +#ifndef _AC97_CODEC_H_ +#define _AC97_CODEC_H_ + +#ifdef __linux__ +#include +#include +#endif + +/* AC97 1.0 */ +#define AC97_RESET 0x0000 // +#define AC97_MASTER_VOL_STEREO 0x0002 // Line Out +#define AC97_HEADPHONE_VOL 0x0004 // +#define AC97_MASTER_VOL_MONO 0x0006 // TAD Output +#define AC97_MASTER_TONE 0x0008 // +#define AC97_PCBEEP_VOL 0x000a // none +#define AC97_PHONE_VOL 0x000c // TAD Input (mono) +#define AC97_MIC_VOL 0x000e // MIC Input (mono) +#define AC97_LINEIN_VOL 0x0010 // Line Input (stereo) +#define AC97_CD_VOL 0x0012 // CD Input (stereo) +#define AC97_VIDEO_VOL 0x0014 // none +#define AC97_AUX_VOL 0x0016 // Aux Input (stereo) +#define AC97_PCMOUT_VOL 0x0018 // Wave Output (stereo) +#define AC97_RECORD_SELECT 0x001a // +#define AC97_RECORD_GAIN 0x001c +#define AC97_RECORD_GAIN_MIC 0x001e +#define AC97_GENERAL_PURPOSE 0x0020 +#define AC97_3D_CONTROL 0x0022 +#define AC97_MODEM_RATE 0x0024 +#define AC97_POWER_CONTROL 0x0026 + +/* AC'97 2.0 */ +#define AC97_EXTENDED_ID 0x0028 /* Extended Audio ID */ +#define AC97_EXTENDED_STATUS 0x002A /* Extended Audio Status */ +#define AC97_PCM_FRONT_DAC_RATE 0x002C /* PCM Front DAC Rate */ +#define AC97_PCM_SURR_DAC_RATE 0x002E /* PCM Surround DAC Rate */ +#define AC97_PCM_LFE_DAC_RATE 0x0030 /* PCM LFE DAC Rate */ +#define AC97_PCM_LR_ADC_RATE 0x0032 /* PCM LR ADC Rate */ +#define AC97_PCM_MIC_ADC_RATE 0x0034 /* PCM MIC ADC Rate */ +#define AC97_CENTER_LFE_MASTER 0x0036 /* Center + LFE Master Volume */ +#define AC97_SURROUND_MASTER 0x0038 /* Surround (Rear) Master Volume */ +#define AC97_RESERVED_3A 0x003A /* Reserved in AC '97 < 2.2 */ + +/* AC'97 2.2 */ +#define AC97_SPDIF_CONTROL 0x003A /* S/PDIF Control */ + +/* range 0x3c-0x58 - MODEM */ +#define AC97_EXTENDED_MODEM_ID 0x003C +#define AC97_EXTEND_MODEM_STAT 0x003E +#define AC97_LINE1_RATE 0x0040 +#define AC97_LINE2_RATE 0x0042 +#define AC97_HANDSET_RATE 0x0044 +#define AC97_LINE1_LEVEL 0x0046 +#define AC97_LINE2_LEVEL 0x0048 +#define AC97_HANDSET_LEVEL 0x004A +#define AC97_GPIO_CONFIG 0x004C +#define AC97_GPIO_POLARITY 0x004E +#define AC97_GPIO_STICKY 0x0050 +#define AC97_GPIO_WAKE_UP 0x0052 +#define AC97_GPIO_STATUS 0x0054 +#define AC97_MISC_MODEM_STAT 0x0056 +#define AC97_RESERVED_58 0x0058 + +/* registers 0x005a - 0x007a are vendor reserved */ + +#define AC97_VENDOR_ID1 0x007c +#define AC97_VENDOR_ID2 0x007e + +/* volume control bit defines */ +#ifndef AC97_MUTE +#define AC97_MUTE 0x8000 +#endif +#define AC97_MICBOOST 0x0040 +#define AC97_LEFTVOL 0x3f00 +#define AC97_RIGHTVOL 0x003f + +/* record mux defines */ +#define AC97_RECMUX_MIC 0x0000 +#define AC97_RECMUX_CD 0x0101 +#define AC97_RECMUX_VIDEO 0x0202 +#define AC97_RECMUX_AUX 0x0303 +#define AC97_RECMUX_LINE 0x0404 +#define AC97_RECMUX_STEREO_MIX 0x0505 +#define AC97_RECMUX_MONO_MIX 0x0606 +#define AC97_RECMUX_PHONE 0x0707 + +/* general purpose register bit defines */ +#define AC97_GP_LPBK 0x0080 /* Loopback mode */ +#define AC97_GP_MS 0x0100 /* Mic Select 0=Mic1, 1=Mic2 */ +#define AC97_GP_MIX 0x0200 /* Mono output select 0=Mix, 1=Mic */ +#define AC97_GP_RLBK 0x0400 /* Remote Loopback - Modem line codec */ +#define AC97_GP_LLBK 0x0800 /* Local Loopback - Modem Line codec */ +#define AC97_GP_LD 0x1000 /* Loudness 1=on */ +#define AC97_GP_3D 0x2000 /* 3D Enhancement 1=on */ +#define AC97_GP_ST 0x4000 /* Stereo Enhancement 1=on */ +#define AC97_GP_POP 0x8000 /* Pcm Out Path, 0=pre 3D, 1=post 3D */ + +/* extended audio status and control bit defines */ +#define AC97_EA_VRA 0x0001 /* Variable bit rate enable bit */ +#define AC97_EA_DRA 0x0002 /* Double-rate audio enable bit */ +#define AC97_EA_SPDIF 0x0004 /* S/PDIF Enable bit */ +#define AC97_EA_VRM 0x0008 /* Variable bit rate for MIC enable bit */ +#define AC97_EA_CDAC 0x0040 /* PCM Center DAC is ready (Read only) */ +#define AC97_EA_SDAC 0x0040 /* PCM Surround DACs are ready (Read only) */ +#define AC97_EA_LDAC 0x0080 /* PCM LFE DAC is ready (Read only) */ +#define AC97_EA_MDAC 0x0100 /* MIC ADC is ready (Read only) */ +#define AC97_EA_SPCV 0x0400 /* S/PDIF configuration valid (Read only) */ +#define AC97_EA_PRI 0x0800 /* Turns the PCM Center DAC off */ +#define AC97_EA_PRJ 0x1000 /* Turns the PCM Surround DACs off */ +#define AC97_EA_PRK 0x2000 /* Turns the PCM LFE DAC off */ +#define AC97_EA_PRL 0x4000 /* Turns the MIC ADC off */ +#define AC97_EA_SLOT_MASK 0xffcf /* Mask for slot assignment bits */ +#define AC97_EA_SPSA_3_4 0x0000 /* Slot assigned to 3 & 4 */ +#define AC97_EA_SPSA_7_8 0x0010 /* Slot assigned to 7 & 8 */ +#define AC97_EA_SPSA_6_9 0x0020 /* Slot assigned to 6 & 9 */ +#define AC97_EA_SPSA_10_11 0x0030 /* Slot assigned to 10 & 11 */ + +/* S/PDIF control bit defines */ +#define AC97_SC_PRO 0x0001 /* Professional status */ +#define AC97_SC_NAUDIO 0x0002 /* Non audio stream */ +#define AC97_SC_COPY 0x0004 /* Copyright status */ +#define AC97_SC_PRE 0x0008 /* Preemphasis status */ +#define AC97_SC_CC_MASK 0x07f0 /* Category Code mask */ +#define AC97_SC_L 0x0800 /* Generation Level status */ +#define AC97_SC_SPSR_MASK 0xcfff /* S/PDIF Sample Rate bits */ +#define AC97_SC_SPSR_44K 0x0000 /* Use 44.1kHz Sample rate */ +#define AC97_SC_SPSR_48K 0x2000 /* Use 48kHz Sample rate */ +#define AC97_SC_SPSR_32K 0x3000 /* Use 32kHz Sample rate */ +#define AC97_SC_DRS 0x4000 /* Double Rate S/PDIF */ +#define AC97_SC_V 0x8000 /* Validity status */ + +/* powerdown control and status bit defines */ + +/* status */ +#define AC97_PWR_MDM 0x0010 /* Modem section ready */ +#define AC97_PWR_REF 0x0008 /* Vref nominal */ +#define AC97_PWR_ANL 0x0004 /* Analog section ready */ +#define AC97_PWR_DAC 0x0002 /* DAC section ready */ +#define AC97_PWR_ADC 0x0001 /* ADC section ready */ + +/* control */ +#define AC97_PWR_PR0 0x0100 /* ADC and Mux powerdown */ +#define AC97_PWR_PR1 0x0200 /* DAC powerdown */ +#define AC97_PWR_PR2 0x0400 /* Output mixer powerdown (Vref on) */ +#define AC97_PWR_PR3 0x0800 /* Output mixer powerdown (Vref off) */ +#define AC97_PWR_PR4 0x1000 /* AC-link powerdown */ +#define AC97_PWR_PR5 0x2000 /* Internal Clk disable */ +#define AC97_PWR_PR6 0x4000 /* HP amp powerdown */ +#define AC97_PWR_PR7 0x8000 /* Modem off - if supported */ + +/* extended audio ID register bit defines */ +#define AC97_EXTID_VRA 0x0001 +#define AC97_EXTID_DRA 0x0002 +#define AC97_EXTID_SPDIF 0x0004 +#define AC97_EXTID_VRM 0x0008 +#define AC97_EXTID_DSA0 0x0010 +#define AC97_EXTID_DSA1 0x0020 +#define AC97_EXTID_CDAC 0x0040 +#define AC97_EXTID_SDAC 0x0080 +#define AC97_EXTID_LDAC 0x0100 +#define AC97_EXTID_AMAP 0x0200 +#define AC97_EXTID_REV0 0x0400 +#define AC97_EXTID_REV1 0x0800 +#define AC97_EXTID_ID0 0x4000 +#define AC97_EXTID_ID1 0x8000 + +/* extended status register bit defines */ +#define AC97_EXTSTAT_VRA 0x0001 +#define AC97_EXTSTAT_DRA 0x0002 +#define AC97_EXTSTAT_SPDIF 0x0004 +#define AC97_EXTSTAT_VRM 0x0008 +#define AC97_EXTSTAT_SPSA0 0x0010 +#define AC97_EXTSTAT_SPSA1 0x0020 +#define AC97_EXTSTAT_CDAC 0x0040 +#define AC97_EXTSTAT_SDAC 0x0080 +#define AC97_EXTSTAT_LDAC 0x0100 +#define AC97_EXTSTAT_MADC 0x0200 +#define AC97_EXTSTAT_SPCV 0x0400 +#define AC97_EXTSTAT_PRI 0x0800 +#define AC97_EXTSTAT_PRJ 0x1000 +#define AC97_EXTSTAT_PRK 0x2000 +#define AC97_EXTSTAT_PRL 0x4000 + +/* useful power states */ +#define AC97_PWR_D0 0x0000 /* everything on */ +#define AC97_PWR_D1 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR4 +#define AC97_PWR_D2 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_D3 AC97_PWR_PR0|AC97_PWR_PR1|AC97_PWR_PR2|AC97_PWR_PR3|AC97_PWR_PR4 +#define AC97_PWR_ANLOFF AC97_PWR_PR2|AC97_PWR_PR3 /* analog section off */ + +/* Total number of defined registers. */ +#define AC97_REG_CNT 64 + + +/* OSS interface to the ac97s.. */ +#define AC97_STEREO_MASK (SOUND_MASK_VOLUME|SOUND_MASK_PCM|\ + SOUND_MASK_LINE|SOUND_MASK_CD|\ + SOUND_MASK_ALTPCM|SOUND_MASK_IGAIN|\ + SOUND_MASK_LINE1|SOUND_MASK_VIDEO) + +#define AC97_SUPPORTED_MASK (AC97_STEREO_MASK | \ + SOUND_MASK_BASS|SOUND_MASK_TREBLE|\ + SOUND_MASK_SPEAKER|SOUND_MASK_MIC|\ + SOUND_MASK_PHONEIN|SOUND_MASK_PHONEOUT) + +#define AC97_RECORD_MASK (SOUND_MASK_MIC|\ + SOUND_MASK_CD|SOUND_MASK_IGAIN|SOUND_MASK_VIDEO|\ + SOUND_MASK_LINE1| SOUND_MASK_LINE|\ + SOUND_MASK_PHONEIN) + +/* original check is not good enough in case FOO is greater than + * SOUND_MIXER_NRDEVICES because the supported_mixers has exactly + * SOUND_MIXER_NRDEVICES elements. + * before matching the given mixer against the bitmask in supported_mixers we + * check if mixer number exceeds maximum allowed size which is as mentioned + * above SOUND_MIXER_NRDEVICES */ +#define supported_mixer(CODEC,FOO) ((FOO >= 0) && \ + (FOO < SOUND_MIXER_NRDEVICES) && \ + (CODEC)->supported_mixers & (1<, + * Creative Labs, Inc. + * Definitions for EMU10K1 (SB Live!) chips * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. * - * You should have received a copy of the GNU General Public - * License along with this program; if not, write to the Free - * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, - * USA. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - ********************************************************************** - * $FreeBSD: src/sys/gnu/dev/sound/pci/emu10k1.h,v 1.2.2.3 2001/08/01 03:41:09 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/gnu/emu10k1.h,v 1.3 2004/01/06 16:07:41 asmodai Exp $ */ +/* $FreeBSD: src/sys/gnu/dev/sound/pci/emu10k1-alsa.h,v 1.2 2005/01/06 18:26:37 imp Exp $ */ +/* $DragonFly: src/sys/dev/sound/pci/gnu/emu10k1-alsa.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ */ + +#ifdef __KERNEL__ -#ifndef EMU10K1_H -#define EMU10K1_H +#include +#include +#include +#include +#include +#include +#include + +#ifndef PCI_VENDOR_ID_CREATIVE +#define PCI_VENDOR_ID_CREATIVE 0x1102 +#endif +#ifndef PCI_DEVICE_ID_CREATIVE_EMU10K1 +#define PCI_DEVICE_ID_CREATIVE_EMU10K1 0x0002 +#endif /* ------------------- DEFINES -------------------- */ -#define EMUPAGESIZE 4096 /* don't change */ -#define MAXREQVOICES 8 -#define MAXPAGES (32768 * 64 / EMUPAGESIZE) /* WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE */ -#define RESERVED 0 -#define NUM_MIDI 16 -#define NUM_G 64 /* use all channels */ -#define NUM_FXSENDS 4 +#define EMUPAGESIZE 4096 +#define MAXREQVOICES 8 +#define MAXPAGES 8192 +#define RESERVED 0 +#define NUM_MIDI 16 +#define NUM_G 64 /* use all channels */ +#define NUM_FXSENDS 4 +#define EMU10K1_DMA_MASK 0x7fffffffUL /* 31bit */ +#define AUDIGY_DMA_MASK 0xffffffffUL /* 32bit */ -#define TMEMSIZE 256*1024 -#define TMEMSIZEREG 4 +#define TMEMSIZE 256*1024 +#define TMEMSIZEREG 4 #define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) +// Audigy specify registers are prefixed with 'A_' + /************************************************************************************************/ /* PCI function 0 registers, address = + PCIBASE0 */ /************************************************************************************************/ @@ -71,7 +75,7 @@ /* accessed. For non per-channel registers the */ /* value should be set to zero. */ #define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ -#define A_PTR_ADDRESS_MASK 0x0fff0000 /* Audigy register index */ +#define A_PTR_ADDRESS_MASK 0x0fff0000 #define DATA 0x04 /* Indexed register set data register */ @@ -80,8 +84,8 @@ /* the relevant bits and zero to the other bits */ /* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ -#define A_IPR_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ -#define A_IPR_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ +#define IPR_A_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ +#define IPR_A_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ #define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ #define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ @@ -107,10 +111,6 @@ /* IP is written with CL set, the bit in CLIPL */ /* or CLIPH corresponding to the CIN value */ /* written will be cleared. */ -/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU2) */ -#define A_IPR_MIDITRANSBUFEMPTY1 IPR_MIDITRANSBUFEMPTY /* MIDI UART transmit buffer empty */ -#define A_IPR_MIDIRECVBUFEMPTY1 IPR_MIDIRECVBUFEMPTY /* MIDI UART receive buffer empty */ - #define INTE 0x0c /* Interrupt enable register */ #define INTE_VIRTUALSB_MASK 0xc0000000 /* Virtual Soundblaster I/O port capture */ @@ -138,9 +138,9 @@ /* behavior and possibly random segfaults and */ /* lockups if enabled. */ -/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ -#define A_INTE_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ -#define A_INTE_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +#define INTE_A_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ +#define INTE_A_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ #define INTE_SAMPLERATETRACKER 0x00002000 /* Enable sample rate tracker interrupts */ @@ -159,10 +159,6 @@ #define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ #define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ -/* The next two interrupts are for the midi port on the Audigy (A_MPU2) */ -#define A_INTE_MIDITXENABLE1 INTE_MIDITXENABLE -#define A_INTE_MIDIRXENABLE1 INTE_MIDIRXENABLE - #define WC 0x10 /* Wall Clock register */ #define WC_SAMPLECOUNTER_MASK 0x03FFFFC0 /* Sample periods elapsed since reset */ #define WC_SAMPLECOUNTER 0x14060010 @@ -194,28 +190,27 @@ #define HCFG_CODECFORMAT_I2S 0x00010000 /* I2S CODEC format -- Secondary (Rear) Output */ #define HCFG_GPINPUT0 0x00004000 /* External pin112 */ #define HCFG_GPINPUT1 0x00002000 /* External pin110 */ - #define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ -#define HCFG_GPOUT0 0x00001000 /* set to enable digital out on 5.1 cards */ -#define HCFG_GPOUT1 0x00000800 /* External pin (IR) */ -#define HCFG_GPOUT2 0x00000400 /* External pin (IR) */ - +#define HCFG_GPOUT0 0x00001000 /* External pin? (spdif enable on 5.1) */ +#define HCFG_GPOUT1 0x00000800 /* External pin? (IR) */ +#define HCFG_GPOUT2 0x00000400 /* External pin? (IR) */ #define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ #define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ /* 1 = Force all 3 async digital inputs to use */ /* the same async sample rate tracker (ZVIDEO) */ -#define HCFG_AC3ENABLE_MASK 0x0x0000e0 /* AC3 async input control - Not implemented */ +#define HCFG_AC3ENABLE_MASK 0x000000e0 /* AC3 async input control - Not implemented */ #define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ #define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ -#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ #define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ /* will automatically mute their output when */ /* they are not rate-locked to the external */ /* async audio source */ #define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ /* NOTE: This should generally never be used. */ -#define HCFG_LOCKTANKCACHE 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 #define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ /* NOTE: This is a 'cheap' way to implement a */ /* master mute function on the mute button, and */ @@ -226,7 +221,7 @@ /* Should be set to 1 when the EMU10K1 is */ /* completely initialized. */ -/* For Audigy, MPU port move to 0x70-0x74 ptr register */ +//For Audigy, MPU port move to 0x70-0x74 ptr register #define MUDATA 0x18 /* MPU401 data register (8 bits) */ @@ -240,13 +235,13 @@ #define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ #define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ -#define A_IOCFG_GPOUT_MASK 0x00ff -#define A_IOCFG_GPOUT_D 0x04 /* Digital Output */ -#define A_IOCFG_GPOUT_A 0x40 /* Analog Output */ -#define A_IOCFG_GPOUT_AD (A_IOCFG_GPOUT_A|A_IOCFG_GPOUT_D) -#define A_IOCFG_GPINPUT_MASK 0xff00 +#define A_GPINPUT_MASK 0xff00 +#define A_GPOUTPUT_MASK 0x00ff +#define A_IOCFG_GPOUT0 0x0044 /* analog/digital? */ +#define A_IOCFG_GPOUT1 0x0002 /* IR */ +#define A_IOCFG_GPOUT2 0x0001 /* IR */ -#define TIMER 0x1a /* Timer terminal count register (16-bit) */ +#define TIMER 0x1a /* Timer terminal count register */ /* NOTE: After the rate is changed, a maximum */ /* of 1024 sample periods should be allowed */ /* before the new rate is guaranteed accurate. */ @@ -279,35 +274,6 @@ #define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ -/********************************************************************************************************/ -/* AC97 pointer-offset register set, accessed through the AC97ADDRESS and AC97DATA registers */ -/********************************************************************************************************/ - -#define AC97_RESET 0x00 -#define AC97_MASTERVOLUME 0x02 /* Master volume */ -#define AC97_HEADPHONEVOLUME 0x04 /* Headphone volume */ -#define AC97_MASTERVOLUMEMONO 0x06 /* Mast volume mono */ -#define AC97_MASTERTONE 0x08 -#define AC97_PCBEEPVOLUME 0x0a /* PC speaker system beep volume */ -#define AC97_PHONEVOLUME 0x0c -#define AC97_MICVOLUME 0x0e -#define AC97_LINEINVOLUME 0x10 -#define AC97_CDVOLUME 0x12 -#define AC97_VIDEOVOLUME 0x14 -#define AC97_AUXVOLUME 0x16 -#define AC97_PCMOUTVOLUME 0x18 -#define AC97_RECORDSELECT 0x1a -#define AC97_RECORDGAIN 0x1c -#define AC97_RECORDGAINMIC 0x1e -#define AC97_GENERALPUPOSE 0x20 -#define AC97_3DCONTROL 0x22 -#define AC97_MODEMRATE 0x24 -#define AC97_POWERDOWN 0x26 -#define AC97_VENDORID1 0x7c -#define AC97_VENDORID2 0x7e -#define AC97_ZVIDEOVOLUME 0xec -#define AC97_AC3VOLUME 0xed - /********************************************************************************************************/ /* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ /********************************************************************************************************/ @@ -335,7 +301,9 @@ #define VTFT 0x03 /* Volume target and filter cutoff target register */ #define VTFT_VOLUMETARGET_MASK 0xffff0000 /* Volume target of specified channel */ +#define VTFT_VOLUMETARGET 0x10100003 #define VTFT_FILTERTARGET_MASK 0x0000ffff /* Filter cutoff target of specified channel */ +#define VTFT_FILTERTARGET 0x10000003 #define Z1 0x05 /* Filter delay memory 1 register */ @@ -410,7 +378,7 @@ #define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ #define ENVVOL 0x10 /* Volume envelope register */ -#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ /* 0x8000-n == 666*n usec delay */ #define ATKHLDV 0x11 /* Volume envelope hold and attack register */ @@ -484,6 +452,8 @@ #define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ /* Signed 2's complement, with +/- 12dB extremes */ +#define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */ + /* ??Hz steps, maximum of ?? Hz. */ #define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ #define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ /* Signed 2's complement, +/- one octave extremes */ @@ -526,9 +496,8 @@ #define A_ADCCR_RCHANENABLE 0x00000020 #define A_ADCCR_LCHANENABLE 0x00000010 -#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ +#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ #define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ - #define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ #define ADCCR_SAMPLERATE_44 0x00000001 /* 44.1kHz sample rate */ #define ADCCR_SAMPLERATE_32 0x00000002 /* 32kHz sample rate */ @@ -537,17 +506,27 @@ #define ADCCR_SAMPLERATE_16 0x00000005 /* 16kHz sample rate */ #define ADCCR_SAMPLERATE_11 0x00000006 /* 11.025kHz sample rate */ #define ADCCR_SAMPLERATE_8 0x00000007 /* 8kHz sample rate */ - #define A_ADCCR_SAMPLERATE_12 0x00000006 /* 12kHz sample rate */ #define A_ADCCR_SAMPLERATE_11 0x00000007 /* 11.025kHz sample rate */ #define A_ADCCR_SAMPLERATE_8 0x00000008 /* 8kHz sample rate */ #define FXWC 0x43 /* FX output write channels register */ /* When set, each bit enables the writing of the */ - /* corresponding FX output channel (internal registers */ - /* 0x20-0x3f) into host memory. This mode of recording */ - /* is 16bit, 48kHz only. All 32 channels can be enabled */ - /* simultaneously. */ + /* corresponding FX output channel into host memory */ +#define FXWC_DEFAULTROUTE_C (1<<0) /* left emu out? */ +#define FXWC_DEFAULTROUTE_B (1<<1) /* right emu out? */ +#define FXWC_DEFAULTROUTE_A (1<<12) +#define FXWC_DEFAULTROUTE_D (1<<13) +#define FXWC_ADCLEFT (1<<18) +#define FXWC_CDROMSPDIFLEFT (1<<18) +#define FXWC_ADCRIGHT (1<<19) +#define FXWC_CDROMSPDIFRIGHT (1<<19) +#define FXWC_MIC (1<<20) +#define FXWC_ZOOMLEFT (1<<20) +#define FXWC_ZOOMRIGHT (1<<21) +#define FXWC_SPDIFLEFT (1<<22) /* 0x00400000 */ +#define FXWC_SPDIFRIGHT (1<<23) /* 0x00800000 */ + #define TCBS 0x44 /* Tank cache buffer size register */ #define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ #define TCBS_BUFFSIZE_16K 0x00000000 @@ -615,16 +594,6 @@ #define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ -/* definitions for debug register - taken from the alsa drivers */ -#define DBG_ZC 0x80000000 /* zero tram counter */ -#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ -#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ -#define DBG_SINGLE_STEP 0x00008000 /* single step mode */ -#define DBG_STEP 0x00004000 /* start single step */ -#define DBG_CONDITION_CODE 0x00003e00 /* condition code */ -#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ - - #define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ #define A_DBG 0x53 @@ -680,9 +649,9 @@ #define SPBYPASS 0x5e /* SPDIF BYPASS mode register */ #define SPBYPASS_ENABLE 0x00000001 /* Enable SPDIF bypass mode */ -#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ -#define AC97SLOT_CNTR 0x10 /* Center enable */ -#define AC97SLOT_LFE 0x20 /* LFE enable */ +#define AC97SLOT 0x5f /* additional AC97 slots enable bits */ +#define AC97SLOT_CNTR 0x10 /* Center enable */ +#define AC97SLOT_LFE 0x20 /* LFE enable */ #define CDSRCS 0x60 /* CD-ROM Sample Rate Converter status register */ @@ -697,8 +666,7 @@ #define SRCS_RATELOCKED 0x01000000 /* Sample rate locked */ #define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ - -/* Note that these values can vary +/- by a small amount */ +/* Note that these values can vary +/- by a small amount */ #define SRCS_SPDIFRATE_44 0x0003acd9 #define SRCS_SPDIFRATE_48 0x00040000 #define SRCS_SPDIFRATE_96 0x00080000 @@ -707,13 +675,16 @@ #define MICIDX_MASK 0x0000ffff /* 16-bit value */ #define MICIDX_IDX 0x10000063 -#define A_ADCIDX 0x63 -#define A_ADCIDX_IDX 0x10000063 - #define ADCIDX 0x64 /* ADC recording buffer index register */ #define ADCIDX_MASK 0x0000ffff /* 16 bit index field */ #define ADCIDX_IDX 0x10000064 +#define A_ADCIDX 0x63 +#define A_ADCIDX_IDX 0x10000063 + +#define A_MICIDX 0x64 +#define A_MICIDX_IDX 0x10000064 + #define FXIDX 0x65 /* FX recording buffer index register */ #define FXIDX_MASK 0x0000ffff /* 16-bit value */ #define FXIDX_IDX 0x10000065 @@ -723,16 +694,16 @@ #define A_MUCMD1 0x71 #define A_MUSTAT1 A_MUCMD1 -/* This is the MPU port on the Audigy Drive */ +/* This is the MPU port on the Audigy Drive */ #define A_MUDATA2 0x72 #define A_MUCMD2 0x73 -#define A_MUSTAT2 A_MUCMD2 +#define A_MUSTAT2 A_MUCMD2 /* The next two are the Audigy equivalent of FXWC */ -/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */ +/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */ /* Each bit selects a channel for recording */ -#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ -#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ +#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ +#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ #define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ #define A_SPDIF_48000 0x00000080 @@ -746,10 +717,10 @@ #define A_FXRT_CHANNELH 0x3f000000 /* Effects send bus number for channel's effects send H */ #define A_SENDAMOUNTS 0x7d -#define A_FXSENDAMOUNT_E_MASK 0xff000000 -#define A_FXSENDAMOUNT_F_MASK 0x00ff0000 -#define A_FXSENDAMOUNT_G_MASK 0x0000ff00 -#define A_FXSENDAMOUNT_H_MASK 0x000000ff +#define A_FXSENDAMOUNT_E_MASK 0xFF000000 +#define A_FXSENDAMOUNT_F_MASK 0x00FF0000 +#define A_FXSENDAMOUNT_G_MASK 0x0000FF00 +#define A_FXSENDAMOUNT_H_MASK 0x000000FF /* The send amounts for this one are the same as used with the emu10k1 */ #define A_FXRT1 0x7e @@ -762,6 +733,7 @@ /* Each FX general purpose register is 32 bits in length, all bits are used */ #define FXGPREGBASE 0x100 /* FX general purpose registers base */ #define A_FXGPREGBASE 0x400 /* Audigy GPRs, 0x400 to 0x5ff */ + /* Tank audio data is logarithmically compressed down to 16 bits before writing to TRAM and is */ /* decompressed back to 20 bits on a read. There are a total of 160 locations, the last 32 */ /* locations are for external TRAM. */ @@ -788,68 +760,396 @@ /* Audigy Soundcard have a different instruction format */ -#define AUDIGY_CODEBASE 0x600 +#define A_MICROCODEBASE 0x600 #define A_LOWORD_OPY_MASK 0x000007ff #define A_LOWORD_OPX_MASK 0x007ff000 #define A_HIWORD_OPCODE_MASK 0x0f000000 #define A_HIWORD_RESULT_MASK 0x007ff000 #define A_HIWORD_OPA_MASK 0x000007ff -/* Opcodes macros -- Based on ALSA emu10k1.h */ - -/* FX8010/EMU10K1/EMU10K2 Instruction set */ -#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ -#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ -#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ -#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ -#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ -#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ -#define iACC3 0x06 /* R = A + X + Y ; saturation */ -#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ -#define iANDXOR 0x08 /* R = (A & X) ^ Y */ -#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ -#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ -#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ -#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ -#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ -#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ -#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ - -/* FX8010 opcode macros */ -#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ -#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ -#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ -#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ -#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ -#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ - -/* FX8010 Constants */ -#define C_00000000 0x40 -#define C_00000001 0x41 -#define C_00000002 0x42 -#define C_00000003 0x43 -#define C_00000004 0x44 -#define C_00000008 0x45 -#define C_00000010 0x46 -#define C_00000020 0x47 -#define C_00000100 0x48 -#define C_00010000 0x49 -#define C_00080000 0x4a -#define C_10000000 0x4b -#define C_20000000 0x4c -#define C_40000000 0x4d -#define C_80000000 0x4e -#define C_7fffffff 0x4f -#define C_ffffffff 0x50 -#define C_fffffffe 0x51 -#define C_c0000000 0x52 -#define C_4f1bbcdc 0x53 -#define C_5a7ef9db 0x54 -#define C_00100000 0x55 /* ?? */ - -/* FX8010 FX Send Bus Registers */ + +/* ------------------- STRUCTURES -------------------- */ + +typedef struct _snd_emu10k1 emu10k1_t; +typedef struct _snd_emu10k1_voice emu10k1_voice_t; +typedef struct _snd_emu10k1_pcm emu10k1_pcm_t; + +typedef enum { + EMU10K1_PCM, + EMU10K1_SYNTH, + EMU10K1_MIDI +} emu10k1_voice_type_t; + +struct _snd_emu10k1_voice { + emu10k1_t *emu; + int number; + int use: 1, + pcm: 1, + synth: 1, + midi: 1; + void (*interrupt)(emu10k1_t *emu, emu10k1_voice_t *pvoice); + + emu10k1_pcm_t *epcm; +}; + +typedef enum { + PLAYBACK_EMUVOICE, + CAPTURE_AC97ADC, + CAPTURE_AC97MIC, + CAPTURE_EFX +} snd_emu10k1_pcm_type_t; + +struct _snd_emu10k1_pcm { + emu10k1_t *emu; + snd_emu10k1_pcm_type_t type; + snd_pcm_substream_t *substream; + emu10k1_voice_t *voices[2]; + emu10k1_voice_t *extra; + unsigned short running; + unsigned short first_ptr; + snd_util_memblk_t *memblk; + unsigned int start_addr; + unsigned int ccca_start_addr; + unsigned int capture_ipr; /* interrupt acknowledge mask */ + unsigned int capture_inte; /* interrupt enable mask */ + unsigned int capture_ba_reg; /* buffer address register */ + unsigned int capture_bs_reg; /* buffer size register */ + unsigned int capture_idx_reg; /* buffer index register */ + unsigned int capture_cr_val; /* control value */ + unsigned int capture_cr_val2; /* control value2 (for audigy) */ + unsigned int capture_bs_val; /* buffer size value */ + unsigned int capture_bufsize; /* buffer size in bytes */ +}; + +typedef struct { + unsigned char send_routing[3][8]; + unsigned char send_volume[3][8]; + unsigned short attn[3]; + emu10k1_pcm_t *epcm; +} emu10k1_pcm_mixer_t; + +#define snd_emu10k1_compose_send_routing(route) \ +((route[0] | (route[1] << 4) | (route[2] << 8) | (route[3] << 12)) << 16) + +#define snd_emu10k1_compose_audigy_fxrt1(route) \ +(((unsigned int)route[0] | ((unsigned int)route[1] << 8) | ((unsigned int)route[2] << 16) | ((unsigned int)route[3] << 12)) << 24) + +#define snd_emu10k1_compose_audigy_fxrt2(route) \ +(((unsigned int)route[4] | ((unsigned int)route[5] << 8) | ((unsigned int)route[6] << 16) | ((unsigned int)route[7] << 12)) << 24) + +typedef struct snd_emu10k1_memblk { + snd_util_memblk_t mem; + /* private part */ + short first_page, last_page, pages, mapped_page; + unsigned int map_locked; + struct list_head mapped_link; + struct list_head mapped_order_link; +} emu10k1_memblk_t; + +#define snd_emu10k1_memblk_offset(blk) (((blk)->mapped_page << PAGE_SHIFT) | ((blk)->mem.offset & (PAGE_SIZE - 1))) + +#define EMU10K1_MAX_TRAM_BLOCKS_PER_CODE 16 + +typedef struct { + struct list_head list; /* list link container */ + unsigned int vcount; + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ + snd_kcontrol_t *kcontrol; +} snd_emu10k1_fx8010_ctl_t; + +typedef void (snd_fx8010_irq_handler_t)(emu10k1_t *emu, void *private_data); + +typedef struct _snd_emu10k1_fx8010_irq { + struct _snd_emu10k1_fx8010_irq *next; + snd_fx8010_irq_handler_t *handler; + unsigned char gpr_running; + void *private_data; +} snd_emu10k1_fx8010_irq_t; + +typedef struct { + unsigned int valid: 1, + opened: 1, + active: 1; + unsigned int channels; /* 16-bit channels count */ + unsigned int tram_start; /* initial ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ring buffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char etram[32]; /* external TRAM address & data */ + unsigned int sw_data, hw_data; + unsigned int sw_io, hw_io; + unsigned int sw_ready, hw_ready; + unsigned int appl_ptr; + unsigned int tram_pos; + unsigned int tram_shift; + snd_emu10k1_fx8010_irq_t *irq; +} snd_emu10k1_fx8010_pcm_t; + +typedef struct { + unsigned short fxbus_mask; /* used FX buses (bitmask) */ + unsigned short extin_mask; /* used external inputs (bitmask) */ + unsigned short extout_mask; /* used external outputs (bitmask) */ + unsigned short pad1; + unsigned int itram_size; /* internal TRAM size in samples */ + unsigned int etram_size; /* external TRAM size in samples */ + void *etram_pages; /* allocated pages for external TRAM */ + dma_addr_t etram_pages_dmaaddr; + unsigned int dbg; /* FX debugger register */ + unsigned char name[128]; + int gpr_size; /* size of allocated GPR controls */ + int gpr_count; /* count of used kcontrols */ + struct list_head gpr_ctl; /* GPR controls */ + struct semaphore lock; + snd_emu10k1_fx8010_pcm_t pcm[8]; + spinlock_t irq_lock; + snd_emu10k1_fx8010_irq_t *irq_handlers; +} snd_emu10k1_fx8010_t; + +#define emu10k1_gpr_ctl(n) list_entry(n, snd_emu10k1_fx8010_ctl_t, list) + +typedef struct { + struct _snd_emu10k1 *emu; + snd_rawmidi_t *rmidi; + snd_rawmidi_substream_t *substream_input; + snd_rawmidi_substream_t *substream_output; + unsigned int midi_mode; + spinlock_t input_lock; + spinlock_t output_lock; + spinlock_t open_lock; + int tx_enable, rx_enable; + int port; + int ipr_tx, ipr_rx; + void (*interrupt)(emu10k1_t *emu, unsigned int status); +} emu10k1_midi_t; + +struct _snd_emu10k1 { + int irq; + + unsigned long port; /* I/O port number */ + struct resource *res_port; + int APS: 1, /* APS flag */ + no_ac97: 1, /* no AC'97 */ + tos_link: 1; /* tos link detected */ + unsigned int audigy; /* is Audigy? */ + unsigned int revision; /* chip revision */ + unsigned int serial; /* serial number */ + unsigned short model; /* subsystem id */ + unsigned int card_type; /* EMU10K1_CARD_* */ + unsigned int ecard_ctrl; /* ecard control bits */ + unsigned long dma_mask; /* PCI DMA mask */ + int max_cache_pages; /* max memory size / PAGE_SIZE */ + void *silent_page; /* silent page */ + dma_addr_t silent_page_dmaaddr; + volatile u32 *ptb_pages; /* page table pages */ + dma_addr_t ptb_pages_dmaaddr; + snd_util_memhdr_t *memhdr; /* page allocation list */ + emu10k1_memblk_t *reserved_page; /* reserved page */ + + struct list_head mapped_link_head; + struct list_head mapped_order_link_head; + void **page_ptr_table; + unsigned long *page_addr_table; + spinlock_t memblk_lock; + + unsigned int spdif_bits[3]; /* s/pdif out setup */ + + snd_emu10k1_fx8010_t fx8010; /* FX8010 info */ + int gpr_base; + + ac97_t *ac97; + + struct pci_dev *pci; + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_t *pcm_mic; + snd_pcm_t *pcm_efx; + snd_pcm_t *pcm_fx8010; + + spinlock_t synth_lock; + void *synth; + int (*get_synth_voice)(emu10k1_t *emu); + + spinlock_t reg_lock; + spinlock_t emu_lock; + spinlock_t voice_lock; + struct semaphore ptb_lock; + + emu10k1_voice_t voices[64]; + emu10k1_pcm_mixer_t pcm_mixer[32]; + snd_kcontrol_t *ctl_send_routing; + snd_kcontrol_t *ctl_send_volume; + snd_kcontrol_t *ctl_attn; + + void (*hwvol_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_mic_interrupt)(emu10k1_t *emu, unsigned int status); + void (*capture_efx_interrupt)(emu10k1_t *emu, unsigned int status); + void (*timer_interrupt)(emu10k1_t *emu); + void (*spdif_interrupt)(emu10k1_t *emu, unsigned int status); + void (*dsp_interrupt)(emu10k1_t *emu); + + snd_pcm_substream_t *pcm_capture_substream; + snd_pcm_substream_t *pcm_capture_mic_substream; + snd_pcm_substream_t *pcm_capture_efx_substream; + + emu10k1_midi_t midi; + emu10k1_midi_t midi2; /* for audigy */ + + unsigned int efx_voices_mask[2]; +}; + +int snd_emu10k1_create(snd_card_t * card, + struct pci_dev *pci, + unsigned short extin_mask, + unsigned short extout_mask, + long max_cache_bytes, + int enable_ir, + emu10k1_t ** remu); + +int snd_emu10k1_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_mic(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_pcm_efx(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_fx8010_pcm(emu10k1_t * emu, int device, snd_pcm_t ** rpcm); +int snd_emu10k1_mixer(emu10k1_t * emu); +int snd_emu10k1_fx8010_new(emu10k1_t *emu, int device, snd_hwdep_t ** rhwdep); + +irqreturn_t snd_emu10k1_interrupt(int irq, void *dev_id, struct pt_regs *regs); + +/* initialization */ +void snd_emu10k1_voice_init(emu10k1_t * emu, int voice); +int snd_emu10k1_init_efx(emu10k1_t *emu); +void snd_emu10k1_free_efx(emu10k1_t *emu); +int snd_emu10k1_fx8010_tram_setup(emu10k1_t *emu, u32 size); + +/* I/O functions */ +unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn); +void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data); +void snd_emu10k1_efx_write(emu10k1_t *emu, unsigned int pc, unsigned int data); +unsigned int snd_emu10k1_efx_read(emu10k1_t *emu, unsigned int pc); +void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb); +void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum); +void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait); +static inline unsigned int snd_emu10k1_wc(emu10k1_t *emu) { return (inl(emu->port + WC) >> 6) & 0xfffff; } +unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg); +void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data); +unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate); +unsigned char snd_emu10k1_sum_vol_attn(unsigned int value); + +/* memory allocation */ +snd_util_memblk_t *snd_emu10k1_alloc_pages(emu10k1_t *emu, snd_pcm_substream_t *substream); +int snd_emu10k1_free_pages(emu10k1_t *emu, snd_util_memblk_t *blk); +snd_util_memblk_t *snd_emu10k1_synth_alloc(emu10k1_t *emu, unsigned int size); +int snd_emu10k1_synth_free(emu10k1_t *emu, snd_util_memblk_t *blk); +int snd_emu10k1_synth_bzero(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, int size); +int snd_emu10k1_synth_copy_from_user(emu10k1_t *emu, snd_util_memblk_t *blk, int offset, const char *data, int size); +int snd_emu10k1_memblk_map(emu10k1_t *emu, emu10k1_memblk_t *blk); + +/* voice allocation */ +int snd_emu10k1_voice_alloc(emu10k1_t *emu, emu10k1_voice_type_t type, int pair, emu10k1_voice_t **rvoice); +int snd_emu10k1_voice_free(emu10k1_t *emu, emu10k1_voice_t *pvoice); + +/* MIDI uart */ +int snd_emu10k1_midi(emu10k1_t * emu); +int snd_emu10k1_audigy_midi(emu10k1_t * emu); + +/* proc interface */ +int snd_emu10k1_proc_init(emu10k1_t * emu); + +#endif /* __KERNEL__ */ + +/* + * ---- FX8010 ---- + */ + +#define EMU10K1_CARD_CREATIVE 0x00000000 +#define EMU10K1_CARD_EMUAPS 0x00000001 + +#define EMU10K1_FX8010_PCM_COUNT 8 + +/* instruction set */ +#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ +#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ +#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ +#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ +#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ +#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ +#define iACC3 0x06 /* R = A + X + Y ; saturation */ +#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ +#define iANDXOR 0x08 /* R = (A & X) ^ Y */ +#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ +#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ +#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ +#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ +#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ +#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ +#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ + +/* GPRs */ +#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ +#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ +#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ +#define C_00000000 0x40 +#define C_00000001 0x41 +#define C_00000002 0x42 +#define C_00000003 0x43 +#define C_00000004 0x44 +#define C_00000008 0x45 +#define C_00000010 0x46 +#define C_00000020 0x47 +#define C_00000100 0x48 +#define C_00010000 0x49 +#define C_00080000 0x4a +#define C_10000000 0x4b +#define C_20000000 0x4c +#define C_40000000 0x4d +#define C_80000000 0x4e +#define C_7fffffff 0x4f +#define C_ffffffff 0x50 +#define C_fffffffe 0x51 +#define C_c0000000 0x52 +#define C_4f1bbcdc 0x53 +#define C_5a7ef9db 0x54 +#define C_00100000 0x55 /* ?? */ +#define GPR_ACCU 0x56 /* ACCUM, accumulator */ +#define GPR_COND 0x57 /* CCR, condition register */ +#define GPR_NOISE0 0x58 /* noise source */ +#define GPR_NOISE1 0x59 /* noise source */ +#define GPR_IRQ 0x5a /* IRQ register */ +#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ +#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ +#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ +#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ +#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ + +#define A_FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x3f? */ +#define A_EXTIN(x) (0x40 + (x)) /* x = 0x00 - 0x1f? */ +#define A_EXTOUT(x) (0x60 + (x)) /* x = 0x00 - 0x1f? */ +#define A_GPR(x) (A_FXGPREGBASE + (x)) + +/* cc_reg constants */ +#define CC_REG_NORMALIZED C_00000001 +#define CC_REG_BORROW C_00000002 +#define CC_REG_MINUS C_00000004 +#define CC_REG_ZERO C_00000008 +#define CC_REG_SATURATE C_00000010 +#define CC_REG_NONZERO C_00000100 + +/* FX buses */ #define FXBUS_PCM_LEFT 0x00 #define FXBUS_PCM_RIGHT 0x01 #define FXBUS_PCM_LEFT_REAR 0x02 @@ -858,140 +1158,216 @@ #define FXBUS_MIDI_RIGHT 0x05 #define FXBUS_PCM_CENTER 0x06 #define FXBUS_PCM_LFE 0x07 +#define FXBUS_PCM_LEFT_FRONT 0x08 +#define FXBUS_PCM_RIGHT_FRONT 0x09 #define FXBUS_MIDI_REVERB 0x0c #define FXBUS_MIDI_CHORUS 0x0d - -/* FX8010 GPRs */ -#define GPR_ACCU 0x56 /* ACCUM, accumulator */ -#define GPR_COND 0x57 /* CCR, condition register */ -#define GPR_NOISE0 0x58 /* noise source */ -#define GPR_NOISE1 0x59 /* noise source */ -#define GPR_IRQ 0x5a /* IRQ register */ -#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ - -/* FX8010 Inputs */ -#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ -#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ -#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ -#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ -#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ -#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ -#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ -#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ -#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ -#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ -#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ -#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ -#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ -#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ - -/* FX8010 Outputs */ -#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ -#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ -#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ -#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ -#define EXTOUT_CENTER 0x04 /* SB Live 5.1 - center */ -#define EXTOUT_LFE 0x05 /* SB Live 5.1 - LFE */ -#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ -#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ -#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ -#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ -#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ -#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ -#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ -#define EXTOUT_ACENTER 0x11 /* Analog Center */ -#define EXTOUT_ALFE 0x12 /* Analog LFE */ - -/* Audigy opcode macros */ -#define A_FXBUS(x) ((x) + 0x00) -#define A_EXTIN(x) ((x) + 0x40) -#define A_EXTOUT(x) ((x) + 0x60) -#define A_GPR(x) ((x) + A_FXGPREGBASE) - -/* Audigy constants */ -#define A_C_00000000 0xc0 -#define A_C_00000001 0xc1 -#define A_C_00000002 0xc2 -#define A_C_00000003 0xc3 -#define A_C_00000004 0xc4 -#define A_C_00000008 0xc5 -#define A_C_00000010 0xc6 -#define A_C_00000020 0xc7 -#define A_C_00000100 0xc8 -#define A_C_00010000 0xc9 -#define A_C_00000800 0xca -#define A_C_10000000 0xcb -#define A_C_20000000 0xcc -#define A_C_40000000 0xcd -#define A_C_80000000 0xce -#define A_C_7fffffff 0xcf -#define A_C_ffffffff 0xd0 -#define A_C_fffffffe 0xd1 -#define A_C_c0000000 0xd2 -#define A_C_4f1bbcdc 0xd3 -#define A_C_5a7ef9db 0xd4 -#define A_C_00100000 0xd5 - -/* Audigy FX Send Bus Registers */ -#define A_FXBUS_PCM_LEFT FXBUS_PCM_LEFT -#define A_FXBUS_PCM_RIGHT FXBUS_PCM_RIGHT -#define A_FXBUS_PCM_LEFT_REAR FXBUS_PCM_LEFT_REAR -#define A_FXBUS_PCM_RIGHT_REAR FXBUS_PCM_RIGHT_REAR -#define A_FXBUS_MIDI_LEFT FXBUS_MIDI_LEFT -#define A_FXBUS_MIDI_RIGHT FXBUS_MIDI_RIGHT -#define A_FXBUS_PCM_CENTER FXBUS_PCM_CENTER -#define A_FXBUS_PCM_LFE FXBUS_PCM_LFE -#define A_FXBUS_MIDI_REVERB FXBUS_MIDI_REVERB -#define A_FXBUS_MIDI_CHORUS FXBUS_MIDI_CHORUS - -#define A_GPR_ACCU 0xd6 /* 0xd6 = 0x7fffffff (?) ACCUM? */ -#define A_GPR_COND 0xd7 /* 0xd7 = 0x0000000 CCR */ -#define A_GPR_NOISE0 0xd8 /* 0xd8 = noise1 */ -#define A_GPR_NOISE1 0xd9 /* 0xd9 = noise2 */ -#define A_GPR_IRQ 0xda /* IRQ register */ -#define A_GPR_DBAC 0xdb /* TRAM Delay Base Address Counter */ +#define FXBUS_PT_LEFT 0x14 +#define FXBUS_PT_RIGHT 0x15 + +/* Inputs */ +#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ +#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ +#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ +#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ +#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ +#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ +#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ +#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ +#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ +#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ +#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ +#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ + +/* Outputs */ +#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ +#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ +#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ +#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ +#define EXTOUT_CENTER 0x04 /* SB Live 5.1 - center */ +#define EXTOUT_LFE 0x05 /* SB Live 5.1 - LFE */ +#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ +#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ +#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ +#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ +#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ +#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ +#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ +#define EXTOUT_ACENTER 0x11 /* Analog Center */ +#define EXTOUT_ALFE 0x12 /* Analog LFE */ /* Audigy Inputs */ -#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ -#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ -#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ -#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ -#define A_EXTIN_OPT_SPDIF_L 0x04 /* audigy drive Optical SPDIF - left */ -#define A_EXTIN_OPT_SPDIF_R 0x05 /* right */ -#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ -#define A_EXTIN_LINE2_R 0x09 /* right */ -#define A_EXTIN_RCA_SPDIF_L 0x0a /* audigy drive RCA SPDIF - left */ -#define A_EXTIN_RCA_SPDIF_R 0x0b /* right */ -#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ -#define A_EXTIN_AUX2_R 0x0d /* - right */ - -/* Audigy Outputs */ -#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ -#define A_EXTOUT_FRONT_R 0x01 /* right */ -#define A_EXTOUT_CENTER 0x02 /* digital front center */ -#define A_EXTOUT_LFE 0x03 /* digital front lfe */ -#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ -#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ -#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ -#define A_EXTOUT_REAR_R 0x07 /* right */ -#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ -#define A_EXTOUT_AFRONT_R 0x09 /* right */ -#define A_EXTOUT_ACENTER 0x0a /* analog center */ -#define A_EXTOUT_ALFE 0x0b /* analog LFE */ +#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ +#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ +#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ +#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ +#define A_EXTIN_OPT_SPDIF_L 0x04 /* audigy drive Optical SPDIF - left */ +#define A_EXTIN_OPT_SPDIF_R 0x05 /* right */ +#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ +#define A_EXTIN_LINE2_R 0x09 /* right */ +#define A_EXTIN_ADC_L 0x0a /* Philips ADC - left */ +#define A_EXTIN_ADC_R 0x0b /* right */ +#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ +#define A_EXTIN_AUX2_R 0x0d /* - right */ + +/* Audigiy Outputs */ +#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ +#define A_EXTOUT_FRONT_R 0x01 /* right */ +#define A_EXTOUT_CENTER 0x02 /* digital front center */ +#define A_EXTOUT_LFE 0x03 /* digital front lfe */ +#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ +#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ +#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ +#define A_EXTOUT_REAR_R 0x07 /* right */ +#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ +#define A_EXTOUT_AFRONT_R 0x09 /* right */ +#define A_EXTOUT_ACENTER 0x0a /* analog center */ +#define A_EXTOUT_ALFE 0x0b /* analog LFE */ /* 0x0c ?? */ /* 0x0d ?? */ -#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ -#define A_EXTOUT_AREAR_R 0x0f /* right */ -#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ -#define A_EXTOUT_AC97_R 0x11 /* right */ -#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ -#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ +#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ +#define A_EXTOUT_AREAR_R 0x0f /* right */ +#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ +#define A_EXTOUT_AC97_R 0x11 /* right */ +#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ +#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ +#define A_EXTOUT_MIC_CAP 0x18 /* Mic capture buffer */ - -#define ENABLE 0xffffffff -#define DISABLE 0x00000000 - -#define ENV_ON 0x80 -#define ENV_OFF 0x00 - -#endif /* EMU10K1_H */ +/* Audigy constants */ +#define A_C_00000000 0xc0 +#define A_C_00000001 0xc1 +#define A_C_00000002 0xc2 +#define A_C_00000003 0xc3 +#define A_C_00000004 0xc4 +#define A_C_00000008 0xc5 +#define A_C_00000010 0xc6 +#define A_C_00000020 0xc7 +#define A_C_00000100 0xc8 +#define A_C_00010000 0xc9 +#define A_C_00000800 0xca +#define A_C_10000000 0xcb +#define A_C_20000000 0xcc +#define A_C_40000000 0xcd +#define A_C_80000000 0xce +#define A_C_7fffffff 0xcf +#define A_C_ffffffff 0xd0 +#define A_C_fffffffe 0xd1 +#define A_C_c0000000 0xd2 +#define A_C_4f1bbcdc 0xd3 +#define A_C_5a7ef9db 0xd4 +#define A_C_00100000 0xd5 +#define A_GPR_ACCU 0xd6 /* ACCUM, accumulator */ +#define A_GPR_COND 0xd7 /* CCR, condition register */ +/* 0xd8 = noise1 */ +/* 0xd9 = noise2 */ + +/* definitions for debug register */ +#define EMU10K1_DBG_ZC 0x80000000 /* zero tram counter */ +#define EMU10K1_DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define EMU10K1_DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define EMU10K1_DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define EMU10K1_DBG_STEP 0x00004000 /* start single step */ +#define EMU10K1_DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define EMU10K1_DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ + +/* tank memory address line */ +#ifndef __KERNEL__ +#define TANKMEMADDRREG_ADDR_MASK 0x000fffff /* 20 bit tank address field */ +#define TANKMEMADDRREG_CLEAR 0x00800000 /* Clear tank memory */ +#define TANKMEMADDRREG_ALIGN 0x00400000 /* Align read or write relative to tank access */ +#define TANKMEMADDRREG_WRITE 0x00200000 /* Write to tank memory */ +#define TANKMEMADDRREG_READ 0x00100000 /* Read from tank memory */ +#endif + +typedef struct { + unsigned int card; /* card type */ + unsigned int internal_tram_size; /* in samples */ + unsigned int external_tram_size; /* in samples */ + char fxbus_names[16][32]; /* names of FXBUSes */ + char extin_names[16][32]; /* names of external inputs */ + char extout_names[32][32]; /* names of external outputs */ + unsigned int gpr_controls; /* count of GPR controls */ +} emu10k1_fx8010_info_t; + +#define EMU10K1_GPR_TRANSLATION_NONE 0 +#define EMU10K1_GPR_TRANSLATION_TABLE100 1 +#define EMU10K1_GPR_TRANSLATION_BASS 2 +#define EMU10K1_GPR_TRANSLATION_TREBLE 3 +#define EMU10K1_GPR_TRANSLATION_ONOFF 4 + +typedef struct { + snd_ctl_elem_id_t id; /* full control ID definition */ + unsigned int vcount; /* visible count */ + unsigned int count; /* count of GPR (1..16) */ + unsigned char gpr[32]; /* GPR number(s) */ + unsigned int value[32]; /* initial values */ + unsigned int min; /* minimum range */ + unsigned int max; /* maximum range */ + unsigned int translation; /* translation type (EMU10K1_GPR_TRANSLATION*) */ +} emu10k1_fx8010_control_gpr_t; + +typedef struct { + char name[128]; + + unsigned long gpr_valid[0x100/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */ + unsigned int gpr_map[0x100]; /* initializers */ + + unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */ + emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */ + + unsigned int gpr_del_control_count; /* count of GPR controls to remove */ + snd_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */ + + unsigned int gpr_list_control_count; /* count of GPR controls to list */ + unsigned int gpr_list_control_total; /* total count of GPR controls */ + emu10k1_fx8010_control_gpr_t *gpr_list_controls; /* listed GPR controls */ + + unsigned long tram_valid[0xa0/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */ + unsigned int tram_data_map[0xa0]; /* data initializers */ + unsigned int tram_addr_map[0xa0]; /* map initializers */ + + unsigned long code_valid[512/(sizeof(unsigned long)*8)]; /* bitmask of valid instructions */ + unsigned int code[512][2]; /* one instruction - 64 bits */ +} emu10k1_fx8010_code_t; + +typedef struct { + unsigned int address; /* 31.bit == 1 -> external TRAM */ + unsigned int size; /* size in samples (4 bytes) */ + unsigned int *samples; /* pointer to samples (20-bit) */ + /* NULL->clear memory */ +} emu10k1_fx8010_tram_t; + +typedef struct { + unsigned int substream; /* substream number */ + unsigned int res1; /* reserved */ + unsigned int channels; /* 16-bit channels count, zero = remove this substream */ + unsigned int tram_start; /* ring buffer position in TRAM (in samples) */ + unsigned int buffer_size; /* count of buffered samples */ + unsigned char gpr_size; /* GPR containing size of ringbuffer in samples (host) */ + unsigned char gpr_ptr; /* GPR containing current pointer in the ring buffer (host = reset, FX8010) */ + unsigned char gpr_count; /* GPR containing count of samples between two interrupts (host) */ + unsigned char gpr_tmpcount; /* GPR containing current count of samples to interrupt (host = set, FX8010) */ + unsigned char gpr_trigger; /* GPR containing trigger (activate) information (host) */ + unsigned char gpr_running; /* GPR containing info if PCM is running (FX8010) */ + unsigned char pad; /* reserved */ + unsigned char etram[32]; /* external TRAM address & data (one per channel) */ + unsigned int res2; /* reserved */ +} emu10k1_fx8010_pcm_t; + +#define SNDRV_EMU10K1_IOCTL_INFO _IOR ('H', 0x10, emu10k1_fx8010_info_t) +#define SNDRV_EMU10K1_IOCTL_CODE_POKE _IOW ('H', 0x11, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_CODE_PEEK _IOWR('H', 0x12, emu10k1_fx8010_code_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_SETUP _IOW ('H', 0x20, int) +#define SNDRV_EMU10K1_IOCTL_TRAM_POKE _IOW ('H', 0x21, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_TRAM_PEEK _IOWR('H', 0x22, emu10k1_fx8010_tram_t) +#define SNDRV_EMU10K1_IOCTL_PCM_POKE _IOW ('H', 0x30, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_PCM_PEEK _IOWR('H', 0x31, emu10k1_fx8010_pcm_t) +#define SNDRV_EMU10K1_IOCTL_STOP _IO ('H', 0x80) +#define SNDRV_EMU10K1_IOCTL_CONTINUE _IO ('H', 0x81) +#define SNDRV_EMU10K1_IOCTL_ZERO_TRAM_COUNTER _IO ('H', 0x82) +#define SNDRV_EMU10K1_IOCTL_SINGLE_STEP _IOW ('H', 0x83, int) +#define SNDRV_EMU10K1_IOCTL_DBG_READ _IOR ('H', 0x84, int) + +#endif /* __SOUND_EMU10K1_H */ diff --git a/sys/dev/sound/pci/gnu/emu10k1.h b/sys/dev/sound/pci/gnu/emu10k1.h index a0f6d85fe3..1853008462 100644 --- a/sys/dev/sound/pci/gnu/emu10k1.h +++ b/sys/dev/sound/pci/gnu/emu10k1.h @@ -1,20 +1,18 @@ -/* +/*- ********************************************************************** * emu10k1.h, derived from 8010.h - * Copyright 1999, 2000 Creative Labs, Inc. + * Copyright 1999-2001 Creative Labs, Inc. * ********************************************************************** * - * Date Author Summary of changes - * ---- ------ ------------------ - * October 20, 1999 Bertrand Lee base code release - * November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS - * line endings - * December 8, 1999 Jon Taylor Added lots of new register info - * February 10, 2003 Orlando Bassotto Added Audigy registers - * and opcode macros from - * ALSA project emu10k1.h. - * + * Date Author Summary of changes + * ---- ------ ------------------ + * October 20, 1999 Bertrand Lee base code release + * November 2, 1999 Alan Cox Cleaned of 8bit chars, DOS + * line endings + * December 8, 1999 Jon Taylor Added lots of new register info + * May 16, 2001 Daniel Bertrand Added unofficial DBG register info + * Oct-Nov 2001 D.B. Added unofficial Audigy registers * ********************************************************************** * @@ -35,29 +33,25 @@ * * ********************************************************************** - * $FreeBSD: src/sys/gnu/dev/sound/pci/emu10k1.h,v 1.2.2.3 2001/08/01 03:41:09 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/gnu/emu10k1.h,v 1.3 2004/01/06 16:07:41 asmodai Exp $ + * $FreeBSD: src/sys/gnu/dev/sound/pci/emu10k1.h,v 1.8 2005/01/06 18:27:30 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/gnu/emu10k1.h,v 1.4 2007/01/04 21:47:03 corecode Exp $ */ -#ifndef EMU10K1_H -#define EMU10K1_H +#ifndef _8010_H +#define _8010_H -/* ------------------- DEFINES -------------------- */ + /* +#include + */ -#define EMUPAGESIZE 4096 /* don't change */ -#define MAXREQVOICES 8 -#define MAXPAGES (32768 * 64 / EMUPAGESIZE) /* WAVEOUT_MAXBUFSIZE * NUM_G / EMUPAGESIZE */ -#define RESERVED 0 -#define NUM_MIDI 16 -#define NUM_G 64 /* use all channels */ -#define NUM_FXSENDS 4 +/* Driver version: */ +#define MAJOR_VER 0 +#define MINOR_VER 20 +#define DRIVER_VERSION "0.20a" -#define TMEMSIZE 256*1024 -#define TMEMSIZEREG 4 - -#define IP_TO_CP(ip) ((ip == 0) ? 0 : (((0x00001000uL | (ip & 0x00000FFFL)) << (((ip >> 12) & 0x000FL) + 4)) & 0xFFFF0000uL)) +/* Audigy specify registers are prefixed with 'A_' */ /************************************************************************************************/ /* PCI function 0 registers, address = + PCIBASE0 */ @@ -71,7 +65,6 @@ /* accessed. For non per-channel registers the */ /* value should be set to zero. */ #define PTR_ADDRESS_MASK 0x07ff0000 /* Register index */ -#define A_PTR_ADDRESS_MASK 0x0fff0000 /* Audigy register index */ #define DATA 0x04 /* Indexed register set data register */ @@ -80,8 +73,8 @@ /* the relevant bits and zero to the other bits */ /* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ -#define A_IPR_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ -#define A_IPR_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ +#define A_IPR_MIDITRANSBUFEMPTY2 0x10000000 /* MIDI UART transmit buffer empty */ +#define A_IPR_MIDIRECVBUFEMPTY2 0x08000000 /* MIDI UART receive buffer empty */ #define IPR_SAMPLERATETRACKER 0x01000000 /* Sample rate tracker lock status change */ #define IPR_FXDSP 0x00800000 /* Enable FX DSP interrupts */ @@ -107,9 +100,9 @@ /* IP is written with CL set, the bit in CLIPL */ /* or CLIPH corresponding to the CIN value */ /* written will be cleared. */ -/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU2) */ -#define A_IPR_MIDITRANSBUFEMPTY1 IPR_MIDITRANSBUFEMPTY /* MIDI UART transmit buffer empty */ -#define A_IPR_MIDIRECVBUFEMPTY1 IPR_MIDIRECVBUFEMPTY /* MIDI UART receive buffer empty */ +#define A_IPR_MIDITRANSBUFEMPTY1 IPR_MIDITRANSBUFEMPTY /* MIDI UART transmit buffer empty */ +#define A_IPR_MIDIRECVBUFEMPTY1 IPR_MIDIRECVBUFEMPTY /* MIDI UART receive buffer empty */ + #define INTE 0x0c /* Interrupt enable register */ @@ -138,7 +131,7 @@ /* behavior and possibly random segfaults and */ /* lockups if enabled. */ -/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ +/* The next two interrupts are for the midi port on the Audigy Drive (A_MPU1) */ #define A_INTE_MIDITXENABLE2 0x00020000 /* Enable MIDI transmit-buffer-empty interrupts */ #define A_INTE_MIDIRXENABLE2 0x00010000 /* Enable MIDI receive-buffer-empty interrupts */ @@ -159,8 +152,8 @@ #define INTE_MIDITXENABLE 0x00000002 /* Enable MIDI transmit-buffer-empty interrupts */ #define INTE_MIDIRXENABLE 0x00000001 /* Enable MIDI receive-buffer-empty interrupts */ -/* The next two interrupts are for the midi port on the Audigy (A_MPU2) */ -#define A_INTE_MIDITXENABLE1 INTE_MIDITXENABLE +/* The next two interrupts are for the midi port on the Audigy (A_MPU2) */ +#define A_INTE_MIDITXENABLE1 INTE_MIDITXENABLE #define A_INTE_MIDIRXENABLE1 INTE_MIDIRXENABLE #define WC 0x10 /* Wall Clock register */ @@ -197,8 +190,6 @@ #define HCFG_GPOUTPUT_MASK 0x00001c00 /* External pins which may be controlled */ #define HCFG_GPOUT0 0x00001000 /* set to enable digital out on 5.1 cards */ -#define HCFG_GPOUT1 0x00000800 /* External pin (IR) */ -#define HCFG_GPOUT2 0x00000400 /* External pin (IR) */ #define HCFG_JOYENABLE 0x00000200 /* Internal joystick enable */ #define HCFG_PHASETRACKENABLE 0x00000100 /* Phase tracking enable */ @@ -207,15 +198,16 @@ #define HCFG_AC3ENABLE_MASK 0x0x0000e0 /* AC3 async input control - Not implemented */ #define HCFG_AC3ENABLE_ZVIDEO 0x00000080 /* Channels 0 and 1 replace ZVIDEO */ #define HCFG_AC3ENABLE_CDSPDIF 0x00000040 /* Channels 0 and 1 replace CDSPDIF */ -#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ +#define HCFG_AC3ENABLE_GPSPDIF 0x00000020 /* Channels 0 and 1 replace GPSPDIF */ #define HCFG_AUTOMUTE 0x00000010 /* When set, the async sample rate convertors */ /* will automatically mute their output when */ /* they are not rate-locked to the external */ /* async audio source */ #define HCFG_LOCKSOUNDCACHE 0x00000008 /* 1 = Cancel bustmaster accesses to soundcache */ /* NOTE: This should generally never be used. */ -#define HCFG_LOCKTANKCACHE 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ +#define HCFG_LOCKTANKCACHE_MASK 0x00000004 /* 1 = Cancel bustmaster accesses to tankcache */ /* NOTE: This should generally never be used. */ +#define HCFG_LOCKTANKCACHE 0x01020014 #define HCFG_MUTEBUTTONENABLE 0x00000002 /* 1 = Master mute button sets AUDIOENABLE = 0. */ /* NOTE: This is a 'cheap' way to implement a */ /* master mute function on the mute button, and */ @@ -240,19 +232,15 @@ #define MUSTAT_ORDYN 0x40 /* 0 = MUDATA can accept a command or data */ #define A_IOCFG 0x18 /* GPIO on Audigy card (16bits) */ -#define A_IOCFG_GPOUT_MASK 0x00ff -#define A_IOCFG_GPOUT_D 0x04 /* Digital Output */ -#define A_IOCFG_GPOUT_A 0x40 /* Analog Output */ -#define A_IOCFG_GPOUT_AD (A_IOCFG_GPOUT_A|A_IOCFG_GPOUT_D) -#define A_IOCFG_GPINPUT_MASK 0xff00 +#define A_GPINPUT_MASK 0xff00 +#define A_GPOUTPUT_MASK 0x00ff #define TIMER 0x1a /* Timer terminal count register (16-bit) */ /* NOTE: After the rate is changed, a maximum */ /* of 1024 sample periods should be allowed */ /* before the new rate is guaranteed accurate. */ -#define TIMER_RATE_MASK 0x000003ff /* Timer interrupt rate in sample periods */ +#define TIMER_RATE_MASK 0x03ff /* Timer interrupt rate in sample periods */ /* 0 == 1024 periods, [1..4] are not useful */ -#define TIMER_RATE 0x0a00001a #define AC97DATA 0x1c /* AC97 register set data register (16 bit) */ @@ -260,54 +248,6 @@ #define AC97ADDRESS_READY 0x80 /* Read-only bit, reflects CODEC READY signal */ #define AC97ADDRESS_ADDRESS 0x7f /* Address of indexed AC97 register */ -/************************************************************************************************/ -/* PCI function 1 registers, address = + PCIBASE1 */ -/************************************************************************************************/ - -#define JOYSTICK1 0x00 /* Analog joystick port register */ -#define JOYSTICK2 0x01 /* Analog joystick port register */ -#define JOYSTICK3 0x02 /* Analog joystick port register */ -#define JOYSTICK4 0x03 /* Analog joystick port register */ -#define JOYSTICK5 0x04 /* Analog joystick port register */ -#define JOYSTICK6 0x05 /* Analog joystick port register */ -#define JOYSTICK7 0x06 /* Analog joystick port register */ -#define JOYSTICK8 0x07 /* Analog joystick port register */ - -/* When writing, any write causes JOYSTICK_COMPARATOR output enable to be pulsed on write. */ -/* When reading, use these bitfields: */ -#define JOYSTICK_BUTTONS 0x0f /* Joystick button data */ -#define JOYSTICK_COMPARATOR 0xf0 /* Joystick comparator data */ - - -/********************************************************************************************************/ -/* AC97 pointer-offset register set, accessed through the AC97ADDRESS and AC97DATA registers */ -/********************************************************************************************************/ - -#define AC97_RESET 0x00 -#define AC97_MASTERVOLUME 0x02 /* Master volume */ -#define AC97_HEADPHONEVOLUME 0x04 /* Headphone volume */ -#define AC97_MASTERVOLUMEMONO 0x06 /* Mast volume mono */ -#define AC97_MASTERTONE 0x08 -#define AC97_PCBEEPVOLUME 0x0a /* PC speaker system beep volume */ -#define AC97_PHONEVOLUME 0x0c -#define AC97_MICVOLUME 0x0e -#define AC97_LINEINVOLUME 0x10 -#define AC97_CDVOLUME 0x12 -#define AC97_VIDEOVOLUME 0x14 -#define AC97_AUXVOLUME 0x16 -#define AC97_PCMOUTVOLUME 0x18 -#define AC97_RECORDSELECT 0x1a -#define AC97_RECORDGAIN 0x1c -#define AC97_RECORDGAINMIC 0x1e -#define AC97_GENERALPUPOSE 0x20 -#define AC97_3DCONTROL 0x22 -#define AC97_MODEMRATE 0x24 -#define AC97_POWERDOWN 0x26 -#define AC97_VENDORID1 0x7c -#define AC97_VENDORID2 0x7e -#define AC97_ZVIDEOVOLUME 0xec -#define AC97_AC3VOLUME 0xed - /********************************************************************************************************/ /* Emu10k1 pointer-offset register set, accessed through the PTR and DATA registers */ /********************************************************************************************************/ @@ -410,7 +350,7 @@ #define MAP_PTI_MASK 0x00001fff /* The 13 bit index to one of the 8192 PTE dwords */ #define ENVVOL 0x10 /* Volume envelope register */ -#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ +#define ENVVOL_MASK 0x0000ffff /* Current value of volume envelope state variable */ /* 0x8000-n == 666*n usec delay */ #define ATKHLDV 0x11 /* Volume envelope hold and attack register */ @@ -483,6 +423,8 @@ #define TREMFRQ 0x1c /* Tremolo amount and modulation LFO frequency register */ #define TREMFRQ_DEPTH 0x0000ff00 /* Tremolo depth */ /* Signed 2's complement, with +/- 12dB extremes */ +#define TREMFRQ_FREQUENCY 0x000000ff /* Tremolo LFO frequency */ + /* ??Hz steps, maximum of ?? Hz. */ #define FM2FRQ2 0x1d /* Vibrato amount and vibrato LFO frequency register */ #define FM2FRQ2_DEPTH 0x0000ff00 /* Vibrato LFO vibrato depth */ @@ -526,7 +468,7 @@ #define A_ADCCR_RCHANENABLE 0x00000020 #define A_ADCCR_LCHANENABLE 0x00000010 -#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ +#define A_ADCCR_SAMPLERATE_MASK 0x0000000F /* Audigy sample rate convertor output rate */ #define ADCCR_SAMPLERATE_MASK 0x00000007 /* Sample rate convertor output rate */ #define ADCCR_SAMPLERATE_48 0x00000000 /* 48kHz sample rate */ @@ -544,9 +486,9 @@ #define FXWC 0x43 /* FX output write channels register */ /* When set, each bit enables the writing of the */ - /* corresponding FX output channel (internal registers */ + /* corresponding FX output channel (internal registers */ /* 0x20-0x3f) into host memory. This mode of recording */ - /* is 16bit, 48kHz only. All 32 channels can be enabled */ + /* is 16bit, 48KHz only. All 32 channels can be enabled */ /* simultaneously. */ #define TCBS 0x44 /* Tank cache buffer size register */ #define TCBS_MASK 0x00000007 /* Tank cache buffer size field */ @@ -616,13 +558,13 @@ #define DBG 0x52 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ /* definitions for debug register - taken from the alsa drivers */ -#define DBG_ZC 0x80000000 /* zero tram counter */ -#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ -#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ -#define DBG_SINGLE_STEP 0x00008000 /* single step mode */ -#define DBG_STEP 0x00004000 /* start single step */ -#define DBG_CONDITION_CODE 0x00003e00 /* condition code */ -#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ +#define DBG_ZC 0x80000000 /* zero tram counter */ +#define DBG_SATURATION_OCCURED 0x02000000 /* saturation control */ +#define DBG_SATURATION_ADDR 0x01ff0000 /* saturation address */ +#define DBG_SINGLE_STEP 0x00008000 /* single step mode */ +#define DBG_STEP 0x00004000 /* start single step */ +#define DBG_CONDITION_CODE 0x00003e00 /* condition code */ +#define DBG_SINGLE_STEP_ADDR 0x000001ff /* single step address */ #define REG53 0x53 /* DO NOT PROGRAM THIS REGISTER!!! MAY DESTROY CHIP */ @@ -698,7 +640,7 @@ #define SRCS_ESTSAMPLERATE 0x0007ffff /* Do not modify this field. */ -/* Note that these values can vary +/- by a small amount */ +/* Note that these values can vary +/- by a small amount */ #define SRCS_SPDIFRATE_44 0x0003acd9 #define SRCS_SPDIFRATE_48 0x00040000 #define SRCS_SPDIFRATE_96 0x00080000 @@ -723,16 +665,16 @@ #define A_MUCMD1 0x71 #define A_MUSTAT1 A_MUCMD1 -/* This is the MPU port on the Audigy Drive */ +/* This is the MPU port on the Audigy Drive */ #define A_MUDATA2 0x72 #define A_MUCMD2 0x73 -#define A_MUSTAT2 A_MUCMD2 +#define A_MUSTAT2 A_MUCMD2 /* The next two are the Audigy equivalent of FXWC */ -/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */ +/* the Audigy can record any output (16bit, 48kHz, up to 64 channel simultaneously) */ /* Each bit selects a channel for recording */ -#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ -#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ +#define A_FXWC1 0x74 /* Selects 0x7f-0x60 for FX recording */ +#define A_FXWC2 0x75 /* Selects 0x9f-0x80 for FX recording */ #define A_SPDIF_SAMPLERATE 0x76 /* Set the sample rate of SPDIF output */ #define A_SPDIF_48000 0x00000080 @@ -789,209 +731,11 @@ /* Audigy Soundcard have a different instruction format */ #define AUDIGY_CODEBASE 0x600 -#define A_LOWORD_OPY_MASK 0x000007ff +#define A_LOWORD_OPY_MASK 0x000007ff #define A_LOWORD_OPX_MASK 0x007ff000 #define A_HIWORD_OPCODE_MASK 0x0f000000 #define A_HIWORD_RESULT_MASK 0x007ff000 #define A_HIWORD_OPA_MASK 0x000007ff -/* Opcodes macros -- Based on ALSA emu10k1.h */ - -/* FX8010/EMU10K1/EMU10K2 Instruction set */ -#define iMAC0 0x00 /* R = A + (X * Y >> 31) ; saturation */ -#define iMAC1 0x01 /* R = A + (-X * Y >> 31) ; saturation */ -#define iMAC2 0x02 /* R = A + (X * Y >> 31) ; wraparound */ -#define iMAC3 0x03 /* R = A + (-X * Y >> 31) ; wraparound */ -#define iMACINT0 0x04 /* R = A + X * Y ; saturation */ -#define iMACINT1 0x05 /* R = A + X * Y ; wraparound (31-bit) */ -#define iACC3 0x06 /* R = A + X + Y ; saturation */ -#define iMACMV 0x07 /* R = A, acc += X * Y >> 31 */ -#define iANDXOR 0x08 /* R = (A & X) ^ Y */ -#define iTSTNEG 0x09 /* R = (A >= Y) ? X : ~X */ -#define iLIMITGE 0x0a /* R = (A >= Y) ? X : Y */ -#define iLIMITLT 0x0b /* R = (A < Y) ? X : Y */ -#define iLOG 0x0c /* R = linear_data, A (log_data), X (max_exp), Y (format_word) */ -#define iEXP 0x0d /* R = log_data, A (linear_data), X (max_exp), Y (format_word) */ -#define iINTERP 0x0e /* R = A + (X * (Y - A) >> 31) ; saturation */ -#define iSKIP 0x0f /* R = A (cc_reg), X (count), Y (cc_test) */ - -/* FX8010 opcode macros */ -#define FXBUS(x) (0x00 + (x)) /* x = 0x00 - 0x0f */ -#define EXTIN(x) (0x10 + (x)) /* x = 0x00 - 0x0f */ -#define EXTOUT(x) (0x20 + (x)) /* x = 0x00 - 0x0f */ -#define GPR(x) (FXGPREGBASE + (x)) /* free GPRs: x = 0x00 - 0xff */ -#define ITRAM_DATA(x) (TANKMEMDATAREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_DATA(x) (TANKMEMDATAREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ -#define ITRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x00 + (x)) /* x = 0x00 - 0x7f */ -#define ETRAM_ADDR(x) (TANKMEMADDRREGBASE + 0x80 + (x)) /* x = 0x00 - 0x1f */ - -/* FX8010 Constants */ -#define C_00000000 0x40 -#define C_00000001 0x41 -#define C_00000002 0x42 -#define C_00000003 0x43 -#define C_00000004 0x44 -#define C_00000008 0x45 -#define C_00000010 0x46 -#define C_00000020 0x47 -#define C_00000100 0x48 -#define C_00010000 0x49 -#define C_00080000 0x4a -#define C_10000000 0x4b -#define C_20000000 0x4c -#define C_40000000 0x4d -#define C_80000000 0x4e -#define C_7fffffff 0x4f -#define C_ffffffff 0x50 -#define C_fffffffe 0x51 -#define C_c0000000 0x52 -#define C_4f1bbcdc 0x53 -#define C_5a7ef9db 0x54 -#define C_00100000 0x55 /* ?? */ - -/* FX8010 FX Send Bus Registers */ -#define FXBUS_PCM_LEFT 0x00 -#define FXBUS_PCM_RIGHT 0x01 -#define FXBUS_PCM_LEFT_REAR 0x02 -#define FXBUS_PCM_RIGHT_REAR 0x03 -#define FXBUS_MIDI_LEFT 0x04 -#define FXBUS_MIDI_RIGHT 0x05 -#define FXBUS_PCM_CENTER 0x06 -#define FXBUS_PCM_LFE 0x07 -#define FXBUS_MIDI_REVERB 0x0c -#define FXBUS_MIDI_CHORUS 0x0d - -/* FX8010 GPRs */ -#define GPR_ACCU 0x56 /* ACCUM, accumulator */ -#define GPR_COND 0x57 /* CCR, condition register */ -#define GPR_NOISE0 0x58 /* noise source */ -#define GPR_NOISE1 0x59 /* noise source */ -#define GPR_IRQ 0x5a /* IRQ register */ -#define GPR_DBAC 0x5b /* TRAM Delay Base Address Counter */ - -/* FX8010 Inputs */ -#define EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ -#define EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ -#define EXTIN_SPDIF_CD_L 0x02 /* internal S/PDIF CD - onboard - left */ -#define EXTIN_SPDIF_CD_R 0x03 /* internal S/PDIF CD - onboard - right */ -#define EXTIN_ZOOM_L 0x04 /* Zoom Video I2S - left */ -#define EXTIN_ZOOM_R 0x05 /* Zoom Video I2S - right */ -#define EXTIN_TOSLINK_L 0x06 /* LiveDrive - TOSLink Optical - left */ -#define EXTIN_TOSLINK_R 0x07 /* LiveDrive - TOSLink Optical - right */ -#define EXTIN_LINE1_L 0x08 /* LiveDrive - Line/Mic 1 - left */ -#define EXTIN_LINE1_R 0x09 /* LiveDrive - Line/Mic 1 - right */ -#define EXTIN_COAX_SPDIF_L 0x0a /* LiveDrive - Coaxial S/PDIF - left */ -#define EXTIN_COAX_SPDIF_R 0x0b /* LiveDrive - Coaxial S/PDIF - right */ -#define EXTIN_LINE2_L 0x0c /* LiveDrive - Line/Mic 2 - left */ -#define EXTIN_LINE2_R 0x0d /* LiveDrive - Line/Mic 2 - right */ - -/* FX8010 Outputs */ -#define EXTOUT_AC97_L 0x00 /* AC'97 playback channel - left */ -#define EXTOUT_AC97_R 0x01 /* AC'97 playback channel - right */ -#define EXTOUT_TOSLINK_L 0x02 /* LiveDrive - TOSLink Optical - left */ -#define EXTOUT_TOSLINK_R 0x03 /* LiveDrive - TOSLink Optical - right */ -#define EXTOUT_CENTER 0x04 /* SB Live 5.1 - center */ -#define EXTOUT_LFE 0x05 /* SB Live 5.1 - LFE */ -#define EXTOUT_HEADPHONE_L 0x06 /* LiveDrive - Headphone - left */ -#define EXTOUT_HEADPHONE_R 0x07 /* LiveDrive - Headphone - right */ -#define EXTOUT_REAR_L 0x08 /* Rear channel - left */ -#define EXTOUT_REAR_R 0x09 /* Rear channel - right */ -#define EXTOUT_ADC_CAP_L 0x0a /* ADC Capture buffer - left */ -#define EXTOUT_ADC_CAP_R 0x0b /* ADC Capture buffer - right */ -#define EXTOUT_MIC_CAP 0x0c /* MIC Capture buffer */ -#define EXTOUT_ACENTER 0x11 /* Analog Center */ -#define EXTOUT_ALFE 0x12 /* Analog LFE */ - -/* Audigy opcode macros */ -#define A_FXBUS(x) ((x) + 0x00) -#define A_EXTIN(x) ((x) + 0x40) -#define A_EXTOUT(x) ((x) + 0x60) -#define A_GPR(x) ((x) + A_FXGPREGBASE) - -/* Audigy constants */ -#define A_C_00000000 0xc0 -#define A_C_00000001 0xc1 -#define A_C_00000002 0xc2 -#define A_C_00000003 0xc3 -#define A_C_00000004 0xc4 -#define A_C_00000008 0xc5 -#define A_C_00000010 0xc6 -#define A_C_00000020 0xc7 -#define A_C_00000100 0xc8 -#define A_C_00010000 0xc9 -#define A_C_00000800 0xca -#define A_C_10000000 0xcb -#define A_C_20000000 0xcc -#define A_C_40000000 0xcd -#define A_C_80000000 0xce -#define A_C_7fffffff 0xcf -#define A_C_ffffffff 0xd0 -#define A_C_fffffffe 0xd1 -#define A_C_c0000000 0xd2 -#define A_C_4f1bbcdc 0xd3 -#define A_C_5a7ef9db 0xd4 -#define A_C_00100000 0xd5 - -/* Audigy FX Send Bus Registers */ -#define A_FXBUS_PCM_LEFT FXBUS_PCM_LEFT -#define A_FXBUS_PCM_RIGHT FXBUS_PCM_RIGHT -#define A_FXBUS_PCM_LEFT_REAR FXBUS_PCM_LEFT_REAR -#define A_FXBUS_PCM_RIGHT_REAR FXBUS_PCM_RIGHT_REAR -#define A_FXBUS_MIDI_LEFT FXBUS_MIDI_LEFT -#define A_FXBUS_MIDI_RIGHT FXBUS_MIDI_RIGHT -#define A_FXBUS_PCM_CENTER FXBUS_PCM_CENTER -#define A_FXBUS_PCM_LFE FXBUS_PCM_LFE -#define A_FXBUS_MIDI_REVERB FXBUS_MIDI_REVERB -#define A_FXBUS_MIDI_CHORUS FXBUS_MIDI_CHORUS - -#define A_GPR_ACCU 0xd6 /* 0xd6 = 0x7fffffff (?) ACCUM? */ -#define A_GPR_COND 0xd7 /* 0xd7 = 0x0000000 CCR */ -#define A_GPR_NOISE0 0xd8 /* 0xd8 = noise1 */ -#define A_GPR_NOISE1 0xd9 /* 0xd9 = noise2 */ -#define A_GPR_IRQ 0xda /* IRQ register */ -#define A_GPR_DBAC 0xdb /* TRAM Delay Base Address Counter */ - -/* Audigy Inputs */ -#define A_EXTIN_AC97_L 0x00 /* AC'97 capture channel - left */ -#define A_EXTIN_AC97_R 0x01 /* AC'97 capture channel - right */ -#define A_EXTIN_SPDIF_CD_L 0x02 /* digital CD left */ -#define A_EXTIN_SPDIF_CD_R 0x03 /* digital CD left */ -#define A_EXTIN_OPT_SPDIF_L 0x04 /* audigy drive Optical SPDIF - left */ -#define A_EXTIN_OPT_SPDIF_R 0x05 /* right */ -#define A_EXTIN_LINE2_L 0x08 /* audigy drive line2/mic2 - left */ -#define A_EXTIN_LINE2_R 0x09 /* right */ -#define A_EXTIN_RCA_SPDIF_L 0x0a /* audigy drive RCA SPDIF - left */ -#define A_EXTIN_RCA_SPDIF_R 0x0b /* right */ -#define A_EXTIN_AUX2_L 0x0c /* audigy drive aux2 - left */ -#define A_EXTIN_AUX2_R 0x0d /* - right */ - -/* Audigy Outputs */ -#define A_EXTOUT_FRONT_L 0x00 /* digital front left */ -#define A_EXTOUT_FRONT_R 0x01 /* right */ -#define A_EXTOUT_CENTER 0x02 /* digital front center */ -#define A_EXTOUT_LFE 0x03 /* digital front lfe */ -#define A_EXTOUT_HEADPHONE_L 0x04 /* headphone audigy drive left */ -#define A_EXTOUT_HEADPHONE_R 0x05 /* right */ -#define A_EXTOUT_REAR_L 0x06 /* digital rear left */ -#define A_EXTOUT_REAR_R 0x07 /* right */ -#define A_EXTOUT_AFRONT_L 0x08 /* analog front left */ -#define A_EXTOUT_AFRONT_R 0x09 /* right */ -#define A_EXTOUT_ACENTER 0x0a /* analog center */ -#define A_EXTOUT_ALFE 0x0b /* analog LFE */ -/* 0x0c ?? */ -/* 0x0d ?? */ -#define A_EXTOUT_AREAR_L 0x0e /* analog rear left */ -#define A_EXTOUT_AREAR_R 0x0f /* right */ -#define A_EXTOUT_AC97_L 0x10 /* AC97 left (front) */ -#define A_EXTOUT_AC97_R 0x11 /* right */ -#define A_EXTOUT_ADC_CAP_L 0x16 /* ADC capture buffer left */ -#define A_EXTOUT_ADC_CAP_R 0x17 /* right */ - - -#define ENABLE 0xffffffff -#define DISABLE 0x00000000 - -#define ENV_ON 0x80 -#define ENV_OFF 0x00 - -#endif /* EMU10K1_H */ + +#endif /* _8010_H */ diff --git a/sys/dev/sound/pci/gnu/maestro3_dsp.h b/sys/dev/sound/pci/gnu/maestro3_dsp.h index a60c9fe831..5cd3863c12 100644 --- a/sys/dev/sound/pci/gnu/maestro3_dsp.h +++ b/sys/dev/sound/pci/gnu/maestro3_dsp.h @@ -1,6 +1,6 @@ -/* $FreeBSD: src/sys/gnu/dev/sound/pci/maestro3_dsp.h,v 1.1.2.2 2001/08/01 03:41:09 cg Exp $ */ -/* $DragonFly: src/sys/dev/sound/pci/gnu/maestro3_dsp.h,v 1.2 2003/06/17 04:28:33 dillon Exp $ */ -/* +/* $FreeBSD: src/sys/gnu/dev/sound/pci/maestro3_dsp.h,v 1.5 2005/01/06 18:27:30 imp Exp $ */ +/* $DragonFly: src/sys/dev/sound/pci/gnu/maestro3_dsp.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ +/*- * ESS Technology allegro audio driver. * * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.com) diff --git a/sys/dev/sound/pci/gnu/maestro3_reg.h b/sys/dev/sound/pci/gnu/maestro3_reg.h index a0cb667396..de344107b8 100644 --- a/sys/dev/sound/pci/gnu/maestro3_reg.h +++ b/sys/dev/sound/pci/gnu/maestro3_reg.h @@ -1,6 +1,6 @@ -/* $FreeBSD: src/sys/gnu/dev/sound/pci/maestro3_reg.h,v 1.1.2.2 2001/08/01 03:41:09 cg Exp $ */ -/* $DragonFly: src/sys/dev/sound/pci/gnu/maestro3_reg.h,v 1.2 2003/06/17 04:28:33 dillon Exp $ */ -/* +/* $FreeBSD: src/sys/gnu/dev/sound/pci/maestro3_reg.h,v 1.5 2005/01/06 18:27:30 imp Exp $ */ +/* $DragonFly: src/sys/dev/sound/pci/gnu/maestro3_reg.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ +/*- * ESS Technology allegro audio driver. * * Copyright (C) 1992-2000 Don Kim (don.kim@esstech.com) diff --git a/sys/dev/sound/pci/hda/hda_reg.h b/sys/dev/sound/pci/hda/hda_reg.h new file mode 100644 index 0000000000..04bc7ced31 --- /dev/null +++ b/sys/dev/sound/pci/hda/hda_reg.h @@ -0,0 +1,1227 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * 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: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hda_reg.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ + */ + +#ifndef _HDA_REG_H_ +#define _HDA_REG_H_ + +/**************************************************************************** + * HDA Device Verbs + ****************************************************************************/ + +/* HDA Command */ +#define HDA_CMD_VERB_MASK 0x000fffff +#define HDA_CMD_VERB_SHIFT 0 +#define HDA_CMD_NID_MASK 0x0ff00000 +#define HDA_CMD_NID_SHIFT 20 +#define HDA_CMD_CAD_MASK 0xf0000000 +#define HDA_CMD_CAD_SHIFT 28 + +#define HDA_CMD_VERB_4BIT_SHIFT 16 +#define HDA_CMD_VERB_12BIT_SHIFT 8 + +#define HDA_CMD_VERB_4BIT(verb, payload) \ + (((verb) << HDA_CMD_VERB_4BIT_SHIFT) | (payload)) +#define HDA_CMD_4BIT(cad, nid, verb, payload) \ + (((cad) << HDA_CMD_CAD_SHIFT) | \ + ((nid) << HDA_CMD_NID_SHIFT) | \ + (HDA_CMD_VERB_4BIT((verb), (payload)))) + +#define HDA_CMD_VERB_12BIT(verb, payload) \ + (((verb) << HDA_CMD_VERB_12BIT_SHIFT) | (payload)) +#define HDA_CMD_12BIT(cad, nid, verb, payload) \ + (((cad) << HDA_CMD_CAD_SHIFT) | \ + ((nid) << HDA_CMD_NID_SHIFT) | \ + (HDA_CMD_VERB_12BIT((verb), (payload)))) + +/* Get Parameter */ +#define HDA_CMD_VERB_GET_PARAMETER 0xf00 + +#define HDA_CMD_GET_PARAMETER(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PARAMETER, (payload))) + +/* Connection Select Control */ +#define HDA_CMD_VERB_GET_CONN_SELECT_CONTROL 0xf01 +#define HDA_CMD_VERB_SET_CONN_SELECT_CONTROL 0x701 + +#define HDA_CMD_GET_CONN_SELECT_CONTROL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONN_SELECT_CONTROL, 0x0)) +#define HDA_CMD_SET_CONNECTION_SELECT_CONTROL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONN_SELECT_CONTROL, (payload))) + +/* Connection List Entry */ +#define HDA_CMD_VERB_GET_CONN_LIST_ENTRY 0xf02 + +#define HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONN_LIST_ENTRY, (payload))) + +#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_SHORT 1 +#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_LONG 2 + +/* Processing State */ +#define HDA_CMD_VERB_GET_PROCESSING_STATE 0xf03 +#define HDA_CMD_VERB_SET_PROCESSING_STATE 0x703 + +#define HDA_CMD_GET_PROCESSING_STATE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PROCESSING_STATE, 0x0)) +#define HDA_CMD_SET_PROCESSING_STATE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PROCESSING_STATE, (payload))) + +#define HDA_CMD_GET_PROCESSING_STATE_STATE_OFF 0x00 +#define HDA_CMD_GET_PROCESSING_STATE_STATE_ON 0x01 +#define HDA_CMD_GET_PROCESSING_STATE_STATE_BENIGN 0x02 + +/* Coefficient Index */ +#define HDA_CMD_VERB_GET_COEFF_INDEX 0xd +#define HDA_CMD_VERB_SET_COEFF_INDEX 0x5 + +#define HDA_CMD_GET_COEFF_INDEX(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_COEFF_INDEX, 0x0)) +#define HDA_CMD_SET_COEFF_INDEX(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_COEFF_INDEX, (payload))) + +/* Processing Coefficient */ +#define HDA_CMD_VERB_GET_PROCESSING_COEFF 0xc +#define HDA_CMD_VERB_SET_PROCESSING_COEFF 0x4 + +#define HDA_CMD_GET_PROCESSING_COEFF(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PROCESSING_COEFF, 0x0)) +#define HDA_CMD_SET_PROCESSING_COEFF(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PROCESSING_COEFF, (payload))) + +/* Amplifier Gain/Mute */ +#define HDA_CMD_VERB_GET_AMP_GAIN_MUTE 0xb +#define HDA_CMD_VERB_SET_AMP_GAIN_MUTE 0x3 + +#define HDA_CMD_GET_AMP_GAIN_MUTE(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_AMP_GAIN_MUTE, (payload))) +#define HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_AMP_GAIN_MUTE, (payload))) + +#define HDA_CMD_GET_AMP_GAIN_MUTE_INPUT 0x0000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_OUTPUT 0x8000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_RIGHT 0x0000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_LEFT 0x2000 + +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK 0x00000008 +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT 7 +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK 0x00000007 +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT 0 + +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE(rsp) \ + (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK) >> \ + HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT) +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN(rsp) \ + (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK) >> \ + HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT) + +#define HDA_CMD_SET_AMP_GAIN_MUTE_OUTPUT 0x8000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INPUT 0x4000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_LEFT 0x2000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT 0x1000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK 0x0f00 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT 8 +#define HDA_CMD_SET_AMP_GAIN_MUTE_MUTE 0x0080 +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK 0x0007 +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT 0 + +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX(index) \ + (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT) & \ + HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK) +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN(index) \ + (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT) & \ + HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK) + +/* Converter format */ +#define HDA_CMD_VERB_GET_CONV_FMT 0xa +#define HDA_CMD_VERB_SET_CONV_FMT 0x2 + +#define HDA_CMD_GET_CONV_FMT(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_FMT, 0x0)) +#define HDA_CMD_SET_CONV_FMT(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_FMT, (payload))) + +/* Digital Converter Control */ +#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT 0xf0d +#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1 0x70d +#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2 0x70e + +#define HDA_CMD_GET_DIGITAL_CONV_FMT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_DIGITAL_CONV_FMTT, 0x0)) +#define HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1, (payload))) +#define HDA_CMD_SET_DIGITAL_CONV_FMT2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2, (payload))) + +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK 0x7f00 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT 8 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK 0x0080 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT 7 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK 0x0040 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT 6 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK 0x0020 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT 5 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK 0x0010 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT 4 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK 0x0008 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT 3 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK 0x0004 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT 2 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK 0x0002 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT 1 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK 0x0001 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT 0 + +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT) + +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_L 0x80 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRO 0x40 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO 0x20 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_COPY 0x10 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRE 0x08 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_VCFG 0x04 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_V 0x02 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN 0x01 + +/* Power State */ +#define HDA_CMD_VERB_GET_POWER_STATE 0xf05 +#define HDA_CMD_VERB_SET_POWER_STATE 0x705 + +#define HDA_CMD_GET_POWER_STATE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_POWER_STATE, 0x0)) +#define HDA_CMD_SET_POWER_STATE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_POWER_STATE, (payload))) + +#define HDA_CMD_POWER_STATE_D0 0x00 +#define HDA_CMD_POWER_STATE_D1 0x01 +#define HDA_CMD_POWER_STATE_D2 0x02 +#define HDA_CMD_POWER_STATE_D3 0x03 + +#define HDA_CMD_POWER_STATE_ACT_MASK 0x000000f0 +#define HDA_CMD_POWER_STATE_ACT_SHIFT 4 +#define HDA_CMD_POWER_STATE_SET_MASK 0x0000000f +#define HDA_CMD_POWER_STATE_SET_SHIFT 0 + +#define HDA_CMD_GET_POWER_STATE_ACT(rsp) \ + (((rsp) & HDA_CMD_POWER_STATE_ACT_MASK) >> \ + HDA_CMD_POWER_STATE_ACT_SHIFT) +#define HDA_CMD_GET_POWER_STATE_SET(rsp) \ + (((rsp) & HDA_CMD_POWER_STATE_SET_MASK) >> \ + HDA_CMD_POWER_STATE_SET_SHIFT) + +#define HDA_CMD_SET_POWER_STATE_ACT(ps) \ + (((ps) << HDA_CMD_POWER_STATE_ACT_SHIFT) & \ + HDA_CMD_POWER_STATE_ACT_MASK) +#define HDA_CMD_SET_POWER_STATE_SET(ps) \ + (((ps) << HDA_CMD_POWER_STATE_SET_SHIFT) & \ + HDA_CMD_POWER_STATE_ACT_MASK) + +/* Converter Stream, Channel */ +#define HDA_CMD_VERB_GET_CONV_STREAM_CHAN 0xf06 +#define HDA_CMD_VERB_SET_CONV_STREAM_CHAN 0x706 + +#define HDA_CMD_GET_CONV_STREAM_CHAN(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_STREAM_CHAN, 0x0)) +#define HDA_CMD_SET_CONV_STREAM_CHAN(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_STREAM_CHAN, (payload))) + +#define HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK 0x000000f0 +#define HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT 4 +#define HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK 0x0000000f +#define HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT 0 + +#define HDA_CMD_GET_CONV_STREAM_CHAN_STREAM(rsp) \ + (((rsp) & HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) >> \ + HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) +#define HDA_CMD_GET_CONV_STREAM_CHAN_CHAN(rsp) \ + (((rsp) & HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) >> \ + HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) + +#define HDA_CMD_SET_CONV_STREAM_CHAN_STREAM(param) \ + (((param) << HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) & \ + HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) +#define HDA_CMD_SET_CONV_STREAM_CHAN_CHAN(param) \ + (((param) << HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) & \ + HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) + +/* Input Converter SDI Select */ +#define HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT 0xf04 +#define HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT 0x704 + +#define HDA_CMD_GET_INPUT_CONVERTER_SDI_SELECT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT, 0x0)) +#define HDA_CMD_SET_INPUT_CONVERTER_SDI_SELECT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT, (payload))) + +/* Pin Widget Control */ +#define HDA_CMD_VERB_GET_PIN_WIDGET_CTRL 0xf07 +#define HDA_CMD_VERB_SET_PIN_WIDGET_CTRL 0x707 + +#define HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PIN_WIDGET_CTRL, 0x0)) +#define HDA_CMD_SET_PIN_WIDGET_CTRL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PIN_WIDGET_CTRL, (payload))) + +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK 0x00000080 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT 7 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK 0x00000040 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT 6 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK 0x00000020 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT 5 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x00000007 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0 + +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK) >> \ + HDA_GET_CMD_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) + +#define HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE 0x80 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE 0x40 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE 0x20 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x07 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0 + +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(param) \ + (((param) << HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) & \ + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) + +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_HIZ 0 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50 1 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_GROUND 2 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80 4 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100 5 + +/* Unsolicited Response */ +#define HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE 0xf08 +#define HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE 0x708 + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE, 0x0)) +#define HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE, (payload))) + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK 0x00000080 +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT 7 +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK 0x0000001f +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK) >> \ + HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT) +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG(rsp) \ + (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK) >> \ + HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT) + +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE 0x80 +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK 0x1f +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 + +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG(param) \ + (((param) << HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT) & \ + HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK) + +/* Pin Sense */ +#define HDA_CMD_VERB_GET_PIN_SENSE 0xf09 +#define HDA_CMD_VERB_SET_PIN_SENSE 0x709 + +#define HDA_CMD_GET_PIN_SENSE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PIN_SENSE, 0x0)) +#define HDA_CMD_SET_PIN_SENSE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PIN_SENSE, (payload))) + +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_MASK 0x80000000 +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_SHIFT 31 +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK 0x7fffffff +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT 0 + +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_MASK) >> \ + HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT_SHIFT) +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK) >> \ + HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT) + +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_INVALID 0x7fffffff + +#define HDA_CMD_SET_PIN_SENSE_LEFT_CHANNEL 0x00 +#define HDA_CMD_SET_PIN_SENSE_RIGHT_CHANNEL 0x01 + +/* EAPD/BTL Enable */ +#define HDA_CMD_VERB_GET_EAPD_BTL_ENABLE 0xf0c +#define HDA_CMD_VERB_SET_EAPD_BTL_ENABLE 0x70c + +#define HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_EAPD_BTL_ENABLE, 0x0)) +#define HDA_CMD_SET_EAPD_BTL_ENABLE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_EAPD_BTL_ENABLE, (payload))) + +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK 0x00000004 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT 2 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK 0x00000002 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT 1 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK 0x00000001 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT 0 + +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT) +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT) +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT) + +#define HDA_CMD_SET_EAPD_BTL_ENABLE_LR_SWAP 0x04 +#define HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD 0x02 +#define HDA_CMD_SET_EAPD_BTL_ENABLE_BTL 0x01 + +/* GPI Data */ +#define HDA_CMD_VERB_GET_GPI_DATA 0xf10 +#define HDA_CMD_VERB_SET_GPI_DATA 0x710 + +#define HDA_CMD_GET_GPI_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_DATA, 0x0)) +#define HDA_CMD_SET_GPI_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_DATA, (payload))) + +/* GPI Wake Enable Mask */ +#define HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK 0xf11 +#define HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK 0x711 + +#define HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPI_WAKE_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK, (payload))) + +/* GPI Unsolicited Enable Mask */ +#define HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK 0xf12 +#define HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK 0x712 + +#define HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK, (payload))) + +/* GPI Sticky Mask */ +#define HDA_CMD_VERB_GET_GPI_STICKY_MASK 0xf13 +#define HDA_CMD_VERB_SET_GPI_STICKY_MASK 0x713 + +#define HDA_CMD_GET_GPI_STICKY_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_STICKY_MASK, 0x0)) +#define HDA_CMD_SET_GPI_STICKY_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_STICKY_MASK, (payload))) + +/* GPO Data */ +#define HDA_CMD_VERB_GET_GPO_DATA 0xf14 +#define HDA_CMD_VERB_SET_GPO_DATA 0x714 + +#define HDA_CMD_GET_GPO_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPO_DATA, 0x0)) +#define HDA_CMD_SET_GPO_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPO_DATA, (payload))) + +/* GPIO Data */ +#define HDA_CMD_VERB_GET_GPIO_DATA 0xf15 +#define HDA_CMD_VERB_SET_GPIO_DATA 0x715 + +#define HDA_CMD_GET_GPIO_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_DATA, 0x0)) +#define HDA_CMD_SET_GPIO_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_DATA, (payload))) + +/* GPIO Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_ENABLE_MASK 0xf16 +#define HDA_CMD_VERB_SET_GPIO_ENABLE_MASK 0x716 + +#define HDA_CMD_GET_GPIO_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_ENABLE_MASK, (payload))) + +/* GPIO Direction */ +#define HDA_CMD_VERB_GET_GPIO_DIRECTION 0xf17 +#define HDA_CMD_VERB_SET_GPIO_DIRECTION 0x717 + +#define HDA_CMD_GET_GPIO_DIRECTION(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_DIRECTION, 0x0)) +#define HDA_CMD_SET_GPIO_DIRECTION(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_DIRECTION, (payload))) + +/* GPIO Wake Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK 0xf18 +#define HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK 0x718 + +#define HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_WAKE_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK, (payload))) + +/* GPIO Unsolicited Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK 0xf19 +#define HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK 0x719 + +#define HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK, (payload))) + +/* GPIO_STICKY_MASK */ +#define HDA_CMD_VERB_GET_GPIO_STICKY_MASK 0xf1a +#define HDA_CMD_VERB_SET_GPIO_STICKY_MASK 0x71a + +#define HDA_CMD_GET_GPIO_STICKY_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_STICKY_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_STICKY_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_STICKY_MASK, (payload))) + +/* Beep Generation */ +#define HDA_CMD_VERB_GET_BEEP_GENERATION 0xf0a +#define HDA_CMD_VERB_SET_BEEP_GENERATION 0x70a + +#define HDA_CMD_GET_BEEP_GENERATION(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_BEEP_GENERATION, 0x0)) +#define HDA_CMD_SET_BEEP_GENERATION(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_BEEP_GENERATION, (payload))) + +/* Volume Knob */ +#define HDA_CMD_VERB_GET_VOLUME_KNOB 0xf0f +#define HDA_CMD_VERB_SET_VOLUME_KNOB 0x70f + +#define HDA_CMD_GET_VOLUME_KNOB(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_VOLUME_KNOB, 0x0)) +#define HDA_CMD_SET_VOLUME_KNOB(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_VOLUME_KNOB, (payload))) + +/* Subsystem ID */ +#define HDA_CMD_VERB_GET_SUBSYSTEM_ID 0xf20 +#define HDA_CMD_VERB_SET_SUSBYSTEM_ID1 0x720 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID2 0x721 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID3 0x722 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID4 0x723 + +#define HDA_CMD_GET_SUBSYSTEM_ID(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_SUBSYSTEM_ID, 0x0)) +#define HDA_CMD_SET_SUBSYSTEM_ID1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID1, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID2, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID3(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID3, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID4(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID4, (payload))) + +/* Configuration Default */ +#define HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT 0xf1c +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1 0x71c +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2 0x71d +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3 0x71e +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4 0x71f + +#define HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT, 0x0)) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT3(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT4(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4, (payload))) + +/* Stripe Control */ +#define HDA_CMD_VERB_GET_STRIPE_CONTROL 0xf24 +#define HDA_CMD_VERB_SET_STRIPE_CONTROL 0x724 + +#define HDA_CMD_SET_STRIPE_CONTROL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_STRIPE_CONTROL, 0x0)) +#define HDA_CMD_GET_STRIPE_CONTROL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_STRIPE_CONTROL, (payload))) + +/* Function Reset */ +#define HDA_CMD_VERB_FUNCTION_RESET 0x7ff + +#define HDA_CMD_FUNCTION_RESET(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_FUNCTION_RESET, 0x0)) + + +/**************************************************************************** + * HDA Device Parameters + ****************************************************************************/ + +/* Vendor ID */ +#define HDA_PARAM_VENDOR_ID 0x00 + +#define HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK 0xffff0000 +#define HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT 16 +#define HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK 0x0000ffff +#define HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT 0 + +#define HDA_PARAM_VENDOR_ID_VENDOR_ID(param) \ + (((param) & HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK) >> \ + HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT) +#define HDA_PARAM_VENDOR_ID_DEVICE_ID(param) \ + (((param) & HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK) >> \ + HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT) + +/* Revision ID */ +#define HDA_PARAM_REVISION_ID 0x02 + +#define HDA_PARAM_REVISION_ID_MAJREV_MASK 0x00f00000 +#define HDA_PARAM_REVISION_ID_MAJREV_SHIFT 20 +#define HDA_PARAM_REVISION_ID_MINREV_MASK 0x000f0000 +#define HDA_PARAM_REVISION_ID_MINREV_SHIFT 16 +#define HDA_PARAM_REVISION_ID_REVISION_ID_MASK 0x0000ff00 +#define HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT 8 +#define HDA_PARAM_REVISION_ID_STEPPING_ID_MASK 0x000000ff +#define HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT 0 + +#define HDA_PARAM_REVISION_ID_MAJREV(param) \ + (((param) & HDA_PARAM_REVISION_ID_MAJREV_MASK) >> \ + HDA_PARAM_REVISION_ID_MAJREV_SHIFT) +#define HDA_PARAM_REVISION_ID_MINREV(param) \ + (((param) & HDA_PARAM_REVISION_ID_MINREV_MASK) >> \ + HDA_PARAM_REVISION_ID_MINREV_SHIFT) +#define HDA_PARAM_REVISION_ID_REVISION_ID(param) \ + (((param) & HDA_PARAM_REVISION_ID_REVISION_ID_MASK) >> \ + HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT) +#define HDA_PARAM_REVISION_ID_STEPPING_ID(param) \ + (((param) & HDA_PARAM_REVISION_ID_STEPPING_ID_MASK) >> \ + HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT) + +/* Subordinate Node Cound */ +#define HDA_PARAM_SUB_NODE_COUNT 0x04 + +#define HDA_PARAM_SUB_NODE_COUNT_START_MASK 0x00ff0000 +#define HDA_PARAM_SUB_NODE_COUNT_START_SHIFT 16 +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK 0x000000ff +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT 0 + +#define HDA_PARAM_SUB_NODE_COUNT_START(param) \ + (((param) & HDA_PARAM_SUB_NODE_COUNT_START_MASK) >> \ + HDA_PARAM_SUB_NODE_COUNT_START_SHIFT) +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL(param) \ + (((param) & HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK) >> \ + HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT) + +/* Function Group Type */ +#define HDA_PARAM_FCT_GRP_TYPE 0x05 + +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK 0x00000100 +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_SHIFT 8 +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK 0x000000ff +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT 0 + +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL(param) \ + (((param) & HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK) >> \ + HDA_PARAM_FCT_GROUP_TYPE_UNSOL_SHIFT) +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(param) \ + (((param) & HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK) >> \ + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT) + +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO 0x01 +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM 0x02 + +/* Audio Function Group Capabilities */ +#define HDA_PARAM_AUDIO_FCT_GRP_CAP 0x08 + +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK 0x00010000 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT 16 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK 0x00000f00 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT 8 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK 0x0000000f +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT 0 + +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT) +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT) +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT) + +/* Audio Widget Capabilities */ +#define HDA_PARAM_AUDIO_WIDGET_CAP 0x09 + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK 0x00f00000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT 20 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK 0x000f0000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT 16 +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK 0x00000800 +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT 11 +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK 0x00000400 +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT 10 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK 0x00000200 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT 9 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK 0x00000100 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT 8 +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK 0x00000080 +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT 7 +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK 0x00000040 +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT 6 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK 0x00000020 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT 5 +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK 0x00000010 +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT 4 +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK 0x00000008 +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT 3 +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK 0x00000004 +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT 2 +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK 0x00000002 +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT 1 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK 0x00000001 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT 0 + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT) + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT 0x0 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT 0x1 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER 0x2 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR 0x3 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX 0x4 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET 0x5 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET 0x6 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET 0x7 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET 0xf + +/* Supported PCM Size, Rates */ + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE 0x0a + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK 0x00100000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT 20 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK 0x00080000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT 19 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK 0x00040000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT 18 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK 0x00020000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT 17 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK 0x00010000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT 16 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000800 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 11 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000400 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 10 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000200 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 9 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000100 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 8 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000080 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 7 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000040 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 6 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000020 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 5 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000010 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 4 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000008 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 3 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x000000004 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 2 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x000000002 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 1 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x000000001 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 0 + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT) + +/* Supported Stream Formats */ +#define HDA_PARAM_SUPP_STREAM_FORMATS 0x0b + +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK 0x00000004 +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT 2 +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK 0x00000002 +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT 1 +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK 0x00000001 +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT 0 + +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT) +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT) +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT) + +/* Pin Capabilities */ +#define HDA_PARAM_PIN_CAP 0x0c + +#define HDA_PARAM_PIN_CAP_EAPD_CAP_MASK 0x00010000 +#define HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT 16 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_MASK 0x0000ff00 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT 8 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK 0x00002000 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT 13 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK 0x00001000 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT 12 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK 0x00000400 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT 10 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK 0x00000200 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT 9 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK 0x00000100 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT 8 +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK 0x00000040 +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT 6 +#define HDA_PARAM_PIN_CAP_INPUT_CAP_MASK 0x00000020 +#define HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT 5 +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK 0x00000010 +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT 4 +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK 0x00000008 +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT 3 +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK 0x00000004 +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT 2 +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK 0x00000002 +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT 1 +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK 0x00000001 +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT 0 + +#define HDA_PARAM_PIN_CAP_EAPD_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_EAPD_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT) +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(param) \ + (((param) & HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK) >> \ + HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT) +#define HDA_PARAM_PIN_CAP_INPUT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_INPUT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD(param) \ + (((param) & HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK) >> \ + HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT) +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT) + +/* Input Amplifier Capabilities */ +#define HDA_PARAM_INPUT_AMP_CAP 0x0d + +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000 +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT 31 +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000 +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT 16 +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00 +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT 8 +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK 0x0000007f +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT 0 + +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT) + +/* Output Amplifier Capabilities */ +#define HDA_PARAM_OUTPUT_AMP_CAP 0x12 + +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000 +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT 31 +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000 +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT 16 +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00 +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT 8 +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK 0x0000007f +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT 0 + +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT) + +/* Connection List Length */ +#define HDA_PARAM_CONN_LIST_LENGTH 0x0e + +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK 0x00000080 +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT 7 +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK 0x0000007f +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT 0 + +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(param) \ + (((param) & HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK) >> \ + HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT) +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(param) \ + (((param) & HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK) >> \ + HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT) + +/* Supported Power States */ +#define HDA_PARAM_SUPP_POWER_STATES 0x0f + +#define HDA_PARAM_SUPP_POWER_STATES_D3_MASK 0x00000008 +#define HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT 3 +#define HDA_PARAM_SUPP_POWER_STATES_D2_MASK 0x00000004 +#define HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT 2 +#define HDA_PARAM_SUPP_POWER_STATES_D1_MASK 0x00000002 +#define HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT 1 +#define HDA_PARAM_SUPP_POWER_STATES_D0_MASK 0x00000001 +#define HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT 0 + +#define HDA_PARAM_SUPP_POWER_STATES_D3(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D3_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D2(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D2_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D1(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D1_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D0(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D0_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT) + +/* Processing Capabilities */ +#define HDA_PARAM_PROCESSING_CAP 0x10 + +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK 0x0000ff00 +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT 8 +#define HDA_PARAM_PROCESSING_CAP_BENIGN_MASK 0x00000001 +#define HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT 0 + +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF(param) \ + (((param) & HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK) >> \ + HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT) +#define HDA_PARAM_PROCESSING_CAP_BENIGN(param) \ + (((param) & HDA_PARAM_PROCESSING_CAP_BENIGN_MASK) >> \ + HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT) + +/* GPIO Count */ +#define HDA_PARAM_GPIO_COUNT 0x11 + +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK 0x80000000 +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT 31 +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK 0x40000000 +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT 30 +#define HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK 0x00ff0000 +#define HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT 16 +#define HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK 0x0000ff00 +#define HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT 8 +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK 0x000000ff +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT 0 + +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK) >> \ + HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT) +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK) >> \ + HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPI(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPO(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT) + +/* Volume Knob Capabilities */ +#define HDA_PARAM_VOLUME_KNOB_CAP 0x13 + +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK 0x00000080 +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT 7 +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK 0x0000007f +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT 0 + +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA(param) \ + (((param) & HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK) >> \ + HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT) +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS(param) \ + (((param) & HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK) >> \ + HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT) + + +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK 0x00000000f +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK 0x0000000f0 +#define HDA_CONFIG_DEFAULTCONF_MISC_MASK 0x000000f00 +#define HDA_CONFIG_DEFAULTCONF_COLOR_MASK 0x00000f000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK 0x000f00000 +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MASK 0x000f00000 +#define HDA_CONFIG_DEFAULTCONF_LOCATION_MASK 0x03f000000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK 0x0c0000000 + +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK (0<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE (1<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED (2<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH (3<<30) + +#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT (0<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER (1<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT (2<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_CD (3<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT (4<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT (5<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE (6<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET (7<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN (8<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_AUX (9<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN (10<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY (11<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN (12<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN (13<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER (15<<20) + +#endif diff --git a/sys/dev/sound/pci/hda/hdac.c b/sys/dev/sound/pci/hda/hdac.c new file mode 100644 index 0000000000..786660cb7d --- /dev/null +++ b/sys/dev/sound/pci/hda/hdac.c @@ -0,0 +1,4883 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * Copyright (c) 2006 Ariff Abdullah + * 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: src/sys/dev/sound/pci/hda/hdac.c,v 1.6 2006/10/12 04:19:37 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.1 2007/01/04 21:47:03 corecode Exp $ + */ + +/* + * Intel High Definition Audio (Controller) driver for FreeBSD. Be advised + * that this driver still in its early stage, and possible of rewrite are + * pretty much guaranteed. There are supposedly several distinct parent/child + * busses to make this "perfect", but as for now and for the sake of + * simplicity, everything is gobble up within single source. + * + * List of subsys: + * 1) HDA Controller support + * 2) HDA Codecs support, which may include + * - HDA + * - Modem + * - HDMI + * 3) Widget parser - the real magic of why this driver works on so + * many hardwares with minimal vendor specific quirk. The original + * parser was written using Ruby and can be found at + * http://people.freebsd.org/~ariff/HDA/parser.rb . This crude + * ruby parser take the verbose dmesg dump as its input. Refer to + * http://www.microsoft.com/whdc/device/audio/default.mspx for various + * interesting documents, especiall UAA (Universal Audio Architecture). + * 4) Possible vendor specific support. + * (snd_hda_intel, snd_hda_ati, etc..) + * + * Thanks to Ahmad Ubaidah Omar @ Defenxis Sdn. Bhd. for the + * Compaq V3000 with Conexant HDA. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * * + * * This driver is a collaborative effort made by: * + * * * + * * Stephane E. Potvin * + * * Andrea Bittau * + * * Wesley Morgan * + * * Daniel Eischen * + * * Maxime Guillaud * + * * Ariff Abdullah * + * * * + * * ....and various people from freebsd-multimedia@FreeBSD.org * + * * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "mixer_if.h" + +#define HDA_DRV_TEST_REV "20061009_0031" +#define HDA_WIDGET_PARSER_REV 1 + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/hda/hdac.c,v 1.1 2007/01/04 21:47:03 corecode Exp $"); + +#undef HDA_DEBUG_ENABLED +#define HDA_DEBUG_ENABLED 1 + +#ifdef HDA_DEBUG_ENABLED +#define HDA_DEBUG(stmt) do { \ + stmt \ +} while(0) +#else +#define HDA_DEBUG(stmt) +#endif + +#define HDA_BOOTVERBOSE(stmt) do { \ + if (bootverbose) { \ + stmt \ + } \ +} while(0) + +#if 1 +#undef HDAC_INTR_EXTRA +#define HDAC_INTR_EXTRA 1 +#endif + +#define hdac_lock(sc) snd_mtxlock((sc)->lock) +#define hdac_unlock(sc) snd_mtxunlock((sc)->lock) +#define hdac_lockassert(sc) snd_mtxassert((sc)->lock) +#define hdac_lockowned(sc) (1)/*mtx_owned((sc)->lock)*/ + +#define HDA_FLAG_MATCH(fl, v) (((fl) & (v)) == (v)) +#define HDA_DEV_MATCH(fl, v) ((fl) == (v) || \ + (fl) == 0xffffffff || \ + (((fl) & 0xffff0000) == 0xffff0000 && \ + ((fl) & 0x0000ffff) == ((v) & 0x0000ffff)) || \ + (((fl) & 0x0000ffff) == 0x0000ffff && \ + ((fl) & 0xffff0000) == ((v) & 0xffff0000))) +#define HDA_MATCH_ALL 0xffffffff +#define HDAC_INVALID 0xffffffff + +#define HDA_MODEL_CONSTRUCT(vendor, model) \ + (((uint32_t)(model) << 16) | ((vendor##_VENDORID) & 0xffff)) + +/* Controller models */ + +/* Intel */ +#define INTEL_VENDORID 0x8086 +#define HDA_INTEL_82801F HDA_MODEL_CONSTRUCT(INTEL, 0x2668) +#define HDA_INTEL_82801G HDA_MODEL_CONSTRUCT(INTEL, 0x27d8) +#define HDA_INTEL_82801H HDA_MODEL_CONSTRUCT(INTEL, 0x284b) +#define HDA_INTEL_63XXESB HDA_MODEL_CONSTRUCT(INTEL, 0x269a) +#define HDA_INTEL_ALL HDA_MODEL_CONSTRUCT(INTEL, 0xffff) + +/* Nvidia */ +#define NVIDIA_VENDORID 0x10de +#define HDA_NVIDIA_MCP51 HDA_MODEL_CONSTRUCT(NVIDIA, 0x026c) +#define HDA_NVIDIA_MCP55 HDA_MODEL_CONSTRUCT(NVIDIA, 0x0371) +#define HDA_NVIDIA_MCP61A HDA_MODEL_CONSTRUCT(NVIDIA, 0x03e4) +#define HDA_NVIDIA_MCP61B HDA_MODEL_CONSTRUCT(NVIDIA, 0x03f0) +#define HDA_NVIDIA_MCP65A HDA_MODEL_CONSTRUCT(NVIDIA, 0x044a) +#define HDA_NVIDIA_MCP65B HDA_MODEL_CONSTRUCT(NVIDIA, 0x044b) +#define HDA_NVIDIA_ALL HDA_MODEL_CONSTRUCT(NVIDIA, 0xffff) + +/* ATI */ +#define ATI_VENDORID 0x1002 +#define HDA_ATI_SB450 HDA_MODEL_CONSTRUCT(ATI, 0x437b) +#define HDA_ATI_SB600 HDA_MODEL_CONSTRUCT(ATI, 0x4383) +#define HDA_ATI_ALL HDA_MODEL_CONSTRUCT(ATI, 0xffff) + +/* VIA */ +#define VIA_VENDORID 0x1106 +#define HDA_VIA_VT82XX HDA_MODEL_CONSTRUCT(VIA, 0x3288) +#define HDA_VIA_ALL HDA_MODEL_CONSTRUCT(VIA, 0xffff) + +/* SiS */ +#define SIS_VENDORID 0x1039 +#define HDA_SIS_966 HDA_MODEL_CONSTRUCT(SIS, 0x7502) +#define HDA_SIS_ALL HDA_MODEL_CONSTRUCT(SIS, 0xffff) + +/* OEM/subvendors */ + +/* HP/Compaq */ +#define HP_VENDORID 0x103c +#define HP_V3000_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30b5) +#define HP_NX7400_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30a2) +#define HP_NX6310_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0x30aa) +#define HP_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(HP, 0xffff) + +/* Dell */ +#define DELL_VENDORID 0x1028 +#define DELL_D820_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01cc) +#define DELL_I1300_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0x01c9) +#define DELL_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(DELL, 0xffff) + +/* Clevo */ +#define CLEVO_VENDORID 0x1558 +#define CLEVO_D900T_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0x0900) +#define CLEVO_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(CLEVO, 0xffff) + +/* Acer */ +#define ACER_VENDORID 0x1025 +#define ACER_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ACER, 0xffff) + +/* Asus */ +#define ASUS_VENDORID 0x1043 +#define ASUS_M5200_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1993) +#define ASUS_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0xffff) + +/* IBM / Lenovo */ +#define IBM_VENDORID 0x1014 +#define IBM_M52_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0x02f6) +#define IBM_ALL_SUBVENDOR HDA_MODEL_CONSTRUCT(IBM, 0xffff) + + +/* Misc constants.. */ +#define HDA_AMP_MUTE_DEFAULT (0xffffffff) +#define HDA_AMP_MUTE_NONE (0) +#define HDA_AMP_MUTE_LEFT (1 << 0) +#define HDA_AMP_MUTE_RIGHT (1 << 1) +#define HDA_AMP_MUTE_ALL (HDA_AMP_MUTE_LEFT | HDA_AMP_MUTE_RIGHT) + +#define HDA_AMP_LEFT_MUTED(v) ((v) & (HDA_AMP_MUTE_LEFT)) +#define HDA_AMP_RIGHT_MUTED(v) (((v) & HDA_AMP_MUTE_RIGHT) >> 1) + +#define HDA_DAC_PATH (1 << 0) +#define HDA_ADC_PATH (1 << 1) +#define HDA_ADC_RECSEL (1 << 2) + +#define HDA_CTL_OUT (1 << 0) +#define HDA_CTL_IN (1 << 1) +#define HDA_CTL_BOTH (HDA_CTL_IN | HDA_CTL_OUT) + +#define HDA_GPIO_MAX 15 +/* 0 - 14 = GPIO */ +#define HDA_QUIRK_GPIO0 (1 << 0) +#define HDA_QUIRK_GPIO1 (1 << 1) +#define HDA_QUIRK_GPIO2 (1 << 2) +#define HDA_QUIRK_SOFTPCMVOL (1 << 15) +#define HDA_QUIRK_FIXEDRATE (1 << 16) +#define HDA_QUIRK_FORCESTEREO (1 << 17) + +static const struct { + char *key; + uint32_t value; +} hdac_quirks_tab[] = { + { "gpio0", HDA_QUIRK_GPIO0 }, + { "gpio1", HDA_QUIRK_GPIO1 }, + { "gpio2", HDA_QUIRK_GPIO2 }, + { "softpcmvol", HDA_QUIRK_SOFTPCMVOL }, + { "fixedrate", HDA_QUIRK_FIXEDRATE }, + { "forcestereo", HDA_QUIRK_FORCESTEREO }, +}; +#define HDAC_QUIRKS_TAB_LEN \ + (sizeof(hdac_quirks_tab) / sizeof(hdac_quirks_tab[0])) + +#define HDA_BDL_MIN 2 +#define HDA_BDL_MAX 256 +#define HDA_BDL_DEFAULT HDA_BDL_MIN + +#define HDA_BUFSZ_MIN 4096 +#define HDA_BUFSZ_MAX 65536 +#define HDA_BUFSZ_DEFAULT 16384 + +#define HDA_PARSE_MAXDEPTH 10 + +#define HDAC_UNSOLTAG_EVENT_HP 0x00 + +static MALLOC_DEFINE(M_HDAC, "hdac", "High Definition Audio Controller"); + +enum { + HDA_PARSE_MIXER, + HDA_PARSE_DIRECT +}; + +/* Default */ +static uint32_t hdac_fmt[] = { + AFMT_STEREO | AFMT_S16_LE, + 0 +}; + +static struct pcmchan_caps hdac_caps = {48000, 48000, hdac_fmt, 0}; + +static const struct { + uint32_t model; + char *desc; +} hdac_devices[] = { + { HDA_INTEL_82801F, "Intel 82801F" }, + { HDA_INTEL_82801G, "Intel 82801G" }, + { HDA_INTEL_82801H, "Intel 82801H" }, + { HDA_INTEL_63XXESB, "Intel 631x/632xESB" }, + { HDA_NVIDIA_MCP51, "NVidia MCP51" }, + { HDA_NVIDIA_MCP55, "NVidia MCP55" }, + { HDA_NVIDIA_MCP61A, "NVidia MCP61A" }, + { HDA_NVIDIA_MCP61B, "NVidia MCP61B" }, + { HDA_NVIDIA_MCP65A, "NVidia MCP65A" }, + { HDA_NVIDIA_MCP65B, "NVidia MCP65B" }, + { HDA_ATI_SB450, "ATI SB450" }, + { HDA_ATI_SB600, "ATI SB600" }, + { HDA_VIA_VT82XX, "VIA VT8251/8237A" }, + { HDA_SIS_966, "SiS 966" }, + /* Unknown */ + { HDA_INTEL_ALL, "Intel (Unknown)" }, + { HDA_NVIDIA_ALL, "NVidia (Unknown)" }, + { HDA_ATI_ALL, "ATI (Unknown)" }, + { HDA_VIA_ALL, "VIA (Unknown)" }, + { HDA_SIS_ALL, "SiS (Unknown)" }, +}; +#define HDAC_DEVICES_LEN (sizeof(hdac_devices) / sizeof(hdac_devices[0])) + +static const struct { + uint32_t rate; + int valid; + uint16_t base; + uint16_t mul; + uint16_t div; +} hda_rate_tab[] = { + { 8000, 1, 0x0000, 0x0000, 0x0500 }, /* (48000 * 1) / 6 */ + { 9600, 0, 0x0000, 0x0000, 0x0400 }, /* (48000 * 1) / 5 */ + { 12000, 0, 0x0000, 0x0000, 0x0300 }, /* (48000 * 1) / 4 */ + { 16000, 1, 0x0000, 0x0000, 0x0200 }, /* (48000 * 1) / 3 */ + { 18000, 0, 0x0000, 0x1000, 0x0700 }, /* (48000 * 3) / 8 */ + { 19200, 0, 0x0000, 0x0800, 0x0400 }, /* (48000 * 2) / 5 */ + { 24000, 0, 0x0000, 0x0000, 0x0100 }, /* (48000 * 1) / 2 */ + { 28800, 0, 0x0000, 0x1000, 0x0400 }, /* (48000 * 3) / 5 */ + { 32000, 1, 0x0000, 0x0800, 0x0200 }, /* (48000 * 2) / 3 */ + { 36000, 0, 0x0000, 0x1000, 0x0300 }, /* (48000 * 3) / 4 */ + { 38400, 0, 0x0000, 0x1800, 0x0400 }, /* (48000 * 4) / 5 */ + { 48000, 1, 0x0000, 0x0000, 0x0000 }, /* (48000 * 1) / 1 */ + { 64000, 0, 0x0000, 0x1800, 0x0200 }, /* (48000 * 4) / 3 */ + { 72000, 0, 0x0000, 0x1000, 0x0100 }, /* (48000 * 3) / 2 */ + { 96000, 1, 0x0000, 0x0800, 0x0000 }, /* (48000 * 2) / 1 */ + { 144000, 0, 0x0000, 0x1000, 0x0000 }, /* (48000 * 3) / 1 */ + { 192000, 1, 0x0000, 0x1800, 0x0000 }, /* (48000 * 4) / 1 */ + { 8820, 0, 0x4000, 0x0000, 0x0400 }, /* (44100 * 1) / 5 */ + { 11025, 1, 0x4000, 0x0000, 0x0300 }, /* (44100 * 1) / 4 */ + { 12600, 0, 0x4000, 0x0800, 0x0600 }, /* (44100 * 2) / 7 */ + { 14700, 0, 0x4000, 0x0000, 0x0200 }, /* (44100 * 1) / 3 */ + { 17640, 0, 0x4000, 0x0800, 0x0400 }, /* (44100 * 2) / 5 */ + { 18900, 0, 0x4000, 0x1000, 0x0600 }, /* (44100 * 3) / 7 */ + { 22050, 1, 0x4000, 0x0000, 0x0100 }, /* (44100 * 1) / 2 */ + { 25200, 0, 0x4000, 0x1800, 0x0600 }, /* (44100 * 4) / 7 */ + { 26460, 0, 0x4000, 0x1000, 0x0400 }, /* (44100 * 3) / 5 */ + { 29400, 0, 0x4000, 0x0800, 0x0200 }, /* (44100 * 2) / 3 */ + { 33075, 0, 0x4000, 0x1000, 0x0300 }, /* (44100 * 3) / 4 */ + { 35280, 0, 0x4000, 0x1800, 0x0400 }, /* (44100 * 4) / 5 */ + { 44100, 1, 0x4000, 0x0000, 0x0000 }, /* (44100 * 1) / 1 */ + { 58800, 0, 0x4000, 0x1800, 0x0200 }, /* (44100 * 4) / 3 */ + { 66150, 0, 0x4000, 0x1000, 0x0100 }, /* (44100 * 3) / 2 */ + { 88200, 1, 0x4000, 0x0800, 0x0000 }, /* (44100 * 2) / 1 */ + { 132300, 0, 0x4000, 0x1000, 0x0000 }, /* (44100 * 3) / 1 */ + { 176400, 1, 0x4000, 0x1800, 0x0000 }, /* (44100 * 4) / 1 */ +}; +#define HDA_RATE_TAB_LEN (sizeof(hda_rate_tab) / sizeof(hda_rate_tab[0])) + +/* All codecs you can eat... */ +#define HDA_CODEC_CONSTRUCT(vendor, id) \ + (((uint32_t)(vendor##_VENDORID) << 16) | ((id) & 0xffff)) + +/* Realtek */ +#define REALTEK_VENDORID 0x10ec +#define HDA_CODEC_ALC260 HDA_CODEC_CONSTRUCT(REALTEK, 0x0260) +#define HDA_CODEC_ALC861 HDA_CODEC_CONSTRUCT(REALTEK, 0x0861) +#define HDA_CODEC_ALC880 HDA_CODEC_CONSTRUCT(REALTEK, 0x0880) +#define HDA_CODEC_ALC882 HDA_CODEC_CONSTRUCT(REALTEK, 0x0882) +#define HDA_CODEC_ALC883 HDA_CODEC_CONSTRUCT(REALTEK, 0x0883) +#define HDA_CODEC_ALCXXXX HDA_CODEC_CONSTRUCT(REALTEK, 0xffff) + +/* Analog Device */ +#define ANALOGDEVICE_VENDORID 0x11d4 +#define HDA_CODEC_AD1981HD HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1981) +#define HDA_CODEC_AD1983 HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1983) +#define HDA_CODEC_AD1986A HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0x1986) +#define HDA_CODEC_ADXXXX HDA_CODEC_CONSTRUCT(ANALOGDEVICE, 0xffff) + +/* CMedia */ +#define CMEDIA_VENDORID 0x434d +#define HDA_CODEC_CMI9880 HDA_CODEC_CONSTRUCT(CMEDIA, 0x4980) +#define HDA_CODEC_CMIXXXX HDA_CODEC_CONSTRUCT(CMEDIA, 0xffff) + +/* Sigmatel */ +#define SIGMATEL_VENDORID 0x8384 +#define HDA_CODEC_STAC9221 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7680) +#define HDA_CODEC_STAC9221D HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7683) +#define HDA_CODEC_STAC9220 HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7690) +#define HDA_CODEC_STAC922XD HDA_CODEC_CONSTRUCT(SIGMATEL, 0x7681) +#define HDA_CODEC_STACXXXX HDA_CODEC_CONSTRUCT(SIGMATEL, 0xffff) + +/* + * Conexant + * + * Ok, the truth is, I don't have any idea at all whether + * it is "Venice" or "Waikiki" or other unnamed CXyadayada. The only + * place that tell me it is "Venice" is from its Windows driver INF. + * + * Venice - CX????? + * Waikiki - CX20551-22 + */ +#define CONEXANT_VENDORID 0x14f1 +#define HDA_CODEC_CXVENICE HDA_CODEC_CONSTRUCT(CONEXANT, 0x5045) +#define HDA_CODEC_CXWAIKIKI HDA_CODEC_CONSTRUCT(CONEXANT, 0x5047) +#define HDA_CODEC_CXXXXX HDA_CODEC_CONSTRUCT(CONEXANT, 0xffff) + + +/* Codecs */ +static const struct { + uint32_t id; + char *name; +} hdac_codecs[] = { + { HDA_CODEC_ALC260, "Realtek ALC260" }, + { HDA_CODEC_ALC861, "Realtek ALC861" }, + { HDA_CODEC_ALC880, "Realtek ALC880" }, + { HDA_CODEC_ALC882, "Realtek ALC882" }, + { HDA_CODEC_ALC883, "Realtek ALC883" }, + { HDA_CODEC_AD1981HD, "Analog Device AD1981HD" }, + { HDA_CODEC_AD1983, "Analog Device AD1983" }, + { HDA_CODEC_AD1986A, "Analog Device AD1986A" }, + { HDA_CODEC_CMI9880, "CMedia CMI9880" }, + { HDA_CODEC_STAC9221, "Sigmatel STAC9221" }, + { HDA_CODEC_STAC9221D, "Sigmatel STAC9221D" }, + { HDA_CODEC_STAC9220, "Sigmatel STAC9220" }, + { HDA_CODEC_STAC922XD, "Sigmatel STAC9220D/9223D" }, + { HDA_CODEC_CXVENICE, "Conexant Venice" }, + { HDA_CODEC_CXWAIKIKI, "Conexant Waikiki" }, + /* Unknown codec */ + { HDA_CODEC_ALCXXXX, "Realtek (Unknown)" }, + { HDA_CODEC_ADXXXX, "Analog Device (Unknown)" }, + { HDA_CODEC_CMIXXXX, "CMedia (Unknown)" }, + { HDA_CODEC_STACXXXX, "Sigmatel (Unknown)" }, + { HDA_CODEC_CXXXXX, "Conexant (Unknown)" }, +}; +#define HDAC_CODECS_LEN (sizeof(hdac_codecs) / sizeof(hdac_codecs[0])) + +enum { + HDAC_HP_SWITCH_CTL, + HDAC_HP_SWITCH_CTRL +}; + +static const struct { + uint32_t model; + uint32_t id; + int type; + nid_t hpnid; + nid_t spkrnid[8]; + nid_t eapdnid; +} hdac_hp_switch[] = { + /* Specific OEM models */ + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 17, { 16, -1 }, 16 }, + { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 6, { 5, -1 }, 5 }, + { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 6, { 5, -1 }, 5 }, + { DELL_D820_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 13, { 14, -1 }, -1 }, + { DELL_I1300_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 13, { 14, -1 }, -1 }, + /* + * All models that at least come from the same vendor with + * simmilar codec. + */ + { HP_ALL_SUBVENDOR, HDA_CODEC_CXVENICE, HDAC_HP_SWITCH_CTL, + 17, { 16, -1 }, 16 }, + { HP_ALL_SUBVENDOR, HDA_CODEC_AD1981HD, HDAC_HP_SWITCH_CTL, + 6, { 5, -1 }, 5 }, + { DELL_ALL_SUBVENDOR, HDA_CODEC_STAC9220, HDAC_HP_SWITCH_CTRL, + 13, { 14, -1 }, -1 }, +}; +#define HDAC_HP_SWITCH_LEN \ + (sizeof(hdac_hp_switch) / sizeof(hdac_hp_switch[0])) + +static const struct { + uint32_t model; + uint32_t id; + nid_t eapdnid; + int hp_switch; +} hdac_eapd_switch[] = { + { HP_V3000_SUBVENDOR, HDA_CODEC_CXVENICE, 16, 1 }, + { HP_NX7400_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, + { HP_NX6310_SUBVENDOR, HDA_CODEC_AD1981HD, 5, 1 }, +}; +#define HDAC_EAPD_SWITCH_LEN \ + (sizeof(hdac_eapd_switch) / sizeof(hdac_eapd_switch[0])) + +/**************************************************************************** + * Function prototypes + ****************************************************************************/ +static void hdac_intr_handler(void *); +static int hdac_reset(struct hdac_softc *); +static int hdac_get_capabilities(struct hdac_softc *); +static void hdac_dma_cb(void *, bus_dma_segment_t *, int, int); +static int hdac_dma_alloc(struct hdac_softc *, + struct hdac_dma *, bus_size_t); +static void hdac_dma_free(struct hdac_dma *); +static int hdac_mem_alloc(struct hdac_softc *); +static void hdac_mem_free(struct hdac_softc *); +static int hdac_irq_alloc(struct hdac_softc *); +static void hdac_irq_free(struct hdac_softc *); +static void hdac_corb_init(struct hdac_softc *); +static void hdac_rirb_init(struct hdac_softc *); +static void hdac_corb_start(struct hdac_softc *); +static void hdac_rirb_start(struct hdac_softc *); +static void hdac_scan_codecs(struct hdac_softc *); +static int hdac_probe_codec(struct hdac_codec *); +static struct hdac_devinfo *hdac_probe_function(struct hdac_codec *, nid_t); +static void hdac_add_child(struct hdac_softc *, struct hdac_devinfo *); + +static void hdac_attach2(void *); + +static uint32_t hdac_command_sendone_internal(struct hdac_softc *, + uint32_t, int); +static void hdac_command_send_internal(struct hdac_softc *, + struct hdac_command_list *, int); + +static int hdac_probe(device_t); +static int hdac_attach(device_t); +static int hdac_detach(device_t); +static void hdac_widget_connection_select(struct hdac_widget *, uint8_t); +static void hdac_audio_ctl_amp_set(struct hdac_audio_ctl *, + uint32_t, int, int); +static struct hdac_audio_ctl *hdac_audio_ctl_amp_get(struct hdac_devinfo *, + nid_t, int, int); +static void hdac_audio_ctl_amp_set_internal(struct hdac_softc *, + nid_t, nid_t, int, int, int, int, int, int); +static int hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *); +static struct hdac_widget *hdac_widget_get(struct hdac_devinfo *, nid_t); + +#define hdac_command(a1, a2, a3) \ + hdac_command_sendone_internal(a1, a2, a3) + +#define hdac_codec_id(d) \ + ((uint32_t)((d == NULL) ? 0x00000000 : \ + ((((uint32_t)(d)->vendor_id & 0x0000ffff) << 16) | \ + ((uint32_t)(d)->device_id & 0x0000ffff)))) + +static char * +hdac_codec_name(struct hdac_devinfo *devinfo) +{ + uint32_t id; + int i; + + id = hdac_codec_id(devinfo); + + for (i = 0; i < HDAC_CODECS_LEN; i++) { + if (HDA_DEV_MATCH(hdac_codecs[i].id, id)) + return (hdac_codecs[i].name); + } + + return ((id == 0x00000000) ? "NULL Codec" : "Unknown Codec"); +} + +static char * +hdac_audio_ctl_ossmixer_mask2name(uint32_t devmask) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + static char *unknown = "???"; + int i; + + for (i = SOUND_MIXER_NRDEVICES - 1; i >= 0; i--) { + if (devmask & (1 << i)) + return (ossname[i]); + } + return (unknown); +} + +static void +hdac_audio_ctl_ossmixer_mask2allname(uint32_t mask, char *buf, size_t len) +{ + static char *ossname[] = SOUND_DEVICE_NAMES; + int i, first = 1; + + bzero(buf, len); + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (mask & (1 << i)) { + if (first == 0) + strlcat(buf, ", ", len); + strlcat(buf, ossname[i], len); + first = 0; + } + } +} + +static struct hdac_audio_ctl * +hdac_audio_ctl_each(struct hdac_devinfo *devinfo, int *index) +{ + if (devinfo == NULL || + devinfo->node_type != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO || + index == NULL || devinfo->function.audio.ctl == NULL || + devinfo->function.audio.ctlcnt < 1 || + *index < 0 || *index >= devinfo->function.audio.ctlcnt) + return (NULL); + return (&devinfo->function.audio.ctl[(*index)++]); +} + +static struct hdac_audio_ctl * +hdac_audio_ctl_amp_get(struct hdac_devinfo *devinfo, nid_t nid, + int index, int cnt) +{ + struct hdac_audio_ctl *ctl, *retctl = NULL; + int i, at, atindex, found = 0; + + if (devinfo == NULL || devinfo->function.audio.ctl == NULL) + return (NULL); + + at = cnt; + if (at == 0) + at = 1; + else if (at < 0) + at = -1; + atindex = index; + if (atindex < 0) + atindex = -1; + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (!(ctl->widget->nid == nid && (atindex == -1 || + ctl->index == atindex))) + continue; + found++; + if (found == cnt) + return (ctl); + retctl = ctl; + } + + return ((at == -1) ? retctl : NULL); +} + +static void +hdac_hp_switch_handler(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t id, res; + int i = 0, j, forcemute; + nid_t cad; + + if (devinfo == NULL || devinfo->codec == NULL || + devinfo->codec->sc == NULL) + return; + + sc = devinfo->codec->sc; + cad = devinfo->codec->cad; + id = hdac_codec_id(devinfo); + for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { + if (HDA_DEV_MATCH(hdac_hp_switch[i].model, + sc->pci_subvendor) && + hdac_hp_switch[i].id == id) + break; + } + + if (i >= HDAC_HP_SWITCH_LEN) + return; + + forcemute = 0; + if (hdac_hp_switch[i].eapdnid != -1) { + w = hdac_widget_get(devinfo, hdac_hp_switch[i].eapdnid); + if (w != NULL && w->param.eapdbtl != HDAC_INVALID) + forcemute = (w->param.eapdbtl & + HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD) ? 0 : 1; + } + + res = hdac_command(sc, + HDA_CMD_GET_PIN_SENSE(cad, hdac_hp_switch[i].hpnid), cad); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Pin sense: nid=%d res=0x%08x\n", + hdac_hp_switch[i].hpnid, res); + ); + res >>= 31; + + switch (hdac_hp_switch[i].type) { + case HDAC_HP_SWITCH_CTL: + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].hpnid, 0, 1); + if (ctl != NULL) { + ctl->muted = (res != 0 && forcemute == 0) ? + HDA_AMP_MUTE_NONE : HDA_AMP_MUTE_ALL; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, ctl->left, + ctl->right); + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + ctl = hdac_audio_ctl_amp_get(devinfo, + hdac_hp_switch[i].spkrnid[j], 0, 1); + if (ctl != NULL) { + ctl->muted = (res != 0 || forcemute == 1) ? + HDA_AMP_MUTE_ALL : HDA_AMP_MUTE_NONE; + hdac_audio_ctl_amp_set(ctl, + HDA_AMP_MUTE_DEFAULT, ctl->left, + ctl->right); + } + } + break; + case HDAC_HP_SWITCH_CTRL: + if (res != 0) { + /* HP in */ + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (forcemute == 0) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, + w->wclass.pin.ctrl), cad); + } + } + } else { + /* HP out */ + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + for (j = 0; hdac_hp_switch[i].spkrnid[j] != -1; j++) { + w = hdac_widget_get(devinfo, + hdac_hp_switch[i].spkrnid[j]); + if (w != NULL && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if (forcemute == 0) + w->wclass.pin.ctrl |= + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + else + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, + w->nid, + w->wclass.pin.ctrl), cad); + } + } + } + break; + default: + break; + } +} + +static void +hdac_unsolicited_handler(struct hdac_codec *codec, uint32_t tag) +{ + struct hdac_softc *sc; + struct hdac_devinfo *devinfo = NULL; + device_t *devlist = NULL; + int devcount, i; + + if (codec == NULL || codec->sc == NULL) + return; + + sc = codec->sc; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Unsol Tag: 0x%08x\n", tag); + ); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo != NULL && devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->codec != NULL && + devinfo->codec->cad == codec->cad) { + break; + } else + devinfo = NULL; + } + if (devlist != NULL) + kfree(devlist, M_TEMP); + + if (devinfo == NULL) + return; + + switch (tag) { + case HDAC_UNSOLTAG_EVENT_HP: + hdac_hp_switch_handler(devinfo); + break; + default: + break; + } +} + +static void +hdac_stream_intr(struct hdac_softc *sc, struct hdac_chan *ch) +{ + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + uint32_t res; +#endif + + if (ch->blkcnt == 0) + return; + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + res = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDSTS); +#endif + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + HDA_BOOTVERBOSE( + if (res & (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE)) + device_printf(sc->dev, + "PCMDIR_%s intr triggered beyond stream boundary:" + "%08x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", res); + ); +#endif + + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDSTS, + HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS ); + + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + if (res & HDAC_SDSTS_BCIS) { +#endif + ch->prevptr = ch->ptr; + ch->ptr += sndbuf_getblksz(ch->b); + ch->ptr %= sndbuf_getsize(ch->b); + hdac_unlock(sc); + chn_intr(ch->c); + hdac_lock(sc); + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + } +#endif +} + +/**************************************************************************** + * void hdac_intr_handler(void *) + * + * Interrupt handler. Processes interrupts received from the hdac. + ****************************************************************************/ +static void +hdac_intr_handler(void *context) +{ + struct hdac_softc *sc; + uint32_t intsts; + uint8_t rirbsts; + uint8_t rirbwp; + struct hdac_rirb *rirb_base, *rirb; + nid_t ucad; + uint32_t utag; + + sc = (struct hdac_softc *)context; + + hdac_lock(sc); + /* Do we have anything to do? */ + intsts = HDAC_READ_4(&sc->mem, HDAC_INTSTS); + if (!HDA_FLAG_MATCH(intsts, HDAC_INTSTS_GIS)) { + hdac_unlock(sc); + return; + } + + /* Was this a controller interrupt? */ + if (HDA_FLAG_MATCH(intsts, HDAC_INTSTS_CIS)) { + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); + /* Get as many responses that we can */ + while (HDA_FLAG_MATCH(rirbsts, HDAC_RIRBSTS_RINTFL)) { + HDAC_WRITE_1(&sc->mem, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL); + rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_POSTREAD); + while (sc->rirb_rp != rirbwp) { + sc->rirb_rp++; + sc->rirb_rp %= sc->rirb_size; + rirb = &rirb_base[sc->rirb_rp]; + if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { + ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); + utag = rirb->response >> 26; + if (ucad > -1 && ucad < HDAC_CODEC_MAX && + sc->codecs[ucad] != NULL) { + sc->unsolq[sc->unsolq_wp++] = + (ucad << 16) | + (utag & 0xffff); + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } + } + } + rirbsts = HDAC_READ_1(&sc->mem, HDAC_RIRBSTS); + } + /* XXX to be removed */ + /* Clear interrupt and exit */ +#ifdef HDAC_INTR_EXTRA + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, HDAC_INTSTS_CIS); +#endif + } + if (intsts & HDAC_INTSTS_SIS_MASK) { + if (intsts & (1 << sc->num_iss)) + hdac_stream_intr(sc, &sc->play); + if (intsts & (1 << 0)) + hdac_stream_intr(sc, &sc->rec); + /* XXX to be removed */ +#ifdef HDAC_INTR_EXTRA + HDAC_WRITE_4(&sc->mem, HDAC_INTSTS, intsts & HDAC_INTSTS_SIS_MASK); +#endif + } + + if (sc->unsolq_st == HDAC_UNSOLQ_READY) { + sc->unsolq_st = HDAC_UNSOLQ_BUSY; + while (sc->unsolq_rp != sc->unsolq_wp) { + ucad = sc->unsolq[sc->unsolq_rp] >> 16; + utag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + hdac_unsolicited_handler(sc->codecs[ucad], utag); + } + sc->unsolq_st = HDAC_UNSOLQ_READY; + } + + hdac_unlock(sc); +} + +/**************************************************************************** + * int hdac_reset(hdac_softc *) + * + * Reset the hdac to a quiescent and known state. + ****************************************************************************/ +static int +hdac_reset(struct hdac_softc *sc) +{ + uint32_t gctl; + int count, i; + + /* + * Stop all Streams DMA engine + */ + for (i = 0; i < sc->num_iss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_ISDCTL(sc, i), 0x0); + for (i = 0; i < sc->num_oss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_OSDCTL(sc, i), 0x0); + for (i = 0; i < sc->num_bss; i++) + HDAC_WRITE_4(&sc->mem, HDAC_BSDCTL(sc, i), 0x0); + + /* + * Stop Control DMA engines + */ + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, 0x0); + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, 0x0); + + /* + * Reset the controller. The reset must remain asserted for + * a minimum of 100us. + */ + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl & ~HDAC_GCTL_CRST); + count = 10000; + do { + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + if (!(gctl & HDAC_GCTL_CRST)) + break; + DELAY(10); + } while (--count); + if (gctl & HDAC_GCTL_CRST) { + device_printf(sc->dev, "Unable to put hdac in reset\n"); + return (ENXIO); + } + DELAY(100); + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, gctl | HDAC_GCTL_CRST); + count = 10000; + do { + gctl = HDAC_READ_4(&sc->mem, HDAC_GCTL); + if (gctl & HDAC_GCTL_CRST) + break; + DELAY(10); + } while (--count); + if (!(gctl & HDAC_GCTL_CRST)) { + device_printf(sc->dev, "Device stuck in reset\n"); + return (ENXIO); + } + + /* + * Wait for codecs to finish their own reset sequence. The delay here + * should be of 250us but for some reasons, on it's not enough on my + * computer. Let's use twice as much as necessary to make sure that + * it's reset properly. + */ + DELAY(1000); + + return (0); +} + + +/**************************************************************************** + * int hdac_get_capabilities(struct hdac_softc *); + * + * Retreive the general capabilities of the hdac; + * Number of Input Streams + * Number of Output Streams + * Number of bidirectional Streams + * 64bit ready + * CORB and RIRB sizes + ****************************************************************************/ +static int +hdac_get_capabilities(struct hdac_softc *sc) +{ + uint16_t gcap; + uint8_t corbsize, rirbsize; + + gcap = HDAC_READ_2(&sc->mem, HDAC_GCAP); + sc->num_iss = HDAC_GCAP_ISS(gcap); + sc->num_oss = HDAC_GCAP_OSS(gcap); + sc->num_bss = HDAC_GCAP_BSS(gcap); + + sc->support_64bit = HDA_FLAG_MATCH(gcap, HDAC_GCAP_64OK); + + corbsize = HDAC_READ_1(&sc->mem, HDAC_CORBSIZE); + if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_256) == + HDAC_CORBSIZE_CORBSZCAP_256) + sc->corb_size = 256; + else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_16) == + HDAC_CORBSIZE_CORBSZCAP_16) + sc->corb_size = 16; + else if ((corbsize & HDAC_CORBSIZE_CORBSZCAP_2) == + HDAC_CORBSIZE_CORBSZCAP_2) + sc->corb_size = 2; + else { + device_printf(sc->dev, "%s: Invalid corb size (%x)\n", + __func__, corbsize); + return (ENXIO); + } + + rirbsize = HDAC_READ_1(&sc->mem, HDAC_RIRBSIZE); + if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_256) == + HDAC_RIRBSIZE_RIRBSZCAP_256) + sc->rirb_size = 256; + else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_16) == + HDAC_RIRBSIZE_RIRBSZCAP_16) + sc->rirb_size = 16; + else if ((rirbsize & HDAC_RIRBSIZE_RIRBSZCAP_2) == + HDAC_RIRBSIZE_RIRBSZCAP_2) + sc->rirb_size = 2; + else { + device_printf(sc->dev, "%s: Invalid rirb size (%x)\n", + __func__, rirbsize); + return (ENXIO); + } + + return (0); +} + + +/**************************************************************************** + * void hdac_dma_cb + * + * This function is called by bus_dmamap_load when the mapping has been + * established. We just record the physical address of the mapping into + * the struct hdac_dma passed in. + ****************************************************************************/ +static void +hdac_dma_cb(void *callback_arg, bus_dma_segment_t *segs, int nseg, int error) +{ + struct hdac_dma *dma; + + if (error == 0) { + dma = (struct hdac_dma *)callback_arg; + dma->dma_paddr = segs[0].ds_addr; + } +} + +static void +hdac_dma_nocache(void *ptr) +{ +#if defined(__i386__) || defined(__amd64__) + pt_entry_t *pte; + vm_offset_t va; + + va = (vm_offset_t)ptr; + pte = vtopte(va); + if (pte) { + *pte |= PG_N; + cpu_invltlb(); + } +#endif +} + +/**************************************************************************** + * int hdac_dma_alloc + * + * This function allocate and setup a dma region (struct hdac_dma). + * It must be freed by a corresponding hdac_dma_free. + ****************************************************************************/ +static int +hdac_dma_alloc(struct hdac_softc *sc, struct hdac_dma *dma, bus_size_t size) +{ + int result; + int lowaddr; + + lowaddr = (sc->support_64bit) ? BUS_SPACE_MAXADDR : + BUS_SPACE_MAXADDR_32BIT; + bzero(dma, sizeof(*dma)); + + /* + * Create a DMA tag + */ + result = bus_dma_tag_create(NULL, /* parent */ + HDAC_DMA_ALIGNMENT, /* alignment */ + 0, /* boundary */ + lowaddr, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, /* filtfunc */ + NULL, /* fistfuncarg */ + size, /* maxsize */ + 1, /* nsegments */ + size, /* maxsegsz */ + 0, /* flags */ + &dma->dma_tag); /* dmat */ + if (result != 0) { + device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", + __func__, result); + goto fail; + } + + /* + * Allocate DMA memory + */ + result = bus_dmamem_alloc(dma->dma_tag, (void **)&dma->dma_vaddr, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &dma->dma_map); + if (result != 0) { + device_printf(sc->dev, "%s: bus_dmamem_alloc failed (%x)\n", + __func__, result); + goto fail; + } + + /* + * Map the memory + */ + result = bus_dmamap_load(dma->dma_tag, dma->dma_map, + (void *)dma->dma_vaddr, size, hdac_dma_cb, (void *)dma, + BUS_DMA_NOWAIT); + if (result != 0 || dma->dma_paddr == 0) { + device_printf(sc->dev, "%s: bus_dmamem_load failed (%x)\n", + __func__, result); + goto fail; + } + bzero((void *)dma->dma_vaddr, size); + hdac_dma_nocache(dma->dma_vaddr); + + return (0); +fail: + if (dma->dma_map != NULL) + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + if (dma->dma_tag != NULL) + bus_dma_tag_destroy(dma->dma_tag); + return (result); +} + + +/**************************************************************************** + * void hdac_dma_free(struct hdac_dma *) + * + * Free a struct dhac_dma that has been previously allocated via the + * hdac_dma_alloc function. + ****************************************************************************/ +static void +hdac_dma_free(struct hdac_dma *dma) +{ + if (dma->dma_tag != NULL) { + /* Flush caches */ + bus_dmamap_sync(dma->dma_tag, dma->dma_map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + bus_dmamem_free(dma->dma_tag, dma->dma_vaddr, dma->dma_map); + bus_dma_tag_destroy(dma->dma_tag); + } +} + +/**************************************************************************** + * int hdac_mem_alloc(struct hdac_softc *) + * + * Allocate all the bus resources necessary to speak with the physical + * controller. + ****************************************************************************/ +static int +hdac_mem_alloc(struct hdac_softc *sc) +{ + struct hdac_mem *mem; + + mem = &sc->mem; + mem->mem_rid = PCIR_BAR(0); + mem->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, + &mem->mem_rid, RF_ACTIVE); + if (mem->mem_res == NULL) { + device_printf(sc->dev, + "%s: Unable to allocate memory resource\n", __func__); + return (ENOMEM); + } + mem->mem_tag = rman_get_bustag(mem->mem_res); + mem->mem_handle = rman_get_bushandle(mem->mem_res); + + return (0); +} + +/**************************************************************************** + * void hdac_mem_free(struct hdac_softc *) + * + * Free up resources previously allocated by hdac_mem_alloc. + ****************************************************************************/ +static void +hdac_mem_free(struct hdac_softc *sc) +{ + struct hdac_mem *mem; + + mem = &sc->mem; + if (mem->mem_res != NULL) + bus_release_resource(sc->dev, SYS_RES_MEMORY, mem->mem_rid, + mem->mem_res); +} + +/**************************************************************************** + * int hdac_irq_alloc(struct hdac_softc *) + * + * Allocate and setup the resources necessary for interrupt handling. + ****************************************************************************/ +static int +hdac_irq_alloc(struct hdac_softc *sc) +{ + struct hdac_irq *irq; + int result; + + irq = &sc->irq; + irq->irq_rid = 0x0; + irq->irq_res = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, + &irq->irq_rid, RF_SHAREABLE | RF_ACTIVE); + if (irq->irq_res == NULL) { + device_printf(sc->dev, "%s: Unable to allocate irq\n", + __func__); + goto fail; + } + result = snd_setup_intr(sc->dev, irq->irq_res, INTR_MPSAFE, + hdac_intr_handler, sc, &irq->irq_handle); + if (result != 0) { + device_printf(sc->dev, + "%s: Unable to setup interrupt handler (%x)\n", + __func__, result); + goto fail; + } + + return (0); + +fail: + if (irq->irq_res != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, + irq->irq_res); + return (ENXIO); +} + +/**************************************************************************** + * void hdac_irq_free(struct hdac_softc *) + * + * Free up resources previously allocated by hdac_irq_alloc. + ****************************************************************************/ +static void +hdac_irq_free(struct hdac_softc *sc) +{ + struct hdac_irq *irq; + + irq = &sc->irq; + if (irq->irq_handle != NULL) + bus_teardown_intr(sc->dev, irq->irq_res, irq->irq_handle); + if (irq->irq_res != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, irq->irq_rid, + irq->irq_res); +} + +/**************************************************************************** + * void hdac_corb_init(struct hdac_softc *) + * + * Initialize the corb registers for operations but do not start it up yet. + * The CORB engine must not be running when this function is called. + ****************************************************************************/ +static void +hdac_corb_init(struct hdac_softc *sc) +{ + uint8_t corbsize; + uint64_t corbpaddr; + + /* Setup the CORB size. */ + switch (sc->corb_size) { + case 256: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_256); + break; + case 16: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_16); + break; + case 2: + corbsize = HDAC_CORBSIZE_CORBSIZE(HDAC_CORBSIZE_CORBSIZE_2); + break; + default: + panic("%s: Invalid CORB size (%x)\n", __func__, sc->corb_size); + } + HDAC_WRITE_1(&sc->mem, HDAC_CORBSIZE, corbsize); + + /* Setup the CORB Address in the hdac */ + corbpaddr = (uint64_t)sc->corb_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_CORBLBASE, (uint32_t)corbpaddr); + HDAC_WRITE_4(&sc->mem, HDAC_CORBUBASE, (uint32_t)(corbpaddr >> 32)); + + /* Set the WP and RP */ + sc->corb_wp = 0; + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); + HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, HDAC_CORBRP_CORBRPRST); + /* + * The HDA specification indicates that the CORBRPRST bit will always + * read as zero. Unfortunately, it seems that at least the 82801G + * doesn't reset the bit to zero, which stalls the corb engine. + * manually reset the bit to zero before continuing. + */ + HDAC_WRITE_2(&sc->mem, HDAC_CORBRP, 0x0); + + /* Enable CORB error reporting */ +#if 0 + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, HDAC_CORBCTL_CMEIE); +#endif +} + +/**************************************************************************** + * void hdac_rirb_init(struct hdac_softc *) + * + * Initialize the rirb registers for operations but do not start it up yet. + * The RIRB engine must not be running when this function is called. + ****************************************************************************/ +static void +hdac_rirb_init(struct hdac_softc *sc) +{ + uint8_t rirbsize; + uint64_t rirbpaddr; + + /* Setup the RIRB size. */ + switch (sc->rirb_size) { + case 256: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_256); + break; + case 16: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_16); + break; + case 2: + rirbsize = HDAC_RIRBSIZE_RIRBSIZE(HDAC_RIRBSIZE_RIRBSIZE_2); + break; + default: + panic("%s: Invalid RIRB size (%x)\n", __func__, sc->rirb_size); + } + HDAC_WRITE_1(&sc->mem, HDAC_RIRBSIZE, rirbsize); + + /* Setup the RIRB Address in the hdac */ + rirbpaddr = (uint64_t)sc->rirb_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, HDAC_RIRBLBASE, (uint32_t)rirbpaddr); + HDAC_WRITE_4(&sc->mem, HDAC_RIRBUBASE, (uint32_t)(rirbpaddr >> 32)); + + /* Setup the WP and RP */ + sc->rirb_rp = 0; + HDAC_WRITE_2(&sc->mem, HDAC_RIRBWP, HDAC_RIRBWP_RIRBWPRST); + + /* Setup the interrupt threshold */ + HDAC_WRITE_2(&sc->mem, HDAC_RINTCNT, sc->rirb_size / 2); + + /* Enable Overrun and response received reporting */ +#if 0 + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, + HDAC_RIRBCTL_RIRBOIC | HDAC_RIRBCTL_RINTCTL); +#else + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, HDAC_RIRBCTL_RINTCTL); +#endif + + /* + * Make sure that the Host CPU cache doesn't contain any dirty + * cache lines that falls in the rirb. If I understood correctly, it + * should be sufficient to do this only once as the rirb is purely + * read-only from now on. + */ + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_PREREAD); +} + +/**************************************************************************** + * void hdac_corb_start(hdac_softc *) + * + * Startup the corb DMA engine + ****************************************************************************/ +static void +hdac_corb_start(struct hdac_softc *sc) +{ + uint32_t corbctl; + + corbctl = HDAC_READ_1(&sc->mem, HDAC_CORBCTL); + corbctl |= HDAC_CORBCTL_CORBRUN; + HDAC_WRITE_1(&sc->mem, HDAC_CORBCTL, corbctl); +} + +/**************************************************************************** + * void hdac_rirb_start(hdac_softc *) + * + * Startup the rirb DMA engine + ****************************************************************************/ +static void +hdac_rirb_start(struct hdac_softc *sc) +{ + uint32_t rirbctl; + + rirbctl = HDAC_READ_1(&sc->mem, HDAC_RIRBCTL); + rirbctl |= HDAC_RIRBCTL_RIRBDMAEN; + HDAC_WRITE_1(&sc->mem, HDAC_RIRBCTL, rirbctl); +} + + +/**************************************************************************** + * void hdac_scan_codecs(struct hdac_softc *) + * + * Scan the bus for available codecs. + ****************************************************************************/ +static void +hdac_scan_codecs(struct hdac_softc *sc) +{ + struct hdac_codec *codec; + int i; + uint16_t statests; + + statests = HDAC_READ_2(&sc->mem, HDAC_STATESTS); + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (HDAC_STATESTS_SDIWAKE(statests, i)) { + /* We have found a codec. */ + hdac_unlock(sc); + codec = (struct hdac_codec *)kmalloc(sizeof(*codec), + M_HDAC, M_ZERO | M_NOWAIT); + hdac_lock(sc); + if (codec == NULL) { + device_printf(sc->dev, + "Unable to allocate memory for codec\n"); + continue; + } + codec->verbs_sent = 0; + codec->sc = sc; + codec->cad = i; + sc->codecs[i] = codec; + if (hdac_probe_codec(codec) != 0) + break; + } + } + /* All codecs have been probed, now try to attach drivers to them */ + /* bus_generic_attach(sc->dev); */ +} + +/**************************************************************************** + * void hdac_probe_codec(struct hdac_softc *, int) + * + * Probe a the given codec_id for available function groups. + ****************************************************************************/ +static int +hdac_probe_codec(struct hdac_codec *codec) +{ + struct hdac_softc *sc = codec->sc; + struct hdac_devinfo *devinfo; + uint32_t vendorid, revisionid, subnode; + int startnode; + int endnode; + int i; + nid_t cad = codec->cad; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Probing codec: %d\n", cad); + ); + vendorid = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_VENDOR_ID), + cad); + revisionid = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_REVISION_ID), + cad); + subnode = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, 0x0, HDA_PARAM_SUB_NODE_COUNT), + cad); + startnode = HDA_PARAM_SUB_NODE_COUNT_START(subnode); + endnode = startnode + HDA_PARAM_SUB_NODE_COUNT_TOTAL(subnode); + + if (vendorid == HDAC_INVALID || + revisionid == HDAC_INVALID || + subnode == HDAC_INVALID) { + device_printf(sc->dev, "invalid response\n"); + return (0); + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: \tstartnode=%d endnode=%d\n", + startnode, endnode); + ); + for (i = startnode; i < endnode; i++) { + devinfo = hdac_probe_function(codec, i); + if (devinfo != NULL) { + /* XXX Ignore other FG. */ + devinfo->vendor_id = + HDA_PARAM_VENDOR_ID_VENDOR_ID(vendorid); + devinfo->device_id = + HDA_PARAM_VENDOR_ID_DEVICE_ID(vendorid); + devinfo->revision_id = + HDA_PARAM_REVISION_ID_REVISION_ID(revisionid); + devinfo->stepping_id = + HDA_PARAM_REVISION_ID_STEPPING_ID(revisionid); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: \tFound AFG nid=%d " + "[startnode=%d endnode=%d]\n", + devinfo->nid, startnode, endnode); + ); + return (1); + } + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: \tAFG not found\n"); + ); + return (0); +} + +static struct hdac_devinfo * +hdac_probe_function(struct hdac_codec *codec, nid_t nid) +{ + struct hdac_softc *sc = codec->sc; + struct hdac_devinfo *devinfo; + uint32_t fctgrptype; + nid_t cad = codec->cad; + + fctgrptype = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_FCT_GRP_TYPE), cad)); + + /* XXX For now, ignore other FG. */ + if (fctgrptype != HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) + return (NULL); + + hdac_unlock(sc); + devinfo = (struct hdac_devinfo *)kmalloc(sizeof(*devinfo), M_HDAC, + M_NOWAIT | M_ZERO); + hdac_lock(sc); + if (devinfo == NULL) { + device_printf(sc->dev, "%s: Unable to allocate ivar\n", + __func__); + return (NULL); + } + + devinfo->nid = nid; + devinfo->node_type = fctgrptype; + devinfo->codec = codec; + + hdac_add_child(sc, devinfo); + + return (devinfo); +} + +static void +hdac_add_child(struct hdac_softc *sc, struct hdac_devinfo *devinfo) +{ + devinfo->dev = device_add_child(sc->dev, NULL, -1); + device_set_ivars(devinfo->dev, (void *)devinfo); + /* XXX - Print more information when booting verbose??? */ +} + +static void +hdac_widget_connection_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t res; + int i, j, max, found, entnum, cnid; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_CONN_LIST_LENGTH), cad); + + w->nconns = HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(res); + + if (w->nconns < 1) + return; + + entnum = HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(res) ? 2 : 4; + res = 0; + i = 0; + found = 0; + max = (sizeof(w->conns) / sizeof(w->conns[0])) - 1; + + while (i < w->nconns) { + res = hdac_command(sc, + HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, i), cad); + for (j = 0; j < entnum; j++) { + cnid = res; + cnid >>= (32 / entnum) * j; + cnid &= (1 << (32 / entnum)) - 1; + if (cnid == 0) + continue; + if (found > max) { + device_printf(sc->dev, + "node %d: Adding %d: " + "Max connection reached!\n", + nid, cnid); + continue; + } + w->conns[found++] = cnid; + } + i += entnum; + } + + HDA_BOOTVERBOSE( + if (w->nconns != found) { + device_printf(sc->dev, + "HDA_DEBUG: nid=%d WARNING!!! Connection " + "length=%d != found=%d\n", + nid, w->nconns, found); + } + ); +} + +static uint32_t +hdac_widget_pin_getconfig(struct hdac_widget *w) +{ + struct hdac_softc *sc; + uint32_t config, id; + nid_t cad, nid; + + sc = w->devinfo->codec->sc; + cad = w->devinfo->codec->cad; + nid = w->nid; + id = hdac_codec_id(w->devinfo); + + config = hdac_command(sc, + HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid), + cad); + /* + * XXX REWRITE!!!! Don't argue! + */ + if (id == HDA_CODEC_ALC880 && + (sc->pci_subvendor == CLEVO_D900T_SUBVENDOR || + sc->pci_subvendor == ASUS_M5200_SUBVENDOR)) { + /* + * Super broken BIOS + */ + switch (nid) { + case 20: + break; + case 21: + break; + case 22: + break; + case 23: + break; + case 24: /* MIC1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 25: /* XXX MIC2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN; + break; + case 26: /* LINE1 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 27: /* XXX LINE2 */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN; + break; + case 28: /* CD */ + config &= ~HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + config |= HDA_CONFIG_DEFAULTCONF_DEVICE_CD; + break; + case 30: + break; + case 31: + break; + default: + break; + } + } + + return (config); +} + +static void +hdac_widget_pin_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t config, pincap; + char *devstr, *connstr; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + config = hdac_widget_pin_getconfig(w); + w->wclass.pin.config = config; + + pincap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_PIN_CAP), cad); + w->wclass.pin.cap = pincap; + + w->wclass.pin.ctrl = hdac_command(sc, + HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid), cad) & + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE); + + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE; + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + w->wclass.pin.ctrl |= HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) { + w->param.eapdbtl = hdac_command(sc, + HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid), cad); + w->param.eapdbtl &= 0x7; + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + } else + w->param.eapdbtl = HDAC_INVALID; + + switch (config & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT: + devstr = "line out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER: + devstr = "speaker"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT: + devstr = "headphones out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + devstr = "CD"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT: + devstr = "SPDIF out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT: + devstr = "digital (other) out"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE: + devstr = "modem, line side"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET: + devstr = "modem, handset side"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + devstr = "line in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_AUX: + devstr = "AUX"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + devstr = "Mic in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY: + devstr = "telephony"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN: + devstr = "SPDIF in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN: + devstr = "digital (other) in"; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER: + devstr = "other"; + break; + default: + devstr = "unknown"; + break; + } + + switch (config & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) { + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK: + connstr = "jack"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE: + connstr = "none"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED: + connstr = "fixed"; + break; + case HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH: + connstr = "jack / fixed"; + break; + default: + connstr = "unknown"; + break; + } + + strlcat(w->name, ": ", sizeof(w->name)); + strlcat(w->name, devstr, sizeof(w->name)); + strlcat(w->name, " (", sizeof(w->name)); + strlcat(w->name, connstr, sizeof(w->name)); + strlcat(w->name, ")", sizeof(w->name)); +} + +static void +hdac_widget_parse(struct hdac_widget *w) +{ + struct hdac_softc *sc = w->devinfo->codec->sc; + uint32_t wcap, cap; + char *typestr; + nid_t cad = w->devinfo->codec->cad; + nid_t nid = w->nid; + + wcap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_AUDIO_WIDGET_CAP), + cad); + w->param.widget_cap = wcap; + w->type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(wcap); + + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + typestr = "audio output"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + typestr = "audio input"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + typestr = "audio mixer"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + typestr = "audio selector"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + typestr = "pin"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET: + typestr = "power widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET: + typestr = "volume widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET: + typestr = "beep widget"; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET: + typestr = "vendor widget"; + break; + default: + typestr = "unknown type"; + break; + } + + strlcpy(w->name, typestr, sizeof(w->name)); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(wcap)) { + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), + cad); + DELAY(1000); + } + + hdac_widget_connection_parse(w); + + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.outamp_cap = + hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_OUTPUT_AMP_CAP), cad); + else + w->param.outamp_cap = + w->devinfo->function.audio.outamp_cap; + } else + w->param.outamp_cap = 0; + + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(wcap)) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(wcap)) + w->param.inamp_cap = + hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_INPUT_AMP_CAP), cad); + else + w->param.inamp_cap = + w->devinfo->function.audio.inamp_cap; + } else + w->param.inamp_cap = 0; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + if (HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(wcap)) { + cap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_SUPP_STREAM_FORMATS), cad); + w->param.supp_stream_formats = (cap != 0) ? cap : + w->devinfo->function.audio.supp_stream_formats; + cap = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, + HDA_PARAM_SUPP_PCM_SIZE_RATE), cad); + w->param.supp_pcm_size_rate = (cap != 0) ? cap : + w->devinfo->function.audio.supp_pcm_size_rate; + } else { + w->param.supp_stream_formats = + w->devinfo->function.audio.supp_stream_formats; + w->param.supp_pcm_size_rate = + w->devinfo->function.audio.supp_pcm_size_rate; + } + } else { + w->param.supp_stream_formats = 0; + w->param.supp_pcm_size_rate = 0; + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_widget_pin_parse(w); +} + +static struct hdac_widget * +hdac_widget_get(struct hdac_devinfo *devinfo, nid_t nid) +{ + if (devinfo == NULL || devinfo->widget == NULL || + nid < devinfo->startnode || nid >= devinfo->endnode) + return (NULL); + return (&devinfo->widget[nid - devinfo->startnode]); +} + +static void +hdac_stream_stop(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl &= ~(HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN); + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl &= ~(1 << (ch->off >> 5)); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); +} + +static void +hdac_stream_start(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_4(&sc->mem, HDAC_INTCTL); + ctl |= 1 << (ch->off >> 5); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, ctl); + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_IOCE | HDAC_SDCTL_FEIE | HDAC_SDCTL_DEIE | + HDAC_SDCTL_RUN; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); +} + +static void +hdac_stream_reset(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int timeout = 1000; + int to = timeout; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + ctl |= HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + do { + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + if (ctl & HDAC_SDCTL_SRST) + break; + DELAY(10); + } while (--to); + if (!(ctl & HDAC_SDCTL_SRST)) { + device_printf(sc->dev, "timeout in reset\n"); + } + ctl &= ~HDAC_SDCTL_SRST; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL0, ctl); + to = timeout; + do { + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL0); + if (!(ctl & HDAC_SDCTL_SRST)) + break; + DELAY(10); + } while (--to); + if (ctl & HDAC_SDCTL_SRST) + device_printf(sc->dev, "can't reset!\n"); +} + +static void +hdac_stream_setid(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint32_t ctl; + + ctl = HDAC_READ_1(&sc->mem, ch->off + HDAC_SDCTL2); + ctl &= ~HDAC_SDCTL2_STRM_MASK; + ctl |= ch->sid << HDAC_SDCTL2_STRM_SHIFT; + HDAC_WRITE_1(&sc->mem, ch->off + HDAC_SDCTL2, ctl); +} + +static void +hdac_bdl_setup(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + uint64_t addr; + int blks, size, blocksize; + struct hdac_bdle *bdle; + int i; + + addr = (uint64_t)sndbuf_getbufaddr(ch->b); + size = sndbuf_getsize(ch->b); + blocksize = sndbuf_getblksz(ch->b); + blks = size / blocksize; + bdle = (struct hdac_bdle*)ch->bdl_dma.dma_vaddr; + + for (i = 0; i < blks; i++, bdle++) { + bdle->addrl = (uint32_t)addr; + bdle->addrh = (uint32_t)(addr >> 32); + bdle->len = blocksize; + bdle->ioc = 1; + + addr += blocksize; + } + + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDCBL, size); + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDLVI, blks - 1); + addr = ch->bdl_dma.dma_paddr; + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPL, (uint32_t)addr); + HDAC_WRITE_4(&sc->mem, ch->off + HDAC_SDBDPU, (uint32_t)(addr >> 32)); +} + +static int +hdac_bdl_alloc(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int rc; + + rc = hdac_dma_alloc(sc, &ch->bdl_dma, + sizeof(struct hdac_bdle) * HDA_BDL_MAX); + if (rc) { + device_printf(sc->dev, "can't alloc bdl\n"); + return (rc); + } + hdac_dma_nocache(ch->bdl_dma.dma_vaddr); + + return (0); +} + +static void +hdac_audio_ctl_amp_set_internal(struct hdac_softc *sc, nid_t cad, nid_t nid, + int index, int lmute, int rmute, + int left, int right, int dir) +{ + uint16_t v = 0; + + if (sc == NULL) + return; + + if (left != right || lmute != rmute) { + v = (1 << (15 - dir)) | (1 << 13) | (index << 8) | + (lmute << 7) | left; + hdac_command(sc, + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); + v = (1 << (15 - dir)) | (1 << 12) | (index << 8) | + (rmute << 7) | right; + } else + v = (1 << (15 - dir)) | (3 << 12) | (index << 8) | + (lmute << 7) | left; + + hdac_command(sc, + HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, v), cad); +} + +static void +hdac_audio_ctl_amp_set(struct hdac_audio_ctl *ctl, uint32_t mute, + int left, int right) +{ + struct hdac_softc *sc; + nid_t nid, cad; + int lmute, rmute; + + if (ctl == NULL || ctl->widget == NULL || + ctl->widget->devinfo == NULL || + ctl->widget->devinfo->codec == NULL || + ctl->widget->devinfo->codec->sc == NULL) + return; + + sc = ctl->widget->devinfo->codec->sc; + cad = ctl->widget->devinfo->codec->cad; + nid = ctl->widget->nid; + + if (mute == HDA_AMP_MUTE_DEFAULT) { + lmute = HDA_AMP_LEFT_MUTED(ctl->muted); + rmute = HDA_AMP_RIGHT_MUTED(ctl->muted); + } else { + lmute = HDA_AMP_LEFT_MUTED(mute); + rmute = HDA_AMP_RIGHT_MUTED(mute); + } + + if (ctl->dir & HDA_CTL_OUT) + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + lmute, rmute, left, right, 0); + if (ctl->dir & HDA_CTL_IN) + hdac_audio_ctl_amp_set_internal(sc, cad, nid, ctl->index, + lmute, rmute, left, right, 1); + ctl->left = left; + ctl->right = right; +} + +static void +hdac_widget_connection_select(struct hdac_widget *w, uint8_t index) +{ + if (w == NULL || w->nconns < 1 || index > (w->nconns - 1)) + return; + hdac_command(w->devinfo->codec->sc, + HDA_CMD_SET_CONNECTION_SELECT_CONTROL(w->devinfo->codec->cad, + w->nid, index), w->devinfo->codec->cad); + w->selconn = index; +} + + +/**************************************************************************** + * uint32_t hdac_command_sendone_internal + * + * Wrapper function that sends only one command to a given codec + ****************************************************************************/ +static uint32_t +hdac_command_sendone_internal(struct hdac_softc *sc, uint32_t verb, nid_t cad) +{ + struct hdac_command_list cl; + uint32_t response = HDAC_INVALID; + + if (!hdac_lockowned(sc)) + device_printf(sc->dev, "WARNING!!!! mtx not owned!!!!\n"); + cl.num_commands = 1; + cl.verbs = &verb; + cl.responses = &response; + + hdac_command_send_internal(sc, &cl, cad); + + return (response); +} + +/**************************************************************************** + * hdac_command_send_internal + * + * Send a command list to the codec via the corb. We queue as much verbs as + * we can and msleep on the codec. When the interrupt get the responses + * back from the rirb, it will wake us up so we can queue the remaining verbs + * if any. + ****************************************************************************/ +static void +hdac_command_send_internal(struct hdac_softc *sc, + struct hdac_command_list *commands, nid_t cad) +{ + struct hdac_codec *codec; + int corbrp; + uint32_t *corb; + uint8_t rirbwp; + int timeout; + int retry = 10; + struct hdac_rirb *rirb_base, *rirb; + nid_t ucad; + uint32_t utag; + + if (sc == NULL || sc->codecs[cad] == NULL || commands == NULL) + return; + + codec = sc->codecs[cad]; + codec->commands = commands; + codec->responses_received = 0; + codec->verbs_sent = 0; + corb = (uint32_t *)sc->corb_dma.dma_vaddr; + rirb_base = (struct hdac_rirb *)sc->rirb_dma.dma_vaddr; + + do { + if (codec->verbs_sent != commands->num_commands) { + /* Queue as many verbs as possible */ + corbrp = HDAC_READ_2(&sc->mem, HDAC_CORBRP); + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_PREWRITE); + while (codec->verbs_sent != commands->num_commands && + ((sc->corb_wp + 1) % sc->corb_size) != corbrp) { + sc->corb_wp++; + sc->corb_wp %= sc->corb_size; + corb[sc->corb_wp] = + commands->verbs[codec->verbs_sent++]; + } + + /* Send the verbs to the codecs */ + bus_dmamap_sync(sc->corb_dma.dma_tag, + sc->corb_dma.dma_map, BUS_DMASYNC_POSTWRITE); + HDAC_WRITE_2(&sc->mem, HDAC_CORBWP, sc->corb_wp); + } + + timeout = 10; + do { + rirbwp = HDAC_READ_1(&sc->mem, HDAC_RIRBWP); + bus_dmamap_sync(sc->rirb_dma.dma_tag, sc->rirb_dma.dma_map, + BUS_DMASYNC_POSTREAD); + if (sc->rirb_rp != rirbwp) { + do { + sc->rirb_rp++; + sc->rirb_rp %= sc->rirb_size; + rirb = &rirb_base[sc->rirb_rp]; + if (rirb->response_ex & HDAC_RIRB_RESPONSE_EX_UNSOLICITED) { + ucad = HDAC_RIRB_RESPONSE_EX_SDATA_IN(rirb->response_ex); + utag = rirb->response >> 26; + if (ucad > -1 && ucad < HDAC_CODEC_MAX && + sc->codecs[ucad] != NULL) { + sc->unsolq[sc->unsolq_wp++] = + (ucad << 16) | + (utag & 0xffff); + sc->unsolq_wp %= HDAC_UNSOLQ_MAX; + } + } else if (codec->responses_received < commands->num_commands) + codec->commands->responses[codec->responses_received++] = + rirb->response; + } while (sc->rirb_rp != rirbwp); + break; + } + DELAY(10); + } while (--timeout); + } while ((codec->verbs_sent != commands->num_commands || + codec->responses_received != commands->num_commands) && + --retry); + + if (retry == 0) + device_printf(sc->dev, + "%s: TIMEOUT numcmd=%d, sent=%d, received=%d\n", + __func__, commands->num_commands, + codec->verbs_sent, codec->responses_received); + + codec->verbs_sent = 0; + + if (sc->unsolq_st == HDAC_UNSOLQ_READY) { + sc->unsolq_st = HDAC_UNSOLQ_BUSY; + while (sc->unsolq_rp != sc->unsolq_wp) { + ucad = sc->unsolq[sc->unsolq_rp] >> 16; + utag = sc->unsolq[sc->unsolq_rp++] & 0xffff; + sc->unsolq_rp %= HDAC_UNSOLQ_MAX; + hdac_unsolicited_handler(sc->codecs[ucad], utag); + } + sc->unsolq_st = HDAC_UNSOLQ_READY; + } +} + + +/**************************************************************************** + * Device Methods + ****************************************************************************/ + +/**************************************************************************** + * int hdac_probe(device_t) + * + * Probe for the presence of an hdac. If none is found, check for a generic + * match using the subclass of the device. + ****************************************************************************/ +static int +hdac_probe(device_t dev) +{ + int i, result; + uint32_t model; + uint16_t class, subclass; + char desc[64]; + + model = (uint32_t)pci_get_device(dev) << 16; + model |= (uint32_t)pci_get_vendor(dev) & 0x0000ffff; + class = pci_get_class(dev); + subclass = pci_get_subclass(dev); + + bzero(desc, sizeof(desc)); + result = ENXIO; + for (i = 0; i < HDAC_DEVICES_LEN; i++) { + if (hdac_devices[i].model == model) { + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = 0; + break; + } + if (HDA_DEV_MATCH(hdac_devices[i].model, model) && + class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, hdac_devices[i].desc, sizeof(desc)); + result = 0; + break; + } + } + if (result == ENXIO && class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, "Generic", sizeof(desc)); + result = 0; + } + if (result != ENXIO) { + strlcat(desc, " High Definition Audio Controller", + sizeof(desc)); + device_set_desc_copy(dev, desc); + } + + return (result); +} + +static void * +hdac_channel_init(kobj_t obj, void *data, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct hdac_devinfo *devinfo = data; + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_chan *ch; + + hdac_lock(sc); + if (dir == PCMDIR_PLAY) { + ch = &sc->play; + ch->off = (sc->num_iss + devinfo->function.audio.playcnt) << 5; + ch->dir = PCMDIR_PLAY; + ch->sid = ++sc->streamcnt; + devinfo->function.audio.playcnt++; + } else { + ch = &sc->rec; + ch->off = devinfo->function.audio.reccnt << 5; + ch->dir = PCMDIR_REC; + ch->sid = ++sc->streamcnt; + devinfo->function.audio.reccnt++; + } + if (devinfo->function.audio.quirks & HDA_QUIRK_FIXEDRATE) { + ch->caps.minspeed = ch->caps.maxspeed = 48000; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + } + ch->b = b; + ch->c = c; + ch->devinfo = devinfo; + ch->blksz = sc->chan_size / sc->chan_blkcnt; + ch->blkcnt = sc->chan_blkcnt; + hdac_unlock(sc); + + if (hdac_bdl_alloc(ch) != 0) { + ch->blkcnt = 0; + return (NULL); + } + + if (sndbuf_alloc(ch->b, sc->chan_dmat, sc->chan_size) != 0) + return (NULL); + + hdac_dma_nocache(sndbuf_getbuf(ch->b)); + + return (ch); +} + +static int +hdac_channel_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct hdac_chan *ch = data; + int i; + + for (i = 0; ch->caps.fmtlist[i] != 0; i++) { + if (format == ch->caps.fmtlist[i]) { + ch->fmt = format; + return (0); + } + } + + return (EINVAL); +} + +static int +hdac_channel_setspeed(kobj_t obj, void *data, uint32_t speed) +{ + struct hdac_chan *ch = data; + uint32_t spd = 0; + int i; + + for (i = 0; ch->pcmrates[i] != 0; i++) { + spd = ch->pcmrates[i]; + if (spd >= speed) + break; + } + + if (spd == 0) + ch->spd = 48000; + else + ch->spd = spd; + + return (ch->spd); +} + +static void +hdac_stream_setup(struct hdac_chan *ch) +{ + struct hdac_softc *sc = ch->devinfo->codec->sc; + int i; + nid_t cad = ch->devinfo->codec->cad; + uint16_t fmt; + + fmt = 0; + if (ch->fmt & AFMT_S16_LE) + fmt |= ch->bit16 << 4; + else if (ch->fmt & AFMT_S32_LE) + fmt |= ch->bit32 << 4; + else + fmt |= 1 << 4; + + for (i = 0; i < HDA_RATE_TAB_LEN; i++) { + if (hda_rate_tab[i].valid && ch->spd == hda_rate_tab[i].rate) { + fmt |= hda_rate_tab[i].base; + fmt |= hda_rate_tab[i].mul; + fmt |= hda_rate_tab[i].div; + break; + } + } + + if (ch->fmt & AFMT_STEREO) + fmt |= 1; + + HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); + + for (i = 0; ch->io[i] != -1; i++) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: PCMDIR_%s: Stream setup nid=%d " + "fmt=0x%08x\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", + ch->io[i], fmt); + ); + hdac_command(sc, + HDA_CMD_SET_CONV_FMT(cad, ch->io[i], fmt), cad); + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], + ch->sid << 4), cad); + } +} + +static int +hdac_channel_setblocksize(kobj_t obj, void *data, uint32_t blocksize) +{ + struct hdac_chan *ch = data; + + sndbuf_resize(ch->b, ch->blkcnt, ch->blksz); + + return (ch->blksz); +} + +static void +hdac_channel_stop(struct hdac_softc *sc, struct hdac_chan *ch) +{ + struct hdac_devinfo *devinfo = ch->devinfo; + nid_t cad = devinfo->codec->cad; + int i; + + hdac_stream_stop(ch); + + for (i = 0; ch->io[i] != -1; i++) { + hdac_command(sc, + HDA_CMD_SET_CONV_STREAM_CHAN(cad, ch->io[i], + 0), cad); + } +} + +static void +hdac_channel_start(struct hdac_softc *sc, struct hdac_chan *ch) +{ + ch->ptr = 0; + ch->prevptr = 0; + hdac_stream_stop(ch); + hdac_stream_reset(ch); + hdac_bdl_setup(ch); + hdac_stream_setid(ch); + hdac_stream_setup(ch); + hdac_stream_start(ch); +} + +static int +hdac_channel_trigger(kobj_t obj, void *data, int go) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + + hdac_lock(sc); + switch (go) { + case PCMTRIG_START: + hdac_channel_start(sc, ch); + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + hdac_channel_stop(sc, ch); + break; + } + hdac_unlock(sc); + + return (0); +} + +static int +hdac_channel_getptr(kobj_t obj, void *data) +{ + struct hdac_chan *ch = data; + struct hdac_softc *sc = ch->devinfo->codec->sc; + int sz, delta; + uint32_t ptr; + + hdac_lock(sc); + ptr = HDAC_READ_4(&sc->mem, ch->off + HDAC_SDLPIB); + hdac_unlock(sc); + + sz = sndbuf_getsize(ch->b); + ptr %= sz; + + if (ch->dir == PCMDIR_REC) { + delta = ptr % sndbuf_getblksz(ch->b); + if (delta != 0) { + ptr -= delta; + if (ptr < delta) + ptr = sz - delta; + else + ptr -= delta; + } + } + + return (ptr); +} + +static struct pcmchan_caps * +hdac_channel_getcaps(kobj_t obj, void *data) +{ + return (&((struct hdac_chan *)data)->caps); +} + +static kobj_method_t hdac_channel_methods[] = { + KOBJMETHOD(channel_init, hdac_channel_init), + KOBJMETHOD(channel_setformat, hdac_channel_setformat), + KOBJMETHOD(channel_setspeed, hdac_channel_setspeed), + KOBJMETHOD(channel_setblocksize, hdac_channel_setblocksize), + KOBJMETHOD(channel_trigger, hdac_channel_trigger), + KOBJMETHOD(channel_getptr, hdac_channel_getptr), + KOBJMETHOD(channel_getcaps, hdac_channel_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(hdac_channel); + +static int +hdac_audio_ctl_ossmixer_init(struct snd_mixer *m) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + uint32_t mask, recmask, id; + int i, j, softpcmvol; + nid_t cad; + + hdac_lock(sc); + + mask = 0; + recmask = 0; + + id = hdac_codec_id(devinfo); + cad = devinfo->codec->cad; + for (i = 0; i < HDAC_HP_SWITCH_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_hp_switch[i].model, + sc->pci_subvendor) && + hdac_hp_switch[i].id == id)) + continue; + w = hdac_widget_get(devinfo, hdac_hp_switch[i].hpnid); + if (w != NULL && w->enable != 0 + && w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(w->param.widget_cap)) { + hdac_command(sc, + HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, + w->nid, + HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE| + HDAC_UNSOLTAG_EVENT_HP), cad); + hdac_hp_switch_handler(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling headphone/speaker " + "audio routing switching:\n"); + device_printf(sc->dev, + "HDA_DEBUG: \tindex=%d nid=%d " + "pci_subvendor=0x%08x " + "codec=0x%08x\n", + i, w->nid, sc->pci_subvendor, id); + ); + } + break; + } + for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_eapd_switch[i].model, + sc->pci_subvendor) && + hdac_eapd_switch[i].id == id)) + continue; + w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); + if (w == NULL || w->enable == 0) + break; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) + break; + mask |= SOUND_MASK_OGAIN; + break; + } + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + mask |= w->ctlflags; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + recmask |= cw->ctlflags; + } + } + + if (!(mask & SOUND_MASK_PCM)) { + softpcmvol = 1; + mask |= SOUND_MASK_PCM; + } else + softpcmvol = (devinfo->function.audio.quirks & + HDA_QUIRK_SOFTPCMVOL) ? 1 : 0; + + i = 0; + ctl = NULL; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (!(ctl->ossmask & SOUND_MASK_PCM)) + continue; + if (ctl->step > 0) + break; + } + + if (softpcmvol == 1 || ctl == NULL) { + device_printf(sc->dev, "no softpcm support available\n"); +#if notyet + struct snddev_info *d = NULL; + d = device_get_softc(sc->dev); + if (d != NULL) { + d->flags |= SD_F_SOFTPCMVOL; + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: %s Soft PCM volume\n", + (softpcmvol == 1) ? + "Forcing" : "Enabling"); + ); + } + i = 0; + /* + * XXX Temporary quirk for STAC9220, until the parser + * become smarter. + */ + if (id == HDA_CODEC_STAC9220) { + mask |= SOUND_MASK_VOLUME; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (ctl->widget->nid == 11 && ctl->index == 0) { + ctl->ossmask = SOUND_MASK_VOLUME; + ctl->ossval = 100 | (100 << 8); + } else + ctl->ossmask &= ~SOUND_MASK_VOLUME; + } + } else { + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM); + if (!(mask & SOUND_MASK_VOLUME)) + mix_setrealdev(m, SOUND_MIXER_VOLUME, + SOUND_MIXER_NONE); + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != + NULL) { + if (ctl->widget == NULL || ctl->enable == 0) + continue; + if (!HDA_FLAG_MATCH(ctl->ossmask, + SOUND_MASK_VOLUME | SOUND_MASK_PCM)) + continue; + if (!(ctl->mute == 1 && ctl->step == 0)) + ctl->enable = 0; + } + } +#endif + } + + recmask &= ~(SOUND_MASK_PCM | SOUND_MASK_RECLEV | SOUND_MASK_SPEAKER); + + mix_setrecdevs(m, recmask); + mix_setdevs(m, mask); + + hdac_unlock(sc); + + return (0); +} + +static int +hdac_audio_ctl_ossmixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t id, mute; + int lvol, rvol, mlvol, mrvol; + int i = 0; + + hdac_lock(sc); + if (dev == SOUND_MIXER_OGAIN) { + uint32_t orig; + /*if (left != right || !(left == 0 || left == 1)) { + hdac_unlock(sc); + return (-1); + }*/ + id = hdac_codec_id(devinfo); + for (i = 0; i < HDAC_EAPD_SWITCH_LEN; i++) { + if (HDA_DEV_MATCH(hdac_eapd_switch[i].model, + sc->pci_subvendor) && + hdac_eapd_switch[i].id == id) + break; + } + if (i >= HDAC_EAPD_SWITCH_LEN) { + hdac_unlock(sc); + return (-1); + } + w = hdac_widget_get(devinfo, hdac_eapd_switch[i].eapdnid); + if (w == NULL || + w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + w->param.eapdbtl == HDAC_INVALID) { + hdac_unlock(sc); + return (-1); + } + orig = w->param.eapdbtl; + if (left == 0) + w->param.eapdbtl &= ~HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + else + w->param.eapdbtl |= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + if (orig != w->param.eapdbtl) { + if (hdac_eapd_switch[i].hp_switch != 0) + hdac_hp_switch_handler(devinfo); + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, + w->nid, w->param.eapdbtl), devinfo->codec->cad); + } + hdac_unlock(sc); + return (left | (left << 8)); + } + if (dev == SOUND_MIXER_VOLUME) + devinfo->function.audio.mvol = left | (right << 8); + + mlvol = devinfo->function.audio.mvol & 0x7f; + mrvol = (devinfo->function.audio.mvol >> 8) & 0x7f; + lvol = 0; + rvol = 0; + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL || ctl->enable == 0 || + !(ctl->ossmask & (1 << dev))) + continue; + switch (dev) { + case SOUND_MIXER_VOLUME: + lvol = ((ctl->ossval & 0x7f) * left) / 100; + lvol = (lvol * ctl->step) / 100; + rvol = (((ctl->ossval >> 8) & 0x7f) * right) / 100; + rvol = (rvol * ctl->step) / 100; + break; + default: + if (ctl->ossmask & SOUND_MASK_VOLUME) { + lvol = (left * mlvol) / 100; + lvol = (lvol * ctl->step) / 100; + rvol = (right * mrvol) / 100; + rvol = (rvol * ctl->step) / 100; + } else { + lvol = (left * ctl->step) / 100; + rvol = (right * ctl->step) / 100; + } + ctl->ossval = left | (right << 8); + break; + } + mute = 0; + if (ctl->step < 1) { + mute |= (left == 0) ? HDA_AMP_MUTE_LEFT : + (ctl->muted & HDA_AMP_MUTE_LEFT); + mute |= (right == 0) ? HDA_AMP_MUTE_RIGHT : + (ctl->muted & HDA_AMP_MUTE_RIGHT); + } else { + mute |= (lvol == 0) ? HDA_AMP_MUTE_LEFT : + (ctl->muted & HDA_AMP_MUTE_LEFT); + mute |= (rvol == 0) ? HDA_AMP_MUTE_RIGHT : + (ctl->muted & HDA_AMP_MUTE_RIGHT); + } + hdac_audio_ctl_amp_set(ctl, mute, lvol, rvol); + } + hdac_unlock(sc); + + return (left | (right << 8)); +} + +static int +hdac_audio_ctl_ossmixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + struct hdac_devinfo *devinfo = mix_getdevinfo(m); + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + uint32_t ret = src, target; + int i, j; + + target = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (src & (1 << i)) { + target = 1 << i; + break; + } + } + + hdac_lock(sc); + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if ((target == SOUND_MASK_VOLUME && + cw->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) || + (target != SOUND_MASK_VOLUME && + cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER)) + continue; + if (cw->ctlflags & target) { + hdac_widget_connection_select(w, j); + ret = target; + j += w->nconns; + } + } + } + + hdac_unlock(sc); + + return (ret); +} + +static kobj_method_t hdac_audio_ctl_ossmixer_methods[] = { + KOBJMETHOD(mixer_init, hdac_audio_ctl_ossmixer_init), + KOBJMETHOD(mixer_set, hdac_audio_ctl_ossmixer_set), + KOBJMETHOD(mixer_setrecsrc, hdac_audio_ctl_ossmixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(hdac_audio_ctl_ossmixer); + +/**************************************************************************** + * int hdac_attach(device_t) + * + * Attach the device into the kernel. Interrupts usually won't be enabled + * when this function is called. Setup everything that doesn't require + * interrupts and defer probing of codecs until interrupts are enabled. + ****************************************************************************/ +static int +hdac_attach(device_t dev) +{ + struct hdac_softc *sc; + int result; + int i = 0; + + sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO); + if (sc == NULL) { + device_printf(dev, "cannot allocate softc\n"); + return (ENOMEM); + } + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), HDAC_MTX_NAME); + if (sc->lock == NULL) { + device_printf(dev, "mutex creation failed\n"); + kfree(sc, M_DEVBUF); + return (ENOMEM); + } + + sc->dev = dev; + sc->pci_subvendor = (uint32_t)pci_get_subdevice(sc->dev) << 16; + sc->pci_subvendor |= (uint32_t)pci_get_subvendor(sc->dev) & 0x0000ffff; + + sc->chan_size = pcm_getbuffersize(dev, + HDA_BUFSZ_MIN, HDA_BUFSZ_DEFAULT, HDA_BUFSZ_DEFAULT); + if (resource_int_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "blocksize", &i) == 0 && + i > 0) { + sc->chan_blkcnt = sc->chan_size / i; + i = 0; + while (sc->chan_blkcnt >> i) + i++; + sc->chan_blkcnt = 1 << (i - 1); + if (sc->chan_blkcnt < HDA_BDL_MIN) + sc->chan_blkcnt = HDA_BDL_MIN; + else if (sc->chan_blkcnt > HDA_BDL_MAX) + sc->chan_blkcnt = HDA_BDL_MAX; + } else + sc->chan_blkcnt = HDA_BDL_DEFAULT; + + result = bus_dma_tag_create(NULL, /* parent */ + HDAC_DMA_ALIGNMENT, /* alignment */ + 0, /* boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, /* filtfunc */ + NULL, /* fistfuncarg */ + sc->chan_size, /* maxsize */ + 1, /* nsegments */ + sc->chan_size, /* maxsegsz */ + 0, /* flags */ + &sc->chan_dmat); /* dmat */ + if (result != 0) { + device_printf(sc->dev, "%s: bus_dma_tag_create failed (%x)\n", + __func__, result); + snd_mtxfree(sc->lock); + kfree(sc, M_DEVBUF); + return (ENXIO); + } + + + sc->hdabus = NULL; + for (i = 0; i < HDAC_CODEC_MAX; i++) + sc->codecs[i] = NULL; + + pci_enable_busmaster(dev); + + /* Allocate resources */ + result = hdac_mem_alloc(sc); + if (result != 0) + goto hdac_attach_fail; + result = hdac_irq_alloc(sc); + if (result != 0) + goto hdac_attach_fail; + + /* Get Capabilities */ + result = hdac_get_capabilities(sc); + if (result != 0) + goto hdac_attach_fail; + + /* Allocate CORB and RIRB dma memory */ + result = hdac_dma_alloc(sc, &sc->corb_dma, + sc->corb_size * sizeof(uint32_t)); + if (result != 0) + goto hdac_attach_fail; + result = hdac_dma_alloc(sc, &sc->rirb_dma, + sc->rirb_size * sizeof(struct hdac_rirb)); + if (result != 0) + goto hdac_attach_fail; + + /* Quiesce everything */ + hdac_reset(sc); + + /* Disable PCI-Express QOS */ + pci_write_config(sc->dev, 0x44, + pci_read_config(sc->dev, 0x44, 1) & 0xf8, 1); + + /* Initialize the CORB and RIRB */ + hdac_corb_init(sc); + hdac_rirb_init(sc); + + /* Defer remaining of initialization until interrupts are enabled */ + sc->intrhook.ich_func = hdac_attach2; + sc->intrhook.ich_arg = (void *)sc; + if (cold == 0 || config_intrhook_establish(&sc->intrhook) != 0) { + sc->intrhook.ich_func = NULL; + hdac_attach2((void *)sc); + } + + return (0); + +hdac_attach_fail: + hdac_dma_free(&sc->rirb_dma); + hdac_dma_free(&sc->corb_dma); + hdac_irq_free(sc); + hdac_mem_free(sc); + snd_mtxfree(sc->lock); + kfree(sc, M_DEVBUF); + + return (ENXIO); +} + +static void +hdac_audio_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + uint32_t res; + int i; + nid_t cad, nid; + + cad = devinfo->codec->cad; + nid = devinfo->nid; + + hdac_command(sc, + HDA_CMD_SET_POWER_STATE(cad, nid, HDA_CMD_POWER_STATE_D0), cad); + + DELAY(100); + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad , nid, HDA_PARAM_SUB_NODE_COUNT), cad); + + devinfo->nodecnt = HDA_PARAM_SUB_NODE_COUNT_TOTAL(res); + devinfo->startnode = HDA_PARAM_SUB_NODE_COUNT_START(res); + devinfo->endnode = devinfo->startnode + devinfo->nodecnt; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, " Vendor: 0x%08x\n", + devinfo->vendor_id); + device_printf(sc->dev, " Device: 0x%08x\n", + devinfo->device_id); + device_printf(sc->dev, " Revision: 0x%08x\n", + devinfo->revision_id); + device_printf(sc->dev, " Stepping: 0x%08x\n", + devinfo->stepping_id); + device_printf(sc->dev, "PCI Subvendor: 0x%08x\n", + sc->pci_subvendor); + device_printf(sc->dev, " Nodes: start=%d " + "endnode=%d total=%d\n", + devinfo->startnode, devinfo->endnode, devinfo->nodecnt); + ); + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_STREAM_FORMATS), + cad); + devinfo->function.audio.supp_stream_formats = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_SUPP_PCM_SIZE_RATE), + cad); + devinfo->function.audio.supp_pcm_size_rate = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_OUTPUT_AMP_CAP), + cad); + devinfo->function.audio.outamp_cap = res; + + res = hdac_command(sc, + HDA_CMD_GET_PARAMETER(cad, nid, HDA_PARAM_INPUT_AMP_CAP), + cad); + devinfo->function.audio.inamp_cap = res; + + if (devinfo->nodecnt > 0) { + hdac_unlock(sc); + devinfo->widget = (struct hdac_widget *)kmalloc( + sizeof(*(devinfo->widget)) * devinfo->nodecnt, M_HDAC, + M_NOWAIT | M_ZERO); + hdac_lock(sc); + } else + devinfo->widget = NULL; + + if (devinfo->widget == NULL) { + device_printf(sc->dev, "unable to allocate widgets!\n"); + devinfo->endnode = devinfo->startnode; + devinfo->nodecnt = 0; + return; + } + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + device_printf(sc->dev, "Ghost widget! nid=%d!\n", i); + else { + w->devinfo = devinfo; + w->nid = i; + w->enable = 1; + w->selconn = -1; + w->pflags = 0; + w->ctlflags = 0; + w->param.eapdbtl = HDAC_INVALID; + hdac_widget_parse(w); + } + } +} + +static void +hdac_audio_ctl_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_ctl *ctls; + struct hdac_widget *w, *cw; + int i, j, cnt, max, ocap, icap; + int mute, offset, step, size; + + /* XXX This is redundant */ + max = 0; + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->param.outamp_cap != 0) + max++; + if (w->param.inamp_cap != 0) { + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + max++; + } + break; + default: + max++; + break; + } + } + } + + devinfo->function.audio.ctlcnt = max; + + if (max < 1) + return; + + hdac_unlock(sc); + ctls = (struct hdac_audio_ctl *)kmalloc( + sizeof(*ctls) * max, M_HDAC, M_ZERO | M_NOWAIT); + hdac_lock(sc); + + if (ctls == NULL) { + /* Blekh! */ + device_printf(sc->dev, "unable to allocate ctls!\n"); + devinfo->function.audio.ctlcnt = 0; + return; + } + + cnt = 0; + for (i = devinfo->startnode; cnt < max && i < devinfo->endnode; i++) { + if (cnt >= max) { + device_printf(sc->dev, "%s: Ctl overflow!\n", + __func__); + break; + } + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + ocap = w->param.outamp_cap; + icap = w->param.inamp_cap; + if (ocap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(ocap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(ocap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(ocap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(ocap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: BUGGY outamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_OUT; + } + + if (icap != 0) { + mute = HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(icap); + step = HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(icap); + size = HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(icap); + offset = HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(icap); + /*if (offset > step) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: BUGGY inamp: nid=%d " + "[offset=%d > step=%d]\n", + w->nid, offset, step); + ); + offset = step; + }*/ + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (j = 0; j < w->nconns; j++) { + if (cnt >= max) { + device_printf(sc->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + cw = hdac_widget_get(devinfo, + w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].childwidget = cw; + ctls[cnt].index = j; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_IN; + } + break; + default: + if (cnt >= max) { + device_printf(sc->dev, + "%s: Ctl overflow!\n", + __func__); + break; + } + ctls[cnt].enable = 1; + ctls[cnt].widget = w; + ctls[cnt].mute = mute; + ctls[cnt].step = step; + ctls[cnt].size = size; + ctls[cnt].offset = offset; + ctls[cnt].left = offset; + ctls[cnt].right = offset; + ctls[cnt++].dir = HDA_CTL_IN; + break; + } + } + } + + devinfo->function.audio.ctl = ctls; +} + +static const struct { + uint32_t model; + uint32_t id; + uint32_t set, unset; +} hdac_quirks[] = { + /* + * XXX Fixed rate quirk. Other than 48000 + * sounds pretty much like train wreck. + * + * XXX Force stereo quirk. Monoural recording / playback + * on few codecs (especially ALC880) seems broken or + * perhaps unsupported. + */ + { HDA_MATCH_ALL, HDA_MATCH_ALL, + HDA_QUIRK_FIXEDRATE | HDA_QUIRK_FORCESTEREO, 0 }, + { ACER_ALL_SUBVENDOR, HDA_MATCH_ALL, + HDA_QUIRK_GPIO1, 0 }, + { ASUS_M5200_SUBVENDOR, HDA_CODEC_ALC880, + HDA_QUIRK_GPIO1, 0 }, + { HDA_MATCH_ALL, HDA_CODEC_CXVENICE, + 0, HDA_QUIRK_FORCESTEREO }, + { HDA_MATCH_ALL, HDA_CODEC_STACXXXX, + HDA_QUIRK_SOFTPCMVOL, 0 } +}; +#define HDAC_QUIRKS_LEN (sizeof(hdac_quirks) / sizeof(hdac_quirks[0])) + +static void +hdac_vendor_patch_parse(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + uint32_t id, subvendor; + int i; + + id = hdac_codec_id(devinfo); + subvendor = devinfo->codec->sc->pci_subvendor; + + /* + * Quirks + */ + for (i = 0; i < HDAC_QUIRKS_LEN; i++) { + if (!(HDA_DEV_MATCH(hdac_quirks[i].model, subvendor) && + HDA_DEV_MATCH(hdac_quirks[i].id, id))) + continue; + if (hdac_quirks[i].set != 0) + devinfo->function.audio.quirks |= + hdac_quirks[i].set; + if (hdac_quirks[i].unset != 0) + devinfo->function.audio.quirks &= + ~(hdac_quirks[i].unset); + } + + switch (id) { + case HDA_CODEC_ALC260: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + if (w->nid != 5) + w->enable = 0; + } + break; + case HDA_CODEC_ALC880: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->nid != 9 && w->nid != 29) { + w->enable = 0; + } else if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET && + w->nid == 29) { + w->type = + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET; + w->param.widget_cap &= + ~HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK; + w->param.widget_cap |= + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET << + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT; + strlcpy(w->name, "beep widget", sizeof(w->name)); + } + } + break; + case HDA_CODEC_AD1981HD: + w = hdac_widget_get(devinfo, 11); + if (w != NULL && w->enable != 0 && w->nconns > 3) + w->selconn = 3; + if (subvendor == IBM_M52_SUBVENDOR) { + struct hdac_audio_ctl *ctl; + + ctl = hdac_audio_ctl_amp_get(devinfo, 7, 0, 1); + if (ctl != NULL) + ctl->ossmask = SOUND_MASK_SPEAKER; + } + break; + case HDA_CODEC_AD1986A: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (w->nid != 3) + w->enable = 0; + } + break; + case HDA_CODEC_STAC9221: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) + continue; + if (w->nid != 2) + w->enable = 0; + } + break; + case HDA_CODEC_STAC9221D: + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->nid != 6) + w->enable = 0; + + } + break; + default: + break; + } +} + +static int +hdac_audio_ctl_ossmixer_getnextdev(struct hdac_devinfo *devinfo) +{ + int *dev = &devinfo->function.audio.ossidx; + + while (*dev < SOUND_MIXER_NRDEVICES) { + switch (*dev) { + case SOUND_MIXER_VOLUME: + case SOUND_MIXER_BASS: + case SOUND_MIXER_TREBLE: + case SOUND_MIXER_PCM: + case SOUND_MIXER_SPEAKER: + case SOUND_MIXER_LINE: + case SOUND_MIXER_MIC: + case SOUND_MIXER_CD: + case SOUND_MIXER_RECLEV: + case SOUND_MIXER_OGAIN: /* reserved for EAPD switch */ + (*dev)++; + break; + default: + return (*dev)++; + break; + } + } + + return (-1); +} + +static int +hdac_widget_find_dac_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w; + int i, ret = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT: + w->pflags |= HDA_DAC_PATH; + ret = 1; + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + for (i = 0; i < w->nconns; i++) { + if (hdac_widget_find_dac_path(devinfo, + w->conns[i], depth + 1) != 0) { + if (w->selconn == -1) + w->selconn = i; + ret = 1; + w->pflags |= HDA_DAC_PATH; + } + } + break; + default: + break; + } + return (ret); +} + +static int +hdac_widget_find_adc_path(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w; + int i, conndev, ret = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + switch (w->type) { + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR: + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + for (i = 0; i < w->nconns; i++) { + if (hdac_widget_find_adc_path(devinfo, w->conns[i], + depth + 1) != 0) { + if (w->selconn == -1) + w->selconn = i; + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + } + break; + case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX: + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + (conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_CD || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN)) { + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + break; + /*case HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER: + if (w->pflags & HDA_DAC_PATH) { + w->pflags |= HDA_ADC_PATH; + ret = 1; + } + break;*/ + default: + break; + } + return (ret); +} + +static uint32_t +hdac_audio_ctl_outamp_build(struct hdac_devinfo *devinfo, + nid_t nid, nid_t pnid, int index, int depth) +{ + struct hdac_widget *w, *pw; + struct hdac_audio_ctl *ctl; + uint32_t fl = 0; + int i, ossdev, conndev, strategy; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + + pw = hdac_widget_get(devinfo, pnid); + strategy = devinfo->function.audio.parsing_strategy; + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER + || w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) { + for (i = 0; i < w->nconns; i++) { + fl |= hdac_audio_ctl_outamp_build(devinfo, w->conns[i], + w->nid, i, depth + 1); + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT && + (w->pflags & HDA_DAC_PATH)) { + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if ((ctl->widget->nid == w->nid) || + (ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) { + /*if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_PCM; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_PCM; + ctl->ossdev = SOUND_MIXER_PCM;*/ + if (!(w->ctlflags & SOUND_MASK_PCM) || + (pw != NULL && + !(pw->ctlflags & SOUND_MASK_PCM))) { + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_PCM; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_PCM; + ctl->ossdev = SOUND_MIXER_PCM; + w->ctlflags |= SOUND_MASK_VOLUME; + w->ctlflags |= SOUND_MASK_PCM; + if (pw != NULL) { + if (pw->selconn == -1) + pw->selconn = index; + pw->ctlflags |= + SOUND_MASK_VOLUME; + pw->ctlflags |= + SOUND_MASK_PCM; + } + } + } + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX + && HDA_PARAM_PIN_CAP_INPUT_CAP(w->wclass.pin.cap) && + (w->pflags & HDA_ADC_PATH)) { + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + ossdev = 0; + switch (conndev) { + case HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN: + ossdev = SOUND_MIXER_MIC; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN: + ossdev = SOUND_MIXER_LINE; + break; + case HDA_CONFIG_DEFAULTCONF_DEVICE_CD: + ossdev = SOUND_MIXER_CD; + break; + default: + ossdev = + hdac_audio_ctl_ossmixer_getnextdev( + devinfo); + if (ossdev < 0) + ossdev = 0; + break; + } + if (strategy == HDA_PARSE_MIXER) { + fl |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_VOLUME; + } + fl |= 1 << ossdev; + ctl->ossmask |= 1 << ossdev; + ctl->ossdev = ossdev; + } + } + w->ctlflags |= fl; + return (fl); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + /* XXX This should be compressed! */ + if (((ctl->widget->nid == pnid && ctl->index == index && + (ctl->dir & HDA_CTL_IN)) || + (ctl->widget->nid == pnid && pw != NULL && + pw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR && + (pw->nconns < 2 || pw->selconn == index || + pw->selconn == -1) && + (ctl->dir & HDA_CTL_OUT)) || + (strategy == HDA_PARSE_DIRECT && + ctl->widget->nid == w->nid)) && + !(ctl->ossmask & ~SOUND_MASK_VOLUME)) { + if (pw != NULL && pw->selconn == -1) + pw->selconn = index; + fl |= SOUND_MASK_VOLUME; + fl |= SOUND_MASK_SPEAKER; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_SPEAKER; + ctl->ossdev = SOUND_MIXER_SPEAKER; + } + } + w->ctlflags |= fl; + return (fl); + } + return (0); +} + +static uint32_t +hdac_audio_ctl_inamp_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + struct hdac_audio_ctl *ctl; + uint32_t fl; + int i; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + /*if (!(w->pflags & HDA_ADC_PATH)) + return (0); + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + return (0);*/ + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (ctl->widget->nid == nid) { + ctl->ossmask |= SOUND_MASK_RECLEV; + w->ctlflags |= SOUND_MASK_RECLEV; + return (SOUND_MASK_RECLEV); + } + } + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0) + continue; + if (cw->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + continue; + fl = hdac_audio_ctl_inamp_build(devinfo, cw->nid, depth + 1); + if (fl != 0) { + cw->ctlflags |= fl; + w->ctlflags |= fl; + return (fl); + } + } + return (0); +} + +static int +hdac_audio_ctl_recsel_build(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + int i, child = 0; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0) + return (0); + /*if (!(w->pflags & HDA_ADC_PATH)) + return (0); + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + return (0);*/ + /* XXX weak! */ + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL) + continue; + if (++child > 1) { + w->pflags |= HDA_ADC_RECSEL; + return (1); + } + } + for (i = 0; i < w->nconns; i++) { + if (hdac_audio_ctl_recsel_build(devinfo, + w->conns[i], depth + 1) != 0) + return (1); + } + return (0); +} + +static int +hdac_audio_build_tree_strategy(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + int i, j, conndev, found_dac = 0; + int strategy; + + strategy = devinfo->function.audio.parsing_strategy; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (!HDA_PARAM_PIN_CAP_OUTPUT_CAP(w->wclass.pin.cap)) + continue; + conndev = w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK; + if (!(conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER || + conndev == HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT)) + continue; + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + if (strategy == HDA_PARSE_MIXER && !(cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR)) + continue; + if (hdac_widget_find_dac_path(devinfo, cw->nid, 0) + != 0) { + if (w->selconn == -1) + w->selconn = j; + w->pflags |= HDA_DAC_PATH; + found_dac++; + } + } + } + + return (found_dac); +} + +static void +hdac_audio_build_tree(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + int i, j, dacs, strategy; + + /* Construct DAC path */ + strategy = HDA_PARSE_MIXER; + devinfo->function.audio.parsing_strategy = strategy; + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: HDA Widget Parser - Revision %d\n", + HDA_WIDGET_PARSER_REV); + ); + dacs = hdac_audio_build_tree_strategy(devinfo); + if (dacs == 0) { + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: 0 DAC path found! " + "Retrying parser " + "using HDA_PARSE_DIRECT strategy.\n"); + ); + strategy = HDA_PARSE_DIRECT; + devinfo->function.audio.parsing_strategy = strategy; + dacs = hdac_audio_build_tree_strategy(devinfo); + } + + HDA_BOOTVERBOSE( + device_printf(devinfo->codec->sc->dev, + "HDA_DEBUG: HWiP: Found %d DAC path using HDA_PARSE_%s " + "strategy.\n", + dacs, (strategy == HDA_PARSE_MIXER) ? "MIXER" : "DIRECT"); + ); + + /* Construct ADC path */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) + continue; + (void)hdac_widget_find_adc_path(devinfo, w->nid, 0); + } + + /* Output mixers */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if ((strategy == HDA_PARSE_MIXER && + (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR) + && (w->pflags & HDA_DAC_PATH)) || + (strategy == HDA_PARSE_DIRECT && (w->pflags & + (HDA_DAC_PATH | HDA_ADC_PATH)))) { + w->ctlflags |= hdac_audio_ctl_outamp_build(devinfo, + w->nid, devinfo->startnode - 1, 0, 0); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET) { + j = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &j)) != + NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) + continue; + if (ctl->widget->nid != w->nid) + continue; + ctl->ossmask |= SOUND_MASK_VOLUME; + ctl->ossmask |= SOUND_MASK_SPEAKER; + ctl->ossdev = SOUND_MIXER_SPEAKER; + w->ctlflags |= SOUND_MASK_VOLUME; + w->ctlflags |= SOUND_MASK_SPEAKER; + } + } + } + + /* Input mixers (rec) */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT && + w->pflags & HDA_ADC_PATH)) + continue; + hdac_audio_ctl_inamp_build(devinfo, w->nid, 0); + hdac_audio_ctl_recsel_build(devinfo, w->nid, 0); + } +} + +#define HDA_COMMIT_CONN (1 << 0) +#define HDA_COMMIT_CTRL (1 << 1) +#define HDA_COMMIT_EAPD (1 << 2) +#define HDA_COMMIT_GPIO (1 << 3) +#define HDA_COMMIT_ALL (HDA_COMMIT_CONN | HDA_COMMIT_CTRL | \ + HDA_COMMIT_EAPD | HDA_COMMIT_GPIO) + +static void +hdac_audio_commit(struct hdac_devinfo *devinfo, uint32_t cfl) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w; + nid_t cad, nid; + int i, gpioval; + + if (!(cfl & HDA_COMMIT_ALL)) + return; + + cad = devinfo->codec->cad; + + if (cfl & HDA_COMMIT_GPIO) { + nid = devinfo->nid; + for (i = 0; i < HDA_GPIO_MAX; i++) { + if (!(devinfo->function.audio.quirks & (1 << i))) + continue; + gpioval = (1 << i) - 1; + hdac_command(sc, + HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, gpioval), + cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DIRECTION(cad, nid, gpioval), + cad); + hdac_command(sc, + HDA_CMD_SET_GPIO_DATA(cad, nid, gpioval), + cad); + } + } + + for (i = 0; i < devinfo->nodecnt; i++) { + w = &devinfo->widget[i]; + if (w == NULL || w->enable == 0) + continue; + if (cfl & HDA_COMMIT_CONN) { + if (w->selconn == -1) + w->selconn = 0; + if (w->nconns > 0) + hdac_widget_connection_select(w, w->selconn); + } + if ((cfl & HDA_COMMIT_CTRL) && + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + if ((w->pflags & (HDA_DAC_PATH | HDA_ADC_PATH)) == + (HDA_DAC_PATH | HDA_ADC_PATH)) + device_printf(sc->dev, "WARNING: node %d " + "participate both for DAC/ADC!\n", w->nid); + if (w->pflags & HDA_DAC_PATH) { + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE; + if ((w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) != + HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT) + w->wclass.pin.ctrl &= + ~HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE; + } else if (w->pflags & HDA_ADC_PATH) { + w->wclass.pin.ctrl &= + ~(HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE); + } else + w->wclass.pin.ctrl &= ~( + HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE | + HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE); + hdac_command(sc, + HDA_CMD_SET_PIN_WIDGET_CTRL(cad, w->nid, + w->wclass.pin.ctrl), cad); + } + if ((cfl & HDA_COMMIT_EAPD) && + w->param.eapdbtl != HDAC_INVALID) + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, + w->param.eapdbtl), cad); + + DELAY(1000); + } +} + +static void +hdac_audio_ctl_commit(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_audio_ctl *ctl; + int i; + + devinfo->function.audio.mvol = 100 | (100 << 8); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL) { + HDA_BOOTVERBOSE( + device_printf(sc->dev, "[%2d] Ctl nid=%d", + i, (ctl->widget != NULL) ? + ctl->widget->nid : -1); + if (ctl->childwidget != NULL) + kprintf(" childnid=%d", + ctl->childwidget->nid); + if (ctl->widget == NULL) + kprintf(" NULL WIDGET!"); + kprintf(" DISABLED\n"); + ); + continue; + } + HDA_BOOTVERBOSE( + if (ctl->ossmask == 0) { + device_printf(sc->dev, "[%2d] Ctl nid=%d", + i, ctl->widget->nid); + if (ctl->childwidget != NULL) + kprintf(" childnid=%d", + ctl->childwidget->nid); + kprintf(" Bind to NONE\n"); + } + ); + if (ctl->step > 0) { + ctl->ossval = (ctl->left * 100) / ctl->step; + ctl->ossval |= ((ctl->right * 100) / ctl->step) << 8; + } else + ctl->ossval = 0; + hdac_audio_ctl_amp_set(ctl, HDA_AMP_MUTE_DEFAULT, + ctl->left, ctl->right); + } +} + +static int +hdac_pcmchannel_setup(struct hdac_devinfo *devinfo, int dir) +{ + struct hdac_chan *ch; + struct hdac_widget *w; + uint32_t cap, fmtcap, pcmcap, path; + int i, type, ret, max; + + if (dir == PCMDIR_PLAY) { + type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT; + ch = &devinfo->codec->sc->play; + path = HDA_DAC_PATH; + } else { + type = HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT; + ch = &devinfo->codec->sc->rec; + path = HDA_ADC_PATH; + } + + ch->caps = hdac_caps; + ch->caps.fmtlist = ch->fmtlist; + ch->bit16 = 1; + ch->bit32 = 0; + ch->pcmrates[0] = 48000; + ch->pcmrates[1] = 0; + + ret = 0; + fmtcap = devinfo->function.audio.supp_stream_formats; + pcmcap = devinfo->function.audio.supp_pcm_size_rate; + max = (sizeof(ch->io) / sizeof(ch->io[0])) - 1; + + for (i = devinfo->startnode; i < devinfo->endnode && ret < max; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0 || w->type != type || + !(w->pflags & path)) + continue; + cap = w->param.widget_cap; + /*if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(cap)) + continue;*/ + if (!HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(cap)) + continue; + cap = w->param.supp_stream_formats; + /*if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) { + } + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) { + }*/ + if (!HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + continue; + ch->io[ret++] = i; + fmtcap &= w->param.supp_stream_formats; + pcmcap &= w->param.supp_pcm_size_rate; + } + ch->io[ret] = -1; + + ch->supp_stream_formats = fmtcap; + ch->supp_pcm_size_rate = pcmcap; + + /* + * 8bit = 0 + * 16bit = 1 + * 20bit = 2 + * 24bit = 3 + * 32bit = 4 + */ + if (ret > 0) { + cap = pcmcap; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + ch->bit16 = 1; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + ch->bit16 = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + ch->bit32 = 4; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + ch->bit32 = 3; + else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + ch->bit32 = 2; + i = 0; + if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S16_LE; + ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; + if (ch->bit32 > 0) { + if (!(devinfo->function.audio.quirks & + HDA_QUIRK_FORCESTEREO)) + ch->fmtlist[i++] = AFMT_S32_LE; + ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + } + ch->fmtlist[i] = 0; + i = 0; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + ch->pcmrates[i++] = 8000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + ch->pcmrates[i++] = 11025; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + ch->pcmrates[i++] = 16000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + ch->pcmrates[i++] = 22050; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + ch->pcmrates[i++] = 32000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + ch->pcmrates[i++] = 44100; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(cap)) */ + ch->pcmrates[i++] = 48000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + ch->pcmrates[i++] = 88200; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + ch->pcmrates[i++] = 96000; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + ch->pcmrates[i++] = 176400; + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + ch->pcmrates[i++] = 192000; + /* if (HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(cap)) */ + ch->pcmrates[i] = 0; + if (i > 0) { + ch->caps.minspeed = ch->pcmrates[0]; + ch->caps.maxspeed = ch->pcmrates[i - 1]; + } + } + + return (ret); +} + +static void +hdac_dump_ctls(struct hdac_devinfo *devinfo, const char *banner, uint32_t flag) +{ + struct hdac_audio_ctl *ctl; + struct hdac_softc *sc = devinfo->codec->sc; + int i; + uint32_t fl = 0; + + + if (flag == 0) { + fl = SOUND_MASK_VOLUME | SOUND_MASK_PCM | + SOUND_MASK_CD | SOUND_MASK_LINE | SOUND_MASK_RECLEV | + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_OGAIN; + } + + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL || + ctl->widget->enable == 0) + continue; + if ((flag == 0 && (ctl->ossmask & ~fl)) || + (flag != 0 && (ctl->ossmask & flag))) { + if (banner != NULL) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "%s\n", banner); + } + goto hdac_ctl_dump_it_all; + } + } + + return; + +hdac_ctl_dump_it_all: + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->enable == 0 || ctl->widget == NULL || + ctl->widget->enable == 0) + continue; + if (!((flag == 0 && (ctl->ossmask & ~fl)) || + (flag != 0 && (ctl->ossmask & flag)))) + continue; + if (flag == 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Unknown Ctl (OSS: %s)\n", + hdac_audio_ctl_ossmixer_mask2name(ctl->ossmask)); + } + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " +- nid: %2d index: %2d ", + ctl->widget->nid, ctl->index); + if (ctl->childwidget != NULL) + kprintf("(nid: %2d) ", ctl->childwidget->nid); + else + kprintf(" "); + kprintf("mute: %d step: %3d size: %3d off: %3d dir=0x%x ossmask=0x%08x\n", + ctl->mute, ctl->step, ctl->size, ctl->offset, ctl->dir, + ctl->ossmask); + } +} + +static void +hdac_dump_audio_formats(struct hdac_softc *sc, uint32_t fcap, uint32_t pcmcap) +{ + uint32_t cap; + + cap = fcap; + if (cap != 0) { + device_printf(sc->dev, " Stream cap: 0x%08x\n", cap); + device_printf(sc->dev, " Format:"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(cap)) + kprintf(" AC3"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) + kprintf(" FLOAT32"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + kprintf(" PCM"); + kprintf("\n"); + } + cap = pcmcap; + if (cap != 0) { + device_printf(sc->dev, " PCM cap: 0x%08x\n", cap); + device_printf(sc->dev, " PCM size:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(cap)) + kprintf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + kprintf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + kprintf(" 20"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + kprintf(" 24"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + kprintf(" 32"); + kprintf("\n"); + device_printf(sc->dev, " PCM rate:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + kprintf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + kprintf(" 11"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + kprintf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + kprintf(" 22"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + kprintf(" 32"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + kprintf(" 44"); + kprintf(" 48"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + kprintf(" 88"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + kprintf(" 96"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + kprintf(" 176"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + kprintf(" 192"); + kprintf("\n"); + } +} + +static void +hdac_dump_pin(struct hdac_softc *sc, struct hdac_widget *w) +{ + uint32_t pincap, wcap; + + pincap = w->wclass.pin.cap; + wcap = w->param.widget_cap; + + device_printf(sc->dev, " Pin cap: 0x%08x\n", pincap); + device_printf(sc->dev, " "); + if (HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(pincap)) + kprintf(" ISC"); + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) + kprintf(" TRQD"); + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) + kprintf(" PDC"); + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + kprintf(" HP"); + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + kprintf(" OUT"); + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + kprintf(" IN"); + if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) + kprintf(" BAL"); + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) + kprintf(" EAPD"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(wcap)) + kprintf(" : UNSOL"); + kprintf("\n"); + device_printf(sc->dev, " Pin config: 0x%08x\n", + w->wclass.pin.config); + device_printf(sc->dev, " Pin control: 0x%08x", w->wclass.pin.ctrl); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE) + kprintf(" HP"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) + kprintf(" IN"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) + kprintf(" OUT"); + kprintf("\n"); +} + +static void +hdac_dump_amp(struct hdac_softc *sc, uint32_t cap, char *banner) +{ + device_printf(sc->dev, " %s amp: 0x%08x\n", banner, cap); + device_printf(sc->dev, " " + "mute=%d step=%d size=%d offset=%d\n", + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(cap), + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(cap), + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(cap), + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(cap)); +} + +static void +hdac_dump_nodes(struct hdac_devinfo *devinfo) +{ + struct hdac_softc *sc = devinfo->codec->sc; + struct hdac_widget *w, *cw; + int i, j; + + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Default Parameter\n"); + device_printf(sc->dev, "-----------------\n"); + hdac_dump_audio_formats(sc, + devinfo->function.audio.supp_stream_formats, + devinfo->function.audio.supp_pcm_size_rate); + device_printf(sc->dev, " IN amp: 0x%08x\n", + devinfo->function.audio.inamp_cap); + device_printf(sc->dev, " OUT amp: 0x%08x\n", + devinfo->function.audio.outamp_cap); + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) { + device_printf(sc->dev, "Ghost widget nid=%d\n", i); + continue; + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid: %d [%s]%s\n", w->nid, + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap) ? + "DIGITAL" : "ANALOG", + (w->enable == 0) ? " [DISABLED]" : ""); + device_printf(sc->dev, " name: %s\n", w->name); + device_printf(sc->dev, " widget_cap: 0x%08x\n", + w->param.widget_cap); + device_printf(sc->dev, " Parse flags: 0x%08x\n", + w->pflags); + device_printf(sc->dev, " Ctl flags: 0x%08x\n", + w->ctlflags); + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT) { + hdac_dump_audio_formats(sc, + w->param.supp_stream_formats, + w->param.supp_pcm_size_rate); + } else if (w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + hdac_dump_pin(sc, w); + if (w->param.eapdbtl != HDAC_INVALID) + device_printf(sc->dev, " EAPD: 0x%08x\n", + w->param.eapdbtl); + if (HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(w->param.widget_cap) && + w->param.outamp_cap != 0) + hdac_dump_amp(sc, w->param.outamp_cap, "Output"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(w->param.widget_cap) && + w->param.inamp_cap != 0) + hdac_dump_amp(sc, w->param.inamp_cap, " Input"); + device_printf(sc->dev, " connections: %d\n", w->nconns); + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " + <- nid=%d [%s]", + w->conns[j], (cw == NULL) ? "GHOST!" : cw->name); + if (cw == NULL) + kprintf(" [UNKNOWN]"); + else if (cw->enable == 0) + kprintf(" [DISABLED]"); + if (w->nconns > 1 && w->selconn == j && w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + kprintf(" (selected)"); + kprintf("\n"); + } + } + +} + +static int +hdac_dump_dac_internal(struct hdac_devinfo *devinfo, nid_t nid, int depth) +{ + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + int i; + + if (depth > HDA_PARSE_MAXDEPTH) + return (0); + + w = hdac_widget_get(devinfo, nid); + if (w == NULL || w->enable == 0 || !(w->pflags & HDA_DAC_PATH)) + return (0); + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); + device_printf(sc->dev, " ^\n"); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " +-----<------+\n"); + } else { + device_printf(sc->dev, " ^\n"); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " "); + kprintf(" nid=%d [%s]\n", w->nid, w->name); + } + + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT) { + return (1); + } else if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) { + for (i = 0; i < w->nconns; i++) { + cw = hdac_widget_get(devinfo, w->conns[i]); + if (cw == NULL || cw->enable == 0 || cw->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) + continue; + if (hdac_dump_dac_internal(devinfo, cw->nid, + depth + 1) != 0) + return (1); + } + } else if ((w->type == + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR || + w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX) && + w->selconn > -1 && w->selconn < w->nconns) { + if (hdac_dump_dac_internal(devinfo, w->conns[w->selconn], + depth + 1) != 0) + return (1); + } + + return (0); +} + +static void +hdac_dump_dac(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w; + struct hdac_softc *sc = devinfo->codec->sc; + int i, printed = 0; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (w->type != HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX || + !(w->pflags & HDA_DAC_PATH)) + continue; + if (printed == 0) { + printed = 1; + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Playback path:\n"); + } + hdac_dump_dac_internal(devinfo, w->nid, 0); + } +} + +static void +hdac_dump_adc(struct hdac_devinfo *devinfo) +{ + struct hdac_widget *w, *cw; + struct hdac_softc *sc = devinfo->codec->sc; + int i, j; + int printed = 0; + char ossdevs[256]; + + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL || w->enable == 0) + continue; + if (!(w->pflags & HDA_ADC_RECSEL)) + continue; + if (printed == 0) { + printed = 1; + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "Recording sources:\n"); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " nid=%d [%s]\n", w->nid, w->name); + for (j = 0; j < w->nconns; j++) { + cw = hdac_widget_get(devinfo, w->conns[j]); + if (cw == NULL || cw->enable == 0) + continue; + hdac_audio_ctl_ossmixer_mask2allname(cw->ctlflags, + ossdevs, sizeof(ossdevs)); + device_printf(sc->dev, " |\n"); + device_printf(sc->dev, " + <- nid=%d [%s]", + cw->nid, cw->name); + if (strlen(ossdevs) > 0) { + kprintf(" [recsrc: %s]", ossdevs); + } + kprintf("\n"); + } + } +} + +static void +hdac_dump_pcmchannels(struct hdac_softc *sc, int pcnt, int rcnt) +{ + nid_t *nids; + + if (pcnt > 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " PCM Playback: %d\n", pcnt); + hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, + sc->play.supp_pcm_size_rate); + device_printf(sc->dev, " DAC:"); + for (nids = sc->play.io; *nids != -1; nids++) + kprintf(" %d", *nids); + kprintf("\n"); + } + + if (rcnt > 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, " PCM Record: %d\n", rcnt); + hdac_dump_audio_formats(sc, sc->play.supp_stream_formats, + sc->rec.supp_pcm_size_rate); + device_printf(sc->dev, " ADC:"); + for (nids = sc->rec.io; *nids != -1; nids++) + kprintf(" %d", *nids); + kprintf("\n"); + } +} + +static void +hdac_release_resources(struct hdac_softc *sc) +{ + struct hdac_devinfo *devinfo = NULL; + device_t *devlist = NULL; + int i, devcount; + + if (sc == NULL) + return; + + hdac_lock(sc); + hdac_reset(sc); + hdac_unlock(sc); + snd_mtxfree(sc->lock); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo == NULL) + continue; + if (devinfo->widget != NULL) + kfree(devinfo->widget, M_HDAC); + if (devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->function.audio.ctl != NULL) + kfree(devinfo->function.audio.ctl, M_HDAC); + kfree(devinfo, M_HDAC); + device_delete_child(sc->dev, devlist[i]); + } + if (devlist != NULL) + kfree(devlist, M_TEMP); + + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (sc->codecs[i] != NULL) + kfree(sc->codecs[i], M_HDAC); + sc->codecs[i] = NULL; + } + + hdac_dma_free(&sc->rirb_dma); + hdac_dma_free(&sc->corb_dma); + if (sc->play.blkcnt > 0) + hdac_dma_free(&sc->play.bdl_dma); + if (sc->rec.blkcnt > 0) + hdac_dma_free(&sc->rec.bdl_dma); + hdac_irq_free(sc); + hdac_mem_free(sc); + kfree(sc, M_DEVBUF); + +} + +/* This function surely going to make its way into upper level someday. */ +static void +hdac_config_fetch(struct hdac_softc *sc, uint32_t *on, uint32_t *off) +{ + char *res = NULL; + int i = 0, j, k, len, inv; + + if (on != NULL) + *on = 0; + if (off != NULL) + *off = 0; + if (sc == NULL) + return; + if (resource_string_value(device_get_name(sc->dev), + device_get_unit(sc->dev), "config", &res) != 0) + return; + if (!(res != NULL && strlen(res) > 0)) + return; + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: HDA Config:"); + ); + for (;;) { + while (res[i] != '\0' && + (res[i] == ',' || isspace(res[i]) != 0)) + i++; + if (res[i] == '\0') { + HDA_BOOTVERBOSE( + kprintf("\n"); + ); + return; + } + j = i; + while (res[j] != '\0' && + !(res[j] == ',' || isspace(res[j]) != 0)) + j++; + len = j - i; + if (len > 2 && strncmp(res + i, "no", 2) == 0) + inv = 2; + else + inv = 0; + for (k = 0; len > inv && k < HDAC_QUIRKS_TAB_LEN; k++) { + if (strncmp(res + i + inv, + hdac_quirks_tab[k].key, len - inv) != 0) + continue; + if (len - inv != strlen(hdac_quirks_tab[k].key)) + break; + HDA_BOOTVERBOSE( + kprintf(" %s%s", (inv != 0) ? "no" : "", + hdac_quirks_tab[k].key); + ); + if (inv == 0 && on != NULL) + *on |= hdac_quirks_tab[k].value; + else if (inv != 0 && off != NULL) + *off |= hdac_quirks_tab[k].value; + break; + } + i = j; + } +} + +static void +hdac_attach2(void *arg) +{ + struct hdac_softc *sc; + struct hdac_widget *w; + struct hdac_audio_ctl *ctl; + uint32_t quirks_on, quirks_off; + int pcnt, rcnt; + int i; + char status[SND_STATUSLEN]; + device_t *devlist = NULL; + int devcount; + struct hdac_devinfo *devinfo = NULL; + + sc = (struct hdac_softc *)arg; + + hdac_config_fetch(sc, &quirks_on, &quirks_off); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: HDA Config: on=0x%08x off=0x%08x\n", + quirks_on, quirks_off); + ); + + hdac_lock(sc); + + /* Remove ourselves from the config hooks */ + if (sc->intrhook.ich_func != NULL) { + config_intrhook_disestablish(&sc->intrhook); + sc->intrhook.ich_func = NULL; + } + + /* Start the corb and rirb engines */ + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Starting CORB Engine...\n"); + ); + hdac_corb_start(sc); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Starting RIRB Engine...\n"); + ); + hdac_rirb_start(sc); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Enabling controller interrupt...\n"); + ); + HDAC_WRITE_4(&sc->mem, HDAC_INTCTL, HDAC_INTCTL_CIE | HDAC_INTCTL_GIE); + HDAC_WRITE_4(&sc->mem, HDAC_GCTL, HDAC_READ_4(&sc->mem, HDAC_GCTL) | + HDAC_GCTL_UNSOL); + + DELAY(1000); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Scanning HDA codecs...\n"); + ); + hdac_scan_codecs(sc); + + device_get_children(sc->dev, &devlist, &devcount); + for (i = 0; devlist != NULL && i < devcount; i++) { + devinfo = (struct hdac_devinfo *)device_get_ivars(devlist[i]); + if (devinfo != NULL && devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO) { + break; + } else + devinfo = NULL; + } + if (devlist != NULL) + kfree(devlist, M_TEMP); + + if (devinfo == NULL) { + hdac_unlock(sc); + device_printf(sc->dev, "Audio Function Group not found!\n"); + hdac_release_resources(sc); + return; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Parsing AFG nid=%d cad=%d\n", + devinfo->nid, devinfo->codec->cad); + ); + hdac_audio_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Parsing Ctls...\n"); + ); + hdac_audio_ctl_parse(devinfo); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Parsing vendor patch...\n"); + ); + hdac_vendor_patch_parse(devinfo); + if (quirks_on != 0) + devinfo->function.audio.quirks |= quirks_on; + if (quirks_off != 0) + devinfo->function.audio.quirks &= ~quirks_off; + + /* XXX Disable all DIGITAL path. */ + for (i = devinfo->startnode; i < devinfo->endnode; i++) { + w = hdac_widget_get(devinfo, i); + if (w == NULL) + continue; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) { + w->enable = 0; + continue; + } + /* XXX Disable useless pin ? */ + if (w->type == HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX && + (w->wclass.pin.config & + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) == + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE) + w->enable = 0; + } + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + if (ctl->widget == NULL) + continue; + w = ctl->widget; + if (w->enable == 0) + ctl->enable = 0; + if (HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + ctl->enable = 0; + w = ctl->childwidget; + if (w == NULL) + continue; + if (w->enable == 0 || + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(w->param.widget_cap)) + ctl->enable = 0; + } + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Building AFG tree...\n"); + ); + hdac_audio_build_tree(devinfo); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: AFG commit...\n"); + ); + hdac_audio_commit(devinfo, HDA_COMMIT_ALL); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: Ctls commit...\n"); + ); + hdac_audio_ctl_commit(devinfo); + + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: PCMDIR_PLAY setup...\n"); + ); + pcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_PLAY); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "HDA_DEBUG: PCMDIR_REC setup...\n"); + ); + rcnt = hdac_pcmchannel_setup(devinfo, PCMDIR_REC); + + hdac_unlock(sc); + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: OSS mixer initialization...\n"); + ); + + /* + * There is no point of return after this. If the driver failed, + * so be it. Let the detach procedure do all the cleanup. + */ + if (mixer_init(sc->dev, &hdac_audio_ctl_ossmixer_class, devinfo) != 0) + device_printf(sc->dev, "Can't register mixer\n"); + + if (pcnt > 0) + pcnt = 1; + if (rcnt > 0) + rcnt = 1; + + HDA_BOOTVERBOSE( + device_printf(sc->dev, + "HDA_DEBUG: Registering PCM channels...\n"); + ); + if (pcm_register(sc->dev, devinfo, pcnt, rcnt) != 0) + device_printf(sc->dev, "Can't register PCM\n"); + + sc->registered++; + + for (i = 0; i < pcnt; i++) + pcm_addchan(sc->dev, PCMDIR_PLAY, &hdac_channel_class, devinfo); + for (i = 0; i < rcnt; i++) + pcm_addchan(sc->dev, PCMDIR_REC, &hdac_channel_class, devinfo); + + ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s [%s]", + rman_get_start(sc->mem.mem_res), + rman_get_start(sc->irq.irq_res), + "snd_hda", HDA_DRV_TEST_REV); + pcm_setstatus(sc->dev, status); + device_printf(sc->dev, "\n", hdac_codec_name(devinfo)); + HDA_BOOTVERBOSE( + device_printf(sc->dev, "\n", + hdac_codec_id(devinfo)); + ); + device_printf(sc->dev, "\n", HDA_DRV_TEST_REV); + + HDA_BOOTVERBOSE( + if (devinfo->function.audio.quirks != 0) { + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "HDA config/quirks:"); + for (i = 0; i < HDAC_QUIRKS_TAB_LEN; i++) { + if (devinfo->function.audio.quirks & + hdac_quirks_tab[i].value) + kprintf(" %s", hdac_quirks_tab[i].key); + } + kprintf("\n"); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA NODES |\n"); + device_printf(sc->dev, "+-------------------+\n"); + hdac_dump_nodes(devinfo); + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AMPLIFIERS |\n"); + device_printf(sc->dev, "+------------------------+\n"); + device_printf(sc->dev, "\n"); + i = 0; + while ((ctl = hdac_audio_ctl_each(devinfo, &i)) != NULL) { + device_printf(sc->dev, "%3d: nid=%d", i, + (ctl->widget != NULL) ? ctl->widget->nid : -1); + if (ctl->childwidget != NULL) + kprintf(" cnid=%d", ctl->childwidget->nid); + kprintf(" dir=0x%x index=%d " + "ossmask=0x%08x ossdev=%d%s\n", + ctl->dir, ctl->index, + ctl->ossmask, ctl->ossdev, + (ctl->enable == 0) ? " [DISABLED]" : ""); + } + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+-----------------------------------+\n"); + device_printf(sc->dev, "| DUMPING HDA AUDIO/VOLUME CONTROLS |\n"); + device_printf(sc->dev, "+-----------------------------------+\n"); + hdac_dump_ctls(devinfo, "Master Volume (OSS: vol)", SOUND_MASK_VOLUME); + hdac_dump_ctls(devinfo, "PCM Volume (OSS: pcm)", SOUND_MASK_PCM); + hdac_dump_ctls(devinfo, "CD Volume (OSS: cd)", SOUND_MASK_CD); + hdac_dump_ctls(devinfo, "Microphone Volume (OSS: mic)", SOUND_MASK_MIC); + hdac_dump_ctls(devinfo, "Line-in Volume (OSS: line)", SOUND_MASK_LINE); + hdac_dump_ctls(devinfo, "Recording Level (OSS: rec)", SOUND_MASK_RECLEV); + hdac_dump_ctls(devinfo, "Speaker/Beep (OSS: speaker)", SOUND_MASK_SPEAKER); + hdac_dump_ctls(devinfo, NULL, 0); + hdac_dump_dac(devinfo); + hdac_dump_adc(devinfo); + device_printf(sc->dev, "\n"); + device_printf(sc->dev, "+--------------------------------------+\n"); + device_printf(sc->dev, "| DUMPING PCM Playback/Record Channels |\n"); + device_printf(sc->dev, "+--------------------------------------+\n"); + hdac_dump_pcmchannels(sc, pcnt, rcnt); + ); +} + +/**************************************************************************** + * int hdac_detach(device_t) + * + * Detach and free up resources utilized by the hdac device. + ****************************************************************************/ +static int +hdac_detach(device_t dev) +{ + struct hdac_softc *sc = NULL; + struct hdac_devinfo *devinfo = NULL; + int err; + + devinfo = (struct hdac_devinfo *)pcm_getdevinfo(dev); + if (devinfo != NULL && devinfo->codec != NULL) + sc = devinfo->codec->sc; + if (sc == NULL) + return (0); + + if (sc->registered > 0) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + } + + hdac_release_resources(sc); + + return (0); +} + +static device_method_t hdac_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, hdac_probe), + DEVMETHOD(device_attach, hdac_attach), + DEVMETHOD(device_detach, hdac_detach), + { 0, 0 } +}; + +static driver_t hdac_driver = { + "pcm", + hdac_methods, + PCM_SOFTC_SIZE, +}; + +DRIVER_MODULE(snd_hda, pci, hdac_driver, pcm_devclass, 0, 0); +MODULE_DEPEND(snd_hda, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_hda, 1); diff --git a/sys/dev/sound/pci/hda/hdac.h b/sys/dev/sound/pci/hda/hdac.h new file mode 100644 index 0000000000..0e5d1573db --- /dev/null +++ b/sys/dev/sound/pci/hda/hdac.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * 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: src/sys/dev/sound/pci/hda/hdac.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hdac.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ + */ + +#ifndef _HDAC_H_ +#define _HDAC_H_ + + +#if 0 +/**************************************************************************** + * Miscellanious defines + ****************************************************************************/ + +/**************************************************************************** + * Helper Macros + ****************************************************************************/ + +/**************************************************************************** + * Simplified Accessors for HDA devices + ****************************************************************************/ +enum hdac_device_ivars { + HDAC_IVAR_CODEC_ID, + HDAC_IVAR_NODE_ID, + HDAC_IVAR_VENDOR_ID, + HDAC_IVAR_DEVICE_ID, + HDAC_IVAR_REVISION_ID, + HDAC_IVAR_STEPPING_ID, + HDAC_IVAR_NODE_TYPE, +}; + +#define HDAC_ACCESSOR(var, ivar, type) \ + __BUS_ACCESSOR(hdac, var, HDAC, ivar, type) + +HDAC_ACCESSOR(codec_id, CODEC_ID, uint8_t); +HDAC_ACCESSOR(node_id, NODE_ID, uint8_t); +HDAC_ACCESSOR(vendor_id, VENDOR_ID, uint16_t); +HDAC_ACCESSOR(device_id, DEVICE_ID, uint16_t); +HDAC_ACCESSOR(revision_id, REVISION_ID, uint8_t); +HDAC_ACCESSOR(stepping_id, STEPPING_ID, uint8_t); +HDAC_ACCESSOR(node_type, NODE_TYPE, uint8_t); +#endif + +#define PCIS_MULTIMEDIA_HDA 0x03 + +#endif diff --git a/sys/dev/sound/pci/hda/hdac_private.h b/sys/dev/sound/pci/hda/hdac_private.h new file mode 100644 index 0000000000..bd3136b86e --- /dev/null +++ b/sys/dev/sound/pci/hda/hdac_private.h @@ -0,0 +1,333 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * 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: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.2 2006/10/06 18:59:27 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hdac_private.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ + */ + +#ifndef _HDAC_PRIVATE_H_ +#define _HDAC_PRIVATE_H_ + + +/**************************************************************************** + * Miscellanious defines + ****************************************************************************/ +#define HDAC_DMA_ALIGNMENT 128 +#define HDAC_CODEC_MAX 16 + +#define HDAC_MTX_NAME "hdac driver mutex" + +/**************************************************************************** + * Helper Macros + ****************************************************************************/ +#define HDAC_READ_1(mem, offset) \ + bus_space_read_1((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_READ_2(mem, offset) \ + bus_space_read_2((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_READ_4(mem, offset) \ + bus_space_read_4((mem)->mem_tag, (mem)->mem_handle, (offset)) +#define HDAC_WRITE_1(mem, offset, value) \ + bus_space_write_1((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) +#define HDAC_WRITE_2(mem, offset, value) \ + bus_space_write_2((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) +#define HDAC_WRITE_4(mem, offset, value) \ + bus_space_write_4((mem)->mem_tag, (mem)->mem_handle, (offset), (value)) + +#define HDAC_ISDCTL(sc, n) (_HDAC_ISDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDSTS(sc, n) (_HDAC_ISDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDPICB(sc, n) (_HDAC_ISDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDCBL(sc, n) (_HDAC_ISDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDLVI(sc, n) (_HDAC_ISDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDFIFOD(sc, n) (_HDAC_ISDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDFMT(sc, n) (_HDAC_ISDFMT((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDBDPL(sc, n) (_HDAC_ISDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_ISDBDPU(sc, n) (_HDAC_ISDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + +#define HDAC_OSDCTL(sc, n) (_HDAC_OSDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDSTS(sc, n) (_HDAC_OSDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDPICB(sc, n) (_HDAC_OSDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDCBL(sc, n) (_HDAC_OSDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDLVI(sc, n) (_HDAC_OSDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDFIFOD(sc, n) (_HDAC_OSDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDBDPL(sc, n) (_HDAC_OSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_OSDBDPU(sc, n) (_HDAC_OSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + +#define HDAC_BSDCTL(sc, n) (_HDAC_BSDCTL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDSTS(sc, n) (_HDAC_BSDSTS((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDPICB(sc, n) (_HDAC_BSDPICB((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDCBL(sc, n) (_HDAC_BSDCBL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDLVI(sc, n) (_HDAC_BSDLVI((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDFIFOD(sc, n) (_HDAC_BSDFIFOD((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDBDPL(sc, n) (_HDAC_BSDBDPL((n), (sc)->num_iss, (sc)->num_oss)) +#define HDAC_BSDBDPU(sc, n) (_HDAC_BSDBDPU((n), (sc)->num_iss, (sc)->num_oss)) + + +/**************************************************************************** + * Custom hdac malloc type + ****************************************************************************/ +MALLOC_DECLARE(M_HDAC); + +/**************************************************************************** + * struct hdac_mem + * + * Holds the resources necessary to describe the physical memory associated + * with the device. + ****************************************************************************/ +struct hdac_mem { + struct resource *mem_res; + int mem_rid; + bus_space_tag_t mem_tag; + bus_space_handle_t mem_handle; +}; + +/**************************************************************************** + * struct hdac_irq + * + * Holds the resources necessary to describe the irq associated with the + * device. + ****************************************************************************/ +struct hdac_irq { + struct resource *irq_res; + int irq_rid; + void *irq_handle; +}; + +/**************************************************************************** + * struct hdac_dma + * + * This structure is used to hold all the information to manage the dma + * states. + ****************************************************************************/ +struct hdac_dma { + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_addr_t dma_paddr; + caddr_t dma_vaddr; +}; + +/**************************************************************************** + * struct hdac_rirb + * + * Hold a response from a verb sent to a codec received via the rirb. + ****************************************************************************/ +struct hdac_rirb { + uint32_t response; + uint32_t response_ex; +}; + +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK 0x0000000f +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET 0 +#define HDAC_RIRB_RESPONSE_EX_UNSOLICITED 0x00000010 + +#define HDAC_RIRB_RESPONSE_EX_SDATA_IN(response_ex) \ + (((response_ex) & HDAC_RIRB_RESPONSE_EX_SDATA_IN_MASK) >> \ + HDAC_RIRB_RESPONSE_EX_SDATA_IN_OFFSET) + +/**************************************************************************** + * struct hdac_command_list + * + * This structure holds the list of verbs that are to be sent to the codec + * via the corb and the responses received via the rirb. It's allocated by + * the codec driver and is owned by it. + ****************************************************************************/ +struct hdac_command_list { + int num_commands; + uint32_t *verbs; + uint32_t *responses; +}; + +typedef int nid_t; + +struct hdac_softc; +/**************************************************************************** + * struct hdac_codec + * + ****************************************************************************/ +struct hdac_codec { + int verbs_sent; + int responses_received; + nid_t cad; + struct hdac_command_list *commands; + struct hdac_softc *sc; +}; + +struct hdac_bdle { + volatile uint32_t addrl; + volatile uint32_t addrh; + volatile uint32_t len; + volatile uint32_t ioc; +} __packed; + +#define HDA_MAX_CONNS 32 +#define HDA_MAX_NAMELEN 32 + +struct hdac_devinfo; + +struct hdac_widget { + nid_t nid; + int type; + int enable; + int nconns, selconn; + uint32_t pflags, ctlflags; + nid_t conns[HDA_MAX_CONNS]; + char name[HDA_MAX_NAMELEN]; + struct hdac_devinfo *devinfo; + struct { + uint32_t widget_cap; + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + uint32_t eapdbtl; + int outpath; + } param; + union { + struct { + uint32_t config; + uint32_t cap; + uint32_t ctrl; + } pin; + } wclass; +}; + +struct hdac_audio_ctl { + struct hdac_widget *widget, *childwidget; + int enable; + int index; + int mute, step, size, offset; + int left, right; + uint32_t muted; + int ossdev; + uint32_t dir, ossmask, ossval; +}; + +/**************************************************************************** + * struct hdac_devinfo + * + * Holds all the parameters of a given codec function group. This is stored + * in the ivar of each child of the hdac bus + ****************************************************************************/ +struct hdac_devinfo { + device_t dev; + uint16_t vendor_id; + uint16_t device_id; + uint8_t revision_id; + uint8_t stepping_id; + uint8_t node_type; + nid_t nid; + nid_t startnode, endnode; + int nodecnt; + struct hdac_codec *codec; + struct hdac_widget *widget; + union { + struct { + uint32_t outamp_cap; + uint32_t inamp_cap; + uint32_t supp_stream_formats; + uint32_t supp_pcm_size_rate; + int ctlcnt, pcnt, rcnt; + struct hdac_audio_ctl *ctl; + uint32_t mvol; + uint32_t quirks; + int ossidx; + int playcnt, reccnt; + int parsing_strategy; + } audio; + /* XXX undefined: modem, hdmi. */ + } function; +}; + +struct hdac_chan { + struct snd_dbuf *b; + struct pcm_channel *c; + struct pcmchan_caps caps; + struct hdac_devinfo *devinfo; + struct hdac_dma bdl_dma; + uint32_t spd, fmt, fmtlist[8], pcmrates[16]; + uint32_t supp_stream_formats, supp_pcm_size_rate; + int ptr, prevptr, blkcnt, blksz; + int dir; + int off; + int sid; + int bit16, bit32; + nid_t io[16]; +}; + +/**************************************************************************** + * struct hdac_softc + * + * This structure holds the current state of the hdac driver. + ****************************************************************************/ +struct hdac_softc { + device_t dev; + device_t hdabus; + struct spinlock *lock; + + struct intr_config_hook intrhook; + + struct hdac_mem mem; + struct hdac_irq irq; + uint32_t pci_subvendor; + + + int num_iss; + int num_oss; + int num_bss; + int support_64bit; + int streamcnt; + + int corb_size; + struct hdac_dma corb_dma; + int corb_wp; + + int rirb_size; + struct hdac_dma rirb_dma; + int rirb_rp; + + struct hdac_chan play, rec; + bus_dma_tag_t chan_dmat; + int chan_size; + int chan_blkcnt; + +#define HDAC_UNSOLQ_MAX 64 +#define HDAC_UNSOLQ_READY 0 +#define HDAC_UNSOLQ_BUSY 1 + int unsolq_rp; + int unsolq_wp; + int unsolq_st; + uint32_t unsolq[HDAC_UNSOLQ_MAX]; + + struct hdac_codec *codecs[HDAC_CODEC_MAX]; + + int registered; +}; + +/**************************************************************************** + * struct hdac_command flags + ****************************************************************************/ +#define HDAC_COMMAND_FLAG_WAITOK 0x0000 +#define HDAC_COMMAND_FLAG_NOWAIT 0x0001 + +#endif diff --git a/sys/dev/sound/pci/hda/hdac_reg.h b/sys/dev/sound/pci/hda/hdac_reg.h new file mode 100644 index 0000000000..9bb6424403 --- /dev/null +++ b/sys/dev/sound/pci/hda/hdac_reg.h @@ -0,0 +1,267 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * 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: src/sys/dev/sound/pci/hda/hdac_reg.h,v 1.1 2006/10/01 11:12:59 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/hda/hdac_reg.h,v 1.1 2007/01/04 21:47:03 corecode Exp $ + */ + +#ifndef _HDAC_REG_H_ +#define _HDAC_REG_H_ + +/**************************************************************************** + * HDA Controller Register Set + ****************************************************************************/ +#define HDAC_GCAP 0x00 /* 2 - Global Capabilities*/ +#define HDAC_VMIN 0x02 /* 1 - Minor Version */ +#define HDAC_VMAJ 0x03 /* 1 - Major Version */ +#define HDAC_OUTPAY 0x04 /* 2 - Output Payload Capability */ +#define HDAC_INPAY 0x06 /* 2 - Input Payload Capability */ +#define HDAC_GCTL 0x08 /* 4 - Global Control */ +#define HDAC_WAKEEN 0x0c /* 2 - Wake Enable */ +#define HDAC_STATESTS 0x0e /* 2 - State Change Status */ +#define HDAC_GSTS 0x10 /* 2 - Global Status */ +#define HDAC_OUTSTRMPAY 0x18 /* 2 - Output Stream Payload Capability */ +#define HDAC_INSTRMPAY 0x1a /* 2 - Input Stream Payload Capability */ +#define HDAC_INTCTL 0x20 /* 4 - Interrupt Control */ +#define HDAC_INTSTS 0x24 /* 4 - Interrupt Status */ +#define HDAC_WALCLK 0x30 /* 4 - Wall Clock Counter */ +#define HDAC_SSYNC 0x38 /* 4 - Stream Synchronization */ +#define HDAC_CORBLBASE 0x40 /* 4 - CORB Lower Base Address */ +#define HDAC_CORBUBASE 0x44 /* 4 - CORB Upper Base Address */ +#define HDAC_CORBWP 0x48 /* 2 - CORB Write Pointer */ +#define HDAC_CORBRP 0x4a /* 2 - CORB Read Pointer */ +#define HDAC_CORBCTL 0x4c /* 1 - CORB Control */ +#define HDAC_CORBSTS 0x4d /* 1 - CORB Status */ +#define HDAC_CORBSIZE 0x4e /* 1 - CORB Size */ +#define HDAC_RIRBLBASE 0x50 /* 4 - RIRB Lower Base Address */ +#define HDAC_RIRBUBASE 0x54 /* 4 - RIRB Upper Base Address */ +#define HDAC_RIRBWP 0x58 /* 2 - RIRB Write Pointer */ +#define HDAC_RINTCNT 0x5a /* 2 - Response Interrupt Count */ +#define HDAC_RIRBCTL 0x5c /* 1 - RIRB Control */ +#define HDAC_RIRBSTS 0x5d /* 1 - RIRB Status */ +#define HDAC_RIRBSIZE 0x5e /* 1 - RIRB Size */ +#define HDAC_ICOI 0x60 /* 4 - Immediate Command Output Interface */ +#define HDAC_ICII 0x64 /* 4 - Immediate Command Input Interface */ +#define HDAC_ICIS 0x68 /* 2 - Immediate Command Status */ +#define HDAC_DPIBLBASE 0x70 /* 4 - DMA Position Buffer Lower Base */ +#define HDAC_DPIBUBASE 0x74 /* 4 - DMA Position Buffer Upper Base */ +#define HDAC_SDCTL0 0x80 /* 3 - Stream Descriptor Control */ +#define HDAC_SDCTL1 0x81 /* 3 - Stream Descriptor Control */ +#define HDAC_SDCTL2 0x82 /* 3 - Stream Descriptor Control */ +#define HDAC_SDSTS 0x83 /* 1 - Stream Descriptor Status */ +#define HDAC_SDLPIB 0x84 /* 4 - Link Position in Buffer */ +#define HDAC_SDCBL 0x88 /* 4 - Cyclic Buffer Length */ +#define HDAC_SDLVI 0x8C /* 2 - Last Valid Index */ +#define HDAC_SDFIFOS 0x90 /* 2 - FIFOS */ +#define HDAC_SDFMT 0x92 /* 2 - fmt */ +#define HDAC_SDBDPL 0x98 /* 4 - Buffer Descriptor Pointer Lower Base */ +#define HDAC_SDBDPU 0x9C /* 4 - Buffer Descriptor Pointer Upper Base */ + +#define _HDAC_ISDOFFSET(n, iss, oss) (0x80 + ((n) * 0x20)) +#define _HDAC_ISDCTL(n, iss, oss) (0x00 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDSTS(n, iss, oss) (0x03 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDPICB(n, iss, oss) (0x04 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDCBL(n, iss, oss) (0x08 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDLVI(n, iss, oss) (0x0c + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDFIFOD(n, iss, oss) (0x10 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDFMT(n, iss, oss) (0x12 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDBDPL(n, iss, oss) (0x18 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDBDPU(n, iss, oss) (0x1c + _HDAC_ISDOFFSET(n, iss, oss)) + +#define _HDAC_OSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((n) * 0x20)) +#define _HDAC_OSDCTL(n, iss, oss) (0x00 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDSTS(n, iss, oss) (0x03 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDPICB(n, iss, oss) (0x04 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDCBL(n, iss, oss) (0x08 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDLVI(n, iss, oss) (0x0c + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDFIFOD(n, iss, oss) (0x10 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDFMT(n, iss, oss) (0x12 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDBDPL(n, iss, oss) (0x18 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDBDPU(n, iss, oss) (0x1c + _HDAC_OSDOFFSET(n, iss, oss)) + +#define _HDAC_BSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((oss) * 0x20) + ((n) * 0x20)) +#define _HDAC_BSDCTL(n, iss, oss) (0x00 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDSTS(n, iss, oss) (0x03 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDPICB(n, iss, oss) (0x04 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDCBL(n, iss, oss) (0x08 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDLVI(n, iss, oss) (0x0c + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDFIFOD(n, iss, oss) (0x10 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDFMT(n, iss, oss) (0x12 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDBDPL(n, iss, oss) (0x18 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDBDBU(n, iss, oss) (0x1c + _HDAC_BSDOFFSET(n, iss, oss)) + +/**************************************************************************** + * HDA Controller Register Fields + ****************************************************************************/ + +/* GCAP - Global Capabilities */ +#define HDAC_GCAP_64OK 0x0001 +#define HDAC_GCAP_NSDO_MASK 0x0006 +#define HDAC_GCAP_NSDO_SHIFT 1 +#define HDAC_GCAP_BSS_MASK 0x00f8 +#define HDAC_GCAP_BSS_SHIFT 3 +#define HDAC_GCAP_ISS_MASK 0x0f00 +#define HDAC_GCAP_ISS_SHIFT 8 +#define HDAC_GCAP_OSS_MASK 0xf000 +#define HDAC_GCAP_OSS_SHIFT 12 + +#define HDAC_GCAP_NSDO_1SDO 0x00 +#define HDAC_GCAP_NSDO_2SDO 0x02 +#define HDAC_GCAP_NSDO_4SDO 0x04 + +#define HDAC_GCAP_BSS(gcap) \ + (((gcap) & HDAC_GCAP_BSS_MASK) >> HDAC_GCAP_BSS_SHIFT) +#define HDAC_GCAP_ISS(gcap) \ + (((gcap) & HDAC_GCAP_ISS_MASK) >> HDAC_GCAP_ISS_SHIFT) +#define HDAC_GCAP_OSS(gcap) \ + (((gcap) & HDAC_GCAP_OSS_MASK) >> HDAC_GCAP_OSS_SHIFT) + +/* GCTL - Global Control */ +#define HDAC_GCTL_CRST 0x00000001 +#define HDAC_GCTL_FCNTRL 0x00000002 +#define HDAC_GCTL_UNSOL 0x00000100 + +/* WAKEEN - Wake Enable */ +#define HDAC_WAKEEN_SDIWEN_MASK 0x7fff +#define HDAC_WAKEEN_SDIWEN_SHIFT 0 + +/* STATESTS - State Change Status */ +#define HDAC_STATESTS_SDIWAKE_MASK 0x7fff +#define HDAC_STATESTS_SDIWAKE_SHIFT 0 + +#define HDAC_STATESTS_SDIWAKE(statests, n) \ + (((((statests) & HDAC_STATESTS_SDIWAKE_MASK) >> \ + HDAC_STATESTS_SDIWAKE_SHIFT) >> (n)) & 0x0001) + +/* GSTS - Global Status */ +#define HDAC_GSTS_FSTS 0x0002 + +/* INTCTL - Interrut Control */ +#define HDAC_INTCTL_SIE_MASK 0x3fffffff +#define HDAC_INTCTL_SIE_SHIFT 0 +#define HDAC_INTCTL_CIE 0x40000000 +#define HDAC_INTCTL_GIE 0x80000000 + +/* INTSTS - Interrupt Status */ +#define HDAC_INTSTS_SIS_MASK 0x3fffffff +#define HDAC_INTSTS_SIS_SHIFT 0 +#define HDAC_INTSTS_CIS 0x40000000 +#define HDAC_INTSTS_GIS 0x80000000 + +/* SSYNC - Stream Synchronization */ +#define HDAC_SSYNC_SSYNC_MASK 0x3fffffff +#define HDAC_SSYNC_SSYNC_SHIFT 0 + +/* CORBWP - CORB Write Pointer */ +#define HDAC_CORBWP_CORBWP_MASK 0x00ff +#define HDAC_CORBWP_CORBWP_SHIFT 0 + +/* CORBRP - CORB Read Pointer */ +#define HDAC_CORBRP_CORBRP_MASK 0x00ff +#define HDAC_CORBRP_CORBRP_SHIFT 0 +#define HDAC_CORBRP_CORBRPRST 0x8000 + +/* CORBCTL - CORB Control */ +#define HDAC_CORBCTL_CMEIE 0x01 +#define HDAC_CORBCTL_CORBRUN 0x02 + +/* CORBSTS - CORB Status */ +#define HDAC_CORBSTS_CMEI 0x01 + +/* CORBSIZE - CORB Size */ +#define HDAC_CORBSIZE_CORBSIZE_MASK 0x03 +#define HDAC_CORBSIZE_CORBSIZE_SHIFT 0 +#define HDAC_CORBSIZE_CORBSZCAP_MASK 0xf0 +#define HDAC_CORBSIZE_CORBSZCAP_SHIFT 4 + +#define HDAC_CORBSIZE_CORBSIZE_2 0x00 +#define HDAC_CORBSIZE_CORBSIZE_16 0x01 +#define HDAC_CORBSIZE_CORBSIZE_256 0x02 + +#define HDAC_CORBSIZE_CORBSZCAP_2 0x10 +#define HDAC_CORBSIZE_CORBSZCAP_16 0x20 +#define HDAC_CORBSIZE_CORBSZCAP_256 0x40 + +#define HDAC_CORBSIZE_CORBSIZE(corbsize) \ + (((corbsize) & HDAC_CORBSIZE_CORBSIZE_MASK) >> HDAC_CORBSIZE_CORBSIZE_SHIFT) + +/* RIRBWP - RIRB Write Pointer */ +#define HDAC_RIRBWP_RIRBWP_MASK 0x00ff +#define HDAC_RIRBWP_RIRBWP_SHIFT 0 +#define HDAC_RIRBWP_RIRBWPRST 0x8000 + +/* RINTCTN - Response Interrupt Count */ +#define HDAC_RINTCNT_MASK 0x00ff +#define HDAC_RINTCNT_SHIFT 0 + +/* RIRBCTL - RIRB Control */ +#define HDAC_RIRBCTL_RINTCTL 0x01 +#define HDAC_RIRBCTL_RIRBDMAEN 0x02 +#define HDAC_RIRBCTL_RIRBOIC 0x04 + +/* RIRBSTS - RIRB Status */ +#define HDAC_RIRBSTS_RINTFL 0x01 +#define HDAC_RIRBSTS_RIRBOIS 0x04 + +/* RIRBSIZE - RIRB Size */ +#define HDAC_RIRBSIZE_RIRBSIZE_MASK 0x03 +#define HDAC_RIRBSIZE_RIRBSIZE_SHIFT 0 +#define HDAC_RIRBSIZE_RIRBSZCAP_MASK 0xf0 +#define HDAC_RIRBSIZE_RIRBSZCAP_SHIFT 4 + +#define HDAC_RIRBSIZE_RIRBSIZE_2 0x00 +#define HDAC_RIRBSIZE_RIRBSIZE_16 0x01 +#define HDAC_RIRBSIZE_RIRBSIZE_256 0x02 + +#define HDAC_RIRBSIZE_RIRBSZCAP_2 0x10 +#define HDAC_RIRBSIZE_RIRBSZCAP_16 0x20 +#define HDAC_RIRBSIZE_RIRBSZCAP_256 0x40 + +#define HDAC_RIRBSIZE_RIRBSIZE(rirbsize) \ + (((rirbsize) & HDAC_RIRBSIZE_RIRBSIZE_MASK) >> HDAC_RIRBSIZE_RIRBSIZE_SHIFT) + +/* DPLBASE - DMA Position Lower Base Address */ +#define HDAC_DPLBASE_DPLBASE_MASK 0xffffff80 +#define HDAC_DPLBASE_DPLBASE_SHIFT 7 +#define HDAC_DPLBASE_DPLBASE_DMAPBE 0x00000001 + +/* SDCTL - Stream Descriptor Control */ +#define HDAC_SDCTL_SRST 0x000001 +#define HDAC_SDCTL_RUN 0x000002 +#define HDAC_SDCTL_IOCE 0x000004 +#define HDAC_SDCTL_FEIE 0x000008 +#define HDAC_SDCTL_DEIE 0x000010 +#define HDAC_SDCTL_STRIPE_MASK 0x030000 +#define HDAC_SDCTL_STRIPE_SHIFT 16 +#define HDAC_SDCTL_TP 0x040000 +#define HDAC_SDCTL_DIR 0x080000 +#define HDAC_SDCTL2_STRM_MASK 0xf0 +#define HDAC_SDCTL2_STRM_SHIFT 4 + +#define HDAC_SDSTS_DESE (1 << 4) +#define HDAC_SDSTS_FIFOE (1 << 3) +#define HDAC_SDSTS_BCIS (1 << 2) + +#endif diff --git a/sys/dev/sound/pci/ich.c b/sys/dev/sound/pci/ich.c index 292ad9f55b..47160a97d6 100644 --- a/sys/dev/sound/pci/ich.c +++ b/sys/dev/sound/pci/ich.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Katsurajima Naoto * Copyright (c) 2001 Cameron Grant * All rights reserved. @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/ich.c,v 1.3.2.12 2003/01/20 03:59:42 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/ich.c,v 1.12 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/ich.c,v 1.53.2.8 2006/08/22 02:37:03 yongari Exp $ + * $DragonFly: src/sys/dev/sound/pci/ich.c,v 1.13 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -35,7 +35,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ich.c,v 1.12 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ich.c,v 1.13 2007/01/04 21:47:02 corecode Exp $"); /* -------------------------------------------------------------------- */ @@ -44,10 +44,84 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/ich.c,v 1.12 2006/12/22 23:2 #define ICH_DEFAULT_BUFSZ 16384 #define ICH_MAX_BUFSZ 65536 -#define SIS7012ID 0x70121039 /* SiS 7012 needs special handling */ -#define ICH4ID 0x24c58086 /* ICH4 needs special handling too */ -#define ICH5ID 0x24d58086 /* ICH5 needs to be treated as ICH4 */ -#define ICH6ID 0x266e8086 /* ICH6 needs to be treated as ICH4 */ +#define INTEL_VENDORID 0x8086 +#define SIS_VENDORID 0x1039 +#define NVIDIA_VENDORID 0x10de +#define AMD_VENDORID 0x1022 + +#define INTEL_82440MX 0x7195 +#define INTEL_82801AA 0x2415 +#define INTEL_82801AB 0x2425 +#define INTEL_82801BA 0x2445 +#define INTEL_82801CA 0x2485 +#define INTEL_82801DB 0x24c5 /* ICH4 needs special handling */ +#define INTEL_82801EB 0x24d5 /* ICH5 needs to be treated as ICH4 */ +#define INTEL_6300ESB 0x25a6 /* 6300ESB needs to be treated as ICH4 */ +#define INTEL_82801FB 0x266e /* ICH6 needs to be treated as ICH4 */ +#define INTEL_82801GB 0x27de /* ICH7 needs to be treated as ICH4 */ +#define SIS_7012 0x7012 /* SiS 7012 needs special handling */ +#define NVIDIA_NFORCE 0x01b1 +#define NVIDIA_NFORCE2 0x006a +#define NVIDIA_NFORCE2_400 0x008a +#define NVIDIA_NFORCE3 0x00da +#define NVIDIA_NFORCE3_250 0x00ea +#define NVIDIA_NFORCE4 0x0059 +#define NVIDIA_NFORCE_410_MCP 0x026b +#define AMD_768 0x7445 +#define AMD_8111 0x746d + +#define ICH_LOCK(sc) snd_mtxlock((sc)->ich_lock) +#define ICH_UNLOCK(sc) snd_mtxunlock((sc)->ich_lock) +#define ICH_LOCK_ASSERT(sc) snd_mtxassert((sc)->ich_lock) + +static const struct ich_type { + uint16_t vendor; + uint16_t devid; + uint32_t options; +#define PROBE_LOW 0x01 + char *name; +} ich_devs[] = { + { INTEL_VENDORID, INTEL_82440MX, 0, + "Intel 440MX" }, + { INTEL_VENDORID, INTEL_82801AA, 0, + "Intel ICH (82801AA)" }, + { INTEL_VENDORID, INTEL_82801AB, 0, + "Intel ICH (82801AB)" }, + { INTEL_VENDORID, INTEL_82801BA, 0, + "Intel ICH2 (82801BA)" }, + { INTEL_VENDORID, INTEL_82801CA, 0, + "Intel ICH3 (82801CA)" }, + { INTEL_VENDORID, INTEL_82801DB, PROBE_LOW, + "Intel ICH4 (82801DB)" }, + { INTEL_VENDORID, INTEL_82801EB, PROBE_LOW, + "Intel ICH5 (82801EB)" }, + { INTEL_VENDORID, INTEL_6300ESB, PROBE_LOW, + "Intel 6300ESB" }, + { INTEL_VENDORID, INTEL_82801FB, PROBE_LOW, + "Intel ICH6 (82801FB)" }, + { INTEL_VENDORID, INTEL_82801GB, PROBE_LOW, + "Intel ICH7 (82801GB)" }, + { SIS_VENDORID, SIS_7012, 0, + "SiS 7012" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE, 0, + "nVidia nForce" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE2, 0, + "nVidia nForce2" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE2_400, 0, + "nVidia nForce2 400" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE3, 0, + "nVidia nForce3" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE3_250, 0, + "nVidia nForce3 250" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE4, 0, + "nVidia nForce4" }, + { NVIDIA_VENDORID, NVIDIA_NFORCE_410_MCP, 0, + "nVidia nForce 410 MCP" }, + { AMD_VENDORID, AMD_768, 0, + "AMD-768" }, + { AMD_VENDORID, AMD_8111, 0, + "AMD-8111" } +}; /* buffer descriptor */ struct ich_desc { @@ -70,6 +144,7 @@ struct sc_chinfo { struct sc_info *parent; struct ich_desc *dtbl; + bus_addr_t desc_addr; }; /* device private data */ @@ -91,8 +166,14 @@ struct sc_info { struct sc_chinfo ch[3]; int ac97rate; struct ich_desc *dtbl; + bus_addr_t desc_addr; struct intr_config_hook intrhook; int use_intrhook; + uint16_t vendor; + uint16_t devid; + uint32_t flags; +#define IGNORE_PCR 0x01 + struct spinlock *ich_lock; }; /* -------------------------------------------------------------------- */ @@ -106,7 +187,7 @@ static struct pcmchan_caps ich_caps = {48000, 48000, ich_fmt, 0}; /* -------------------------------------------------------------------- */ /* Hardware */ -static u_int32_t +static __inline u_int32_t ich_rd(struct sc_info *sc, int regno, int size) { switch (size) { @@ -121,7 +202,7 @@ ich_rd(struct sc_info *sc, int regno, int size) } } -static void +static __inline void ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { @@ -149,7 +230,10 @@ ich_waitcd(void *devinfo) data = ich_rd(sc, ICH_REG_ACC_SEMA, 1); if ((data & 0x01) == 0) return 0; + DELAY(1); } + if ((sc->flags & IGNORE_PCR) != 0) + return (0); device_printf(sc->dev, "CODEC semaphore timeout\n"); return ETIMEDOUT; } @@ -190,15 +274,15 @@ AC97_DECLARE(ich_ac97); static void ich_filldtbl(struct sc_chinfo *ch) { + struct sc_info *sc = ch->parent; u_int32_t base; int i; - base = vtophys(sndbuf_getbuf(ch->buffer)); - ch->blkcnt = sndbuf_getsize(ch->buffer) / ch->blksz; - if (ch->blkcnt != 2 && ch->blkcnt != 4 && ch->blkcnt != 8 && ch->blkcnt != 16 && ch->blkcnt != 32) { - ch->blkcnt = 2; - ch->blksz = sndbuf_getsize(ch->buffer) / ch->blkcnt; - } + base = sndbuf_getbufaddr(ch->buffer); + if (ch->blksz > sc->bufsz / ch->blkcnt) + ch->blksz = sc->bufsz / ch->blkcnt; + sndbuf_resize(ch->buffer, ch->blkcnt, ch->blksz); + ch->blksz = sndbuf_getblksz(ch->buffer); for (i = 0; i < ICH_DTBL_LENGTH; i++) { ch->dtbl[i].buffer = base + (ch->blksz * (i % ch->blkcnt)); @@ -222,7 +306,12 @@ ich_resetchan(struct sc_info *sc, int num) return ENXIO; ich_wr(sc, regbase + ICH_REG_X_CR, 0, 1); +#if 1 + /* This may result in no sound output on NForce 2 MBs, see PR 73987 */ DELAY(100); +#else + (void)ich_rd(sc, regbase + ICH_REG_X_CR, 1); +#endif ich_wr(sc, regbase + ICH_REG_X_CR, ICH_X_CR_RR, 1); for (i = 0; i < ICH_TIMEOUT; i++) { cr = ich_rd(sc, regbase + ICH_REG_X_CR, 1); @@ -244,6 +333,7 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * struct sc_chinfo *ch; unsigned int num; + ICH_LOCK(sc); num = sc->chnum++; ch = &sc->ch[num]; ch->num = num; @@ -252,6 +342,8 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->parent = sc; ch->run = 0; ch->dtbl = sc->dtbl + (ch->num * ICH_DTBL_LENGTH); + ch->desc_addr = sc->desc_addr + (ch->num * ICH_DTBL_LENGTH) * + sizeof(struct ich_desc); ch->blkcnt = 2; ch->blksz = sc->bufsz / ch->blkcnt; @@ -281,10 +373,13 @@ ichchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * return NULL; } - if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz)) + ICH_UNLOCK(sc); + if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0) return NULL; - ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); + ICH_LOCK(sc); + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); + ICH_UNLOCK(sc); return ch; } @@ -302,16 +397,20 @@ ichchan_setspeed(kobj_t obj, void *data, u_int32_t speed) struct sc_info *sc = ch->parent; if (ch->spdreg) { - int r; + int r, ac97rate; + + ICH_LOCK(sc); if (sc->ac97rate <= 32000 || sc->ac97rate >= 64000) sc->ac97rate = 48000; - r = (speed * 48000) / sc->ac97rate; + ac97rate = sc->ac97rate; + ICH_UNLOCK(sc); + r = (speed * 48000) / ac97rate; /* * Cast the return value of ac97_setrate() to u_int so that * the math don't overflow into the negative range. */ ch->spd = ((u_int)ac97_setrate(sc->codec, ch->spdreg, r) * - sc->ac97rate) / 48000; + ac97rate) / 48000; } else { ch->spd = 48000; } @@ -326,7 +425,9 @@ ichchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) ch->blksz = blocksize; ich_filldtbl(ch); + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_LVI, ch->blkcnt - 1, 1); + ICH_UNLOCK(sc); return ch->blksz; } @@ -340,12 +441,16 @@ ichchan_trigger(kobj_t obj, void *data, int go) switch (go) { case PCMTRIG_START: ch->run = 1; - ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); + ICH_LOCK(sc); + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); ich_wr(sc, ch->regbase + ICH_REG_X_CR, ICH_X_CR_RPBM | ICH_X_CR_LVBIE | ICH_X_CR_IOCE, 1); + ICH_UNLOCK(sc); break; case PCMTRIG_ABORT: + ICH_LOCK(sc); ich_resetchan(sc, ch->num); + ICH_UNLOCK(sc); ch->run = 0; break; } @@ -359,7 +464,9 @@ ichchan_getptr(kobj_t obj, void *data) struct sc_info *sc = ch->parent; u_int32_t pos; + ICH_LOCK(sc); ch->civ = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1) % ch->blkcnt; + ICH_UNLOCK(sc); pos = ch->civ * ch->blksz; @@ -397,6 +504,7 @@ ich_intr(void *p) u_int32_t cbi, lbi, lvi, st, gs; int i; + ICH_LOCK(sc); gs = ich_rd(sc, ICH_REG_GLOB_STA, 4) & ICH_GLOB_STA_IMASK; if (gs & (ICH_GLOB_STA_PRES | ICH_GLOB_STA_SRES)) { /* Clear resume interrupt(s) - nothing doing with them */ @@ -415,8 +523,11 @@ ich_intr(void *p) st &= ICH_X_SR_FIFOE | ICH_X_SR_BCIS | ICH_X_SR_LVBCI; if (st & (ICH_X_SR_BCIS | ICH_X_SR_LVBCI)) { /* block complete - update buffer */ - if (ch->run) + if (ch->run) { + ICH_UNLOCK(sc); chn_intr(ch->channel); + ICH_LOCK(sc); + } lvi = ich_rd(sc, ch->regbase + ICH_REG_X_LVI, 1); cbi = ch->civ % ch->blkcnt; if (cbi == 0) @@ -437,6 +548,7 @@ ich_intr(void *p) (sc->swap_reg ? ICH_REG_X_PICB : ICH_REG_X_SR), st, 2); } + ICH_UNLOCK(sc); if (gs != 0) { device_printf(sc->dev, "Unhandled interrupt, gs_intr = %x\n", gs); @@ -508,7 +620,7 @@ void ich_calibrate(void *arg) /* prepare */ ociv = ich_rd(sc, ch->regbase + ICH_REG_X_CIV, 1); nciv = ociv; - ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)vtophys(ch->dtbl), 4); + ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); /* start */ microtime(&t1); @@ -562,6 +674,8 @@ void ich_calibrate(void *arg) static void ich_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { + struct sc_info *sc = (struct sc_info *)arg; + sc->desc_addr = segs->ds_addr; return; } @@ -569,7 +683,6 @@ static int ich_init(struct sc_info *sc) { u_int32_t stat; - int sz; ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD, 4); DELAY(600000); @@ -577,125 +690,75 @@ ich_init(struct sc_info *sc) if ((stat & ICH_GLOB_STA_PCR) == 0) { /* ICH4/ICH5 may fail when busmastering is enabled. Continue */ - if ((pci_get_devid(sc->dev) != ICH4ID) && - (pci_get_devid(sc->dev) != ICH5ID) && - (pci_get_devid(sc->dev) != ICH6ID)) { - return ENXIO; + if (sc->vendor == INTEL_VENDORID && ( + sc->devid == INTEL_82801DB || sc->devid == INTEL_82801EB || + sc->devid == INTEL_6300ESB || sc->devid == INTEL_82801FB || + sc->devid == INTEL_82801GB)) { + sc->flags |= IGNORE_PCR; + device_printf(sc->dev, "primary codec not ready!\n"); } } +#if 0 ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD | ICH_GLOB_CTL_PRES, 4); +#else + ich_wr(sc, ICH_REG_GLOB_CNT, ICH_GLOB_CTL_COLD, 4); +#endif if (ich_resetchan(sc, 0) || ich_resetchan(sc, 1)) return ENXIO; if (sc->hasmic && ich_resetchan(sc, 2)) return ENXIO; - if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, BUS_DMA_NOWAIT, &sc->dtmap)) - return ENOSPC; - - sz = sizeof(struct ich_desc) * ICH_DTBL_LENGTH * 3; - if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, sz, ich_setmap, NULL, 0)) { - bus_dmamem_free(sc->dmat, (void **)&sc->dtbl, sc->dtmap); - return ENOSPC; - } - return 0; } static int ich_pci_probe(device_t dev) { - switch(pci_get_devid(dev)) { - case 0x71958086: - device_set_desc(dev, "Intel 443MX"); - return 0; - - case 0x24158086: - device_set_desc(dev, "Intel ICH (82801AA)"); - return 0; - - case 0x24258086: - device_set_desc(dev, "Intel ICH (82801AB)"); - return 0; - - case 0x24458086: - device_set_desc(dev, "Intel ICH2 (82801BA)"); - return 0; - - case 0x24858086: - device_set_desc(dev, "Intel ICH3 (82801CA)"); - return 0; - - case ICH4ID: - device_set_desc(dev, "Intel ICH4 (82801DB)"); - return -1000; /* allow a better driver to override us */ - - case ICH5ID: - device_set_desc(dev, "Intel ICH5 (82801EB)"); - return -1000; /* allow a better driver to override us */ - - case ICH6ID: - device_set_desc(dev, "Intel ICH6 (82801FB)"); - return -1000; /* allow a better driver to override us */ - - case SIS7012ID: - device_set_desc(dev, "SiS 7012"); - return 0; - - case 0x01b110de: - device_set_desc(dev, "nVidia nForce"); - return 0; - - case 0x006a10de: - device_set_desc(dev, "nVidia nForce2"); - return 0; - - case 0x008a10de: - device_set_desc(dev, "nVidia nForce2 400"); - return 0; - - case 0x00da10de: - device_set_desc(dev, "nVidia nForce3"); - return 0; - - case 0x00ea10de: - device_set_desc(dev, "nVidia nForce3 250"); - return 0; - - case 0x74451022: - device_set_desc(dev, "AMD-768"); - return 0; - - case 0x746d1022: - device_set_desc(dev, "AMD-8111"); - return 0; - - default: - return ENXIO; + int i; + uint16_t devid, vendor; + + vendor = pci_get_vendor(dev); + devid = pci_get_device(dev); + for (i = 0; i < sizeof(ich_devs)/sizeof(ich_devs[0]); i++) { + if (vendor == ich_devs[i].vendor && + devid == ich_devs[i].devid) { + device_set_desc(dev, ich_devs[i].name); + /* allow a better driver to override us */ + if ((ich_devs[i].options & PROBE_LOW) != 0) + return (BUS_PROBE_LOW_PRIORITY); + return (BUS_PROBE_DEFAULT); + } } + return (ENXIO); } static int ich_pci_attach(device_t dev) { + uint32_t subdev; u_int16_t extcaps; + uint16_t devid, vendor; struct sc_info *sc; char status[SND_STATUSLEN]; - if ((sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { + if ((sc = kmalloc(sizeof(*sc), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } - bzero(sc, sizeof(*sc)); + sc->ich_lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); sc->dev = dev; + vendor = sc->vendor = pci_get_vendor(dev); + devid = sc->devid = pci_get_device(dev); + subdev = (pci_get_subdevice(dev) << 16) | pci_get_subvendor(dev); /* * The SiS 7012 register set isn't quite like the standard ich. * There really should be a general "quirks" mechanism. */ - if (pci_get_devid(dev) == SIS7012ID) { + if (vendor == SIS_VENDORID && devid == SIS_7012) { sc->swap_reg = 1; sc->sample_size = 1; } else { @@ -703,6 +766,12 @@ ich_pci_attach(device_t dev) sc->sample_size = 2; } + /* + * Enable bus master. On ich4/5 this may prevent the detection of + * the primary codec becoming ready in ich_init(). + */ + pci_enable_busmaster(dev); + /* * By default, ich4 has NAMBAR and NABMBAR i/o spaces as * read-only. Need to enable "legacy support", by poking into @@ -710,30 +779,23 @@ ich_pci_attach(device_t dev) * but doing so will mess things up here. ich4 has enough new * features it warrants it's own driver. */ - if (pci_get_devid(dev) == ICH4ID) { - pci_write_config(dev, PCIR_ICH_LEGACY, ICH_LEGACY_ENABLE, 1); - } - - /* - * Enable bus master. On ich4/5 this may prevent the detection of - * the primary codec becoming ready in ich_init(). - */ - pci_enable_busmaster(dev); - - if (pci_get_devid(dev) == ICH5ID || pci_get_devid(dev) == ICH6ID) { + if (vendor == INTEL_VENDORID && (devid == INTEL_82801DB || + devid == INTEL_82801EB || devid == INTEL_6300ESB || + devid == INTEL_82801FB || devid == INTEL_82801GB)) { sc->nambarid = PCIR_MMBAR; sc->nabmbarid = PCIR_MBBAR; sc->regtype = SYS_RES_MEMORY; - pci_enable_io(dev, SYS_RES_MEMORY); + pci_write_config(dev, PCIR_ICH_LEGACY, ICH_LEGACY_ENABLE, 1); } else { sc->nambarid = PCIR_NAMBAR; sc->nabmbarid = PCIR_NABMBAR; sc->regtype = SYS_RES_IOPORT; - pci_enable_io(dev, SYS_RES_IOPORT); } - sc->nambar = bus_alloc_resource(dev, sc->regtype, &sc->nambarid, 0, ~0, 1, RF_ACTIVE); - sc->nabmbar = bus_alloc_resource(dev, sc->regtype, &sc->nabmbarid, 0, ~0, 1, RF_ACTIVE); + sc->nambar = bus_alloc_resource_any(dev, sc->regtype, + &sc->nambarid, RF_ACTIVE); + sc->nabmbar = bus_alloc_resource_any(dev, sc->regtype, + &sc->nabmbarid, RF_ACTIVE); if (!sc->nambar || !sc->nabmbar) { device_printf(dev, "unable to map IO port space\n"); @@ -747,14 +809,16 @@ ich_pci_attach(device_t dev) sc->bufsz = pcm_getbuffersize(dev, 4096, ICH_DEFAULT_BUFSZ, ICH_MAX_BUFSZ); if (bus_dma_tag_create(NULL, 8, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, - NULL, NULL, sc->bufsz, 1, 0x3ffff, 0, &sc->dmat) != 0) { + NULL, NULL, sc->bufsz, 1, 0x3ffff, 0, + &sc->dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); 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 || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih, NULL)) { + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || snd_setup_intr(dev, sc->irq, INTR_MPSAFE, ich_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -764,9 +828,38 @@ ich_pci_attach(device_t dev) goto bad; } + if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, + BUS_DMA_NOWAIT, &sc->dtmap)) + goto bad; + + if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, + sizeof(struct ich_desc) * ICH_DTBL_LENGTH * 3, + ich_setmap, sc, 0)) + goto bad; + sc->codec = AC97_CREATE(dev, sc, ich_ac97); if (sc->codec == NULL) goto bad; + + /* + * Turn on inverted external amplifier sense flags for few + * 'special' boards. + */ + switch (subdev) { + case 0x202f161f: /* Gateway 7326GZ */ + case 0x203a161f: /* Gateway 4028GZ */ + case 0x204c161f: /* Kvazar-Micro Senator 3592XT */ + case 0x8144104d: /* Sony VAIO PCG-TR* */ + case 0x8197104d: /* Sony S1XP */ + case 0x81c0104d: /* Sony VAIO type T */ + case 0x81c5104d: /* Sony VAIO VGN B1VP/B1XP */ + case 0x3089103c: /* Compaq Presario B3800 */ + ac97_setflags(sc->codec, ac97_getflags(sc->codec) | AC97_F_EAPD_INV); + break; + default: + break; + } + mixer_init(dev, ac97_getmixerclass(), sc->codec); /* check and set VRA function */ @@ -784,8 +877,8 @@ ich_pci_attach(device_t dev) if (sc->hasmic) pcm_addchan(dev, PCMDIR_REC, &ichchan_class, sc); /* record mic */ - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx, 0x%lx irq %ld bufsz %u", - rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq), sc->bufsz); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx, 0x%lx irq %ld bufsz %u %s", + rman_get_start(sc->nambar), rman_get_start(sc->nabmbar), rman_get_start(sc->irq), sc->bufsz,PCM_KLDSTRING(snd_ich)); pcm_setstatus(dev, status); @@ -793,7 +886,6 @@ ich_pci_attach(device_t dev) sc->intrhook.ich_func = ich_calibrate; sc->intrhook.ich_arg = sc; - sc->intrhook.ich_desc = "ich"; sc->use_intrhook = 1; if (config_intrhook_establish(&sc->intrhook) != 0) { device_printf(dev, "Cannot establish calibration hook, will calibrate now\n"); @@ -816,6 +908,12 @@ bad: if (sc->nabmbar) bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); + if (sc->dtmap) + bus_dmamap_unload(sc->dmat, sc->dtmap); + if (sc->dmat) + bus_dma_tag_destroy(sc->dmat); + if (sc->ich_lock) + snd_mtxfree(sc->ich_lock); kfree(sc, M_DEVBUF); return ENXIO; } @@ -835,11 +933,35 @@ ich_pci_detach(device_t dev) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); bus_release_resource(dev, sc->regtype, sc->nambarid, sc->nambar); bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); + bus_dmamap_unload(sc->dmat, sc->dtmap); bus_dma_tag_destroy(sc->dmat); + snd_mtxfree(sc->ich_lock); kfree(sc, M_DEVBUF); return 0; } +static void +ich_pci_codec_reset(struct sc_info *sc) +{ + int i; + uint32_t control; + + control = ich_rd(sc, ICH_REG_GLOB_CNT, 4); + control &= ~(ICH_GLOB_CTL_SHUT); + control |= (control & ICH_GLOB_CTL_COLD) ? + ICH_GLOB_CTL_WARM : ICH_GLOB_CTL_COLD; + ich_wr(sc, ICH_REG_GLOB_CNT, control, 4); + + for (i = 500000; i; i--) { + if (ich_rd(sc, ICH_REG_GLOB_STA, 4) & ICH_GLOB_STA_PCR) + break; /* or ICH_SCR? */ + DELAY(1); + } + + if (i <= 0) + kprintf("%s: time out\n", __func__); +} + static int ich_pci_suspend(device_t dev) { @@ -847,12 +969,16 @@ ich_pci_suspend(device_t dev) int i; sc = pcm_getdevinfo(dev); + ICH_LOCK(sc); for (i = 0 ; i < 3; i++) { sc->ch[i].run_save = sc->ch[i].run; if (sc->ch[i].run) { + ICH_UNLOCK(sc); ichchan_trigger(0, &sc->ch[i], PCMTRIG_ABORT); + ICH_LOCK(sc); } } + ICH_UNLOCK(sc); return 0; } @@ -864,12 +990,23 @@ ich_pci_resume(device_t dev) sc = pcm_getdevinfo(dev); + if (sc->regtype == SYS_RES_IOPORT) + pci_enable_io(dev, SYS_RES_IOPORT); + else + pci_enable_io(dev, SYS_RES_MEMORY); + pci_enable_busmaster(dev); + + ICH_LOCK(sc); /* Reinit audio device */ if (ich_init(sc) == -1) { device_printf(dev, "unable to reinitialize the card\n"); + ICH_UNLOCK(sc); return ENXIO; } /* Reinit mixer */ + ich_pci_codec_reset(sc); + ICH_UNLOCK(sc); + ac97_setextmode(sc->codec, sc->hasvra | sc->hasvrm); if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); return ENXIO; @@ -903,5 +1040,5 @@ static driver_t ich_driver = { }; DRIVER_MODULE(snd_ich, pci, ich_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_ich, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_ich, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_ich, 1); diff --git a/sys/dev/sound/pci/ich.h b/sys/dev/sound/pci/ich.h index 5310cd192c..649d9d8335 100644 --- a/sys/dev/sound/pci/ich.h +++ b/sys/dev/sound/pci/ich.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 Katsurajima Naoto * Copyright (c) 2001 Cameron Grant * All rights reserved. @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/ich.h,v 1.1.2.3 2002/08/22 16:38:21 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/ich.h,v 1.3 2003/08/24 17:55:21 drhodus Exp $ + * $FreeBSD: src/sys/dev/sound/pci/ich.h,v 1.4 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/ich.h,v 1.4 2007/01/04 21:47:02 corecode Exp $ */ #define PCIR_NAMBAR 0x10 diff --git a/sys/dev/sound/pci/maestro.c b/sys/dev/sound/pci/maestro.c index 24a7da1341..fd9f51dfcb 100644 --- a/sys/dev/sound/pci/maestro.c +++ b/sys/dev/sound/pci/maestro.c @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2000 Taku YAMAMOTO + * Copyright (c) 2000-2004 Taku YAMAMOTO * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,9 +23,9 @@ * 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: src/sys/dev/sound/pci/maestro.c,v 1.2.2.5 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/maestro.c,v 1.8 2006/12/22 23:26:25 swildner Exp $ + * maestro.c,v 1.23.2.1 2003/10/03 18:21:38 taku Exp + * $FreeBSD: src/sys/dev/sound/pci/maestro.c,v 1.28.2.3 2006/02/04 11:58:28 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/maestro.c,v 1.9 2007/01/04 21:47:02 corecode Exp $ */ /* @@ -37,13 +37,16 @@ * Zach Brown . * * busdma()-ize and buffer size reduction were suggested by - * Cameron Grant . + * 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. + * + * Hardware volume controller was implemented by + * John Baldwin . */ #include @@ -53,7 +56,8 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro.c,v 1.8 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro.c,v 1.9 2007/01/04 21:47:02 corecode Exp $"); + #define inline __inline @@ -72,60 +76,148 @@ SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro.c,v 1.8 2006/12/22 2 #define NEC_SUBID1 0x80581033 /* Taken from Linux driver */ #define NEC_SUBID2 0x803c1033 /* NEC VersaProNX VA26D */ -#ifndef AGG_MAXPLAYCH +#ifdef AGG_MAXPLAYCH +# if AGG_MAXPLAYCH > 4 +# undef AGG_MAXPLAYCH +# define AGG_MAXPLAYCH 4 +# endif +#else # define AGG_MAXPLAYCH 4 #endif #define AGG_DEFAULT_BUFSZ 0x4000 /* 0x1000, but gets underflows */ +/* compatibility */ +# define critical_enter() crit_enter() +# define critical_exit() crit_exit() + +#ifndef PCIR_BAR +#define PCIR_BAR(x) (PCIR_MAPS + (x) * 4) +#endif + + /* ----------------------------- * Data structures. */ struct agg_chinfo { + /* parent softc */ + struct agg_info *parent; + + /* FreeBSD newpcm related */ + struct pcm_channel *channel; + struct snd_dbuf *buffer; + + /* OS independent */ + bus_addr_t phys; /* channel buffer physical address */ + bus_addr_t base; /* channel buffer segment base */ + u_int32_t blklen; /* DMA block length in WORDs */ + u_int32_t buflen; /* channel buffer length in WORDs */ + u_int32_t speed; + unsigned num : 3; + unsigned stereo : 1; + unsigned qs16 : 1; /* quantum size is 16bit */ + unsigned us : 1; /* in unsigned format */ +}; + +struct agg_rchinfo { + /* parent softc */ struct agg_info *parent; + + /* FreeBSD newpcm related */ struct pcm_channel *channel; struct snd_dbuf *buffer; - bus_addr_t offset; - u_int32_t blocksize; + + /* OS independent */ + bus_addr_t phys; /* channel buffer physical address */ + bus_addr_t base; /* channel buffer segment base */ + u_int32_t blklen; /* DMA block length in WORDs */ + u_int32_t buflen; /* channel buffer length in WORDs */ u_int32_t speed; - int dir; - u_int num; - u_int16_t aputype; - u_int16_t wcreg_tpl; + unsigned : 3; + unsigned stereo : 1; + bus_addr_t srcphys; + int16_t *src; /* stereo peer buffer */ + int16_t *sink; /* channel buffer pointer */ + volatile u_int32_t hwptr; /* ready point in 16bit sample */ }; struct agg_info { + /* FreeBSD newbus related */ device_t dev; + + /* I wonder whether bus_space_* are in common in *BSD... */ 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; + bus_dma_tag_t buf_dmat; + bus_dma_tag_t stat_dmat; + /* FreeBSD SMPng related */ +#ifdef USING_MUTEX + struct spinlock *lock; /* mutual exclusion */ +#endif + /* FreeBSD newpcm related */ struct ac97_info *codec; - void *lock; - unsigned int bufsz; - u_int playchns, active; + /* OS independent */ + u_int8_t *stat; /* status buffer pointer */ + bus_addr_t phys; /* status buffer physical address */ + unsigned int bufsz; /* channel buffer size in bytes */ + u_int playchns; + volatile u_int active; struct agg_chinfo pch[AGG_MAXPLAYCH]; - struct agg_chinfo rch; + struct agg_rchinfo rch; + volatile u_int8_t curpwr; /* current power status: D[0-3] */ }; + +/* ----------------------------- + * Sysctls for debug. + */ +static unsigned int powerstate_active = PCI_POWERSTATE_D1; +#ifdef MAESTRO_AGGRESSIVE_POWERSAVE +static unsigned int powerstate_idle = PCI_POWERSTATE_D2; +#else +static unsigned int powerstate_idle = PCI_POWERSTATE_D1; +#endif +static unsigned int powerstate_init = PCI_POWERSTATE_D2; + +SYSCTL_NODE(_debug, OID_AUTO, maestro, CTLFLAG_RD, 0, ""); +SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_active, CTLFLAG_RW, + &powerstate_active, 0, "The Dx power state when active (0-1)"); +SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_idle, CTLFLAG_RW, + &powerstate_idle, 0, "The Dx power state when idle (0-2)"); +SYSCTL_UINT(_debug_maestro, OID_AUTO, powerstate_init, CTLFLAG_RW, + &powerstate_init, 0, "The Dx power state prior to the first use (0-2)"); + + +/* ----------------------------- + * Prototypes + */ + +static inline void agg_lock(struct agg_info*); +static inline void agg_unlock(struct agg_info*); +static inline void agg_sleep(struct agg_info*, const char *wmesg, int msec); + +static inline u_int32_t agg_rd(struct agg_info*, int, int size); +static inline void agg_wr(struct agg_info*, int, u_int32_t data, int size); + +static inline int agg_rdcodec(struct agg_info*, int); +static inline int agg_wrcodec(struct agg_info*, 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 u_int16_t wp_rdapu(struct agg_info*, unsigned, u_int16_t); +static inline void wp_wrapu(struct agg_info*, unsigned, 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*); @@ -135,16 +227,22 @@ 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 inline void agg_stopclock(struct agg_info*, int part, int st); +static inline void agg_initcodec(struct agg_info*); static void agg_init(struct agg_info*); +static void agg_power(struct agg_info*, int); static void aggch_start_dac(struct agg_chinfo*); static void aggch_stop_dac(struct agg_chinfo*); +static void aggch_start_adc(struct agg_rchinfo*); +static void aggch_stop_adc(struct agg_rchinfo*); +static void aggch_feed_adc_stereo(struct agg_rchinfo*); +static void aggch_feed_adc_mono(struct agg_rchinfo*); static inline void suppress_jitter(struct agg_chinfo*); +static inline void suppress_rec_jitter(struct agg_rchinfo*); -static inline u_int calc_timer_freq(struct agg_chinfo*); static void set_timer(struct agg_info*); static void agg_intr(void *); @@ -155,181 +253,260 @@ 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 *); +static void *dma_malloc(bus_dma_tag_t, u_int32_t, bus_addr_t*); +static void dma_free(bus_dma_tag_t, void *); + /* ----------------------------- * Subsystems. */ -/* Codec/Ringbus */ +/* locking */ -/* -------------------------------------------------------------------- */ +static inline void +agg_lock(struct agg_info *sc) +{ +#ifdef USING_MUTEX + snd_mtxlock(sc->lock); +#endif +} -static u_int32_t -agg_ac97_init(kobj_t obj, void *sc) +static inline void +agg_unlock(struct agg_info *sc) { - struct agg_info *ess = sc; +#ifdef USING_MUTEX + snd_mtxunlock(sc->lock); +#endif +} - return (bus_space_read_1(ess->st, ess->sh, PORT_CODEC_STAT) & CODEC_STAT_MASK)? 0 : 1; +static inline void +agg_sleep(struct agg_info *sc, const char *wmesg, int msec) +{ + int timo; + + timo = msec * hz / 1000; + if (timo == 0) + timo = 1; +#ifdef USING_MUTEX + msleep(sc, sc->lock, 0, wmesg, timo); +#else + tsleep(sc, PWAIT, wmesg, timo); +#endif } -static int -agg_rdcodec(kobj_t obj, void *sc, int regno) + +/* I/O port */ + +static inline u_int32_t +agg_rd(struct agg_info *sc, int regno, int size) { - struct agg_info *ess = sc; - unsigned t; + 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 ~(u_int32_t)0; + } +} - /* 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; +#define AGG_RD(sc, regno, size) \ + bus_space_read_##size( \ + ((struct agg_info*)(sc))->st, \ + ((struct agg_info*)(sc))->sh, (regno)) + +static inline void +agg_wr(struct agg_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; + } +} + +#define AGG_WR(sc, regno, data, size) \ + bus_space_write_##size( \ + ((struct agg_info*)(sc))->st, \ + ((struct agg_info*)(sc))->sh, (regno), (data)) + +/* -------------------------------------------------------------------- */ + +/* Codec/Ringbus */ + +static inline int +agg_codec_wait4idle(struct agg_info *ess) +{ + unsigned t = 26; + + while (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK) { + if (--t == 0) + return EBUSY; DELAY(2); /* 20.8us / 13 */ } - if (t == 20) + return 0; +} + + +static inline int +agg_rdcodec(struct agg_info *ess, int regno) +{ + int ret; + + /* We have to wait for a SAFE time to write addr/data */ + if (agg_codec_wait4idle(ess)) { + /* Timed out. No read performed. */ device_printf(ess->dev, "agg_rdcodec() PROGLESS timed out.\n"); + return -1; + } - bus_space_write_1(ess->st, ess->sh, PORT_CODEC_CMD, - CODEC_CMD_READ | regno); - DELAY(21); /* AC97 cycle = 20.8usec */ + AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_READ | regno, 1); + /*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. */ + if (!agg_codec_wait4idle(ess)) { + ret = AGG_RD(ess, PORT_CODEC_REG, 2); + } else { + /* Timed out. No read performed. */ device_printf(ess->dev, "agg_rdcodec() RW_DONE timed out.\n"); + ret = -1; + } - return bus_space_read_2(ess->st, ess->sh, PORT_CODEC_REG); + return ret; } -static int -agg_wrcodec(kobj_t obj, void *sc, int regno, u_int32_t data) +static inline int +agg_wrcodec(struct agg_info *ess, 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) { + if (agg_codec_wait4idle(ess)) { /* Timed out. Abort writing. */ device_printf(ess->dev, "agg_wrcodec() PROGLESS timed out.\n"); 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); + AGG_WR(ess, PORT_CODEC_REG, data, 2); + AGG_WR(ess, PORT_CODEC_CMD, CODEC_CMD_WRITE | regno, 1); + + /* Wait for write completion */ + if (agg_codec_wait4idle(ess)) { + /* Timed out. */ + device_printf(ess->dev, "agg_wrcodec() RW_DONE timed out.\n"); + return -1; + } 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 = AGG_RD(ess, PORT_RINGBUS_CTRL, 4); data &= ~(0xfU << src); data |= (0xfU & dest) << src; - bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, data); + AGG_WR(ess, PORT_RINGBUS_CTRL, data, 4); } +/* -------------------------------------------------------------------- */ + /* 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); + AGG_WR(ess, PORT_DSP_INDEX, reg, 2); + return AGG_RD(ess, PORT_DSP_DATA, 2); } 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); + AGG_WR(ess, PORT_DSP_INDEX, reg, 2); + AGG_WR(ess, PORT_DSP_DATA, data, 2); } -static inline void -apu_setindex(struct agg_info *ess, u_int16_t reg) +static inline int +wp_wait_data(struct agg_info *ess, u_int16_t data) { - int t; + unsigned t = 0; - 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); + while (AGG_RD(ess, PORT_DSP_DATA, 2) != data) { + if (++t == 1000) { + return EAGAIN; + } + AGG_WR(ess, PORT_DSP_DATA, data, 2); } - if (t == 1000) - device_printf(ess->dev, "apu_setindex() timed out.\n"); + + return 0; } static inline u_int16_t -wp_rdapu(struct agg_info *ess, int ch, u_int16_t reg) +wp_rdapu(struct agg_info *ess, unsigned 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; + wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); + if (wp_wait_data(ess, reg | (ch << 4)) != 0) + device_printf(ess->dev, "wp_rdapu() indexing timed out.\n"); + return wp_rdreg(ess, WPREG_DATA_PORT); } static inline void -wp_wrapu(struct agg_info *ess, int ch, u_int16_t reg, u_int16_t data) +wp_wrapu(struct agg_info *ess, unsigned 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); + wp_wrreg(ess, WPREG_CRAM_PTR, reg | (ch << 4)); + if (wp_wait_data(ess, reg | (ch << 4)) == 0) { + wp_wrreg(ess, WPREG_DATA_PORT, data); + if (wp_wait_data(ess, data) != 0) + device_printf(ess->dev, "wp_wrapu() write timed out.\n"); + } else { + device_printf(ess->dev, "wp_wrapu() indexing timed out.\n"); } - if (t == 1000) - device_printf(ess->dev, "wp_wrapu() timed out.\n"); +} + +static void +apu_setparam(struct agg_info *ess, int apuch, + u_int32_t wpwa, u_int16_t size, int16_t pan, u_int dv) +{ + wp_wrapu(ess, apuch, APUREG_WAVESPACE, (wpwa >> 8) & APU_64KPAGE_MASK); + wp_wrapu(ess, apuch, APUREG_CURPTR, wpwa); + wp_wrapu(ess, apuch, APUREG_ENDPTR, wpwa + size); + wp_wrapu(ess, apuch, APUREG_LOOPLEN, size); + wp_wrapu(ess, apuch, APUREG_ROUTING, 0); + wp_wrapu(ess, apuch, APUREG_AMPLITUDE, 0xf000); + wp_wrapu(ess, apuch, APUREG_POSITION, 0x8f00 + | (APU_RADIUS_MASK & (RADIUS_CENTERCIRCLE << APU_RADIUS_SHIFT)) + | (APU_PAN_MASK & ((pan + PAN_FRONT) << APU_PAN_SHIFT))); + wp_wrapu(ess, apuch, APUREG_FREQ_LOBYTE, + APU_plus6dB | ((dv & 0xff) << APU_FREQ_LOBYTE_SHIFT)); + wp_wrapu(ess, apuch, APUREG_FREQ_HIWORD, dv >> 8); } static inline void -wp_settimer(struct agg_info *ess, u_int freq) +wp_settimer(struct agg_info *ess, u_int divide) { - u_int clock = 48000 << 2; - u_int prescale = 0, divide = (freq != 0) ? (clock / freq) : ~0; + u_int prescale = 0; - RANGE(divide, 4, 32 << 8); + RANGE(divide, 2, 32 << 7); - for (; divide > 32 << 1; divide >>= 1) + for (; divide > 32; divide >>= 1) { prescale++; - divide = (divide + 1) >> 1; + divide++; + } for (; prescale < 7 && divide > 2 && !(divide & 1); divide >>= 1) prescale++; wp_wrreg(ess, WPREG_TIMER_ENABLE, 0); - wp_wrreg(ess, WPREG_TIMER_FREQ, + wp_wrreg(ess, WPREG_TIMER_FREQ, 0x9000 | (prescale << WP_TIMER_FREQ_PRESCALE_SHIFT) | (divide - 1)); wp_wrreg(ess, WPREG_TIMER_ENABLE, 1); } @@ -337,30 +514,37 @@ wp_settimer(struct agg_info *ess, u_int freq) static inline void wp_starttimer(struct agg_info *ess) { + AGG_WR(ess, PORT_INT_STAT, 1, 2); + AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_INT_ENABLED + | AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); wp_wrreg(ess, WPREG_TIMER_START, 1); } static inline void wp_stoptimer(struct agg_info *ess) { + AGG_WR(ess, PORT_HOSTINT_CTRL, ~HOSTINT_CTRL_DSOUND_INT_ENABLED + & AGG_RD(ess, PORT_HOSTINT_CTRL, 2), 2); + AGG_WR(ess, PORT_INT_STAT, 1, 2); 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); + AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); + return AGG_RD(ess, PORT_WAVCACHE_DATA, 2); } 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); + AGG_WR(ess, PORT_WAVCACHE_INDEX, reg, 2); + AGG_WR(ess, PORT_WAVCACHE_DATA, data, 2); } static inline u_int16_t @@ -375,16 +559,26 @@ wc_wrchctl(struct agg_info *ess, int ch, u_int16_t data) wc_wrreg(ess, ch << 3, data); } -/* Power management */ +/* -------------------------------------------------------------------- */ +/* Power management */ static inline void -agg_power(struct agg_info *ess, int status) +agg_stopclock(struct agg_info *ess, int part, int st) { - u_int8_t data; + u_int32_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); + data = pci_read_config(ess->dev, CONF_ACPI_STOPCLOCK, 4); + if (part < 16) { + if (st == PCI_POWERSTATE_D1) + data &= ~(1 << part); + else + data |= (1 << part); + if (st == PCI_POWERSTATE_D1 || st == PCI_POWERSTATE_D2) + data |= (0x10000 << part); + else + data &= ~(0x10000 << part); + pci_write_config(ess->dev, CONF_ACPI_STOPCLOCK, data, 4); + } } @@ -397,46 +591,38 @@ 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); + if (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) & RINGBUS_CTRL_ACLINK_ENABLED) { + AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 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); + AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_AC97_SWRESET, 4); DELAY(2); - bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, - RINGBUS_CTRL_ACLINK_ENABLED); - DELAY(21); - - 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); + AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); + DELAY(50); + + if (agg_rdcodec(ess, 0) < 0) { + AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); 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); + data = AGG_RD(ess, PORT_GPIO_DIR, 2); 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); + data |= 0x009 & ~AGG_RD(ess, PORT_GPIO_DATA, 2); + AGG_WR(ess, PORT_GPIO_MASK, 0xff6, 2); + AGG_WR(ess, PORT_GPIO_DIR, data | 0x009, 2); + AGG_WR(ess, PORT_GPIO_DATA, 0x000, 2); DELAY(2); - bus_space_write_2(ess->st, ess->sh, PORT_GPIO_DATA, 0x001); + AGG_WR(ess, PORT_GPIO_DATA, 0x001, 2); 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); + AGG_WR(ess, PORT_GPIO_DATA, 0x009, 2); + agg_sleep(ess, "agginicd", 500); + AGG_WR(ess, PORT_GPIO_DIR, data, 2); DELAY(84); /* 20.8us * 4 */ - bus_space_write_4(ess->st, ess->sh, PORT_RINGBUS_CTRL, - RINGBUS_CTRL_ACLINK_ENABLED); - DELAY(21); + AGG_WR(ess, PORT_RINGBUS_CTRL, RINGBUS_CTRL_ACLINK_ENABLED, 4); + DELAY(50); } } @@ -457,47 +643,93 @@ agg_init(struct agg_info* ess) * Prefer PCI timing rather than that of ISA. * Don't swap L/R. */ data = pci_read_config(ess->dev, CONF_MAESTRO, 4); + data |= MAESTRO_PMC; 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); + /* Turn off unused parts if necessary. */ + /* consult CONF_MAESTRO. */ + if (data & MAESTRO_SPDIF) + agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D2); + else + agg_stopclock(ess, ACPI_PART_SPDIF, PCI_POWERSTATE_D1); + if (data & MAESTRO_HWVOL) + agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D3); + else + agg_stopclock(ess, ACPI_PART_HW_VOL, PCI_POWERSTATE_D1); + + /* parts that never be used */ + agg_stopclock(ess, ACPI_PART_978, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_DAA, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_GPIO, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_SB, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_FM, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_MIDI, PCI_POWERSTATE_D1); + agg_stopclock(ess, ACPI_PART_GAME_PORT, PCI_POWERSTATE_D1); + + /* parts that will be used only when play/recording */ + agg_stopclock(ess, ACPI_PART_WP, PCI_POWERSTATE_D2); + + /* parts that should always be turned on */ + agg_stopclock(ess, ACPI_PART_CODEC_CLOCK, PCI_POWERSTATE_D3); + agg_stopclock(ess, ACPI_PART_GLUE, PCI_POWERSTATE_D3); + agg_stopclock(ess, ACPI_PART_PCI_IF, PCI_POWERSTATE_D3); + agg_stopclock(ess, ACPI_PART_RINGBUS, PCI_POWERSTATE_D3); - /* 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_HWVOL_ENABLED); + /* Reset direct sound. */ + AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_SOFT_RESET, 2); + DELAY(100); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + DELAY(100); + AGG_WR(ess, PORT_HOSTINT_CTRL, HOSTINT_CTRL_DSOUND_RESET, 2); + DELAY(100); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + DELAY(100); + + /* Enable hardware volume control interruption. */ + if (data & MAESTRO_HWVOL) /* XXX - why not use device flags? */ + AGG_WR(ess, PORT_HOSTINT_CTRL,HOSTINT_CTRL_HWVOL_ENABLED, 2); /* 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); + wp_wrreg(ess, WPREG_CRAM_DATA, 0); + + AGG_WR(ess, PORT_WAVCACHE_CTRL, + WAVCACHE_ENABLED | WAVCACHE_WTSIZE_2MB | WAVCACHE_SGC_32_47, 2); for (data = WAVCACHE_PCMBAR; data < WAVCACHE_PCMBAR + 4; data++) - wc_wrreg(ess, data, ess->baseaddr >> WAVCACHE_BASEADDR_SHIFT); + wc_wrreg(ess, data, ess->phys >> 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 */ + AGG_WR(ess, PORT_RINGBUS_CTRL, + RINGBUS_CTRL_RINGBUS_ENABLED | RINGBUS_CTRL_ACLINK_ENABLED, 4); + + wp_wrreg(ess, 0x08, 0xB004); + wp_wrreg(ess, 0x09, 0x001B); + wp_wrreg(ess, 0x0A, 0x8000); + wp_wrreg(ess, 0x0B, 0x3F37); + wp_wrreg(ess, WPREG_BASE, 0x8598); /* Parallel I/O */ + wp_wrreg(ess, WPREG_BASE + 1, 0x7632); 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); + /* Enable S/PDIF if necessary. */ + if (pci_read_config(ess->dev, CONF_MAESTRO, 4) & MAESTRO_SPDIF) + /* XXX - why not use device flags? */ + AGG_WR(ess, PORT_RINGBUS_CTRL_B, RINGBUS_CTRL_SPDIF | + AGG_RD(ess, PORT_RINGBUS_CTRL_B, 1), 1); + /* 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); + AGG_WR(ess, PORT_ASSP_CTRL_B, 0x00, 1); + AGG_WR(ess, PORT_ASSP_CTRL_A, 0x03, 1); + AGG_WR(ess, PORT_ASSP_CTRL_C, 0x00, 1); /* * Setup GPIO. @@ -509,82 +741,398 @@ agg_init(struct agg_info* ess) 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); + AGG_WR(ess, PORT_GPIO_MASK, 0x9ff, 2); + AGG_WR(ess, PORT_GPIO_DIR, + AGG_RD(ess, PORT_GPIO_DIR, 2) | 0x600, 2); + AGG_WR(ess, PORT_GPIO_DATA, 0x200, 2); + break; + } +} + +/* Deals power state transition. Must be called with softc->lock held. */ +static void +agg_power(struct agg_info *ess, int status) +{ + u_int8_t lastpwr; + + lastpwr = ess->curpwr; + if (lastpwr == status) + return; + + switch (status) { + case PCI_POWERSTATE_D0: + case PCI_POWERSTATE_D1: + switch (lastpwr) { + case PCI_POWERSTATE_D2: + pci_set_powerstate(ess->dev, status); + /* Turn on PCM-related parts. */ + agg_wrcodec(ess, AC97_REG_POWER, 0); + DELAY(100); +#if 0 + if ((agg_rdcodec(ess, AC97_REG_POWER) & 3) != 3) + device_printf(ess->dev, "warning: codec not ready.\n"); +#endif + AGG_WR(ess, PORT_RINGBUS_CTRL, + (AGG_RD(ess, PORT_RINGBUS_CTRL, 4) + & ~RINGBUS_CTRL_ACLINK_ENABLED) + | RINGBUS_CTRL_RINGBUS_ENABLED, 4); + DELAY(50); + AGG_WR(ess, PORT_RINGBUS_CTRL, + AGG_RD(ess, PORT_RINGBUS_CTRL, 4) + | RINGBUS_CTRL_ACLINK_ENABLED, 4); + break; + case PCI_POWERSTATE_D3: + /* Initialize. */ + pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); + DELAY(100); + agg_init(ess); + /* FALLTHROUGH */ + case PCI_POWERSTATE_D0: + case PCI_POWERSTATE_D1: + pci_set_powerstate(ess->dev, status); + break; + } + break; + case PCI_POWERSTATE_D2: + switch (lastpwr) { + case PCI_POWERSTATE_D3: + /* Initialize. */ + pci_set_powerstate(ess->dev, PCI_POWERSTATE_D0); + DELAY(100); + agg_init(ess); + /* FALLTHROUGH */ + case PCI_POWERSTATE_D0: + case PCI_POWERSTATE_D1: + /* Turn off PCM-related parts. */ + AGG_WR(ess, PORT_RINGBUS_CTRL, + AGG_RD(ess, PORT_RINGBUS_CTRL, 4) + & ~RINGBUS_CTRL_RINGBUS_ENABLED, 4); + DELAY(100); + agg_wrcodec(ess, AC97_REG_POWER, 0x300); + DELAY(100); + break; + } + pci_set_powerstate(ess->dev, status); + break; + case PCI_POWERSTATE_D3: + /* Entirely power down. */ + agg_wrcodec(ess, AC97_REG_POWER, 0xdf00); + DELAY(100); + AGG_WR(ess, PORT_RINGBUS_CTRL, 0, 4); + /*DELAY(1);*/ + if (lastpwr != PCI_POWERSTATE_D2) + wp_stoptimer(ess); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + AGG_WR(ess, PORT_HOSTINT_STAT, 0xff, 1); + pci_set_powerstate(ess->dev, status); + break; + default: + /* Invalid power state; let it ignored. */ + status = lastpwr; break; } + + ess->curpwr = status; } +/* -------------------------------------------------------------------- */ + /* Channel controller. */ static void aggch_start_dac(struct agg_chinfo *ch) { - bus_addr_t wpwa = APU_USE_SYSMEM | (ch->offset >> 9); - u_int size = ch->parent->bufsz >> 1; - u_int speed = ch->speed; - bus_addr_t offset = ch->offset >> 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; + bus_addr_t wpwa; + u_int32_t speed; + u_int16_t size, apuch, wtbar, wcreg, aputype; + u_int dv; + int pan; + + speed = ch->speed; + wpwa = (ch->phys - ch->base) >> 1; + wtbar = 0xc & (wpwa >> WPWA_WTBAR_SHIFT(2)); + wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + size = ch->buflen; + apuch = (ch->num << 1) | 32; + pan = PAN_RIGHT - PAN_FRONT; + + if (ch->stereo) { + wcreg |= WAVCACHE_CHCTL_STEREO; + if (ch->qs16) { + aputype = APUTYPE_16BITSTEREO; + wpwa >>= 1; + size >>= 1; + pan = -pan; + } else + aputype = APUTYPE_8BITSTEREO; + } else { + pan = 0; + if (ch->qs16) + aputype = APUTYPE_16BITLINEAR; + else { + aputype = APUTYPE_8BITLINEAR; + speed >>= 1; + } } + if (ch->us) + wcreg |= WAVCACHE_CHCTL_U8; + + if (wtbar > 8) + wtbar = (wtbar >> 1) + 4; 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) + agg_lock(ch->parent); + agg_power(ch->parent, powerstate_active); + + wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar, + ch->base >> WAVCACHE_BASEADDR_SHIFT); + wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 1, + ch->base >> WAVCACHE_BASEADDR_SHIFT); + if (wtbar < 8) { + wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 2, + ch->base >> WAVCACHE_BASEADDR_SHIFT); + wc_wrreg(ch->parent, WAVCACHE_WTBAR + wtbar + 3, + ch->base >> WAVCACHE_BASEADDR_SHIFT); + } + wc_wrchctl(ch->parent, apuch, wcreg); + wc_wrchctl(ch->parent, apuch + 1, wcreg); + + apu_setparam(ch->parent, apuch, wpwa, size, pan, dv); + if (ch->stereo) { + if (ch->qs16) + wpwa |= (WPWA_STEREO >> 1); + apu_setparam(ch->parent, apuch + 1, wpwa, size, -pan, dv); + + critical_enter(); + wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, + (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); wp_wrapu(ch->parent, apuch + 1, APUREG_APUTYPE, - (ch->aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); + (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); + critical_exit(); + } else { + wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, + (aputype << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); + } + + /* to mark that this channel is ready for intr. */ + ch->parent->active |= (1 << ch->num); + + set_timer(ch->parent); + wp_starttimer(ch->parent); + agg_unlock(ch->parent); } static void aggch_stop_dac(struct agg_chinfo *ch) { - wp_wrapu(ch->parent, (ch->num << 1), APUREG_APUTYPE, + agg_lock(ch->parent); + + /* to mark that this channel no longer needs further intrs. */ + ch->parent->active &= ~(1 << ch->num); + + wp_wrapu(ch->parent, (ch->num << 1) | 32, APUREG_APUTYPE, APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); - wp_wrapu(ch->parent, (ch->num << 1) + 1, APUREG_APUTYPE, + wp_wrapu(ch->parent, (ch->num << 1) | 33, APUREG_APUTYPE, APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); + + if (ch->parent->active) { + set_timer(ch->parent); + wp_starttimer(ch->parent); + } else { + wp_stoptimer(ch->parent); + agg_power(ch->parent, powerstate_idle); + } + agg_unlock(ch->parent); +} + +static void +aggch_start_adc(struct agg_rchinfo *ch) +{ + bus_addr_t wpwa, wpwa2; + u_int16_t wcreg, wcreg2; + u_int dv; + int pan; + + /* speed > 48000 not cared */ + dv = ((ch->speed << 16) + 24000) / 48000; + + /* RATECONV doesn't seem to like dv == 0x10000. */ + if (dv == 0x10000) + dv--; + + if (ch->stereo) { + wpwa = (ch->srcphys - ch->base) >> 1; + wpwa2 = (ch->srcphys + ch->parent->bufsz/2 - ch->base) >> 1; + wcreg = (ch->srcphys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + pan = PAN_LEFT - PAN_FRONT; + } else { + wpwa = (ch->phys - ch->base) >> 1; + wpwa2 = (ch->srcphys - ch->base) >> 1; + wcreg = (ch->phys - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + wcreg2 = (ch->base - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; + pan = 0; + } + + agg_lock(ch->parent); + + ch->hwptr = 0; + agg_power(ch->parent, powerstate_active); + + /* Invalidate WaveCache. */ + wc_wrchctl(ch->parent, 0, wcreg | WAVCACHE_CHCTL_STEREO); + wc_wrchctl(ch->parent, 1, wcreg | WAVCACHE_CHCTL_STEREO); + wc_wrchctl(ch->parent, 2, wcreg2 | WAVCACHE_CHCTL_STEREO); + wc_wrchctl(ch->parent, 3, wcreg2 | WAVCACHE_CHCTL_STEREO); + + /* Load APU registers. */ + /* APU #0 : Sample rate converter for left/center. */ + apu_setparam(ch->parent, 0, WPWA_USE_SYSMEM | wpwa, + ch->buflen >> ch->stereo, 0, dv); + wp_wrapu(ch->parent, 0, APUREG_AMPLITUDE, 0); + wp_wrapu(ch->parent, 0, APUREG_ROUTING, 2 << APU_DATASRC_A_SHIFT); + + /* APU #1 : Sample rate converter for right. */ + apu_setparam(ch->parent, 1, WPWA_USE_SYSMEM | wpwa2, + ch->buflen >> ch->stereo, 0, dv); + wp_wrapu(ch->parent, 1, APUREG_AMPLITUDE, 0); + wp_wrapu(ch->parent, 1, APUREG_ROUTING, 3 << APU_DATASRC_A_SHIFT); + + /* APU #2 : Input mixer for left. */ + apu_setparam(ch->parent, 2, WPWA_USE_SYSMEM | 0, + ch->parent->bufsz >> 2, pan, 0x10000); + wp_wrapu(ch->parent, 2, APUREG_AMPLITUDE, 0); + wp_wrapu(ch->parent, 2, APUREG_EFFECT_GAIN, 0xf0); + wp_wrapu(ch->parent, 2, APUREG_ROUTING, 0x15 << APU_DATASRC_A_SHIFT); + + /* APU #3 : Input mixer for right. */ + apu_setparam(ch->parent, 3, WPWA_USE_SYSMEM | (ch->parent->bufsz >> 2), + ch->parent->bufsz >> 2, -pan, 0x10000); + wp_wrapu(ch->parent, 3, APUREG_AMPLITUDE, 0); + wp_wrapu(ch->parent, 3, APUREG_EFFECT_GAIN, 0xf0); + wp_wrapu(ch->parent, 3, APUREG_ROUTING, 0x14 << APU_DATASRC_A_SHIFT); + + /* to mark this channel ready for intr. */ + ch->parent->active |= (1 << ch->parent->playchns); + + /* start adc */ + critical_enter(); + wp_wrapu(ch->parent, 0, APUREG_APUTYPE, + (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); + wp_wrapu(ch->parent, 1, APUREG_APUTYPE, + (APUTYPE_RATECONV << APU_APUTYPE_SHIFT) | APU_DMA_ENABLED | 0xf); + wp_wrapu(ch->parent, 2, APUREG_APUTYPE, + (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); + wp_wrapu(ch->parent, 3, APUREG_APUTYPE, + (APUTYPE_INPUTMIXER << APU_APUTYPE_SHIFT) | 0xf); + critical_exit(); + + set_timer(ch->parent); + wp_starttimer(ch->parent); + agg_unlock(ch->parent); +} + +static void +aggch_stop_adc(struct agg_rchinfo *ch) +{ + int apuch; + + agg_lock(ch->parent); + + /* to mark that this channel no longer needs further intrs. */ + ch->parent->active &= ~(1 << ch->parent->playchns); + + for (apuch = 0; apuch < 4; apuch++) + wp_wrapu(ch->parent, apuch, APUREG_APUTYPE, + APUTYPE_INACTIVE << APU_APUTYPE_SHIFT); + + if (ch->parent->active) { + set_timer(ch->parent); + wp_starttimer(ch->parent); + } else { + wp_stoptimer(ch->parent); + agg_power(ch->parent, powerstate_idle); + } + agg_unlock(ch->parent); +} + +/* + * Feed from L/R channel of ADC to destination with stereo interleaving. + * This function expects n not overwrapping the buffer boundary. + * Note that n is measured in sample unit. + * + * XXX - this function works in 16bit stereo format only. + */ +static inline void +interleave(int16_t *l, int16_t *r, int16_t *p, unsigned n) +{ + int16_t *end; + + for (end = l + n; l < end; ) { + *p++ = *l++; + *p++ = *r++; + } +} + +static void +aggch_feed_adc_stereo(struct agg_rchinfo *ch) +{ + unsigned cur, last; + int16_t *src2; + + agg_lock(ch->parent); + cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); + agg_unlock(ch->parent); + cur -= 0xffff & ((ch->srcphys - ch->base) >> 1); + last = ch->hwptr; + src2 = ch->src + ch->parent->bufsz/4; + + if (cur < last) { + interleave(ch->src + last, src2 + last, + ch->sink + 2*last, ch->buflen/2 - last); + interleave(ch->src, src2, + ch->sink, cur); + } else if (cur > last) + interleave(ch->src + last, src2 + last, + ch->sink + 2*last, cur - last); + ch->hwptr = cur; +} + +/* + * Feed from R channel of ADC and mixdown to destination L/center. + * This function expects n not overwrapping the buffer boundary. + * Note that n is measured in sample unit. + * + * XXX - this function works in 16bit monoral format only. + */ +static inline void +mixdown(int16_t *src, int16_t *dest, unsigned n) +{ + int16_t *end; + + for (end = dest + n; dest < end; dest++) + *dest = (int16_t)(((int)*dest - (int)*src++) / 2); +} + +static void +aggch_feed_adc_mono(struct agg_rchinfo *ch) +{ + unsigned cur, last; + + agg_lock(ch->parent); + cur = wp_rdapu(ch->parent, 0, APUREG_CURPTR); + agg_unlock(ch->parent); + cur -= 0xffff & ((ch->phys - ch->base) >> 1); + last = ch->hwptr; + + if (cur < last) { + mixdown(ch->src + last, ch->sink + last, ch->buflen - last); + mixdown(ch->src, ch->sink, cur); + } else if (cur > last) + mixdown(ch->src + last, ch->sink + last, cur - last); + ch->hwptr = cur; } /* @@ -595,45 +1143,84 @@ aggch_stop_dac(struct agg_chinfo *ch) static inline void suppress_jitter(struct agg_chinfo *ch) { - if (ch->wcreg_tpl & WAVCACHE_CHCTL_STEREO) { - int cp, diff, halfsize = ch->parent->bufsz >> 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); + if (ch->stereo) { + int cp1, cp2, diff /*, halfsize*/ ; + + /*halfsize = (ch->qs16? ch->buflen >> 2 : ch->buflen >> 1);*/ + cp1 = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); + cp2 = wp_rdapu(ch->parent, (ch->num << 1) | 33, APUREG_CURPTR); + if (cp1 != cp2) { + diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); + if (diff > 1 /* && diff < halfsize*/ ) + AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); + } + } +} + +static inline void +suppress_rec_jitter(struct agg_rchinfo *ch) +{ + int cp1, cp2, diff /*, halfsize*/ ; + + /*halfsize = (ch->stereo? ch->buflen >> 2 : ch->buflen >> 1);*/ + cp1 = (ch->stereo? ch->parent->bufsz >> 2 : ch->parent->bufsz >> 1) + + wp_rdapu(ch->parent, 0, APUREG_CURPTR); + cp2 = wp_rdapu(ch->parent, 1, APUREG_CURPTR); + if (cp1 != cp2) { + diff = (cp1 > cp2 ? cp1 - cp2 : cp2 - cp1); + if (diff > 1 /* && diff < halfsize*/ ) + AGG_WR(ch->parent, PORT_DSP_DATA, cp1, 2); } } static inline u_int -calc_timer_freq(struct agg_chinfo *ch) +calc_timer_div(struct agg_chinfo *ch) { - u_int ss = 2; + u_int speed; + + speed = ch->speed; +#ifdef INVARIANTS + if (speed == 0) { + kprintf("snd_maestro: pch[%d].speed == 0, which shouldn't\n", + ch->num); + speed = 1; + } +#endif + return (48000 * (ch->blklen << (!ch->qs16 + !ch->stereo)) + + speed - 1) / speed; +} - if (ch->aputype == APUTYPE_16BITSTEREO) - ss <<= 1; - if (ch->aputype == APUTYPE_8BITLINEAR) - ss >>= 1; +static inline u_int +calc_timer_div_rch(struct agg_rchinfo *ch) +{ + u_int speed; - return (ch->speed * ss) / ch->blocksize; + speed = ch->speed; +#ifdef INVARIANTS + if (speed == 0) { + kprintf("snd_maestro: rch.speed == 0, which shouldn't\n"); + speed = 1; + } +#endif + return (48000 * (ch->blklen << (!ch->stereo)) + + speed - 1) / speed; } static void set_timer(struct agg_info *ess) { int i; - u_int freq = 0; + u_int dv = 32 << 7, newdv; 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); + (dv > (newdv = calc_timer_div(ess->pch + i)))) + dv = newdv; + if ((ess->active & (1 << i)) && + (dv > (newdv = calc_timer_div_rch(&ess->rch)))) + dv = newdv; - wp_settimer(ess, freq); + wp_settimer(ess, dv); } @@ -641,157 +1228,225 @@ set_timer(struct agg_info *ess) * Newpcm glue. */ +/* AC97 mixer interface. */ + +static u_int32_t +agg_ac97_init(kobj_t obj, void *sc) +{ + struct agg_info *ess = sc; + + return (AGG_RD(ess, PORT_CODEC_STAT, 1) & CODEC_STAT_MASK)? 0 : 1; +} + +static int +agg_ac97_read(kobj_t obj, void *sc, int regno) +{ + struct agg_info *ess = sc; + int ret; + + /* XXX sound locking violation: agg_lock(ess); */ + ret = agg_rdcodec(ess, regno); + /* agg_unlock(ess); */ + return ret; +} + +static int +agg_ac97_write(kobj_t obj, void *sc, int regno, u_int32_t data) +{ + struct agg_info *ess = sc; + int ret; + + /* XXX sound locking violation: agg_lock(ess); */ + ret = agg_wrcodec(ess, regno, data); + /* agg_unlock(ess); */ + return ret; +} + + +static kobj_method_t agg_ac97_methods[] = { + KOBJMETHOD(ac97_init, agg_ac97_init), + KOBJMETHOD(ac97_read, agg_ac97_read), + KOBJMETHOD(ac97_write, agg_ac97_write), + { 0, 0 } +}; +AC97_DECLARE(agg_ac97); + + +/* -------------------------------------------------------------------- */ + +/* Playback channel. */ + static void * -aggch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +aggpch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct 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; + KASSERT((dir == PCMDIR_PLAY), + ("aggpch_init() called for RECORDING channel!")); + ch = ess->pch + ess->playchns; ch->parent = ess; ch->channel = c; ch->buffer = b; ch->num = ess->playchns; - ch->dir = dir; - p = dma_malloc(ess, ess->bufsz, &physaddr); + p = dma_malloc(ess->buf_dmat, ess->bufsz, &physaddr); if (p == NULL) return NULL; + ch->phys = physaddr; + ch->base = physaddr & ((~(bus_addr_t)0) << WAVCACHE_BASEADDR_SHIFT); + sndbuf_setup(b, p, ess->bufsz); + ch->blklen = sndbuf_getblksz(b) / 2; + ch->buflen = sndbuf_getsize(b) / 2; + ess->playchns++; - ch->offset = physaddr - ess->baseaddr; - if (physaddr < ess->baseaddr || ch->offset > WPWA_MAXADDR) { - device_printf(ess->dev, - "offset %#llx exceeds limit. ", (long long)ch->offset); - dma_free(ess, sndbuf_getbuf(b)); - return NULL; - } + return ch; +} - ch->wcreg_tpl = (physaddr - 16) & WAVCACHE_CHCTL_ADDRTAG_MASK; +static void +adjust_pchbase(struct agg_chinfo *chans, u_int n, u_int size) +{ + struct agg_chinfo *pchs[AGG_MAXPLAYCH]; + u_int i, j, k; + bus_addr_t base; + + /* sort pchs by phys address */ + for (i = 0; i < n; i++) { + for (j = 0; j < i; j++) + if (chans[i].phys < pchs[j]->phys) { + for (k = i; k > j; k--) + pchs[k] = pchs[k - 1]; + break; + } + pchs[j] = chans + i; + } - if (dir == PCMDIR_PLAY) { - ess->playchns++; - if (bootverbose) - device_printf(ess->dev, "pch[%d].offset = %#llx\n", ch->num, (long long)ch->offset); - } else if (bootverbose) - device_printf(ess->dev, "rch.offset = %#llx\n", (long long)ch->offset); + /* use new base register if next buffer can not be addressed + via current base. */ +#define BASE_SHIFT (WPWA_WTBAR_SHIFT(2) + 2 + 1) + base = pchs[0]->base; + for (k = 1, i = 1; i < n; i++) { + if (pchs[i]->phys + size - base >= 1 << BASE_SHIFT) + /* not addressable: assign new base */ + base = (pchs[i]->base -= k++ << BASE_SHIFT); + else + pchs[i]->base = base; + } +#undef BASE_SHIFT - return ch; + if (bootverbose) { + kprintf("Total of %d bases are assigned.\n", k); + for (i = 0; i < n; i++) { + kprintf("ch.%d: phys 0x%llx, wpwa 0x%llx\n", + i, (long long)chans[i].phys, + (long long)(chans[i].phys - + chans[i].base) >> 1); + } + } } static int -aggch_free(kobj_t obj, void *data) +aggpch_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, sndbuf_getbuf(ch->buffer)); + /* kfree up buffer - called after channel stopped */ + dma_free(ess->buf_dmat, sndbuf_getbuf(ch->buffer)); /* return 0 if ok */ return 0; } static int -aggch_setplayformat(kobj_t obj, void *data, u_int32_t format) +aggpch_setformat(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_BIGENDIAN || format & AFMT_U16_LE) + return EINVAL; + ch->stereo = ch->qs16 = ch->us = 0; + if (format & AFMT_STEREO) + ch->stereo = 1; - 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; + ch->us = 1; + } else + ch->qs16 = 1; return 0; } static int -aggch_setspeed(kobj_t obj, void *data, u_int32_t speed) +aggpch_setspeed(kobj_t obj, void *data, u_int32_t speed) { - struct agg_chinfo *ch = data; - - ch->speed = speed; - return ch->speed; + return ((struct agg_chinfo*)data)->speed = speed; } static int -aggch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +aggpch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { - return ((struct agg_chinfo*)data)->blocksize = blocksize; + struct agg_chinfo *ch = data; + int blkcnt; + + /* try to keep at least 20msec DMA space */ + blkcnt = (ch->speed << (ch->stereo + ch->qs16)) / (50 * blocksize); + RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); + + if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { + sndbuf_resize(ch->buffer, blkcnt, blocksize); + blkcnt = sndbuf_getblkcnt(ch->buffer); + blocksize = sndbuf_getblksz(ch->buffer); + } else { + sndbuf_setblkcnt(ch->buffer, blkcnt); + sndbuf_setblksz(ch->buffer, blocksize); + } + + ch->blklen = blocksize / 2; + ch->buflen = blkcnt * blocksize / 2; + return blocksize; } static int -aggch_trigger(kobj_t obj, void *data, int go) +aggpch_trigger(kobj_t obj, void *data, int go) { struct agg_chinfo *ch = data; switch (go) { case PCMTRIG_EMLDMAWR: - return 0; + break; 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 + aggch_start_dac(ch); 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 + aggch_stop_dac(ch); break; } - - if (ch->parent->active) { - set_timer(ch->parent); - wp_starttimer(ch->parent); - } else - wp_stoptimer(ch->parent); - return 0; } static int -aggch_getplayptr(kobj_t obj, void *data) +aggpch_getptr(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); + agg_lock(ch->parent); + cp = wp_rdapu(ch->parent, (ch->num << 1) | 32, APUREG_CURPTR); + agg_unlock(ch->parent); - return cp; + return ch->qs16 && ch->stereo + ? (cp << 2) - ((0xffff << 2) & (ch->phys - ch->base)) + : (cp << 1) - ((0xffff << 1) & (ch->phys - ch->base)); } static struct pcmchan_caps * -aggch_getcaps(kobj_t obj, void *data) +aggpch_getcaps(kobj_t obj, void *data) { static u_int32_t playfmt[] = { AFMT_U8, @@ -802,33 +1457,161 @@ aggch_getcaps(kobj_t obj, void *data) AFMT_STEREO | AFMT_S16_LE, 0 }; - static struct pcmchan_caps playcaps = {2000, 96000, playfmt, 0}; + static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0}; + return &playcaps; +} + + +static kobj_method_t aggpch_methods[] = { + KOBJMETHOD(channel_init, aggpch_init), + KOBJMETHOD(channel_free, aggpch_free), + KOBJMETHOD(channel_setformat, aggpch_setformat), + KOBJMETHOD(channel_setspeed, aggpch_setspeed), + KOBJMETHOD(channel_setblocksize, aggpch_setblocksize), + KOBJMETHOD(channel_trigger, aggpch_trigger), + KOBJMETHOD(channel_getptr, aggpch_getptr), + KOBJMETHOD(channel_getcaps, aggpch_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(aggpch); + + +/* -------------------------------------------------------------------- */ + +/* Recording channel. */ + +static void * +aggrch_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) +{ + struct agg_info *ess = devinfo; + struct agg_rchinfo *ch; + u_int8_t *p; + + KASSERT((dir == PCMDIR_REC), + ("aggrch_init() called for PLAYBACK channel!")); + ch = &ess->rch; + + ch->parent = ess; + ch->channel = c; + ch->buffer = b; + + /* Uses the bottom-half of the status buffer. */ + p = ess->stat + ess->bufsz; + ch->phys = ess->phys + ess->bufsz; + ch->base = ess->phys; + ch->src = (int16_t *)(p + ess->bufsz); + ch->srcphys = ch->phys + ess->bufsz; + ch->sink = (int16_t *)p; + + sndbuf_setup(b, p, ess->bufsz); + ch->blklen = sndbuf_getblksz(b) / 2; + ch->buflen = sndbuf_getsize(b) / 2; + + return ch; +} + +static int +aggrch_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct agg_rchinfo *ch = data; + + if (!(format & AFMT_S16_LE)) + return EINVAL; + if (format & AFMT_STEREO) + ch->stereo = 1; + else + ch->stereo = 0; + return 0; +} + +static int +aggrch_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + return ((struct agg_rchinfo*)data)->speed = speed; +} + +static int +aggrch_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) +{ + struct agg_rchinfo *ch = data; + int blkcnt; + + /* try to keep at least 20msec DMA space */ + blkcnt = (ch->speed << ch->stereo) / (25 * blocksize); + RANGE(blkcnt, 2, ch->parent->bufsz / blocksize); + + if (sndbuf_getsize(ch->buffer) != blkcnt * blocksize) { + sndbuf_resize(ch->buffer, blkcnt, blocksize); + blkcnt = sndbuf_getblkcnt(ch->buffer); + blocksize = sndbuf_getblksz(ch->buffer); + } else { + sndbuf_setblkcnt(ch->buffer, blkcnt); + sndbuf_setblksz(ch->buffer, blocksize); + } + + ch->blklen = blocksize / 2; + ch->buflen = blkcnt * blocksize / 2; + return blocksize; +} + +static int +aggrch_trigger(kobj_t obj, void *sc, int go) +{ + struct agg_rchinfo *ch = sc; + + switch (go) { + case PCMTRIG_EMLDMARD: + if (ch->stereo) + aggch_feed_adc_stereo(ch); + else + aggch_feed_adc_mono(ch); + break; + case PCMTRIG_START: + aggch_start_adc(ch); + break; + case PCMTRIG_ABORT: + case PCMTRIG_STOP: + aggch_stop_adc(ch); + break; + } + return 0; +} + +static int +aggrch_getptr(kobj_t obj, void *sc) +{ + struct agg_rchinfo *ch = sc; + + return ch->stereo? ch->hwptr << 2 : ch->hwptr << 1; +} + +static struct pcmchan_caps * +aggrch_getcaps(kobj_t obj, void *sc) +{ static u_int32_t recfmt[] = { - AFMT_S8, - AFMT_STEREO | AFMT_S8, AFMT_S16_LE, AFMT_STEREO | AFMT_S16_LE, 0 }; - static struct pcmchan_caps reccaps = {4000, 48000, recfmt, 0}; + static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; - return (((struct agg_chinfo*)data)->dir == PCMDIR_PLAY)? - &playcaps : &reccaps; + return &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), +static kobj_method_t aggrch_methods[] = { + KOBJMETHOD(channel_init, aggrch_init), + /* channel_free: no-op */ + KOBJMETHOD(channel_setformat, aggrch_setformat), + KOBJMETHOD(channel_setspeed, aggrch_setspeed), + KOBJMETHOD(channel_setblocksize, aggrch_setblocksize), + KOBJMETHOD(channel_trigger, aggrch_trigger), + KOBJMETHOD(channel_getptr, aggrch_getptr), + KOBJMETHOD(channel_getcaps, aggrch_getcaps), { 0, 0 } }; -CHANNEL_DECLARE(aggch); +CHANNEL_DECLARE(aggrch); + /* ----------------------------- * Bus space. @@ -838,25 +1621,61 @@ static void agg_intr(void *sc) { struct agg_info* ess = sc; - u_int16_t status; + register u_int8_t status; int i; + u_int m; - status = bus_space_read_1(ess->st, ess->sh, PORT_HOSTINT_STAT); + status = AGG_RD(ess, PORT_HOSTINT_STAT, 1); 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, 0xff); + /* Acknowledge intr. */ + AGG_WR(ess, PORT_HOSTINT_STAT, status, 1); + + if (status & HOSTINT_STAT_DSOUND) { +#ifdef AGG_JITTER_CORRECTION + agg_lock(ess); +#endif + if (ess->curpwr <= PCI_POWERSTATE_D1) { + AGG_WR(ess, PORT_INT_STAT, 1, 2); +#ifdef AGG_JITTER_CORRECTION + for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { + if (ess->active & m) + suppress_jitter(ess->pch + i); + } + if (ess->active & m) + suppress_rec_jitter(&ess->rch); + agg_unlock(ess); +#endif + for (i = 0, m = 1; i < ess->playchns; i++, m <<= 1) { + if (ess->active & m) { + if (ess->curpwr <= PCI_POWERSTATE_D1) + chn_intr(ess->pch[i].channel); + else { + m = 0; + break; + } + } + } + if ((ess->active & m) + && ess->curpwr <= PCI_POWERSTATE_D1) + chn_intr(ess->rch.channel); + } +#ifdef AGG_JITTER_CORRECTION + else + agg_unlock(ess); +#endif + } if (status & HOSTINT_STAT_HWVOL) { - u_int event; + register u_int8_t event; + + agg_lock(ess); + event = AGG_RD(ess, PORT_HWVOL_MASTER, 1); + AGG_WR(ess, PORT_HWVOL_MASTER, HWVOL_NOP, 1); + agg_unlock(ess); - 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; @@ -866,22 +1685,15 @@ agg_intr(void *sc) case HWVOL_NOP: break; default: - device_printf(ess->dev, "%s: unknown HWVOL event 0x%x\n", - device_get_nameunit(ess->dev), event); + if (event & HWVOL_MUTE) { + mixer_hwvol_mute(ess->dev); + break; + } + 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, - HWVOL_NOP); } - - 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 @@ -899,25 +1711,25 @@ setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) } static void * -dma_malloc(struct agg_info *sc, u_int32_t sz, bus_addr_t *phys) +dma_malloc(bus_dma_tag_t dmat, 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)) + if (bus_dmamem_alloc(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); + if (bus_dmamap_load(dmat, map, buf, sz, setmap, phys, 0) + || !*phys || map) { + bus_dmamem_free(dmat, buf, map); return NULL; } return buf; } static void -dma_free(struct agg_info *sc, void *buf) +dma_free(bus_dma_tag_t dmat, void *buf) { - bus_dmamem_free(sc->parent_dmat, buf, NULL); + bus_dmamem_free(dmat, buf, NULL); } static int @@ -941,7 +1753,7 @@ agg_probe(device_t dev) if (s != NULL && pci_get_class(dev) == PCIC_MULTIMEDIA) { device_set_desc(dev, s); - return 0; + return BUS_PROBE_DEFAULT; } return ENXIO; } @@ -951,101 +1763,157 @@ agg_attach(device_t dev) { struct agg_info *ess = NULL; u_int32_t data; - int mapped = 0; - int regid = PCIR_MAPS; + int regid = PCIR_BAR(0); struct resource *reg = NULL; struct ac97_info *codec = NULL; int irqid = 0; struct resource *irq = NULL; void *ih = NULL; char status[SND_STATUSLEN]; + int ret = 0; if ((ess = kmalloc(sizeof *ess, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); - return ENXIO; + ret = ENOMEM; + goto bad; } ess->dev = dev; +#ifdef USING_MUTEX + ess->lock = snd_mtxcreate(device_get_desc(dev), "hardware status lock"); + if (ess->lock == NULL) { + device_printf(dev, "failed to create a mutex.\n"); + ret = ENOMEM; + goto bad; + } +#endif + ess->bufsz = pcm_getbuffersize(dev, 4096, AGG_DEFAULT_BUFSZ, 65536); + if (bus_dma_tag_create(/*parent*/ NULL, + /*align */ 4, 1 << (16+1), + /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, + /*filter*/ NULL, NULL, + /*size */ ess->bufsz, 1, 0x3ffff, + /*flags */ 0, +#if __FreeBSD_version >= 501102 + /*lock */ busdma_lock_mutex, &Giant, +#endif + &ess->buf_dmat) != 0) { + device_printf(dev, "unable to create dma tag\n"); + ret = ENOMEM; + goto bad; + } 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*/ess->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &ess->parent_dmat) != 0) { + /*align */ 1 << WAVCACHE_BASEADDR_SHIFT, + 1 << (16+1), + /*limit */ MAESTRO_MAXADDR, BUS_SPACE_MAXADDR, + /*filter*/ NULL, NULL, + /*size */ 3*ess->bufsz, 1, 0x3ffff, + /*flags */ 0, +#if __FreeBSD_version >= 501102 + /*lock */ busdma_lock_mutex, &Giant, +#endif + &ess->stat_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); + ret = ENOMEM; goto bad; } - ess->stat = dma_malloc(ess, ess->bufsz, &ess->baseaddr); + /* Allocate the room for brain-damaging status buffer. */ + ess->stat = dma_malloc(ess->stat_dmat, 3*ess->bufsz, &ess->phys); if (ess->stat == NULL) { device_printf(dev, "cannot allocate status buffer\n"); + ret = ENOMEM; goto bad; } if (bootverbose) - device_printf(dev, "Maestro DMA base: %#llx\n", - (long long)ess->baseaddr); + device_printf(dev, "Maestro status/record buffer: %#llx\n", + (long long)ess->phys); - agg_power(ess, PPMI_D0); - DELAY(100000); + /* State D0-uninitialized. */ + ess->curpwr = PCI_POWERSTATE_D3; + pci_set_powerstate(dev, PCI_POWERSTATE_D0); 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) { + /* Allocate resources. */ + if (data & PCIM_CMD_PORTEN) + reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, ®id, + RF_ACTIVE); + if (reg != NULL) { + ess->reg = reg; + ess->regid = regid; + ess->st = rman_get_bustag(reg); + ess->sh = rman_get_bushandle(reg); + } else { device_printf(dev, "unable to map register space\n"); + ret = ENXIO; + goto bad; + } + irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &irqid, + RF_ACTIVE | RF_SHAREABLE); + if (irq != NULL) { + ess->irq = irq; + ess->irqid = irqid; + } else { + device_printf(dev, "unable to map interrupt\n"); + ret = ENXIO; goto bad; } - agg_init(ess); - if (agg_rdcodec(NULL, ess, 0) == 0x80) { + /* Setup resources. */ + if (snd_setup_intr(dev, irq, INTR_MPSAFE, agg_intr, ess, &ih)) { + device_printf(dev, "unable to setup interrupt\n"); + ret = ENXIO; + goto bad; + } else + ess->ih = ih; + + /* Transition from D0-uninitialized to D0. */ + agg_lock(ess); + agg_power(ess, PCI_POWERSTATE_D0); + if (agg_rdcodec(ess, 0) == 0x80) { + /* XXX - TODO: PT101 */ + agg_unlock(ess); device_printf(dev, "PT101 codec detected!\n"); + ret = ENXIO; goto bad; } + agg_unlock(ess); codec = AC97_CREATE(dev, ess, agg_ac97); - if (codec == NULL) - goto bad; - if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) + if (codec == NULL) { + device_printf(dev, "failed to create AC97 codec softc!\n"); + ret = ENOMEM; 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 || snd_setup_intr(dev, irq, 0, agg_intr, ess, &ih, NULL)) { - device_printf(dev, "unable to map interrupt\n"); + } + if (mixer_init(dev, ac97_getmixerclass(), codec) == -1) { + device_printf(dev, "mixer initialization failed!\n"); + ret = ENXIO; goto bad; } - ess->irq = irq; - ess->irqid = irqid; - ess->ih = ih; - - ksnprintf(status, SND_STATUSLEN, "at I/O port 0x%lx irq %ld", - rman_get_start(reg), rman_get_start(irq)); + ess->codec = codec; - if (pcm_register(dev, ess, AGG_MAXPLAYCH, 1)) + ret = pcm_register(dev, ess, AGG_MAXPLAYCH, 1); + if (ret) goto bad; mixer_hwvol_init(dev); + agg_lock(ess); + agg_power(ess, powerstate_init); + agg_unlock(ess); for (data = 0; data < AGG_MAXPLAYCH; data++) - pcm_addchan(dev, PCMDIR_PLAY, &aggch_class, ess); -#if 0 /* XXX - RECORDING */ + pcm_addchan(dev, PCMDIR_PLAY, &aggpch_class, ess); pcm_addchan(dev, PCMDIR_REC, &aggrch_class, ess); -#endif + adjust_pchbase(ess->pch, ess->playchns, ess->bufsz); + + ksnprintf(status, SND_STATUSLEN, + "port 0x%lx-0x%lx irq %ld at device %d.%d on pci%d", + rman_get_start(reg), rman_get_end(reg), rman_get_start(irq), + pci_get_slot(dev), pci_get_function(dev), pci_get_bus(dev)); pcm_setstatus(dev, status); return 0; @@ -1060,15 +1928,20 @@ agg_attach(device_t dev) 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); + dma_free(ess->stat_dmat, ess->stat); + if (ess->stat_dmat != NULL) + bus_dma_tag_destroy(ess->stat_dmat); + if (ess->buf_dmat != NULL) + bus_dma_tag_destroy(ess->buf_dmat); +#ifdef USING_MUTEX + if (ess->lock != NULL) + snd_mtxfree(ess->lock); +#endif kfree(ess, M_DEVBUF); } - return ENXIO; + return ret; } static int @@ -1076,24 +1949,38 @@ agg_detach(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); int r; + u_int16_t icr; + + icr = AGG_RD(ess, PORT_HOSTINT_CTRL, 2); + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + + agg_lock(ess); + if (ess->active) { + AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); + agg_unlock(ess); + return EBUSY; + } + agg_unlock(ess); r = pcm_unregister(dev); - if (r) + if (r) { + AGG_WR(ess, PORT_HOSTINT_CTRL, icr, 2); return r; + } - ess = pcm_getdevinfo(dev); - dma_free(ess, ess->stat); - - /* Power down everything except clock and vref. */ - 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); + agg_lock(ess); + agg_power(ess, PCI_POWERSTATE_D3); + agg_unlock(ess); 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); + dma_free(ess->stat_dmat, ess->stat); + bus_dma_tag_destroy(ess->stat_dmat); + bus_dma_tag_destroy(ess->buf_dmat); +#ifdef USING_MUTEX + snd_mtxfree(ess->lock); +#endif kfree(ess, M_DEVBUF); return 0; } @@ -1102,25 +1989,18 @@ static int agg_suspend(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); - int i; - - crit_enter(); - 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); +#ifndef USING_MUTEX + int x; -#if 0 /* XXX - RECORDING */ - aggch_stop_adc(&ess->rch); + x = spltty(); +#endif + AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); + agg_lock(ess); + agg_power(ess, PCI_POWERSTATE_D3); + agg_unlock(ess); +#ifndef USING_MUTEX + splx(x); #endif - crit_exit(); - /* Power down everything except clock. */ - 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; } @@ -1130,28 +2010,30 @@ agg_resume(device_t dev) { int i; struct agg_info *ess = pcm_getdevinfo(dev); +#ifndef USING_MUTEX + int x; - 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; - } - - crit_enter(); + x = spltty(); +#endif 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); + + agg_lock(ess); + if (!ess->active) + agg_power(ess, powerstate_init); + agg_unlock(ess); +#ifndef USING_MUTEX + splx(x); #endif - if (ess->active) { - set_timer(ess); - wp_starttimer(ess); + + if (mixer_reinit(dev)) { + device_printf(dev, "unable to reinitialize the mixer\n"); + return ENXIO; } - crit_exit(); + return 0; } @@ -1159,17 +2041,11 @@ 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); + agg_lock(ess); + agg_power(ess, PCI_POWERSTATE_D3); + agg_unlock(ess); -#if 0 /* XXX - RECORDING */ - aggch_stop_adc(&ess->rch); -#endif return 0; } @@ -1191,6 +2067,8 @@ static driver_t agg_driver = { PCM_SOFTC_SIZE, }; +/*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_DEPEND(snd_maestro, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_maestro, 1); diff --git a/sys/dev/sound/pci/maestro3.c b/sys/dev/sound/pci/maestro3.c index 673ec9f14d..147018254c 100644 --- a/sys/dev/sound/pci/maestro3.c +++ b/sys/dev/sound/pci/maestro3.c @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/maestro3.c,v 1.2.2.11 2002/09/16 19:52:33 scottl Exp $ - * $DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/maestro3.c,v 1.28.2.1 2005/09/21 03:18:30 yongari Exp $ + * $DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ /* @@ -61,10 +61,10 @@ #include #include -#include "gnu/maestro3_reg.h" -#include "gnu/maestro3_dsp.h" +#include +#include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/maestro3.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); /* -------------------------------------------------------------------- */ @@ -89,6 +89,8 @@ static struct m3_card_type { { 0, 0, 0, 0, NULL } }; +#define M3_BUFSIZE_MIN 1024 +#define M3_BUFSIZE_MAX 65536 #define M3_BUFSIZE_DEFAULT 4096 #define M3_PCHANS 4 /* create /dev/dsp0.[0-N] to use more than one */ #define M3_RCHANS 1 @@ -145,8 +147,14 @@ struct sc_info { int pch_active_cnt; unsigned int bufsz; u_int16_t *savemem; + + struct spinlock *sc_lock; }; +#define M3_LOCK(_sc) snd_mtxlock((_sc)->sc_lock) +#define M3_UNLOCK(_sc) snd_mtxunlock((_sc)->sc_lock) +#define M3_LOCK_ASSERT(_sc) snd_mtxassert((_sc)->sc_lock) + /* -------------------------------------------------------------------- */ /* play channel interface */ @@ -156,6 +164,7 @@ static int m3_pchan_setformat(kobj_t, void *, u_int32_t); static int m3_pchan_setspeed(kobj_t, void *, u_int32_t); static int m3_pchan_setblocksize(kobj_t, void *, u_int32_t); static int m3_pchan_trigger(kobj_t, void *, int); +static int m3_pchan_trigger_locked(kobj_t, void *, int); static int m3_pchan_getptr(kobj_t, void *); static struct pcmchan_caps *m3_pchan_getcaps(kobj_t, void *); @@ -166,6 +175,7 @@ static int m3_rchan_setformat(kobj_t, void *, u_int32_t); static int m3_rchan_setspeed(kobj_t, void *, u_int32_t); static int m3_rchan_setblocksize(kobj_t, void *, u_int32_t); static int m3_rchan_trigger(kobj_t, void *, int); +static int m3_rchan_trigger_locked(kobj_t, void *, int); static int m3_rchan_getptr(kobj_t, void *); static struct pcmchan_caps *m3_rchan_getcaps(kobj_t, void *); @@ -334,7 +344,7 @@ m3_wrcd(kobj_t kobj, void *devinfo, int regno, u_int32_t data) struct sc_info *sc = (struct sc_info *)devinfo; if (m3_wait(sc)) { device_printf(sc->dev, "m3_wrcd timed out.\n"); - return -1; + return -1;; } m3_wr_2(sc, CODEC_DATA, data); m3_wr_1(sc, CODEC_COMMAND, regno & 0x7f); @@ -354,31 +364,36 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel struct sc_info *sc = devinfo; struct sc_pchinfo *ch; u_int32_t bus_addr, i; + int idx, data_bytes, dac_data; + int dsp_in_size, dsp_out_size, dsp_in_buf, dsp_out_buf; - int idx = sc->pch_cnt; /* dac instance number, no active reuse! */ - int data_bytes = (((MINISRC_TMP_BUFFER_SIZE & ~1) + - (MINISRC_IN_BUFFER_SIZE & ~1) + - (MINISRC_OUT_BUFFER_SIZE & ~1) + 4) + 255) &~ 255; - int dac_data = 0x1100 + (data_bytes * idx); - - int dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); - int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); - int dsp_in_buf = dac_data + (MINISRC_TMP_BUFFER_SIZE/2); - int dsp_out_buf = dsp_in_buf + (dsp_in_size/2) + 1; - + M3_LOCK(sc); + idx = sc->pch_cnt; /* dac instance number, no active reuse! */ M3_DEBUG(CHANGE, ("m3_pchan_init(dac=%d)\n", idx)); if (dir != PCMDIR_PLAY) { + M3_UNLOCK(sc); device_printf(sc->dev, "m3_pchan_init not PCMDIR_PLAY\n"); - return NULL; + return (NULL); } - ch = &sc->pch[idx]; + data_bytes = (((MINISRC_TMP_BUFFER_SIZE & ~1) + + (MINISRC_IN_BUFFER_SIZE & ~1) + + (MINISRC_OUT_BUFFER_SIZE & ~1) + 4) + 255) &~ 255; + dac_data = 0x1100 + (data_bytes * idx); + + dsp_in_size = MINISRC_IN_BUFFER_SIZE - (0x20 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x20 * 2); + dsp_in_buf = dac_data + (MINISRC_TMP_BUFFER_SIZE/2); + dsp_out_buf = dsp_in_buf + (dsp_in_size/2) + 1; + + ch = &sc->pch[idx]; ch->dac_idx = idx; ch->dac_data = dac_data; if (ch->dac_data + data_bytes/2 >= 0x1c00) { + M3_UNLOCK(sc); device_printf(sc->dev, "m3_pchan_init: revb mem exhausted\n"); - return NULL; + return (NULL); } ch->buffer = b; @@ -386,14 +401,16 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->channel = c; ch->fmt = AFMT_U8; ch->spd = DSP_DEFAULT_SPEED; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1) { + M3_UNLOCK(sc); /* XXX */ + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) { device_printf(sc->dev, "m3_pchan_init chn_allocbuf failed\n"); - return NULL; + return (NULL); } + M3_LOCK(sc); ch->bufsize = sndbuf_getsize(ch->buffer); /* host dma buffer pointers */ - bus_addr = vtophys(sndbuf_getbuf(ch->buffer)); + bus_addr = sndbuf_getbufaddr(ch->buffer); if (bus_addr & 3) { device_printf(sc->dev, "m3_pchan_init unaligned bus_addr\n"); bus_addr = (bus_addr + 4) & ~3; @@ -447,11 +464,15 @@ m3_pchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel m3_wr_assp_data(sc, KDATA_MIXER_XFER0 + sc->pch_cnt, ch->dac_data >> DP_SHIFT_COUNT); - m3_pchan_trigger(NULL, ch, PCMTRIG_START); /* gotta start before stop */ - m3_pchan_trigger(NULL, ch, PCMTRIG_STOP); /* silence noise on load */ + /* gotta start before stop */ + m3_pchan_trigger_locked(NULL, ch, PCMTRIG_START); + /* silence noise on load */ + m3_pchan_trigger_locked(NULL, ch, PCMTRIG_STOP); sc->pch_cnt++; - return ch; + M3_UNLOCK(sc); + + return (ch); } static int @@ -460,6 +481,7 @@ m3_pchan_free(kobj_t kobj, void *chdata) struct sc_pchinfo *ch = chdata; struct sc_info *sc = ch->parent; + M3_LOCK(sc); M3_DEBUG(CHANGE, ("m3_pchan_free(dac=%d)\n", ch->dac_idx)); /* @@ -471,9 +493,10 @@ m3_pchan_free(kobj_t kobj, void *chdata) m3_wr_assp_data(sc, KDATA_DMA_XFER0 + (sc->pch_cnt - 1) + sc->rch_cnt, 0); m3_wr_assp_data(sc, KDATA_MIXER_XFER0 + (sc->pch_cnt-1), 0); - sc->pch_cnt--; - return 0; + M3_UNLOCK(sc); + + return (0); } static int @@ -483,6 +506,7 @@ m3_pchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) struct sc_info *sc = ch->parent; u_int32_t data; + M3_LOCK(sc); M3_DEBUG(CHANGE, ("m3_pchan_setformat(dac=%d, format=0x%x{%s-%s})\n", ch->dac_idx, format, @@ -498,7 +522,9 @@ m3_pchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) m3_wr_assp_data(sc, ch->dac_data + SRC3_WORD_LENGTH_OFFSET, data); ch->fmt = format; - return 0; + M3_UNLOCK(sc); + + return (0); } static int @@ -508,6 +534,7 @@ m3_pchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) struct sc_info *sc = ch->parent; u_int32_t freq; + M3_LOCK(sc); M3_DEBUG(CHANGE, ("m3_pchan_setspeed(dac=%d, speed=%d)\n", ch->dac_idx, speed)); @@ -516,9 +543,11 @@ m3_pchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) } m3_wr_assp_data(sc, ch->dac_data + CDATA_FREQUENCY, freq); - ch->spd = speed; - return speed; /* return closest possible speed */ + M3_UNLOCK(sc); + + /* return closest possible speed */ + return (speed); } static int @@ -534,11 +563,26 @@ m3_pchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize) static int m3_pchan_trigger(kobj_t kobj, void *chdata, int go) +{ + struct sc_pchinfo *ch = chdata; + struct sc_info *sc = ch->parent; + int ret; + + M3_LOCK(sc); + ret = m3_pchan_trigger_locked(kobj, chdata, go); + M3_UNLOCK(sc); + + return (ret); +} + +static int +m3_pchan_trigger_locked(kobj_t kobj, void *chdata, int go) { struct sc_pchinfo *ch = chdata; struct sc_info *sc = ch->parent; u_int32_t data; + M3_LOCK_ASSERT(sc); M3_DEBUG(go == PCMTRIG_START ? CHANGE : go == PCMTRIG_STOP ? CHANGE : go == PCMTRIG_ABORT ? CHANGE : @@ -602,15 +646,17 @@ m3_pchan_getptr(kobj_t kobj, void *chdata) { struct sc_pchinfo *ch = chdata; struct sc_info *sc = ch->parent; - u_int32_t hi, lo, bus_crnt; - u_int32_t bus_base = vtophys(sndbuf_getbuf(ch->buffer)); + u_int32_t hi, lo, bus_base, bus_crnt; + M3_LOCK(sc); + bus_base = sndbuf_getbufaddr(ch->buffer); hi = m3_rd_assp_data(sc, ch->dac_data + CDATA_HOST_SRC_CURRENTH); lo = m3_rd_assp_data(sc, ch->dac_data + CDATA_HOST_SRC_CURRENTL); bus_crnt = lo | (hi << 16); M3_DEBUG(CALL, ("m3_pchan_getptr(dac=%d) result=%d\n", ch->dac_idx, bus_crnt - bus_base)); + M3_UNLOCK(sc); return (bus_crnt - bus_base); /* current byte offset of channel */ } @@ -635,30 +681,35 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel struct sc_rchinfo *ch; u_int32_t bus_addr, i; - int idx = sc->rch_cnt; /* adc instance number, no active reuse! */ - int data_bytes = (((MINISRC_TMP_BUFFER_SIZE & ~1) + - (MINISRC_IN_BUFFER_SIZE & ~1) + - (MINISRC_OUT_BUFFER_SIZE & ~1) + 4) + 255) &~ 255; - int adc_data = 0x1100 + (data_bytes * idx) + data_bytes/2; - - int dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); - int dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); - int dsp_in_buf = adc_data + (MINISRC_TMP_BUFFER_SIZE / 2); - int dsp_out_buf = dsp_in_buf + (dsp_in_size / 2) + 1; + int idx, data_bytes, adc_data; + int dsp_in_size, dsp_out_size, dsp_in_buf, dsp_out_buf; + M3_LOCK(sc); + idx = sc->rch_cnt; /* adc instance number, no active reuse! */ M3_DEBUG(CHANGE, ("m3_rchan_init(adc=%d)\n", idx)); if (dir != PCMDIR_REC) { + M3_UNLOCK(sc); device_printf(sc->dev, "m3_pchan_init not PCMDIR_REC\n"); - return NULL; + return (NULL); } - ch = &sc->rch[idx]; + data_bytes = (((MINISRC_TMP_BUFFER_SIZE & ~1) + + (MINISRC_IN_BUFFER_SIZE & ~1) + + (MINISRC_OUT_BUFFER_SIZE & ~1) + 4) + 255) &~ 255; + adc_data = 0x1100 + (data_bytes * idx) + data_bytes/2; + dsp_in_size = MINISRC_IN_BUFFER_SIZE + (0x10 * 2); + dsp_out_size = MINISRC_OUT_BUFFER_SIZE - (0x10 * 2); + dsp_in_buf = adc_data + (MINISRC_TMP_BUFFER_SIZE / 2); + dsp_out_buf = dsp_in_buf + (dsp_in_size / 2) + 1; + + ch = &sc->rch[idx]; ch->adc_idx = idx; ch->adc_data = adc_data; if (ch->adc_data + data_bytes/2 >= 0x1c00) { + M3_UNLOCK(sc); device_printf(sc->dev, "m3_rchan_init: revb mem exhausted\n"); - return NULL; + return (NULL); } ch->buffer = b; @@ -666,14 +717,16 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel ch->channel = c; ch->fmt = AFMT_U8; ch->spd = DSP_DEFAULT_SPEED; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1) { + M3_UNLOCK(sc); /* XXX */ + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) { device_printf(sc->dev, "m3_rchan_init chn_allocbuf failed\n"); - return NULL; + return (NULL); } + M3_LOCK(sc); ch->bufsize = sndbuf_getsize(ch->buffer); /* host dma buffer pointers */ - bus_addr = vtophys(sndbuf_getbuf(ch->buffer)); + bus_addr = sndbuf_getbufaddr(ch->buffer); if (bus_addr & 3) { device_printf(sc->dev, "m3_rchan_init unaligned bus_addr\n"); bus_addr = (bus_addr + 4) & ~3; @@ -722,11 +775,15 @@ m3_rchan_init(kobj_t kobj, void *devinfo, struct snd_dbuf *b, struct pcm_channel m3_wr_assp_data(sc, KDATA_ADC1_XFER0 + sc->rch_cnt, ch->adc_data >> DP_SHIFT_COUNT); - m3_rchan_trigger(NULL, ch, PCMTRIG_START); /* gotta start before stop */ - m3_rchan_trigger(NULL, ch, PCMTRIG_STOP); /* stop on init */ + /* gotta start before stop */ + m3_rchan_trigger_locked(NULL, ch, PCMTRIG_START); + /* stop on init */ + m3_rchan_trigger_locked(NULL, ch, PCMTRIG_STOP); sc->rch_cnt++; - return ch; + M3_UNLOCK(sc); + + return (ch); } static int @@ -735,6 +792,7 @@ m3_rchan_free(kobj_t kobj, void *chdata) struct sc_rchinfo *ch = chdata; struct sc_info *sc = ch->parent; + M3_LOCK(sc); M3_DEBUG(CHANGE, ("m3_rchan_free(adc=%d)\n", ch->adc_idx)); /* @@ -746,9 +804,10 @@ m3_rchan_free(kobj_t kobj, void *chdata) m3_wr_assp_data(sc, KDATA_DMA_XFER0 + (sc->rch_cnt - 1) + sc->pch_cnt, 0); m3_wr_assp_data(sc, KDATA_ADC1_XFER0 + (sc->rch_cnt - 1), 0); - sc->rch_cnt--; - return 0; + M3_UNLOCK(sc); + + return (0); } static int @@ -758,6 +817,7 @@ m3_rchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) struct sc_info *sc = ch->parent; u_int32_t data; + M3_LOCK(sc); M3_DEBUG(CHANGE, ("m3_rchan_setformat(dac=%d, format=0x%x{%s-%s})\n", ch->adc_idx, format, @@ -771,9 +831,10 @@ m3_rchan_setformat(kobj_t kobj, void *chdata, u_int32_t format) /* 8bit word */ data = ((format & AFMT_U8) || (format & AFMT_S8)) ? 1 : 0; m3_wr_assp_data(sc, ch->adc_data + SRC3_WORD_LENGTH_OFFSET, data); - ch->fmt = format; - return 0; + M3_UNLOCK(sc); + + return (0); } static int @@ -783,6 +844,7 @@ m3_rchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) struct sc_info *sc = ch->parent; u_int32_t freq; + M3_LOCK(sc); M3_DEBUG(CHANGE, ("m3_rchan_setspeed(adc=%d, speed=%d)\n", ch->adc_idx, speed)); @@ -791,9 +853,11 @@ m3_rchan_setspeed(kobj_t kobj, void *chdata, u_int32_t speed) } m3_wr_assp_data(sc, ch->adc_data + CDATA_FREQUENCY, freq); - ch->spd = speed; - return speed; /* return closest possible speed */ + M3_UNLOCK(sc); + + /* return closest possible speed */ + return (speed); } static int @@ -809,11 +873,26 @@ m3_rchan_setblocksize(kobj_t kobj, void *chdata, u_int32_t blocksize) static int m3_rchan_trigger(kobj_t kobj, void *chdata, int go) +{ + struct sc_rchinfo *ch = chdata; + struct sc_info *sc = ch->parent; + int ret; + + M3_LOCK(sc); + ret = m3_rchan_trigger_locked(kobj, chdata, go); + M3_UNLOCK(sc); + + return (ret); +} + +static int +m3_rchan_trigger_locked(kobj_t kobj, void *chdata, int go) { struct sc_rchinfo *ch = chdata; struct sc_info *sc = ch->parent; u_int32_t data; + M3_LOCK_ASSERT(sc); M3_DEBUG(go == PCMTRIG_START ? CHANGE : go == PCMTRIG_STOP ? CHANGE : go == PCMTRIG_ABORT ? CHANGE : @@ -872,15 +951,17 @@ m3_rchan_getptr(kobj_t kobj, void *chdata) { struct sc_rchinfo *ch = chdata; struct sc_info *sc = ch->parent; - u_int32_t hi, lo, bus_crnt; - u_int32_t bus_base = vtophys(sndbuf_getbuf(ch->buffer)); + u_int32_t hi, lo, bus_base, bus_crnt; + M3_LOCK(sc); + bus_base = sndbuf_getbufaddr(ch->buffer); hi = m3_rd_assp_data(sc, ch->adc_data + CDATA_HOST_SRC_CURRENTH); lo = m3_rd_assp_data(sc, ch->adc_data + CDATA_HOST_SRC_CURRENTL); bus_crnt = lo | (hi << 16); M3_DEBUG(CALL, ("m3_rchan_getptr(adc=%d) result=%d\n", ch->adc_idx, bus_crnt - bus_base)); + M3_UNLOCK(sc); return (bus_crnt - bus_base); /* current byte offset of channel */ } @@ -906,9 +987,12 @@ m3_intr(void *p) M3_DEBUG(INTR, ("m3_intr\n")); + M3_LOCK(sc); status = m3_rd_1(sc, HOST_INT_STATUS); - if (!status) + if (!status) { + M3_UNLOCK(sc); return; + } m3_wr_1(sc, HOST_INT_STATUS, 0xff); /* ack the int? */ @@ -949,14 +1033,20 @@ m3_intr(void *p) for (i=0 ; ipch_cnt ; i++) { if (sc->pch[i].active) { + M3_UNLOCK(sc); chn_intr(sc->pch[i].channel); + M3_LOCK(sc); } } for (i=0 ; irch_cnt ; i++) { if (sc->rch[i].active) { + M3_UNLOCK(sc); chn_intr(sc->rch[i].channel); + M3_LOCK(sc); } } + + M3_UNLOCK(sc); } /* -------------------------------------------------------------------- */ @@ -968,6 +1058,7 @@ m3_power(struct sc_info *sc, int state) u_int32_t data; M3_DEBUG(CHANGE, ("m3_power(%d)\n", state)); + M3_LOCK_ASSERT(sc); data = pci_read_config(sc->dev, 0x34, 1); if (pci_read_config(sc->dev, data, 1) == 1) { @@ -983,6 +1074,7 @@ m3_init(struct sc_info *sc) u_int32_t data, i, size; u_int8_t reset_state; + M3_LOCK_ASSERT(sc); M3_DEBUG(CHANGE, ("m3_init\n")); /* diable legacy emulations. */ @@ -1017,7 +1109,7 @@ m3_init(struct sc_info *sc) assp_kernel_image[i]); } /* - * We only have this one client and we know that 0x400 is free in + * We only have this one client and we know that 0x400 is kfree in * our kernel's mem map, so lets just drop it there. It seems that * the minisrc doesn't need vectors, so we won't bother with them.. */ @@ -1073,7 +1165,7 @@ m3_pci_probe(device_t dev) for (card = m3_card_types ; card->pci_id ; card++) { if (pci_get_devid(dev) == card->pci_id) { device_set_desc(dev, card->name); - return 0; + return BUS_PROBE_DEFAULT; } } return ENXIO; @@ -1098,7 +1190,13 @@ m3_pci_attach(device_t dev) sc->dev = dev; sc->type = pci_get_devid(dev); - + sc->sc_lock = snd_mtxcreate(device_get_nameunit(dev), + "sound softc"); + if (sc->sc_lock == NULL) { + device_printf(dev, "cannot create mutex\n"); + kfree(sc, M_DEVBUF); + return (ENXIO); + } for (card = m3_card_types ; card->pci_id ; card++) { if (sc->type == card->pci_id) { sc->which = card->which; @@ -1112,14 +1210,14 @@ m3_pci_attach(device_t dev) data |= (PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, data, 2); - sc->regid = PCIR_MAPS; + sc->regid = PCIR_BAR(0); sc->regtype = SYS_RES_MEMORY; - sc->reg = bus_alloc_resource(dev, sc->regtype, &sc->regid, - 0, ~0, 1, RF_ACTIVE); + sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid, + 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); + sc->reg = bus_alloc_resource_any(dev, sc->regtype, &sc->regid, + RF_ACTIVE); } if (!sc->reg) { device_printf(dev, "unable to allocate register space\n"); @@ -1129,35 +1227,42 @@ m3_pci_attach(device_t dev) sc->sh = rman_get_bushandle(sc->reg); sc->irqid = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irqid, - 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); if (!sc->irq) { device_printf(dev, "unable to allocate interrupt\n"); goto bad; } - if (snd_setup_intr(dev, sc->irq, 0, m3_intr, sc, &sc->ih, NULL)) { + if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, m3_intr, sc, &sc->ih)) { device_printf(dev, "unable to setup interrupt\n"); goto bad; } - sc->bufsz = pcm_getbuffersize(dev, 1024, M3_BUFSIZE_DEFAULT, 65536); - - if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/2, /*boundary*/0, - /*lowaddr*/M3_MAXADDR, - /*highaddr*/BUS_SPACE_MAXADDR, - /*filter*/NULL, /*filterarg*/NULL, - /*maxsize*/sc->bufsz, /*nsegments*/1, - /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { + sc->bufsz = pcm_getbuffersize(dev, M3_BUFSIZE_MAX, M3_BUFSIZE_DEFAULT, + M3_BUFSIZE_MAX); + + if (bus_dma_tag_create( + NULL, /* parent */ + 2, 0, /* alignment, boundary */ + M3_MAXADDR, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filtfunc, filtfuncarg */ + sc->bufsz, /* maxsize */ + 1, /* nsegments */ + 0x3ffff, /* maxsegz */ + 0, /* flags */ + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } + M3_LOCK(sc); m3_power(sc, 0); /* power up */ - /* init chip */ - if (m3_init(sc) == -1) { + i = m3_init(sc); + M3_UNLOCK(sc); + if (i == -1) { device_printf(dev, "unable to initialize the card\n"); goto bad; } @@ -1191,9 +1296,10 @@ m3_pci_attach(device_t dev) goto bad; } } - ksnprintf(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)); + ksnprintf(status, SND_STATUSLEN, "at %s 0x%lx irq %ld %s", + (sc->regtype == SYS_RES_IOPORT)? "io" : "memory", + rman_get_start(sc->reg), rman_get_start(sc->irq), + PCM_KLDSTRING(snd_maestro3)); if (pcm_setstatus(dev, status)) { device_printf(dev, "attach: pcm_setstatus error\n"); goto bad; @@ -1213,21 +1319,18 @@ m3_pci_attach(device_t dev) return 0; bad: - if (codec) { + if (codec) ac97_destroy(codec); - } - if (sc->reg) { - bus_release_resource(dev, sc->regtype, sc->regid, sc->reg); - } - if (sc->ih) { + if (sc->ih) bus_teardown_intr(dev, sc->irq, sc->ih); - } - if (sc->irq) { + if (sc->irq) bus_release_resource(dev, SYS_RES_IRQ, sc->irqid, sc->irq); - } - if (sc->parent_dmat) { + if (sc->reg) + bus_release_resource(dev, sc->regtype, sc->regid, sc->reg); + if (sc->parent_dmat) bus_dma_tag_destroy(sc->parent_dmat); - } + if (sc->sc_lock) + snd_mtxfree(sc->sc_lock); kfree(sc, M_DEVBUF); return ENXIO; } @@ -1243,15 +1346,19 @@ m3_pci_detach(device_t dev) if ((r = pcm_unregister(dev)) != 0) { return r; } + + M3_LOCK(sc); m3_uninit(sc); /* shutdown chip */ m3_power(sc, 3); /* power off */ + M3_UNLOCK(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_release_resource(dev, sc->regtype, sc->regid, sc->reg); bus_dma_tag_destroy(sc->parent_dmat); kfree(sc->savemem, M_DEVBUF); + snd_mtxfree(sc->sc_lock); kfree(sc, M_DEVBUF); return 0; } @@ -1264,14 +1371,17 @@ m3_pci_suspend(device_t dev) M3_DEBUG(CHANGE, ("m3_pci_suspend\n")); + M3_LOCK(sc); for (i=0 ; ipch_cnt ; i++) { if (sc->pch[i].active) { - m3_pchan_trigger(NULL, &sc->pch[i], PCMTRIG_STOP); + m3_pchan_trigger_locked(NULL, &sc->pch[i], + PCMTRIG_STOP); } } for (i=0 ; irch_cnt ; i++) { if (sc->rch[i].active) { - m3_rchan_trigger(NULL, &sc->rch[i], PCMTRIG_STOP); + m3_rchan_trigger_locked(NULL, &sc->rch[i], + PCMTRIG_STOP); } } DELAY(10 * 1000); /* give things a chance to stop */ @@ -1290,6 +1400,7 @@ m3_pci_suspend(device_t dev) /* Power down the card to D3 state */ m3_power(sc, 3); + M3_UNLOCK(sc); return 0; } @@ -1303,6 +1414,7 @@ m3_pci_resume(device_t dev) M3_DEBUG(CHANGE, ("m3_pci_resume\n")); + M3_LOCK(sc); /* Power the card back to D0 */ m3_power(sc, 0); @@ -1328,23 +1440,28 @@ m3_pci_resume(device_t dev) m3_enable_ints(sc); + M3_UNLOCK(sc); /* XXX */ if (mixer_reinit(dev) == -1) { device_printf(dev, "unable to reinitialize the mixer\n"); - return ENXIO; + return (ENXIO); } + M3_LOCK(sc); /* Turn the channels back on */ for (i=0 ; ipch_cnt ; i++) { if (sc->pch[i].active) { - m3_pchan_trigger(NULL, &sc->pch[i], PCMTRIG_START); + m3_pchan_trigger_locked(NULL, &sc->pch[i], + PCMTRIG_START); } } for (i=0 ; irch_cnt ; i++) { if (sc->rch[i].active) { - m3_rchan_trigger(NULL, &sc->rch[i], PCMTRIG_START); + m3_rchan_trigger_locked(NULL, &sc->rch[i], + PCMTRIG_START); } } + M3_UNLOCK(sc); return 0; } @@ -1355,7 +1472,10 @@ m3_pci_shutdown(device_t dev) M3_DEBUG(CALL, ("m3_pci_shutdown\n")); + M3_LOCK(sc); m3_power(sc, 3); /* power off */ + M3_UNLOCK(sc); + return 0; } @@ -1364,6 +1484,8 @@ m3_assp_halt(struct sc_info *sc) { u_int8_t data, reset_state; + M3_LOCK_ASSERT(sc); + data = m3_rd_1(sc, DSP_PORT_CONTROL_REG_B); reset_state = data & ~REGB_STOP_CLOCK; /* remember for continue */ DELAY(10 * 1000); @@ -1379,6 +1501,9 @@ m3_config(struct sc_info *sc) u_int32_t data, hv_cfg; int hint; + M3_LOCK_ASSERT(sc); + + M3_UNLOCK(sc); /* * The volume buttons can be wired up via two different sets of pins. * This presents a problem since we can't tell which way it's @@ -1391,6 +1516,7 @@ m3_config(struct sc_info *sc) hv_cfg = (hint > 0) ? HV_BUTTON_FROM_GD : 0; else hv_cfg = HV_BUTTON_FROM_GD; + M3_LOCK(sc); data = pci_read_config(sc->dev, PCI_ALLEGRO_CONFIG, 4); data &= ~HV_BUTTON_FROM_GD; @@ -1439,6 +1565,8 @@ m3_amp_enable(struct sc_info *sc) u_int32_t gpo, polarity_port, polarity; u_int16_t data; + M3_LOCK_ASSERT(sc); + switch (sc->which) { case ESS_ALLEGRO_1: polarity_port = 0x1800; @@ -1468,6 +1596,7 @@ m3_codec_reset(struct sc_info *sc) u_int16_t data, dir; int retry = 0; + M3_LOCK_ASSERT(sc); do { data = m3_rd_2(sc, GPIO_DIRECTION); dir = data | 0x10; /* assuming pci bus master? */ @@ -1525,5 +1654,5 @@ static driver_t m3_driver = { }; DRIVER_MODULE(snd_maestro3, pci, m3_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_maestro3, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_maestro3, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_maestro3, 1); diff --git a/sys/dev/sound/pci/maestro_reg.h b/sys/dev/sound/pci/maestro_reg.h index f5bf38bb10..e9d30da2ec 100644 --- a/sys/dev/sound/pci/maestro_reg.h +++ b/sys/dev/sound/pci/maestro_reg.h @@ -23,9 +23,9 @@ * 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: src/sys/dev/sound/pci/maestro_reg.h,v 1.1.2.4 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/maestro_reg.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * maestro_reg.h,v 1.13 2001/11/11 18:29:46 taku Exp + * $FreeBSD: src/sys/dev/sound/pci/maestro_reg.h,v 1.3 2004/11/10 04:29:09 julian Exp $ + * $DragonFly: src/sys/dev/sound/pci/maestro_reg.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef MAESTRO_REG_H_INCLUDED @@ -42,10 +42,13 @@ /* Chip configurations */ #define CONF_MAESTRO 0x50 +#define MAESTRO_PMC 0x08000000 +#define MAESTRO_SPDIF 0x01000000 +#define MAESTRO_HWVOL 0x00800000 #define MAESTRO_CHIBUS 0x00100000 #define MAESTRO_POSTEDWRITE 0x00000080 #define MAESTRO_DMA_PCITIMING 0x00000040 -#define MAESTRO_SWAP_LR 0x00000010 +#define MAESTRO_SWAP_LR 0x00000020 /* ACPI configurations */ #define CONF_ACPI_STOPCLOCK 0x54 @@ -136,12 +139,15 @@ #define HOSTINT_STAT_SB 0x01 /* Hardware volume */ +#define PORT_HWVOL_CTRL 0x1b /* BYTE RW */ +#define HWVOL_CTRL_SPLIT_SHADOW 0x01 + #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_MUTE 0x11 #define HWVOL_UP 0xaa #define HWVOL_DOWN 0x66 @@ -164,8 +170,6 @@ #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 @@ -183,6 +187,15 @@ #define RINGBUS_DEST_DSOUND_IN 4 #define RINGBUS_DEST_ASSP_IN 5 +/* Ring bus control B */ +#define PORT_RINGBUS_CTRL_B 0x38 /* BYTE RW */ +#define RINGBUS_CTRL_SSPE 0x40 +#define RINGBUS_CTRL_2ndCODEC 0x20 +#define RINGBUS_CTRL_SPDIF 0x10 +#define RINGBUS_CTRL_ITB_DISABLE 0x08 +#define RINGBUS_CTRL_CODEC_ID_MASK 0x03 +#define RINGBUS_CTRL_CODEC_ID_AC98 2 + /* General Purpose I/O */ #define PORT_GPIO_DATA 0x60 /* WORD RW */ #define PORT_GPIO_MASK 0x64 /* WORD RW */ @@ -298,22 +311,35 @@ /* 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 +/* Wave Processor Wavespace Address */ +#define WPWA_MAX ((1 << 22) - 1) +#define WPWA_STEREO (1 << 23) +#define WPWA_USE_SYSMEM (1 << 22) + +#define WPWA_WTBAR_SHIFT(wtsz) WPWA_WTBAR_SHIFT_##wtsz +#define WPWA_WTBAR_SHIFT_1 15 +#define WPWA_WTBAR_SHIFT_2 16 +#define WPWA_WTBAR_SHIFT_4 17 +#define WPWA_WTBAR_SHIFT_8 18 + +#define WPWA_PCMBAR_SHIFT 20 + /* APU register 5 - 7 */ #define APUREG_CURPTR 5 #define APUREG_ENDPTR 6 #define APUREG_LOOPLEN 7 +/* APU register 8 */ +#define APUREG_EFFECT_GAIN 8 + +/* Effect gain? */ +#define APUREG_EFFECT_GAIN_MASK 0x00ff + /* APU register 9 */ #define APUREG_AMPLITUDE 9 #define APU_AMPLITUDE_NOW_MASK 0xff00 @@ -339,11 +365,20 @@ #define PAN_FRONT 0x08 #define PAN_LEFT 0x10 +/* Source routing. */ +#define APUREG_ROUTING 11 +#define APU_INVERT_POLARITY_B 0x8000 +#define APU_DATASRC_B_MASK 0x7f00 +#define APU_INVERT_POLARITY_A 0x0080 +#define APU_DATASRC_A_MASK 0x007f + +#define APU_DATASRC_A_SHIFT 0 +#define APU_DATASRC_B_SHIFT 8 + /* ----------------------------- * Limits. */ -#define WPWA_MAX ((1 << 22) - 1) #define WPWA_MAXADDR ((1 << 23) - 1) #define MAESTRO_MAXADDR ((1 << 28) - 1) diff --git a/sys/dev/sound/pci/neomagic-coeff.h b/sys/dev/sound/pci/neomagic-coeff.h index 28e5455e78..9c5f87a103 100644 --- a/sys/dev/sound/pci/neomagic-coeff.h +++ b/sys/dev/sound/pci/neomagic-coeff.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Derived from the public domain Linux driver @@ -25,13 +25,13 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/neomagic-coeff.h,v 1.1.2.2 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/neomagic-coeff.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/neomagic-coeff.h,v 1.4 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/neomagic-coeff.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #define NM_TOTAL_COEFF_COUNT 0x3158 -static char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { +static u_char coefficients[NM_TOTAL_COEFF_COUNT * 4] = { 0xFF, 0xFF, 0x2F, 0x00, 0x4B, 0xFF, 0xA5, 0x01, 0xEF, 0xFC, 0x21, 0x05, 0x87, 0xF7, 0x62, 0x11, 0xE9, 0x45, 0x5E, 0xF9, 0xB5, 0x01, 0xDE, 0xFF, 0xA4, 0xFF, 0x60, 0x00, 0xCA, 0xFF, 0x0D, 0x00, 0xFD, diff --git a/sys/dev/sound/pci/neomagic.c b/sys/dev/sound/pci/neomagic.c index a4a21a59fd..f61478e738 100644 --- a/sys/dev/sound/pci/neomagic.c +++ b/sys/dev/sound/pci/neomagic.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Derived from the public domain Linux driver @@ -25,8 +25,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/neomagic.c,v 1.7.2.11 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/neomagic.c,v 1.6 2006/12/20 18:14:40 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/neomagic.c,v 1.34.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/neomagic.c,v 1.7 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -37,7 +37,7 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/neomagic.c,v 1.6 2006/12/20 18:14:40 dillon Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/neomagic.c,v 1.7 2007/01/04 21:47:02 corecode Exp $"); /* -------------------------------------------------------------------- */ @@ -226,7 +226,16 @@ nm_initcd(kobj_t obj, void *devinfo) struct sc_info *sc = (struct sc_info *)devinfo; nm_wr(sc, 0x6c0, 0x01, 1); +#if 0 + /* + * The following code-line may cause a hang for some chipsets, see + * PR 56617. + * In case of a bugreport without this line have a look at the PR and + * conditionize the code-line based upon the specific version of + * the chip. + */ nm_wr(sc, 0x6cc, 0x87, 1); +#endif nm_wr(sc, 0x6cc, 0x80, 1); nm_wr(sc, 0x6cc, 0x00, 1); return 1; @@ -611,10 +620,10 @@ nm_pci_probe(device_t dev) PCIM_CMD_PORTEN | PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN, 2); - sc->regid = PCIR_MAPS + 4; - sc->reg = bus_alloc_resource(dev, SYS_RES_MEMORY, - &sc->regid, 0, ~0, 1, - RF_ACTIVE); + sc->regid = PCIR_BAR(1); + sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->regid, + RF_ACTIVE); if (!sc->reg) { device_printf(dev, "unable to map register space\n"); @@ -678,12 +687,12 @@ nm_pci_attach(device_t dev) 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); + sc->bufid = PCIR_BAR(0); + sc->buf = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->bufid, + RF_ACTIVE); + sc->regid = PCIR_BAR(1); + sc->reg = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->regid, + RF_ACTIVE); if (!sc->buf || !sc->reg) { device_printf(dev, "unable to map register space\n"); @@ -700,16 +709,16 @@ nm_pci_attach(device_t dev) 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 || snd_setup_intr(dev, sc->irq, 0, nm_intr, sc, &sc->ih, NULL)) { + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!sc->irq || snd_setup_intr(dev, sc->irq, 0, nm_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } - ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld", + ksnprintf(status, SND_STATUSLEN, "at memory 0x%lx, 0x%lx irq %ld %s", rman_get_start(sc->buf), rman_get_start(sc->reg), - rman_get_start(sc->irq)); + rman_get_start(sc->irq),PCM_KLDSTRING(snd_neomagic)); if (pcm_register(dev, sc, 1, 1)) goto bad; pcm_addchan(dev, PCMDIR_REC, &nmchan_class, sc); @@ -821,5 +830,5 @@ static driver_t nm_driver = { }; DRIVER_MODULE(snd_neomagic, pci, nm_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_neomagic, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_neomagic, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_neomagic, 1); diff --git a/sys/dev/sound/pci/neomagic.h b/sys/dev/sound/pci/neomagic.h index 0c87a75dfd..885c67d40d 100644 --- a/sys/dev/sound/pci/neomagic.h +++ b/sys/dev/sound/pci/neomagic.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Derived from the public domain Linux driver @@ -25,8 +25,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/neomagic.h,v 1.1.2.2 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/neomagic.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/neomagic.h,v 1.4 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/neomagic.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _NM256_H_ diff --git a/sys/dev/sound/pci/solo.c b/sys/dev/sound/pci/solo.c index 965b78a9f2..e193430883 100644 --- a/sys/dev/sound/pci/solo.c +++ b/sys/dev/sound/pci/solo.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,8 +22,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/solo.c,v 1.9.2.8 2002/04/22 15:49:32 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/solo.c,v 1.8 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/solo.c,v 1.35.2.4 2006/07/13 01:53:54 yongari Exp $ + * $DragonFly: src/sys/dev/sound/pci/solo.c,v 1.9 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -36,17 +36,20 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/solo.c,v 1.8 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/solo.c,v 1.9 2007/01/04 21:47:02 corecode Exp $"); #define SOLO_DEFAULT_BUFSZ 16384 #define ABS(x) (((x) < 0)? -(x) : (x)) /* if defined, playback always uses the 2nd channel and full duplex works */ -#undef ESS18XX_DUPLEX +#define ESS18XX_DUPLEX 1 /* more accurate clocks and split audio1/audio2 rates */ #define ESS18XX_NEWSPEED +/* 1 = INTR_MPSAFE, 0 = GIANT */ +#define ESS18XX_MPSAFE 1 + static u_int32_t ess_playfmt[] = { AFMT_U8, AFMT_STEREO | AFMT_U8, @@ -58,7 +61,7 @@ static u_int32_t ess_playfmt[] = { AFMT_STEREO | AFMT_U16_LE, 0 }; -static struct pcmchan_caps ess_playcaps = {5000, 49000, ess_playfmt, 0}; +static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0}; /* * Recording output is byte-swapped @@ -74,7 +77,7 @@ static u_int32_t ess_recfmt[] = { AFMT_STEREO | AFMT_U16_BE, 0 }; -static struct pcmchan_caps ess_reccaps = {5000, 49000, ess_recfmt, 0}; +static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_recfmt, 0}; struct ess_info; @@ -96,8 +99,21 @@ struct ess_info { unsigned int bufsz; struct ess_chinfo pch, rch; +#if ESS18XX_MPSAFE == 1 + struct spinlock *lock; +#endif }; +#if ESS18XX_MPSAFE == 1 +#define ess_lock(_ess) snd_mtxlock((_ess)->lock) +#define ess_unlock(_ess) snd_mtxunlock((_ess)->lock) +#define ess_lock_assert(_ess) snd_mtxassert((_ess)->lock) +#else +#define ess_lock(_ess) +#define ess_unlock(_ess) +#define ess_lock_assert(_ess) +#endif + 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); @@ -223,12 +239,10 @@ static void ess_setmixer(struct ess_info *sc, u_int port, u_int value) { DEB(kprintf("ess_setmixer: reg=%x, val=%x\n", port, value);) - crit_enter(); 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); - crit_exit(); } static int @@ -236,12 +250,10 @@ ess_getmixer(struct ess_info *sc, u_int port) { int val; - crit_enter(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); val = ess_rd(sc, SB_MIX_DATA); DELAY(10); - crit_exit(); return val; } @@ -296,14 +308,17 @@ ess_intr(void *arg) struct ess_info *sc = (struct ess_info *)arg; int src, pirq = 0, rirq = 0; + ess_lock(sc); src = 0; if (ess_getmixer(sc, 0x7a) & 0x80) src |= 2; if (ess_rd(sc, 0x0c) & 0x01) src |= 1; - if (src == 0) + if (src == 0) { + ess_unlock(sc); return; + } if (sc->duplex) { pirq = (src & sc->pch.hwch)? 1 : 0; @@ -328,7 +343,9 @@ ess_intr(void *arg) else ess_setmixer(sc, 0x78, ess_getmixer(sc, 0x78) & ~0x03); } + ess_unlock(sc); chn_intr(sc->pch.channel); + ess_lock(sc); } if (rirq) { @@ -338,13 +355,17 @@ ess_intr(void *arg) /* XXX: will this stop audio2? */ ess_write(sc, 0xb8, ess_read(sc, 0xb8) & ~0x01); } + ess_unlock(sc); chn_intr(sc->rch.channel); + ess_lock(sc); } if (src & 2) ess_setmixer(sc, 0x7a, ess_getmixer(sc, 0x7a) & ~0x80); if (src & 1) ess_rd(sc, DSP_DATA_AVAIL); + + ess_unlock(sc); } /* utility functions for ESS */ @@ -520,7 +541,7 @@ esschan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->channel = c; ch->buffer = b; ch->dir = dir; - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) == -1) + if (sndbuf_alloc(ch->buffer, sc->parent_dmat, sc->bufsz) != 0) return NULL; ch->hwch = 1; if ((dir == PCMDIR_PLAY) && (sc->duplex)) @@ -570,9 +591,10 @@ esschan_trigger(kobj_t obj, void *data, int go) if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; + ess_lock(sc); switch (go) { case PCMTRIG_START: - ess_dmasetup(sc, ch->hwch, vtophys(sndbuf_getbuf(ch->buffer)), sndbuf_getsize(ch->buffer), ch->dir); + ess_dmasetup(sc, ch->hwch, sndbuf_getbufaddr(ch->buffer), sndbuf_getsize(ch->buffer), ch->dir); ess_dmatrigger(sc, ch->hwch, 1); ess_start(ch); break; @@ -583,6 +605,7 @@ esschan_trigger(kobj_t obj, void *data, int go) ess_stop(ch); break; } + ess_unlock(sc); return 0; } @@ -591,8 +614,12 @@ esschan_getptr(kobj_t obj, void *data) { struct ess_chinfo *ch = data; struct ess_info *sc = ch->parent; + int ret; - return ess_dmapos(sc, ch->hwch); + ess_lock(sc); + ret = ess_dmapos(sc, ch->hwch); + ess_unlock(sc); + return ret; } static struct pcmchan_caps * @@ -762,13 +789,12 @@ ess_dmapos(struct ess_info *sc, int ch) int p = 0, i = 0, j = 0; KASSERT(ch == 1 || ch == 2, ("bad ch")); - crit_enter(); 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 crit, stop the DMA, and try over and over until + * 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. */ @@ -786,7 +812,6 @@ ess_dmapos(struct ess_info *sc, int ch) } else if (ch == 2) p = port_rd(sc->io, 0x4, 2); - crit_exit(); return sc->dmasz[ch - 1] - p; } @@ -811,27 +836,27 @@ ess_release_resources(struct ess_info *sc, device_t dev) sc->irq = 0; } if (sc->io) { - bus_release_resource(dev, SYS_RES_IOPORT, 0 * 4 + PCIR_MAPS, sc->io); + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(0), sc->io); sc->io = 0; } if (sc->sb) { - bus_release_resource(dev, SYS_RES_IOPORT, 1 * 4 + PCIR_MAPS, sc->sb); + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(1), sc->sb); sc->sb = 0; } if (sc->vc) { - bus_release_resource(dev, SYS_RES_IOPORT, 2 * 4 + PCIR_MAPS, sc->vc); + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(2), sc->vc); sc->vc = 0; } if (sc->mpu) { - bus_release_resource(dev, SYS_RES_IOPORT, 3 * 4 + PCIR_MAPS, sc->mpu); + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(3), sc->mpu); sc->mpu = 0; } if (sc->gp) { - bus_release_resource(dev, SYS_RES_IOPORT, 4 * 4 + PCIR_MAPS, sc->gp); + bus_release_resource(dev, SYS_RES_IOPORT, PCIR_BAR(4), sc->gp); sc->gp = 0; } @@ -840,6 +865,13 @@ ess_release_resources(struct ess_info *sc, device_t dev) sc->parent_dmat = 0; } +#if ESS18XX_MPSAFE == 1 + if (sc->lock) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } +#endif + kfree(sc, M_DEVBUF); } @@ -848,25 +880,33 @@ 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 = PCIR_BAR(0); + sc->io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); - rid = 1 * 4 + PCIR_MAPS; - sc->sb = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + rid = PCIR_BAR(1); + sc->sb = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); - rid = 2 * 4 + PCIR_MAPS; - sc->vc = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + rid = PCIR_BAR(2); + sc->vc = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); - rid = 3 * 4 + PCIR_MAPS; - sc->mpu = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + rid = PCIR_BAR(3); + sc->mpu = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); - rid = 4 * 4 + PCIR_MAPS; - sc->gp = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); + rid = PCIR_BAR(4); + sc->gp = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); rid = 0; - sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); + sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE | RF_SHAREABLE); + +#if ESS18XX_MPSAFE == 1 + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); + return (sc->irq && sc->io && sc->sb && sc->vc && + sc->mpu && sc->gp && sc->lock)? 0 : ENXIO; +#else return (sc->irq && sc->io && sc->sb && sc->vc && sc->mpu && sc->gp)? 0 : ENXIO; +#endif } static int @@ -889,12 +929,55 @@ ess_probe(device_t dev) if (s) device_set_desc(dev, s); - return s? 0 : ENXIO; + return s ? BUS_PROBE_DEFAULT : ENXIO; } -#define PCI_LEGACYCONTROL 0x40 -#define PCI_CONFIG 0x50 -#define PCI_DDMACONTROL 0x60 +#define ESS_PCI_LEGACYCONTROL 0x40 +#define ESS_PCI_CONFIG 0x50 +#define ESS_PCI_DDMACONTROL 0x60 + +static int +ess_suspend(device_t dev) +{ + return 0; +} + +static int +ess_resume(device_t dev) +{ + uint16_t ddma; + uint32_t data; + struct ess_info *sc = pcm_getdevinfo(dev); + + ess_lock(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); + + ddma = rman_get_start(sc->vc) | 1; + pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2); + pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); + pci_write_config(dev, ESS_PCI_CONFIG, 0, 2); + + if (ess_reset_dsp(sc)) { + ess_unlock(sc); + goto no; + } + ess_unlock(sc); + if (mixer_reinit(dev)) + goto no; + ess_lock(sc); + if (sc->newspeed) + ess_setmixer(sc, 0x71, 0x2a); + + port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ + ess_unlock(sc); + + return 0; + no: + return EIO; +} static int ess_attach(device_t dev) @@ -919,14 +1002,9 @@ ess_attach(device_t dev) sc->bufsz = pcm_getbuffersize(dev, 4096, SOLO_DEFAULT_BUFSZ, 65536); 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; - if (mixer_init(dev, &solomixer_class, sc)) - goto no; + pci_write_config(dev, ESS_PCI_LEGACYCONTROL, 0x805f, 2); + pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); + pci_write_config(dev, ESS_PCI_CONFIG, 0, 2); port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ #ifdef ESS18XX_DUPLEX @@ -940,27 +1018,47 @@ ess_attach(device_t dev) #else sc->newspeed = 0; #endif - if (sc->newspeed) - ess_setmixer(sc, 0x71, 0x2a); + if (snd_setup_intr(dev, sc->irq, +#if ESS18XX_MPSAFE == 1 + INTR_MPSAFE +#else + 0 +#endif + , ess_intr, sc, &sc->ih)) { + device_printf(dev, "unable to map interrupt\n"); + goto no; + } - snd_setup_intr(dev, sc->irq, 0, ess_intr, sc, &sc->ih, NULL); if (!sc->duplex) pcm_setflags(dev, pcm_getflags(dev) | SD_F_SIMPLEX); +#if 0 if (bus_dma_tag_create(/*parent*/NULL, /*alignment*/65536, /*boundary*/0, +#endif + 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*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &sc->parent_dmat) != 0) { + /*flags*/0, + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld", + if (ess_reset_dsp(sc)) + goto no; + + if (sc->newspeed) + ess_setmixer(sc, 0x71, 0x2a); + + if (mixer_init(dev, &solomixer_class, sc)) + goto no; + + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx,0x%lx,0x%lx irq %ld %s", rman_get_start(sc->io), rman_get_start(sc->sb), rman_get_start(sc->vc), - rman_get_start(sc->irq)); + rman_get_start(sc->irq),PCM_KLDSTRING(snd_solo)); if (pcm_register(dev, sc, 1, 1)) goto no; @@ -995,8 +1093,8 @@ static device_method_t ess_methods[] = { 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), + DEVMETHOD(device_resume, ess_resume), + DEVMETHOD(device_suspend, ess_suspend), { 0, 0 } }; @@ -1008,7 +1106,7 @@ static driver_t ess_driver = { }; DRIVER_MODULE(snd_solo, pci, ess_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_solo, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_solo, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_solo, 1); diff --git a/sys/dev/sound/pci/t4dwave.c b/sys/dev/sound/pci/t4dwave.c index 02a2589fcf..698bca16df 100644 --- a/sys/dev/sound/pci/t4dwave.c +++ b/sys/dev/sound/pci/t4dwave.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/t4dwave.c,v 1.9.2.11 2002/10/22 08:27:13 cognet Exp $ - * $DragonFly: src/sys/dev/sound/pci/t4dwave.c,v 1.8 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/t4dwave.c,v 1.48 2005/03/01 08:58:05 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/t4dwave.c,v 1.9 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -34,7 +34,8 @@ #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/t4dwave.c,v 1.8 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/t4dwave.c,v 1.9 2007/01/04 21:47:02 corecode Exp $"); + /* -------------------------------------------------------------------- */ #define TDX_PCI_ID 0x20001023 @@ -93,7 +94,7 @@ struct tr_info { int regtype, regid, irqid; void *ih; - void *lock; + struct spinlock *lock; u_int32_t playchns; unsigned int bufsz; @@ -274,7 +275,7 @@ tr_wrcd(kobj_t obj, void *devinfo, int regno, u_int32_t data) } } if (tr->type != ALI_PCI_ID || i > 0) { - for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) + for (i=TR_TIMEOUT_CDC; (i>0) && (j & trw); i--) j=tr_rd(tr, treg, 4); if (tr->type == ALI_PCI_ID && tr->rev > 0x01) trw |= 0x0100; @@ -543,7 +544,7 @@ trpchan_trigger(kobj_t obj, void *data, int go) ch->fms = 0; ch->ec = 0; ch->alpha = 0; - ch->lba = vtophys(sndbuf_getbuf(ch->buffer)); + ch->lba = sndbuf_getbufaddr(ch->buffer); ch->cso = 0; ch->eso = (sndbuf_getsize(ch->buffer) / sndbuf_getbps(ch->buffer)) - 1; ch->rvol = ch->cvol = 0x7f; @@ -669,7 +670,7 @@ trrchan_trigger(kobj_t obj, void *data, int go) 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); + tr_wr(tr, TR_REG_DMAR0, sndbuf_getbufaddr(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); @@ -692,7 +693,7 @@ trrchan_getptr(kobj_t obj, void *data) struct tr_info *tr = ch->parent; /* return current byte offset of channel */ - return tr_rd(tr, TR_REG_DMAR0, 4) - vtophys(sndbuf_getbuf(ch->buffer)); + return tr_rd(tr, TR_REG_DMAR0, 4) - sndbuf_getbufaddr(ch->buffer); } static struct pcmchan_caps * @@ -792,16 +793,16 @@ tr_pci_probe(device_t dev) switch (pci_get_devid(dev)) { case SPA_PCI_ID: device_set_desc(dev, "SiS 7018"); - return 0; + return BUS_PROBE_DEFAULT; case ALI_PCI_ID: device_set_desc(dev, "Acer Labs M5451"); - return 0; + return BUS_PROBE_DEFAULT; case TDX_PCI_ID: device_set_desc(dev, "Trident 4DWave DX"); - return 0; + return BUS_PROBE_DEFAULT; case TNX_PCI_ID: device_set_desc(dev, "Trident 4DWave NX"); - return 0; + return BUS_PROBE_DEFAULT; } return ENXIO; @@ -830,9 +831,10 @@ tr_pci_attach(device_t dev) pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); - tr->regid = PCIR_MAPS; + tr->regid = PCIR_BAR(0); tr->regtype = SYS_RES_IOPORT; - tr->reg = bus_alloc_resource(dev, tr->regtype, &tr->regid, 0, ~0, 1, RF_ACTIVE); + tr->reg = bus_alloc_resource_any(dev, tr->regtype, &tr->regid, + RF_ACTIVE); if (tr->reg) { tr->st = rman_get_bustag(tr->reg); tr->sh = rman_get_bushandle(tr->reg); @@ -854,9 +856,9 @@ tr_pci_attach(device_t dev) 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 || snd_setup_intr(dev, tr->irq, INTR_MPSAFE, tr_intr, tr, &tr->ih, NULL)) { + tr->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &tr->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!tr->irq || snd_setup_intr(dev, tr->irq, 0, tr_intr, tr, &tr->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -866,13 +868,14 @@ tr_pci_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/tr->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &tr->parent_dmat) != 0) { + /*flags*/0, + &tr->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } - ksnprintf(status, 64, "at io 0x%lx irq %ld", - rman_get_start(tr->reg), rman_get_start(tr->irq)); + ksnprintf(status, 64, "at io 0x%lx irq %ld %s", + rman_get_start(tr->reg), rman_get_start(tr->irq),PCM_KLDSTRING(snd_t4dwave)); if (pcm_register(dev, tr, TR_MAXPLAYCH, 1)) goto bad; pcm_addchan(dev, PCMDIR_REC, &trrchan_class, tr); @@ -985,5 +988,5 @@ static driver_t tr_driver = { }; DRIVER_MODULE(snd_t4dwave, pci, tr_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_t4dwave, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_t4dwave, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_t4dwave, 1); diff --git a/sys/dev/sound/pci/t4dwave.h b/sys/dev/sound/pci/t4dwave.h index 4ceb257d8a..0a33f5af1b 100644 --- a/sys/dev/sound/pci/t4dwave.h +++ b/sys/dev/sound/pci/t4dwave.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/t4dwave.h,v 1.3.2.4 2002/04/22 15:49:33 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/t4dwave.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/t4dwave.h,v 1.7 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/t4dwave.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _T4DWAVE_REG_H diff --git a/sys/dev/sound/pci/via8233.c b/sys/dev/sound/pci/via8233.c index 3c405ebd43..0939f5c053 100644 --- a/sys/dev/sound/pci/via8233.c +++ b/sys/dev/sound/pci/via8233.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2002 Orion Hodson * Portions of this code derived from via82c686.c: * Copyright (c) 2000 David Jones @@ -25,11 +25,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/via8233.c,v 1.2.2.2 2003/02/06 17:35:56 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.7 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/via8233.c,v 1.20.2.2 2006/04/25 14:39:23 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ */ -/* Some Credits: +/* + * Credits due to: * * Grzybowski Rafal, Russell Davies, Mark Handley, Daniel O'Connor for * comments, machine time, testing patches, and patience. VIA for @@ -46,23 +47,31 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.7 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via8233.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); #define VIA8233_PCI_ID 0x30591106 +#define VIA8233_REV_ID_8233PRE 0x10 +#define VIA8233_REV_ID_8233C 0x20 +#define VIA8233_REV_ID_8233 0x30 +#define VIA8233_REV_ID_8233A 0x40 +#define VIA8233_REV_ID_8235 0x50 +#define VIA8233_REV_ID_8237 0x60 +#define VIA8233_REV_ID_8251 0x70 + #define SEGS_PER_CHAN 2 /* Segments per channel */ -#define NCHANS 2 /* Lines-in,out (mic later) */ +#define NDXSCHANS 4 /* No of DXS channels */ +#define NMSGDCHANS 1 /* No of multichannel SGD */ +#define NWRCHANS 1 /* No of write channels */ +#define NCHANS (NWRCHANS + NDXSCHANS + NMSGDCHANS) #define NSEGS NCHANS * SEGS_PER_CHAN /* Segments in SGD table */ #define VIA_DEFAULT_BUFSZ 0x1000 -#undef DEB -#define DEB(x) x - /* we rely on this struct being packed to 64 bits */ struct via_dma_op { - u_int32_t ptr; - u_int32_t flags; + volatile u_int32_t ptr; + volatile u_int32_t flags; #define VIA_DMAOP_EOL 0x80000000 #define VIA_DMAOP_FLAG 0x40000000 #define VIA_DMAOP_STOP 0x20000000 @@ -76,8 +85,9 @@ struct via_chinfo { struct pcm_channel *channel; struct snd_dbuf *buffer; struct via_dma_op *sgd_table; + bus_addr_t sgd_addr; int dir, blksz; - int rbase; /* base register for channel */ + int rbase; }; struct via_info { @@ -86,6 +96,7 @@ struct via_info { bus_dma_tag_t parent_dmat; bus_dma_tag_t sgd_dmat; bus_dmamap_t sgd_dmamap; + bus_addr_t sgd_addr; struct resource *reg, *irq; int regid, irqid; @@ -93,10 +104,14 @@ struct via_info { struct ac97_info *codec; unsigned int bufsz; + int dxs_src, dma_eol_wake; - struct via_chinfo pch, rch; + struct via_chinfo pch[NDXSCHANS + NMSGDCHANS]; + struct via_chinfo rch[NWRCHANS]; struct via_dma_op *sgd_table; u_int16_t codec_caps; + u_int16_t n_dxs_registered; + struct spinlock *lock; }; static u_int32_t via_fmt[] = { @@ -110,7 +125,90 @@ static u_int32_t via_fmt[] = { static struct pcmchan_caps via_vracaps = { 4000, 48000, via_fmt, 0 }; static struct pcmchan_caps via_caps = { 48000, 48000, via_fmt, 0 }; -static u_int32_t +#ifdef SND_DYNSYSCTL +static int +sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS) +{ + struct via_info *via; + device_t dev; + uint32_t r; + int err, new_en; + + dev = oidp->oid_arg1; + via = pcm_getdevinfo(dev); + snd_mtxlock(via->lock); + r = pci_read_config(dev, VIA_PCI_SPDIF, 1); + snd_mtxunlock(via->lock); + new_en = (r & VIA_SPDIF_EN) ? 1 : 0; + err = sysctl_handle_int(oidp, &new_en, sizeof(new_en), req); + + if (err || req->newptr == NULL) + return err; + if (new_en < 0 || new_en > 1) + return EINVAL; + + if (new_en) + r |= VIA_SPDIF_EN; + else + r &= ~VIA_SPDIF_EN; + snd_mtxlock(via->lock); + pci_write_config(dev, VIA_PCI_SPDIF, r, 1); + snd_mtxunlock(via->lock); + + return 0; +} + +#if 0 +static int +sysctl_via8233_dxs_src(SYSCTL_HANDLER_ARGS) +{ + struct via_info *via; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + via = pcm_getdevinfo(dev); + snd_mtxlock(via->lock); + val = via->dxs_src; + snd_mtxunlock(via->lock); + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + + if (err || req->newptr == NULL) + return err; + if (val < 0 || val > 1) + return EINVAL; + + snd_mtxlock(via->lock); + via->dxs_src = val; + snd_mtxunlock(via->lock); + + return 0; +} +#endif +#endif /* SND_DYNSYSCTL */ + +static void +via_init_sysctls(device_t dev) +{ +#ifdef SND_DYNSYSCTL + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "spdif_enabled", + CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_via8233_spdif_enable, "I", + "Enable S/PDIF output on primary playback channel"); +#if 0 + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), + SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "via_dxs_src", + CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_via8233_dxs_src, "I", + "Enable VIA DXS Sample Rate Converter"); +#endif +#endif +} + +static __inline u_int32_t via_rd(struct via_info *via, int regno, int size) { switch (size) { @@ -125,7 +223,7 @@ via_rd(struct via_info *via, int regno, int size) } } -static void +static __inline void via_wr(struct via_info *via, int regno, u_int32_t data, int size) { @@ -224,15 +322,8 @@ via_buildsgdt(struct via_chinfo *ch) u_int32_t phys_addr, flag; int i, seg_size; - /* - * 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. - */ seg_size = sndbuf_getsize(ch->buffer) / SEGS_PER_CHAN; - - phys_addr = vtophys(sndbuf_getbuf(ch->buffer)); + phys_addr = sndbuf_getbufaddr(ch->buffer); for (i = 0; i < SEGS_PER_CHAN; i++) { flag = (i == SEGS_PER_CHAN - 1) ? VIA_DMAOP_EOL : VIA_DMAOP_FLAG; @@ -243,8 +334,52 @@ via_buildsgdt(struct via_chinfo *ch) return 0; } +/* -------------------------------------------------------------------- */ +/* Format setting functions */ + static int -via8233pchan_setformat(kobj_t obj, void *data, u_int32_t format) +via8233wr_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct via_chinfo *ch = data; + struct via_info *via = ch->parent; + + u_int32_t f = WR_FORMAT_STOP_INDEX; + + if (format & AFMT_STEREO) + f |= WR_FORMAT_STEREO; + if (format & AFMT_S16_LE) + f |= WR_FORMAT_16BIT; + snd_mtxlock(via->lock); + via_wr(via, VIA_WR0_FORMAT, f, 4); + snd_mtxunlock(via->lock); + + return 0; +} + +static int +via8233dxs_setformat(kobj_t obj, void *data, u_int32_t format) +{ + struct via_chinfo *ch = data; + struct via_info *via = ch->parent; + u_int32_t r, v; + + r = ch->rbase + VIA8233_RP_DXS_RATEFMT; + snd_mtxlock(via->lock); + v = via_rd(via, r, 4); + + v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT); + if (format & AFMT_STEREO) + v |= VIA8233_DXS_RATEFMT_STEREO; + if (format & AFMT_16BIT) + v |= VIA8233_DXS_RATEFMT_16BIT; + via_wr(via, r, v, 4); + snd_mtxunlock(via->lock); + + return 0; +} + +static int +via8233msgd_setformat(kobj_t obj, void *data, u_int32_t format) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; @@ -260,30 +395,51 @@ via8233pchan_setformat(kobj_t obj, void *data, u_int32_t format) s |= SLOT3(1) | SLOT4(1); } + snd_mtxlock(via->lock); via_wr(via, VIA_MC_SLOT_SELECT, s, 4); via_wr(via, VIA_MC_SGD_FORMAT, v, 1); + snd_mtxunlock(via->lock); return 0; } +/* -------------------------------------------------------------------- */ +/* Speed setting functions */ + static int -via8233rchan_setformat(kobj_t obj, void *data, u_int32_t format) +via8233wr_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; - - u_int32_t f = WR_FORMAT_STOP_INDEX; - if (format & AFMT_STEREO) - f |= WR_FORMAT_STEREO; - if (format & AFMT_S16_LE) - f |= WR_FORMAT_16BIT; - via_wr(via, VIA_WR0_FORMAT, f, 4); - return 0; + if (via->codec_caps & AC97_EXTCAP_VRA) + return ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed); + + return 48000; +} + +static int +via8233dxs_setspeed(kobj_t obj, void *data, u_int32_t speed) +{ + struct via_chinfo *ch = data; + struct via_info *via = ch->parent; + u_int32_t r, v; + + r = ch->rbase + VIA8233_RP_DXS_RATEFMT; + snd_mtxlock(via->lock); + v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K; + + /* Careful to avoid overflow (divide by 48 per vt8233c docs) */ + + v |= VIA8233_DXS_RATEFMT_48K * (speed / 48) / (48000 / 48); + via_wr(via, r, v, 4); + snd_mtxunlock(via->lock); + + return speed; } static int -via8233pchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +via8233msgd_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; @@ -294,19 +450,53 @@ via8233pchan_setspeed(kobj_t obj, void *data, u_int32_t speed) return 48000; } -static int -via8233rchan_setspeed(kobj_t obj, void *data, u_int32_t speed) +/* -------------------------------------------------------------------- */ +/* Format probing functions */ + +static struct pcmchan_caps * +via8233wr_getcaps(kobj_t obj, void *data) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; - u_int32_t spd = 48000; - if (via->codec_caps & AC97_EXTCAP_VRA) { - spd = ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed); - } - return spd; + /* Controlled by ac97 registers */ + if (via->codec_caps & AC97_EXTCAP_VRA) + return &via_vracaps; + return &via_caps; } +static struct pcmchan_caps * +via8233dxs_getcaps(kobj_t obj, void *data) +{ + struct via_chinfo *ch = data; + struct via_info *via = ch->parent; + + /* + * Controlled by onboard registers + * + * Apparently, few boards can do DXS sample rate + * conversion. + */ + if (via->dxs_src) + return &via_vracaps; + return &via_caps; +} + +static struct pcmchan_caps * +via8233msgd_getcaps(kobj_t obj, void *data) +{ + struct via_chinfo *ch = data; + struct via_info *via = ch->parent; + + /* Controlled by ac97 registers */ + if (via->codec_caps & AC97_EXTCAP_VRA) + return &via_vracaps; + return &via_caps; +} + +/* -------------------------------------------------------------------- */ +/* Common functions */ + static int via8233chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { @@ -322,26 +512,20 @@ via8233chan_getptr(kobj_t obj, void *data) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; - - u_int32_t v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4); - u_int32_t index = v >> 24; /* Last completed buffer */ - u_int32_t count = v & 0x00ffffff; /* Bytes remaining */ - int ptr = (index + 1) * ch->blksz - count; + u_int32_t v, index, count; + int ptr; + + snd_mtxlock(via->lock); + v = via_rd(via, ch->rbase + VIA_RP_CURRENT_COUNT, 4); + snd_mtxunlock(via->lock); + index = v >> 24; /* Last completed buffer */ + count = v & 0x00ffffff; /* Bytes remaining */ + ptr = (index + 1) * ch->blksz - count; ptr %= SEGS_PER_CHAN * ch->blksz; /* Wrap to available space */ return ptr; } -static struct pcmchan_caps * -via8233chan_getcaps(kobj_t obj, void *data) -{ - struct via_chinfo *ch = data; - struct via_info *via = ch->parent; - if (via->codec_caps & AC97_EXTCAP_VRA) - return &via_vracaps; - return &via_caps; -} - static void via8233chan_reset(struct via_info *via, struct via_chinfo *ch) { @@ -351,45 +535,129 @@ via8233chan_reset(struct via_info *via, struct via_chinfo *ch) SGD_STATUS_EOL | SGD_STATUS_FLAG, 1); } +/* -------------------------------------------------------------------- */ +/* Channel initialization functions */ + +static void +via8233chan_sgdinit(struct via_info *via, struct via_chinfo *ch, int chnum) +{ + ch->sgd_table = &via->sgd_table[chnum * SEGS_PER_CHAN]; + ch->sgd_addr = via->sgd_addr + chnum * SEGS_PER_CHAN * sizeof(struct via_dma_op); +} + static void* -via8233chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, - struct pcm_channel *c, int dir) +via8233wr_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) { struct via_info *via = devinfo; - struct via_chinfo *ch = (dir == PCMDIR_PLAY)? &via->pch : &via->rch; + struct via_chinfo *ch = &via->rch[c->num]; ch->parent = via; ch->channel = c; ch->buffer = b; ch->dir = dir; - ch->sgd_table = &via->sgd_table[(dir == PCMDIR_PLAY)? 0 : SEGS_PER_CHAN]; - if (ch->dir == PCMDIR_PLAY) { - ch->rbase = VIA_MC_SGD_STATUS; - } else { - ch->rbase = VIA_WR0_SGD_STATUS; - via_wr(via, VIA_WR0_SGD_FORMAT, WR_FIFO_ENABLE, 1); - } + ch->rbase = VIA_WR_BASE(c->num); + snd_mtxlock(via->lock); + via_wr(via, ch->rbase + VIA_WR_RP_SGD_FORMAT, WR_FIFO_ENABLE, 1); + snd_mtxunlock(via->lock); + + if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0) + return NULL; + + snd_mtxlock(via->lock); + via8233chan_sgdinit(via, ch, c->num); + via8233chan_reset(via, ch); + snd_mtxunlock(via->lock); + + return ch; +} + +static void* +via8233dxs_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct via_info *via = devinfo; + struct via_chinfo *ch = &via->pch[c->num]; + + ch->parent = via; + ch->channel = c; + ch->buffer = b; + ch->dir = dir; + + /* + * All cards apparently support DXS3, but not other DXS + * channels. We therefore want to align first DXS channel to + * DXS3. + */ + snd_mtxlock(via->lock); + ch->rbase = VIA_DXS_BASE(NDXSCHANS - 1 - via->n_dxs_registered); + via->n_dxs_registered++; + snd_mtxunlock(via->lock); + + if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0) + return NULL; + + snd_mtxlock(via->lock); + via8233chan_sgdinit(via, ch, NWRCHANS + c->num); + via8233chan_reset(via, ch); + snd_mtxunlock(via->lock); + + return ch; +} - if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) == -1) +static void* +via8233msgd_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct via_info *via = devinfo; + struct via_chinfo *ch = &via->pch[c->num]; + + ch->parent = via; + ch->channel = c; + ch->buffer = b; + ch->dir = dir; + ch->rbase = VIA_MC_SGD_STATUS; + + if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0) return NULL; + snd_mtxlock(via->lock); + via8233chan_sgdinit(via, ch, NWRCHANS + c->num); via8233chan_reset(via, ch); + snd_mtxunlock(via->lock); return ch; } +static void +via8233chan_mute(struct via_info *via, struct via_chinfo *ch, int muted) +{ + if (BASE_IS_VIA_DXS_REG(ch->rbase)) { + int r; + muted = (muted) ? VIA8233_DXS_MUTE : 0; + via_wr(via, ch->rbase + VIA8233_RP_DXS_LVOL, muted, 1); + via_wr(via, ch->rbase + VIA8233_RP_DXS_RVOL, muted, 1); + r = via_rd(via, ch->rbase + VIA8233_RP_DXS_LVOL, 1) & VIA8233_DXS_MUTE; + if (r != muted) { + kprintf("via: failed to set dxs volume " + "(dxs base 0x%02x).\n", ch->rbase); + } + } +} + static int via8233chan_trigger(kobj_t obj, void* data, int go) { struct via_chinfo *ch = data; struct via_info *via = ch->parent; - struct via_dma_op *ado = ch->sgd_table; + snd_mtxlock(via->lock); switch(go) { case PCMTRIG_START: via_buildsgdt(ch); - via_wr(via, ch->rbase + VIA_RP_TABLE_PTR, vtophys(ado), 4); + via8233chan_mute(via, ch, 0); + via_wr(via, ch->rbase + VIA_RP_TABLE_PTR, ch->sgd_addr, 4); via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_START | SGD_CONTROL_AUTOSTART | SGD_CONTROL_I_EOL | SGD_CONTROL_I_FLAG, 1); @@ -397,35 +665,49 @@ via8233chan_trigger(kobj_t obj, void* data, int go) case PCMTRIG_STOP: case PCMTRIG_ABORT: via_wr(via, ch->rbase + VIA_RP_CONTROL, SGD_CONTROL_STOP, 1); + via8233chan_mute(via, ch, 1); via8233chan_reset(via, ch); break; } + snd_mtxunlock(via->lock); return 0; } -static kobj_method_t via8233pchan_methods[] = { - KOBJMETHOD(channel_init, via8233chan_init), - KOBJMETHOD(channel_setformat, via8233pchan_setformat), - KOBJMETHOD(channel_setspeed, via8233pchan_setspeed), +static kobj_method_t via8233wr_methods[] = { + KOBJMETHOD(channel_init, via8233wr_init), + KOBJMETHOD(channel_setformat, via8233wr_setformat), + KOBJMETHOD(channel_setspeed, via8233wr_setspeed), + KOBJMETHOD(channel_getcaps, via8233wr_getcaps), + KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize), + KOBJMETHOD(channel_trigger, via8233chan_trigger), + KOBJMETHOD(channel_getptr, via8233chan_getptr), + { 0, 0 } +}; +CHANNEL_DECLARE(via8233wr); + +static kobj_method_t via8233dxs_methods[] = { + KOBJMETHOD(channel_init, via8233dxs_init), + KOBJMETHOD(channel_setformat, via8233dxs_setformat), + KOBJMETHOD(channel_setspeed, via8233dxs_setspeed), + KOBJMETHOD(channel_getcaps, via8233dxs_getcaps), KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize), KOBJMETHOD(channel_trigger, via8233chan_trigger), KOBJMETHOD(channel_getptr, via8233chan_getptr), - KOBJMETHOD(channel_getcaps, via8233chan_getcaps), { 0, 0 } }; -CHANNEL_DECLARE(via8233pchan); +CHANNEL_DECLARE(via8233dxs); -static kobj_method_t via8233rchan_methods[] = { - KOBJMETHOD(channel_init, via8233chan_init), - KOBJMETHOD(channel_setformat, via8233rchan_setformat), - KOBJMETHOD(channel_setspeed, via8233rchan_setspeed), +static kobj_method_t via8233msgd_methods[] = { + KOBJMETHOD(channel_init, via8233msgd_init), + KOBJMETHOD(channel_setformat, via8233msgd_setformat), + KOBJMETHOD(channel_setspeed, via8233msgd_setspeed), + KOBJMETHOD(channel_getcaps, via8233msgd_getcaps), KOBJMETHOD(channel_setblocksize, via8233chan_setblocksize), KOBJMETHOD(channel_trigger, via8233chan_trigger), KOBJMETHOD(channel_getptr, via8233chan_getptr), - KOBJMETHOD(channel_getcaps, via8233chan_getcaps), { 0, 0 } }; -CHANNEL_DECLARE(via8233rchan); +CHANNEL_DECLARE(via8233msgd); /* -------------------------------------------------------------------- */ @@ -433,17 +715,55 @@ static void via_intr(void *p) { struct via_info *via = p; - int r = via_rd(via, VIA_MC_SGD_STATUS, 1); - if (r & SGD_STATUS_INTR) { - via_wr(via, VIA_MC_SGD_STATUS, SGD_STATUS_INTR, 1); - chn_intr(via->pch.channel); + int i, reg, stat; + + /* Poll playback channels */ + snd_mtxlock(via->lock); + for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) { + if (via->pch[i].channel == NULL) + continue; + reg = via->pch[i].rbase + VIA_RP_STATUS; + stat = via_rd(via, reg, 1); + if (stat & SGD_STATUS_INTR) { + if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) || + !(stat & SGD_STATUS_ACTIVE))) { + via_wr(via, + via->pch[i].rbase + VIA_RP_CONTROL, + SGD_CONTROL_START | + SGD_CONTROL_AUTOSTART | + SGD_CONTROL_I_EOL | + SGD_CONTROL_I_FLAG, 1); + } + via_wr(via, reg, stat, 1); + snd_mtxunlock(via->lock); + chn_intr(via->pch[i].channel); + snd_mtxlock(via->lock); + } } - r = via_rd(via, VIA_WR0_SGD_STATUS, 1); - if (r & SGD_STATUS_INTR) { - via_wr(via, VIA_WR0_SGD_STATUS, SGD_STATUS_INTR, 1); - chn_intr(via->rch.channel); + /* Poll record channels */ + for (i = 0; i < NWRCHANS; i++) { + if (via->rch[i].channel == NULL) + continue; + reg = via->rch[i].rbase + VIA_RP_STATUS; + stat = via_rd(via, reg, 1); + if (stat & SGD_STATUS_INTR) { + if (via->dma_eol_wake && ((stat & SGD_STATUS_EOL) || + !(stat & SGD_STATUS_ACTIVE))) { + via_wr(via, + via->rch[i].rbase + VIA_RP_CONTROL, + SGD_CONTROL_START | + SGD_CONTROL_AUTOSTART | + SGD_CONTROL_I_EOL | + SGD_CONTROL_I_FLAG, 1); + } + via_wr(via, reg, stat, 1); + snd_mtxunlock(via->lock); + chn_intr(via->rch[i].channel); + snd_mtxlock(via->lock); + } } + snd_mtxunlock(via->lock); } /* @@ -455,24 +775,30 @@ via_probe(device_t dev) switch(pci_get_devid(dev)) { case VIA8233_PCI_ID: switch(pci_get_revid(dev)) { - case 0x10: + case VIA8233_REV_ID_8233PRE: device_set_desc(dev, "VIA VT8233 (pre)"); - return 0; - case 0x20: + return BUS_PROBE_DEFAULT; + case VIA8233_REV_ID_8233C: device_set_desc(dev, "VIA VT8233C"); - return 0; - case 0x30: + return BUS_PROBE_DEFAULT; + case VIA8233_REV_ID_8233: device_set_desc(dev, "VIA VT8233"); - return 0; - case 0x40: + return BUS_PROBE_DEFAULT; + case VIA8233_REV_ID_8233A: device_set_desc(dev, "VIA VT8233A"); - return 0; - case 0x50: + return BUS_PROBE_DEFAULT; + case VIA8233_REV_ID_8235: device_set_desc(dev, "VIA VT8235"); - return 0; + return BUS_PROBE_DEFAULT; + case VIA8233_REV_ID_8237: + device_set_desc(dev, "VIA VT8237"); + return BUS_PROBE_DEFAULT; + case VIA8233_REV_ID_8251: + device_set_desc(dev, "VIA VT8251"); + return BUS_PROBE_DEFAULT; default: device_set_desc(dev, "VIA VT8233X"); /* Unknown */ - return 0; + return BUS_PROBE_DEFAULT; } } return ENXIO; @@ -481,38 +807,63 @@ via_probe(device_t dev) static void dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) { + struct via_info *via = (struct via_info *)p; + via->sgd_addr = bds->ds_addr; } static int via_chip_init(device_t dev) { - int i, s; - - pci_write_config(dev, VIA_PCI_ACLINK_CTRL, 0, 1); - DELAY(100); - - /* assert ACLink reset */ - pci_write_config(dev, VIA_PCI_ACLINK_CTRL, VIA_PCI_ACLINK_NRST, 1); - DELAY(2); - - /* deassert ACLink reset, force SYNC (warm AC'97 reset) */ - pci_write_config(dev, VIA_PCI_ACLINK_CTRL, - VIA_PCI_ACLINK_NRST | VIA_PCI_ACLINK_SYNC, 1); + u_int32_t data, cnt; + + /* Wake up and reset AC97 if necessary */ + data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1); + + if ((data & VIA_PCI_ACLINK_C00_READY) == 0) { + /* Cold reset per ac97r2.3 spec (page 95) */ + /* Assert low */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, + VIA_PCI_ACLINK_EN, 1); + /* Wait T_rst_low */ + DELAY(100); + /* Assert high */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, + VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_NRST, 1); + /* Wait T_rst2clk */ + DELAY(5); + /* Assert low */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, + VIA_PCI_ACLINK_EN, 1); + } else { + /* Warm reset */ + /* Force no sync */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, + VIA_PCI_ACLINK_EN, 1); + DELAY(100); + /* Sync */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, + VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_SYNC, 1); + /* Wait T_sync_high */ + DELAY(5); + /* Force no sync */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, + VIA_PCI_ACLINK_EN, 1); + /* Wait T_sync2clk */ + DELAY(5); + } - /* ACLink on, deassert ACLink reset, VSR, SGD data out */ - pci_write_config(dev, VIA_PCI_ACLINK_CTRL, - VIA_PCI_ACLINK_EN | VIA_PCI_ACLINK_NRST - | VIA_PCI_ACLINK_VRATE | VIA_PCI_ACLINK_SGD, 1); + /* Power everything up */ + pci_write_config(dev, VIA_PCI_ACLINK_CTRL, VIA_PCI_ACLINK_DESIRED, 1); - for (i = 0; i < 100; i++) { - s = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1); - if (s & VIA_PCI_ACLINK_C00_READY) { - s = pci_read_config(dev, VIA_PCI_ACLINK_CTRL, 1); + /* Wait for codec to become ready (largest reported delay 310ms) */ + for (cnt = 0; cnt < 2000; cnt++) { + data = pci_read_config(dev, VIA_PCI_ACLINK_STAT, 1); + if (data & VIA_PCI_ACLINK_C00_READY) { return 0; } - DELAY(10); + DELAY(5000); } - device_printf(dev, "primary codec not ready (s = 0x%02x)\n", s); + device_printf(dev, "primary codec not ready (cnt = 0x%02x)\n", cnt); return ENXIO; } @@ -521,19 +872,21 @@ via_attach(device_t dev) { struct via_info *via = 0; char status[SND_STATUSLEN]; + int i, via_dxs_disabled, via_dxs_src, via_dxs_chnum, via_sgd_chnum; + uint32_t revid; if ((via = kmalloc(sizeof *via, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { device_printf(dev, "cannot allocate softc\n"); return ENXIO; } + via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); - pci_enable_io(dev, SYS_RES_IOPORT); pci_set_powerstate(dev, PCI_POWERSTATE_D0); pci_enable_busmaster(dev); - - via->regid = PCIR_MAPS; - via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, 0, ~0, - 1, RF_ACTIVE); + + via->regid = PCIR_BAR(0); + via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &via->regid, + RF_ACTIVE); if (!via->reg) { device_printf(dev, "cannot allocate bus resource."); goto bad; @@ -544,10 +897,10 @@ via_attach(device_t dev) via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536); via->irqid = 0; - via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, 0, ~0, 1, - RF_ACTIVE | RF_SHAREABLE); + via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid, + RF_ACTIVE | RF_SHAREABLE); if (!via->irq || - snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih, NULL)) { + snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -558,7 +911,8 @@ via_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &via->parent_dmat) != 0) { + /*flags*/0, + &via->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -574,7 +928,8 @@ via_attach(device_t dev) /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/NSEGS * sizeof(struct via_dma_op), /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &via->sgd_dmat) != 0) { + /*flags*/0, + &via->sgd_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -583,7 +938,7 @@ via_attach(device_t dev) BUS_DMA_NOWAIT, &via->sgd_dmamap) == -1) goto bad; if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table, - NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) + NSEGS * sizeof(struct via_dma_op), dma_cb, via, 0)) goto bad; if (via_chip_init(dev)) @@ -607,14 +962,85 @@ via_attach(device_t dev) ac97_setextmode(via->codec, ext); } - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", - rman_get_start(via->reg), rman_get_start(via->irq)); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + rman_get_start(via->reg), rman_get_start(via->irq),PCM_KLDSTRING(snd_via8233)); - /* Register */ - if (pcm_register(dev, via, 1, 1)) goto bad; + revid = pci_get_revid(dev); + + /* + * VIA8251 lost its interrupt after DMA EOL, and need + * a gentle spank on its face within interrupt handler. + */ + if (revid == VIA8233_REV_ID_8251) + via->dma_eol_wake = 1; + else + via->dma_eol_wake = 0; - pcm_addchan(dev, PCMDIR_PLAY, &via8233pchan_class, via); - pcm_addchan(dev, PCMDIR_REC, &via8233rchan_class, via); + /* + * Decide whether DXS had to be disabled or not + */ + if (revid == VIA8233_REV_ID_8233A) { + /* + * DXS channel is disabled. Reports from multiple users + * that it plays at half-speed. Do not see this behaviour + * on available 8233C or when emulating 8233A register set + * on 8233C (either with or without ac97 VRA). + */ + via_dxs_disabled = 1; + } else if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "via_dxs_disabled", + &via_dxs_disabled) == 0) + via_dxs_disabled = (via_dxs_disabled > 0) ? 1 : 0; + else + via_dxs_disabled = 0; + + if (via_dxs_disabled) { + via_dxs_chnum = 0; + via_sgd_chnum = 1; + } else { + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "via_dxs_channels", + &via_dxs_chnum) != 0) + via_dxs_chnum = NDXSCHANS; + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "via_sgd_channels", + &via_sgd_chnum) != 0) + via_sgd_chnum = NMSGDCHANS; + } + if (via_dxs_chnum > NDXSCHANS) + via_dxs_chnum = NDXSCHANS; + else if (via_dxs_chnum < 0) + via_dxs_chnum = 0; + if (via_sgd_chnum > NMSGDCHANS) + via_sgd_chnum = NMSGDCHANS; + else if (via_sgd_chnum < 0) + via_sgd_chnum = 0; + if (via_dxs_chnum + via_sgd_chnum < 1) { + /* Minimalist ? */ + via_dxs_chnum = 1; + via_sgd_chnum = 0; + } + if (via_dxs_chnum > 0 && resource_int_value(device_get_name(dev), + device_get_unit(dev), "via_dxs_src", + &via_dxs_src) == 0) + via->dxs_src = (via_dxs_src > 0) ? 1 : 0; + else + via->dxs_src = 0; + /* Register */ + if (pcm_register(dev, via, via_dxs_chnum + via_sgd_chnum, NWRCHANS)) + goto bad; + for (i = 0; i < via_dxs_chnum; i++) + pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via); + for (i = 0; i < via_sgd_chnum; i++) + pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via); + for (i = 0; i < NWRCHANS; i++) + pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via); + if (via_dxs_chnum > 0) + via_init_sysctls(dev); + device_printf(dev, "\n", + (via_dxs_chnum > 0) ? "En" : "Dis", + (via->dxs_src) ? "(SRC)" : "", + via_dxs_chnum, via_sgd_chnum, NWRCHANS); pcm_setstatus(dev, status); @@ -627,6 +1053,7 @@ bad: if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); + if (via->lock) snd_mtxfree(via->lock); if (via) kfree(via, M_DEVBUF); return ENXIO; } @@ -647,6 +1074,7 @@ via_detach(device_t dev) bus_dma_tag_destroy(via->parent_dmat); bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); bus_dma_tag_destroy(via->sgd_dmat); + snd_mtxfree(via->lock); kfree(via, M_DEVBUF); return 0; } @@ -666,5 +1094,5 @@ static driver_t via_driver = { }; DRIVER_MODULE(snd_via8233, pci, via_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_via8233, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_via8233, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_via8233, 1); diff --git a/sys/dev/sound/pci/via8233.h b/sys/dev/sound/pci/via8233.h index c2428b1e1f..1a16368182 100644 --- a/sys/dev/sound/pci/via8233.h +++ b/sys/dev/sound/pci/via8233.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2002 Orion Hodson * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/via8233.h,v 1.1.2.1 2002/08/22 17:32:48 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/via8233.h,v 1.2 2003/06/17 04:28:30 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/via8233.h,v 1.4 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/via8233.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _SYS_SOUND_PCI_VIA8233_H_ @@ -35,11 +35,30 @@ * * Documentation sources: * - * V8233C specs. from VIA, gratefully received under NDA. - * AC97 R2.2 specs. - * ALSA driver (very useful comments) + * o V8233C specs. from VIA, gratefully received under NDA. + * o AC97 R2.2 specs. + * o ALSA driver (very useful comments) */ +#define VIA_PCI_SPDIF 0x49 +#define VIA_SPDIF_EN 0x08 + +#define VIA_DXS0_BASE 0x00 +#define VIA_DXS1_BASE 0x10 +#define VIA_DXS2_BASE 0x20 +#define VIA_DXS3_BASE 0x30 +#define VIA_DXS_BASE(n) (0x10 * (n)) +#define BASE_IS_VIA_DXS_REG(x) ((x) <= VIA_DXS3_BASE) + +#define VIA8233_RP_DXS_LVOL 0x02 +#define VIA8233_RP_DXS_RVOL 0x03 +#define VIA8233_DXS_MUTE 0x3f +#define VIA8233_RP_DXS_RATEFMT 0x08 +#define VIA8233_DXS_STOP_INDEX 0xff000000 +#define VIA8233_DXS_RATEFMT_48K 0x000fffff +#define VIA8233_DXS_RATEFMT_STEREO 0x00100000 +#define VIA8233_DXS_RATEFMT_16BIT 0x00200000 + #define VIA_PCI_ACLINK_STAT 0x40 # define VIA_PCI_ACLINK_C11_READY 0x20 # define VIA_PCI_ACLINK_C10_READY 0x10 @@ -54,6 +73,10 @@ # define VIA_PCI_ACLINK_SERIAL 0x10 # define VIA_PCI_ACLINK_VRATE 0x08 # define VIA_PCI_ACLINK_SGD 0x04 +# define VIA_PCI_ACLINK_DESIRED (VIA_PCI_ACLINK_EN | \ + VIA_PCI_ACLINK_NRST | \ + VIA_PCI_ACLINK_VRATE | \ + VIA_PCI_ACLINK_SGD) #define VIA_MC_SGD_STATUS 0x40 #define VIA_WR0_SGD_STATUS 0x60 @@ -66,6 +89,8 @@ # define SGD_STATUS_FLAG 0x01 # define SGD_STATUS_INTR (SGD_STATUS_EOL | SGD_STATUS_FLAG) +#define VIA_WR_BASE(n) (0x60 + (n) * 0x10) + #define VIA_MC_SGD_CONTROL 0x41 #define VIA_WR0_SGD_CONTROL 0x61 #define VIA_WR1_SGD_CONTROL 0x71 @@ -84,6 +109,7 @@ #define VIA_WR0_SGD_FORMAT 0x62 #define VIA_WR1_SGD_FORMAT 0x72 +#define VIA_WR_RP_SGD_FORMAT 0x02 # define WR_FIFO_ENABLE 0x40 #define VIA_WR0_SGD_INPUT 0x63 @@ -134,7 +160,6 @@ # define VIA_AC97_INDEX(x) ((x) << 16) # define VIA_AC97_DATA(x) ((x) & 0xffff) - #define VIA_CODEC_BUSY 0x01000000 #define VIA_CODEC_PRIVALID 0x02000000 #define VIA_CODEC_INDEX(x) ((x)<<16) diff --git a/sys/dev/sound/pci/via82c686.c b/sys/dev/sound/pci/via82c686.c index 3665f1db75..f24ae28ea2 100644 --- a/sys/dev/sound/pci/via82c686.c +++ b/sys/dev/sound/pci/via82c686.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2000 David Jones * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/via82c686.c,v 1.4.2.10 2003/05/11 01:45:53 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.7 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/via82c686.c,v 1.34.2.1 2005/12/30 19:55:53 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.8 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -36,7 +36,7 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.7 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/via82c686.c,v 1.8 2007/01/04 21:47:02 corecode Exp $"); #define VIA_PCI_ID 0x30581106 #define NSEGS 4 /* Number of segments in SGD table */ @@ -66,6 +66,7 @@ struct via_chinfo { struct pcm_channel *channel; struct snd_dbuf *buffer; struct via_dma_op *sgd_table; + bus_addr_t sgd_addr; int dir, blksz; int base, count, mode, ctrl; }; @@ -76,6 +77,7 @@ struct via_info { bus_dma_tag_t parent_dmat; bus_dma_tag_t sgd_dmat; bus_dmamap_t sgd_dmamap; + bus_addr_t sgd_addr; struct resource *reg, *irq; int regid, irqid; @@ -87,6 +89,7 @@ struct via_info { struct via_chinfo pch, rch; struct via_dma_op *sgd_table; u_int16_t codec_caps; + struct spinlock *lock; }; static u_int32_t via_fmt[] = { @@ -99,7 +102,7 @@ static u_int32_t via_fmt[] = { static struct pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0}; static struct pcmchan_caps via_caps = {48000, 48000, via_fmt, 0}; -static u_int32_t +static __inline u_int32_t via_rd(struct via_info *via, int regno, int size) { @@ -116,7 +119,7 @@ via_rd(struct via_info *via, int regno, int size) } -static void +static __inline void via_wr(struct via_info *via, int regno, u_int32_t data, int size) { @@ -227,7 +230,7 @@ via_buildsgdt(struct via_chinfo *ch) */ seg_size = ch->blksz; segs = sndbuf_getsize(ch->buffer) / seg_size; - phys_addr = vtophys(sndbuf_getbuf(ch->buffer)); + phys_addr = sndbuf_getbufaddr(ch->buffer); for (i = 0; i < segs; i++) { flag = (i == segs - 1)? VIA_DMAOP_EOL : VIA_DMAOP_FLAG; @@ -243,27 +246,36 @@ static void * viachan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { struct via_info *via = devinfo; - struct via_chinfo *ch = (dir == PCMDIR_PLAY)? &via->pch : &via->rch; + struct via_chinfo *ch; - ch->parent = via; - ch->channel = c; - ch->buffer = b; - ch->dir = dir; - ch->sgd_table = &via->sgd_table[(dir == PCMDIR_PLAY)? 0 : SEGS_PER_CHAN]; - if (ch->dir == PCMDIR_PLAY) { + snd_mtxlock(via->lock); + if (dir == PCMDIR_PLAY) { + ch = &via->pch; ch->base = VIA_PLAY_DMAOPS_BASE; ch->count = VIA_PLAY_DMAOPS_COUNT; ch->ctrl = VIA_PLAY_CONTROL; ch->mode = VIA_PLAY_MODE; + ch->sgd_addr = via->sgd_addr; + ch->sgd_table = &via->sgd_table[0]; } else { + ch = &via->rch; ch->base = VIA_RECORD_DMAOPS_BASE; ch->count = VIA_RECORD_DMAOPS_COUNT; ch->ctrl = VIA_RECORD_CONTROL; ch->mode = VIA_RECORD_MODE; + ch->sgd_addr = via->sgd_addr + sizeof(struct via_dma_op) * SEGS_PER_CHAN; + ch->sgd_table = &via->sgd_table[SEGS_PER_CHAN]; } - if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) == -1) + ch->parent = via; + ch->channel = c; + ch->buffer = b; + ch->dir = dir; + snd_mtxunlock(via->lock); + + if (sndbuf_alloc(ch->buffer, via->parent_dmat, via->bufsz) != 0) return NULL; + return ch; } @@ -281,10 +293,12 @@ viachan_setformat(kobj_t obj, void *data, u_int32_t format) mode_set |= VIA_RPMODE_16BIT; DEB(kprintf("set format: dir = %d, format=%x\n", ch->dir, format)); + snd_mtxlock(via->lock); mode = via_rd(via, ch->mode, 1); mode &= ~(VIA_RPMODE_16BIT | VIA_RPMODE_STEREO); mode |= mode_set; via_wr(via, ch->mode, mode, 1); + snd_mtxunlock(via->lock); return 0; } @@ -329,19 +343,22 @@ 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; + bus_addr_t sgd_addr = ch->sgd_addr; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; ado = ch->sgd_table; - DEB(kprintf("ado located at va=%p pa=%x\n", ado, vtophys(ado))); + DEB(kprintf("ado located at va=%p pa=%x\n", ado, sgd_addr)); + snd_mtxlock(via->lock); if (go == PCMTRIG_START) { via_buildsgdt(ch); - via_wr(via, ch->base, vtophys(ado), 4); + via_wr(via, ch->base, sgd_addr, 4); via_wr(via, ch->ctrl, VIA_RPCTRL_START, 1); } else via_wr(via, ch->ctrl, VIA_RPCTRL_TERMINATE, 1); + snd_mtxunlock(via->lock); DEB(kprintf("viachan_trigger: go=%d\n", go)); return 0; @@ -353,21 +370,24 @@ viachan_getptr(kobj_t obj, void *data) struct via_chinfo *ch = data; struct via_info *via = ch->parent; struct via_dma_op *ado; + bus_addr_t sgd_addr = ch->sgd_addr; int ptr, base, base1, len, seg; ado = ch->sgd_table; + snd_mtxlock(via->lock); base1 = via_rd(via, ch->base, 4); len = via_rd(via, ch->count, 4); base = via_rd(via, ch->base, 4); if (base != base1) /* Avoid race hazard */ len = via_rd(via, ch->count, 4); + snd_mtxunlock(via->lock); DEB(kprintf("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); + seg = (base - sgd_addr) / sizeof(struct via_dma_op); if (seg == 0) seg = SEGS_PER_CHAN; @@ -410,22 +430,25 @@ static void via_intr(void *p) { struct via_info *via = p; - int st; /* DEB(kprintf("viachan_intr\n")); */ /* Read channel */ - st = via_rd(via, VIA_PLAY_STAT, 1); - if (st & VIA_RPSTAT_INTR) { + snd_mtxlock(via->lock); + if (via_rd(via, VIA_PLAY_STAT, 1) & VIA_RPSTAT_INTR) { via_wr(via, VIA_PLAY_STAT, VIA_RPSTAT_INTR, 1); + snd_mtxunlock(via->lock); chn_intr(via->pch.channel); + snd_mtxlock(via->lock); } /* Write channel */ - st = via_rd(via, VIA_RECORD_STAT, 1); - if (st & VIA_RPSTAT_INTR) { + if (via_rd(via, VIA_RECORD_STAT, 1) & VIA_RPSTAT_INTR) { via_wr(via, VIA_RECORD_STAT, VIA_RPSTAT_INTR, 1); + snd_mtxunlock(via->lock); chn_intr(via->rch.channel); + return; } + snd_mtxunlock(via->lock); } /* @@ -435,8 +458,8 @@ static int via_probe(device_t dev) { if (pci_get_devid(dev) == VIA_PCI_ID) { - device_set_desc(dev, "VIA VT82C686A"); - return 0; + device_set_desc(dev, "VIA VT82C686A"); + return BUS_PROBE_DEFAULT; } return ENXIO; } @@ -445,6 +468,8 @@ via_probe(device_t dev) static void dma_cb(void *p, bus_dma_segment_t *bds, int a, int b) { + struct via_info *via = (struct via_info *)p; + via->sgd_addr = bds->ds_addr; } @@ -459,6 +484,7 @@ via_attach(device_t dev) device_printf(dev, "cannot allocate softc\n"); return ENXIO; } + via->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); /* Get resources */ data = pci_read_config(dev, PCIR_COMMAND, 2); @@ -497,8 +523,9 @@ via_attach(device_t dev) DELAY(5000); } - via->regid = PCIR_MAPS; - via->reg = bus_alloc_resource(dev, SYS_RES_IOPORT, &via->regid, 0, ~0, 1, RF_ACTIVE); + via->regid = PCIR_BAR(0); + via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, + &via->regid, RF_ACTIVE); if (!via->reg) { device_printf(dev, "cannot allocate bus resource."); goto bad; @@ -509,8 +536,9 @@ via_attach(device_t dev) via->bufsz = pcm_getbuffersize(dev, 4096, VIA_DEFAULT_BUFSZ, 65536); via->irqid = 0; - via->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &via->irqid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); - if (!via->irq || snd_setup_intr(dev, via->irq, 0, via_intr, via, &via->ih, NULL)) { + via->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &via->irqid, + RF_ACTIVE | RF_SHAREABLE); + if (!via->irq || snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -535,7 +563,8 @@ via_attach(device_t dev) /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &via->parent_dmat) != 0) { + /*flags*/0, + &via->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -551,17 +580,22 @@ via_attach(device_t dev) /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/NSEGS * sizeof(struct via_dma_op), /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, &via->sgd_dmat) != 0) { + /*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, &via->sgd_dmamap) == -1) + if (bus_dmamem_alloc(via->sgd_dmat, (void **)&via->sgd_table, + BUS_DMA_NOWAIT, &via->sgd_dmamap) != 0) goto bad; - if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table, NSEGS * sizeof(struct via_dma_op), dma_cb, 0, 0)) + if (bus_dmamap_load(via->sgd_dmat, via->sgd_dmamap, via->sgd_table, + NSEGS * sizeof(struct via_dma_op), dma_cb, via, 0) != 0) goto bad; - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", rman_get_start(via->reg), rman_get_start(via->irq)); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + rman_get_start(via->reg), rman_get_start(via->irq), + PCM_KLDSTRING(snd_via82c686)); /* Register */ if (pcm_register(dev, via, 1, 1)) goto bad; @@ -577,6 +611,7 @@ bad: if (via->parent_dmat) bus_dma_tag_destroy(via->parent_dmat); if (via->sgd_dmamap) bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); if (via->sgd_dmat) bus_dma_tag_destroy(via->sgd_dmat); + if (via->lock) snd_mtxfree(via->lock); if (via) kfree(via, M_DEVBUF); return ENXIO; } @@ -598,6 +633,7 @@ via_detach(device_t dev) bus_dma_tag_destroy(via->parent_dmat); bus_dmamap_unload(via->sgd_dmat, via->sgd_dmamap); bus_dma_tag_destroy(via->sgd_dmat); + snd_mtxfree(via->lock); kfree(via, M_DEVBUF); return 0; } @@ -617,5 +653,5 @@ static driver_t via_driver = { }; DRIVER_MODULE(snd_via82c686, pci, via_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_via82c686, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_via82c686, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_via82c686, 1); diff --git a/sys/dev/sound/pci/via82c686.h b/sys/dev/sound/pci/via82c686.h index 1fb1ecb5f2..52c0d76e11 100644 --- a/sys/dev/sound/pci/via82c686.h +++ b/sys/dev/sound/pci/via82c686.h @@ -33,8 +33,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/via82c686.h,v 1.1.2.7 2003/05/11 01:45:49 orion Exp $ - * $DragonFly: src/sys/dev/sound/pci/via82c686.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/via82c686.h,v 1.6 2003/03/26 05:35:38 orion Exp $ + * $DragonFly: src/sys/dev/sound/pci/via82c686.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ #ifndef _VIA_H diff --git a/sys/dev/sound/pci/vibes.c b/sys/dev/sound/pci/vibes.c index 0c715cd775..62b8e85ce3 100644 --- a/sys/dev/sound/pci/vibes.c +++ b/sys/dev/sound/pci/vibes.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2001 Orion Hodson * All rights reserved. * @@ -27,8 +27,8 @@ * detached, haven't been able to remedy this with any combination of * muting. * - * $FreeBSD: src/sys/dev/sound/pci/vibes.c,v 1.4.2.6 2002/04/22 15:49:33 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/vibes.c,v 1.9 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pci/vibes.c,v 1.19.2.1 2006/01/24 18:54:22 joel Exp $ + * $DragonFly: src/sys/dev/sound/pci/vibes.c,v 1.10 2007/01/04 21:47:02 corecode Exp $ */ #include @@ -39,7 +39,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/vibes.c,v 1.9 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pci/vibes.c,v 1.10 2007/01/04 21:47:02 corecode Exp $"); /* ------------------------------------------------------------------------- */ /* Constants */ @@ -327,7 +327,7 @@ svrchan_trigger(kobj_t obj, void *data, int go) /* Program DMA */ count = sndbuf_getsize(ch->buffer) / 2; /* DMAC uses words */ sv_dma_set_config(sc->dmac_st, sc->dmac_sh, - vtophys(sndbuf_getbuf(ch->buffer)), + sndbuf_getbufaddr(ch->buffer), count - 1, SV_DMA_MODE_AUTO | SV_DMA_MODE_RD); count = count / SV_INTR_PER_BUFFER - 1; @@ -402,7 +402,7 @@ svpchan_trigger(kobj_t obj, void *data, int go) /* Program DMA */ count = sndbuf_getsize(ch->buffer); sv_dma_set_config(sc->dmaa_st, sc->dmaa_sh, - vtophys(sndbuf_getbuf(ch->buffer)), + sndbuf_getbufaddr(ch->buffer), count - 1, SV_DMA_MODE_AUTO | SV_DMA_MODE_WR); count = count / SV_INTR_PER_BUFFER - 1; @@ -550,6 +550,7 @@ sv_mix_setrecsrc(struct snd_mixer *m, u_int32_t mask) } } DEB(kprintf("sv_mix_setrecsrc: mask 0x%08x adc_input 0x%02x\n", mask, v)); + sv_indirect_set(sc, SV_REG_ADC_INPUT, v); return mask; } @@ -706,7 +707,7 @@ sv_probe(device_t dev) switch(pci_get_devid(dev)) { case SV_PCI_ID: device_set_desc(dev, "S3 Sonicvibes"); - return 0; + return BUS_PROBE_DEFAULT; default: return ENXIO; } @@ -714,14 +715,11 @@ sv_probe(device_t dev) static int sv_attach(device_t dev) { - struct snddev_info *d; struct sc_info *sc; u_int32_t data; char status[SND_STATUSLEN]; u_long midi_start, games_start, count, sdmaa, sdmac, ml, mu; - d = device_get_softc(dev); - sc = kmalloc(sizeof(struct sc_info), M_DEVBUF, M_NOWAIT | M_ZERO); if (sc == NULL) { device_printf(dev, "cannot allocate softc"); @@ -734,7 +732,7 @@ sv_attach(device_t dev) { pci_write_config(dev, PCIR_COMMAND, data, 2); data = pci_read_config(dev, PCIR_COMMAND, 2); -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 +#if __FreeBSD_version > 500000 if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0) { device_printf(dev, "chip is in D%d power mode " "-- setting to D0\n", pci_get_powerstate(dev)); @@ -767,7 +765,7 @@ sv_attach(device_t dev) { 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, 0, sv_intr, sc, &sc->ih, NULL)) { + bus_setup_intr(dev, sc->irq, INTR_TYPE_AV, sv_intr, sc, &sc->ih, NULL)) { device_printf(dev, "sv_attach: Unable to map interrupt\n"); goto fail; } @@ -779,7 +777,7 @@ sv_attach(device_t dev) { /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, - &sc->parent_dmat) != 0) { + &sc->parent_dmat) != 0) { device_printf(dev, "sv_attach: Unable to create dma tag\n"); goto fail; } @@ -880,8 +878,8 @@ sv_attach(device_t dev) { pcm_addchan(dev, PCMDIR_PLAY, &svpchan_class, sc); pcm_addchan(dev, PCMDIR_REC, &svrchan_class, sc); - ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld", - rman_get_start(sc->enh_reg), rman_get_start(sc->irq)); + ksnprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld %s", + rman_get_start(sc->enh_reg), rman_get_start(sc->irq),PCM_KLDSTRING(snd_vibes)); pcm_setstatus(dev, status); DEB(kprintf("sv_attach: succeeded\n")); @@ -944,5 +942,5 @@ static driver_t sonicvibes_driver = { }; DRIVER_MODULE(snd_vibes, pci, sonicvibes_driver, pcm_devclass, 0, 0); -MODULE_DEPEND(snd_vibes, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(snd_vibes, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(snd_vibes, 1); diff --git a/sys/dev/sound/pci/vibes.h b/sys/dev/sound/pci/vibes.h index 25e1f68002..d735b2ec62 100644 --- a/sys/dev/sound/pci/vibes.h +++ b/sys/dev/sound/pci/vibes.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 2001 Orion Hodson * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pci/vibes.h,v 1.1.2.3 2002/04/22 15:49:33 cg Exp $ - * $DragonFly: src/sys/dev/sound/pci/vibes.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pci/vibes.h,v 1.2 2005/01/06 01:43:19 imp Exp $ + * $DragonFly: src/sys/dev/sound/pci/vibes.h,v 1.3 2007/01/04 21:47:02 corecode Exp $ */ /* ------------------------------------------------------------------------- */ diff --git a/sys/dev/sound/pcm/Makefile b/sys/dev/sound/pcm/Makefile index c9d14ffca2..a5ce497cef 100644 --- a/sys/dev/sound/pcm/Makefile +++ b/sys/dev/sound/pcm/Makefile @@ -1,13 +1,31 @@ -# $FreeBSD: src/sys/modules/sound/pcm/Makefile,v 1.3.2.4 2003/02/07 16:26:45 orion Exp $ -# $DragonFly: src/sys/dev/sound/pcm/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../dev/sound/pcm -KMOD = snd_pcm -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += ac97_if.h channel_if.h feeder_if.h mixer_if.h -SRCS += ac97_if.c channel_if.c feeder_if.c mixer_if.c -SRCS += ac97.c ac97_patch.c buffer.c channel.c dsp.c -SRCS += fake.c feeder.c feeder_fmt.c feeder_rate.c -SRCS += mixer.c sndstat.c sound.c vchan.c +# $FreeBSD: src/sys/modules/sound/sound/Makefile,v 1.16.2.2 2006/09/13 08:40:21 des Exp $ +# $DragonFly: src/sys/dev/sound/pcm/Makefile,v 1.3 2007/01/04 21:47:03 corecode Exp $ + +.PATH: ${.CURDIR}/../isa + +KMOD= sound +SRCS= device_if.h bus_if.h isa_if.h pci_if.h use_isa.h +SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h +SRCS+= ac97_if.c channel_if.c feeder_if.c mixer_if.c +SRCS+= ac97.c ac97_patch.c buffer.c channel.c dsp.c +SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c +SRCS+= mixer.c sndstat.c sound.c vchan.c + +EXPORT_SYMS= YES # XXX evaluate + +.if ${MACHINE_ARCH} == "sparc64" +# Create an empty opt_isa.h in order to keep kmod.mk from linking in an +# existing one from KERNBUILDDIR which possibly has DEV_ISA defined so +# sound.ko is always built without isadma support. +use_isa.h: + :> ${.TARGET} +.else +SRCS+= sndbuf_dma.c + +.ifndef BUILDING_WITH_KERNEL +use_isa.h: + echo "#define NISA 1" > ${.TARGET} +.endif +.endif .include diff --git a/sys/dev/sound/pcm/ac97.c b/sys/dev/sound/pcm/ac97.c index 1eee20c996..42ab5c8c81 100644 --- a/sys/dev/sound/pcm/ac97.c +++ b/sys/dev/sound/pcm/ac97.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.49 2003/11/11 22:15:17 kuriyama Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.22 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.53.2.3 2006/01/09 02:06:42 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.23 2007/01/04 21:47:03 corecode Exp $ */ #include @@ -33,7 +33,7 @@ #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.22 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.23 2007/01/04 21:47:03 corecode Exp $"); MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); @@ -59,7 +59,7 @@ struct ac97_info { u_int32_t flags; struct ac97mixtable_entry mix[32]; char name[AC97_NAMELEN]; - struct mtx *lock; + struct spinlock *lock; }; struct ac97_vendorid { @@ -87,10 +87,8 @@ static const struct ac97mixtable_entry ac97mixtable_default[32] = { [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, 1, 1 }, -#if 0 /* use igain for the mic 20dB boost */ [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, -#endif [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 }, @@ -101,7 +99,7 @@ static const struct ac97_vendorid ac97vendorid[] = { { 0x41445300, "Analog Devices" }, { 0x414b4d00, "Asahi Kasei" }, { 0x414c4300, "Realtek" }, - { 0x414c4700, "Avance Logic" }, /* Nowadays Realtek */ + { 0x414c4700, "Avance Logic" }, { 0x43525900, "Cirrus Logic" }, { 0x434d4900, "C-Media Electronics" }, { 0x43585400, "Conexant" }, @@ -121,6 +119,11 @@ static const struct ac97_vendorid ac97vendorid[] = { { 0x57454300, "Winbond" }, { 0x574d4c00, "Wolfson" }, { 0x594d4800, "Yamaha" }, + /* + * XXX This is a fluke, really! The real vendor + * should be SigmaTel, not this! This should be + * removed someday! + */ { 0x01408300, "Creative" }, { 0x00000000, NULL } }; @@ -133,10 +136,12 @@ static struct ac97_codecid ac97codecid[] = { { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, { 0x41445362, 0x00, 0, "AD1887", 0 }, { 0x41445363, 0x00, 0, "AD1886A", 0 }, + { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, { 0x41445372, 0x00, 0, "AD1981A", 0 }, { 0x41445374, 0x00, 0, "AD1981B", 0 }, { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, + { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, { 0x414b4d00, 0x00, 1, "AK4540", 0 }, { 0x414b4d01, 0x00, 1, "AK4542", 0 }, { 0x414b4d02, 0x00, 1, "AK4543", 0 }, @@ -147,7 +152,7 @@ static struct ac97_codecid ac97codecid[] = { { 0x414c4710, 0x0f, 0, "ALC200", 0 }, { 0x414c4740, 0x0f, 0, "ALC202", 0 }, { 0x414c4720, 0x0f, 0, "ALC650", 0 }, - { 0x414c4750, 0x0f, 0, "ALC250", 0 }, + { 0x414c4752, 0x0f, 0, "ALC250", 0 }, { 0x414c4760, 0x0f, 0, "ALC655", 0 }, { 0x414c4770, 0x0f, 0, "ALC203", 0 }, { 0x414c4780, 0x0f, 0, "ALC658", 0 }, @@ -160,10 +165,14 @@ static struct ac97_codecid ac97codecid[] = { { 0x43525940, 0x07, 0, "CS4201", 0 }, { 0x43525958, 0x07, 0, "CS4205", 0 }, { 0x43525960, 0x07, 0, "CS4291A", 0 }, - { 0x434d4961, 0x00, 0, "CMI9739", 0 }, + { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, { 0x434d4941, 0x00, 0, "CMI9738", 0 }, + { 0x434d4978, 0x00, 0, "CMI9761", 0 }, + { 0x434d4982, 0x00, 0, "CMI9761", 0 }, + { 0x434d4983, 0x00, 0, "CMI9761", 0 }, { 0x43585421, 0x00, 0, "HSD11246", 0 }, { 0x43585428, 0x07, 0, "CX20468", 0 }, + { 0x43585430, 0x00, 0, "CX20468-21", 0 }, { 0x44543000, 0x00, 0, "DT0398", 0 }, { 0x454d4323, 0x00, 0, "EM28023", 0 }, { 0x454d4328, 0x00, 0, "EM28028", 0 }, @@ -196,6 +205,7 @@ static struct ac97_codecid ac97codecid[] = { { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ + { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, { 0x53494c22, 0x00, 0, "Si3036", 0 }, { 0x53494c23, 0x00, 0, "Si3038", 0 }, { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ @@ -205,6 +215,7 @@ static struct ac97_codecid ac97codecid[] = { { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, { 0x56494161, 0x00, 0, "VIA1612A", 0 }, + { 0x56494170, 0x00, 0, "VIA1617A", 0 }, { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, @@ -215,6 +226,11 @@ static struct ac97_codecid ac97codecid[] = { { 0x594d4800, 0x00, 0, "YMF743", 0 }, { 0x594d4802, 0x00, 0, "YMF752", 0 }, { 0x594d4803, 0x00, 0, "YMF753", 0 }, + /* + * XXX This is a fluke, really! The real codec + * should be STAC9704, not this! This should be + * removed someday! + */ { 0x01408384, 0x00, 0, "EV1938", 0 }, { 0, 0, 0, NULL, 0 } }; @@ -287,6 +303,21 @@ static char *ac97extfeature[] = { u_int16_t ac97_rdcd(struct ac97_info *codec, int reg) { + if (codec->flags & AC97_F_RDCD_BUG) { + u_int16_t i[2], j = 100; + + i[0] = AC97_READ(codec->methods, codec->devinfo, reg); + i[1] = AC97_READ(codec->methods, codec->devinfo, reg); + while (i[0] != i[1] && j) + i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); +#if 0 + if (j < 100) { + device_printf(codec->dev, "%s(): Inconsistent register value at" + " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); + } +#endif + return i[!(j & 1)]; + } return AC97_READ(codec->methods, codec->devinfo, reg); } @@ -456,14 +487,16 @@ ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned */ snd_mtxlock(codec->lock); if (e->mask) { - int cur = ac97_rdcd(codec, e->reg); + int cur = ac97_rdcd(codec, reg); val |= cur & ~(mask); } ac97_wrcd(codec, reg, val); snd_mtxunlock(codec->lock); return left | (right << 8); } else { - /* kprintf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ +#if 0 + kprintf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); +#endif return -1; } } @@ -515,6 +548,40 @@ ac97_fix_tone(struct ac97_info *codec) } } +static void +ac97_fix_volume(struct ac97_info *codec) +{ + struct snddev_info *d = device_get_softc(codec->dev); + +#if 0 + /* XXX For the sake of debugging purposes */ + ac97_wrcd(codec, AC97_MIX_PCM, 0); + bzero(&codec->mix[SOUND_MIXER_PCM], + sizeof(codec->mix[SOUND_MIXER_PCM])); + codec->flags |= AC97_F_SOFTVOL; + if (d) + d->flags |= SD_F_SOFTVOL; + return; +#endif + switch (codec->id) { + case 0x434d4941: /* CMI9738 */ + case 0x434d4961: /* CMI9739 */ + case 0x434d4978: /* CMI9761 */ + case 0x434d4982: /* CMI9761 */ + case 0x434d4983: /* CMI9761 */ + ac97_wrcd(codec, AC97_MIX_PCM, 0); + break; + default: + return; + break; + } + bzero(&codec->mix[SOUND_MIXER_PCM], + sizeof(codec->mix[SOUND_MIXER_PCM])); + codec->flags |= AC97_F_SOFTVOL; + if (d) + d->flags |= SD_F_SOFTVOL; +} + static const char* ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) { @@ -540,8 +607,9 @@ ac97_initmixer(struct ac97_info *codec) const char *cname, *vname; char desc[80]; u_int8_t model, step; - unsigned i, j, k, old; + unsigned i, j, k, bit, old; u_int32_t id; + int reg; snd_mtxlock(codec->lock); codec->count = AC97_INIT(codec->methods, codec->devinfo); @@ -556,6 +624,16 @@ ac97_initmixer(struct ac97_info *codec) ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); i = ac97_rdcd(codec, AC97_REG_RESET); + j = ac97_rdcd(codec, AC97_REG_RESET); + /* + * Let see if this codec can return consistent value. + * If not, turn on aggressive read workaround + * (STAC9704 comes in mind). + */ + if (i != j) { + codec->flags |= AC97_F_RDCD_BUG; + i = ac97_rdcd(codec, AC97_REG_RESET); + } codec->caps = i & 0x03ff; codec->se = (i & 0x7c00) >> 10; @@ -609,27 +687,82 @@ ac97_initmixer(struct ac97_info *codec) } ac97_fix_auxout(codec); ac97_fix_tone(codec); + ac97_fix_volume(codec); if (codec_patch) codec_patch(codec); for (i = 0; i < 32; i++) { k = codec->noext? codec->mix[i].enable : 1; - if (k && (codec->mix[i].reg > 0)) { - old = ac97_rdcd(codec, codec->mix[i].reg); - ac97_wrcd(codec, codec->mix[i].reg, 0x3f); - j = ac97_rdcd(codec, codec->mix[i].reg); - ac97_wrcd(codec, codec->mix[i].reg, old); - 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; + reg = codec->mix[i].reg; + if (reg < 0) + reg = -reg; + if (k && reg) { + j = old = ac97_rdcd(codec, reg); + /* + * Test for mute bit (except for AC97_MIX_TONE, + * where we simply assume it as available). + */ + if (codec->mix[i].mute) { + ac97_wrcd(codec, reg, j | 0x8000); + j = ac97_rdcd(codec, reg); + } else + j |= 0x8000; + if ((j & 0x8000)) { + /* + * Test whether the control width should be + * 4, 5 or 6 bit. For 5bit register, we should + * test it whether it's really 5 or 6bit. Leave + * 4bit register alone, because sometimes an + * attempt to write past 4th bit may cause + * incorrect result especially for AC97_MIX_BEEP + * (ac97 2.3). + */ + bit = codec->mix[i].bits; + if (bit == 5) + bit++; + j = ((1 << bit) - 1) << codec->mix[i].ofs; + ac97_wrcd(codec, reg, + j | (codec->mix[i].mute ? 0x8000 : 0)); + k = ac97_rdcd(codec, reg) & j; + k >>= codec->mix[i].ofs; + if (reg == AC97_MIX_TONE && + ((k & 0x0001) == 0x0000)) + k >>= 1; + for (j = 0; k >> j; j++) + ; + if (j != 0) { +#if 0 + device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", + i, k, bit, codec->mix[i].bits, j); +#endif + codec->mix[i].enable = 1; + codec->mix[i].bits = j; + } else if (reg == AC97_MIX_BEEP) { + /* + * Few codec such as CX20468-21 does + * have this control register, although + * the only usable part is the mute bit. + */ + codec->mix[i].enable = 1; + } else + codec->mix[i].enable = 0; + } else + codec->mix[i].enable = 0; + ac97_wrcd(codec, reg, old); } - /* kprintf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ +#if 0 + kprintf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); +#endif } device_printf(codec->dev, "<%s>\n", ac97_hw_desc(codec->id, vname, cname, desc)); if (bootverbose) { + if (codec->flags & AC97_F_RDCD_BUG) + device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); + if (codec->flags & AC97_F_SOFTVOL) + device_printf(codec->dev, "Soft PCM volume\n"); device_printf(codec->dev, "Codec features "); for (i = j = 0; i < 10; i++) if (codec->caps & (1 << i)) @@ -649,8 +782,16 @@ ac97_initmixer(struct ac97_info *codec) } } - if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) - device_printf(codec->dev, "ac97 codec reports dac not ready\n"); + i = 0; + while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { + if (++i == 100) { + device_printf(codec->dev, "ac97 codec reports dac not ready\n"); + break; + } + DELAY(1000); + } + if (bootverbose) + device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); snd_mtxunlock(codec->lock); return 0; } @@ -697,9 +838,8 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls) ksnprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); - codec->methods = kobj_create(cls, M_AC97, 0); + codec->methods = kobj_create(cls, M_AC97, M_WAITOK); if (codec->methods == NULL) { - snd_mtxlock(codec->lock); snd_mtxfree(codec->lock); kfree(codec, M_AC97); return NULL; @@ -714,7 +854,6 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls) void ac97_destroy(struct ac97_info *codec) { - snd_mtxlock(codec->lock); if (codec->methods != NULL) kobj_delete(codec->methods, M_AC97); snd_mtxfree(codec->lock); diff --git a/sys/dev/sound/pcm/ac97.h b/sys/dev/sound/pcm/ac97.h index 323f442e61..a3b9fa2751 100644 --- a/sys/dev/sound/pcm/ac97.h +++ b/sys/dev/sound/pcm/ac97.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright (c) 1999 Cameron Grant * All rights reserved. * @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97.h,v 1.15 2003/09/07 16:28:03 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97.h,v 1.3 2004/01/21 20:02:08 asmodai Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97.h,v 1.16.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97.h,v 1.4 2007/01/04 21:47:03 corecode Exp $ */ #define AC97_MUTE 0x8080 @@ -82,6 +82,8 @@ #define AC97_REG_ID2 0x7e #define AC97_F_EAPD_INV 0x00000001 +#define AC97_F_RDCD_BUG 0x00000002 +#define AC97_F_SOFTVOL 0x00000004 #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) diff --git a/sys/dev/sound/pcm/ac97_if.m b/sys/dev/sound/pcm/ac97_if.m index 0c4a597c68..2edb36bde3 100644 --- a/sys/dev/sound/pcm/ac97_if.m +++ b/sys/dev/sound/pcm/ac97_if.m @@ -1,3 +1,4 @@ +#- # KOBJ # # Copyright (c) 2000 Cameron Grant @@ -24,8 +25,8 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $FreeBSD: src/sys/dev/sound/pcm/ac97_if.m,v 1.1.2.3 2002/04/22 15:49:35 cg Exp $ -# $DragonFly: src/sys/dev/sound/pcm/ac97_if.m,v 1.3 2003/11/15 21:12:53 asmodai Exp $ +# $FreeBSD: src/sys/dev/sound/pcm/ac97_if.m,v 1.4 2005/01/06 01:43:20 imp Exp $ +# $DragonFly: src/sys/dev/sound/pcm/ac97_if.m,v 1.4 2007/01/04 21:47:03 corecode Exp $ # #include @@ -59,4 +60,3 @@ METHOD int write { int regno; u_int32_t data; }; - diff --git a/sys/dev/sound/pcm/ac97_patch.c b/sys/dev/sound/pcm/ac97_patch.c index d3bc0f8384..4a957be1c3 100644 --- a/sys/dev/sound/pcm/ac97_patch.c +++ b/sys/dev/sound/pcm/ac97_patch.c @@ -1,4 +1,4 @@ -/* +/*- * Copyright 2002 FreeBSD, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,15 +22,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.c,v 1.2 2003/08/21 15:44:55 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.3 2004/01/21 20:02:08 asmodai Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.c,v 1.3.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.4 2007/01/04 21:47:03 corecode Exp $ */ #include #include #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.3 2004/01/21 20:02:08 asmodai Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97_patch.c,v 1.4 2007/01/04 21:47:03 corecode Exp $"); void ad1886_patch(struct ac97_info* codec) { @@ -49,3 +49,13 @@ void ad198x_patch(struct ac97_info* codec) ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420); } +void cmi9739_patch(struct ac97_info* codec) +{ + /* + * Few laptops (notably ASUS W1000N) need extra register + * initialization to power up the internal speakers. + */ + ac97_wrcd(codec, AC97_REG_POWER, 0x000f); + ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000); + ac97_wrcd(codec, 0x64, 0x7110); +} diff --git a/sys/dev/sound/pcm/ac97_patch.h b/sys/dev/sound/pcm/ac97_patch.h index 48538f9275..888aa143a3 100644 --- a/sys/dev/sound/pcm/ac97_patch.h +++ b/sys/dev/sound/pcm/ac97_patch.h @@ -1,4 +1,4 @@ -/* +/*- * Copyright 2003 FreeBSD, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,11 +22,12 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.h,v 1.2 2003/08/21 15:44:55 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.h,v 1.3 2004/01/21 20:02:08 asmodai Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/ac97_patch.h,v 1.3.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/ac97_patch.h,v 1.4 2007/01/04 21:47:03 corecode Exp $ */ typedef void (*ac97_patch)(struct ac97_info*); void ad1886_patch(struct ac97_info*); void ad198x_patch(struct ac97_info*); +void cmi9739_patch(struct ac97_info*); diff --git a/sys/dev/sound/pcm/buffer.c b/sys/dev/sound/pcm/buffer.c index 89a80f4fdb..c2f20ba54b 100644 --- a/sys/dev/sound/pcm/buffer.c +++ b/sys/dev/sound/pcm/buffer.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,46 +23,25 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/buffer.c,v 1.1.2.4 2002/04/22 15:49:35 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.7 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/buffer.c,v 1.25.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.8 2007/01/04 21:47:03 corecode Exp $ */ #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.7 2006/12/22 23:26:25 swildner Exp $"); - -#define SNDBUF_NAMELEN 48 -struct snd_dbuf { - device_t dev; - u_int8_t *buf, *tmpbuf; - unsigned int bufsize, maxsize; - volatile int dl; /* transfer size */ - volatile int rp; /* pointers to the ready area */ - volatile int rl; /* length of ready area */ - volatile int hp; - volatile u_int32_t total, prev_total; - int isadmachan; /* dma channel */ - u_int32_t fmt, spd, bps; - unsigned int blksz, blkcnt; - int xrun; - u_int32_t flags; - bus_dmamap_t dmamap; - bus_dma_tag_t dmatag; - unsigned dmaflags; - struct selinfo sel; - char name[SNDBUF_NAMELEN]; -}; +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/buffer.c,v 1.8 2007/01/04 21:47:03 corecode Exp $"); struct snd_dbuf * -sndbuf_create(device_t dev, char *drv, char *desc) +sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel) { struct snd_dbuf *b; b = kmalloc(sizeof(*b), M_DEVBUF, M_WAITOK | M_ZERO); ksnprintf(b->name, SNDBUF_NAMELEN, "%s:%s", drv, desc); b->dev = dev; + b->channel = channel; return b; } @@ -73,33 +52,56 @@ sndbuf_destroy(struct snd_dbuf *b) kfree(b, M_DEVBUF); } +bus_addr_t +sndbuf_getbufaddr(struct snd_dbuf *buf) +{ + return (buf->buf_addr); +} + static void sndbuf_setmap(void *arg, bus_dma_segment_t *segs, int nseg, int error) { struct snd_dbuf *b = (struct snd_dbuf *)arg; if (bootverbose) { - device_printf(b->dev, "sndbuf_setmap %lx, %lx; ", (unsigned long)segs->ds_addr, - (unsigned long)segs->ds_len); - kprintf("%p -> %lx\n", b->buf, (unsigned long)vtophys(b->buf)); + device_printf(b->dev, "sndbuf_setmap %lx, %lx; ", + (u_long)segs[0].ds_addr, (u_long)segs[0].ds_len); + kprintf("%p -> %lx\n", b->buf, (u_long)segs[0].ds_addr); } + if (error == 0) + b->buf_addr = segs[0].ds_addr; + else + b->buf_addr = 0; } /* * Allocate memory for DMA buffer. If the device does not use DMA transfers, - * the driver can call malloc(9) and sndbuf_setup() itself. + * the driver can call kmalloc(9) and sndbuf_setup() itself. */ + int sndbuf_alloc(struct snd_dbuf *b, bus_dma_tag_t dmatag, unsigned int size) { + int ret; + b->dmatag = dmatag; b->maxsize = size; b->bufsize = b->maxsize; - if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT, &b->dmamap)) - return ENOSPC; - if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, sndbuf_setmap, b, 0)) - return ENOSPC; - return sndbuf_resize(b, 2, b->maxsize / 2); + b->buf_addr = 0; + if (bus_dmamem_alloc(b->dmatag, (void **)&b->buf, BUS_DMA_NOWAIT, + &b->dmamap)) + return (ENOMEM); + if (bus_dmamap_load(b->dmatag, b->dmamap, b->buf, b->maxsize, + sndbuf_setmap, b, 0) != 0 || b->buf_addr == 0) { + bus_dmamem_free(b->dmatag, b->buf, b->dmamap); + b->dmamap = NULL; + return (ENOMEM); + } + + ret = sndbuf_resize(b, 2, b->maxsize / 2); + if (ret != 0) + sndbuf_free(b); + return (ret); } int @@ -130,52 +132,90 @@ sndbuf_free(struct snd_dbuf *b) int sndbuf_resize(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) { + u_int8_t *tmpbuf, *f2; + + chn_lock(b->channel); if (b->maxsize == 0) - return 0; + goto out; if (blkcnt == 0) blkcnt = b->blkcnt; if (blksz == 0) blksz = b->blksz; - if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize)) + if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz > b->maxsize)) { + chn_unlock(b->channel); return EINVAL; + } if (blkcnt == b->blkcnt && blksz == b->blksz) - return 0; + goto out; + + chn_unlock(b->channel); + tmpbuf = kmalloc(blkcnt * blksz, M_DEVBUF, M_NOWAIT); + if (tmpbuf == NULL) + return ENOMEM; + chn_lock(b->channel); b->blkcnt = blkcnt; b->blksz = blksz; b->bufsize = blkcnt * blksz; - if (b->tmpbuf) - kfree(b->tmpbuf, M_DEVBUF); - b->tmpbuf = kmalloc(b->bufsize, M_DEVBUF, M_WAITOK); + f2 = b->tmpbuf; + b->tmpbuf = tmpbuf; sndbuf_reset(b); + chn_unlock(b->channel); + if (f2 != NULL) + kfree(f2, M_DEVBUF); + return 0; +out: + chn_unlock(b->channel); return 0; } int sndbuf_remalloc(struct snd_dbuf *b, unsigned int blkcnt, unsigned int blksz) { + u_int8_t *buf, *tmpbuf, *f1, *f2; + unsigned int bufsize; + int ret; + if (blkcnt < 2 || blksz < 16) return EINVAL; - b->blkcnt = blkcnt; - b->blksz = blksz; + bufsize = blksz * blkcnt; - b->maxsize = blkcnt * blksz; - b->bufsize = b->maxsize; + chn_unlock(b->channel); + buf = kmalloc(bufsize, M_DEVBUF, M_WAITOK); + if (buf == NULL) { + ret = ENOMEM; + goto out; + } - if (b->buf) - kfree(b->buf, M_DEVBUF); - b->buf = kmalloc(b->bufsize, M_DEVBUF, M_WAITOK); - if (b->buf == NULL) - return ENOMEM; + tmpbuf = kmalloc(bufsize, M_DEVBUF, M_WAITOK); + if (tmpbuf == NULL) { + kfree(buf, M_DEVBUF); + ret = ENOMEM; + goto out; + } + chn_lock(b->channel); - if (b->tmpbuf) - kfree(b->tmpbuf, M_DEVBUF); - b->tmpbuf = kmalloc(b->bufsize, M_DEVBUF, M_WAITOK); - if (b->tmpbuf == NULL) - return ENOMEM; + b->blkcnt = blkcnt; + b->blksz = blksz; + b->bufsize = bufsize; + b->maxsize = bufsize; + f1 = b->buf; + f2 = b->tmpbuf; + b->buf = buf; + b->tmpbuf = tmpbuf; sndbuf_reset(b); - return 0; + + chn_unlock(b->channel); + if (f1) + kfree(f1, M_DEVBUF); + if (f2) + kfree(f2, M_DEVBUF); + + ret = 0; +out: + chn_lock(b->channel); + return ret; } void @@ -250,8 +290,12 @@ sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt) b->fmt = fmt; b->bps = 1; b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0; - b->bps <<= (b->fmt & AFMT_16BIT)? 1 : 0; - b->bps <<= (b->fmt & AFMT_32BIT)? 2 : 0; + if (b->fmt & AFMT_16BIT) + b->bps <<= 1; + else if (b->fmt & AFMT_24BIT) + b->bps *= 3; + else if (b->fmt & AFMT_32BIT) + b->bps <<= 2; return 0; } @@ -458,7 +502,7 @@ sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count) { int l; - KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > free %d", __func__, count, sndbuf_getfree(b))); + KASSERT(count <= sndbuf_getfree(b), ("%s: count %d > kfree %d", __func__, count, sndbuf_getfree(b))); KASSERT((b->rl >= 0) && (b->rl <= b->bufsize), ("%s: b->rl invalid %d", __func__, b->rl)); b->total += count; if (from != NULL) { @@ -501,33 +545,6 @@ sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count) return 0; } -int -sndbuf_uiomove(struct snd_dbuf *b, struct uio *uio, unsigned int count) -{ - int x, c, p, rd, err; - - err = 0; - rd = (uio->uio_rw == UIO_READ)? 1 : 0; - if (count > uio->uio_resid) - return EINVAL; - - if (count > (rd? sndbuf_getready(b) : sndbuf_getfree(b))) { - return EINVAL; - } - - while (err == 0 && count > 0) { - p = rd? sndbuf_getreadyptr(b) : sndbuf_getfreeptr(b); - c = MIN(count, sndbuf_getsize(b) - p); - x = uio->uio_resid; - err = uiomove(sndbuf_getbufofs(b, p), c, uio); - x -= uio->uio_resid; - count -= x; - x = rd? sndbuf_dispose(b, NULL, x) : sndbuf_acquire(b, NULL, x); - } - - return 0; -} - /* count is number of bytes we want added to destination buffer */ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) @@ -578,78 +595,3 @@ sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on) if (on) b->flags |= flags; } - -/************************************************************/ - -int -sndbuf_isadmasetup(struct snd_dbuf *b, struct resource *drq) -{ - /* should do isa_dma_acquire/isa_dma_release here */ - if (drq == NULL) { - b->isadmachan = -1; - } else { - sndbuf_setflags(b, SNDBUF_F_ISADMA, 1); - b->isadmachan = rman_get_start(drq); - } - return 0; -} - -int -sndbuf_isadmasetdir(struct snd_dbuf *b, int dir) -{ - KASSERT(b, ("sndbuf_isadmasetdir called with b == NULL")); - KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmasetdir called on non-ISA buffer")); - - b->dmaflags = (dir == PCMDIR_PLAY)? ISADMA_WRITE : ISADMA_READ; - return 0; -} - -void -sndbuf_isadma(struct snd_dbuf *b, int go) -{ - KASSERT(b, ("sndbuf_isadma called with b == NULL")); - KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadma called on non-ISA buffer")); - - switch (go) { - case PCMTRIG_START: - /* isa_dmainit(b->chan, size); */ - isa_dmastart(b->dmaflags | ISADMA_RAW, b->buf, b->bufsize, b->isadmachan); - break; - - case PCMTRIG_STOP: - case PCMTRIG_ABORT: - isa_dmastop(b->isadmachan); - isa_dmadone(b->dmaflags | ISADMA_RAW, b->buf, b->bufsize, b->isadmachan); - break; - } - - DEB(kprintf("buf 0x%p ISA DMA %s, channel %d\n", - b, - (go == PCMTRIG_START)? "started" : "stopped", - b->isadmachan)); -} - -int -sndbuf_isadmaptr(struct snd_dbuf *b) -{ - int i; - - KASSERT(b, ("sndbuf_isadmaptr called with b == NULL")); - KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmaptr called on non-ISA buffer")); - - if (!sndbuf_runsz(b)) - return 0; - i = isa_dmastatus(b->isadmachan); - KASSERT(i >= 0, ("isa_dmastatus returned %d", i)); - return b->bufsize - i; -} - -void -sndbuf_isadmabounce(struct snd_dbuf *b) -{ - KASSERT(b, ("sndbuf_isadmabounce called with b == NULL")); - KASSERT(sndbuf_getflags(b) & SNDBUF_F_ISADMA, ("sndbuf_isadmabounce called on non-ISA buffer")); - - /* tell isa_dma to bounce data in/out */ -} - diff --git a/sys/dev/sound/pcm/buffer.h b/sys/dev/sound/pcm/buffer.h index bb45930a25..fcae33d140 100644 --- a/sys/dev/sound/pcm/buffer.h +++ b/sys/dev/sound/pcm/buffer.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,18 +23,42 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/buffer.h,v 1.1.2.3 2002/04/22 15:49:35 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/buffer.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/buffer.h,v 1.10 2005/01/06 01:43:20 imp Exp $ + * $DragonFly: src/sys/dev/sound/pcm/buffer.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ -#define ISA_DMA(b) (sndbuf_getflags((b)) & SNDBUF_F_ISADMA) +#define SND_DMA(b) (sndbuf_getflags((b)) & SNDBUF_F_DMA) #define SNDBUF_LOCKASSERT(b) -#define SNDBUF_F_ISADMA 0x00000001 +#define SNDBUF_F_DMA 0x00000001 #define SNDBUF_F_XRUN 0x00000002 #define SNDBUF_F_RUNNING 0x00000004 -struct snd_dbuf *sndbuf_create(device_t dev, char *drv, char *desc); +#define SNDBUF_NAMELEN 48 + +struct snd_dbuf { + device_t dev; + u_int8_t *buf, *tmpbuf; + unsigned int bufsize, maxsize; + volatile int dl; /* transfer size */ + volatile int rp; /* pointers to the ready area */ + volatile int rl; /* length of ready area */ + volatile int hp; + volatile u_int32_t total, prev_total; + int dmachan, dir; /* dma channel */ + u_int32_t fmt, spd, bps; + unsigned int blksz, blkcnt; + int xrun; + u_int32_t flags; + bus_dmamap_t dmamap; + bus_dma_tag_t dmatag; + u_int32_t buf_addr; + struct selinfo sel; + struct pcm_channel *channel; + char name[SNDBUF_NAMELEN]; +}; + +struct snd_dbuf *sndbuf_create(device_t dev, char *drv, char *desc, struct pcm_channel *channel); void sndbuf_destroy(struct snd_dbuf *b); void sndbuf_dump(struct snd_dbuf *b, char *s, u_int32_t what); @@ -54,6 +78,8 @@ unsigned int sndbuf_getspd(struct snd_dbuf *b); void sndbuf_setspd(struct snd_dbuf *b, unsigned int spd); unsigned int sndbuf_getbps(struct snd_dbuf *b); +bus_addr_t sndbuf_getbufaddr(struct snd_dbuf *buf); + void *sndbuf_getbuf(struct snd_dbuf *b); void *sndbuf_getbufofs(struct snd_dbuf *b, unsigned int ofs); unsigned int sndbuf_getsize(struct snd_dbuf *b); @@ -82,16 +108,13 @@ void sndbuf_updateprevtotal(struct snd_dbuf *b); int sndbuf_acquire(struct snd_dbuf *b, u_int8_t *from, unsigned int count); int sndbuf_dispose(struct snd_dbuf *b, u_int8_t *to, unsigned int count); -int sndbuf_uiomove(struct snd_dbuf *b, struct uio *uio, unsigned int count); int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count); u_int32_t sndbuf_getflags(struct snd_dbuf *b); void sndbuf_setflags(struct snd_dbuf *b, u_int32_t flags, int on); -int sndbuf_isadmasetup(struct snd_dbuf *b, struct resource *drq); -int sndbuf_isadmasetdir(struct snd_dbuf *b, int dir); -void sndbuf_isadma(struct snd_dbuf *b, int go); -int sndbuf_isadmaptr(struct snd_dbuf *b); -void sndbuf_isadmabounce(struct snd_dbuf *b); - - +int sndbuf_dmasetup(struct snd_dbuf *b, struct resource *drq); +int sndbuf_dmasetdir(struct snd_dbuf *b, int dir); +void sndbuf_dma(struct snd_dbuf *b, int go); +int sndbuf_dmaptr(struct snd_dbuf *b); +void sndbuf_dmabounce(struct snd_dbuf *b); diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c index adffc0291e..56d7d363d6 100644 --- a/sys/dev/sound/pcm/channel.c +++ b/sys/dev/sound/pcm/channel.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * Portions Copyright by Luigi Rizzo - 1997-99 * All rights reserved. * @@ -24,20 +24,24 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.19.2.19 2003/03/11 15:15:41 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.10 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.99.2.4 2006/04/04 17:37:51 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.11 2007/01/04 21:47:03 corecode Exp $ */ +#include "use_isa.h" + #include -#include +#include /* IO_NDELAY */ #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.10 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/channel.c,v 1.11 2007/01/04 21:47:03 corecode Exp $"); #define MIN_CHUNK_SIZE 256 /* for uiomove etc. */ +#if 0 #define DMA_ALIGN_THRESHOLD 4 #define DMA_ALIGN_MASK (~(DMA_ALIGN_THRESHOLD - 1)) +#endif #define CANCHANGE(c) (!(c->flags & CHN_F_TRIGGERED)) @@ -71,9 +75,22 @@ SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, static int chn_buildfeeder(struct pcm_channel *c); static void -chn_lockinit(struct pcm_channel *c) +chn_lockinit(struct pcm_channel *c, int dir) { - c->lock = snd_mtxcreate(c->name, "pcm channel"); + switch(dir) { + case PCMDIR_PLAY: + c->lock = snd_mtxcreate(c->name, "pcm play channel"); + break; + case PCMDIR_REC: + c->lock = snd_mtxcreate(c->name, "pcm record channel"); + break; + case PCMDIR_VIRTUAL: + c->lock = snd_mtxcreate(c->name, "pcm virtual play channel"); + break; + case 0: + c->lock = snd_mtxcreate(c->name, "pcm fake channel"); + break; + } } static void @@ -96,7 +113,9 @@ chn_polltrigger(struct pcm_channel *c) return (sndbuf_getblocks(bs) > sndbuf_getprevblocks(bs))? 1 : 0; } else { amt = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); +#if 0 lim = (c->flags & CHN_F_HAS_SIZE)? sndbuf_getblksz(bs) : 1; +#endif lim = 1; return (amt >= lim)? 1 : 0; } @@ -116,11 +135,22 @@ chn_pollreset(struct pcm_channel *c) static void chn_wakeup(struct pcm_channel *c) { - struct snd_dbuf *bs = c->bufsoft; + struct snd_dbuf *bs = c->bufsoft; + struct pcmchan_children *pce; CHN_LOCKASSERT(c); - if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c)) - selwakeup(sndbuf_getsel(bs)); + if (SLIST_EMPTY(&c->children)) { + /*if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))*/ + if (sndbuf_getsel(bs)->si_pid && chn_polltrigger(c)) + selwakeup(sndbuf_getsel(bs)); + } else { + SLIST_FOREACH(pce, &c->children, link) { + CHN_LOCK(pce->channel); + chn_wakeup(pce->channel); + CHN_UNLOCK(pce->channel); + } + } + wakeup(bs); } @@ -134,7 +164,7 @@ chn_sleep(struct pcm_channel *c, char *str, int timeout) #ifdef USING_MUTEX ret = msleep(bs, c->lock, PCATCH, str, timeout); #else - ret = tsleep(bs, PCATCH, str, timeout); + ret = tsleep(bs, PRIBIO | PCATCH, str, timeout); #endif return ret; @@ -142,7 +172,7 @@ chn_sleep(struct pcm_channel *c, char *str, int timeout) /* * chn_dmaupdate() tracks the status of a dma transfer, - * updating pointers. It must be called from a critical section. + * updating pointers. */ static unsigned int @@ -206,20 +236,29 @@ chn_wrfeed(struct pcm_channel *c) unsigned int ret, amt; CHN_LOCKASSERT(c); +#if 0 DEB( if (c->flags & CHN_F_CLOSING) { sndbuf_dump(b, "b", 0x02); sndbuf_dump(bs, "bs", 0x02); }) +#endif if (c->flags & CHN_F_MAPPED) sndbuf_acquire(bs, NULL, sndbuf_getfree(bs)); amt = sndbuf_getfree(b); - if (sndbuf_getready(bs) < amt) + KASSERT(amt <= sndbuf_getsize(bs), + ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, + amt, sndbuf_getsize(bs), c->flags)); + + ret = (amt > 0) ? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; + /* + * Possible xruns. There should be no empty space left in buffer. + */ + if (sndbuf_getfree(b) > 0) c->xruns++; - ret = (amt > 0)? sndbuf_feed(bs, b, c, c->feeder, amt) : ENOSPC; if (ret == 0 && sndbuf_getfree(b) < amt) chn_wakeup(c); @@ -255,15 +294,17 @@ chn_write(struct pcm_channel *c, struct uio *buf, int ioflags) int ret, timeout, newsize, count, sz; int nbio; struct snd_dbuf *bs = c->bufsoft; + void *off; + int t, x,togo,p; CHN_LOCKASSERT(c); - nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY); /* * 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. */ + nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY); if (nbio && buf->uio_resid > sndbuf_getblksz(bs)) { DEB(device_printf(c->dev, "broken app, nbio and tried to write %d bytes with fragsz %d\n", buf->uio_resid, sndbuf_getblksz(bs))); @@ -279,9 +320,9 @@ chn_write(struct pcm_channel *c, struct uio *buf, int ioflags) while (!ret && (buf->uio_resid > 0) && (count > 0)) { sz = sndbuf_getfree(bs); if (sz == 0) { - if (nbio) { + if (nbio) ret = EWOULDBLOCK; - } else { + else { timeout = (hz * sndbuf_getblksz(bs)) / (sndbuf_getspd(bs) * sndbuf_getbps(bs)); if (timeout < 1) timeout = 1; @@ -297,7 +338,24 @@ chn_write(struct pcm_channel *c, struct uio *buf, int ioflags) sz = MIN(sz, buf->uio_resid); KASSERT(sz > 0, ("confusion in chn_write")); /* kprintf("sz: %d\n", sz); */ - ret = sndbuf_uiomove(bs, buf, sz); + + /* + * The following assumes that the free space in + * the buffer can never be less around the + * unlock-uiomove-lock sequence. + */ + togo = sz; + while (ret == 0 && togo> 0) { + p = sndbuf_getfreeptr(bs); + t = MIN(togo, sndbuf_getsize(bs) - p); + off = sndbuf_getbufofs(bs, p); + CHN_UNLOCK(c); + ret = uiomove(off, t, buf); + CHN_LOCK(c); + togo -= t; + x = sndbuf_acquire(bs, NULL, t); + } + ret = 0; if (ret == 0 && !(c->flags & CHN_F_TRIGGERED)) chn_start(c, 0); } @@ -312,19 +370,25 @@ chn_write(struct pcm_channel *c, struct uio *buf, int ioflags) return ret; } +#if 0 static int chn_rddump(struct pcm_channel *c, unsigned int cnt) { struct snd_dbuf *b = c->bufhard; CHN_LOCKASSERT(c); +#if 0 + static uint32_t kk = 0; + printf("%u: dumping %d bytes\n", ++kk, cnt); +#endif + c->xruns++; sndbuf_setxrun(b, sndbuf_getxrun(b) + cnt); return sndbuf_dispose(b, NULL, cnt); } +#endif /* * Feed new data from the read buffer. Can be called in the bottom half. - * Hence must be called from a critical section. */ int chn_rdfeed(struct pcm_channel *c) @@ -340,16 +404,21 @@ chn_rdfeed(struct pcm_channel *c) sndbuf_dump(bs, "bs", 0x02); }) +#if 0 amt = sndbuf_getready(b); if (sndbuf_getfree(bs) < amt) { c->xruns++; amt = sndbuf_getfree(bs); } +#endif + amt = sndbuf_getfree(bs); ret = (amt > 0)? sndbuf_feed(b, bs, c, c->feeder, amt) : 0; amt = sndbuf_getready(b); - if (amt > 0) - chn_rddump(c, amt); + if (amt > 0) { + c->xruns++; + sndbuf_dispose(b, NULL, amt); + } chn_wakeup(c); @@ -369,9 +438,8 @@ chn_rdupdate(struct pcm_channel *c) chn_trigger(c, PCMTRIG_EMLDMARD); chn_dmaupdate(c); ret = chn_rdfeed(c); - if (ret) - kprintf("chn_rdfeed: %d\n", ret); - + DEB(if (ret) + kprintf("chn_rdfeed: %d\n", ret);) } /* read interrupt routine. Must be called with interrupts blocked. */ @@ -399,22 +467,40 @@ chn_rdintr(struct pcm_channel *c) int chn_read(struct pcm_channel *c, struct uio *buf, int ioflags) { - int ret, timeout, sz, count; + int ret, timeout, sz, count; int nbio; struct snd_dbuf *bs = c->bufsoft; + void *off; + int t, x,togo,p; CHN_LOCKASSERT(c); + nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY); if (!(c->flags & CHN_F_TRIGGERED)) chn_start(c, 0); - nbio = (c->flags & CHN_F_NBIO) || (ioflags & IO_NDELAY); ret = 0; count = hz; while (!ret && (buf->uio_resid > 0) && (count > 0)) { sz = MIN(buf->uio_resid, sndbuf_getready(bs)); if (sz > 0) { - ret = sndbuf_uiomove(bs, buf, sz); + /* + * The following assumes that the free space in + * the buffer can never be less around the + * unlock-uiomove-lock sequence. + */ + togo = sz; + while (ret == 0 && togo> 0) { + p = sndbuf_getreadyptr(bs); + t = MIN(togo, sndbuf_getsize(bs) - p); + off = sndbuf_getbufofs(bs, p); + CHN_UNLOCK(c); + ret = uiomove(off, t, buf); + CHN_LOCK(c); + togo -= t; + x = sndbuf_dispose(bs, NULL, t); + } + ret = 0; } else { if (nbio) { ret = EWOULDBLOCK; @@ -478,10 +564,12 @@ chn_start(struct pcm_channel *c, int force) * fed at the first irq. */ if (c->direction == PCMDIR_PLAY) { + /* + * Reduce pops during playback startup. + */ + sndbuf_fillsilence(b); if (SLIST_EMPTY(&c->children)) chn_wrfeed(c); - else - sndbuf_fillsilence(b); } sndbuf_setrun(b, 1); c->xruns = 0; @@ -516,7 +604,19 @@ chn_sync(struct pcm_channel *c, int threshold) struct snd_dbuf *bs = c->bufsoft; CHN_LOCKASSERT(c); - for (;;) { + + /* if we haven't yet started and nothing is buffered, else start*/ + if (!(c->flags & CHN_F_TRIGGERED)) { + if (sndbuf_getready(bs) > 0) { + ret = chn_start(c, 1); + if (ret) + return ret; + } else { + return 0; + } + } + + for (;;) { rdy = (c->direction == PCMDIR_PLAY)? sndbuf_getfree(bs) : sndbuf_getready(bs); if (rdy <= threshold) { ret = chn_sleep(c, "pcmsyn", 1); @@ -532,7 +632,7 @@ chn_sync(struct pcm_channel *c, int threshold) /* called externally, handle locking */ int -chn_poll(struct pcm_channel *c, int ev) +chn_poll(struct pcm_channel *c, int ev, struct thread *td) { struct snd_dbuf *bs = c->bufsoft; int ret; @@ -544,7 +644,7 @@ chn_poll(struct pcm_channel *c, int ev) if (chn_polltrigger(c) && chn_pollreset(c)) ret = ev; else - selrecord(curthread, sndbuf_getsel(bs)); + selrecord(td, sndbuf_getsel(bs)); return ret; } @@ -580,10 +680,13 @@ chn_abort(struct pcm_channel *c) /* * 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. + * on a close of a playback channel. + * first, if there is data in the buffer, but the dma has not yet + * begun, we need to start it. + * next, we wait for the play buffer to drain + * finally, we stop the dma. * - * called from: dsp_close + * called from: dsp_close, not valid for record channels. */ int @@ -594,10 +697,19 @@ chn_flush(struct pcm_channel *c) struct snd_dbuf *bs = c->bufsoft; CHN_LOCKASSERT(c); - KASSERT(c->direction == PCMDIR_PLAY, ("chn_wrupdate on bad channel")); - DEB(kprintf("chn_flush c->flags 0x%08x\n", c->flags)); - if (!(c->flags & CHN_F_TRIGGERED)) - return 0; + KASSERT(c->direction == PCMDIR_PLAY, ("chn_flush on bad channel")); + DEB(kprintf("chn_flush: c->flags 0x%08x\n", c->flags)); + + /* if we haven't yet started and nothing is buffered, else start*/ + if (!(c->flags & CHN_F_TRIGGERED)) { + if (sndbuf_getready(bs) > 0) { + ret = chn_start(c, 1); + if (ret) + return ret; + } else { + return 0; + } + } c->flags |= CHN_F_CLOSING; resid = sndbuf_getready(bs) + sndbuf_getready(b); @@ -611,13 +723,16 @@ chn_flush(struct pcm_channel *c) ret = 0; if (ret == 0) { resid = sndbuf_getready(bs) + sndbuf_getready(b); - if (resid >= resid_p) + if (resid == resid_p) count--; + if (resid > resid_p) + DEB(printf("chn_flush: buffer length increasind %d -> %d\n", resid_p, resid)); resid_p = resid; } } if (count == 0) - DEB(kprintf("chn_flush: timeout\n")); + DEB(kprintf("chn_flush: timeout, hw %d, sw %d\n", + sndbuf_getready(b), sndbuf_getready(bs))); c->flags &= ~CHN_F_TRIGGERED; /* kill the channel */ @@ -651,18 +766,24 @@ chn_reset(struct pcm_channel *c, u_int32_t fmt) r = CHANNEL_RESET(c->methods, c->devinfo); if (fmt != 0) { +#if 0 hwspd = DSP_DEFAULT_SPEED; /* only do this on a record channel until feederbuilder works */ if (c->direction == PCMDIR_REC) RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); c->speed = hwspd; +#endif + hwspd = chn_getcaps(c)->minspeed; + c->speed = hwspd; if (r == 0) r = chn_setformat(c, fmt); if (r == 0) r = chn_setspeed(c, hwspd); +#if 0 if (r == 0) r = chn_setvolume(c, 100, 100); +#endif } if (r == 0) r = chn_setblocksize(c, 0, 0); @@ -674,20 +795,29 @@ chn_reset(struct pcm_channel *c, u_int32_t fmt) } int -chn_init(struct pcm_channel *c, void *devinfo, int dir) +chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction) { struct feeder_class *fc; struct snd_dbuf *b, *bs; int ret; - chn_lockinit(c); - CHN_LOCK(c); + chn_lockinit(c, dir); b = NULL; bs = NULL; c->devinfo = NULL; c->feeder = NULL; + ret = ENOMEM; + b = sndbuf_create(c->dev, c->name, "primary", c); + if (b == NULL) + goto out; + bs = sndbuf_create(c->dev, c->name, "secondary", c); + if (bs == NULL) + goto out; + + CHN_LOCK(c); + ret = EINVAL; fc = feeder_getclass(NULL); if (fc == NULL) @@ -695,21 +825,23 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir) if (chn_addfeeder(c, fc, NULL)) goto out; - ret = ENOMEM; - b = sndbuf_create(c->dev, c->name, "primary"); - if (b == NULL) - goto out; - bs = sndbuf_create(c->dev, c->name, "secondary"); - if (bs == NULL) - goto out; + /* + * XXX - sndbuf_setup() & sndbuf_resize() expect to be called + * with the channel unlocked because they are also called + * from driver methods that don't know about locking + */ + CHN_UNLOCK(c); sndbuf_setup(bs, NULL, 0); + CHN_LOCK(c); c->bufhard = b; c->bufsoft = bs; c->flags = 0; c->feederflags = 0; ret = ENODEV; - c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, dir); + CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() kmalloc() call */ + c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); + CHN_LOCK(c); if (c->devinfo == NULL) goto out; @@ -717,7 +849,7 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir) if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) goto out; - ret = chn_setdir(c, dir); + ret = chn_setdir(c, direction); if (ret) goto out; @@ -731,6 +863,7 @@ chn_init(struct pcm_channel *c, void *devinfo, int dir) out: + CHN_UNLOCK(c); if (ret) { if (c->devinfo) { if (CHANNEL_FREE(c->methods, c->devinfo)) @@ -746,7 +879,6 @@ out: return ret; } - CHN_UNLOCK(c); return 0; } @@ -756,7 +888,6 @@ chn_kill(struct pcm_channel *c) struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; - CHN_LOCK(c); if (c->flags & CHN_F_TRIGGERED) chn_trigger(c, PCMTRIG_ABORT); while (chn_removefeeder(c) == 0); @@ -772,14 +903,18 @@ chn_kill(struct pcm_channel *c) int chn_setdir(struct pcm_channel *c, int dir) { +#if NISA > 0 struct snd_dbuf *b = c->bufhard; +#endif int r; CHN_LOCKASSERT(c); c->direction = dir; r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); - if (!r && ISA_DMA(b)) - sndbuf_isadmasetdir(b, c->direction); +#if NISA > 0 + if (!r && SND_DMA(b)) + sndbuf_dmasetdir(b, c->direction); +#endif return r; } @@ -787,8 +922,16 @@ int chn_setvolume(struct pcm_channel *c, int left, int right) { CHN_LOCKASSERT(c); - /* could add a feeder for volume changing if channel returns -1 */ - c->volume = (left << 8) | right; + /* should add a feeder for volume changing if channel returns -1 */ + if (left > 100) + left = 100; + if (left < 0) + left = 0; + if (right > 100) + right = 100; + if (right < 0) + right = 0; + c->volume = left | (right << 8); return 0; } @@ -820,7 +963,10 @@ chn_tryspeed(struct pcm_channel *c, int speed) delta = -delta; c->feederflags &= ~(1 << FEEDER_RATE); - if (delta > 500) + /* + * Used to be 500. It was too big! + */ + if (delta > 25) c->feederflags |= 1 << FEEDER_RATE; else sndbuf_setspd(bs, sndbuf_getspd(b)); @@ -853,6 +999,11 @@ chn_tryspeed(struct pcm_channel *c, int speed) r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); DEB(kprintf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); out: + if (!r) + r = CHANNEL_SETFORMAT(c->methods, c->devinfo, + sndbuf_getfmt(b)); + if (!r) + sndbuf_setfmt(bs, c->format); DEB(kprintf("setspeed done, r = %d\n", r)); return r; } else @@ -910,40 +1061,87 @@ chn_setformat(struct pcm_channel *c, u_int32_t fmt) return r; } +/* + * given a bufsz value, round it to a power of 2 in the min-max range + * XXX only works if min and max are powers of 2 + */ +static int +round_bufsz(int bufsz, int min, int max) +{ + int tmp = min * 2; + + KASSERT((min & (min-1)) == 0, ("min %d must be power of 2\n", min)); + KASSERT((max & (max-1)) == 0, ("max %d must be power of 2\n", max)); + while (tmp <= bufsz) + tmp <<= 1; + tmp >>= 1; + if (tmp > max) + tmp = max; + return tmp; +} + +/* + * set the channel's blocksize both for soft and hard buffers. + * + * blksz should be a power of 2 between 2**4 and 2**16 -- it is useful + * that it has the same value for both bufsoft and bufhard. + * blksz == -1 computes values according to a target irq rate. + * blksz == 0 reuses previous values if available, otherwise + * behaves as for -1 + * + * blkcnt is set by the user, between 2 and (2**17)/blksz for bufsoft, + * but should be a power of 2 for bufhard to simplify life to low + * level drivers. + * Note, for the rec channel a large blkcnt is ok, + * but for the play channel we want blksz as small as possible to keep + * the delay small, because routines in the write path always try to + * keep bufhard full. + * + * Unless we have good reason to, use the values suggested by the caller. + */ int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) { struct snd_dbuf *b = c->bufhard; struct snd_dbuf *bs = c->bufsoft; - int bufsz, irqhz, tmp, ret; + int irqhz, ret, maxsz, maxsize, reqblksz; CHN_LOCKASSERT(c); - if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) + if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED)) { + KASSERT(sndbuf_getsize(bs) == 0 || + sndbuf_getsize(bs) >= sndbuf_getsize(b), + ("%s(%s): bufsoft size %d < bufhard size %d", __func__, + c->name, sndbuf_getsize(bs), sndbuf_getsize(b))); return EINVAL; + } + c->flags |= CHN_F_SETBLOCKSIZE; ret = 0; DEB(kprintf("%s(%d, %d)\n", __func__, blkcnt, blksz)); - if (blksz == 0 || blksz == -1) { - if (blksz == -1) + if (blksz == 0 || blksz == -1) { /* let the driver choose values */ + if (blksz == -1) /* delete previous values */ c->flags &= ~CHN_F_HAS_SIZE; - if (!(c->flags & CHN_F_HAS_SIZE)) { - blksz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / chn_targetirqrate; - tmp = 32; - while (tmp <= blksz) - tmp <<= 1; - tmp >>= 1; - blksz = tmp; + if (!(c->flags & CHN_F_HAS_SIZE)) { /* no previous value */ + /* + * compute a base blksz according to the target irq + * rate, then round to a suitable power of 2 + * in the range 16.. 2^17/2. + * Finally compute a suitable blkcnt. + */ + blksz = round_bufsz( (sndbuf_getbps(bs) * + sndbuf_getspd(bs)) / chn_targetirqrate, + 16, CHN_2NDBUFMAXSIZE / 2); blkcnt = CHN_2NDBUFMAXSIZE / blksz; - - RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); - RANGE(blkcnt, 2, CHN_2NDBUFMAXSIZE / blksz); - DEB(kprintf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz)); - } else { + } else { /* use previously defined value */ blkcnt = sndbuf_getblkcnt(bs); blksz = sndbuf_getblksz(bs); - DEB(kprintf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz)); } } else { + /* + * use supplied values if reasonable. Note that here we + * might have blksz which is not a power of 2 if the + * ioctl() to compute it allows such values. + */ ret = EINVAL; if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE)) goto out; @@ -951,49 +1149,103 @@ chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz) c->flags |= CHN_F_HAS_SIZE; } - bufsz = blkcnt * blksz; - - ret = ENOMEM; - if (sndbuf_remalloc(bs, blkcnt, blksz)) - goto out; - ret = 0; + reqblksz = blksz; + if (reqblksz < sndbuf_getbps(bs)) + reqblksz = sndbuf_getbps(bs); + if (reqblksz % sndbuf_getbps(bs)) + reqblksz -= reqblksz % sndbuf_getbps(bs); /* adjust for different hw format/speed */ - irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / sndbuf_getblksz(bs); - DEB(kprintf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); - RANGE(irqhz, 16, 512); - - sndbuf_setblksz(b, (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz); + /* + * Now compute the approx irq rate for the given (soft) blksz, + * reduce to the acceptable range and compute a corresponding blksz + * for the hard buffer. Then set the channel's blocksize and + * corresponding hardbuf value. The number of blocks used should + * be set by the device-specific routine. In fact, even the + * call to sndbuf_setblksz() should not be here! XXX + */ - /* round down to 2^x */ - blksz = 32; - while (blksz <= sndbuf_getblksz(b)) - blksz <<= 1; - blksz >>= 1; + irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz; + RANGE(irqhz, 16, 512); - /* round down to fit hw buffer size */ - RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); - DEB(kprintf("%s: hard blksz requested %d (maxsize %d), ", __func__, blksz, sndbuf_getmaxsize(b))); + maxsz = sndbuf_getmaxsize(b); + if (maxsz == 0) /* virtual channels don't appear to allocate bufhard */ + maxsz = CHN_2NDBUFMAXSIZE; + blksz = round_bufsz( (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz, + 16, maxsz / 2); + + /* Increase the size of bufsoft if before increasing bufhard. */ + maxsize = sndbuf_getsize(b); + if (sndbuf_getsize(bs) > maxsize) + maxsize = sndbuf_getsize(bs); + if (reqblksz * blkcnt > maxsize) + maxsize = reqblksz * blkcnt; + if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { + ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); + if (ret) + goto out1; + } + CHN_UNLOCK(c); sndbuf_setblksz(b, CHANNEL_SETBLOCKSIZE(c->methods, c->devinfo, blksz)); + CHN_LOCK(c); - irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); - DEB(kprintf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); + /* Decrease the size of bufsoft after decreasing bufhard. */ + maxsize = sndbuf_getsize(b); + if (reqblksz * blkcnt > maxsize) + maxsize = reqblksz * blkcnt; + if (maxsize > sndbuf_getsize(bs)) + kprintf("Danger! %s bufsoft size increasing from %d to %d after CHANNEL_SETBLOCKSIZE()\n", + c->name, sndbuf_getsize(bs), maxsize); + if (sndbuf_getsize(bs) != maxsize || sndbuf_getblksz(bs) != reqblksz) { + ret = sndbuf_remalloc(bs, maxsize/reqblksz, reqblksz); + if (ret) + goto out1; + } chn_resetbuf(c); +out1: + KASSERT(sndbuf_getsize(bs) == 0 || + sndbuf_getsize(bs) >= sndbuf_getsize(b), + ("%s(%s): bufsoft size %d < bufhard size %d, reqblksz=%d blksz=%d maxsize=%d blkcnt=%d", + __func__, c->name, sndbuf_getsize(bs), sndbuf_getsize(b), reqblksz, + blksz, maxsize, blkcnt)); out: + c->flags &= ~CHN_F_SETBLOCKSIZE; +#if 0 + if (1) { + static uint32_t kk = 0; + printf("%u: b %d/%d/%d : (%d)%d/0x%0x | bs %d/%d/%d : (%d)%d/0x%0x\n", ++kk, + sndbuf_getsize(b), sndbuf_getblksz(b), sndbuf_getblkcnt(b), + sndbuf_getbps(b), + sndbuf_getspd(b), sndbuf_getfmt(b), + sndbuf_getsize(bs), sndbuf_getblksz(bs), sndbuf_getblkcnt(bs), + sndbuf_getbps(bs), + sndbuf_getspd(bs), sndbuf_getfmt(bs)); + if (sndbuf_getsize(b) % sndbuf_getbps(b) || + sndbuf_getblksz(b) % sndbuf_getbps(b) || + sndbuf_getsize(bs) % sndbuf_getbps(bs) || + sndbuf_getblksz(b) % sndbuf_getbps(b)) { + printf("%u: bps/blksz alignment screwed!\n", kk); + } + } +#endif return ret; } int chn_trigger(struct pcm_channel *c, int go) { +#if NISA > 0 struct snd_dbuf *b = c->bufhard; +#endif int ret; CHN_LOCKASSERT(c); - if (ISA_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) - sndbuf_isadmabounce(b); +#if NISA > 0 + if (SND_DMA(b) && (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)) + sndbuf_dmabounce(b); +#endif ret = CHANNEL_TRIGGER(c->methods, c->devinfo, go); return ret; @@ -1002,6 +1254,7 @@ chn_trigger(struct pcm_channel *c, int go) int chn_getptr(struct pcm_channel *c) { +#if 0 int hwptr; int a = (1 << c->align) - 1; @@ -1013,6 +1266,12 @@ chn_getptr(struct pcm_channel *c) #endif hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */ return hwptr; +#endif + int hwptr; + + CHN_LOCKASSERT(c); + hwptr = (c->flags & CHN_F_TRIGGERED)? CHANNEL_GETPTR(c->methods, c->devinfo) : 0; + return (hwptr - (hwptr % sndbuf_getbps(c->bufhard))); } struct pcmchan_caps * @@ -1035,7 +1294,9 @@ chn_getformats(struct pcm_channel *c) /* report software-supported formats */ if (report_soft_formats) - fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U16_LE|AFMT_U16_BE| + fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| + AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| + AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; return fmts; @@ -1046,7 +1307,7 @@ chn_buildfeeder(struct pcm_channel *c) { struct feeder_class *fc; struct pcm_feederdesc desc; - u_int32_t tmp[2], type, flags, hwfmt; + u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; int err; CHN_LOCKASSERT(c); @@ -1067,8 +1328,13 @@ chn_buildfeeder(struct pcm_channel *c) } c->feeder->desc->out = c->format; } else { - desc.type = FEEDER_MIXER; - desc.in = 0; + if (c->flags & CHN_F_HAS_VCHAN) { + desc.type = FEEDER_MIXER; + desc.in = 0; + } else { + DEB(printf("can't decide which feeder type to use!\n")); + return EOPNOTSUPP; + } desc.out = c->format; desc.flags = 0; fc = feeder_getclass(&desc); @@ -1085,9 +1351,16 @@ chn_buildfeeder(struct pcm_channel *c) return err; } } + c->feederflags &= ~(1 << FEEDER_VOLUME); + if (c->direction == PCMDIR_PLAY && + !(c->flags & CHN_F_VIRTUAL) && + c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTVOL) && + c->parentsnddev->mixer_dev) + c->feederflags |= 1 << FEEDER_VOLUME; flags = c->feederflags; + fmtlist = chn_getcaps(c)->fmtlist; - DEB(kprintf("not mapped, feederflags %x\n", flags)); + DEB(kprintf("feederflags %x\n", flags)); for (type = FEEDER_RATE; type <= FEEDER_LAST; type++) { if (flags & (1 << type)) { @@ -1104,45 +1377,61 @@ chn_buildfeeder(struct pcm_channel *c) return EOPNOTSUPP; } - if (c->feeder->desc->out != fc->desc->in) { - DEB(kprintf("build fmtchain from %x to %x: ", c->feeder->desc->out, fc->desc->in)); - tmp[0] = fc->desc->in; - tmp[1] = 0; - if (chn_fmtchain(c, tmp) == 0) { - DEB(kprintf("failed\n")); + DEB(kprintf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); + tmp[0] = fc->desc->in; + tmp[1] = 0; + if (chn_fmtchain(c, tmp) == 0) { + DEB(printf("failed\n")); - return ENODEV; - } - DEB(kprintf("ok\n")); + return ENODEV; } + DEB(printf("ok\n")); err = chn_addfeeder(c, fc, fc->desc); if (err) { - DEB(kprintf("can't add feeder %p, output %x, err %d\n", fc, fc->desc->out, err)); + DEB(kprintf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); return err; } - DEB(kprintf("added feeder %p, output %x\n", fc, c->feeder->desc->out)); + DEB(kprintf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); } } - if (fmtvalid(c->feeder->desc->out, chn_getcaps(c)->fmtlist)) { - hwfmt = c->feeder->desc->out; - } else { - if (c->direction == PCMDIR_REC) { - tmp[0] = c->format; - tmp[1] = NULL; - hwfmt = chn_fmtchain(c, tmp); - } else { - hwfmt = chn_fmtchain(c, chn_getcaps(c)->fmtlist); - } - } + if (c->direction == PCMDIR_REC) { + tmp[0] = c->format; + tmp[1] = 0; + hwfmt = chn_fmtchain(c, tmp); + } else + hwfmt = chn_fmtchain(c, fmtlist); - if (hwfmt == 0) + if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { + DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt)); return ENODEV; + } sndbuf_setfmt(c->bufhard, hwfmt); + if ((flags & (1 << FEEDER_VOLUME))) { + struct dev_ioctl_args map; + int vol = 100 | (100 << 8); + + CHN_UNLOCK(c); + /* + * XXX This is ugly! The way mixer subs being so secretive + * about its own internals force us to use this silly + * monkey trick. + */ + map.a_head.a_dev = c->parentsnddev->mixer_dev; + map.a_cmd = MIXER_READ(SOUND_MIXER_PCM); + map.a_data = (caddr_t)&vol; + map.a_fflag = -1; + map.a_cred = NULL; + if (mixer_ioctl(&map) != 0) + device_printf(c->dev, "Soft Volume: Failed to read default value\n"); + CHN_LOCK(c); + chn_setvolume(c, vol & 0x7f, (vol >> 8) & 0x7f); + } + return 0; } @@ -1153,8 +1442,12 @@ chn_notify(struct pcm_channel *c, u_int32_t flags) struct pcm_channel *child; int run; - if (SLIST_EMPTY(&c->children)) + CHN_LOCK(c); + + if (SLIST_EMPTY(&c->children)) { + CHN_UNLOCK(c); return ENODEV; + } run = (c->flags & CHN_F_TRIGGERED)? 1 : 0; /* @@ -1190,8 +1483,10 @@ chn_notify(struct pcm_channel *c, u_int32_t flags) blksz = sndbuf_getmaxsize(c->bufhard) / 2; SLIST_FOREACH(pce, &c->children, link) { child = pce->channel; + CHN_LOCK(child); if (sndbuf_getblksz(child->bufhard) < blksz) blksz = sndbuf_getblksz(child->bufhard); + CHN_UNLOCK(child); } chn_setblocksize(c, 2, blksz); } @@ -1205,13 +1500,28 @@ chn_notify(struct pcm_channel *c, u_int32_t flags) nrun = 0; SLIST_FOREACH(pce, &c->children, link) { child = pce->channel; + CHN_LOCK(child); if (child->flags & CHN_F_TRIGGERED) nrun = 1; + CHN_UNLOCK(child); } if (nrun && !run) chn_start(c, 1); if (!nrun && run) chn_abort(c); } + CHN_UNLOCK(c); return 0; } + +void +chn_lock(struct pcm_channel *c) +{ + CHN_LOCK(c); +} + +void +chn_unlock(struct pcm_channel *c) +{ + CHN_UNLOCK(c); +} diff --git a/sys/dev/sound/pcm/channel.h b/sys/dev/sound/pcm/channel.h index a10a2ef9dc..8b80b98dbe 100644 --- a/sys/dev/sound/pcm/channel.h +++ b/sys/dev/sound/pcm/channel.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/channel.h,v 1.5.2.6 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/channel.h,v 1.4 2006/07/28 02:17:38 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/channel.h,v 1.31.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/channel.h,v 1.5 2007/01/04 21:47:03 corecode Exp $ */ struct pcmchan_children { @@ -63,7 +63,7 @@ struct pcm_channel { void *devinfo; device_t dev; char name[CHN_NAMELEN]; - void *lock; + struct spinlock *lock; SLIST_HEAD(, pcmchan_children) children; }; @@ -75,9 +75,9 @@ int chn_read(struct pcm_channel *c, struct uio *buf, int ioflags); u_int32_t chn_start(struct pcm_channel *c, int force); int chn_sync(struct pcm_channel *c, int threshold); int chn_flush(struct pcm_channel *c); -int chn_poll(struct pcm_channel *c, int ev); +int chn_poll(struct pcm_channel *c, int ev, struct thread *td); -int chn_init(struct pcm_channel *c, void *devinfo, int dir); +int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction); int chn_kill(struct pcm_channel *c); int chn_setdir(struct pcm_channel *c, int dir); int chn_reset(struct pcm_channel *c, u_int32_t fmt); @@ -100,10 +100,12 @@ void chn_wrupdate(struct pcm_channel *c); void chn_rdupdate(struct pcm_channel *c); int chn_notify(struct pcm_channel *c, u_int32_t flags); +void chn_lock(struct pcm_channel *c); +void chn_unlock(struct pcm_channel *c); #ifdef USING_MUTEX -#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock)) -#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock)) +#define CHN_LOCK(c) spin_lock_wr((struct spinlock *)((c)->lock)) +#define CHN_UNLOCK(c) spin_unlock_wr((struct spinlock *)((c)->lock)) #define CHN_LOCKASSERT(c) #else #define CHN_LOCK(c) @@ -135,10 +137,14 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); #define CHN_F_MAPPED 0x00010000 /* has been mmap()ed */ #define CHN_F_DEAD 0x00020000 #define CHN_F_BADSETTING 0x00040000 +#define CHN_F_SETBLOCKSIZE 0x00080000 +#define CHN_F_HAS_VCHAN 0x00100000 #define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */ -#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | CHN_F_VIRTUAL) +#define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \ + CHN_F_HAS_VCHAN | CHN_F_VIRTUAL) + #define CHN_N_RATE 0x00000001 #define CHN_N_FORMAT 0x00000002 diff --git a/sys/dev/sound/pcm/channel_if.m b/sys/dev/sound/pcm/channel_if.m index c7e326cde5..cf29566f69 100644 --- a/sys/dev/sound/pcm/channel_if.m +++ b/sys/dev/sound/pcm/channel_if.m @@ -1,3 +1,4 @@ +#- # KOBJ # # Copyright (c) 2000 Cameron Grant @@ -24,8 +25,8 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $FreeBSD: src/sys/dev/sound/pcm/channel_if.m,v 1.1.2.3 2002/04/22 15:49:36 cg Exp $ -# $DragonFly: src/sys/dev/sound/pcm/channel_if.m,v 1.2 2003/06/17 04:28:31 dillon Exp $ +# $FreeBSD: src/sys/dev/sound/pcm/channel_if.m,v 1.5 2005/01/06 01:43:20 imp Exp $ +# $DragonFly: src/sys/dev/sound/pcm/channel_if.m,v 1.3 2007/01/04 21:47:03 corecode Exp $ # #include diff --git a/sys/dev/sound/pcm/dsp.c b/sys/dev/sound/pcm/dsp.c index f2feadf208..66c61eaa1e 100644 --- a/sys/dev/sound/pcm/dsp.c +++ b/sys/dev/sound/pcm/dsp.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.15.2.13 2002/08/30 13:53:03 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.13 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.80.2.6 2006/04/04 17:43:48 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.14 2007/01/04 21:47:03 corecode Exp $ */ #include @@ -32,7 +32,7 @@ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.13 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/dsp.c,v 1.14 2007/01/04 21:47:03 corecode Exp $"); #define OLDPCM_IOCTL @@ -44,8 +44,9 @@ static d_ioctl_t dsp_ioctl; static d_poll_t dsp_poll; static d_mmap_t dsp_mmap; -static struct dev_ops dsp_ops = { - { "dsp", SND_CDEV_MAJOR, 0 }, +struct dev_ops dsp_cdevsw = { + { "dsp", SND_CDEV_MAJOR, 0}, + /*.d_flags = D_NEEDGIANT,*/ .d_open = dsp_open, .d_close = dsp_close, .d_read = dsp_read, @@ -60,7 +61,7 @@ static eventhandler_tag dsp_ehtag; #endif static struct snddev_info * -dsp_get_info(cdev_t dev) +dsp_get_info(struct cdev *dev) { struct snddev_info *d; int unit; @@ -74,7 +75,7 @@ dsp_get_info(cdev_t dev) } static u_int32_t -dsp_get_flags(cdev_t dev) +dsp_get_flags(struct cdev *dev) { device_t bdev; int unit; @@ -88,7 +89,7 @@ dsp_get_flags(cdev_t dev) } static void -dsp_set_flags(cdev_t dev, u_int32_t flags) +dsp_set_flags(struct cdev *dev, u_int32_t flags) { device_t bdev; int unit; @@ -102,21 +103,21 @@ dsp_set_flags(cdev_t dev, u_int32_t flags) } /* - * return the channels channels associated with an open device instance. + * return the channels associated with an open device instance. * set the priority if the device is simplex and one direction (only) is * specified. * lock channels specified. */ static int -getchns(cdev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio) +getchns(struct cdev *dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int32_t prio) { struct snddev_info *d; u_int32_t flags; flags = dsp_get_flags(dev); d = dsp_get_info(dev); - pcm_lock(d); pcm_inprog(d, 1); + pcm_lock(d); KASSERT((flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \ ("getchns: read and write both prioritised")); @@ -152,7 +153,7 @@ getchns(cdev_t dev, struct pcm_channel **rdch, struct pcm_channel **wrch, u_int3 /* unlock specified channels */ static void -relchns(cdev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio) +relchns(struct cdev *dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_t prio) { struct snddev_info *d; @@ -161,23 +162,31 @@ relchns(cdev_t dev, struct pcm_channel *rdch, struct pcm_channel *wrch, u_int32_ CHN_UNLOCK(wrch); if (rdch && rdch != pcm_getfakechan(d) && (prio & SD_F_PRIO_RD)) CHN_UNLOCK(rdch); - pcm_lock(d); pcm_inprog(d, -1); - pcm_unlock(d); } static int dsp_open(struct dev_open_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; + struct thread *td = curthread; + int flags = ap->a_oflags; struct pcm_channel *rdch, *wrch; struct snddev_info *d; u_int32_t fmt; int devtype; + int error; + int chnum; - crit_enter(); - d = dsp_get_info(dev); - devtype = PCMDEV(dev); + if (i_dev == NULL || td == NULL) + return ENODEV; + + if ((flags & (FREAD | FWRITE)) == 0) + return EINVAL; + + d = dsp_get_info(i_dev); + devtype = PCMDEV(i_dev); + chnum = -1; /* decide default format */ switch (devtype) { @@ -199,10 +208,9 @@ dsp_open(struct dev_open_args *ap) case SND_DEV_DSPREC: fmt = AFMT_U8; - if (ap->a_oflags & FWRITE) { - crit_exit(); + if (flags & FWRITE) return EINVAL; - } + chnum = PCMCHAN(i_dev); break; default: @@ -212,233 +220,205 @@ dsp_open(struct dev_open_args *ap) /* lock snddev so nobody else can monkey with it */ pcm_lock(d); - rdch = dev->si_drv1; - wrch = dev->si_drv2; + rdch = i_dev->si_drv1; + wrch = i_dev->si_drv2; - if ((dsp_get_flags(dev) & SD_F_SIMPLEX) && (rdch || wrch)) { - /* simplex device, already open, exit */ + if (rdch || wrch || ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && + (flags & (FREAD | FWRITE)) == (FREAD | FWRITE))) { + /* simplex or not, better safe than sorry. */ pcm_unlock(d); - crit_exit(); return EBUSY; } - if (((ap->a_oflags & FREAD) && rdch) || - ((ap->a_oflags & FWRITE) && wrch)) { - /* device already open in one or both directions */ + /* + * if we get here, the open request is valid- either: + * * we were previously not open + * * we were open for play xor record and the opener wants + * the non-open direction + */ + if (flags & FREAD) { + /* open for read */ pcm_unlock(d); - crit_exit(); - return EBUSY; - } + error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, chnum); + if (error != 0 && error != EBUSY && chnum != -1 && (flags & FWRITE)) + error = pcm_chnalloc(d, &rdch, PCMDIR_REC, td->td_proc->p_pid, -1); - /* if we get here, the open request is valid */ - if (ap->a_oflags & FREAD) { - /* open for read */ - if (devtype == SND_DEV_DSPREC) - rdch = pcm_chnalloc(d, PCMDIR_REC, curproc->p_pid, PCMCHAN(dev)); - else - rdch = pcm_chnalloc(d, PCMDIR_REC, curproc->p_pid, -1); - if (!rdch) { - /* no channel available, exit */ - pcm_unlock(d); - crit_exit(); - return EBUSY; - } - /* got a channel, already locked for us */ - } + if (error == 0 && (chn_reset(rdch, fmt) || + (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED)))) + error = ENODEV; - if (ap->a_oflags & FWRITE) { - /* open for write */ - wrch = pcm_chnalloc(d, PCMDIR_PLAY, curproc->p_pid, -1); - if (!wrch) { - /* no channel available */ - if (rdch && (ap->a_oflags & FREAD)) { - /* just opened a read channel, release it */ + if (error != 0) { + if (rdch) pcm_chnrelease(rdch); - } - /* exit */ - pcm_unlock(d); - crit_exit(); - return EBUSY; + return error; } - /* got a channel, already locked for us */ - } - - dev->si_drv1 = rdch; - dev->si_drv2 = wrch; - pcm_unlock(d); - /* finished with snddev, new channels still locked */ - /* bump refcounts, reset and unlock any channels that we just opened */ - if (ap->a_oflags & FREAD) { - if (chn_reset(rdch, fmt)) { - pcm_lock(d); - pcm_chnrelease(rdch); - if (wrch && (ap->a_oflags & FWRITE)) - pcm_chnrelease(wrch); - pcm_unlock(d); - crit_exit(); - return ENODEV; - } -#if 0 - /* removed, will be passed as an IO_ flag */ - if (ap->a_oflags & O_NONBLOCK) + if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; -#endif pcm_chnref(rdch, 1); CHN_UNLOCK(rdch); + pcm_lock(d); } - if (ap->a_oflags & FWRITE) { - if (chn_reset(wrch, fmt)) { - pcm_lock(d); - pcm_chnrelease(wrch); - if (ap->a_oflags & FREAD) { - CHN_LOCK(rdch); - pcm_chnref(rdch, -1); - pcm_chnrelease(rdch); - CHN_UNLOCK(rdch); - } - pcm_unlock(d); - crit_exit(); - return ENODEV; + + if (flags & FWRITE) { + /* open for write */ + pcm_unlock(d); + error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, chnum); + if (error != 0 && error != EBUSY && chnum != -1 && (flags & FREAD)) + error = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, td->td_proc->p_pid, -1); + + if (error == 0 && (chn_reset(wrch, fmt) || + (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))) + error = ENODEV; + + if (error != 0) { + if (wrch) + pcm_chnrelease(wrch); + if (rdch) { + /* + * Lock, deref and release previously created record channel + */ + CHN_LOCK(rdch); + pcm_chnref(rdch, -1); + pcm_chnrelease(rdch); } -#if 0 - /* removed, will be passed as an IO_ flag */ - if (ap->a_oflags & O_NONBLOCK) - wrch->flags |= CHN_F_NBIO; -#endif - pcm_chnref(wrch, 1); - CHN_UNLOCK(wrch); + + return error; + } + + if (flags & O_NONBLOCK) + wrch->flags |= CHN_F_NBIO; + pcm_chnref(wrch, 1); + CHN_UNLOCK(wrch); + pcm_lock(d); } - crit_exit(); + + i_dev->si_drv1 = rdch; + i_dev->si_drv2 = wrch; + + pcm_unlock(d); return 0; } static int dsp_close(struct dev_close_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; struct pcm_channel *rdch, *wrch; struct snddev_info *d; - int exit; + int refs; - crit_enter(); - d = dsp_get_info(dev); + d = dsp_get_info(i_dev); pcm_lock(d); - rdch = dev->si_drv1; - wrch = dev->si_drv2; - - exit = 0; + rdch = i_dev->si_drv1; + wrch = i_dev->si_drv2; + pcm_unlock(d); - /* decrement refcount for each channel, exit if nonzero */ - if (rdch) { - CHN_LOCK(rdch); - if (pcm_chnref(rdch, -1) > 0) { - CHN_UNLOCK(rdch); - exit = 1; + if (rdch || wrch) { + refs = 0; + if (rdch) { + CHN_LOCK(rdch); + refs += pcm_chnref(rdch, -1); + chn_abort(rdch); /* won't sleep */ + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + chn_reset(rdch, 0); + pcm_chnrelease(rdch); } - } - if (wrch) { - CHN_LOCK(wrch); - if (pcm_chnref(wrch, -1) > 0) { - CHN_UNLOCK(wrch); - exit = 1; + if (wrch) { + CHN_LOCK(wrch); + refs += pcm_chnref(wrch, -1); + /* + * XXX: Maybe the right behaviour is to abort on non_block. + * It seems that mplayer flushes the audio queue by quickly + * closing and re-opening. In FBSD, there's a long pause + * while the audio queue flushes that I presume isn't there in + * linux. + */ + chn_flush(wrch); /* may sleep */ + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); + chn_reset(wrch, 0); + pcm_chnrelease(wrch); } - } - if (exit) { - pcm_unlock(d); - crit_exit(); - return 0; - } - - /* both refcounts are zero, abort and release */ - - if (pcm_getfakechan(d)) - pcm_getfakechan(d)->flags = 0; - - dev->si_drv1 = NULL; - dev->si_drv2 = NULL; - - dsp_set_flags(dev, dsp_get_flags(dev) & ~SD_F_TRANSIENT); - pcm_unlock(d); - if (rdch) { - chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(rdch, 0); - pcm_chnrelease(rdch); - } - if (wrch) { - chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(wrch, 0); - pcm_chnrelease(wrch); + pcm_lock(d); + if (rdch) + i_dev->si_drv1 = NULL; + if (wrch) + i_dev->si_drv2 = NULL; + /* + * If there are no more references, release the channels. + */ + if (refs == 0 && i_dev->si_drv1 == NULL && + i_dev->si_drv2 == NULL) { + if (pcm_getfakechan(d)) + pcm_getfakechan(d)->flags = 0; + /* What is this?!? */ + dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); + } + pcm_unlock(d); } - - crit_exit(); return 0; } static int dsp_read(struct dev_read_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; + struct uio *buf = ap->a_uio; + int flag = ap->a_ioflag; struct pcm_channel *rdch, *wrch; int ret; - crit_enter(); - getchns(dev, &rdch, &wrch, SD_F_PRIO_RD); + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD); 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)) { - relchns(dev, rdch, wrch, SD_F_PRIO_RD); - crit_exit(); + relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); return EINVAL; } if (!(rdch->flags & CHN_F_RUNNING)) rdch->flags |= CHN_F_RUNNING; - ret = chn_read(rdch, ap->a_uio, ap->a_ioflag); - relchns(dev, rdch, wrch, SD_F_PRIO_RD); + ret = chn_read(rdch, buf, flag); + relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); - crit_exit(); return ret; } static int dsp_write(struct dev_write_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; + struct uio *buf = ap->a_uio; + int flag = ap->a_ioflag; struct pcm_channel *rdch, *wrch; int ret; - crit_enter(); - getchns(dev, &rdch, &wrch, SD_F_PRIO_WR); + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR); 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)) { - relchns(dev, rdch, wrch, SD_F_PRIO_WR); - crit_exit(); + relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); return EINVAL; } if (!(wrch->flags & CHN_F_RUNNING)) wrch->flags |= CHN_F_RUNNING; - ret = chn_write(wrch, ap->a_uio, ap->a_ioflag); - relchns(dev, rdch, wrch, SD_F_PRIO_WR); + ret = chn_write(wrch, buf, flag); + relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); - crit_exit(); return ret; } static int dsp_ioctl(struct dev_ioctl_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; u_long cmd = ap->a_cmd; caddr_t arg = ap->a_data; - struct pcm_channel *wrch, *rdch; + struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; int kill; int ret = 0, *arg_i = (int *)arg, tmp; @@ -448,18 +428,20 @@ dsp_ioctl(struct dev_ioctl_args *ap) * on dsp devices. */ + d = dsp_get_info(i_dev); if (IOCGROUP(cmd) == 'M') { - cdev_t pdev; - - pdev = make_adhoc_dev(&dsp_ops, - PCMMKMINOR(PCMUNIT(dev), SND_DEV_CTL, 0)); - ap->a_head.a_dev = pdev; - return mixer_ioctl(ap); + /* + * This is at least, a bug to bug compatible with OSS. + */ + if (d->mixer_dev != NULL) { + ap->a_head.a_dev = d->mixer_dev; + return mixer_ioctl(ap); + } else { + return EBADF; + } } - crit_enter(); - d = dsp_get_info(dev); - getchns(dev, &rdch, &wrch, 0); + getchns(i_dev, &rdch, &wrch, 0); kill = 0; if (wrch && (wrch->flags & CHN_F_DEAD)) @@ -467,26 +449,32 @@ dsp_ioctl(struct dev_ioctl_args *ap) if (rdch && (rdch->flags & CHN_F_DEAD)) kill |= 2; if (kill == 3) { - relchns(dev, rdch, wrch, 0); - crit_exit(); + relchns(i_dev, rdch, wrch, 0); return EINVAL; } if (kill & 1) wrch = NULL; if (kill & 2) rdch = NULL; - + switch(cmd) { #ifdef OLDPCM_IOCTL /* * we start with the new ioctl interface. */ case AIONWRITE: /* how many bytes can write ? */ + if (wrch) { + CHN_LOCK(wrch); /* if (wrch && wrch->bufhard.dl) while (chn_wrfeed(wrch) == 0); */ - *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0; + *arg_i = sndbuf_getfree(wrch->bufsoft); + CHN_UNLOCK(wrch); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case AIOSSIZE: /* set the current blocksize */ @@ -513,48 +501,64 @@ dsp_ioctl(struct dev_ioctl_args *ap) { struct snd_size *p = (struct snd_size *)arg; - if (wrch) + if (wrch) { + CHN_LOCK(wrch); p->play_size = sndbuf_getblksz(wrch->bufsoft); - if (rdch) + CHN_UNLOCK(wrch); + } + if (rdch) { + CHN_LOCK(rdch); p->rec_size = sndbuf_getblksz(rdch->bufsoft); + CHN_UNLOCK(rdch); + } } break; case AIOSFMT: + case AIOGFMT: { snd_chan_param *p = (snd_chan_param *)arg; + if (cmd == AIOSFMT && + ((p->play_format != 0 && p->play_rate == 0) || + (p->rec_format != 0 && p->rec_rate == 0))) { + ret = EINVAL; + break; + } if (wrch) { CHN_LOCK(wrch); - chn_setformat(wrch, p->play_format); - chn_setspeed(wrch, p->play_rate); + if (cmd == AIOSFMT && p->play_format != 0) { + chn_setformat(wrch, p->play_format); + chn_setspeed(wrch, p->play_rate); + } + p->play_rate = wrch->speed; + p->play_format = wrch->format; CHN_UNLOCK(wrch); + } else { + p->play_rate = 0; + p->play_format = 0; } if (rdch) { CHN_LOCK(rdch); - chn_setformat(rdch, p->rec_format); - chn_setspeed(rdch, p->rec_rate); + if (cmd == AIOSFMT && p->rec_format != 0) { + chn_setformat(rdch, p->rec_format); + chn_setspeed(rdch, p->rec_rate); + } + p->rec_rate = rdch->speed; + p->rec_format = rdch->format; CHN_UNLOCK(rdch); + } else { + p->rec_rate = 0; + p->rec_format = 0; } } - /* 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; struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; - cdev_t pdev; + struct cdev *pdev; if (rdch) { CHN_LOCK(rdch); @@ -574,24 +578,28 @@ dsp_ioctl(struct dev_ioctl_args *ap) p->formats = (rdch? chn_getformats(rdch) : 0xffffffff) & (wrch? chn_getformats(wrch) : 0xffffffff); if (rdch && wrch) - p->formats |= (dsp_get_flags(dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; - pdev = make_adhoc_dev(&dsp_ops, PCMMKMINOR(PCMUNIT(dev), SND_DEV_CTL, 0)); + p->formats |= (dsp_get_flags(i_dev) & SD_F_SIMPLEX)? 0 : AFMT_FULLDUPLEX; + pdev = d->mixer_dev; p->mixers = 1; /* default: one mixer */ p->inputs = pdev->si_drv1? mix_getdevs(pdev->si_drv1) : 0; p->left = p->right = 100; - if (wrch) - CHN_UNLOCK(wrch); if (rdch) CHN_UNLOCK(rdch); + if (wrch) + CHN_UNLOCK(wrch); } break; case AIOSTOP: - if (*arg_i == AIOSYNC_PLAY && wrch) + if (*arg_i == AIOSYNC_PLAY && wrch) { + CHN_LOCK(wrch); *arg_i = chn_abort(wrch); - else if (*arg_i == AIOSYNC_CAPTURE && rdch) + CHN_UNLOCK(wrch); + } else if (*arg_i == AIOSYNC_CAPTURE && rdch) { + CHN_LOCK(rdch); *arg_i = chn_abort(rdch); - else { + CHN_UNLOCK(rdch); + } else { kprintf("AIOSTOP: bad channel 0x%x\n", *arg_i); *arg_i = 0; } @@ -606,9 +614,17 @@ dsp_ioctl(struct dev_ioctl_args *ap) * here follow the standard ioctls (filio.h etc.) */ case FIONREAD: /* get # bytes to read */ -/* if (rdch && rdch->bufhard.dl) - while (chn_rdfeed(rdch) == 0); -*/ *arg_i = rdch? sndbuf_getready(rdch->bufsoft) : 0; + if (rdch) { + CHN_LOCK(rdch); +/* if (rdch && rdch->bufhard.dl) + while (chn_rdfeed(rdch) == 0); +*/ + *arg_i = sndbuf_getready(rdch->bufsoft); + CHN_UNLOCK(rdch); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case FIOASYNC: /*set/clear async i/o */ @@ -616,19 +632,22 @@ dsp_ioctl(struct dev_ioctl_args *ap) break; case SNDCTL_DSP_NONBLOCK: - /* - * set/clear non-blocking I/O. WARNING: non-blocking I/O - * can also be set with FIONBIO - */ - if (rdch) - rdch->flags &= ~CHN_F_NBIO; - if (wrch) - wrch->flags &= ~CHN_F_NBIO; - if (*arg_i) { - if (rdch) + case FIONBIO: /* set/clear non-blocking i/o */ + if (rdch) { + CHN_LOCK(rdch); + if (*arg_i) rdch->flags |= CHN_F_NBIO; - if (wrch) + else + rdch->flags &= ~CHN_F_NBIO; + CHN_UNLOCK(rdch); + } + if (wrch) { + CHN_LOCK(wrch); + if (*arg_i) wrch->flags |= CHN_F_NBIO; + else + wrch->flags &= ~CHN_F_NBIO; + CHN_UNLOCK(wrch); } break; @@ -638,12 +657,15 @@ dsp_ioctl(struct dev_ioctl_args *ap) #define THE_REAL_SNDCTL_DSP_GETBLKSIZE _IOWR('P', 4, int) case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: - if (wrch) - *arg_i = sndbuf_getblksz(wrch->bufsoft); - else if (rdch) - *arg_i = sndbuf_getblksz(rdch->bufsoft); - else + chn = wrch ? wrch : rdch; + if (chn) { + CHN_LOCK(chn); + *arg_i = sndbuf_getblksz(chn->bufsoft); + CHN_UNLOCK(chn); + } else { *arg_i = 0; + ret = EINVAL; + } break ; case SNDCTL_DSP_SETBLKSIZE: @@ -662,10 +684,18 @@ dsp_ioctl(struct dev_ioctl_args *ap) case SNDCTL_DSP_RESET: DEB(kprintf("dsp reset\n")); - if (wrch) + if (wrch) { + CHN_LOCK(wrch); chn_abort(wrch); - if (rdch) + chn_resetbuf(wrch); + CHN_UNLOCK(wrch); + } + if (rdch) { + CHN_LOCK(rdch); chn_abort(rdch); + chn_resetbuf(rdch); + CHN_UNLOCK(rdch); + } break; case SNDCTL_DSP_SYNC: @@ -698,7 +728,15 @@ dsp_ioctl(struct dev_ioctl_args *ap) break; case SOUND_PCM_READ_RATE: - *arg_i = wrch? wrch->speed : rdch->speed; + chn = wrch ? wrch : rdch; + if (chn) { + CHN_LOCK(chn); + *arg_i = chn->speed; + CHN_UNLOCK(chn); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case SNDCTL_DSP_STEREO: @@ -740,20 +778,38 @@ dsp_ioctl(struct dev_ioctl_args *ap) } *arg_i = tmp; } else { - *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1; + chn = wrch ? wrch : rdch; + CHN_LOCK(chn); + *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + CHN_UNLOCK(chn); } break; case SOUND_PCM_READ_CHANNELS: - *arg_i = ((wrch? wrch->format : rdch->format) & AFMT_STEREO)? 2 : 1; + chn = wrch ? wrch : rdch; + if (chn) { + CHN_LOCK(chn); + *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + CHN_UNLOCK(chn); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case SNDCTL_DSP_GETFMTS: /* returns a mask of supported fmts */ - *arg_i = wrch? chn_getformats(wrch) : chn_getformats(rdch); + chn = wrch ? wrch : rdch; + if (chn) { + CHN_LOCK(chn); + *arg_i = chn_getformats(chn); + CHN_UNLOCK(chn); + } else { + *arg_i = 0; + ret = EINVAL; + } break ; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ - /* XXX locking */ if ((*arg_i != AFMT_QUERY)) { tmp = 0; if (wrch) { @@ -770,27 +826,29 @@ dsp_ioctl(struct dev_ioctl_args *ap) CHN_UNLOCK(rdch); } *arg_i = tmp; - } else - *arg_i = (wrch? wrch->format : rdch->format) & ~AFMT_STEREO; + } else { + chn = wrch ? wrch : rdch; + CHN_LOCK(chn); + *arg_i = chn->format & ~AFMT_STEREO; + CHN_UNLOCK(chn); + } break; case SNDCTL_DSP_SETFRAGMENT: - /* XXX locking */ DEB(kprintf("SNDCTL_DSP_SETFRAGMENT 0x%08x\n", *(int *)arg)); { u_int32_t fragln = (*arg_i) & 0x0000ffff; u_int32_t maxfrags = ((*arg_i) & 0xffff0000) >> 16; u_int32_t fragsz; + u_int32_t r_maxfrags, r_fragsz; RANGE(fragln, 4, 16); fragsz = 1 << fragln; if (maxfrags == 0) maxfrags = CHN_2NDBUFMAXSIZE / fragsz; - if (maxfrags < 2) { - ret = EINVAL; - break; - } + if (maxfrags < 2) + maxfrags = 2; if (maxfrags * fragsz > CHN_2NDBUFMAXSIZE) maxfrags = CHN_2NDBUFMAXSIZE / fragsz; @@ -798,9 +856,12 @@ dsp_ioctl(struct dev_ioctl_args *ap) if (rdch) { CHN_LOCK(rdch); ret = chn_setblocksize(rdch, maxfrags, fragsz); - maxfrags = sndbuf_getblkcnt(rdch->bufsoft); - fragsz = sndbuf_getblksz(rdch->bufsoft); + r_maxfrags = sndbuf_getblkcnt(rdch->bufsoft); + r_fragsz = sndbuf_getblksz(rdch->bufsoft); CHN_UNLOCK(rdch); + } else { + r_maxfrags = maxfrags; + r_fragsz = fragsz; } if (wrch && ret == 0) { CHN_LOCK(wrch); @@ -808,6 +869,9 @@ dsp_ioctl(struct dev_ioctl_args *ap) maxfrags = sndbuf_getblkcnt(wrch->bufsoft); fragsz = sndbuf_getblksz(wrch->bufsoft); CHN_UNLOCK(wrch); + } else { /* use whatever came from the read channel */ + maxfrags = r_maxfrags; + fragsz = r_fragsz; } fragln = 0; @@ -844,7 +908,7 @@ dsp_ioctl(struct dev_ioctl_args *ap) struct snd_dbuf *bs = wrch->bufsoft; CHN_LOCK(wrch); - chn_wrupdate(wrch); + /* XXX abusive DMA update: chn_wrupdate(wrch); */ a->bytes = sndbuf_getfree(bs); a->fragments = a->bytes / sndbuf_getblksz(bs); a->fragstotal = sndbuf_getblkcnt(bs); @@ -861,7 +925,7 @@ dsp_ioctl(struct dev_ioctl_args *ap) struct snd_dbuf *bs = rdch->bufsoft; CHN_LOCK(rdch); - chn_rdupdate(rdch); + /* XXX abusive DMA update: chn_rdupdate(rdch); */ a->bytes = sndbuf_gettotal(bs); a->blocks = sndbuf_getblocks(bs) - rdch->blocks; a->ptr = sndbuf_getreadyptr(bs); @@ -879,7 +943,7 @@ dsp_ioctl(struct dev_ioctl_args *ap) struct snd_dbuf *bs = wrch->bufsoft; CHN_LOCK(wrch); - chn_wrupdate(wrch); + /* XXX abusive DMA update: chn_wrupdate(wrch); */ a->bytes = sndbuf_gettotal(bs); a->blocks = sndbuf_getblocks(bs) - wrch->blocks; a->ptr = sndbuf_getreadyptr(bs); @@ -892,12 +956,29 @@ dsp_ioctl(struct dev_ioctl_args *ap) case SNDCTL_DSP_GETCAPS: *arg_i = DSP_CAP_REALTIME | DSP_CAP_MMAP | DSP_CAP_TRIGGER; - if (rdch && wrch && !(dsp_get_flags(dev) & SD_F_SIMPLEX)) + if (rdch && wrch && !(dsp_get_flags(i_dev) & 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; + chn = wrch ? wrch : rdch; + if (chn) { + CHN_LOCK(chn); + if (chn->format & AFMT_8BIT) + *arg_i = 8; + else if (chn->format & AFMT_16BIT) + *arg_i = 16; + else if (chn->format & AFMT_24BIT) + *arg_i = 24; + else if (chn->format & AFMT_32BIT) + *arg_i = 32; + else + ret = EINVAL; + CHN_UNLOCK(chn); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case SNDCTL_DSP_SETTRIGGER: @@ -917,16 +998,24 @@ dsp_ioctl(struct dev_ioctl_args *ap) chn_start(wrch, 1); else wrch->flags |= CHN_F_NOTRIGGER; - CHN_UNLOCK(wrch); + CHN_UNLOCK(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; + if (wrch) { + CHN_LOCK(wrch); + if (wrch->flags & CHN_F_TRIGGERED) + *arg_i |= PCM_ENABLE_OUTPUT; + CHN_UNLOCK(wrch); + } + if (rdch) { + CHN_LOCK(rdch); + if (rdch->flags & CHN_F_TRIGGERED) + *arg_i |= PCM_ENABLE_INPUT; + CHN_UNLOCK(rdch); + } break; case SNDCTL_DSP_GETODELAY: @@ -935,7 +1024,7 @@ dsp_ioctl(struct dev_ioctl_args *ap) struct snd_dbuf *bs = wrch->bufsoft; CHN_LOCK(wrch); - chn_wrupdate(wrch); + /* XXX abusive DMA update: chn_wrupdate(wrch); */ *arg_i = sndbuf_getready(b) + sndbuf_getready(bs); CHN_UNLOCK(wrch); } else @@ -951,6 +1040,15 @@ dsp_ioctl(struct dev_ioctl_args *ap) } break; + case SNDCTL_DSP_SETDUPLEX: + /* + * switch to full-duplex mode if card is in half-duplex + * mode and is able to work in full-duplex mode + */ + if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) + dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); + break; + case SNDCTL_DSP_MAPINBUF: case SNDCTL_DSP_MAPOUTBUF: case SNDCTL_DSP_SETSYNCRO: @@ -960,40 +1058,40 @@ dsp_ioctl(struct dev_ioctl_args *ap) case SOUND_PCM_WRITE_FILTER: case SOUND_PCM_READ_FILTER: /* dunno what these do, don't sound important */ + default: DEB(kprintf("default ioctl fn 0x%08lx fail\n", cmd)); ret = EINVAL; break; } - relchns(dev, rdch, wrch, 0); - crit_exit(); + relchns(i_dev, rdch, wrch, 0); return ret; } static int dsp_poll(struct dev_poll_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; + int events = ap->a_events; + struct thread *td = curthread; struct pcm_channel *wrch = NULL, *rdch = NULL; int ret, e; - crit_enter(); ret = 0; - getchns(dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); if (wrch) { - e = (ap->a_events & (POLLOUT | POLLWRNORM)); + e = (events & (POLLOUT | POLLWRNORM)); if (e) - ret |= chn_poll(wrch, e); + ret |= chn_poll(wrch, e, td); } if (rdch) { - e = (ap->a_events & (POLLIN | POLLRDNORM)); + e = (events & (POLLIN | POLLRDNORM)); if (e) - ret |= chn_poll(rdch, e); + ret |= chn_poll(rdch, e, td); } - relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); + relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - crit_exit(); ap->a_events = ret; return (0); } @@ -1001,119 +1099,73 @@ dsp_poll(struct dev_poll_args *ap) static int dsp_mmap(struct dev_mmap_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; + vm_offset_t offset = ap->a_offset; + int nprot = ap->a_nprot; struct pcm_channel *wrch = NULL, *rdch = NULL, *c; - int ret; - if (ap->a_nprot & PROT_EXEC) - return EINVAL; + if (nprot & PROT_EXEC) + return -1; - crit_enter(); - getchns(dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); + getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); #if 0 /* * XXX the linux api uses the nprot to select read/write buffer * our vm system doesn't allow this, so force write buffer */ - if (wrch && (ap->a_nprot & PROT_WRITE)) { + if (wrch && (nprot & PROT_WRITE)) { c = wrch; - } else if (rdch && (ap->a_nprot & PROT_READ)) { + } else if (rdch && (nprot & PROT_READ)) { c = rdch; } else { - crit_exit(); - return EINVAL; + return -1; } #else c = wrch; #endif if (c == NULL) { - relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - crit_exit(); - return EINVAL; + relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); + return -1; } - if (ap->a_offset >= sndbuf_getsize(c->bufsoft)) { - relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - crit_exit(); - return EINVAL; + if (offset >= sndbuf_getsize(c->bufsoft)) { + relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); + return -1; } if (!(c->flags & CHN_F_MAPPED)) c->flags |= CHN_F_MAPPED; - ret = atop(vtophys(sndbuf_getbufofs(c->bufsoft, ap->a_offset))); - relchns(dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - crit_exit(); - ap->a_result = ret; - return (0); -} + ap->a_result = vtophys(sndbuf_getbufofs(c->bufsoft, offset)); + relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); -int -dsp_register(int unit, int channel) -{ - dev_ops_add(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0)); - dev_ops_add(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0)); - dev_ops_add(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); - dev_ops_add(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0)); - make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_DSP, channel), - UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", unit, channel); - make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_DSP16, channel), - UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", unit, channel); - make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_AUDIO, channel), - UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", unit, channel); - - return 0; -} - -int -dsp_registerrec(int unit, int channel) -{ - dev_ops_add(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0)); - make_dev(&dsp_ops, PCMMKMINOR(unit, SND_DEV_DSPREC, channel), - UID_ROOT, GID_WHEEL, 0666, "dspr%d.%d", unit, channel); - - return 0; -} - -int -dsp_unregister(int unit, int channel) -{ - dev_ops_remove(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP, 0)); - dev_ops_remove(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSP16, 0)); - dev_ops_remove(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_AUDIO, 0)); - dev_ops_remove(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_NORESET, 0)); - return 0; -} - -int -dsp_unregisterrec(int unit, int channel) -{ - dev_ops_remove(&dsp_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_DSPREC, 0)); - return 0; + return (0); } #ifdef USING_DEVFS + +/* + * Clone logic is this: + * x E X = {dsp, dspW, audio} + * x -> x${sysctl("hw.snd.unit")} + * xN-> + * for i N = 1 to channels of device N + * if xN.i isn't busy, return its dev_t + */ static void -dsp_clone(void *arg, char *name, int namelen, cdev_t *dev) +dsp_clone(void *arg, struct ucred *cred, char *name, int namelen, + struct cdev **dev) { - cdev_t pdev; - int i, cont, unit, devtype; - int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; - char *devnames[3] = {"dsp", "dspW", "audio"}; - - if (*dev != NOCDEV) + struct cdev *pdev; + struct snddev_info *pcm_dev; + struct snddev_channel *pcm_chan; + int i, unit, devtype; + static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; + static char *devnames[3] = {"dsp", "dspW", "audio"}; + + if (*dev != NULL) return; if (pcm_devclass == NULL) return; @@ -1132,16 +1184,31 @@ dsp_clone(void *arg, char *name, int namelen, cdev_t *dev) if (unit == -1 || unit >= devclass_get_maxunit(pcm_devclass)) return; - cont = 1; - for (i = 0; cont; i++) { - pdev = make_adhoc_dev(&dsp_ops, PCMMKMINOR(unit, devtype, i)); - if (pdev->si_flags & SI_NAMED) { - if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { - *dev = pdev; - return; - } - } else { - cont = 0; + pcm_dev = devclass_get_softc(pcm_devclass, unit); + + if (pcm_dev == NULL) + return; + + SLIST_FOREACH(pcm_chan, &pcm_dev->channels, link) { + + switch(devtype) { + case SND_DEV_DSP: + pdev = pcm_chan->dsp_devt; + break; + case SND_DEV_DSP16: + pdev = pcm_chan->dspW_devt; + break; + case SND_DEV_AUDIO: + pdev = pcm_chan->audio_devt; + break; + default: + panic("Unknown devtype %d", devtype); + } + + if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { + *dev = pdev; + dev_ref(*dev); + return; } } } diff --git a/sys/dev/sound/pcm/dsp.h b/sys/dev/sound/pcm/dsp.h index cadfa84962..a068a34650 100644 --- a/sys/dev/sound/pcm/dsp.h +++ b/sys/dev/sound/pcm/dsp.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,11 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/dsp.h,v 1.2.2.3 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/dsp.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/dsp.h,v 1.9 2005/01/06 01:43:20 imp Exp $ + * $DragonFly: src/sys/dev/sound/pcm/dsp.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ -int dsp_register(int unit, int channel); -int dsp_registerrec(int unit, int channel); -int dsp_unregister(int unit, int channel); -int dsp_unregisterrec(int unit, int channel); +extern struct dev_ops dsp_cdevsw; diff --git a/sys/dev/sound/pcm/fake.c b/sys/dev/sound/pcm/fake.c index ecf1db0cf1..e4b1840a9a 100644 --- a/sys/dev/sound/pcm/fake.c +++ b/sys/dev/sound/pcm/fake.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,15 +23,18 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/fake.c,v 1.4.2.5 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/fake.c,v 1.4 2006/12/20 18:14:41 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/fake.c,v 1.14.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/fake.c,v 1.5 2007/01/04 21:47:03 corecode Exp $ */ #include -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/fake.c,v 1.4 2006/12/20 18:14:41 dillon Exp $"); static u_int32_t fk_fmt[] = { + AFMT_MU_LAW, + AFMT_STEREO | AFMT_MU_LAW, + AFMT_A_LAW, + AFMT_STEREO | AFMT_A_LAW, AFMT_U8, AFMT_STEREO | AFMT_U8, AFMT_S8, @@ -44,6 +47,22 @@ static u_int32_t fk_fmt[] = { AFMT_STEREO | AFMT_S16_BE, AFMT_U16_BE, AFMT_STEREO | AFMT_U16_BE, + AFMT_S24_LE, + AFMT_STEREO | AFMT_S24_LE, + AFMT_U24_LE, + AFMT_STEREO | AFMT_U24_LE, + AFMT_S24_BE, + AFMT_STEREO | AFMT_S24_BE, + AFMT_U24_BE, + AFMT_STEREO | AFMT_U24_BE, + AFMT_S32_LE, + AFMT_STEREO | AFMT_S32_LE, + AFMT_U32_LE, + AFMT_STEREO | AFMT_U32_LE, + AFMT_S32_BE, + AFMT_STEREO | AFMT_S32_BE, + AFMT_U32_BE, + AFMT_STEREO | AFMT_U32_BE, 0 }; static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0}; @@ -123,6 +142,12 @@ fkchan_setup(device_t dev) c = kmalloc(sizeof(*c), M_DEVBUF, M_WAITOK); c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK); c->parentsnddev = d; + /* + * Fake channel is such a blessing in disguise. Using this, + * we can keep track prefered virtual channel speed without + * querying kernel hint repetitively (see vchan_create / vchan.c). + */ + c->speed = 0; ksnprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev)); return c; diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c index 990fb2d8b8..649401b32e 100644 --- a/sys/dev/sound/pcm/feeder.c +++ b/sys/dev/sound/pcm/feeder.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,15 +23,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/feeder.c,v 1.8.2.9 2003/02/08 01:43:07 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.5 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/feeder.c,v 1.33.2.3 2006/03/07 15:51:19 jhb Exp $ + * $DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.6 2007/01/04 21:47:03 corecode Exp $ */ #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.5 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder.c,v 1.6 2007/01/04 21:47:03 corecode Exp $"); MALLOC_DEFINE(M_FEEDER, "feeder", "pcm feeder"); @@ -62,9 +62,11 @@ feeder_register(void *p) KASSERT(fc->desc == NULL, ("first feeder not root: %s", fc->name)); SLIST_INIT(&feedertab); - fte = kmalloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); + fte = kmalloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); if (fte == NULL) { - kprintf("can't allocate memory for root feeder\n"); + kprintf("can't allocate memory for root feeder: %s\n", + fc->name); + return; } fte->feederclass = fc; @@ -85,7 +87,7 @@ feeder_register(void *p) i = 0; while ((feedercnt < MAXFEEDERS) && (fc->desc[i].type > 0)) { /* kprintf("adding feeder %s, %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); */ - fte = kmalloc(sizeof(*fte), M_FEEDER, M_WAITOK | M_ZERO); + fte = kmalloc(sizeof(*fte), M_FEEDER, M_NOWAIT | M_ZERO); if (fte == NULL) { kprintf("can't allocate memory for feeder '%s', %x -> %x\n", fc->name, fc->desc[i].in, fc->desc[i].out); @@ -138,7 +140,7 @@ feeder_create(struct feeder_class *fc, struct pcm_feederdesc *desc) struct pcm_feeder *f; int err; - f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_WAITOK | M_ZERO); + f = (struct pcm_feeder *)kobj_create((kobj_class_t)fc, M_FEEDER, M_NOWAIT | M_ZERO); if (f == NULL) return NULL; @@ -195,11 +197,13 @@ chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederd nf->source = c->feeder; + /* XXX we should use the lowest common denominator for align */ if (nf->align > 0) c->align += nf->align; else if (nf->align < 0 && c->align < -nf->align) c->align = -nf->align; - + if (c->feeder != NULL) + c->feeder->parent = nf; c->feeder = nf; return 0; @@ -264,9 +268,9 @@ feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *sto struct feedertab_entry *fte; struct pcm_feeder *try, *ret; - /* kprintf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */ + DEB(kprintf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); if (fmtvalid(source->desc->out, to)) { - /* kprintf("got it\n"); */ + DEB(kprintf("got it\n")); return source; } @@ -294,11 +298,105 @@ feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *sto return NULL; } +int +chn_fmtscore(u_int32_t fmt) +{ + if (fmt & AFMT_32BIT) + return 60; + if (fmt & AFMT_24BIT) + return 50; + if (fmt & AFMT_16BIT) + return 40; + if (fmt & (AFMT_U8|AFMT_S8)) + return 30; + if (fmt & AFMT_MU_LAW) + return 20; + if (fmt & AFMT_A_LAW) + return 10; + return 0; +} + +u_int32_t +chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) +{ + u_int32_t best; + int i, score, score2, oldscore; + + best = 0; + score = chn_fmtscore(fmt); + oldscore = 0; + for (i = 0; fmts[i] != 0; i++) { + score2 = chn_fmtscore(fmts[i]); + if (oldscore == 0 || (score2 == score) || + (score2 > oldscore && score2 < score) || + (score2 < oldscore && score2 > score) || + (oldscore < score && score2 > oldscore)) { + best = fmts[i]; + oldscore = score2; + } + } + return best; +} + +u_int32_t +chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) +{ + u_int32_t best; + int i, score, score2, oldscore; + + best = 0; + score = chn_fmtscore(fmt); + oldscore = 0; + for (i = 0; fmts[i] != 0; i++) { + if ((fmt & AFMT_STEREO) == (fmts[i] & AFMT_STEREO)) { + score2 = chn_fmtscore(fmts[i]); + if (oldscore == 0 || (score2 == score) || + (score2 > oldscore && score2 < score) || + (score2 < oldscore && score2 > score) || + (oldscore < score && score2 > oldscore)) { + best = fmts[i]; + oldscore = score2; + } + } + } + return best; +} + +u_int32_t +chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) +{ + u_int32_t best1, best2; + int score, score1, score2; + + best1 = chn_fmtbeststereo(fmt, fmts); + best2 = chn_fmtbestbit(fmt, fmts); + + if (best1 != 0 && best2 != 0) { + if (fmt & AFMT_STEREO) + return best1; + else { + score = chn_fmtscore(fmt); + score1 = chn_fmtscore(best1); + score2 = chn_fmtscore(best2); + if (score1 == score2 || score1 == score) + return best1; + else if (score2 == score) + return best2; + else if (score1 > score2) + return best1; + return best2; + } + } else if (best2 == 0) + return best1; + else + return best2; +} + u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to) { struct pcm_feeder *try, *del, *stop; - u_int32_t tmpfrom[2], best, *from; + u_int32_t tmpfrom[2], tmpto[2], best, *from; int i, max, bestmax; KASSERT(c != NULL, ("c == NULL")); @@ -310,10 +408,34 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to) if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { from = chn_getcaps(c)->fmtlist; + if (fmtvalid(to[0], from)) + from = to; + else { + best = chn_fmtbest(to[0], from); + if (best != 0) { + tmpfrom[0] = best; + tmpfrom[1] = 0; + from = tmpfrom; + } + } } else { tmpfrom[0] = c->feeder->desc->out; tmpfrom[1] = 0; from = tmpfrom; + if (to[1] != 0) { + if (fmtvalid(tmpfrom[0], to)) { + tmpto[0] = tmpfrom[0]; + tmpto[1] = 0; + to = tmpto; + } else { + best = chn_fmtbest(tmpfrom[0], to); + if (best != 0) { + tmpto[0] = best; + tmpto[1] = 0; + to = tmpto; + } + } + } } i = 0; @@ -370,7 +492,30 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to) kprintf("%s [%d]\n", try->class->name, try->desc->idx); #endif - return (c->direction == PCMDIR_REC)? best : c->feeder->desc->out; + if (c->direction == PCMDIR_REC) { + try = c->feeder; + while (try != NULL) { + if (try->desc->type == FEEDER_ROOT) + return try->desc->out; + try = try->source; + } + return best; + } else + return c->feeder->desc->out; +} + +void +feeder_printchain(struct pcm_feeder *head) +{ + struct pcm_feeder *f; + + kprintf("feeder chain (head @%p)\n", head); + f = head; + while (f != NULL) { + kprintf("%s/%d @ %p\n", f->class->name, f->desc->idx, f); + f = f->source; + } + kprintf("[end]\n\n"); } /*****************************************************************************/ @@ -410,12 +555,12 @@ static kobj_method_t feeder_root_methods[] = { { 0, 0 } }; static struct feeder_class feeder_root_class = { - name: "feeder_root", - methods: feeder_root_methods, - size: sizeof(struct pcm_feeder), - align: 0, - desc: NULL, - data: NULL, + .name = "feeder_root", + .methods = feeder_root_methods, + .size = sizeof(struct pcm_feeder), + .align = 0, + .desc = NULL, + .data = NULL, }; SYSINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_register, &feeder_root_class); SYSUNINIT(feeder_root, SI_SUB_DRIVERS, SI_ORDER_FIRST, feeder_unregisterall, NULL); diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h index 75e2002d6f..1ddc010c29 100644 --- a/sys/dev/sound/pcm/feeder.h +++ b/sys/dev/sound/pcm/feeder.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/feeder.h,v 1.2.2.4 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/feeder.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/feeder.h,v 1.12.2.1 2006/01/29 02:27:28 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/feeder.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ struct pcm_feederdesc { @@ -54,19 +54,24 @@ struct pcm_feeder { void feeder_register(void *p); struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc); +int chn_fmtscore(u_int32_t fmt); +u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts); +u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts); +u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts); u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to); int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc); int chn_removefeeder(struct pcm_channel *c); struct pcm_feeder *chn_findfeeder(struct pcm_channel *c, u_int32_t type); +void feeder_printchain(struct pcm_feeder *head); #define FEEDER_DECLARE(feeder, palign, pdata) \ static struct feeder_class feeder ## _class = { \ - name: #feeder, \ - methods: feeder ## _methods, \ - size: sizeof(struct pcm_feeder), \ - align: palign, \ - desc: feeder ## _desc, \ - data: pdata, \ + .name = #feeder, \ + .methods = feeder ## _methods, \ + .size = sizeof(struct pcm_feeder), \ + .align = palign, \ + .desc = feeder ## _desc, \ + .data = pdata, \ }; \ SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class); diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c index 55aba1e9a8..3e428a20f9 100644 --- a/sys/dev/sound/pcm/feeder_fmt.c +++ b/sys/dev/sound/pcm/feeder_fmt.c @@ -1,5 +1,6 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,56 +24,110 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.1.2.5 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.3 2006/09/05 00:55:43 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.14.2.2 2006/01/29 02:27:28 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.4 2007/01/04 21:47:03 corecode Exp $ + * + * + * *New* and rewritten soft format converter, supporting 24/32bit pcm data, + * simplified and optimized. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This new implementation is fully dedicated in memory of Cameron Grant, * + * the creator of the magnificent, highly addictive feeder infrastructure. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * */ #include - #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.3 2006/09/05 00:55:43 dillon Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.4 2007/01/04 21:47:03 corecode Exp $"); MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder"); #define FEEDBUFSZ 8192 +#define FEEDBUF24SZ 8190 -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, +#define FMT_TRACE(x...) /* printf(x) */ +#define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */ +#define FMT_ALIGNBYTE(x) /* x */ + +/* + * Sign inverted ulaw/alaw -> 8 table + */ +static uint8_t ulaw_to_s8_tbl[] = { + 131, 135, 139, 143, 147, 151, 155, 159, + 163, 167, 171, 175, 179, 183, 187, 191, + 194, 196, 198, 200, 202, 204, 206, 208, + 210, 212, 214, 216, 218, 220, 222, 224, + 226, 227, 228, 229, 230, 231, 232, 233, + 234, 235, 236, 237, 238, 239, 240, 241, + 241, 242, 242, 243, 243, 244, 244, 245, + 245, 246, 246, 247, 247, 248, 248, 249, + 249, 249, 250, 250, 250, 250, 251, 251, + 251, 251, 252, 252, 252, 252, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, + 254, 254, 254, 254, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 125, 121, 117, 113, 109, 105, 101, 97, + 93, 89, 85, 81, 77, 73, 69, 65, + 62, 60, 58, 56, 54, 52, 50, 48, + 46, 44, 42, 40, 38, 36, 34, 32, + 30, 29, 28, 27, 26, 25, 24, 23, + 22, 21, 20, 19, 18, 17, 16, 15, + 15, 14, 14, 13, 13, 12, 12, 11, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 6, 6, 5, 5, + 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }; -static unsigned char u8_to_ulaw[] = { +static uint8_t alaw_to_s8_tbl[] = { + 236, 237, 234, 235, 240, 241, 238, 239, + 228, 229, 226, 227, 232, 233, 230, 231, + 246, 246, 245, 245, 248, 248, 247, 247, + 242, 242, 241, 241, 244, 244, 243, 243, + 171, 175, 163, 167, 187, 191, 179, 183, + 139, 143, 131, 135, 155, 159, 147, 151, + 214, 216, 210, 212, 222, 224, 218, 220, + 198, 200, 194, 196, 206, 208, 202, 204, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 251, 251, 251, 251, 252, 252, 252, 252, + 249, 249, 249, 249, 250, 250, 250, 250, + 254, 254, 254, 254, 254, 254, 254, 254, + 253, 253, 253, 253, 253, 253, 253, 253, + 20, 19, 22, 21, 16, 15, 18, 17, + 28, 27, 30, 29, 24, 23, 26, 25, + 10, 10, 11, 11, 8, 8, 9, 9, + 14, 14, 15, 15, 12, 12, 13, 13, + 85, 81, 93, 89, 69, 65, 77, 73, + 117, 113, 125, 121, 101, 97, 109, 105, + 42, 40, 46, 44, 34, 32, 38, 36, + 58, 56, 62, 60, 50, 48, 54, 52, + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 5, 5, 5, 5, 4, 4, 4, 4, + 7, 7, 7, 7, 6, 6, 6, 6, + 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, +}; + +static uint8_t u8_to_ulaw_tbl[] = { 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, @@ -107,117 +162,176 @@ static unsigned char u8_to_ulaw[] = { 129, 129, 129, 129, 128, 128, 128, 128, }; -static unsigned char alaw_to_ulaw[] = { - 42, 43, 40, 41, 46, 47, 44, 45, - 34, 35, 32, 33, 38, 39, 36, 37, - 57, 58, 55, 56, 61, 62, 59, 60, - 49, 50, 48, 48, 53, 54, 51, 52, - 10, 11, 8, 9, 14, 15, 12, 13, - 2, 3, 0, 1, 6, 7, 4, 5, - 26, 27, 24, 25, 30, 31, 28, 29, - 18, 19, 16, 17, 22, 23, 20, 21, - 98, 99, 96, 97, 102, 103, 100, 101, - 93, 93, 92, 92, 95, 95, 94, 94, - 116, 118, 112, 114, 124, 126, 120, 122, - 106, 107, 104, 105, 110, 111, 108, 109, - 72, 73, 70, 71, 76, 77, 74, 75, - 64, 65, 63, 63, 68, 69, 66, 67, - 86, 87, 84, 85, 90, 91, 88, 89, - 79, 79, 78, 78, 82, 83, 80, 81, - 170, 171, 168, 169, 174, 175, 172, 173, - 162, 163, 160, 161, 166, 167, 164, 165, - 185, 186, 183, 184, 189, 190, 187, 188, - 177, 178, 176, 176, 181, 182, 179, 180, - 138, 139, 136, 137, 142, 143, 140, 141, - 130, 131, 128, 129, 134, 135, 132, 133, - 154, 155, 152, 153, 158, 159, 156, 157, - 146, 147, 144, 145, 150, 151, 148, 149, - 226, 227, 224, 225, 230, 231, 228, 229, - 221, 221, 220, 220, 223, 223, 222, 222, - 244, 246, 240, 242, 252, 254, 248, 250, - 234, 235, 232, 233, 238, 239, 236, 237, - 200, 201, 198, 199, 204, 205, 202, 203, - 192, 193, 191, 191, 196, 197, 194, 195, - 214, 215, 212, 213, 218, 219, 216, 217, - 207, 207, 206, 206, 210, 211, 208, 209, -}; - -static unsigned char ulaw_to_alaw[] = { - 42, 43, 40, 41, 46, 47, 44, 45, - 34, 35, 32, 33, 38, 39, 36, 37, - 58, 59, 56, 57, 62, 63, 60, 61, - 50, 51, 48, 49, 54, 55, 52, 53, - 10, 11, 8, 9, 14, 15, 12, 13, - 2, 3, 0, 1, 6, 7, 4, 5, - 27, 24, 25, 30, 31, 28, 29, 18, - 19, 16, 17, 22, 23, 20, 21, 106, - 104, 105, 110, 111, 108, 109, 98, 99, - 96, 97, 102, 103, 100, 101, 122, 120, - 126, 127, 124, 125, 114, 115, 112, 113, - 118, 119, 116, 117, 75, 73, 79, 77, - 66, 67, 64, 65, 70, 71, 68, 69, - 90, 91, 88, 89, 94, 95, 92, 93, - 82, 82, 83, 83, 80, 80, 81, 81, - 86, 86, 87, 87, 84, 84, 85, 85, - 170, 171, 168, 169, 174, 175, 172, 173, - 162, 163, 160, 161, 166, 167, 164, 165, - 186, 187, 184, 185, 190, 191, 188, 189, - 178, 179, 176, 177, 182, 183, 180, 181, - 138, 139, 136, 137, 142, 143, 140, 141, - 130, 131, 128, 129, 134, 135, 132, 133, - 155, 152, 153, 158, 159, 156, 157, 146, - 147, 144, 145, 150, 151, 148, 149, 234, - 232, 233, 238, 239, 236, 237, 226, 227, - 224, 225, 230, 231, 228, 229, 250, 248, - 254, 255, 252, 253, 242, 243, 240, 241, - 246, 247, 244, 245, 203, 201, 207, 205, - 194, 195, 192, 193, 198, 199, 196, 197, - 218, 219, 216, 217, 222, 223, 220, 221, - 210, 210, 211, 211, 208, 208, 209, 209, - 214, 214, 215, 215, 212, 212, 213, 213, -}; - -/*****************************************************************************/ +static uint8_t u8_to_alaw_tbl[] = { + 42, 42, 42, 42, 42, 43, 43, 43, + 43, 40, 40, 40, 40, 41, 41, 41, + 41, 46, 46, 46, 46, 47, 47, 47, + 47, 44, 44, 44, 44, 45, 45, 45, + 45, 34, 34, 34, 34, 35, 35, 35, + 35, 32, 32, 32, 32, 33, 33, 33, + 33, 38, 38, 38, 38, 39, 39, 39, + 39, 36, 36, 36, 36, 37, 37, 37, + 37, 58, 58, 59, 59, 56, 56, 57, + 57, 62, 62, 63, 63, 60, 60, 61, + 61, 50, 50, 51, 51, 48, 48, 49, + 49, 54, 54, 55, 55, 52, 52, 53, + 53, 10, 11, 8, 9, 14, 15, 12, + 13, 2, 3, 0, 1, 6, 7, 4, + 5, 24, 30, 28, 18, 16, 22, 20, + 106, 110, 98, 102, 122, 114, 75, 90, + 213, 197, 245, 253, 229, 225, 237, 233, + 149, 151, 145, 147, 157, 159, 153, 155, + 133, 132, 135, 134, 129, 128, 131, 130, + 141, 140, 143, 142, 137, 136, 139, 138, + 181, 181, 180, 180, 183, 183, 182, 182, + 177, 177, 176, 176, 179, 179, 178, 178, + 189, 189, 188, 188, 191, 191, 190, 190, + 185, 185, 184, 184, 187, 187, 186, 186, + 165, 165, 165, 165, 164, 164, 164, 164, + 167, 167, 167, 167, 166, 166, 166, 166, + 161, 161, 161, 161, 160, 160, 160, 160, + 163, 163, 163, 163, 162, 162, 162, 162, + 173, 173, 173, 173, 172, 172, 172, 172, + 175, 175, 175, 175, 174, 174, 174, 174, + 169, 169, 169, 169, 168, 168, 168, 168, + 171, 171, 171, 171, 170, 170, 170, 170, +}; static int -feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_table_u8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - int i, j, k; + int j, k = FEEDER_FEED(f->source, c, b, count, source); + uint8_t *tbl = (uint8_t *)f->data; + + j = k; + while (j > 0) { + j--; + b[j] = tbl[b[j]] ^ 0x80; + } + return k; +} - k = FEEDER_FEED(f->source, c, b, count / 2, source); - j = k - 1; - i = j * 2 + 1; - while (i > 0 && j >= 0) { - b[i--] = b[j--]; - b[i--] = 0; +static int +feed_table_s16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + uint8_t *tbl = (uint8_t *)f->data; + + i = k; + k <<= 1; + j = k; + while (i > 0) { + b[--j] = tbl[b[--i]]; + b[--j] = 0; } - return k * 2; + return k; } -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 int +feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int j, k = FEEDER_FEED(f->source, c, b, count, source); + uint8_t *tbl = (uint8_t *)f->data; + + j = k; + while (j > 0) { + j--; + b[j] = tbl[b[j]]; + } + return k; +} + +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, 0, 0, 0}, }; -static kobj_method_t feeder_8to16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_8to16le), - { 0, 0 } +static kobj_method_t feeder_ulawtou8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_u8), + {0, 0} }; -FEEDER_DECLARE(feeder_8to16le, 0, NULL); +FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_alawtou8_desc[] = { + {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_alawtou8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_u8), + {0, 0} +}; +FEEDER_DECLARE(feeder_alawtou8, 0, alaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_ulawtos16le_desc[] = { + {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_ulawtos16le_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_s16le), + {0, 0} +}; +FEEDER_DECLARE(feeder_ulawtos16le, 0, ulaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_alawtos16le_desc[] = { + {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_alawtos16le_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_s16le), + {0, 0} +}; +FEEDER_DECLARE(feeder_alawtos16le, 0, alaw_to_s8_tbl); + +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, 0, 0, 0}, +}; +static kobj_method_t feeder_u8toulaw_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_xlaw), + {0, 0} +}; +FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw_tbl); -/*****************************************************************************/ +static struct pcm_feederdesc feeder_u8toalaw_desc[] = { + {FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_u8toalaw_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_xlaw), + {0, 0} +}; +FEEDER_DECLARE(feeder_u8toalaw, 0, u8_to_alaw_tbl); + +/* + * Conversion rules:- + * 1. BE -> LE + * 2. if fmt == u8 , u8 -> s8 (economical) + * 3. Xle -> 16le + * 4. if fmt != u8 && fmt == u16le , u16le -> s16le + * 4. s16le mono -> s16le stereo + * + * All conversion done in byte level to preserve endianess. + */ static int -feed_16to8_init(struct pcm_feeder *f) +feed_common_init(struct pcm_feeder *f) { - f->data = kmalloc(FEEDBUFSZ, M_FMTFEEDER, M_WAITOK | M_ZERO); - return (f->data)? 0 : ENOMEM; + f->data = kmalloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT|M_ZERO); + if (f->data == NULL) + return ENOMEM; + return 0; } static int -feed_16to8_free(struct pcm_feeder *f) +feed_common_free(struct pcm_feeder *f) { if (f->data) kfree(f->data, M_FMTFEEDER); @@ -225,322 +339,808 @@ feed_16to8_free(struct pcm_feeder *f) return 0; } +/* + * Bit conversion + */ static int -feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - u_int32_t i = 0, toget = count * 2; - int j = 1, k; - - k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source); - while (j < k) { - b[i++] = ((u_int8_t *)f->data)[j]; - j += 2; + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + + i = k; + k <<= 1; + j = k; + while (i > 0) { + b[--j] = b[--i]; + b[--j] = 0; } - return i; + return k; } +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, 0, 0, 0}, +}; +static kobj_method_t feeder_8to16le_methods[] = { + KOBJMETHOD(feeder_feed, feed_8to16le), + {0, 0} +}; +FEEDER_DECLARE(feeder_8to16le, 0, NULL); +static int +feed_16leto8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k; + uint8_t *src = (uint8_t *)f->data; + + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source); + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~1); + i = k; + j = k >> 1; + while (i > 0) { + b[--j] = src[--i]; + i--; + } + return k >> 1; +} 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_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}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; 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 } + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_16leto8), + {0, 0} }; -FEEDER_DECLARE(feeder_16leto8, 1, NULL); +FEEDER_DECLARE(feeder_16leto8, 0, NULL); -/*****************************************************************************/ +static int +feed_16leto24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k; + + k = (count / 3) << 1; + k = FEEDER_FEED(f->source, c, b, k, source); + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~1); + i = k; + j = (k >> 1) * 3; + k = j; + while (i > 0) { + b[--j] = b[--i]; + b[--j] = b[--i]; + b[--j] = 0; + } + return k; +} +static struct pcm_feederdesc feeder_16leto24le_desc[] = { + {FEEDER_FMT, AFMT_U16_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_16leto24le_methods[] = { + KOBJMETHOD(feeder_feed, feed_16leto24le), + {0, 0} +}; +FEEDER_DECLARE(feeder_16leto24le, 0, NULL); static int -feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_24leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source); + int i, j, k; + uint8_t *src = (uint8_t *)f->data; - j = k - 1; - i = j * 2 + 1; - while (i > 0 && j >= 0) { - b[i--] = b[j]; - b[i--] = b[j]; - j--; + k = (count * 3) >> 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source); + if (k < 3) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__); + FMT_ALIGNBYTE(k -= k % 3); + i = (k / 3) << 1; + j = i; + while (i > 0) { + b[--i] = src[--k]; + b[--i] = src[--k]; + k--; } - return k * 2; + return j; } +static struct pcm_feederdesc feeder_24leto16le_desc[] = { + {FEEDER_FMT, AFMT_U24_LE, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_24leto16le_methods[] = { + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_24leto16le), + {0, 0} +}; +FEEDER_DECLARE(feeder_24leto16le, 1, NULL); + +static int +feed_16leto32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~1); + i = k; + j = k << 1; + k = j; + while (i > 0) { + b[--j] = b[--i]; + b[--j] = b[--i]; + b[--j] = 0; + b[--j] = 0; + } + return k; +} +static struct pcm_feederdesc feeder_16leto32le_desc[] = { + {FEEDER_FMT, AFMT_U16_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_16leto32le_methods[] = { + KOBJMETHOD(feeder_feed, feed_16leto32le), + {0, 0} +}; +FEEDER_DECLARE(feeder_16leto32le, 0, NULL); + +static int +feed_32leto16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k; + uint8_t *src = (uint8_t *)f->data; + + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source); + if (k < 4) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~3); + i = k; + k >>= 1; + j = k; + while (i > 0) { + b[--j] = src[--i]; + b[--j] = src[--i]; + i -= 2; + } + return k; +} +static struct pcm_feederdesc feeder_32leto16le_desc[] = { + {FEEDER_FMT, AFMT_U32_LE, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_32leto16le_methods[] = { + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_32leto16le), + {0, 0} +}; +FEEDER_DECLARE(feeder_32leto16le, 1, NULL); +/* + * Bit conversion end + */ +/* + * Channel conversion (mono -> stereo) + */ +static int +feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + + i = k; + j = k << 1; + while (i > 0) { + b[--j] = b[--i]; + b[--j] = b[i]; + } + return k << 1; +} 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}, + {FEEDER_FMT, AFMT_U8, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_S8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; static kobj_method_t feeder_monotostereo8_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo8), - { 0, 0 } + KOBJMETHOD(feeder_feed, feed_monotostereo8), + {0, 0} }; FEEDER_DECLARE(feeder_monotostereo8, 0, NULL); -/*****************************************************************************/ - static int -feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_monotostereo16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - int i, j, k = FEEDER_FEED(f->source, c, b, count / 2, source); - 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; -} + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + uint8_t l, m; + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~1); + i = k; + j = k << 1; + while (i > 0) { + l = b[--i]; + m = b[--i]; + b[--j] = l; + b[--j] = m; + b[--j] = l; + b[--j] = m; + } + return k << 1; +} 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}, + {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, 0, 0, 0}, }; static kobj_method_t feeder_monotostereo16_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo16), - { 0, 0 } + KOBJMETHOD(feeder_feed, feed_monotostereo16), + {0, 0} }; FEEDER_DECLARE(feeder_monotostereo16, 0, NULL); -/*****************************************************************************/ - static int -feed_stereotomono8_init(struct pcm_feeder *f) +feed_monotostereo24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - f->data = kmalloc(FEEDBUFSZ, M_FMTFEEDER, M_WAITOK | M_ZERO); - return (f->data)? 0 : ENOMEM; + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + uint8_t l, m, n; + + if (k < 3) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k % 3, "%s: Bytes not 24bit aligned.\n", __func__); + FMT_ALIGNBYTE(k -= k % 3); + i = k; + j = k << 1; + while (i > 0) { + l = b[--i]; + m = b[--i]; + n = b[--i]; + b[--j] = l; + b[--j] = m; + b[--j] = n; + b[--j] = l; + b[--j] = m; + b[--j] = n; + } + return k << 1; } +static struct pcm_feederdesc feeder_monotostereo24_desc[] = { + {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_monotostereo24_methods[] = { + KOBJMETHOD(feeder_feed, feed_monotostereo24), + {0, 0} +}; +FEEDER_DECLARE(feeder_monotostereo24, 0, NULL); static int -feed_stereotomono8_free(struct pcm_feeder *f) +feed_monotostereo32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - if (f->data) - kfree(f->data, M_FMTFEEDER); - f->data = NULL; - return 0; + int i, j, k = FEEDER_FEED(f->source, c, b, count >> 1, source); + uint8_t l, m, n, o; + + if (k < 4) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 3, "%s: Bytes not 32bit aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~3); + i = k; + j = k << 1; + while (i > 0) { + l = b[--i]; + m = b[--i]; + n = b[--i]; + o = b[--i]; + b[--j] = l; + b[--j] = m; + b[--j] = n; + b[--j] = o; + b[--j] = l; + b[--j] = m; + b[--j] = n; + b[--j] = o; + } + return k << 1; } +static struct pcm_feederdesc feeder_monotostereo32_desc[] = { + {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_monotostereo32_methods[] = { + KOBJMETHOD(feeder_feed, feed_monotostereo32), + {0, 0} +}; +FEEDER_DECLARE(feeder_monotostereo32, 0, NULL); +/* + * Channel conversion (mono -> stereo) end + */ +/* + * Channel conversion (stereo -> mono) + */ static int -feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_stereotomono8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - u_int32_t i = 0, toget = count * 2; - int j = 0, k; + int i, j, k; + uint8_t *src = (uint8_t *)f->data; - k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source); - while (j < k) { - b[i++] = ((u_int8_t *)f->data)[j]; - j += 2; + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source); + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 1, "%s: Bytes not 8bit (stereo) aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~1); + i = k >> 1; + j = i; + while (i > 0) { + k--; + b[--i] = src[--k]; } - return i; + return j; } - 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}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S8, 0}, + {0, 0, 0, 0}, }; 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 } + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_stereotomono8), + {0, 0} }; -FEEDER_DECLARE(feeder_stereotomono8, 1, NULL); - -/*****************************************************************************/ +FEEDER_DECLARE(feeder_stereotomono8, 0, NULL); static int -feed_stereotomono16_init(struct pcm_feeder *f) +feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - f->data = kmalloc(FEEDBUFSZ, M_FMTFEEDER, M_WAITOK | M_ZERO); - return (f->data)? 0 : ENOMEM; -} + int i, j, k; + uint8_t *src = (uint8_t *)f->data; -static int -feed_stereotomono16_free(struct pcm_feeder *f) -{ - if (f->data) - kfree(f->data, M_FMTFEEDER); - f->data = NULL; - return 0; + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source); + if (k < 4) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 3, "%s: Bytes not 16bit (stereo) aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~3); + i = k >> 1; + j = i; + while (i > 0) { + k -= 2; + b[--i] = src[--k]; + b[--i] = src[--k]; + } + return j; } +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, 0, 0, 0}, +}; +static kobj_method_t feeder_stereotomono16_methods[] = { + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_stereotomono16), + {0, 0} +}; +FEEDER_DECLARE(feeder_stereotomono16, 0, NULL); static int -feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_stereotomono24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - u_int32_t i = 0, toget = count * 2; - int j = 0, k; + int i, j, k; + uint8_t *src = (uint8_t *)f->data; - k = FEEDER_FEED(f->source, c, f->data, min(toget, FEEDBUFSZ), source); - while (j < k) { - b[i++] = ((u_int8_t *)f->data)[j]; - b[i++] = ((u_int8_t *)f->data)[j + 1]; - j += 4; + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUF24SZ), source); + if (k < 6) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; } - return i; + FMT_TEST(k % 6, "%s: Bytes not 24bit (stereo) aligned.\n", __func__); + FMT_ALIGNBYTE(k -= k % 6); + i = k >> 1; + j = i; + while (i > 0) { + k -= 3; + b[--i] = src[--k]; + b[--i] = src[--k]; + b[--i] = src[--k]; + } + return j; } - -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 struct pcm_feederdesc feeder_stereotomono24_desc[] = { + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S24_BE, 0}, + {0, 0, 0, 0}, }; -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 } +static kobj_method_t feeder_stereotomono24_methods[] = { + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_stereotomono24), + {0, 0} }; -FEEDER_DECLARE(feeder_stereotomono16, 1, NULL); - -/*****************************************************************************/ +FEEDER_DECLARE(feeder_stereotomono24, 0, NULL); static int -feed_endian(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_stereotomono32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - u_int8_t t; - int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); + int i, j, k; + uint8_t *src = (uint8_t *)f->data; - while (i < j) { - t = b[i]; - b[i] = b[i + 1]; - b[i + 1] = t; - i += 2; + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, FEEDBUFSZ), source); + if (k < 8) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k & 7, "%s: Bytes not 32bit (stereo) aligned.\n", __func__); + FMT_ALIGNBYTE(k &= ~7); + i = k >> 1; + j = i; + while (i > 0) { + k -= 4; + b[--i] = src[--k]; + b[--i] = src[--k]; + b[--i] = src[--k]; + b[--i] = src[--k]; } - return i; + return j; } - -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 struct pcm_feederdesc feeder_stereotomono32_desc[] = { + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S32_BE, 0}, + {0, 0, 0, 0}, }; -static kobj_method_t feeder_endian_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - { 0, 0 } +static kobj_method_t feeder_stereotomono32_methods[] = { + KOBJMETHOD(feeder_init, feed_common_init), + KOBJMETHOD(feeder_free, feed_common_free), + KOBJMETHOD(feeder_feed, feed_stereotomono32), + {0, 0} }; -FEEDER_DECLARE(feeder_endian, 0, NULL); - -/*****************************************************************************/ +FEEDER_DECLARE(feeder_stereotomono32, 0, NULL); +/* + * Channel conversion (stereo -> mono) end + */ +/* + * Sign conversion + */ static int -feed_sign(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_sign8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); - intptr_t ssz = (intptr_t)f->data, ofs = ssz - 1; + int i, j = FEEDER_FEED(f->source, c, b, count, source); - while (i < j) { - b[i + ofs] ^= 0x80; - i += ssz; - } - return i; + i = j; + while (i > 0) + b[--i] ^= 0x80; + return j; } - 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_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}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; static kobj_method_t feeder_sign8_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - { 0, 0 } + KOBJMETHOD(feeder_feed, feed_sign8), + {0, 0} }; -FEEDER_DECLARE(feeder_sign8, 0, (void *)1); +FEEDER_DECLARE(feeder_sign8, 0, NULL); + +static int +feed_sign16le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j = FEEDER_FEED(f->source, c, b, count, source); + if (j < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, j); + return 0; + } + FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__); + FMT_ALIGNBYTE(j &= ~1); + i = j; + while (i > 0) { + b[--i] ^= 0x80; + i--; + } + return j; +} 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_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}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; static kobj_method_t feeder_sign16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - { 0, 0 } + KOBJMETHOD(feeder_feed, feed_sign16le), + {0, 0} }; -FEEDER_DECLARE(feeder_sign16le, -1, (void *)2); - -/*****************************************************************************/ +FEEDER_DECLARE(feeder_sign16le, 0, NULL); static int -feed_table(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_sign24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); + int i, j = FEEDER_FEED(f->source, c, b, count, source); - while (i < j) { - b[i] = ((u_int8_t *)f->data)[b[i]]; - i++; + if (j < 3) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, j); + return 0; + } + FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__); + FMT_ALIGNBYTE(j -= j % 3); + i = j; + while (i > 0) { + b[--i] ^= 0x80; + i -= 2; } - return i; + return j; } - -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 struct pcm_feederdesc feeder_sign24le_desc[] = { + {FEEDER_FMT, AFMT_U24_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; -static kobj_method_t feeder_ulawtou8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } +static kobj_method_t feeder_sign24le_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign24le), + {0, 0} }; -FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_u8); +FEEDER_DECLARE(feeder_sign24le, 0, NULL); -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 int +feed_sign32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j = FEEDER_FEED(f->source, c, b, count, source); + + if (j < 4) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, j); + return 0; + } + FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__); + FMT_ALIGNBYTE(j &= ~3); + i = j; + while (i > 0) { + b[--i] ^= 0x80; + i -= 3; + } + return j; +} +static struct pcm_feederdesc feeder_sign32le_desc[] = { + {FEEDER_FMT, AFMT_U32_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; -static kobj_method_t feeder_u8toulaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } +static kobj_method_t feeder_sign32le_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign32le), + {0, 0} }; -FEEDER_DECLARE(feeder_u8toulaw, 0, u8_to_ulaw); +FEEDER_DECLARE(feeder_sign32le, 0, NULL); +/* + * Sign conversion end. + */ -static struct pcm_feederdesc feeder_alawtoulaw_desc[] = { - {FEEDER_FMT, AFMT_A_LAW, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0}, - {0}, +/* + * Endian conversion. + */ +static int +feed_endian16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j = FEEDER_FEED(f->source, c, b, count, source); + uint8_t v; + + if (j < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, j); + return 0; + } + FMT_TEST(j & 1, "%s: Bytes not 16bit aligned.\n", __func__); + FMT_ALIGNBYTE(j &= ~1); + i = j; + while (i > 0) { + v = b[--i]; + b[i] = b[i - 1]; + b[--i] = v; + } + return j; +} +static struct pcm_feederdesc feeder_endian16_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, 0, 0, 0}, }; -static kobj_method_t feeder_alawtoulaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } +static kobj_method_t feeder_endian16_methods[] = { + KOBJMETHOD(feeder_feed, feed_endian16), + {0, 0} }; -FEEDER_DECLARE(feeder_alawtoulaw, 0, alaw_to_ulaw); +FEEDER_DECLARE(feeder_endian16, 0, NULL); + +static int +feed_endian24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j = FEEDER_FEED(f->source, c, b, count, source); + uint8_t v; -static struct pcm_feederdesc feeder_ulawtoalaw_desc[] = { - {FEEDER_FMT, AFMT_MU_LAW, AFMT_A_LAW, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {0}, + if (j < 3) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, j); + return 0; + } + FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__); + FMT_ALIGNBYTE(j -= j % 3); + i = j; + while (i > 0) { + v = b[--i]; + b[i] = b[i - 2]; + b[i -= 2] = v; + } + return j; +} +static struct pcm_feederdesc feeder_endian24_desc[] = { + {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, }; -static kobj_method_t feeder_ulawtoalaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } +static kobj_method_t feeder_endian24_methods[] = { + KOBJMETHOD(feeder_feed, feed_endian24), + {0, 0} }; -FEEDER_DECLARE(feeder_ulawtoalaw, 0, ulaw_to_alaw); - +FEEDER_DECLARE(feeder_endian24, 0, NULL); +static int +feed_endian32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j = FEEDER_FEED(f->source, c, b, count, source); + uint8_t l, m; + if (j < 4) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, j); + return 0; + } + FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__); + FMT_ALIGNBYTE(j &= ~3); + i = j; + while (i > 0) { + l = b[--i]; + m = b[--i]; + b[i] = b[i - 1]; + b[i + 1] = b[i - 2]; + b[--i] = m; + b[--i] = l; + } + return j; +} +static struct pcm_feederdesc feeder_endian32_desc[] = { + {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_endian32_methods[] = { + KOBJMETHOD(feeder_feed, feed_endian32), + {0, 0} +}; +FEEDER_DECLARE(feeder_endian32, 0, NULL); +/* + * Endian conversion end + */ diff --git a/sys/dev/sound/pcm/feeder_if.m b/sys/dev/sound/pcm/feeder_if.m index 87718d076e..538d8b0c62 100644 --- a/sys/dev/sound/pcm/feeder_if.m +++ b/sys/dev/sound/pcm/feeder_if.m @@ -1,3 +1,4 @@ +#- # KOBJ # # Copyright (c) 2000 Cameron Grant @@ -24,8 +25,8 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $FreeBSD: src/sys/dev/sound/pcm/feeder_if.m,v 1.1.2.3 2002/04/22 15:49:36 cg Exp $ -# $DragonFly: src/sys/dev/sound/pcm/feeder_if.m,v 1.3 2003/11/15 21:12:53 asmodai Exp $ +# $FreeBSD: src/sys/dev/sound/pcm/feeder_if.m,v 1.6 2005/01/06 01:43:21 imp Exp $ +# $DragonFly: src/sys/dev/sound/pcm/feeder_if.m,v 1.4 2007/01/04 21:47:03 corecode Exp $ # #include @@ -86,5 +87,3 @@ METHOD int feed { u_int32_t count; void* source; }; - - diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c index 6bb08e13c1..6494a115c2 100644 --- a/sys/dev/sound/pcm/feeder_rate.c +++ b/sys/dev/sound/pcm/feeder_rate.c @@ -1,5 +1,7 @@ -/* - * Copyright (c) 2003 Orion Hodson +/*- + * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2003 Orion Hodson + * Copyright (c) 2005 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,7 +25,25 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * MAINTAINER: Orion Hodson + * 2005-06-11: + * ========== + * + * *New* and rewritten soft sample rate converter supporting arbitrary sample + * rates, fine grained scaling/coefficients and a unified up/down stereo + * converter. Most of the disclaimers from orion's notes also applies + * here, regarding linear interpolation deficiencies and pre/post + * anti-aliasing filtering issues. This version comes with a much simpler and + * tighter interface, although it works almost exactly like the older one. + * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This new implementation is fully dedicated in memory of Cameron Grant, * + * the creator of the magnificent, highly addictive feeder infrastructure. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * + * Orion's notes: + * ============= * * This rate conversion code uses linear interpolation without any * pre- or post- interpolation filtering to combat aliasing. This @@ -31,203 +51,437 @@ * stage in the future. * * Since this accuracy of interpolation is sensitive and examination - * of the algorithm output is harder from the kernel, th code is + * of the algorithm output is harder from the kernel, the code is * designed to be compiled in the kernel and in a userland test * harness. This is done by selectively including and excluding code * with several portions based on whether _KERNEL is defined. It's a * little ugly, but exceedingly useful. The testsuite and its * revisions can be found at: - * http://people.freebsd.org/~orion/feedrate/ + * http://people.freebsd.org/~orion/files/feedrate/ * * Special thanks to Ken Marx for exposing flaws in the code and for * testing revisions. * - * $FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.2.2.3 2003/02/08 02:38:21 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/feeder_rate.c,v 1.4 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.11.2.2 2006/01/29 02:27:28 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/feeder_rate.c,v 1.5 2007/01/04 21:47:03 corecode Exp $ */ -#ifdef _KERNEL - #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder_rate.c,v 1.4 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder_rate.c,v 1.5 2007/01/04 21:47:03 corecode Exp $"); -#endif /* _KERNEL */ +#define RATE_ASSERT(x, y) /* KASSERT(x,y) */ +#define RATE_TEST(x, y) /* if (!(x)) printf y */ +#define RATE_TRACE(x...) /* printf(x) */ MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); -#ifndef RATE_ASSERT -#define RATE_ASSERT(x, y) /* KASSERT(x) */ -#endif /* RATE_ASSERT */ +#define FEEDBUFSZ 8192 +#define ROUNDHZ 25 +#define RATEMIN 4000 +/* 8000 * 138 or 11025 * 100 . This is insane, indeed! */ +#define RATEMAX 1102500 +#define MINGAIN 92 +#define MAXGAIN 96 + +#define FEEDRATE_CONVERT_64 0 +#define FEEDRATE_CONVERT_SCALE64 1 +#define FEEDRATE_CONVERT_SCALE32 2 +#define FEEDRATE_CONVERT_PLAIN 3 +#define FEEDRATE_CONVERT_FIXED 4 +#define FEEDRATE_CONVERT_OPTIMAL 5 +#define FEEDRATE_CONVERT_WORST 6 + +#define FEEDRATE_64_MAXROLL 32 +#define FEEDRATE_32_MAXROLL 16 -#ifndef RATE_TRACE -#define RATE_TRACE(x...) /* kprintf(x) */ -#endif +struct feed_rate_info { + uint32_t src, dst; /* rounded source / destination rates */ + uint32_t rsrc, rdst; /* original source / destination rates */ + uint32_t gx, gy; /* interpolation / decimation ratio */ + uint32_t alpha; /* interpolation distance */ + uint32_t pos, bpos; /* current sample / buffer positions */ + uint32_t bufsz; /* total buffer size */ + uint32_t stray; /* stray bytes */ + int32_t scale, roll; /* scale / roll factor */ + int16_t *buffer; + uint32_t (*convert)(struct feed_rate_info *, int16_t *, uint32_t); +}; -/*****************************************************************************/ +static uint32_t +feed_convert_64(struct feed_rate_info *, int16_t *, uint32_t); +static uint32_t +feed_convert_scale64(struct feed_rate_info *, int16_t *, uint32_t); +static uint32_t +feed_convert_scale32(struct feed_rate_info *, int16_t *, uint32_t); +static uint32_t +feed_convert_plain(struct feed_rate_info *, int16_t *, uint32_t); -/* The following coefficients are coupled. They are chosen to be - * guarantee calculable factors for the interpolation routine. They - * have been tested over the range of RATEMIN-RATEMAX Hz. Decreasing - * the granularity increases the required buffer size and affects the - * gain values at different points in the space. These values were - * found by running the test program with -p (probe) and some trial - * and error. - * - * ROUNDHZ the granularity of sample rates (fits n*11025 and n*8000). - * FEEDBUFSZ the amount of buffer space. - * MINGAIN the minimum acceptable gain in coefficients search. +int feeder_rate_ratemin = RATEMIN; +int feeder_rate_ratemax = RATEMAX; +/* + * See 'Feeder Scaling Type' below.. */ -#define ROUNDHZ 25 -#define FEEDBUFSZ 8192 -#define MINGAIN 92 - -#define RATEMIN 4000 -#define RATEMAX 48000 +static int feeder_rate_scaling = FEEDRATE_CONVERT_OPTIMAL; +static int feeder_rate_buffersize = FEEDBUFSZ & ~1; -struct feed_rate_info; +#if 0 +/* + * sysctls.. I love sysctls.. + */ +TUNABLE_INT("hw.snd.feeder_rate_ratemin", &feeder_rate_ratemin); +TUNABLE_INT("hw.snd.feeder_rate_ratemax", &feeder_rate_ratemin); +TUNABLE_INT("hw.snd.feeder_rate_scaling", &feeder_rate_scaling); +TUNABLE_INT("hw.snd.feeder_rate_buffersize", &feeder_rate_buffersize); -typedef int (*rate_convert_method)(struct feed_rate_info *, - uint32_t, uint32_t, int16_t *); +static int +sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = feeder_rate_ratemin; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (val < 1 || val >= feeder_rate_ratemax) + err = EINVAL; + else + feeder_rate_ratemin = val; + return err; +} +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemin, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemin, "I", ""); -static int -convert_stereo_up(struct feed_rate_info *info, - uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst); +static int +sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = feeder_rate_ratemax; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (val <= feeder_rate_ratemin || val > 0x7fffff) + err = EINVAL; + else + feeder_rate_ratemax = val; + return err; +} +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemax, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemax, "I", ""); static int -convert_stereo_down(struct feed_rate_info *info, - uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst); +sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS) +{ + int err, val; -struct feed_rate_info { - uint32_t src, dst; /* source and destination rates */ - uint16_t buffer_ticks; /* number of available samples in buffer */ - uint16_t buffer_pos; /* next available sample in buffer */ - uint16_t rounds; /* maximum number of cycle rounds w buffer */ - uint16_t alpha; /* interpolation distance */ - uint16_t sscale; /* src clock scale */ - uint16_t dscale; /* dst clock scale */ - uint16_t mscale; /* scale factor to avoid divide per sample */ - uint16_t mroll; /* roll to again avoid divide per sample */ - uint16_t channels; /* 1 = mono, 2 = stereo */ - - rate_convert_method convert; - int16_t buffer[FEEDBUFSZ]; -}; + val = feeder_rate_scaling; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + /* + * Feeder Scaling Type + * =================== + * + * 1. Plain 64bit (high precision) + * 2. 64bit scaling (high precision, CPU friendly, but can + * cause gain up/down). + * 3. 32bit scaling (somehow can cause hz roundup, gain + * up/down). + * 4. Plain copy (default if src == dst. Except if src == dst, + * this is the worst / silly conversion method!). + * + * Sysctl options:- + * + * 0 - Plain 64bit - no fallback. + * 1 - 64bit scaling - no fallback. + * 2 - 32bit scaling - no fallback. + * 3 - Plain copy - no fallback. + * 4 - Fixed rate. Means that, choose optimal conversion method + * without causing hz roundup. + * 32bit scaling (as long as hz roundup does not occur), + * 64bit scaling, Plain 64bit. + * 5 - Optimal / CPU friendly (DEFAULT). + * 32bit scaling, 64bit scaling, Plain 64bit + * 6 - Optimal to worst, no 64bit arithmetic involved. + * 32bit scaling, Plain copy. + */ + if (val < FEEDRATE_CONVERT_64 || val > FEEDRATE_CONVERT_WORST) + err = EINVAL; + else + feeder_rate_scaling = val; + return err; +} +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_scaling, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_scaling, "I", ""); -#define bytes_per_sample 2 -#define src_ticks_per_cycle(info) (info->dscale * info->rounds) -#define dst_ticks_per_cycle(info) (info->sscale * info->rounds) -#define bytes_per_tick(info) (info->channels * bytes_per_sample) -#define src_bytes_per_cycle(info) \ - (src_ticks_per_cycle(info) * bytes_per_tick(info)) -#define dst_bytes_per_cycle(info) \ - (dst_ticks_per_cycle(info) * bytes_per_tick(info)) +static int +sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS) +{ + int err, val; -static uint32_t -gcd(uint32_t x, uint32_t y) + val = feeder_rate_buffersize; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + /* + * Don't waste too much kernel space + */ + if (val < 2 || val > 65536) + err = EINVAL; + else + feeder_rate_buffersize = val & ~1; + return err; +} +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", ""); +#endif + +static void +feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy) { - uint32_t w; + uint32_t w, src = x, dst = y; + while (y != 0) { w = x % y; x = y; y = w; } - return x; + *gx = src / x; + *gy = dst / x; +} + +static void +feed_scale_roll(uint32_t dst, int32_t *scale, int32_t *roll, int32_t max) +{ + int64_t k, tscale; + int32_t j, troll; + + *scale = *roll = -1; + for (j = MAXGAIN; j >= MINGAIN; j -= 3) { + for (troll = 0; troll < max; troll++) { + tscale = (1 << troll) / dst; + k = (tscale * dst * 100) >> troll; + if (k > j && k <= 100) { + *scale = tscale; + *roll = troll; + return; + } + } + } +} + +static int +feed_get_best_coef(uint32_t *src, uint32_t *dst, uint32_t *gx, uint32_t *gy, + int32_t *scale, int32_t *roll) +{ + uint32_t tsrc, tdst, sscale, dscale; + int32_t tscale, troll; + int i, j, hzmin, hzmax; + + *scale = *roll = -1; + for (i = 0; i < 2; i++) { + hzmin = (ROUNDHZ * i) + 1; + hzmax = hzmin + ROUNDHZ; + for (j = hzmin; j < hzmax; j++) { + tsrc = *src - (*src % j); + tdst = *dst; + if (tsrc < 1 || tdst < 1) + goto coef_failed; + feed_speed_ratio(tsrc, tdst, &sscale, &dscale); + feed_scale_roll(dscale, &tscale, &troll, + FEEDRATE_32_MAXROLL); + if (tscale != -1 && troll != -1) { + *src = tsrc; + *gx = sscale; + *gy = dscale; + *scale = tscale; + *roll = troll; + return j; + } + } + for (j = hzmin; j < hzmax; j++) { + tsrc = *src - (*src % j); + tdst = *dst - (*dst % j); + if (tsrc < 1 || tdst < 1) + goto coef_failed; + feed_speed_ratio(tsrc, tdst, &sscale, &dscale); + feed_scale_roll(dscale, &tscale, &troll, + FEEDRATE_32_MAXROLL); + if (tscale != -1 && troll != -1) { + *src = tsrc; + *dst = tdst; + *gx = sscale; + *gy = dscale; + *scale = tscale; + *roll = troll; + return j; + } + } + for (j = hzmin; j < hzmax; j++) { + tsrc = *src; + tdst = *dst - (*dst % j); + if (tsrc < 1 || tdst < 1) + goto coef_failed; + feed_speed_ratio(tsrc, tdst, &sscale, &dscale); + feed_scale_roll(dscale, &tscale, &troll, + FEEDRATE_32_MAXROLL); + if (tscale != -1 && troll != -1) { + *src = tsrc; + *dst = tdst; + *gx = sscale; + *gy = dscale; + *scale = tscale; + *roll = troll; + return j; + } + } + } +coef_failed: + feed_speed_ratio(*src, *dst, gx, gy); + feed_scale_roll(*gy, scale, roll, FEEDRATE_32_MAXROLL); + return 0; +} + +static void +feed_rate_reset(struct feed_rate_info *info) +{ + info->scale = -1; + info->roll = -1; + info->src = info->rsrc; + info->dst = info->rdst; + info->gx = 0; + info->gy = 0; } static int feed_rate_setup(struct pcm_feeder *f) { struct feed_rate_info *info = f->data; - uint32_t mscale, mroll, l, r, g; - - /* Beat sample rates down by greatest common divisor */ - g = gcd(info->src, info->dst); - info->sscale = info->dst / g; - info->dscale = info->src / g; + int r = 0; + info->pos = 2; + info->bpos = 4; info->alpha = 0; - info->buffer_ticks = 0; - info->buffer_pos = 0; - - /* Pick suitable conversion routine */ - if (info->src > info->dst) { - info->convert = convert_stereo_down; + info->stray = 0; + feed_rate_reset(info); + if (info->src == info->dst) { + /* + * No conversion ever needed. Just do plain copy. + */ + info->convert = feed_convert_plain; + info->gx = 1; + info->gy = 1; } else { - info->convert = convert_stereo_up; - } - - /* - * Determine number of conversion rounds that will fit into - * buffer. NB Must set info->rounds to one before using - * src_ticks_per_cycle here since it used by src_ticks_per_cycle. - */ - info->rounds = 1; - r = (FEEDBUFSZ - bytes_per_tick(info)) / - (src_ticks_per_cycle(info) * bytes_per_tick(info)); - if (r == 0) { - RATE_TRACE("Insufficient buffer space for conversion %d -> %d " - "(%d < %d)\n", info->src, info->dst, FEEDBUFSZ, - src_ticks_per_cycle(info) * bytes_per_tick(info)); - return -1; + switch (feeder_rate_scaling) { + case FEEDRATE_CONVERT_64: + feed_speed_ratio(info->src, info->dst, + &info->gx, &info->gy); + info->convert = feed_convert_64; + break; + case FEEDRATE_CONVERT_SCALE64: + feed_speed_ratio(info->src, info->dst, + &info->gx, &info->gy); + feed_scale_roll(info->gy, &info->scale, + &info->roll, FEEDRATE_64_MAXROLL); + if (info->scale == -1 || info->roll == -1) + return -1; + info->convert = feed_convert_scale64; + break; + case FEEDRATE_CONVERT_SCALE32: + r = feed_get_best_coef(&info->src, &info->dst, + &info->gx, &info->gy, &info->scale, + &info->roll); + if (r == 0) + return -1; + info->convert = feed_convert_scale32; + break; + case FEEDRATE_CONVERT_PLAIN: + feed_speed_ratio(info->src, info->dst, + &info->gx, &info->gy); + info->convert = feed_convert_plain; + break; + case FEEDRATE_CONVERT_FIXED: + r = feed_get_best_coef(&info->src, &info->dst, + &info->gx, &info->gy, &info->scale, + &info->roll); + if (r != 0 && info->src == info->rsrc && + info->dst == info->rdst) + info->convert = feed_convert_scale32; + else { + /* Fallback */ + feed_rate_reset(info); + feed_speed_ratio(info->src, info->dst, + &info->gx, &info->gy); + feed_scale_roll(info->gy, &info->scale, + &info->roll, FEEDRATE_64_MAXROLL); + if (info->scale != -1 && info->roll != -1) + info->convert = feed_convert_scale64; + else + info->convert = feed_convert_64; + } + break; + case FEEDRATE_CONVERT_OPTIMAL: + r = feed_get_best_coef(&info->src, &info->dst, + &info->gx, &info->gy, &info->scale, + &info->roll); + if (r != 0) + info->convert = feed_convert_scale32; + else { + /* Fallback */ + feed_rate_reset(info); + feed_speed_ratio(info->src, info->dst, + &info->gx, &info->gy); + feed_scale_roll(info->gy, &info->scale, + &info->roll, FEEDRATE_64_MAXROLL); + if (info->scale != -1 && info->roll != -1) + info->convert = feed_convert_scale64; + else + info->convert = feed_convert_64; + } + break; + case FEEDRATE_CONVERT_WORST: + r = feed_get_best_coef(&info->src, &info->dst, + &info->gx, &info->gy, &info->scale, + &info->roll); + if (r != 0) + info->convert = feed_convert_scale32; + else { + /* Fallback */ + feed_rate_reset(info); + feed_speed_ratio(info->src, info->dst, + &info->gx, &info->gy); + info->convert = feed_convert_plain; + } + break; + default: + return -1; + break; + } + /* No way! */ + if (info->gx == 0 || info->gy == 0) + return -1; + /* + * No need to interpolate/decimate, just do plain copy. + * This probably caused by Hz roundup. + */ + if (info->gx == info->gy) + info->convert = feed_convert_plain; } - info->rounds = r; - - /* - * Find scale and roll combination that allows us to trade - * costly divide operations in the main loop for multiply-rolls. - */ - for (l = 96; l >= MINGAIN; l -= 3) { - for (mroll = 0; mroll < 16; mroll ++) { - mscale = (1 << mroll) / info->sscale; - - r = (mscale * info->sscale * 100) >> mroll; - if (r > l && r <= 100) { - info->mscale = mscale; - info->mroll = mroll; - RATE_TRACE("Converting %d to %d with " - "mscale = %d and mroll = %d " - "(gain = %d / 100)\n", - info->src, info->dst, - info->mscale, info->mroll, r); - return 0; - } - } - } - - RATE_TRACE("Failed to find a converter within %d%% gain for " - "%d to %d.\n", l, info->src, info->dst); - - return -2; + return 0; } static int feed_rate_set(struct pcm_feeder *f, int what, int value) { struct feed_rate_info *info = f->data; - int rvalue; - - if (value < RATEMIN || value > RATEMAX) { + + if (value < feeder_rate_ratemin || value > feeder_rate_ratemax) return -1; - } - - rvalue = (value / ROUNDHZ) * ROUNDHZ; - if (value - rvalue > ROUNDHZ / 2) { - rvalue += ROUNDHZ; - } - switch(what) { - case FEEDRATE_SRC: - info->src = rvalue; - break; - case FEEDRATE_DST: - info->dst = rvalue; - break; - default: - return -1; + switch (what) { + case FEEDRATE_SRC: + info->rsrc = value; + break; + case FEEDRATE_DST: + info->rdst = value; + break; + default: + return -1; } - return feed_rate_setup(f); } @@ -236,13 +490,16 @@ feed_rate_get(struct pcm_feeder *f, int what) { struct feed_rate_info *info = f->data; - switch(what) { - case FEEDRATE_SRC: - return info->src; - case FEEDRATE_DST: - return info->dst; - default: - return -1; + /* + * Return *real* src/dst rate. + */ + switch (what) { + case FEEDRATE_SRC: + return info->rsrc; + case FEEDRATE_DST: + return info->rdst; + default: + return -1; } return -1; } @@ -252,16 +509,23 @@ feed_rate_init(struct pcm_feeder *f) { struct feed_rate_info *info; - info = kmalloc(sizeof(*info), M_RATEFEEDER, M_WAITOK | M_ZERO); + info = kmalloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); if (info == NULL) return ENOMEM; - - info->src = DSP_DEFAULT_SPEED; - info->dst = DSP_DEFAULT_SPEED; - info->channels = 2; - + /* + * bufsz = sample from last cycle + conversion space + */ + info->bufsz = 2 + feeder_rate_buffersize; + info->buffer = kmalloc(sizeof(*info->buffer) * info->bufsz, + M_RATEFEEDER, M_NOWAIT | M_ZERO); + if (info->buffer == NULL) { + kfree(info, M_RATEFEEDER); + return ENOMEM; + } + info->rsrc = DSP_DEFAULT_SPEED; + info->rdst = DSP_DEFAULT_SPEED; f->data = info; - return 0; + return feed_rate_setup(f); } static int @@ -270,211 +534,290 @@ feed_rate_free(struct pcm_feeder *f) struct feed_rate_info *info = f->data; if (info) { + if (info->buffer) + kfree(info->buffer, M_RATEFEEDER); kfree(info, M_RATEFEEDER); } f->data = NULL; return 0; } -static int -convert_stereo_up(struct feed_rate_info *info, - uint32_t src_ticks, - uint32_t dst_ticks, - int16_t *dst) +static uint32_t +feed_convert_64(struct feed_rate_info *info, int16_t *dst, uint32_t max) { - uint32_t max_dst_ticks; - int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o; + int64_t x, alpha, distance; + uint32_t ret; + int32_t pos, bpos, gx, gy; int16_t *src; - - sp = info->buffer_pos * 2; - se = sp + src_ticks * 2; - - src = info->buffer; - alpha = info->alpha * info->mscale; - dalpha = info->dscale * info->mscale; /* Alpha increment */ - malpha = info->sscale * info->mscale; /* Maximum allowed alpha value */ - mroll = info->mroll; - /* - * For efficiency the main conversion loop should only depend on - * one variable. We use the state to work out the maximum number - * of output samples that are available and eliminate the checking of - * sp from the loop. + * Plain, straight forward 64bit arith. No bit-magic applied here. */ - max_dst_ticks = src_ticks * info->dst / info->src - alpha / dalpha; - if (max_dst_ticks < dst_ticks) { - dst_ticks = max_dst_ticks; + ret = 0; + alpha = info->alpha; + gx = info->gx; + gy = info->gy; + pos = info->pos; + bpos = info->bpos; + src = info->buffer; + for (;;) { + if (alpha < gx) { + alpha += gy; + pos += 2; + if (pos == bpos) + break; + } else { + alpha -= gx; + distance = gy - alpha; + x = (alpha * src[pos - 2]) + (distance * src[pos]); + dst[ret++] = x / gy; + x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); + dst[ret++] = x / gy; + if (ret == max) + break; + } } + info->alpha = alpha; + info->pos = pos; + return ret; +} - dp = 0; - de = dst_ticks * 2; +static uint32_t +feed_convert_scale64(struct feed_rate_info *info, int16_t *dst, uint32_t max) +{ + int64_t x, alpha, distance; + uint32_t ret; + int32_t pos, bpos, gx, gy, roll; + int16_t *src; /* - * Unrolling this loop manually does not help much here because - * of the alpha, malpha comparison. + * 64bit scaling. */ - while (dp < de) { - o = malpha - alpha; - x = alpha * src[sp + 2] + o * src[sp]; - dst[dp++] = x >> mroll; - x = alpha * src[sp + 3] + o * src[sp + 1]; - dst[dp++] = x >> mroll; - alpha += dalpha; - if (alpha >= malpha) { - alpha -= malpha; - sp += 2; + ret = 0; + roll = info->roll; + alpha = info->alpha * info->scale; + gx = info->gx * info->scale; + gy = info->gy * info->scale; + pos = info->pos; + bpos = info->bpos; + src = info->buffer; + for (;;) { + if (alpha < gx) { + alpha += gy; + pos += 2; + if (pos == bpos) + break; + } else { + alpha -= gx; + distance = gy - alpha; + x = (alpha * src[pos - 2]) + (distance * src[pos]); + dst[ret++] = x >> roll; + x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); + dst[ret++] = x >> roll; + if (ret == max) + break; } } - RATE_ASSERT(sp <= se, ("%s: Source overrun\n", __func__)); - - info->buffer_pos = sp / info->channels; - info->alpha = alpha / info->mscale; - - return dp / info->channels; + info->alpha = alpha / info->scale; + info->pos = pos; + return ret; } -static int -convert_stereo_down(struct feed_rate_info *info, - uint32_t src_ticks, - uint32_t dst_ticks, - int16_t *dst) +static uint32_t +feed_convert_scale32(struct feed_rate_info *info, int16_t *dst, uint32_t max) { - int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o, m, - mdalpha, mstep; + uint32_t ret; + int32_t x, pos, bpos, gx, gy, alpha, roll, distance; int16_t *src; - - sp = info->buffer_pos * 2; - se = sp + src_ticks * 2; - + /* + * 32bit scaling. + */ + ret = 0; + roll = info->roll; + alpha = info->alpha * info->scale; + gx = info->gx * info->scale; + gy = info->gy * info->scale; + pos = info->pos; + bpos = info->bpos; src = info->buffer; - alpha = info->alpha * info->mscale; - dalpha = info->dscale * info->mscale; /* Alpha increment */ - malpha = info->sscale * info->mscale; /* Maximum allowed alpha value */ - mroll = info->mroll; - - dp = 0; - de = dst_ticks * 2; - - m = dalpha / malpha; - mstep = m * 2; - mdalpha = dalpha - m * malpha; + for (;;) { + if (alpha < gx) { + alpha += gy; + pos += 2; + if (pos == bpos) + break; + } else { + alpha -= gx; + distance = gy - alpha; + x = (alpha * src[pos - 2]) + (distance * src[pos]); + dst[ret++] = x >> roll; + x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); + dst[ret++] = x >> roll; + if (ret == max) + break; + } + } + info->alpha = alpha / info->scale; + info->pos = pos; + return ret; +} +static uint32_t +feed_convert_plain(struct feed_rate_info *info, int16_t *dst, uint32_t max) +{ + uint32_t ret; + int32_t pos, bpos, gx, gy, alpha; + int16_t *src; /* - * TODO: eliminate sp or dp from this loop comparison for a few - * extra % performance. + * Plain copy. */ - while (sp < se && dp < de) { - o = malpha - alpha; - x = alpha * src[sp + 2] + o * src[sp]; - dst[dp++] = x >> mroll; - x = alpha * src[sp + 3] + o * src[sp + 1]; - dst[dp++] = x >> mroll; - - alpha += mdalpha; - sp += mstep; - if (alpha >= malpha) { - alpha -= malpha; - sp += 2; + ret = 0; + gx = info->gx; + gy = info->gy; + alpha = info->alpha; + pos = info->pos; + bpos = info->bpos; + src = info->buffer; + for (;;) { + if (alpha < gx) { + alpha += gy; + pos += 2; + if (pos == bpos) + break; + } else { + alpha -= gx; + dst[ret++] = src[pos]; + dst[ret++] = src[pos + 1]; + if (ret == max) + break; } } - - info->buffer_pos = sp / 2; - info->alpha = alpha / info->mscale; - - RATE_ASSERT(info->buffer_pos <= info->buffer_ticks, - ("%s: Source overrun\n", __func__)); - - return dp / 2; + info->pos = pos; + info->alpha = alpha; + return ret; } -static int -feed_rate(struct pcm_feeder *f, - struct pcm_channel *c, - uint8_t *b, - uint32_t count, - void *source) +static int32_t +feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { struct feed_rate_info *info = f->data; - - uint32_t done, s_ticks, d_ticks; - done = 0; - - RATE_ASSERT(info->channels == 2, - ("%s: channels (%d) != 2", __func__, info->channels)); - - while (done < count) { - /* Slurp in more data if input buffer is not full */ - while (info->buffer_ticks < src_ticks_per_cycle(info)) { - uint8_t *u8b; - int fetch; - fetch = src_bytes_per_cycle(info) - - info->buffer_ticks * bytes_per_tick(info); - u8b = (uint8_t*)info->buffer + - (info->buffer_ticks + 1) * - bytes_per_tick(info); - fetch = FEEDER_FEED(f->source, c, u8b, fetch, source); - RATE_ASSERT(fetch % bytes_per_tick(info) == 0, - ("%s: fetched unaligned bytes (%d)", - __func__, fetch)); - info->buffer_ticks += fetch / bytes_per_tick(info); - RATE_ASSERT(src_ticks_per_cycle(info) >= - info->buffer_ticks, - ("%s: buffer overfilled (%d > %d).", - __func__, info->buffer_ticks, - src_ticks_per_cycle(info))); - if (fetch == 0) + uint32_t i; + int32_t fetch, slot; + int16_t *dst = (int16_t *)b; + /* + * This loop has been optimized to generalize both up / down + * sampling without causing missing samples or excessive buffer + * feeding. + */ + RATE_TEST(count >= 4 && (count & 3) == 0, + ("%s: Count size not byte integral (%d)\n", __func__, count)); + if (count < 4) + return 0; + count >>= 1; + count &= ~1; + slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1; + RATE_TEST((slot & 1) == 0, ("%s: Slot count not sample integral (%d)\n", + __func__, slot)); + /* + * Optimize buffer feeding aggressively to ensure calculated slot + * can be fitted nicely into available buffer free space, hence + * avoiding multiple feeding. + */ + RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n", + __func__,info->stray)); + if (info->pos != 2 && info->bpos - info->pos == 2 && + info->bpos + slot > info->bufsz) { + /* + * Copy last unit sample and its previous to + * beginning of buffer. + */ + info->buffer[0] = info->buffer[info->pos - 2]; + info->buffer[1] = info->buffer[info->pos - 1]; + info->buffer[2] = info->buffer[info->pos]; + info->buffer[3] = info->buffer[info->pos + 1]; + info->pos = 2; + info->bpos = 4; + } + RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", + __func__, slot)); + i = 0; + for (;;) { + for (;;) { + fetch = (info->bufsz - info->bpos) << 1; + fetch -= info->stray; + RATE_ASSERT(fetch >= 0, + ("%s: [1] Buffer overrun: %d > %d\n", + __func__, info->bpos, info->bufsz)); + if ((slot << 1) < fetch) + fetch = slot << 1; + if (fetch > 0) { + RATE_ASSERT(((info->bpos << 1) - info->stray) >= 0 && + ((info->bpos << 1) - info->stray) < (info->bufsz << 1), + ("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", __func__, + info->bufsz << 1, (info->bpos << 1) - info->stray)); + fetch = FEEDER_FEED(f->source, c, + (uint8_t *)(info->buffer) + (info->bpos << 1) - info->stray, + fetch, source); + info->stray = 0; + if (fetch == 0) + break; + RATE_TEST((fetch & 3) == 0, + ("%s: Fetch size not byte integral (%d)\n", + __func__, fetch)); + info->stray += fetch & 3; + RATE_TEST(info->stray == 0, + ("%s: Stray bytes detected (%d)\n", + __func__, info->stray)); + fetch >>= 1; + fetch &= ~1; + info->bpos += fetch; + slot -= fetch; + RATE_ASSERT(slot >= 0, + ("%s: Negative Slot: %d\n", __func__, + slot)); + if (slot == 0) + break; + if (info->bpos == info->bufsz) + break; + } else break; } - - /* Find amount of input buffer data that should be processed */ - d_ticks = (count - done) / bytes_per_tick(info); - s_ticks = info->buffer_ticks - info->buffer_pos; - if (info->buffer_ticks != src_ticks_per_cycle(info)) { - if (s_ticks > 8) - s_ticks -= 8; - else - s_ticks = 0; - } - - d_ticks = info->convert(info, s_ticks, d_ticks, - (int16_t*)(b + done)); - if (d_ticks == 0) + if (info->pos == info->bpos) { + RATE_TEST(info->pos == 2, + ("%s: EOF while in progress\n", __func__)); break; - done += d_ticks * bytes_per_tick(info); - - RATE_ASSERT(info->buffer_pos <= info->buffer_ticks, - ("%s: buffer_ticks too big\n", __func__)); - RATE_ASSERT(info->buffer_ticks <= src_ticks_per_cycle(info), - ("too many ticks %d / %d\n", - info->buffer_ticks, src_ticks_per_cycle(info))); - RATE_TRACE("%s: ticks %5d / %d pos %d\n", __func__, - info->buffer_ticks, src_ticks_per_cycle(info), - info->buffer_pos); - - if (src_ticks_per_cycle(info) <= info->buffer_pos) { - /* End of cycle reached, copy last samples to start */ - uint8_t *u8b; - u8b = (uint8_t*)info->buffer; - bcopy(u8b + src_bytes_per_cycle(info), u8b, - bytes_per_tick(info)); - - RATE_ASSERT(info->alpha == 0, - ("%s: completed cycle with " - "alpha non-zero", __func__, info->alpha)); - - info->buffer_pos = 0; - info->buffer_ticks = 0; } + RATE_ASSERT(info->pos <= info->bpos, + ("%s: [2] Buffer overrun: %d > %d\n", __func__, + info->pos, info->bpos)); + RATE_ASSERT(info->pos < info->bpos, + ("%s: Zero buffer!\n", __func__)); + RATE_ASSERT(((info->bpos - info->pos) & 1) == 0, + ("%s: Buffer not sample integral (%d)\n", + __func__, info->bpos - info->pos)); + i += info->convert(info, dst + i, count - i); + RATE_ASSERT(info->pos <= info->bpos, + ("%s: [3] Buffer overrun: %d > %d\n", + __func__, info->pos, info->bpos)); + if (info->pos == info->bpos) { + /* + * End of buffer cycle. Copy last unit sample + * to beginning of buffer so next cycle can + * interpolate using it. + */ + RATE_TEST(info->stray == 0, ("%s: [2] Stray bytes: %u\n", __func__, info->stray)); + info->buffer[0] = info->buffer[info->pos - 2]; + info->buffer[1] = info->buffer[info->pos - 1]; + info->bpos = 2; + info->pos = 2; + } + if (i == count) + break; } - - RATE_ASSERT(count >= done, - ("%s: generated too many bytes of data (%d > %d).", - __func__, done, count)); - - if (done != count) { - RATE_TRACE("Only did %d of %d\n", done, count); - } - - return done; +#if 0 + RATE_TEST(count == i, ("Expect: %u , Got: %u\n", count << 1, i << 1)); +#endif + RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, info->stray)); + return i << 1; } static struct pcm_feederdesc feeder_rate_desc[] = { @@ -490,4 +833,3 @@ static kobj_method_t feeder_rate_methods[] = { {0, 0} }; FEEDER_DECLARE(feeder_rate, 2, NULL); - diff --git a/sys/dev/sound/pcm/feeder_volume.c b/sys/dev/sound/pcm/feeder_volume.c new file mode 100644 index 0000000000..4267083e35 --- /dev/null +++ b/sys/dev/sound/pcm/feeder_volume.c @@ -0,0 +1,82 @@ +/*- + * Copyright (c) 2005 Ariff Abdullah + * 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: src/sys/dev/sound/pcm/feeder_volume.c,v 1.2.2.1 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/feeder_volume.c,v 1.1 2007/01/04 21:47:03 corecode Exp $ + * + * + * feeder_volume, a long 'Lost Technology' rather than a new feature. + */ + +#include +#include "feeder_if.h" + +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/feeder_volume.c,v 1.1 2007/01/04 21:47:03 corecode Exp $"); + +static int +feed_volume_s16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k, vol[2]; + int16_t *buf; + + k = FEEDER_FEED(f->source, c, b, count & ~1, source); + if (k < 2) { +#if 0 + device_printf(c->dev, "%s: Not enough data (Got: %d bytes)\n", + __func__, k); +#endif + return 0; + } +#if 0 + if (k & 1) + device_printf(c->dev, "%s: Bytes not 16bit aligned.\n", __func__); +#endif + k &= ~1; + i = k >> 1; + buf = (int16_t *)b; + vol[0] = c->volume & 0x7f; + vol[1] = (c->volume >> 8) & 0x7f; + while (i > 0) { + i--; + j = (vol[i & 1] * buf[i]) / 100; + if (j > 32767) + j = 32767; + if (j < -32768) + j = -32768; + buf[i] = j; + } + return k; +} + +static struct pcm_feederdesc feeder_volume_s16_desc[] = { + {FEEDER_VOLUME, AFMT_S16_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_volume_s16_methods[] = { + KOBJMETHOD(feeder_feed, feed_volume_s16), + {0, 0} +}; +FEEDER_DECLARE(feeder_volume_s16, 2, NULL); diff --git a/sys/dev/sound/pcm/mixer.c b/sys/dev/sound/pcm/mixer.c index 1fa801250f..dfe5668575 100644 --- a/sys/dev/sound/pcm/mixer.c +++ b/sys/dev/sound/pcm/mixer.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,15 +23,15 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.4.2.8 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.13 2006/12/20 18:14:41 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.43.2.4 2006/04/04 17:43:48 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.14 2007/01/04 21:47:03 corecode Exp $ */ #include #include "mixer_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.13 2006/12/20 18:14:41 dillon Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/mixer.c,v 1.14 2007/01/04 21:47:03 corecode Exp $"); MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); @@ -44,13 +44,14 @@ struct snd_mixer { int hwvol_muted; int hwvol_mixer; int hwvol_step; + device_t dev; u_int32_t hwvol_mute_level; u_int32_t devs; u_int32_t recdevs; u_int32_t recsrc; u_int16_t level[32]; char name[MIXER_NAMELEN]; - void *lock; + struct spinlock *lock; }; static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { @@ -63,6 +64,7 @@ static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = { [SOUND_MIXER_LINE] = 75, [SOUND_MIXER_MIC] = 0, [SOUND_MIXER_CD] = 75, + [SOUND_MIXER_IGAIN] = 0, [SOUND_MIXER_LINE1] = 75, [SOUND_MIXER_VIDEO] = 75, [SOUND_MIXER_RECLEV] = 0, @@ -75,8 +77,9 @@ static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES; static d_open_t mixer_open; static d_close_t mixer_close; -static struct dev_ops mixer_ops = { - { "mixer", SND_CDEV_MAJOR, 0 }, +static struct dev_ops mixer_cdevsw = { + { "mixer", SND_CDEV_MAJOR, D_TRACKCLOSE }, + /* .d_flags = D_TRACKCLOSE | D_NEEDGIANT, */ .d_open = mixer_open, .d_close = mixer_close, .d_ioctl = mixer_ioctl, @@ -86,16 +89,14 @@ static struct dev_ops mixer_ops = { static eventhandler_tag mixer_ehtag; #endif -static cdev_t +static struct cdev * mixer_get_devt(device_t dev) { - cdev_t pdev; - int unit; + struct snddev_info *snddev; - unit = device_get_unit(dev); - pdev = make_adhoc_dev(&mixer_ops, PCMMKMINOR(unit, SND_DEV_CTL, 0)); + snddev = device_get_softc(dev); - return pdev; + return snddev->mixer_dev; } #ifdef SND_DYNSYSCTL @@ -112,9 +113,13 @@ mixer_lookup(char *devname) } #endif +/* + * Always called with mixer->lock held. + */ static int mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) { + struct snddev_info *d; unsigned l, r; int v; @@ -124,9 +129,27 @@ mixer_set(struct snd_mixer *mixer, unsigned dev, unsigned lev) l = min((lev & 0x00ff), 100); r = min(((lev & 0xff00) >> 8), 100); - v = MIXER_SET(mixer, dev, l, r); - if (v < 0) - return -1; + d = device_get_softc(mixer->dev); + if (dev == SOUND_MIXER_PCM && d && + (d->flags & SD_F_SOFTVOL)) { + struct snddev_channel *sce; + struct pcm_channel *ch; + + snd_mtxunlock(mixer->lock); + SLIST_FOREACH(sce, &d->channels, link) { + ch = sce->channel; + CHN_LOCK(ch); + if (ch->direction == PCMDIR_PLAY && + (ch->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume(ch, l, r); + CHN_UNLOCK(ch); + } + snd_mtxlock(mixer->lock); + } else { + v = MIXER_SET(mixer, dev, l, r); + if (v < 0) + return -1; + } mixer->level[dev] = l | (r << 8); return 0; @@ -159,6 +182,9 @@ mixer_getrecsrc(struct snd_mixer *mixer) void mix_setdevs(struct snd_mixer *m, u_int32_t v) { + struct snddev_info *d = device_get_softc(m->dev); + if (d && (d->flags & SD_F_SOFTVOL)) + v |= SOUND_MASK_PCM; m->devs = v; } @@ -189,10 +215,11 @@ mix_getdevinfo(struct snd_mixer *m) int mixer_init(device_t dev, kobj_class_t cls, void *devinfo) { + struct snddev_info *snddev; struct snd_mixer *m; u_int16_t v; - cdev_t pdev; - int i, unit; + struct cdev *pdev; + int i, unit, val; m = (struct snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO); ksnprintf(m->name, MIXER_NAMELEN, "%s:mixer", device_get_nameunit(dev)); @@ -200,28 +227,40 @@ mixer_init(device_t dev, kobj_class_t cls, void *devinfo) m->type = cls->name; m->devinfo = devinfo; m->busy = 0; + m->dev = dev; if (MIXER_INIT(m)) goto bad; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { v = snd_mixerdefaults[i]; + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), snd_mixernames[i], &val) == 0) { + if (val >= 0 && val <= 100) { + v = (u_int16_t) val; + } + } + + snd_mtxlock(m->lock); mixer_set(m, i, v | (v << 8)); + snd_mtxunlock(m->lock); } mixer_setrecsrc(m, SOUND_MASK_MIC); unit = device_get_unit(dev); - dev_ops_add(&mixer_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_CTL, 0)); - pdev = make_dev(&mixer_ops, PCMMKMINOR(unit, SND_DEV_CTL, 0), + dev_ops_add(&mixer_cdevsw, -1, PCMMKMINOR(unit, SND_DEV_CTL, 0)); + pdev = make_dev(&mixer_cdevsw, PCMMKMINOR(unit, SND_DEV_CTL, 0), UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); + reference_dev(pdev); pdev->si_drv1 = m; + snddev = device_get_softc(dev); + snddev->mixer_dev = pdev; return 0; bad: - snd_mtxlock(m->lock); snd_mtxfree(m->lock); kobj_delete((kobj_t)m, M_MIXER); return -1; @@ -232,10 +271,14 @@ mixer_uninit(device_t dev) { int i; int unit; + struct snddev_info *d; struct snd_mixer *m; - cdev_t pdev; + struct cdev *pdev; + d = device_get_softc(dev); pdev = mixer_get_devt(dev); + if (d == NULL || pdev == NULL || pdev->si_drv1 == NULL) + return EBADF; m = pdev->si_drv1; snd_mtxlock(m->lock); @@ -245,10 +288,9 @@ mixer_uninit(device_t dev) } pdev->si_drv1 = NULL; - + release_dev(pdev); unit = device_get_unit(dev); - dev_ops_remove(&mixer_ops, - PCMMKMINOR(-1, -1, 0), PCMMKMINOR(unit, SND_DEV_CTL, 0)); + dev_ops_remove(&mixer_cdevsw, -1, PCMMKMINOR(unit, SND_DEV_CTL, 0)); for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) mixer_set(m, i, 0); @@ -257,9 +299,12 @@ mixer_uninit(device_t dev) MIXER_UNINIT(m); + snd_mtxunlock(m->lock); snd_mtxfree(m->lock); kobj_delete((kobj_t)m, M_MIXER); + d->mixer_dev = NULL; + return 0; } @@ -267,7 +312,7 @@ int mixer_reinit(device_t dev) { struct snd_mixer *m; - cdev_t pdev; + struct cdev *pdev; int i; pdev = mixer_get_devt(dev); @@ -300,7 +345,9 @@ sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) m = oidp->oid_arg1; snd_mtxlock(m->lock); strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname)); + snd_mtxunlock(m->lock); error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req); + snd_mtxlock(m->lock); if (error == 0 && req->newptr != NULL) { dev = mixer_lookup(devname); if (dev == -1) { @@ -321,11 +368,10 @@ int mixer_hwvol_init(device_t dev) { struct snd_mixer *m; - cdev_t pdev; + struct cdev *pdev; pdev = mixer_get_devt(dev); m = pdev->si_drv1; - snd_mtxlock(m->lock); m->hwvol_mixer = SOUND_MIXER_VOLUME; m->hwvol_step = 5; @@ -334,9 +380,8 @@ mixer_hwvol_init(device_t dev) OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, - sysctl_hw_snd_hwvol_mixer, "A", "") + sysctl_hw_snd_hwvol_mixer, "A", ""); #endif - snd_mtxunlock(m->lock); return 0; } @@ -344,7 +389,7 @@ void mixer_hwvol_mute(device_t dev) { struct snd_mixer *m; - cdev_t pdev; + struct cdev *pdev; pdev = mixer_get_devt(dev); m = pdev->si_drv1; @@ -365,7 +410,7 @@ mixer_hwvol_step(device_t dev, int left_step, int right_step) { struct snd_mixer *m; int level, left, right; - cdev_t pdev; + struct cdev *pdev; pdev = mixer_get_devt(dev); m = pdev->si_drv1; @@ -394,68 +439,69 @@ mixer_hwvol_step(device_t dev, int left_step, int right_step) static int mixer_open(struct dev_open_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; struct snd_mixer *m; - m = dev->si_drv1; - crit_enter(); + m = i_dev->si_drv1; snd_mtxlock(m->lock); m->busy++; snd_mtxunlock(m->lock); - crit_exit(); return 0; } static int mixer_close(struct dev_close_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; struct snd_mixer *m; - m = dev->si_drv1; - crit_enter(); + m = i_dev->si_drv1; snd_mtxlock(m->lock); if (!m->busy) { snd_mtxunlock(m->lock); - crit_exit(); return EBADF; } m->busy--; snd_mtxunlock(m->lock); - crit_exit(); return 0; } int mixer_ioctl(struct dev_ioctl_args *ap) { - cdev_t dev = ap->a_head.a_dev; + struct cdev *i_dev = ap->a_head.a_dev; + u_long cmd = ap->a_cmd; caddr_t arg = ap->a_data; + int mode = ap->a_fflag; struct snd_mixer *m; int ret, *arg_i = (int *)arg; - int v = -1, j = ap->a_cmd & 0xff; + int v = -1, j = cmd & 0xff; + + m = i_dev->si_drv1; - m = dev->si_drv1; - if (!m->busy) + if (m == NULL) return EBADF; - crit_enter(); snd_mtxlock(m->lock); - if ((ap->a_cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + if (mode != -1 && !m->busy) { + snd_mtxunlock(m->lock); + return EBADF; + } + + 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); snd_mtxunlock(m->lock); - crit_exit(); return (ret == 0)? 0 : ENXIO; } - if ((ap->a_cmd & MIXER_READ(0)) == MIXER_READ(0)) { + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { switch (j) { case SOUND_MIXER_DEVMASK: case SOUND_MIXER_CAPS: @@ -476,27 +522,27 @@ mixer_ioctl(struct dev_ioctl_args *ap) } *arg_i = v; snd_mtxunlock(m->lock); - crit_exit(); return (v != -1)? 0 : ENXIO; } snd_mtxunlock(m->lock); - crit_exit(); return ENXIO; } #ifdef USING_DEVFS static void -mixer_clone(void *arg, char *name, int namelen, cdev_t *dev) +mixer_clone(void *arg, struct ucred *cred, char *name, int namelen, + struct cdev **dev) { - cdev_t pdev; + struct snddev_info *sd; - if (*dev != NOCDEV) + if (*dev != NULL) return; if (strcmp(name, "mixer") == 0) { - pdev = make_adhoc_dev(&mixer_ops, - PCMMKMINOR(snd_unit, SND_DEV_CTL, 0)); - if (pdev->si_flags & SI_NAMED) - *dev = pdev; + sd = devclass_get_softc(pcm_devclass, snd_unit); + if (sd != NULL && sd->mixer_dev != NULL) { + *dev = sd->mixer_dev; + dev_ref(*dev); + } } } diff --git a/sys/dev/sound/pcm/mixer.h b/sys/dev/sound/pcm/mixer.h index 6874edb2f6..92219083c9 100644 --- a/sys/dev/sound/pcm/mixer.h +++ b/sys/dev/sound/pcm/mixer.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,14 +23,14 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/mixer.h,v 1.2.2.6 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/mixer.h,v 1.4 2006/07/28 02:17:38 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/mixer.h,v 1.14 2005/01/06 01:43:21 imp Exp $ + * $DragonFly: src/sys/dev/sound/pcm/mixer.h,v 1.5 2007/01/04 21:47:03 corecode Exp $ */ int mixer_init(device_t dev, kobj_class_t cls, void *devinfo); int mixer_uninit(device_t dev); int mixer_reinit(device_t dev); -int mixer_ioctl(struct dev_ioctl_args *); +d_ioctl_t mixer_ioctl; int mixer_hwvol_init(device_t dev); void mixer_hwvol_mute(device_t dev); diff --git a/sys/dev/sound/pcm/mixer_if.m b/sys/dev/sound/pcm/mixer_if.m index 95c5b33869..5497dd8f74 100644 --- a/sys/dev/sound/pcm/mixer_if.m +++ b/sys/dev/sound/pcm/mixer_if.m @@ -1,3 +1,4 @@ +#- # KOBJ # # Copyright (c) 2000 Cameron Grant @@ -24,8 +25,8 @@ # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # -# $FreeBSD: src/sys/dev/sound/pcm/mixer_if.m,v 1.1.2.3 2002/04/22 15:49:36 cg Exp $ -# $DragonFly: src/sys/dev/sound/pcm/mixer_if.m,v 1.3 2003/11/16 22:30:49 asmodai Exp $ +# $FreeBSD: src/sys/dev/sound/pcm/mixer_if.m,v 1.6 2005/01/06 01:43:21 imp Exp $ +# $DragonFly: src/sys/dev/sound/pcm/mixer_if.m,v 1.4 2007/01/04 21:47:03 corecode Exp $ # #include @@ -65,6 +66,3 @@ METHOD u_int32_t setrecsrc { struct snd_mixer *m; u_int32_t src; }; - - - diff --git a/sys/dev/sound/pcm/sndstat.c b/sys/dev/sound/pcm/sndstat.c index 3ca2f5ebd7..bc5885982d 100644 --- a/sys/dev/sound/pcm/sndstat.c +++ b/sys/dev/sound/pcm/sndstat.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2001 Cameron Grant +/*- + * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,14 +23,17 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/sndstat.c,v 1.4.2.2 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.10 2006/09/05 00:55:43 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/sndstat.c,v 1.20.2.2 2005/12/30 19:55:54 netchild Exp $ + * $DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.11 2007/01/04 21:47:03 corecode Exp $ */ #include #include +#ifdef USING_MUTEX +#include +#endif -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.10 2006/09/05 00:55:43 dillon Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sndstat.c,v 1.11 2007/01/04 21:47:03 corecode Exp $"); #define SS_TYPE_MODULE 0 #define SS_TYPE_FIRST 1 @@ -43,8 +46,9 @@ static d_open_t sndstat_open; static d_close_t sndstat_close; static d_read_t sndstat_read; -static struct dev_ops sndstat_ops = { +static struct dev_ops sndstat_cdevsw = { { "sndstat", SND_CDEV_MAJOR, 0 }, + /* .d_flags = D_NEEDGIANT, */ .d_open = sndstat_open, .d_close = sndstat_close, .d_read = sndstat_read, @@ -58,6 +62,9 @@ struct sndstat_entry { int type, unit; }; +#ifdef USING_MUTEX +static struct lock sndstat_lock; +#endif static struct sbuf sndstat_sbuf; static int sndstat_isopen = 0; static int sndstat_bufptr; @@ -83,12 +90,12 @@ sysctl_hw_sndverbose(SYSCTL_HANDLER_ARGS) verbose = sndstat_verbose; error = sysctl_handle_int(oidp, &verbose, sizeof(verbose), req); if (error == 0 && req->newptr != NULL) { - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); if (verbose < 0 || verbose > 3) error = EINVAL; else sndstat_verbose = verbose; - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); } return error; } @@ -98,60 +105,61 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, verbose, CTLTYPE_INT | CTLFLAG_RW, static int sndstat_open(struct dev_open_args *ap) { - int err; + int error; - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); if (sndstat_isopen) { - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return EBUSY; } + sndstat_isopen = 1; + lockmgr(&sndstat_lock, LK_RELEASE); if (sbuf_new(&sndstat_sbuf, NULL, 4096, 0) == NULL) { - crit_exit(); - return ENXIO; + error = ENXIO; + goto out; } sndstat_bufptr = 0; - err = (sndstat_prepare(&sndstat_sbuf) > 0)? 0 : ENOMEM; - if (!err) - sndstat_isopen = 1; - - crit_exit(); - return err; + error = (sndstat_prepare(&sndstat_sbuf) > 0) ? 0 : ENOMEM; +out: + if (error) { + lockmgr(&sndstat_lock, LK_EXCLUSIVE); + sndstat_isopen = 0; + lockmgr(&sndstat_lock, LK_RELEASE); + } + return (error); } static int sndstat_close(struct dev_close_args *ap) { - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); if (!sndstat_isopen) { - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return EBADF; } sbuf_delete(&sndstat_sbuf); sndstat_isopen = 0; - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return 0; } static int sndstat_read(struct dev_read_args *ap) { - struct uio *uio = ap->a_uio; + struct uio *buf = ap->a_uio; int l, err; - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); if (!sndstat_isopen) { - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return EBADF; } - l = min(uio->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); - if (l > 0) - err = uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, uio); - else - err = 0; + l = min(buf->uio_resid, sbuf_len(&sndstat_sbuf) - sndstat_bufptr); + err = (l > 0)? uiomove(sbuf_data(&sndstat_sbuf) + sndstat_bufptr, l, buf) : 0; sndstat_bufptr += l; - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return err; } @@ -170,6 +178,32 @@ sndstat_find(int type, int unit) return NULL; } +int +sndstat_acquire(void) +{ + lockmgr(&sndstat_lock, LK_EXCLUSIVE); + if (sndstat_isopen) { + lockmgr(&sndstat_lock, LK_RELEASE); + return EBUSY; + } + sndstat_isopen = 1; + lockmgr(&sndstat_lock, LK_RELEASE); + return 0; +} + +int +sndstat_release(void) +{ + lockmgr(&sndstat_lock, LK_EXCLUSIVE); + if (!sndstat_isopen) { + lockmgr(&sndstat_lock, LK_RELEASE); + return EBADF; + } + sndstat_isopen = 0; + lockmgr(&sndstat_lock, LK_RELEASE); + return 0; +} + int sndstat_register(device_t dev, char *str, sndstat_handler handler) { @@ -203,12 +237,12 @@ sndstat_register(device_t dev, char *str, sndstat_handler handler) ent->unit = unit; ent->handler = handler; - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); SLIST_INSERT_HEAD(&sndstat_devlist, ent, link); if (type == SS_TYPE_MODULE) sndstat_files++; sndstat_maxunit = (unit > sndstat_maxunit)? unit : sndstat_maxunit; - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return 0; } @@ -224,17 +258,17 @@ sndstat_unregister(device_t dev) { struct sndstat_entry *ent; - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); SLIST_FOREACH(ent, &sndstat_devlist, link) { if (ent->dev == dev) { SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); + lockmgr(&sndstat_lock, LK_RELEASE); kfree(ent, M_DEVBUF); - crit_exit(); return 0; } } - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return ENXIO; } @@ -244,18 +278,18 @@ sndstat_unregisterfile(char *str) { struct sndstat_entry *ent; - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); SLIST_FOREACH(ent, &sndstat_devlist, link) { if (ent->dev == NULL && ent->str == str) { SLIST_REMOVE(&sndstat_devlist, ent, sndstat_entry, link); - kfree(ent, M_DEVBUF); sndstat_files--; - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); + kfree(ent, M_DEVBUF); return 0; } } - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return ENXIO; } @@ -309,29 +343,28 @@ sndstat_prepare(struct sbuf *s) static int sndstat_init(void) { - dev_ops_add(&sndstat_ops, -1, SND_DEV_STATUS); - make_dev(&sndstat_ops, SND_DEV_STATUS, - UID_ROOT, GID_WHEEL, 0444, "sndstat"); - return (0); + lockinit(&sndstat_lock, "sndstat", 0, 0); + if (make_dev(&sndstat_cdevsw, SND_DEV_STATUS, + UID_ROOT, GID_WHEEL, 0444, "sndstat") == NOCDEV) + return ENXIO; + dev_ops_add(&sndstat_cdevsw, -1, SND_DEV_STATUS); + + return 0; } static int sndstat_uninit(void) { - crit_enter(); + lockmgr(&sndstat_lock, LK_EXCLUSIVE); if (sndstat_isopen) { - crit_exit(); + lockmgr(&sndstat_lock, LK_RELEASE); return EBUSY; } - dev_ops_remove(&sndstat_ops, -1, SND_DEV_STATUS); - crit_exit(); - return 0; -} -int -sndstat_busy(void) -{ - return (sndstat_isopen); + dev_ops_remove(&sndstat_cdevsw, -1, SND_DEV_STATUS); + + lockmgr(&sndstat_lock, LK_RELEASE); + return 0; } static void @@ -343,7 +376,10 @@ sndstat_sysinit(void *p) static void sndstat_sysuninit(void *p) { - sndstat_uninit(); + int error; + + error = sndstat_uninit(); + KASSERT(error == 0, ("%s: error = %d", __func__, error)); } SYSINIT(sndstat_sysinit, SI_SUB_DRIVERS, SI_ORDER_FIRST, sndstat_sysinit, NULL); diff --git a/sys/dev/sound/pcm/sound.c b/sys/dev/sound/pcm/sound.c index ac536c71c0..2622f9fd2b 100644 --- a/sys/dev/sound/pcm/sound.c +++ b/sys/dev/sound/pcm/sound.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * (C) 1997 Luigi Rizzo (luigi@iet.unipi.it) * All rights reserved. * @@ -24,37 +24,18 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.17.2.14 2002/11/07 23:17:18 cognet Exp $ - * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.7 2006/12/20 18:14:41 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/sound.c,v 1.93.2.3 2006/04/04 17:43:48 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode Exp $ */ #include #include +#include #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.7 2006/12/20 18:14:41 dillon Exp $"); - -struct snddev_channel { - SLIST_ENTRY(snddev_channel) link; - struct pcm_channel *channel; -}; - -struct snddev_info { - SLIST_HEAD(, snddev_channel) channels; - struct pcm_channel *fakechan; - unsigned devcount, playcount, reccount, vchancount; - unsigned flags; - int inprog; - unsigned int bufsz; - void *devinfo; - device_t dev; - char status[SND_STATUSLEN]; - struct sysctl_ctx_list sysctl_tree; - struct sysctl_oid *sysctl_tree_top; - void *lock; -}; +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/sound.c,v 1.8 2007/01/04 21:47:03 corecode Exp $"); devclass_t pcm_devclass; @@ -75,7 +56,7 @@ static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); struct sysctl_ctx_list * snd_sysctl_tree(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return &d->sysctl_tree; } @@ -83,7 +64,7 @@ snd_sysctl_tree(device_t dev) struct sysctl_oid * snd_sysctl_tree_top(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return d->sysctl_tree_top; } @@ -92,12 +73,12 @@ void * snd_mtxcreate(const char *desc, const char *type) { #ifdef USING_MUTEX - struct mtx *m; + struct spinlock *m; m = kmalloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); if (m == NULL) return NULL; - mtx_init(m, desc, type, MTX_RECURSE); + spin_init(m); return m; #else return (void *)0xcafebabe; @@ -108,10 +89,10 @@ void snd_mtxfree(void *m) { #ifdef USING_MUTEX - struct mtx *mtx = m; + struct spinlock *mtx = m; - mtx_assert(mtx, MA_OWNED); - mtx_destroy(mtx); + /* mtx_assert(mtx, MA_OWNED); */ + spin_uninit(mtx); kfree(mtx, M_DEVBUF); #endif } @@ -121,20 +102,18 @@ snd_mtxassert(void *m) { #ifdef USING_MUTEX #ifdef INVARIANTS - struct mtx *mtx = m; - - mtx_assert(mtx, MA_OWNED); + /* XXX can't assert spinlocks */ #endif #endif } - +/* void snd_mtxlock(void *m) { #ifdef USING_MUTEX - struct mtx *mtx = m; + struct spinlock *mtx = m; - mtx_lock(mtx); + spin_lock_wr(mtx); #endif } @@ -142,29 +121,25 @@ void snd_mtxunlock(void *m) { #ifdef USING_MUTEX - struct mtx *mtx = m; + struct spinlock *mtx = m; - mtx_unlock(mtx); + spin_unlock_wr(mtx); #endif } - +*/ int -snd_setup_intr(device_t dev, struct resource *res, int flags, - driver_intr_t hand, void *param, void **cookiep, - lwkt_serialize_t serializer) +snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) { - int error; - #ifdef USING_MUTEX flags &= INTR_MPSAFE; + flags |= INTR_TYPE_AV; #else - flags = 0; + flags = INTR_TYPE_AV; #endif - error = bus_setup_intr(dev, res, flags, hand, param, - cookiep, serializer); - return (error); + return bus_setup_intr(dev, res, flags, hand, param, cookiep, NULL); } +#ifndef PCM_DEBUG_MTX void pcm_lock(struct snddev_info *d) { @@ -176,6 +151,7 @@ pcm_unlock(struct snddev_info *d) { snd_mtxunlock(d->lock); } +#endif struct pcm_channel * pcm_getfakechan(struct snddev_info *d) @@ -183,48 +159,161 @@ pcm_getfakechan(struct snddev_info *d) return d->fakechan; } -/* return a locked channel */ -struct pcm_channel * -pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum) +static int +pcm_setvchans(struct snddev_info *d, int newcnt) +{ + struct snddev_channel *sce = NULL; + struct pcm_channel *c = NULL; + int err = 0, vcnt, dcnt, i; + + pcm_inprog(d, 1); + + if (!(d->flags & SD_F_AUTOVCHAN)) { + err = EINVAL; + goto setvchans_out; + } + + vcnt = d->vchancount; + dcnt = d->playcount + d->reccount; + + if (newcnt < 0 || (dcnt + newcnt) > (PCMMAXCHAN + 1)) { + err = E2BIG; + goto setvchans_out; + } + + dcnt += vcnt; + + if (newcnt > vcnt) { + /* add new vchans - find a parent channel first */ + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + ((c->flags & CHN_F_HAS_VCHAN) || + (vcnt == 0 && + !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) + goto addok; + CHN_UNLOCK(c); + } + err = EBUSY; + goto setvchans_out; +addok: + c->flags |= CHN_F_BUSY; + while (err == 0 && newcnt > vcnt) { + if (dcnt > PCMMAXCHAN) { + device_printf(d->dev, "%s: Maximum channel reached.\n", __func__); + break; + } + err = vchan_create(c); + if (err == 0) { + vcnt++; + dcnt++; + } else if (err == E2BIG && newcnt > vcnt) + device_printf(d->dev, "%s: err=%d Maximum channel reached.\n", __func__, err); + } + if (vcnt == 0) + c->flags &= ~CHN_F_BUSY; + CHN_UNLOCK(c); + } else if (newcnt < vcnt) { +#define ORPHAN_CDEVT(cdevt) \ + ((cdevt) == NULL || ((cdevt)->si_drv1 == NULL && \ + (cdevt)->si_drv2 == NULL)) + while (err == 0 && newcnt < vcnt) { + i = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + (c->flags & CHN_F_VIRTUAL) && + (i++ == newcnt)) { + if (!(c->flags & CHN_F_BUSY) && + ORPHAN_CDEVT(sce->dsp_devt) && + ORPHAN_CDEVT(sce->dspW_devt) && + ORPHAN_CDEVT(sce->audio_devt) && + ORPHAN_CDEVT(sce->dspr_devt)) + goto remok; + /* + * Either we're busy, or our cdev + * has been stolen by dsp_clone(). + * Skip, and increase newcnt. + */ + if (!(c->flags & CHN_F_BUSY)) + device_printf(d->dev, + "%s: <%s> somebody steal my cdev!\n", + __func__, c->name); + newcnt++; + } + CHN_UNLOCK(c); + } + if (vcnt != newcnt) + err = EBUSY; + break; +remok: + CHN_UNLOCK(c); + err = vchan_destroy(c); + if (err == 0) + vcnt--; + else + device_printf(d->dev, + "%s: WARNING: vchan_destroy() failed!", + __func__); + } + } + +setvchans_out: + pcm_inprog(d, -1); + return err; +} + +/* return error status and a locked channel */ +int +pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, + pid_t pid, int chnum) { struct pcm_channel *c; - struct snddev_channel *sce; + struct snddev_channel *sce; int err; - snd_mtxassert(d->lock); - +retry_chnalloc: + err = ENODEV; /* scan for a free channel */ SLIST_FOREACH(sce, &d->channels, link) { c = sce->channel; CHN_LOCK(c); - if ((c->direction == direction) && !(c->flags & CHN_F_BUSY)) { - if (chnum == -1 || c->num == chnum) { + if (c->direction == direction && !(c->flags & CHN_F_BUSY)) { + if (chnum < 0 || sce->chan_num == chnum) { c->flags |= CHN_F_BUSY; c->pid = pid; - return c; + *ch = c; + return 0; } } + if (sce->chan_num == chnum) { + if (c->direction != direction) + err = EOPNOTSUPP; + else if (c->flags & CHN_F_BUSY) + err = EBUSY; + else + err = EINVAL; + CHN_UNLOCK(c); + return err; + } else if (c->direction == direction && (c->flags & CHN_F_BUSY)) + err = EBUSY; CHN_UNLOCK(c); } /* no channel available */ - if (direction == PCMDIR_PLAY) { - if ((d->vchancount > 0) && (d->vchancount < snd_maxautovchans)) { - /* try to create a vchan */ - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if (!SLIST_EMPTY(&c->children)) { - err = vchan_create(c); - if (!err) - return pcm_chnalloc(d, direction, pid, -1); - else - device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); - } - } + if (chnum == -1 && direction == PCMDIR_PLAY && d->vchancount > 0 && + d->vchancount < snd_maxautovchans && + d->devcount <= PCMMAXCHAN) { + err = pcm_setvchans(d, d->vchancount + 1); + if (err == 0) { + chnum = -2; + goto retry_chnalloc; } } - return NULL; + return err; } /* release a locked channel and unlock it */ @@ -252,47 +341,26 @@ pcm_chnref(struct pcm_channel *c, int ref) int pcm_inprog(struct snddev_info *d, int delta) { + int r; + + if (delta == 0) + return d->inprog; + + /* backtrace(); */ + pcm_lock(d); d->inprog += delta; - return d->inprog; + r = d->inprog; + pcm_unlock(d); + return r; } static void pcm_setmaxautovchans(struct snddev_info *d, int num) { - struct pcm_channel *c; - struct snddev_channel *sce; - int err, done; - - if (num > 0 && d->vchancount == 0) { - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if ((c->direction == PCMDIR_PLAY) && !(c->flags & CHN_F_BUSY)) { - c->flags |= CHN_F_BUSY; - err = vchan_create(c); - if (err) { - device_printf(d->dev, "vchan_create(%s) == %d\n", c->name, err); - c->flags &= ~CHN_F_BUSY; - } - return; - } - } - } - if (num == 0 && d->vchancount > 0) { - done = 0; - while (!done) { - done = 1; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if ((c->flags & CHN_F_VIRTUAL) && !(c->flags & CHN_F_BUSY)) { - done = 0; - err = vchan_destroy(c); - if (err) - device_printf(d->dev, "vchan_destroy(%s) == %d\n", c->name, err); - break; - } - } - } - } + if (num > 0 && d->vchancount == 0) + pcm_setvchans(d, 1); + else if (num == 0 && d->vchancount > 0) + pcm_setvchans(d, 0); } #ifdef USING_DEVFS @@ -327,9 +395,9 @@ sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) v = snd_maxautovchans; error = sysctl_handle_int(oidp, &v, sizeof(v), req); if (error == 0 && req->newptr != NULL) { - if (v < 0 || v >= SND_MAXVCHANS) - return EINVAL; - if (v != snd_maxautovchans) { + if (v < 0 || v > PCMMAXCHAN) + return E2BIG; + if (pcm_devclass != NULL && v != snd_maxautovchans) { for (i = 0; i < devclass_get_maxunit(pcm_devclass); i++) { d = devclass_get_softc(pcm_devclass, i); if (!d) @@ -347,25 +415,30 @@ SYSCTL_PROC(_hw_snd, OID_AUTO, maxautovchans, CTLTYPE_INT | CTLFLAG_RW, struct pcm_channel * pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo) { - struct pcm_channel *ch; + struct snddev_channel *sce; + struct pcm_channel *ch, *c; char *dirs; - int err, *pnum; + uint32_t flsearch = 0; + int direction, err, rpnum, *pnum; switch(dir) { case PCMDIR_PLAY: dirs = "play"; + direction = PCMDIR_PLAY; pnum = &d->playcount; break; case PCMDIR_REC: dirs = "record"; + direction = PCMDIR_REC; pnum = &d->reccount; break; case PCMDIR_VIRTUAL: dirs = "virtual"; - dir = PCMDIR_PLAY; + direction = PCMDIR_PLAY; pnum = &d->vchancount; + flsearch = CHN_F_VIRTUAL; break; default: @@ -383,20 +456,64 @@ pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t c return NULL; } - ch->num = (*pnum)++; + snd_mtxlock(d->lock); + ch->num = 0; + rpnum = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + if (direction != c->direction || + (c->flags & CHN_F_VIRTUAL) != flsearch) + continue; + if (ch->num == c->num) + ch->num++; + else { +#if 0 + device_printf(d->dev, + "%s: %s channel numbering screwed (Expect: %d, Got: %d)\n", + __func__, dirs, ch->num, c->num); +#endif + goto retry_num_search; + } + rpnum++; + } + goto retry_num_search_out; +retry_num_search: + rpnum = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + if (direction != c->direction || + (c->flags & CHN_F_VIRTUAL) != flsearch) + continue; + if (ch->num == c->num) { + ch->num++; + goto retry_num_search; + } + rpnum++; + } +retry_num_search_out: + if (*pnum != rpnum) { + device_printf(d->dev, + "%s: WARNING: pnum screwed : dirs=%s, pnum=%d, rpnum=%d\n", + __func__, dirs, *pnum, rpnum); + *pnum = rpnum; + } + (*pnum)++; + snd_mtxunlock(d->lock); ch->pid = -1; ch->parentsnddev = d; ch->parentchannel = parent; ch->dev = d->dev; - ksnprintf(ch->name, 32, "%s:%s:%d", device_get_nameunit(d->dev), dirs, ch->num); + ksnprintf(ch->name, CHN_NAMELEN, "%s:%s:%d", device_get_nameunit(ch->dev), dirs, ch->num); - err = chn_init(ch, devinfo, dir); + err = chn_init(ch, devinfo, dir, direction); if (err) { device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); kobj_delete(ch->methods, M_DEVBUF); kfree(ch, M_DEVBUF); + snd_mtxlock(d->lock); (*pnum)--; + snd_mtxunlock(d->lock); return NULL; } @@ -417,13 +534,6 @@ pcm_chn_destroy(struct pcm_channel *ch) return err; } - if (ch->direction == PCMDIR_REC) - d->reccount--; - else if (ch->flags & CHN_F_VIRTUAL) - d->vchancount--; - else - d->playcount--; - kobj_delete(ch->methods, M_DEVBUF); kfree(ch, M_DEVBUF); @@ -431,64 +541,196 @@ pcm_chn_destroy(struct pcm_channel *ch) } int -pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev) +pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { - struct snddev_channel *sce, *tmp, *after; - int unit = device_get_unit(d->dev); - - snd_mtxlock(d->lock); + struct snddev_channel *sce, *tmp, *after; + unsigned rdevcount; + int device = device_get_unit(d->dev); + size_t namelen; + + /* + * Note it's confusing nomenclature. + * dev_t + * device -> pcm_device + * unit -> pcm_channel + * channel -> snddev_channel + * device_t + * unit -> pcm_device + */ sce = kmalloc(sizeof(*sce), M_DEVBUF, M_WAITOK | M_ZERO); if (!sce) { - snd_mtxunlock(d->lock); return ENOMEM; } + snd_mtxlock(d->lock); sce->channel = ch; - if (SLIST_EMPTY(&d->channels)) { + sce->chan_num = 0; + rdevcount = 0; + after = NULL; + SLIST_FOREACH(tmp, &d->channels, link) { + if (sce->chan_num == tmp->chan_num) + sce->chan_num++; + else { +#if 0 + device_printf(d->dev, + "%s: cdev numbering screwed (Expect: %d, Got: %d)\n", + __func__, sce->chan_num, tmp->chan_num); +#endif + goto retry_chan_num_search; + } + after = tmp; + rdevcount++; + } + goto retry_chan_num_search_out; +retry_chan_num_search: + /* + * Look for possible channel numbering collision. This may not + * be optimized, but it will ensure that no collision occured. + * Can be considered cheap since none of the locking/unlocking + * operations involved. + */ + rdevcount = 0; + after = NULL; + SLIST_FOREACH(tmp, &d->channels, link) { + if (sce->chan_num == tmp->chan_num) { + sce->chan_num++; + goto retry_chan_num_search; + } + if (sce->chan_num > tmp->chan_num) + after = tmp; + rdevcount++; + } +retry_chan_num_search_out: + /* + * Don't overflow PCMMKMINOR / PCMMAXCHAN. + */ + if (sce->chan_num > PCMMAXCHAN) { + snd_mtxunlock(d->lock); + device_printf(d->dev, + "%s: WARNING: sce->chan_num overflow! (%d)\n", + __func__, sce->chan_num); + kfree(sce, M_DEVBUF); + return E2BIG; + } + if (d->devcount != rdevcount) { + device_printf(d->dev, + "%s: WARNING: devcount screwed! d->devcount=%u, rdevcount=%u\n", + __func__, d->devcount, rdevcount); + d->devcount = rdevcount; + } + d->devcount++; + if (after == NULL) { SLIST_INSERT_HEAD(&d->channels, sce, link); } else { - after = NULL; + SLIST_INSERT_AFTER(after, sce, link); + } +#if 0 + if (1) { + int cnum = 0; SLIST_FOREACH(tmp, &d->channels, link) { - after = tmp; + if (cnum != tmp->chan_num) + device_printf(d->dev, + "%s: WARNING: inconsistent cdev numbering! (Expect: %d, Got: %d)\n", + __func__, cnum, tmp->chan_num); + cnum++; } - SLIST_INSERT_AFTER(after, sce, link); } +#endif - if (mkdev) { - dsp_register(unit, d->devcount++); - if (ch->direction == PCMDIR_REC) - dsp_registerrec(unit, ch->num); + namelen = strlen(ch->name); + if ((CHN_NAMELEN - namelen) > 10) { /* ":dspXX.YYY" */ + ksnprintf(ch->name + namelen, + CHN_NAMELEN - namelen, ":dsp%d.%d", + device, sce->chan_num); } - snd_mtxunlock(d->lock); + /* + * I will revisit these someday, and nuke it mercilessly.. + */ + dev_ops_add(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num)); + sce->dsp_devt = make_dev(&dsp_cdevsw, + PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), + UID_ROOT, GID_WHEEL, 0666, "dsp%d.%d", + device, sce->chan_num); + reference_dev(sce->dsp_devt); + + dev_ops_add(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num)); + sce->dspW_devt = make_dev(&dsp_cdevsw, + PCMMKMINOR(device, SND_DEV_DSP16, sce->chan_num), + UID_ROOT, GID_WHEEL, 0666, "dspW%d.%d", + device, sce->chan_num); + reference_dev(sce->dspW_devt); + + /* + dev_ops_add(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num)); + sce->audio_devt = make_dev(&dsp_cdevsw, + PCMMKMINOR(device, SND_DEV_DSP, sce->chan_num), + UID_ROOT, GID_WHEEL, 0666, "audio%d.%d", + device, sce->chan_num); + */ + sce->audio_devt = sce->dsp_devt; + reference_dev(sce->audio_devt); + + if (ch->direction == PCMDIR_REC) { + dev_ops_add(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(device, SND_DEV_DSPREC, sce->chan_num)); + sce->dspr_devt = make_dev(&dsp_cdevsw, + PCMMKMINOR(device, SND_DEV_DSPREC, + sce->chan_num), UID_ROOT, GID_WHEEL, + 0666, "dspr%d.%d", device, sce->chan_num); + reference_dev(sce->dspr_devt); + } + return 0; } int -pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev) +pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch) { - struct snddev_channel *sce; - int unit = device_get_unit(d->dev); + struct snddev_channel *sce; +#if 0 + int ourlock; + + ourlock = 0; + if (!mtx_owned(d->lock)) { + snd_mtxlock(d->lock); + ourlock = 1; + } +#endif - snd_mtxlock(d->lock); SLIST_FOREACH(sce, &d->channels, link) { if (sce->channel == ch) goto gotit; } - snd_mtxunlock(d->lock); +#if 0 + if (ourlock) + snd_mtxunlock(d->lock); +#endif return EINVAL; gotit: SLIST_REMOVE(&d->channels, sce, snddev_channel, link); - kfree(sce, M_DEVBUF); - if (rmdev) { - dsp_unregister(unit, --d->devcount); - if (ch->direction == PCMDIR_REC) - dsp_unregisterrec(unit, ch->num); - } - snd_mtxunlock(d->lock); + if (ch->flags & CHN_F_VIRTUAL) + d->vchancount--; + else if (ch->direction == PCMDIR_REC) + d->reccount--; + else + d->playcount--; + +#if 0 + if (ourlock) + snd_mtxunlock(d->lock); +#endif + kfree(sce, M_DEVBUF); return 0; } @@ -496,9 +738,9 @@ gotit: int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); struct pcm_channel *ch; - int err; + int err; ch = pcm_chn_create(d, NULL, cls, dir, devinfo); if (!ch) { @@ -506,39 +748,28 @@ pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo) return ENODEV; } - err = pcm_chn_add(d, ch, 1); + err = pcm_chn_add(d, ch); if (err) { device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); pcm_chn_destroy(ch); return err; } - if (snd_maxautovchans > 0 && (d->flags & SD_F_AUTOVCHAN)) { - ch->flags |= CHN_F_BUSY; - err = vchan_create(ch); - if (err) { - device_printf(d->dev, "vchan_create(%s) == %d\n", ch->name, err); - ch->flags &= ~CHN_F_BUSY; - } - } - return err; } static int pcm_killchan(device_t dev) { - struct snddev_info *d = device_get_softc(dev); - struct snddev_channel *sce; + struct snddev_info *d = device_get_softc(dev); + struct snddev_channel *sce; struct pcm_channel *ch; - int error; + int error = 0; - snd_mtxlock(d->lock); sce = SLIST_FIRST(&d->channels); - snd_mtxunlock(d->lock); ch = sce->channel; - error = pcm_chn_remove(d, sce->channel, 1); + error = pcm_chn_remove(d, sce->channel); if (error) return (error); return (pcm_chn_destroy(ch)); @@ -547,26 +778,28 @@ pcm_killchan(device_t dev) int pcm_setstatus(device_t dev, char *str) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); snd_mtxlock(d->lock); strncpy(d->status, str, SND_STATUSLEN); snd_mtxunlock(d->lock); + if (snd_maxautovchans > 0) + pcm_setvchans(d, 1); return 0; } -u_int32_t +uint32_t pcm_getflags(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return d->flags; } void -pcm_setflags(device_t dev, u_int32_t val) +pcm_setflags(device_t dev, uint32_t val) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); d->flags = val; } @@ -574,7 +807,7 @@ pcm_setflags(device_t dev, u_int32_t val) void * pcm_getdevinfo(device_t dev) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); return d->devinfo; } @@ -582,7 +815,7 @@ pcm_getdevinfo(device_t dev) unsigned int pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); int sz, x; sz = 0; @@ -612,7 +845,7 @@ pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned i int pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { - struct snddev_info *d = device_get_softc(dev); + struct snddev_info *d = device_get_softc(dev); if (pcm_veto_load) { device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); @@ -621,9 +854,15 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) } d->lock = snd_mtxcreate(device_get_nameunit(dev), "sound cdev"); - snd_mtxlock(d->lock); +#if 0 + /* + * d->flags should be cleared by the allocator of the softc. + * We cannot clear this field here because several devices set + * this flag before calling pcm_register(). + */ d->flags = 0; +#endif d->dev = dev; d->devinfo = devinfo; d->devcount = 0; @@ -632,11 +871,13 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) d->vchancount = 0; d->inprog = 0; - if (((numplay == 0) || (numrec == 0)) && (numplay != numrec)) + SLIST_INIT(&d->channels); + + if ((numplay == 0 || numrec == 0) && numplay != numrec) d->flags |= SD_F_SIMPLEX; d->fakechan = fkchan_setup(dev); - chn_init(d->fakechan, NULL, 0); + chn_init(d->fakechan, NULL, 0, 0); #ifdef SND_DYNSYSCTL sysctl_ctx_init(&d->sysctl_tree); @@ -650,14 +891,13 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) SYSCTL_ADD_INT(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, ""); #endif - if (numplay > 0) - vchan_initsys(dev); - if (numplay == 1) + if (numplay > 0) { d->flags |= SD_F_AUTOVCHAN; + vchan_initsys(dev); + } - snd_mtxunlock(d->lock); sndstat_register(dev, d->status, sndstat_prepare_pcm); - return 0; + return 0; no: snd_mtxfree(d->lock); return ENXIO; @@ -666,46 +906,122 @@ no: int pcm_unregister(device_t dev) { - struct snddev_info *d = device_get_softc(dev); - struct snddev_channel *sce; + struct snddev_info *d = device_get_softc(dev); + struct snddev_channel *sce; + struct pcmchan_children *pce; struct pcm_channel *ch; + if (sndstat_acquire() != 0) { + device_printf(dev, "unregister: sndstat busy\n"); + return EBUSY; + } + snd_mtxlock(d->lock); if (d->inprog) { device_printf(dev, "unregister: operation in progress\n"); snd_mtxunlock(d->lock); + sndstat_release(); return EBUSY; } - if (sndstat_busy() != 0) { - device_printf(dev, "unregister: sndstat busy\n"); - snd_mtxunlock(d->lock); - return EBUSY; - } + SLIST_FOREACH(sce, &d->channels, link) { ch = sce->channel; if (ch->refcount > 0) { device_printf(dev, "unregister: channel %s busy (pid %d)\n", ch->name, ch->pid); snd_mtxunlock(d->lock); + sndstat_release(); return EBUSY; } } - if (mixer_uninit(dev)) { + + if (mixer_uninit(dev) == EBUSY) { device_printf(dev, "unregister: mixer busy\n"); snd_mtxunlock(d->lock); + sndstat_release(); return EBUSY; } + SLIST_FOREACH(sce, &d->channels, link) { + int unit = device_get_unit(d->dev); + + if (sce->dsp_devt) { + release_dev(sce->dsp_devt); + dev_ops_remove(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(unit, SND_DEV_DSP, sce->chan_num)); + sce->dsp_devt = NULL; + } + if (sce->dspW_devt) { + release_dev(sce->dspW_devt); + dev_ops_remove(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num)); + sce->dspW_devt = NULL; + } + if (sce->audio_devt) { + release_dev(sce->audio_devt); + /* + dev_ops_remove(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(unit, SND_DEV_DSP16, sce->chan_num)); + */ + sce->audio_devt = NULL; + } + if (sce->dspr_devt) { + release_dev(sce->dspr_devt); + dev_ops_remove(&dsp_cdevsw, + PCMMKMINOR(-1, -1, 0), + PCMMKMINOR(unit, SND_DEV_DSPREC, sce->chan_num)); + sce->dspr_devt = NULL; + } + d->devcount--; + ch = sce->channel; + if (ch == NULL) + continue; + pce = SLIST_FIRST(&ch->children); + while (pce != NULL) { +#if 0 + device_printf(d->dev, "<%s> removing <%s>\n", + ch->name, (pce->channel != NULL) ? + pce->channel->name : "unknown"); +#endif + SLIST_REMOVE(&ch->children, pce, pcmchan_children, link); + kfree(pce, M_DEVBUF); + pce = SLIST_FIRST(&ch->children); + } + } + #ifdef SND_DYNSYSCTL d->sysctl_tree_top = NULL; sysctl_ctx_free(&d->sysctl_tree); #endif + +#if 0 + SLIST_FOREACH(sce, &d->channels, link) { + ch = sce->channel; + if (ch == NULL) + continue; + if (!SLIST_EMPTY(&ch->children)) + device_printf(d->dev, "%s: WARNING: <%s> dangling child!\n", + __func__, ch->name); + } +#endif while (!SLIST_EMPTY(&d->channels)) pcm_killchan(dev); chn_kill(d->fakechan); fkchan_kill(d->fakechan); +#if 0 + device_printf(d->dev, "%s: devcount=%u, playcount=%u, " + "reccount=%u, vchancount=%u\n", + __func__, d->devcount, d->playcount, d->reccount, + d->vchancount); +#endif + snd_mtxunlock(d->lock); snd_mtxfree(d->lock); + sndstat_unregister(dev); + sndstat_release(); return 0; } @@ -714,11 +1030,11 @@ pcm_unregister(device_t dev) static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) { - struct snddev_info *d; - struct snddev_channel *sce; + struct snddev_info *d; + struct snddev_channel *sce; struct pcm_channel *c; struct pcm_feeder *f; - int pc, rc, vc; + int pc, rc, vc; if (verbose < 1) return 0; @@ -748,12 +1064,21 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) "" #endif ); - if (verbose <= 1) - goto skipverbose; + + if (verbose <= 1) { + snd_mtxunlock(d->lock); + return 0; + } + SLIST_FOREACH(sce, &d->channels, link) { c = sce->channel; + + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, + ("hosed pcm channel setup")); + sbuf_printf(s, "\n\t"); + /* it would be better to indent child channels */ sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); sbuf_printf(s, "spd %d", c->speed); if (c->speed != sndbuf_getspd(c->bufhard)) @@ -761,21 +1086,27 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) sbuf_printf(s, ", fmt 0x%08x", c->format); if (c->format != sndbuf_getfmt(c->bufhard)) sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags %08x", c->flags); + sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); if (c->pid != -1) sbuf_printf(s, ", pid %d", c->pid); sbuf_printf(s, "\n\t"); - if (c->bufhard != NULL && c->bufsoft != NULL) { - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, hfree %d, sfree %d", - c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, ready %d", - c->xruns, sndbuf_getready(c->bufsoft)); - sbuf_printf(s, "\n\t"); - } + sbuf_printf(s, "interrupts %d, ", c->interrupts); + if (c->direction == PCMDIR_REC) + sbuf_printf(s, "overruns %d, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", + c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), + sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), + sndbuf_getblkcnt(c->bufhard), + sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), + sndbuf_getblkcnt(c->bufsoft)); + else + sbuf_printf(s, "underruns %d, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", + c->xruns, sndbuf_getready(c->bufsoft), + sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), + sndbuf_getblkcnt(c->bufhard), + sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), + sndbuf_getblkcnt(c->bufsoft)); + sbuf_printf(s, "\n\t"); sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); sbuf_printf(s, " -> "); @@ -788,17 +1119,16 @@ sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); if (f->desc->type == FEEDER_RATE) sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) + if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || + f->desc->type == FEEDER_VOLUME) sbuf_printf(s, "(0x%08x)", f->desc->out); sbuf_printf(s, " -> "); f = f->parent; } sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); } - } else { + } else sbuf_printf(s, " (mixer only)"); - } -skipverbose: snd_mtxunlock(d->lock); return 0; @@ -811,90 +1141,31 @@ int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct snddev_channel *sce; - struct pcm_channel *c; - int err, oldcnt, newcnt, cnt; + int err, newcnt; d = oidp->oid_arg1; - pcm_lock(d); - cnt = 0; - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if ((c->direction == PCMDIR_PLAY) && (c->flags & CHN_F_VIRTUAL)) - cnt++; - } - oldcnt = cnt; - newcnt = cnt; - + newcnt = d->vchancount; err = sysctl_handle_int(oidp, &newcnt, sizeof(newcnt), req); - if (err == 0 && req->newptr != NULL) { - if (newcnt < 0 || newcnt > SND_MAXVCHANS) { - pcm_unlock(d); - return EINVAL; - } - if (newcnt > cnt) { - /* add new vchans - find a parent channel first */ - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - /* not a candidate if not a play channel */ - if (c->direction != PCMDIR_PLAY) - continue; - /* not a candidate if a virtual channel */ - if (c->flags & CHN_F_VIRTUAL) - continue; - /* not a candidate if it's in use */ - if ((c->flags & CHN_F_BUSY) && (SLIST_EMPTY(&c->children))) - continue; - /* - * if we get here we're a nonvirtual play channel, and either - * 1) not busy - * 2) busy with children, not directly open - * - * thus we can add children - */ - goto addok; - } - pcm_unlock(d); - return EBUSY; -addok: - c->flags |= CHN_F_BUSY; - while (err == 0 && newcnt > cnt) { - err = vchan_create(c); - if (err == 0) - cnt++; - } - if (SLIST_EMPTY(&c->children)) - c->flags &= ~CHN_F_BUSY; - } else if (newcnt < cnt) { - while (err == 0 && newcnt < cnt) { - SLIST_FOREACH(sce, &d->channels, link) { - c = sce->channel; - if ((c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL)) == CHN_F_VIRTUAL) - goto remok; - } - pcm_unlock(d); - return EINVAL; -remok: - err = vchan_destroy(c); - if (err == 0) - cnt--; - } - } - } + if (err == 0 && req->newptr != NULL && d->vchancount != newcnt) + err = pcm_setvchans(d, newcnt); - pcm_unlock(d); return err; } #endif /************************************************************************/ -static moduledata_t sndpcm_mod = { - "snd_pcm", - NULL, - NULL -}; -DECLARE_MODULE(snd_pcm, sndpcm_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE); -MODULE_VERSION(snd_pcm, PCM_MODVER); +static int +sound_modevent(module_t mod, int type, void *data) +{ +#if 0 + return (midi_modevent(mod, type, data)); +#else + return 0; +#endif +} + +DEV_MODULE(sound, sound_modevent, NULL); +MODULE_VERSION(sound, SOUND_MODVER); diff --git a/sys/dev/sound/pcm/sound.h b/sys/dev/sound/pcm/sound.h index b01a49dc7b..9c3d1f4789 100644 --- a/sys/dev/sound/pcm/sound.h +++ b/sys/dev/sound/pcm/sound.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 1999 Cameron Grant +/*- + * Copyright (c) 1999 Cameron Grant * Copyright by Hannu Savolainen 1995 * All rights reserved. * @@ -24,8 +24,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/sound.h,v 1.10.2.11 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/sound.h,v 1.9 2006/10/25 20:56:02 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/sound.h,v 1.63.2.2 2006/04/04 17:43:48 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/sound.h,v 1.10 2007/01/04 21:47:03 corecode Exp $ */ /* @@ -54,6 +54,9 @@ #include #include #include +#include /* for DELAY */ +#include +#include #include #include #include @@ -65,18 +68,17 @@ #include /* for DELAY */ -#include - #include #include #undef USING_MUTEX #undef USING_DEVFS -#if defined(__FreeBSD__) && __FreeBSD_version > 500000 +#include +#include + #define USING_MUTEX -#define USING_DEVFS -#endif +#define INTR_TYPE_AV 0 #define SND_DYNSYSCTL @@ -89,18 +91,16 @@ struct snd_mixer; #include #include #include -#include #define PCM_SOFTC_SIZE 512 #define SND_STATUSLEN 64 -/* descriptor of audio device */ -#define PCM_MODVER 1 +#define SOUND_MODVER 1 -#define PCM_MINVER 1 -#define PCM_PREFVER PCM_MODVER -#define PCM_MAXVER 1 +#define SOUND_MINVER 1 +#define SOUND_PREFVER SOUND_MODVER +#define SOUND_MAXVER 1 /* PROPOSAL: @@ -121,14 +121,19 @@ nomenclature: [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)) +#define PCMMAXCHAN 0xff +#define PCMMAXDEV 0x0f +#define PCMMAXUNIT 0x0f +#define PCMMINOR(x) (minor(x)) +#define PCMCHAN(x) ((PCMMINOR(x) >> 16) & PCMMAXCHAN) +#define PCMUNIT(x) ((PCMMINOR(x) >> 4) & PCMMAXUNIT) +#define PCMDEV(x) (PCMMINOR(x) & PCMMAXDEV) +#define PCMMKMINOR(u, d, c) ((((c) & PCMMAXCHAN) << 16) | \ + (((u) & PCMMAXUNIT) << 4) | ((d) & PCMMAXDEV)) #define SD_F_SIMPLEX 0x00000001 -#define SD_F_AUTOVCHAN 0x00000002 +#define SD_F_AUTOVCHAN 0x00000002 +#define SD_F_SOFTVOL 0x00000004 #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) @@ -142,10 +147,13 @@ nomenclature: /* 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_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_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) +#define AFMT_8BIT (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S8) +#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \ + AFMT_S16_LE | AFMT_S16_BE | AFMT_S8) +#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \ + AFMT_S16_BE | AFMT_U16_BE) struct pcm_channel *fkchan_setup(device_t dev); int fkchan_kill(struct pcm_channel *c); @@ -155,7 +163,8 @@ int fkchan_kill(struct pcm_channel *c); */ #define SND_CDEV_MAJOR 30 -#define SND_MAXVCHANS 255 +/* XXX Flawed definition. I'll fix it someday. */ +#define SND_MAXVCHANS PCMMAXCHAN /* * Minor numbers for the sound driver. @@ -194,7 +203,6 @@ extern devclass_t pcm_devclass; * 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 @@ -206,18 +214,16 @@ SYSCTL_DECL(_hw_snd); struct sysctl_ctx_list *snd_sysctl_tree(device_t dev); struct sysctl_oid *snd_sysctl_tree_top(device_t dev); -void pcm_lock(struct snddev_info *d); -void pcm_unlock(struct snddev_info *d); struct pcm_channel *pcm_getfakechan(struct snddev_info *d); -struct pcm_channel *pcm_chnalloc(struct snddev_info *d, int direction, pid_t pid, int chnum); +int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int chnum); int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); int pcm_inprog(struct snddev_info *d, int delta); struct pcm_channel *pcm_chn_create(struct snddev_info *d, struct pcm_channel *parent, kobj_class_t cls, int dir, void *devinfo); int pcm_chn_destroy(struct pcm_channel *ch); -int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch, int mkdev); -int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch, int rmdev); +int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch); +int pcm_chn_remove(struct snddev_info *d, struct pcm_channel *ch); int pcm_addchan(device_t dev, int dir, kobj_class_t cls, void *devinfo); unsigned int pcm_getbuffersize(device_t dev, unsigned int min, unsigned int deflt, unsigned int max); @@ -228,24 +234,25 @@ u_int32_t pcm_getflags(device_t dev); void pcm_setflags(device_t dev, u_int32_t val); void *pcm_getdevinfo(device_t dev); + int snd_setup_intr(device_t dev, struct resource *res, int flags, - driver_intr_t hand, void *param, - void **cookiep, lwkt_serialize_t serializer); + driver_intr_t hand, void *param, void **cookiep); void *snd_mtxcreate(const char *desc, const char *type); void snd_mtxfree(void *m); void snd_mtxassert(void *m); -void snd_mtxlock(void *m); -void snd_mtxunlock(void *m); +#define snd_mtxlock(m) spin_lock_wr(m) +#define snd_mtxunlock(m) spin_unlock_wr(m) int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS); typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose); +int sndstat_acquire(void); +int sndstat_release(void); int sndstat_register(device_t dev, char *str, sndstat_handler handler); int sndstat_registerfile(char *str); int sndstat_unregister(device_t dev); int sndstat_unregisterfile(char *str); -int sndstat_busy(void); #define SND_DECLARE_FILE(version) \ _SND_DECLARE_FILE(__LINE__, version) @@ -262,10 +269,59 @@ int sndstat_busy(void); #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 */ +/* ought to be made obsolete but still used by mss */ #define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */ #define DV_F_DEV_SHIFT 8 /* force device type/class */ +#define PCM_DEBUG_MTX + +/* + * this is rather kludgey- we need to duplicate these struct def'ns from sound.c + * so that the macro versions of pcm_{,un}lock can dereference them. + * we also have to do this now makedev() has gone away. + */ + +struct snddev_channel { + SLIST_ENTRY(snddev_channel) link; + struct pcm_channel *channel; + int chan_num; + struct cdev *dsp_devt; + struct cdev *dspW_devt; + struct cdev *audio_devt; + struct cdev *dspr_devt; +}; + +struct snddev_info { + SLIST_HEAD(, snddev_channel) channels; + struct pcm_channel *fakechan; + unsigned devcount, playcount, reccount, vchancount; + unsigned flags; + int inprog; + unsigned int bufsz; + void *devinfo; + device_t dev; + char status[SND_STATUSLEN]; + struct sysctl_ctx_list sysctl_tree; + struct sysctl_oid *sysctl_tree_top; + struct spinlock *lock; + struct cdev *mixer_dev; + +}; + +#ifdef PCM_DEBUG_MTX +#define pcm_lock(d) spin_lock_wr(((struct snddev_info *)(d))->lock) +#define pcm_unlock(d) spin_unlock_wr(((struct snddev_info *)(d))->lock) +#else +void pcm_lock(struct snddev_info *d); +void pcm_unlock(struct snddev_info *d); +#endif + +#ifdef KLD_MODULE +#define PCM_KLDSTRING(a) ("kld " # a) +#else +#define PCM_KLDSTRING(a) "" +#endif + #endif /* _KERNEL */ #endif /* _OS_H_ */ diff --git a/sys/dev/sound/pcm/vchan.c b/sys/dev/sound/pcm/vchan.c index dde8eb0fd5..8f8201812e 100644 --- a/sys/dev/sound/pcm/vchan.c +++ b/sys/dev/sound/pcm/vchan.c @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2001 Cameron Grant +/*- + * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,15 +23,23 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.5.2.4 2003/02/11 15:54:16 orion Exp $ - * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.4 2006/12/22 23:26:25 swildner Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.17.2.4 2006/04/04 17:43:49 ariff Exp $ + * $DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.5 2007/01/04 21:47:03 corecode Exp $ */ #include #include #include "feeder_if.h" -SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.4 2006/12/22 23:26:25 swildner Exp $"); +SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/vchan.c,v 1.5 2007/01/04 21:47:03 corecode Exp $"); + +/* + * Default speed + */ +#define VCHAN_DEFAULT_SPEED 48000 + +extern int feeder_rate_ratemin; +extern int feeder_rate_ratemax; struct vchinfo { u_int32_t spd, fmt, blksz, bps, run; @@ -77,11 +85,21 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32 struct snd_dbuf *src = source; struct pcmchan_children *cce; struct pcm_channel *ch; + uint32_t sz; int16_t *tmp, *dst; - unsigned int cnt; - - KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize")); + unsigned int cnt, rcnt = 0; + + #if 0 + if (sndbuf_getsize(src) < count) + panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x", + c->name, sndbuf_getsize(src), count, c->flags); + #endif + sz = sndbuf_getsize(src); + if (sz < count) + count = sz; count &= ~1; + if (count < 2) + return 0; bzero(b, count); /* @@ -95,15 +113,19 @@ feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32 bzero(tmp, count); SLIST_FOREACH(cce, &c->children, link) { ch = cce->channel; + CHN_LOCK(ch); if (ch->flags & CHN_F_TRIGGERED) { if (ch->flags & CHN_F_MAPPED) sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft)); cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft); - vchan_mix_s16(dst, tmp, cnt / 2); + vchan_mix_s16(dst, tmp, cnt >> 1); + if (cnt > rcnt) + rcnt = cnt; } + CHN_UNLOCK(ch); } - return count; + return rcnt & ~1; } static struct pcm_feederdesc feeder_vchan_s16_desc[] = { @@ -126,6 +148,8 @@ vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction")); ch = kmalloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); + if (!ch) + return NULL; ch->parent = parent; ch->channel = c; ch->fmt = AFMT_U8; @@ -148,13 +172,21 @@ vchan_setformat(kobj_t obj, void *data, u_int32_t format) { struct vchinfo *ch = data; struct pcm_channel *parent = ch->parent; + struct pcm_channel *channel = ch->channel; ch->fmt = format; ch->bps = 1; ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0; - ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0; - ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0; + if (ch->fmt & AFMT_16BIT) + ch->bps <<= 1; + else if (ch->fmt & AFMT_24BIT) + ch->bps *= 3; + else if (ch->fmt & AFMT_32BIT) + ch->bps <<= 2; + CHN_UNLOCK(channel); chn_notify(parent, CHN_N_FORMAT); + CHN_LOCK(channel); + sndbuf_setfmt(channel->bufsoft, format); return 0; } @@ -163,9 +195,14 @@ vchan_setspeed(kobj_t obj, void *data, u_int32_t speed) { struct vchinfo *ch = data; struct pcm_channel *parent = ch->parent; + struct pcm_channel *channel = ch->channel; ch->spd = speed; - chn_notify(parent, CHN_N_RATE); + CHN_UNLOCK(channel); + CHN_LOCK(parent); + speed = sndbuf_getspd(parent->bufsoft); + CHN_UNLOCK(parent); + CHN_LOCK(channel); return speed; } @@ -173,15 +210,22 @@ static int vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct vchinfo *ch = data; + struct pcm_channel *channel = ch->channel; struct pcm_channel *parent = ch->parent; + /* struct pcm_channel *channel = ch->channel; */ int prate, crate; ch->blksz = blocksize; + /* CHN_UNLOCK(channel); */ + sndbuf_setblksz(channel->bufhard, blocksize); chn_notify(parent, CHN_N_BLOCKSIZE); + CHN_LOCK(parent); + /* CHN_LOCK(channel); */ crate = ch->spd * ch->bps; - prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard); - blocksize = sndbuf_getblksz(parent->bufhard); + prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft); + blocksize = sndbuf_getblksz(parent->bufsoft); + CHN_UNLOCK(parent); blocksize *= prate; blocksize /= crate; @@ -193,12 +237,15 @@ vchan_trigger(kobj_t obj, void *data, int go) { struct vchinfo *ch = data; struct pcm_channel *parent = ch->parent; + struct pcm_channel *channel = ch->channel; if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; ch->run = (go == PCMTRIG_START)? 1 : 0; + CHN_UNLOCK(channel); chn_notify(parent, CHN_N_TRIGGER); + CHN_LOCK(channel); return 0; } @@ -208,7 +255,7 @@ vchan_getcaps(kobj_t obj, void *data) { struct vchinfo *ch = data; - ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard); + ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft); ch->caps.maxspeed = ch->caps.minspeed; ch->caps.fmtlist = vchan_fmt; ch->caps.caps = 0; @@ -228,6 +275,105 @@ static kobj_method_t vchan_methods[] = { }; CHANNEL_DECLARE(vchan); +#if 0 +/* + * On the fly vchan rate settings + */ +#ifdef SND_DYNSYSCTL +static int +sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct snddev_channel *sce; + struct pcm_channel *c, *ch = NULL, *fake; + struct pcmchan_caps *caps; + int err = 0; + int newspd = 0; + + d = oidp->oid_arg1; + if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1) + return EINVAL; + if (pcm_inprog(d, 1) != 1 && req->newptr != NULL) { + pcm_inprog(d, -1); + return EINPROGRESS; + } + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY) { + if (c->flags & CHN_F_VIRTUAL) { + /* Sanity check */ + if (ch != NULL && ch != c->parentchannel) { + CHN_UNLOCK(c); + pcm_inprog(d, -1); + return EINVAL; + } + if (req->newptr != NULL && + (c->flags & CHN_F_BUSY)) { + CHN_UNLOCK(c); + pcm_inprog(d, -1); + return EBUSY; + } + } else if (c->flags & CHN_F_HAS_VCHAN) { + /* No way!! */ + if (ch != NULL) { + CHN_UNLOCK(c); + pcm_inprog(d, -1); + return EINVAL; + } + ch = c; + newspd = ch->speed; + } + } + CHN_UNLOCK(c); + } + if (ch == NULL) { + pcm_inprog(d, -1); + return EINVAL; + } + err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req); + if (err == 0 && req->newptr != NULL) { + if (newspd < 1 || newspd < feeder_rate_ratemin || + newspd > feeder_rate_ratemax) { + pcm_inprog(d, -1); + return EINVAL; + } + CHN_LOCK(ch); + caps = chn_getcaps(ch); + if (caps == NULL || newspd < caps->minspeed || + newspd > caps->maxspeed) { + CHN_UNLOCK(ch); + pcm_inprog(d, -1); + return EINVAL; + } + if (newspd != ch->speed) { + err = chn_setspeed(ch, newspd); + /* + * Try to avoid FEEDER_RATE on parent channel if the + * requested value is not supported by the hardware. + */ + if (!err && (ch->feederflags & (1 << FEEDER_RATE))) { + newspd = sndbuf_getspd(ch->bufhard); + err = chn_setspeed(ch, newspd); + } + CHN_UNLOCK(ch); + if (err == 0) { + fake = pcm_getfakechan(d); + if (fake != NULL) { + CHN_LOCK(fake); + fake->speed = newspd; + CHN_UNLOCK(fake); + } + } + } else + CHN_UNLOCK(ch); + } + pcm_inprog(d, -1); + return err; +} +#endif +#endif + /* virtual channel interface */ int @@ -235,18 +381,19 @@ vchan_create(struct pcm_channel *parent) { struct snddev_info *d = parent->parentsnddev; struct pcmchan_children *pce; - struct pcm_channel *child; - int err, first; + struct pcm_channel *child, *fake; + struct pcmchan_caps *parent_caps; + int err, first, speed = 0; - CHN_LOCK(parent); - if (!(parent->flags & CHN_F_BUSY)) { - CHN_UNLOCK(parent); + if (!(parent->flags & CHN_F_BUSY)) return EBUSY; - } + + + CHN_UNLOCK(parent); pce = kmalloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO); if (!pce) { - CHN_UNLOCK(parent); + CHN_LOCK(parent); return ENOMEM; } @@ -254,34 +401,137 @@ vchan_create(struct pcm_channel *parent) child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent); if (!child) { kfree(pce, M_DEVBUF); - CHN_UNLOCK(parent); + CHN_LOCK(parent); return ENODEV; } - - first = SLIST_EMPTY(&parent->children); - /* add us to our parent channel's children */ pce->channel = child; - SLIST_INSERT_HEAD(&parent->children, pce, link); - CHN_UNLOCK(parent); /* add us to our grandparent's channel list */ - err = pcm_chn_add(d, child, !first); + /* + * XXX maybe we shouldn't always add the dev_t + */ + err = pcm_chn_add(d, child); if (err) { pcm_chn_destroy(child); kfree(pce, M_DEVBUF); + CHN_LOCK(parent); + return err; } - /* XXX gross ugly hack, kill murder death */ - if (first && !err) { - err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); - if (err) - kprintf("chn_reset: %d\n", err); - err = chn_setspeed(parent, 44100); - if (err) - kprintf("chn_setspeed: %d\n", err); + CHN_LOCK(parent); + /* add us to our parent channel's children */ + first = SLIST_EMPTY(&parent->children); + SLIST_INSERT_HEAD(&parent->children, pce, link); + parent->flags |= CHN_F_HAS_VCHAN; + + if (first) { + parent_caps = chn_getcaps(parent); + if (parent_caps == NULL) + err = EINVAL; + + if (!err) + err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE); + + if (!err) { + fake = pcm_getfakechan(d); + if (fake != NULL) { + /* + * Avoid querying kernel hint, use saved value + * from fake channel. + */ + CHN_UNLOCK(parent); + CHN_LOCK(fake); + speed = fake->speed; + CHN_UNLOCK(fake); + CHN_LOCK(parent); + } + + /* + * This is very sad. Few soundcards advertised as being + * able to do (insanely) higher/lower speed, but in + * reality, they simply can't. At least, we give user chance + * to set sane value via kernel hints or sysctl. + */ + if (speed < 1) { + int r; + CHN_UNLOCK(parent); + r = resource_int_value(device_get_name(parent->dev), + device_get_unit(parent->dev), + "vchanrate", &speed); + CHN_LOCK(parent); + if (r != 0) { + /* + * Workaround for sb16 running + * poorly at 45k / 49k. + */ + switch (parent_caps->maxspeed) { + case 45000: + case 49000: + speed = 44100; + break; + default: + speed = VCHAN_DEFAULT_SPEED; + break; + } + } + } + + /* + * Limit speed based on driver caps. + * This is supposed to help fixed rate, non-VRA + * AC97 cards, but.. (see below) + */ + if (speed < parent_caps->minspeed) + speed = parent_caps->minspeed; + if (speed > parent_caps->maxspeed) + speed = parent_caps->maxspeed; + + /* + * We still need to limit the speed between + * feeder_rate_ratemin <-> feeder_rate_ratemax. This is + * just an escape goat if all of the above failed + * miserably. + */ + if (speed < feeder_rate_ratemin) + speed = feeder_rate_ratemin; + if (speed > feeder_rate_ratemax) + speed = feeder_rate_ratemax; + + err = chn_setspeed(parent, speed); + /* + * Try to avoid FEEDER_RATE on parent channel if the + * requested value is not supported by the hardware. + */ + if (!err && (parent->feederflags & (1 << FEEDER_RATE))) { + speed = sndbuf_getspd(parent->bufhard); + err = chn_setspeed(parent, speed); + } + + if (!err && fake != NULL) { + /* + * Save new value to fake channel. + */ + CHN_UNLOCK(parent); + CHN_LOCK(fake); + fake->speed = speed; + CHN_UNLOCK(fake); + CHN_LOCK(parent); + } + } + + if (err) { + SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); + parent->flags &= ~CHN_F_HAS_VCHAN; + CHN_UNLOCK(parent); + kfree(pce, M_DEVBUF); + if (pcm_chn_remove(d, child) == 0) + pcm_chn_destroy(child); + CHN_LOCK(parent); + return err; + } } - return err; + return 0; } int @@ -290,7 +540,9 @@ vchan_destroy(struct pcm_channel *c) struct pcm_channel *parent = c->parentchannel; struct snddev_info *d = parent->parentsnddev; struct pcmchan_children *pce; - int err, last; + struct snddev_channel *sce; + uint32_t spd; + int err; CHN_LOCK(parent); if (!(parent->flags & CHN_F_BUSY)) { @@ -310,21 +562,45 @@ vchan_destroy(struct pcm_channel *c) CHN_UNLOCK(parent); return EINVAL; gotch: + SLIST_FOREACH(sce, &d->channels, link) { + if (sce->channel == c) { + if (sce->dsp_devt) { + destroy_dev(sce->dsp_devt); + sce->dsp_devt = NULL; + } + if (sce->dspW_devt) { + destroy_dev(sce->dspW_devt); + sce->dspW_devt = NULL; + } + if (sce->audio_devt) { + destroy_dev(sce->audio_devt); + sce->audio_devt = NULL; + } + if (sce->dspr_devt) { + destroy_dev(sce->dspr_devt); + sce->dspr_devt = NULL; + } + d->devcount--; + break; + } + } SLIST_REMOVE(&parent->children, pce, pcmchan_children, link); kfree(pce, M_DEVBUF); - last = SLIST_EMPTY(&parent->children); - if (last) - parent->flags &= ~CHN_F_BUSY; + if (SLIST_EMPTY(&parent->children)) { + parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); + spd = parent->speed; + if (chn_reset(parent, parent->format) == 0) + chn_setspeed(parent, spd); + } - /* remove us from our grantparent's channel list */ - err = pcm_chn_remove(d, c, !last); - if (err) - return err; + /* remove us from our grandparent's channel list */ + err = pcm_chn_remove(d, c); CHN_UNLOCK(parent); /* destroy ourselves */ - err = pcm_chn_destroy(c); + if (!err) + err = pcm_chn_destroy(c); return err; } @@ -338,10 +614,13 @@ vchan_initsys(device_t dev) d = device_get_softc(dev); SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), - sysctl_hw_snd_vchans, "I", "") + sysctl_hw_snd_vchans, "I", ""); +#if 0 + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + sysctl_hw_snd_vchanrate, "I", ""); +#endif #endif return 0; } - - diff --git a/sys/dev/sound/pcm/vchan.h b/sys/dev/sound/pcm/vchan.h index 3af28a823e..797499a8ae 100644 --- a/sys/dev/sound/pcm/vchan.h +++ b/sys/dev/sound/pcm/vchan.h @@ -1,5 +1,5 @@ -/* - * Copyright (c) 2001 Cameron Grant +/*- + * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -23,8 +23,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/sys/dev/sound/pcm/vchan.h,v 1.1.2.2 2002/04/22 15:49:36 cg Exp $ - * $DragonFly: src/sys/dev/sound/pcm/vchan.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ + * $FreeBSD: src/sys/dev/sound/pcm/vchan.h,v 1.4 2005/01/06 01:43:21 imp Exp $ + * $DragonFly: src/sys/dev/sound/pcm/vchan.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ int vchan_create(struct pcm_channel *parent); diff --git a/sys/dev/sound/snd/Makefile b/sys/dev/sound/snd/Makefile deleted file mode 100644 index 411609d944..0000000000 --- a/sys/dev/sound/snd/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# $FreeBSD: src/sys/modules/sound/snd/Makefile,v 1.1.2.3 2001/08/01 05:37:31 cg Exp $ -# $DragonFly: src/sys/dev/sound/snd/Attic/Makefile,v 1.2 2003/06/17 04:28:46 dillon Exp $ - -.PATH: ${.CURDIR}/../../../dev/sound - -KMOD = snd -SRCS = device_if.h bus_if.h isa_if.h pci_if.h -SRCS += ac97_if.h channel_if.h feeder_if.h mixer_if.h -SRCS += driver.c -KMODDEPS += snd_pcm -KMODDEPS += snd_ad1816 snd_als4000 snd_cmi snd_cs4281 snd_csa snd_ds1 -KMODDEPS += snd_emu10k1 snd_es137x snd_fm801 snd_ich -KMODDEPS += snd_maestro snd_maestro3 snd_mss snd_neomagic -KMODDEPS += snd_solo snd_t4dwave snd_via82c686 snd_vibes -KMODDEPS += snd_sbc snd_sb16 snd_sb8 snd_ess - -.include diff --git a/sys/dev/sound/usb/Makefile b/sys/dev/sound/usb/Makefile deleted file mode 100644 index 6dcb805a26..0000000000 --- a/sys/dev/sound/usb/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -# $FreeBSD: src/sys/modules/usb/Makefile,v 1.7 2000/01/28 11:26:40 bde Exp $ -# $DragonFly: src/sys/dev/sound/usb/Attic/Makefile,v 1.4 2004/08/13 17:51:06 dillon Exp $ - -S = ${.CURDIR}/../.. - -.PATH: $S/dev/usb $S/pci -KMOD = usb -SRCS = bus_if.h device_if.h usb_if.h usb_if.c \ - opt_usb.h \ - hid.c hid.h usbhid.h \ - uhub.c \ - usb.c usb.h \ - usb_mem.h \ - usb_port.h \ - usb_quirks.c usb_quirks.h \ - usb_subr.c \ - usbdevs.h usbdevs_data.h \ - usbdi.c usbdi.h usbdivar.h \ - usbdi_util.c usbdi_util.h \ - usb_ethersubr.c - -SRCS += uhci_pci.c uhci.c uhcireg.h uhcivar.h -SRCS += ohci_pci.c ohci.c ohcireg.h ohcivar.h -SRCS += opt_bus.h pci_if.h - -NOMAN = - -.include diff --git a/sys/dev/sound/usb/uaudio.c b/sys/dev/sound/usb/uaudio.c index 217f9eedf8..8fb3bf3f2d 100644 --- a/sys/dev/sound/usb/uaudio.c +++ b/sys/dev/sound/usb/uaudio.c @@ -1,8 +1,8 @@ -/* $NetBSD: uaudio.c,v 1.41 2001/01/23 14:04:13 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/sound/usb/uaudio.c,v 1.6.2.2 2002/11/06 21:18:17 joe Exp $: */ -/* $DragonFly: src/sys/dev/sound/usb/uaudio.c,v 1.10 2006/12/22 23:26:25 swildner Exp $: */ +/* $NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $ */ +/* $FreeBSD: src/sys/dev/sound/usb/uaudio.c,v 1.14.2.2 2006/04/04 17:34:10 ariff Exp $ */ +/* $DragonFly: src/sys/dev/sound/usb/uaudio.c,v 1.11 2007/01/04 21:47:03 corecode Exp $: */ -/* +/*- * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * @@ -40,9 +40,22 @@ */ /* - * USB audio specs: http://www.usb.org/developers/data/devclass/audio10.pdf - * http://www.usb.org/developers/data/devclass/frmts10.pdf - * http://www.usb.org/developers/data/devclass/termt10.pdf + * USB audio specs: http://www.usb.org/developers/devclass_docs/audio10.pdf + * http://www.usb.org/developers/devclass_docs/frmts10.pdf + * http://www.usb.org/developers/devclass_docs/termt10.pdf + */ + +#include +#if defined(__NetBSD__) || defined(__OpenBSD__) +__KERNEL_RCSID(0, "$NetBSD: uaudio.c,v 1.91 2004/11/05 17:46:14 kent Exp $"); +#endif + +/* + * Also merged: + * $NetBSD: uaudio.c,v 1.94 2005/01/15 15:19:53 kent Exp $ + * $NetBSD: uaudio.c,v 1.95 2005/01/16 06:02:19 dsainty Exp $ + * $NetBSD: uaudio.c,v 1.96 2005/01/16 12:46:00 kent Exp $ + * $NetBSD: uaudio.c,v 1.97 2005/02/24 08:19:38 martin Exp $ */ #include @@ -60,23 +73,30 @@ #include #if defined(__NetBSD__) || defined(__OpenBSD__) #include -#elif defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) #include #include #include #endif #include +#if defined(__FreeBSD__) +#include +#include +#elif defined(__DragonFly__) #include #include +#endif #if defined(__NetBSD__) || defined(__OpenBSD__) #include #include +#include #include #include -#elif defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) #include /* XXXXX */ #include +#include "feeder_if.h" #endif #include @@ -84,44 +104,64 @@ #include #include +#if defined(__NetBSD__) || defined(__OpenBSD__) +#include +#elif defined(__FreeBSD__) || defined(__DragonFly__) #include #include +#endif +#if defined(__NetBSD__) || defined(__OpenBSD__) +/* #define UAUDIO_DEBUG */ +#else +/* #define USB_DEBUG */ +#endif +/* #define UAUDIO_MULTIPLE_ENDPOINTS */ #ifdef USB_DEBUG -#define DPRINTF(x) if (uaudiodebug) logprintf x -#define DPRINTFN(n,x) if (uaudiodebug>(n)) logprintf x +#define DPRINTF(x) do { if (uaudiodebug) logprintf x; } while (0) +#define DPRINTFN(n,x) do { if (uaudiodebug>(n)) logprintf x; } while (0) int uaudiodebug = 0; +#if defined(__FreeBSD__) || defined(__DragonFly__) SYSCTL_NODE(_hw_usb, OID_AUTO, uaudio, CTLFLAG_RW, 0, "USB uaudio"); SYSCTL_INT(_hw_usb_uaudio, OID_AUTO, debug, CTLFLAG_RW, &uaudiodebug, 0, "uaudio debug level"); +#endif #else #define DPRINTF(x) #define DPRINTFN(n,x) #endif #define UAUDIO_NCHANBUFS 6 /* number of outstanding request */ +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define UAUDIO_NFRAMES 10 /* ms of sound in each request */ +#elif defined(__FreeBSD__) || defined(__DragonFly__) #define UAUDIO_NFRAMES 20 /* ms of sound in each request */ +#endif #define MIX_MAX_CHAN 8 struct mixerctl { - u_int16_t wValue[MIX_MAX_CHAN]; /* using nchan */ - u_int16_t wIndex; - u_int8_t nchan; - u_int8_t type; + uint16_t wValue[MIX_MAX_CHAN]; /* using nchan */ + uint16_t wIndex; + uint8_t nchan; + uint8_t type; #define MIX_ON_OFF 1 #define MIX_SIGNED_16 2 #define MIX_UNSIGNED_16 3 #define MIX_SIGNED_8 4 +#define MIX_SELECTOR 5 #define MIX_SIZE(n) ((n) == MIX_SIGNED_16 || (n) == MIX_UNSIGNED_16 ? 2 : 1) #define MIX_UNSIGNED(n) ((n) == MIX_UNSIGNED_16) int minval, maxval; u_int delta; u_int mul; -#if defined(__DragonFly__) /* XXXXX */ +#if defined(__FreeBSD__) || defined(__DragonFly__) /* XXXXX */ unsigned ctl; -#else - u_int8_t class; +#define MAX_SELECTOR_INPUT_PIN 256 + uint8_t slctrtype[MAX_SELECTOR_INPUT_PIN]; +#endif + uint8_t class; +#if !defined(__FreeBSD__) && !defined(__DragonFly__) char ctlname[MAX_AUDIO_DEV_LEN]; char *ctlunit; #endif @@ -129,24 +169,28 @@ struct mixerctl { #define MAKE(h,l) (((h) << 8) | (l)) struct as_info { - u_int8_t alt; - u_int8_t encoding; + uint8_t alt; + uint8_t encoding; + uint8_t attributes; /* Copy of bmAttributes of + * usb_audio_streaming_endpoint_descriptor + */ usbd_interface_handle ifaceh; - usb_interface_descriptor_t *idesc; - usb_endpoint_descriptor_audio_t *edesc; - struct usb_audio_streaming_type1_descriptor *asf1desc; + const usb_interface_descriptor_t *idesc; + const usb_endpoint_descriptor_audio_t *edesc; + const usb_endpoint_descriptor_audio_t *edesc1; + const struct usb_audio_streaming_type1_descriptor *asf1desc; + int sc_busy; /* currently used */ }; struct chan { - int terminal; /* terminal id */ #if defined(__NetBSD__) || defined(__OpenBSD__) - void (*intr)(void *); /* dma completion intr handler */ + void (*intr)(void *); /* DMA completion intr handler */ void *arg; /* arg for intr() */ #else struct pcm_channel *pcm_ch; #endif usbd_pipe_handle pipe; - int dir; /* direction */ + usbd_pipe_handle sync_pipe; u_int sample_size; u_int sample_rate; @@ -160,19 +204,20 @@ struct chan { int blksize; /* chunk size to report up */ int transferred; /* transferred bytes not reported up */ - char nofrac; /* don't do sample rate adjustment */ + int altidx; /* currently used altidx */ int curchanbuf; struct chanbuf { - struct chan *chan; + struct chan *chan; usbd_xfer_handle xfer; - u_char *buffer; - u_int16_t sizes[UAUDIO_NFRAMES]; - u_int16_t size; + u_char *buffer; + u_int16_t sizes[UAUDIO_NFRAMES]; + u_int16_t offsets[UAUDIO_NFRAMES]; + u_int16_t size; } chanbufs[UAUDIO_NCHANBUFS]; struct uaudio_softc *sc; /* our softc */ -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) u_int32_t format; int precision; int channels; @@ -180,151 +225,206 @@ struct chan { }; struct uaudio_softc { - USBBASEDEVICE sc_dev; /* base device */ + USBBASEDEVICE sc_dev; /* base device */ usbd_device_handle sc_udev; /* USB device */ - - char sc_dead; /* The device is dead -- kill it */ - - int sc_ac_iface; /* Audio Control interface */ + int sc_ac_iface; /* Audio Control interface */ usbd_interface_handle sc_ac_ifaceh; + struct chan sc_playchan; /* play channel */ + struct chan sc_recchan; /* record channel */ + int sc_nullalt; + int sc_audio_rev; + struct as_info *sc_alts; /* alternate settings */ + int sc_nalts; /* # of alternate settings */ + int sc_altflags; +#define HAS_8 0x01 +#define HAS_16 0x02 +#define HAS_8U 0x04 +#define HAS_ALAW 0x08 +#define HAS_MULAW 0x10 +#define UA_NOFRAC 0x20 /* don't do sample rate adjustment */ +#define HAS_24 0x40 +#define HAS_32 0x80 + int sc_mode; /* play/record capability */ + struct mixerctl *sc_ctls; /* mixer controls */ + int sc_nctls; /* # of mixer controls */ + device_ptr_t sc_audiodev; + char sc_dying; +#if defined(__FreeBSD__) || defined(__DragonFly__) + struct sbuf uaudio_sndstat; + int uaudio_sndstat_flag; +#endif +}; - struct chan sc_chan; - - int sc_curaltidx; - - int sc_nullalt; - - int sc_audio_rev; - - struct as_info *sc_alts; - int sc_nalts; - int sc_props; - - int sc_altflags; -#define HAS_8 0x01 -#define HAS_16 0x02 -#define HAS_8U 0x04 -#define HAS_ALAW 0x08 -#define HAS_MULAW 0x10 - - struct mixerctl *sc_ctls; - int sc_nctls; - - device_ptr_t sc_audiodev; - char sc_dying; +struct terminal_list { + int size; + uint16_t terminals[1]; +}; +#define TERMINAL_LIST_SIZE(N) (offsetof(struct terminal_list, terminals) \ + + sizeof(uint16_t) * (N)) + +struct io_terminal { + union { + const usb_descriptor_t *desc; + const struct usb_audio_input_terminal *it; + const struct usb_audio_output_terminal *ot; + const struct usb_audio_mixer_unit *mu; + const struct usb_audio_selector_unit *su; + const struct usb_audio_feature_unit *fu; + const struct usb_audio_processing_unit *pu; + const struct usb_audio_extension_unit *eu; + } d; + int inputs_size; + struct terminal_list **inputs; /* list of source input terminals */ + struct terminal_list *output; /* list of destination output terminals */ + int direct; /* directly connected to an output terminal */ }; -#define UAC_OUTPUT 0 -#define UAC_INPUT 1 -#define UAC_EQUAL 2 +#define UAC_OUTPUT 0 +#define UAC_INPUT 1 +#define UAC_EQUAL 2 +#define UAC_RECORD 3 +#define UAC_NCLASSES 4 +#ifdef USB_DEBUG +#if defined(__FreeBSD__) || defined(__DragonFly__) +#define AudioCinputs "inputs" +#define AudioCoutputs "outputs" +#define AudioCrecord "record" +#define AudioCequalization "equalization" +#endif +Static const char *uac_names[] = { + AudioCoutputs, AudioCinputs, AudioCequalization, AudioCrecord, +}; +#endif -Static usbd_status uaudio_identify_ac(struct uaudio_softc *sc, - usb_config_descriptor_t *cdesc); -Static usbd_status uaudio_identify_as(struct uaudio_softc *sc, - usb_config_descriptor_t *cdesc); -Static usbd_status uaudio_process_as(struct uaudio_softc *sc, - char *buf, int *offsp, int size, - usb_interface_descriptor_t *id); +Static usbd_status uaudio_identify_ac + (struct uaudio_softc *, const usb_config_descriptor_t *); +Static usbd_status uaudio_identify_as + (struct uaudio_softc *, const usb_config_descriptor_t *); +Static usbd_status uaudio_process_as + (struct uaudio_softc *, const char *, int *, int, + const usb_interface_descriptor_t *); -Static void uaudio_add_alt(struct uaudio_softc *sc, - struct as_info *ai); +Static void uaudio_add_alt(struct uaudio_softc *, const struct as_info *); -Static usb_interface_descriptor_t *uaudio_find_iface(char *buf, - int size, int *offsp, int subtype); +Static const usb_interface_descriptor_t *uaudio_find_iface + (const char *, int, int *, int); -Static void uaudio_mixer_add_ctl(struct uaudio_softc *sc, - struct mixerctl *mp); +Static void uaudio_mixer_add_ctl(struct uaudio_softc *, struct mixerctl *); #if defined(__NetBSD__) || defined(__OpenBSD__) -Static char *uaudio_id_name(struct uaudio_softc *sc, - usb_descriptor_t **dps, int id); -#endif - -Static struct usb_audio_cluster uaudio_get_cluster(int id, - usb_descriptor_t **dps); -Static void uaudio_add_input(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_output(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_mixer(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_selector(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_feature(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_processing_updown(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_processing(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static void uaudio_add_extension(struct uaudio_softc *sc, - usb_descriptor_t *v, usb_descriptor_t **dps); -Static usbd_status uaudio_identify(struct uaudio_softc *sc, - usb_config_descriptor_t *cdesc); - -Static int uaudio_signext(int type, int val); +Static char *uaudio_id_name + (struct uaudio_softc *, const struct io_terminal *, int); +#endif + +#ifdef USB_DEBUG +Static void uaudio_dump_cluster(const struct usb_audio_cluster *); +#endif +Static struct usb_audio_cluster uaudio_get_cluster + (int, const struct io_terminal *); +Static void uaudio_add_input + (struct uaudio_softc *, const struct io_terminal *, int); +Static void uaudio_add_output + (struct uaudio_softc *, const struct io_terminal *, int); +Static void uaudio_add_mixer + (struct uaudio_softc *, const struct io_terminal *, int); +Static void uaudio_add_selector + (struct uaudio_softc *, const struct io_terminal *, int); +#ifdef USB_DEBUG +Static const char *uaudio_get_terminal_name(int); +#endif +Static int uaudio_determine_class + (const struct io_terminal *, struct mixerctl *); +#if defined(__FreeBSD__) || defined(__DragonFly__) +Static const int uaudio_feature_name(const struct io_terminal *, + struct mixerctl *); +#else +Static const char *uaudio_feature_name + (const struct io_terminal *, struct mixerctl *); +#endif +Static void uaudio_add_feature + (struct uaudio_softc *, const struct io_terminal *, int); +Static void uaudio_add_processing_updown + (struct uaudio_softc *, const struct io_terminal *, int); +Static void uaudio_add_processing + (struct uaudio_softc *, const struct io_terminal *, int); +Static void uaudio_add_extension + (struct uaudio_softc *, const struct io_terminal *, int); +Static struct terminal_list *uaudio_merge_terminal_list + (const struct io_terminal *); +Static struct terminal_list *uaudio_io_terminaltype + (int, struct io_terminal *, int); +Static usbd_status uaudio_identify + (struct uaudio_softc *, const usb_config_descriptor_t *); + +Static int uaudio_signext(int, int); #if defined(__NetBSD__) || defined(__OpenBSD__) -Static int uaudio_value2bsd(struct mixerctl *mc, int val); +Static int uaudio_value2bsd(struct mixerctl *, int); #endif -Static int uaudio_bsd2value(struct mixerctl *mc, int val); -Static int uaudio_get(struct uaudio_softc *sc, int type, - int which, int wValue, int wIndex, int len); +Static int uaudio_bsd2value(struct mixerctl *, int); +Static int uaudio_get(struct uaudio_softc *, int, int, int, int, int); #if defined(__NetBSD__) || defined(__OpenBSD__) -Static int uaudio_ctl_get(struct uaudio_softc *sc, int which, - struct mixerctl *mc, int chan); +Static int uaudio_ctl_get + (struct uaudio_softc *, int, struct mixerctl *, int); #endif -Static void uaudio_set(struct uaudio_softc *sc, int type, - int which, int wValue, int wIndex, int l, int v); -Static void uaudio_ctl_set(struct uaudio_softc *sc, int which, - struct mixerctl *mc, int chan, int val); +Static void uaudio_set + (struct uaudio_softc *, int, int, int, int, int, int); +Static void uaudio_ctl_set + (struct uaudio_softc *, int, struct mixerctl *, int, int); -Static usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int); +Static usbd_status uaudio_set_speed(struct uaudio_softc *, int, u_int); -Static usbd_status uaudio_chan_open(struct uaudio_softc *sc, - struct chan *ch); -Static void uaudio_chan_close(struct uaudio_softc *sc, - struct chan *ch); -Static usbd_status uaudio_chan_alloc_buffers(struct uaudio_softc *, - struct chan *); -Static void uaudio_chan_free_buffers(struct uaudio_softc *, - struct chan *); +Static usbd_status uaudio_chan_open(struct uaudio_softc *, struct chan *); +Static void uaudio_chan_close(struct uaudio_softc *, struct chan *); +Static usbd_status uaudio_chan_alloc_buffers + (struct uaudio_softc *, struct chan *); +Static void uaudio_chan_free_buffers(struct uaudio_softc *, struct chan *); #if defined(__NetBSD__) || defined(__OpenBSD__) -Static void uaudio_chan_set_param(struct chan *ch, - struct audio_params *param, u_char *start, - u_char *end, int blksize); +Static void uaudio_chan_init + (struct chan *, int, const struct audio_params *, int); +Static void uaudio_chan_set_param(struct chan *, u_char *, u_char *, int); #endif -Static void uaudio_chan_ptransfer(struct chan *ch); -Static void uaudio_chan_pintr(usbd_xfer_handle xfer, - usbd_private_handle priv, usbd_status status); +Static void uaudio_chan_ptransfer(struct chan *); +Static void uaudio_chan_pintr + (usbd_xfer_handle, usbd_private_handle, usbd_status); -Static void uaudio_chan_rtransfer(struct chan *ch); -Static void uaudio_chan_rintr(usbd_xfer_handle xfer, - usbd_private_handle priv, usbd_status status); +Static void uaudio_chan_rtransfer(struct chan *); +Static void uaudio_chan_rintr + (usbd_xfer_handle, usbd_private_handle, usbd_status); #if defined(__NetBSD__) || defined(__OpenBSD__) -Static int uaudio_open(void *, int); -Static void uaudio_close(void *); -Static int uaudio_drain(void *); -Static int uaudio_query_encoding(void *, struct audio_encoding *); -Static int uaudio_set_params(void *, int, int, - struct audio_params *, struct audio_params *); -Static int uaudio_round_blocksize(void *, int); -Static int uaudio_trigger_output(void *, void *, void *, - int, void (*)(void *), void *, - struct audio_params *); -Static int uaudio_trigger_input (void *, void *, void *, - int, void (*)(void *), void *, - struct audio_params *); -Static int uaudio_halt_in_dma(void *); -Static int uaudio_halt_out_dma(void *); -Static int uaudio_getdev(void *, struct audio_device *); -Static int uaudio_mixer_set_port(void *, mixer_ctrl_t *); -Static int uaudio_mixer_get_port(void *, mixer_ctrl_t *); -Static int uaudio_query_devinfo(void *, mixer_devinfo_t *); -Static int uaudio_get_props(void *); - -Static struct audio_hw_if uaudio_hw_if = { +Static int uaudio_open(void *, int); +Static void uaudio_close(void *); +Static int uaudio_drain(void *); +Static int uaudio_query_encoding(void *, struct audio_encoding *); +Static void uaudio_get_minmax_rates + (int, const struct as_info *, const struct audio_params *, + int, u_long *, u_long *); +Static int uaudio_match_alt_sub + (int, const struct as_info *, const struct audio_params *, int, u_long); +Static int uaudio_match_alt_chan + (int, const struct as_info *, struct audio_params *, int); +Static int uaudio_match_alt + (int, const struct as_info *, struct audio_params *, int); +Static int uaudio_set_params + (void *, int, int, struct audio_params *, struct audio_params *); +Static int uaudio_round_blocksize(void *, int); +Static int uaudio_trigger_output + (void *, void *, void *, int, void (*)(void *), void *, + struct audio_params *); +Static int uaudio_trigger_input + (void *, void *, void *, int, void (*)(void *), void *, + struct audio_params *); +Static int uaudio_halt_in_dma(void *); +Static int uaudio_halt_out_dma(void *); +Static int uaudio_getdev(void *, struct audio_device *); +Static int uaudio_mixer_set_port(void *, mixer_ctrl_t *); +Static int uaudio_mixer_get_port(void *, mixer_ctrl_t *); +Static int uaudio_query_devinfo(void *, mixer_devinfo_t *); +Static int uaudio_get_props(void *); + +Static const struct audio_hw_if uaudio_hw_if = { uaudio_open, uaudio_close, uaudio_drain, @@ -351,6 +451,7 @@ Static struct audio_hw_if uaudio_hw_if = { uaudio_get_props, uaudio_trigger_output, uaudio_trigger_input, + NULL, }; Static struct audio_device uaudio_device = { @@ -359,9 +460,10 @@ Static struct audio_device uaudio_device = { "uaudio" }; -#elif defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) Static int audio_attach_mi(device_t); -Static void uaudio_init_params(struct uaudio_softc * sc, struct chan *ch); +Static int uaudio_init_params(struct uaudio_softc * sc, struct chan *ch, int mode); +static int uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); /* for NetBSD compatibirity */ #define AUMODE_PLAY 0x01 @@ -376,14 +478,14 @@ Static void uaudio_init_params(struct uaudio_softc * sc, struct chan *ch); #define AUDIO_ENCODING_ULINEAR_LE 8 #define AUDIO_ENCODING_ULINEAR_BE 9 -#endif /* FreeBSD */ +#endif /* FreeBSD || DragonFly */ #if defined(__NetBSD__) || defined(__OpenBSD__) USB_DECLARE_DRIVER(uaudio); -#elif defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) USB_DECLARE_DRIVER_INIT(uaudio, DEVMETHOD(device_suspend, bus_generic_suspend), @@ -398,19 +500,19 @@ USB_MATCH(uaudio) { USB_MATCH_START(uaudio, uaa); usb_interface_descriptor_t *id; - + if (uaa->iface == NULL) - return (UMATCH_NONE); + return UMATCH_NONE; id = usbd_get_interface_descriptor(uaa->iface); /* Trigger on the control interface. */ - if (id == NULL || + if (id == NULL || id->bInterfaceClass != UICLASS_AUDIO || id->bInterfaceSubClass != UISUBCLASS_AUDIOCONTROL || (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_AUDIO)) - return (UMATCH_NONE); + return UMATCH_NONE; - return (UMATCH_IFACECLASS_IFACESUBCLASS); + return UMATCH_IFACECLASS_IFACESUBCLASS; } USB_ATTACH(uaudio) @@ -422,10 +524,11 @@ USB_ATTACH(uaudio) usbd_status err; int i, j, found; +#if defined(__FreeBSD__) || defined(__DragonFly__) usbd_devinfo(uaa->device, 0, devinfo); USB_ATTACH_SETUP; - -#if !defined(__DragonFly__) +#else + usbd_devinfo(uaa->device, 0, devinfo, sizeof(devinfo)); kprintf(": %s\n", devinfo); #endif @@ -476,10 +579,12 @@ USB_ATTACH(uaudio) kprintf("%s: audio rev %d.%02x\n", USBDEVNAME(sc->sc_dev), sc->sc_audio_rev >> 8, sc->sc_audio_rev & 0xff); - sc->sc_chan.sc = sc; + sc->sc_playchan.sc = sc->sc_recchan.sc = sc; + sc->sc_playchan.altidx = -1; + sc->sc_recchan.altidx = -1; if (usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_AU_NO_FRAC) - sc->sc_chan.nofrac = 1; + sc->sc_altflags |= UA_NOFRAC; #ifndef USB_DEBUG if (bootverbose) @@ -487,7 +592,7 @@ USB_ATTACH(uaudio) kprintf("%s: %d mixer controls\n", USBDEVNAME(sc->sc_dev), sc->sc_nctls); -#if !defined(__DragonFly__) +#if !defined(__FreeBSD__) && !defined(__DragonFly__) usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev, USBDEV(sc->sc_dev)); #endif @@ -497,7 +602,7 @@ USB_ATTACH(uaudio) audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); #elif defined(__NetBSD__) sc->sc_audiodev = audio_attach_mi(&uaudio_hw_if, sc, &sc->sc_dev); -#elif defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) sc->sc_dying = 0; if (audio_attach_mi(sc->sc_dev)) { kprintf("audio_attach_mi failed\n"); @@ -512,13 +617,14 @@ USB_ATTACH(uaudio) int uaudio_activate(device_ptr_t self, enum devact act) { - struct uaudio_softc *sc = (struct uaudio_softc *)self; - int rv = 0; + struct uaudio_softc *sc; + int rv; + sc = (struct uaudio_softc *)self; + rv = 0; switch (act) { case DVACT_ACTIVATE: - return (EOPNOTSUPP); - break; + return EOPNOTSUPP; case DVACT_DEACTIVATE: if (sc->sc_audiodev != NULL) @@ -526,7 +632,7 @@ uaudio_activate(device_ptr_t self, enum devact act) sc->sc_dying = 1; break; } - return (rv); + return rv; } #endif @@ -534,9 +640,11 @@ uaudio_activate(device_ptr_t self, enum devact act) int uaudio_detach(device_ptr_t self, int flags) { - struct uaudio_softc *sc = (struct uaudio_softc *)self; - int rv = 0; + struct uaudio_softc *sc; + int rv; + sc = (struct uaudio_softc *)self; + rv = 0; /* Wait for outstanding requests to complete. */ usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); @@ -546,14 +654,17 @@ uaudio_detach(device_ptr_t self, int flags) usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev, USBDEV(sc->sc_dev)); - return (rv); + return rv; } -#elif defined(__DragonFly__) +#elif defined(__FreeBSD__) || defined(__DragonFly__) USB_DETACH(uaudio) { USB_DETACH_START(uaudio, sc); + sbuf_delete(&(sc->uaudio_sndstat)); + sc->uaudio_sndstat_flag = 0; + sc->sc_dying = 1; #if 0 /* XXX */ @@ -567,65 +678,67 @@ USB_DETACH(uaudio) #endif #if defined(__NetBSD__) || defined(__OpenBSD__) -int +Static int uaudio_query_encoding(void *addr, struct audio_encoding *fp) { - struct uaudio_softc *sc = addr; - int flags = sc->sc_altflags; + struct uaudio_softc *sc; + int flags; int idx; + sc = addr; + flags = sc->sc_altflags; if (sc->sc_dying) - return (EIO); - + return EIO; + if (sc->sc_nalts == 0 || flags == 0) - return (ENXIO); + return ENXIO; idx = fp->index; switch (idx) { case 0: - strcpy(fp->name, AudioEulinear); + strlcpy(fp->name, AudioEulinear, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULINEAR; fp->precision = 8; fp->flags = flags&HAS_8U ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 1: - strcpy(fp->name, AudioEmulaw); + strlcpy(fp->name, AudioEmulaw, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULAW; fp->precision = 8; fp->flags = flags&HAS_MULAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 2: - strcpy(fp->name, AudioEalaw); + strlcpy(fp->name, AudioEalaw, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ALAW; fp->precision = 8; fp->flags = flags&HAS_ALAW ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); case 3: - strcpy(fp->name, AudioEslinear); + strlcpy(fp->name, AudioEslinear, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_SLINEAR; fp->precision = 8; fp->flags = flags&HAS_8 ? 0 : AUDIO_ENCODINGFLAG_EMULATED; return (0); - case 4: - strcpy(fp->name, AudioEslinear_le); + case 4: + strlcpy(fp->name, AudioEslinear_le, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_SLINEAR_LE; fp->precision = 16; fp->flags = 0; return (0); case 5: - strcpy(fp->name, AudioEulinear_le); + strlcpy(fp->name, AudioEulinear_le, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULINEAR_LE; fp->precision = 16; fp->flags = AUDIO_ENCODINGFLAG_EMULATED; return (0); case 6: - strcpy(fp->name, AudioEslinear_be); + strlcpy(fp->name, AudioEslinear_be, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_SLINEAR_BE; fp->precision = 16; fp->flags = AUDIO_ENCODINGFLAG_EMULATED; return (0); case 7: - strcpy(fp->name, AudioEulinear_be); + strlcpy(fp->name, AudioEulinear_be, sizeof(fp->name)); fp->encoding = AUDIO_ENCODING_ULINEAR_BE; fp->precision = 16; fp->flags = AUDIO_ENCODINGFLAG_EMULATED; @@ -636,45 +749,70 @@ uaudio_query_encoding(void *addr, struct audio_encoding *fp) } #endif -usb_interface_descriptor_t * -uaudio_find_iface(char *buf, int size, int *offsp, int subtype) +Static const usb_interface_descriptor_t * +uaudio_find_iface(const char *buf, int size, int *offsp, int subtype) { - usb_interface_descriptor_t *d; + const usb_interface_descriptor_t *d; while (*offsp < size) { - d = (void *)(buf + *offsp); + d = (const void *)(buf + *offsp); *offsp += d->bLength; if (d->bDescriptorType == UDESC_INTERFACE && d->bInterfaceClass == UICLASS_AUDIO && d->bInterfaceSubClass == subtype) - return (d); + return d; } - return (NULL); + return NULL; } -void +Static void uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) { int res; - size_t len = sizeof(*mc) * (sc->sc_nctls + 1); - struct mixerctl *nmc = sc->sc_nctls == 0 ? - kmalloc(len, M_USBDEV, M_NOWAIT) : - krealloc(sc->sc_ctls, len, M_USBDEV, M_NOWAIT); + size_t len; + struct mixerctl *nmc; - if(nmc == NULL){ +#if defined(__FreeBSD__) || defined(__DragonFly__) + if (mc->class < UAC_NCLASSES) { + DPRINTF(("%s: adding %s.%d\n", + __func__, uac_names[mc->class], mc->ctl)); + } else { + DPRINTF(("%s: adding %d\n", __func__, mc->ctl)); + } +#else + if (mc->class < UAC_NCLASSES) { + DPRINTF(("%s: adding %s.%s\n", + __func__, uac_names[mc->class], mc->ctlname)); + } else { + DPRINTF(("%s: adding %s\n", __func__, mc->ctlname)); + } +#endif + len = sizeof(*mc) * (sc->sc_nctls + 1); + nmc = kmalloc(len, M_USBDEV, M_NOWAIT); + if (nmc == NULL) { kprintf("uaudio_mixer_add_ctl: no memory\n"); return; } + /* Copy old data, if there was any */ + if (sc->sc_nctls != 0) { + memcpy(nmc, sc->sc_ctls, sizeof(*mc) * (sc->sc_nctls)); + kfree(sc->sc_ctls, M_USBDEV); + } sc->sc_ctls = nmc; mc->delta = 0; - if (mc->type != MIX_ON_OFF) { + if (mc->type == MIX_ON_OFF) { + mc->minval = 0; + mc->maxval = 1; + } else if (mc->type == MIX_SELECTOR) { + ; + } else { /* Determine min and max values. */ - mc->minval = uaudio_signext(mc->type, - uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE, - mc->wValue[0], mc->wIndex, + mc->minval = uaudio_signext(mc->type, + uaudio_get(sc, GET_MIN, UT_READ_CLASS_INTERFACE, + mc->wValue[0], mc->wIndex, MIX_SIZE(mc->type))); - mc->maxval = 1 + uaudio_signext(mc->type, + mc->maxval = 1 + uaudio_signext(mc->type, uaudio_get(sc, GET_MAX, UT_READ_CLASS_INTERFACE, mc->wValue[0], mc->wIndex, MIX_SIZE(mc->type))); @@ -685,10 +823,7 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) mc->wValue[0], mc->wIndex, MIX_SIZE(mc->type)); if (res > 0) - mc->delta = (res * 256 + mc->mul/2) / mc->mul; - } else { - mc->minval = 0; - mc->maxval = 1; + mc->delta = (res * 255 + mc->mul/2) / mc->mul; } sc->sc_ctls[sc->sc_nctls++] = *mc; @@ -699,13 +834,13 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) DPRINTF(("uaudio_mixer_add_ctl: wValue=%04x",mc->wValue[0])); for (i = 1; i < mc->nchan; i++) DPRINTF((",%04x", mc->wValue[i])); -#if defined(__DragonFly__) - DPRINTF((" wIndex=%04x type=%d " +#if defined(__FreeBSD__) || defined(__DragonFly__) + DPRINTF((" wIndex=%04x type=%d ctl='%d' " "min=%d max=%d\n", - mc->wIndex, mc->type, + mc->wIndex, mc->type, mc->ctl, mc->minval, mc->maxval)); #else - DPRINTF((" wIndex=%04x type=%d ctl='%d' unit='%s'" + DPRINTF((" wIndex=%04x type=%d name='%s' unit='%s' " "min=%d max=%d\n", mc->wIndex, mc->type, mc->ctlname, mc->ctlunit, mc->minval, mc->maxval)); @@ -715,68 +850,82 @@ uaudio_mixer_add_ctl(struct uaudio_softc *sc, struct mixerctl *mc) } #if defined(__NetBSD__) || defined(__OpenBSD__) -char * -uaudio_id_name(struct uaudio_softc *sc, usb_descriptor_t **dps, int id) +Static char * +uaudio_id_name(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { static char buf[32]; - ksprintf(buf, "i%d", id); - return (buf); + + ksnprintf(buf, sizeof(buf), "i%d", id); + return buf; +} +#endif + +#ifdef USB_DEBUG +Static void +uaudio_dump_cluster(const struct usb_audio_cluster *cl) +{ + static const char *channel_names[16] = { + "LEFT", "RIGHT", "CENTER", "LFE", + "LEFT_SURROUND", "RIGHT_SURROUND", "LEFT_CENTER", "RIGHT_CENTER", + "SURROUND", "LEFT_SIDE", "RIGHT_SIDE", "TOP", + "RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15", + }; + int cc, i, first; + + cc = UGETW(cl->wChannelConfig); + logprintf("cluster: bNrChannels=%u wChannelConfig=0x%.4x", + cl->bNrChannels, cc); + first = TRUE; + for (i = 0; cc != 0; i++) { + if (cc & 1) { + logprintf("%c%s", first ? '<' : ',', channel_names[i]); + first = FALSE; + } + cc = cc >> 1; + } + logprintf("> iChannelNames=%u", cl->iChannelNames); } #endif -struct usb_audio_cluster -uaudio_get_cluster(int id, usb_descriptor_t **dps) +Static struct usb_audio_cluster +uaudio_get_cluster(int id, const struct io_terminal *iot) { struct usb_audio_cluster r; - usb_descriptor_t *dp; + const usb_descriptor_t *dp; int i; for (i = 0; i < 25; i++) { /* avoid infinite loops */ - dp = dps[id]; + dp = iot[id].d.desc; if (dp == 0) goto bad; switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_INPUT: -#define p ((struct usb_audio_input_terminal *)dp) - r.bNrChannels = p->bNrChannels; - USETW(r.wChannelConfig, UGETW(p->wChannelConfig)); - r.iChannelNames = p->iChannelNames; -#undef p - return (r); + r.bNrChannels = iot[id].d.it->bNrChannels; + USETW(r.wChannelConfig, UGETW(iot[id].d.it->wChannelConfig)); + r.iChannelNames = iot[id].d.it->iChannelNames; + return r; case UDESCSUB_AC_OUTPUT: -#define p ((struct usb_audio_output_terminal *)dp) - id = p->bSourceId; -#undef p + id = iot[id].d.ot->bSourceId; break; case UDESCSUB_AC_MIXER: -#define p ((struct usb_audio_mixer_unit *)dp) - r = *(struct usb_audio_cluster *) - &p->baSourceId[p->bNrInPins]; -#undef p - return (r); + r = *(const struct usb_audio_cluster *) + &iot[id].d.mu->baSourceId[iot[id].d.mu->bNrInPins]; + return r; case UDESCSUB_AC_SELECTOR: /* XXX This is not really right */ -#define p ((struct usb_audio_selector_unit *)dp) - id = p->baSourceId[0]; -#undef p + id = iot[id].d.su->baSourceId[0]; break; case UDESCSUB_AC_FEATURE: -#define p ((struct usb_audio_feature_unit *)dp) - id = p->bSourceId; -#undef p + id = iot[id].d.fu->bSourceId; break; case UDESCSUB_AC_PROCESSING: -#define p ((struct usb_audio_processing_unit *)dp) - r = *(struct usb_audio_cluster *) - &p->baSourceId[p->bNrInPins]; -#undef p - return (r); + r = *(const struct usb_audio_cluster *) + &iot[id].d.pu->baSourceId[iot[id].d.pu->bNrInPins]; + return r; case UDESCSUB_AC_EXTENSION: -#define p ((struct usb_audio_extension_unit *)dp) - r = *(struct usb_audio_cluster *) - &p->baSourceId[p->bNrInPins]; -#undef p - return (r); + r = *(const struct usb_audio_cluster *) + &iot[id].d.eu->baSourceId[iot[id].d.eu->bNrInPins]; + return r; default: goto bad; } @@ -784,17 +933,15 @@ uaudio_get_cluster(int id, usb_descriptor_t **dps) bad: kprintf("uaudio_get_cluster: bad data\n"); memset(&r, 0, sizeof r); - return (r); + return r; } -void -uaudio_add_input(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) +Static void +uaudio_add_input(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { #ifdef USB_DEBUG - struct usb_audio_input_terminal *d = - (struct usb_audio_input_terminal *)v; + const struct usb_audio_input_terminal *d = iot[id].d.it; DPRINTFN(2,("uaudio_add_input: bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bNrChannels=%d wChannelConfig=%d " @@ -805,14 +952,13 @@ uaudio_add_input(struct uaudio_softc *sc, usb_descriptor_t *v, #endif } -void -uaudio_add_output(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) +Static void +uaudio_add_output(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { #ifdef USB_DEBUG - struct usb_audio_output_terminal *d = - (struct usb_audio_output_terminal *)v; + const struct usb_audio_output_terminal *d; + d = iot[id].d.ot; DPRINTFN(2,("uaudio_add_output: bTerminalId=%d wTerminalType=0x%04x " "bAssocTerminal=%d bSourceId=%d iTerminal=%d\n", d->bTerminalId, UGETW(d->wTerminalType), d->bAssocTerminal, @@ -820,42 +966,39 @@ uaudio_add_output(struct uaudio_softc *sc, usb_descriptor_t *v, #endif } -void -uaudio_add_mixer(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) +Static void +uaudio_add_mixer(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - struct usb_audio_mixer_unit *d = (struct usb_audio_mixer_unit *)v; - struct usb_audio_mixer_unit_1 *d1; + const struct usb_audio_mixer_unit *d = iot[id].d.mu; + const struct usb_audio_mixer_unit_1 *d1; int c, chs, ichs, ochs, i, o, bno, p, mo, mc, k; - uByte *bm; + const uByte *bm; struct mixerctl mix; DPRINTFN(2,("uaudio_add_mixer: bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins)); - + /* Compute the number of input channels */ ichs = 0; for (i = 0; i < d->bNrInPins; i++) - ichs += uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels; + ichs += uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; /* and the number of output channels */ - d1 = (struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; + d1 = (const struct usb_audio_mixer_unit_1 *)&d->baSourceId[d->bNrInPins]; ochs = d1->bNrChannels; DPRINTFN(2,("uaudio_add_mixer: ichs=%d ochs=%d\n", ichs, ochs)); bm = d1->bmControls; mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); -#if !defined(__DragonFly__) - mix.class = -1; -#endif + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_SIGNED_16; -#if !defined(__DragonFly__) /* XXXXX */ +#if !defined(__FreeBSD__) && !defined(__DragonFly__) /* XXXXX */ mix.ctlunit = AudioNvolume; #endif #define BIT(bno) ((bm[bno / 8] >> (7 - bno % 8)) & 1) for (p = i = 0; i < d->bNrInPins; i++) { - chs = uaudio_get_cluster(d->baSourceId[i], dps).bNrChannels; + chs = uaudio_get_cluster(d->baSourceId[i], iot).bNrChannels; mc = 0; for (c = 0; c < chs; c++) { mo = 0; @@ -873,12 +1016,13 @@ uaudio_add_mixer(struct uaudio_softc *sc, usb_descriptor_t *v, for (o = 0; o < ochs; o++) { bno = (p + c) * ochs + o; if (BIT(bno)) - mix.wValue[k++] = + mix.wValue[k++] = MAKE(p+c+1, o+1); } -#if !defined(__DragonFly__) - ksprintf(mix.ctlname, "mix%d-%s", d->bUnitId, - uaudio_id_name(sc, dps, d->baSourceId[i])); +#if !defined(__FreeBSD__) && !defined(__DragonFly__) + ksnprintf(mix.ctlname, sizeof(mix.ctlname), "mix%d-%s", + d->bUnitId, uaudio_id_name(sc, iot, + d->baSourceId[i])); #endif mix.nchan = chs; uaudio_mixer_add_ctl(sc, &mix); @@ -891,38 +1035,407 @@ uaudio_add_mixer(struct uaudio_softc *sc, usb_descriptor_t *v, } -void -uaudio_add_selector(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) +Static void +uaudio_add_selector(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { -#ifdef USB_DEBUG - struct usb_audio_selector_unit *d = - (struct usb_audio_selector_unit *)v; + const struct usb_audio_selector_unit *d; + struct mixerctl mix; +#if !defined(__FreeBSD__) && !defined(__DragonFly__) + int i, wp; +#else + int i; + struct mixerctl dummy; +#endif + d = iot[id].d.su; DPRINTFN(2,("uaudio_add_selector: bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins)); + mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); + mix.wValue[0] = MAKE(0, 0); + uaudio_determine_class(&iot[id], &mix); + mix.nchan = 1; + mix.type = MIX_SELECTOR; +#if defined(__FreeBSD__) || defined(__DragonFly__) + mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ + mix.minval = 1; + mix.maxval = d->bNrInPins; + mix.mul = mix.maxval - mix.minval; + for (i = 0; i < MAX_SELECTOR_INPUT_PIN; i++) { + mix.slctrtype[i] = SOUND_MIXER_NRDEVICES; + } + for (i = mix.minval; i <= mix.maxval; i++) { + mix.slctrtype[i - 1] = uaudio_feature_name(&iot[d->baSourceId[i - 1]], &dummy); + } +#else + mix.ctlunit = ""; + mix.minval = 1; + mix.maxval = d->bNrInPins; + mix.mul = mix.maxval - mix.minval; + wp = ksnprintf(mix.ctlname, MAX_AUDIO_DEV_LEN, "sel%d-", d->bUnitId); + for (i = 1; i <= d->bNrInPins; i++) { + wp += ksnprintf(mix.ctlname + wp, MAX_AUDIO_DEV_LEN - wp, + "i%d", d->baSourceId[i - 1]); + if (wp > MAX_AUDIO_DEV_LEN - 1) + break; + } #endif - kprintf("uaudio_add_selector: NOT IMPLEMENTED\n"); + uaudio_mixer_add_ctl(sc, &mix); } -void -uaudio_add_feature(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) +#ifdef USB_DEBUG +Static const char * +uaudio_get_terminal_name(int terminal_type) { - struct usb_audio_feature_unit *d = (struct usb_audio_feature_unit *)v; - uByte *ctls = d->bmaControls; - int ctlsize = d->bControlSize; - int nchan = (d->bLength - 7) / ctlsize; -#if !defined(__DragonFly__) - int srcId = d->bSourceId; + static char buf[100]; + + switch (terminal_type) { + /* USB terminal types */ + case UAT_UNDEFINED: return "UAT_UNDEFINED"; + case UAT_STREAM: return "UAT_STREAM"; + case UAT_VENDOR: return "UAT_VENDOR"; + /* input terminal types */ + case UATI_UNDEFINED: return "UATI_UNDEFINED"; + case UATI_MICROPHONE: return "UATI_MICROPHONE"; + case UATI_DESKMICROPHONE: return "UATI_DESKMICROPHONE"; + case UATI_PERSONALMICROPHONE: return "UATI_PERSONALMICROPHONE"; + case UATI_OMNIMICROPHONE: return "UATI_OMNIMICROPHONE"; + case UATI_MICROPHONEARRAY: return "UATI_MICROPHONEARRAY"; + case UATI_PROCMICROPHONEARR: return "UATI_PROCMICROPHONEARR"; + /* output terminal types */ + case UATO_UNDEFINED: return "UATO_UNDEFINED"; + case UATO_SPEAKER: return "UATO_SPEAKER"; + case UATO_HEADPHONES: return "UATO_HEADPHONES"; + case UATO_DISPLAYAUDIO: return "UATO_DISPLAYAUDIO"; + case UATO_DESKTOPSPEAKER: return "UATO_DESKTOPSPEAKER"; + case UATO_ROOMSPEAKER: return "UATO_ROOMSPEAKER"; + case UATO_COMMSPEAKER: return "UATO_COMMSPEAKER"; + case UATO_SUBWOOFER: return "UATO_SUBWOOFER"; + /* bidir terminal types */ + case UATB_UNDEFINED: return "UATB_UNDEFINED"; + case UATB_HANDSET: return "UATB_HANDSET"; + case UATB_HEADSET: return "UATB_HEADSET"; + case UATB_SPEAKERPHONE: return "UATB_SPEAKERPHONE"; + case UATB_SPEAKERPHONEESUP: return "UATB_SPEAKERPHONEESUP"; + case UATB_SPEAKERPHONEECANC: return "UATB_SPEAKERPHONEECANC"; + /* telephony terminal types */ + case UATT_UNDEFINED: return "UATT_UNDEFINED"; + case UATT_PHONELINE: return "UATT_PHONELINE"; + case UATT_TELEPHONE: return "UATT_TELEPHONE"; + case UATT_DOWNLINEPHONE: return "UATT_DOWNLINEPHONE"; + /* external terminal types */ + case UATE_UNDEFINED: return "UATE_UNDEFINED"; + case UATE_ANALOGCONN: return "UATE_ANALOGCONN"; + case UATE_LINECONN: return "UATE_LINECONN"; + case UATE_LEGACYCONN: return "UATE_LEGACYCONN"; + case UATE_DIGITALAUIFC: return "UATE_DIGITALAUIFC"; + case UATE_SPDIF: return "UATE_SPDIF"; + case UATE_1394DA: return "UATE_1394DA"; + case UATE_1394DV: return "UATE_1394DV"; + /* embedded function terminal types */ + case UATF_UNDEFINED: return "UATF_UNDEFINED"; + case UATF_CALIBNOISE: return "UATF_CALIBNOISE"; + case UATF_EQUNOISE: return "UATF_EQUNOISE"; + case UATF_CDPLAYER: return "UATF_CDPLAYER"; + case UATF_DAT: return "UATF_DAT"; + case UATF_DCC: return "UATF_DCC"; + case UATF_MINIDISK: return "UATF_MINIDISK"; + case UATF_ANALOGTAPE: return "UATF_ANALOGTAPE"; + case UATF_PHONOGRAPH: return "UATF_PHONOGRAPH"; + case UATF_VCRAUDIO: return "UATF_VCRAUDIO"; + case UATF_VIDEODISCAUDIO: return "UATF_VIDEODISCAUDIO"; + case UATF_DVDAUDIO: return "UATF_DVDAUDIO"; + case UATF_TVTUNERAUDIO: return "UATF_TVTUNERAUDIO"; + case UATF_SATELLITE: return "UATF_SATELLITE"; + case UATF_CABLETUNER: return "UATF_CABLETUNER"; + case UATF_DSS: return "UATF_DSS"; + case UATF_RADIORECV: return "UATF_RADIORECV"; + case UATF_RADIOXMIT: return "UATF_RADIOXMIT"; + case UATF_MULTITRACK: return "UATF_MULTITRACK"; + case UATF_SYNTHESIZER: return "UATF_SYNTHESIZER"; + default: + ksnprintf(buf, sizeof(buf), "unknown type (0x%.4x)", terminal_type); + return buf; + } +} +#endif + +Static int +uaudio_determine_class(const struct io_terminal *iot, struct mixerctl *mix) +{ + int terminal_type; + + if (iot == NULL || iot->output == NULL) { + mix->class = UAC_OUTPUT; + return 0; + } + terminal_type = 0; + if (iot->output->size == 1) + terminal_type = iot->output->terminals[0]; + /* + * If the only output terminal is USB, + * the class is UAC_RECORD. + */ + if ((terminal_type & 0xff00) == (UAT_UNDEFINED & 0xff00)) { + mix->class = UAC_RECORD; + if (iot->inputs_size == 1 + && iot->inputs[0] != NULL + && iot->inputs[0]->size == 1) + return iot->inputs[0]->terminals[0]; + else + return 0; + } + /* + * If the ultimate destination of the unit is just one output + * terminal and the unit is connected to the output terminal + * directly, the class is UAC_OUTPUT. + */ + if (terminal_type != 0 && iot->direct) { + mix->class = UAC_OUTPUT; + return terminal_type; + } + /* + * If the unit is connected to just one input terminal, + * the class is UAC_INPUT. + */ + if (iot->inputs_size == 1 && iot->inputs[0] != NULL + && iot->inputs[0]->size == 1) { + mix->class = UAC_INPUT; + return iot->inputs[0]->terminals[0]; + } + /* + * Otherwise, the class is UAC_OUTPUT. + */ + mix->class = UAC_OUTPUT; + return terminal_type; +} + +#if defined(__FreeBSD__) || defined(__DragonFly__) +const int +uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix) +{ + int terminal_type; + + terminal_type = uaudio_determine_class(iot, mix); + if (mix->class == UAC_RECORD && terminal_type == 0) + return SOUND_MIXER_IMIX; + DPRINTF(("%s: terminal_type=%s\n", __func__, + uaudio_get_terminal_name(terminal_type))); + switch (terminal_type) { + case UAT_STREAM: + return SOUND_MIXER_PCM; + + case UATI_MICROPHONE: + case UATI_DESKMICROPHONE: + case UATI_PERSONALMICROPHONE: + case UATI_OMNIMICROPHONE: + case UATI_MICROPHONEARRAY: + case UATI_PROCMICROPHONEARR: + return SOUND_MIXER_MIC; + + case UATO_SPEAKER: + case UATO_DESKTOPSPEAKER: + case UATO_ROOMSPEAKER: + case UATO_COMMSPEAKER: + return SOUND_MIXER_SPEAKER; + + case UATE_ANALOGCONN: + case UATE_LINECONN: + case UATE_LEGACYCONN: + return SOUND_MIXER_LINE; + + case UATE_DIGITALAUIFC: + case UATE_SPDIF: + case UATE_1394DA: + case UATE_1394DV: + return SOUND_MIXER_ALTPCM; + + case UATF_CDPLAYER: + return SOUND_MIXER_CD; + + case UATF_SYNTHESIZER: + return SOUND_MIXER_SYNTH; + + case UATF_VIDEODISCAUDIO: + case UATF_DVDAUDIO: + case UATF_TVTUNERAUDIO: + return SOUND_MIXER_VIDEO; + +/* telephony terminal types */ + case UATT_UNDEFINED: + case UATT_PHONELINE: + case UATT_TELEPHONE: + case UATT_DOWNLINEPHONE: + return SOUND_MIXER_PHONEIN; +/* return SOUND_MIXER_PHONEOUT;*/ + + case UATF_RADIORECV: + case UATF_RADIOXMIT: + return SOUND_MIXER_RADIO; + + case UAT_UNDEFINED: + case UAT_VENDOR: + case UATI_UNDEFINED: +/* output terminal types */ + case UATO_UNDEFINED: + case UATO_DISPLAYAUDIO: + case UATO_SUBWOOFER: + case UATO_HEADPHONES: +/* bidir terminal types */ + case UATB_UNDEFINED: + case UATB_HANDSET: + case UATB_HEADSET: + case UATB_SPEAKERPHONE: + case UATB_SPEAKERPHONEESUP: + case UATB_SPEAKERPHONEECANC: +/* external terminal types */ + case UATE_UNDEFINED: +/* embedded function terminal types */ + case UATF_UNDEFINED: + case UATF_CALIBNOISE: + case UATF_EQUNOISE: + case UATF_DAT: + case UATF_DCC: + case UATF_MINIDISK: + case UATF_ANALOGTAPE: + case UATF_PHONOGRAPH: + case UATF_VCRAUDIO: + case UATF_SATELLITE: + case UATF_CABLETUNER: + case UATF_DSS: + case UATF_MULTITRACK: + case 0xffff: + default: + DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type)); + return SOUND_MIXER_VOLUME; + } + return SOUND_MIXER_VOLUME; +} +#else +Static const char * +uaudio_feature_name(const struct io_terminal *iot, struct mixerctl *mix) +{ + int terminal_type; + + terminal_type = uaudio_determine_class(iot, mix); + if (mix->class == UAC_RECORD && terminal_type == 0) + return AudioNmixerout; + DPRINTF(("%s: terminal_type=%s\n", __func__, + uaudio_get_terminal_name(terminal_type))); + switch (terminal_type) { + case UAT_STREAM: + return AudioNdac; + + case UATI_MICROPHONE: + case UATI_DESKMICROPHONE: + case UATI_PERSONALMICROPHONE: + case UATI_OMNIMICROPHONE: + case UATI_MICROPHONEARRAY: + case UATI_PROCMICROPHONEARR: + return AudioNmicrophone; + + case UATO_SPEAKER: + case UATO_DESKTOPSPEAKER: + case UATO_ROOMSPEAKER: + case UATO_COMMSPEAKER: + return AudioNspeaker; + + case UATO_HEADPHONES: + return AudioNheadphone; + + case UATO_SUBWOOFER: + return AudioNlfe; + + /* telephony terminal types */ + case UATT_UNDEFINED: + case UATT_PHONELINE: + case UATT_TELEPHONE: + case UATT_DOWNLINEPHONE: + return "phone"; + + case UATE_ANALOGCONN: + case UATE_LINECONN: + case UATE_LEGACYCONN: + return AudioNline; + + case UATE_DIGITALAUIFC: + case UATE_SPDIF: + case UATE_1394DA: + case UATE_1394DV: + return AudioNaux; + + case UATF_CDPLAYER: + return AudioNcd; + + case UATF_SYNTHESIZER: + return AudioNfmsynth; + + case UATF_VIDEODISCAUDIO: + case UATF_DVDAUDIO: + case UATF_TVTUNERAUDIO: + return AudioNvideo; + + case UAT_UNDEFINED: + case UAT_VENDOR: + case UATI_UNDEFINED: +/* output terminal types */ + case UATO_UNDEFINED: + case UATO_DISPLAYAUDIO: +/* bidir terminal types */ + case UATB_UNDEFINED: + case UATB_HANDSET: + case UATB_HEADSET: + case UATB_SPEAKERPHONE: + case UATB_SPEAKERPHONEESUP: + case UATB_SPEAKERPHONEECANC: +/* external terminal types */ + case UATE_UNDEFINED: +/* embedded function terminal types */ + case UATF_UNDEFINED: + case UATF_CALIBNOISE: + case UATF_EQUNOISE: + case UATF_DAT: + case UATF_DCC: + case UATF_MINIDISK: + case UATF_ANALOGTAPE: + case UATF_PHONOGRAPH: + case UATF_VCRAUDIO: + case UATF_SATELLITE: + case UATF_CABLETUNER: + case UATF_DSS: + case UATF_RADIORECV: + case UATF_RADIOXMIT: + case UATF_MULTITRACK: + case 0xffff: + default: + DPRINTF(("%s: 'master' for 0x%.4x\n", __func__, terminal_type)); + return AudioNmaster; + } + return AudioNmaster; +} #endif + +Static void +uaudio_add_feature(struct uaudio_softc *sc, const struct io_terminal *iot, int id) +{ + const struct usb_audio_feature_unit *d; + const uByte *ctls; + int ctlsize; + int nchan; u_int fumask, mmask, cmask; struct mixerctl mix; int chan, ctl, i, unit; +#if defined(__FreeBSD__) || defined(__DragonFly__) + int mixernumber; +#else + const char *mixername; +#endif #define GET(i) (ctls[(i)*ctlsize] | \ (ctlsize > 1 ? ctls[(i)*ctlsize+1] << 8 : 0)) - + d = iot[id].d.fu; + ctls = d->bmaControls; + ctlsize = d->bControlSize; + nchan = (d->bLength - 7) / ctlsize; mmask = GET(0); /* Figure out what we can control */ for (cmask = 0, chan = 1; chan < nchan; chan++) { @@ -931,10 +1444,10 @@ uaudio_add_feature(struct uaudio_softc *sc, usb_descriptor_t *v, cmask |= GET(chan); } -#if !defined(__DragonFly__) - DPRINTFN(1,("uaudio_add_feature: bUnitId=%d bSourceId=%d, " - "%d channels, mmask=0x%04x, cmask=0x%04x\n", - d->bUnitId, srcId, nchan, mmask, cmask)); +#if !defined(__FreeBSD__) && !defined(__DragonFly__) + DPRINTFN(1,("uaudio_add_feature: bUnitId=%d, " + "%d channels, mmask=0x%04x, cmask=0x%04x\n", + d->bUnitId, nchan, mmask, cmask)); #endif if (nchan > MIX_MAX_CHAN) @@ -961,64 +1474,59 @@ uaudio_add_feature(struct uaudio_softc *sc, usb_descriptor_t *v, } #undef GET -#if !defined(__DragonFly__) - mix.class = -1; /* XXX */ +#if defined(__FreeBSD__) || defined(__DragonFly__) + mixernumber = uaudio_feature_name(&iot[id], &mix); +#else + mixername = uaudio_feature_name(&iot[id], &mix); #endif switch (ctl) { case MUTE_CONTROL: mix.type = MIX_ON_OFF; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_NRDEVICES; #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNmute); mix.ctlunit = ""; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNmute); #endif break; case VOLUME_CONTROL: mix.type = MIX_SIGNED_16; -#if defined(__DragonFly__) - /* mix.ctl = SOUND_MIXER_VOLUME; */ - mix.ctl = SOUND_MIXER_PCM; +#if defined(__FreeBSD__) || defined(__DragonFly__) + mix.ctl = mixernumber; #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNmaster); mix.ctlunit = AudioNvolume; + strlcpy(mix.ctlname, mixername, sizeof(mix.ctlname)); #endif break; case BASS_CONTROL: mix.type = MIX_SIGNED_8; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_BASS; #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNbass); mix.ctlunit = AudioNbass; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNbass); #endif break; case MID_CONTROL: mix.type = MIX_SIGNED_8; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNmid); mix.ctlunit = AudioNmid; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNmid); #endif break; case TREBLE_CONTROL: mix.type = MIX_SIGNED_8; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_TREBLE; #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNtreble); mix.ctlunit = AudioNtreble; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNtreble); #endif break; case GRAPHIC_EQUALIZER_CONTROL: @@ -1026,46 +1534,42 @@ uaudio_add_feature(struct uaudio_softc *sc, usb_descriptor_t *v, break; case AGC_CONTROL: mix.type = MIX_ON_OFF; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNagc); mix.ctlunit = ""; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), "%s.%s", + mixername, AudioNagc); #endif break; case DELAY_CONTROL: mix.type = MIX_UNSIGNED_16; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNdelay); mix.ctlunit = "4 ms"; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNdelay); #endif break; case BASS_BOOST_CONTROL: mix.type = MIX_ON_OFF; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_NRDEVICES; /* XXXXX */ #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNbassboost); mix.ctlunit = ""; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNbassboost); #endif break; case LOUDNESS_CONTROL: mix.type = MIX_ON_OFF; -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) mix.ctl = SOUND_MIXER_LOUD; /* Is this correct ? */ #else - ksprintf(mix.ctlname, "fea%d-%s-%s", unit, - uaudio_id_name(sc, dps, srcId), - AudioNloudness); mix.ctlunit = ""; + ksnprintf(mix.ctlname, sizeof(mix.ctlname), + "%s.%s", mixername, AudioNloudness); #endif break; } @@ -1073,20 +1577,21 @@ uaudio_add_feature(struct uaudio_softc *sc, usb_descriptor_t *v, } } -void -uaudio_add_processing_updown(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) -{ - struct usb_audio_processing_unit *d = - (struct usb_audio_processing_unit *)v; - struct usb_audio_processing_unit_1 *d1 = - (struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins]; - struct usb_audio_processing_unit_updown *ud = - (struct usb_audio_processing_unit_updown *) - &d1->bmControls[d1->bControlSize]; +Static void +uaudio_add_processing_updown(struct uaudio_softc *sc, + const struct io_terminal *iot, int id) +{ + const struct usb_audio_processing_unit *d; + const struct usb_audio_processing_unit_1 *d1; + const struct usb_audio_processing_unit_updown *ud; struct mixerctl mix; int i; + d = iot[id].d.pu; + d1 = (const struct usb_audio_processing_unit_1 *) + &d->baSourceId[d->bNrInPins]; + ud = (const struct usb_audio_processing_unit_updown *) + &d1->bmControls[d1->bControlSize]; DPRINTFN(2,("uaudio_add_processing_updown: bUnitId=%d bNrModes=%d\n", d->bUnitId, ud->bNrModes)); @@ -1098,13 +1603,11 @@ uaudio_add_processing_updown(struct uaudio_softc *sc, usb_descriptor_t *v, mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(UD_MODE_SELECT_CONTROL, 0); -#if !defined(__DragonFly__) - mix.class = -1; -#endif + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; /* XXX */ -#if !defined(__DragonFly__) +#if !defined(__FreeBSD__) && !defined(__DragonFly__) mix.ctlunit = ""; - ksprintf(mix.ctlname, "pro%d-mode", d->bUnitId); + ksnprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d-mode", d->bUnitId); #endif for (i = 0; i < ud->bNrModes; i++) { @@ -1115,17 +1618,18 @@ uaudio_add_processing_updown(struct uaudio_softc *sc, usb_descriptor_t *v, uaudio_mixer_add_ctl(sc, &mix); } -void -uaudio_add_processing(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) -{ - struct usb_audio_processing_unit *d = - (struct usb_audio_processing_unit *)v; - struct usb_audio_processing_unit_1 *d1 = - (struct usb_audio_processing_unit_1 *)&d->baSourceId[d->bNrInPins]; - int ptype = UGETW(d->wProcessType); +Static void +uaudio_add_processing(struct uaudio_softc *sc, const struct io_terminal *iot, int id) +{ + const struct usb_audio_processing_unit *d; + const struct usb_audio_processing_unit_1 *d1; + int ptype; struct mixerctl mix; + d = iot[id].d.pu; + d1 = (const struct usb_audio_processing_unit_1 *) + &d->baSourceId[d->bNrInPins]; + ptype = UGETW(d->wProcessType); DPRINTFN(2,("uaudio_add_processing: wProcessType=%d bUnitId=%d " "bNrInPins=%d\n", ptype, d->bUnitId, d->bNrInPins)); @@ -1133,20 +1637,19 @@ uaudio_add_processing(struct uaudio_softc *sc, usb_descriptor_t *v, mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(XX_ENABLE_CONTROL, 0); -#if !defined(__DragonFly__) - mix.class = -1; -#endif + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; -#if !defined(__DragonFly__) +#if !defined(__FreeBSD__) && !defined(__DragonFly__) mix.ctlunit = ""; - ksprintf(mix.ctlname, "pro%d.%d-enable", d->bUnitId, ptype); + ksnprintf(mix.ctlname, sizeof(mix.ctlname), "pro%d.%d-enable", + d->bUnitId, ptype); #endif uaudio_mixer_add_ctl(sc, &mix); } switch(ptype) { case UPDOWNMIX_PROCESS: - uaudio_add_processing_updown(sc, v, dps); + uaudio_add_processing_updown(sc, iot, id); break; case DOLBY_PROLOGIC_PROCESS: case P3D_STEREO_EXTENDER_PROCESS: @@ -1162,16 +1665,16 @@ uaudio_add_processing(struct uaudio_softc *sc, usb_descriptor_t *v, } } -void -uaudio_add_extension(struct uaudio_softc *sc, usb_descriptor_t *v, - usb_descriptor_t **dps) +Static void +uaudio_add_extension(struct uaudio_softc *sc, const struct io_terminal *iot, int id) { - struct usb_audio_extension_unit *d = - (struct usb_audio_extension_unit *)v; - struct usb_audio_extension_unit_1 *d1 = - (struct usb_audio_extension_unit_1 *)&d->baSourceId[d->bNrInPins]; + const struct usb_audio_extension_unit *d; + const struct usb_audio_extension_unit_1 *d1; struct mixerctl mix; + d = iot[id].d.eu; + d1 = (const struct usb_audio_extension_unit_1 *) + &d->baSourceId[d->bNrInPins]; DPRINTFN(2,("uaudio_add_extension: bUnitId=%d bNrInPins=%d\n", d->bUnitId, d->bNrInPins)); @@ -1182,86 +1685,281 @@ uaudio_add_extension(struct uaudio_softc *sc, usb_descriptor_t *v, mix.wIndex = MAKE(d->bUnitId, sc->sc_ac_iface); mix.nchan = 1; mix.wValue[0] = MAKE(UA_EXT_ENABLE, 0); -#if !defined(__DragonFly__) - mix.class = -1; -#endif + uaudio_determine_class(&iot[id], &mix); mix.type = MIX_ON_OFF; -#if !defined(__DragonFly__) +#if !defined(__FreeBSD__) && !defined(__DragonFly__) mix.ctlunit = ""; - ksprintf(mix.ctlname, "ext%d-enable", d->bUnitId); + ksnprintf(mix.ctlname, sizeof(mix.ctlname), "ext%d-enable", + d->bUnitId); #endif uaudio_mixer_add_ctl(sc, &mix); } } -usbd_status -uaudio_identify(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc) +Static struct terminal_list* +uaudio_merge_terminal_list(const struct io_terminal *iot) +{ + struct terminal_list *tml; + uint16_t *ptm; + int i, len; + + len = 0; + if (iot->inputs == NULL) + return NULL; + for (i = 0; i < iot->inputs_size; i++) { + if (iot->inputs[i] != NULL) + len += iot->inputs[i]->size; + } + tml = kmalloc(TERMINAL_LIST_SIZE(len), M_TEMP, M_NOWAIT); + if (tml == NULL) { + kprintf("uaudio_merge_terminal_list: no memory\n"); + return NULL; + } + tml->size = 0; + ptm = tml->terminals; + for (i = 0; i < iot->inputs_size; i++) { + if (iot->inputs[i] == NULL) + continue; + if (iot->inputs[i]->size > len) + break; + memcpy(ptm, iot->inputs[i]->terminals, + iot->inputs[i]->size * sizeof(uint16_t)); + tml->size += iot->inputs[i]->size; + ptm += iot->inputs[i]->size; + len -= iot->inputs[i]->size; + } + return tml; +} + +Static struct terminal_list * +uaudio_io_terminaltype(int outtype, struct io_terminal *iot, int id) +{ + struct terminal_list *tml; + struct io_terminal *it; + int src_id, i; + + it = &iot[id]; + if (it->output != NULL) { + /* already has outtype? */ + for (i = 0; i < it->output->size; i++) + if (it->output->terminals[i] == outtype) + return uaudio_merge_terminal_list(it); + tml = kmalloc(TERMINAL_LIST_SIZE(it->output->size + 1), + M_TEMP, M_NOWAIT); + if (tml == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return uaudio_merge_terminal_list(it); + } + memcpy(tml, it->output, TERMINAL_LIST_SIZE(it->output->size)); + tml->terminals[it->output->size] = outtype; + tml->size++; + kfree(it->output, M_TEMP); + it->output = tml; + if (it->inputs != NULL) { + for (i = 0; i < it->inputs_size; i++) + if (it->inputs[i] != NULL) + kfree(it->inputs[i], M_TEMP); + kfree(it->inputs, M_TEMP); + } + it->inputs_size = 0; + it->inputs = NULL; + } else { /* end `iot[id] != NULL' */ + it->inputs_size = 0; + it->inputs = NULL; + it->output = kmalloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT); + if (it->output == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + it->output->terminals[0] = outtype; + it->output->size = 1; + it->direct = FALSE; + } + + switch (it->d.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + it->inputs = kmalloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + tml = kmalloc(TERMINAL_LIST_SIZE(1), M_TEMP, M_NOWAIT); + if (tml == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + kfree(it->inputs, M_TEMP); + it->inputs = NULL; + return NULL; + } + it->inputs[0] = tml; + tml->terminals[0] = UGETW(it->d.it->wTerminalType); + tml->size = 1; + it->inputs_size = 1; + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_FEATURE: + src_id = it->d.fu->bSourceId; + it->inputs = kmalloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return uaudio_io_terminaltype(outtype, iot, src_id); + } + it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); + it->inputs_size = 1; + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_OUTPUT: + it->inputs = kmalloc(sizeof(struct terminal_list *), M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + src_id = it->d.ot->bSourceId; + it->inputs[0] = uaudio_io_terminaltype(outtype, iot, src_id); + it->inputs_size = 1; + iot[src_id].direct = TRUE; + return NULL; + case UDESCSUB_AC_MIXER: + it->inputs_size = 0; + it->inputs = kmalloc(sizeof(struct terminal_list *) + * it->d.mu->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.mu->bNrInPins; i++) { + src_id = it->d.mu->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_SELECTOR: + it->inputs_size = 0; + it->inputs = kmalloc(sizeof(struct terminal_list *) + * it->d.su->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.su->bNrInPins; i++) { + src_id = it->d.su->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_PROCESSING: + it->inputs_size = 0; + it->inputs = kmalloc(sizeof(struct terminal_list *) + * it->d.pu->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.pu->bNrInPins; i++) { + src_id = it->d.pu->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_EXTENSION: + it->inputs_size = 0; + it->inputs = kmalloc(sizeof(struct terminal_list *) + * it->d.eu->bNrInPins, M_TEMP, M_NOWAIT); + if (it->inputs == NULL) { + kprintf("uaudio_io_terminaltype: no memory\n"); + return NULL; + } + for (i = 0; i < it->d.eu->bNrInPins; i++) { + src_id = it->d.eu->baSourceId[i]; + it->inputs[i] = uaudio_io_terminaltype(outtype, iot, + src_id); + it->inputs_size++; + } + return uaudio_merge_terminal_list(it); + case UDESCSUB_AC_HEADER: + default: + return NULL; + } +} + +Static usbd_status +uaudio_identify(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { usbd_status err; err = uaudio_identify_ac(sc, cdesc); if (err) - return (err); - return (uaudio_identify_as(sc, cdesc)); + return err; + return uaudio_identify_as(sc, cdesc); } -void -uaudio_add_alt(struct uaudio_softc *sc, struct as_info *ai) +Static void +uaudio_add_alt(struct uaudio_softc *sc, const struct as_info *ai) { - size_t len = sizeof(*ai) * (sc->sc_nalts + 1); - struct as_info *nai = sc->sc_nalts == 0 ? - kmalloc(len, M_USBDEV, M_NOWAIT) : - krealloc(sc->sc_alts, len, M_USBDEV, M_NOWAIT); + size_t len; + struct as_info *nai; + len = sizeof(*ai) * (sc->sc_nalts + 1); + nai = kmalloc(len, M_USBDEV, M_NOWAIT); if (nai == NULL) { kprintf("uaudio_add_alt: no memory\n"); return; } - + /* Copy old data, if there was any */ + if (sc->sc_nalts != 0) { + memcpy(nai, sc->sc_alts, sizeof(*ai) * (sc->sc_nalts)); + kfree(sc->sc_alts, M_USBDEV); + } sc->sc_alts = nai; DPRINTFN(2,("uaudio_add_alt: adding alt=%d, enc=%d\n", ai->alt, ai->encoding)); sc->sc_alts[sc->sc_nalts++] = *ai; } -usbd_status -uaudio_process_as(struct uaudio_softc *sc, char *buf, int *offsp, - int size, usb_interface_descriptor_t *id) +Static usbd_status +uaudio_process_as(struct uaudio_softc *sc, const char *buf, int *offsp, + int size, const usb_interface_descriptor_t *id) #define offs (*offsp) { - struct usb_audio_streaming_interface_descriptor *asid; - struct usb_audio_streaming_type1_descriptor *asf1d; - usb_endpoint_descriptor_audio_t *ed; - struct usb_audio_streaming_endpoint_descriptor *sed; + const struct usb_audio_streaming_interface_descriptor *asid; + const struct usb_audio_streaming_type1_descriptor *asf1d; + const usb_endpoint_descriptor_audio_t *ed; + const usb_endpoint_descriptor_audio_t *epdesc1; + const struct usb_audio_streaming_endpoint_descriptor *sed; int format, chan, prec, enc; - int dir, type; + int dir, type, sync; struct as_info ai; + const char *format_str; + + asid = (const void *)(buf + offs); - asid = (void *)(buf + offs); if (asid->bDescriptorType != UDESC_CS_INTERFACE || asid->bDescriptorSubtype != AS_GENERAL) - return (USBD_INVAL); + return USBD_INVAL; + DPRINTF(("uaudio_process_as: asid: bTerminakLink=%d wFormatTag=%d\n", + asid->bTerminalLink, UGETW(asid->wFormatTag))); offs += asid->bLength; if (offs > size) - return (USBD_INVAL); - asf1d = (void *)(buf + offs); + return USBD_INVAL; + + asf1d = (const void *)(buf + offs); if (asf1d->bDescriptorType != UDESC_CS_INTERFACE || asf1d->bDescriptorSubtype != FORMAT_TYPE) - return (USBD_INVAL); + return USBD_INVAL; offs += asf1d->bLength; if (offs > size) - return (USBD_INVAL); + return USBD_INVAL; if (asf1d->bFormatType != FORMAT_TYPE_I) { kprintf("%s: ignored setting with type %d format\n", USBDEVNAME(sc->sc_dev), UGETW(asid->wFormatTag)); - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; } - ed = (void *)(buf + offs); + ed = (const void *)(buf + offs); if (ed->bDescriptorType != UDESC_ENDPOINT) - return (USBD_INVAL); - DPRINTF(("uaudio_process_as: endpoint bLength=%d bDescriptorType=%d " + return USBD_INVAL; + DPRINTF(("uaudio_process_as: endpoint[0] bLength=%d bDescriptorType=%d " "bEndpointAddress=%d bmAttributes=0x%x wMaxPacketSize=%d " "bInterval=%d bRefresh=%d bSynchAddress=%d\n", ed->bLength, ed->bDescriptorType, ed->bEndpointAddress, @@ -1269,9 +1967,9 @@ uaudio_process_as(struct uaudio_softc *sc, char *buf, int *offsp, ed->bInterval, ed->bRefresh, ed->bSynchAddress)); offs += ed->bLength; if (offs > size) - return (USBD_INVAL); + return USBD_INVAL; if (UE_GET_XFERTYPE(ed->bmAttributes) != UE_ISOCHRONOUS) - return (USBD_INVAL); + return USBD_INVAL; dir = UE_GET_DIR(ed->bEndpointAddress); type = UE_GET_ISO_TYPE(ed->bmAttributes); @@ -1280,92 +1978,206 @@ uaudio_process_as(struct uaudio_softc *sc, char *buf, int *offsp, type = UE_ISO_ASYNC; /* We can't handle endpoints that need a sync pipe yet. */ - if (dir == UE_DIR_IN ? type == UE_ISO_ADAPT : type == UE_ISO_ASYNC) { - kprintf("%s: ignored %sput endpoint of type %s\n", - USBDEVNAME(sc->sc_dev), - dir == UE_DIR_IN ? "in" : "out", - dir == UE_DIR_IN ? "adaptive" : "async"); - return (USBD_NORMAL_COMPLETION); + sync = FALSE; + if (dir == UE_DIR_IN && type == UE_ISO_ADAPT) { + sync = TRUE; +#ifndef UAUDIO_MULTIPLE_ENDPOINTS + kprintf("%s: ignored input endpoint of type adaptive\n", + USBDEVNAME(sc->sc_dev)); + return USBD_NORMAL_COMPLETION; +#endif } - - sed = (void *)(buf + offs); + if (dir != UE_DIR_IN && type == UE_ISO_ASYNC) { + sync = TRUE; +#ifndef UAUDIO_MULTIPLE_ENDPOINTS + kprintf("%s: ignored output endpoint of type async\n", + USBDEVNAME(sc->sc_dev)); + return USBD_NORMAL_COMPLETION; +#endif + } + + sed = (const void *)(buf + offs); if (sed->bDescriptorType != UDESC_CS_ENDPOINT || sed->bDescriptorSubtype != AS_GENERAL) - return (USBD_INVAL); + return USBD_INVAL; + DPRINTF((" streadming_endpoint: offset=%d bLength=%d\n", offs, sed->bLength)); offs += sed->bLength; if (offs > size) - return (USBD_INVAL); - + return USBD_INVAL; + + if (sync && id->bNumEndpoints <= 1) { + kprintf("%s: a sync-pipe endpoint but no other endpoint\n", + USBDEVNAME(sc->sc_dev)); + return USBD_INVAL; + } + if (!sync && id->bNumEndpoints > 1) { + kprintf("%s: non sync-pipe endpoint but multiple endpoints\n", + USBDEVNAME(sc->sc_dev)); + return USBD_INVAL; + } + epdesc1 = NULL; + if (id->bNumEndpoints > 1) { + epdesc1 = (const void*)(buf + offs); + if (epdesc1->bDescriptorType != UDESC_ENDPOINT) + return USBD_INVAL; + DPRINTF(("uaudio_process_as: endpoint[1] bLength=%d " + "bDescriptorType=%d bEndpointAddress=%d " + "bmAttributes=0x%x wMaxPacketSize=%d bInterval=%d " + "bRefresh=%d bSynchAddress=%d\n", + epdesc1->bLength, epdesc1->bDescriptorType, + epdesc1->bEndpointAddress, epdesc1->bmAttributes, + UGETW(epdesc1->wMaxPacketSize), epdesc1->bInterval, + epdesc1->bRefresh, epdesc1->bSynchAddress)); + offs += epdesc1->bLength; + if (offs > size) + return USBD_INVAL; + if (epdesc1->bSynchAddress != 0) { + kprintf("%s: invalid endpoint: bSynchAddress=0\n", + USBDEVNAME(sc->sc_dev)); + return USBD_INVAL; + } + if (UE_GET_XFERTYPE(epdesc1->bmAttributes) != UE_ISOCHRONOUS) { + kprintf("%s: invalid endpoint: bmAttributes=0x%x\n", + USBDEVNAME(sc->sc_dev), epdesc1->bmAttributes); + return USBD_INVAL; + } + if (epdesc1->bEndpointAddress != ed->bSynchAddress) { + kprintf("%s: invalid endpoint addresses: " + "ep[0]->bSynchAddress=0x%x " + "ep[1]->bEndpointAddress=0x%x\n", + USBDEVNAME(sc->sc_dev), ed->bSynchAddress, + epdesc1->bEndpointAddress); + return USBD_INVAL; + } + /* UE_GET_ADDR(epdesc1->bEndpointAddress), and epdesc1->bRefresh */ + } + format = UGETW(asid->wFormatTag); chan = asf1d->bNrChannels; prec = asf1d->bBitResolution; - if (prec != 8 && prec != 16) { -#ifdef USB_DEBUG + if (prec != 8 && prec != 16 && prec != 24 && prec != 32) { kprintf("%s: ignored setting with precision %d\n", USBDEVNAME(sc->sc_dev), prec); -#endif - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; } switch (format) { case UA_FMT_PCM: - sc->sc_altflags |= prec == 8 ? HAS_8 : HAS_16; + if (prec == 8) { + sc->sc_altflags |= HAS_8; + } else if (prec == 16) { + sc->sc_altflags |= HAS_16; + } else if (prec == 24) { + sc->sc_altflags |= HAS_24; + } else if (prec == 32) { + sc->sc_altflags |= HAS_32; + } enc = AUDIO_ENCODING_SLINEAR_LE; + format_str = "pcm"; break; case UA_FMT_PCM8: enc = AUDIO_ENCODING_ULINEAR_LE; sc->sc_altflags |= HAS_8U; + format_str = "pcm8"; break; case UA_FMT_ALAW: enc = AUDIO_ENCODING_ALAW; sc->sc_altflags |= HAS_ALAW; + format_str = "alaw"; break; case UA_FMT_MULAW: enc = AUDIO_ENCODING_ULAW; sc->sc_altflags |= HAS_MULAW; + format_str = "mulaw"; break; + case UA_FMT_IEEE_FLOAT: default: kprintf("%s: ignored setting with format %d\n", USBDEVNAME(sc->sc_dev), format); - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; + } +#ifdef USB_DEBUG + kprintf("%s: %s: %dch, %d/%dbit, %s,", USBDEVNAME(sc->sc_dev), + dir == UE_DIR_IN ? "recording" : "playback", + chan, prec, asf1d->bSubFrameSize * 8, format_str); + if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + kprintf(" %d-%dHz\n", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); + } else { + int r; + kprintf(" %d", UA_GETSAMP(asf1d, 0)); + for (r = 1; r < asf1d->bSamFreqType; r++) + kprintf(",%d", UA_GETSAMP(asf1d, r)); + kprintf("Hz\n"); + } +#endif +#if defined(__FreeBSD__) || defined(__DragonFly__) + if (sc->uaudio_sndstat_flag != 0) { + sbuf_printf(&(sc->uaudio_sndstat), "\n\t"); + sbuf_printf(&(sc->uaudio_sndstat), + "mode %d:(%s) %dch, %d/%dbit, %s,", + id->bAlternateSetting, + dir == UE_DIR_IN ? "input" : "output", + chan, prec, asf1d->bSubFrameSize * 8, format_str); + if (asf1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + sbuf_printf(&(sc->uaudio_sndstat), " %d-%dHz", + UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); + } else { + int r; + sbuf_printf(&(sc->uaudio_sndstat), + " %d", UA_GETSAMP(asf1d, 0)); + for (r = 1; r < asf1d->bSamFreqType; r++) + sbuf_printf(&(sc->uaudio_sndstat), + ",%d", UA_GETSAMP(asf1d, r)); + sbuf_printf(&(sc->uaudio_sndstat), "Hz"); + } } - DPRINTFN(1,("uaudio_identify: alt=%d enc=%d chan=%d prec=%d\n", - id->bAlternateSetting, enc, chan, prec)); +#endif ai.alt = id->bAlternateSetting; ai.encoding = enc; + ai.attributes = sed->bmAttributes; ai.idesc = id; ai.edesc = ed; + ai.edesc1 = epdesc1; ai.asf1desc = asf1d; + ai.sc_busy = 0; uaudio_add_alt(sc, &ai); - sc->sc_chan.terminal = asid->bTerminalLink; /* XXX */ - sc->sc_chan.dir |= dir == UE_DIR_OUT ? AUMODE_PLAY : AUMODE_RECORD; - return (USBD_NORMAL_COMPLETION); +#ifdef USB_DEBUG + if (ai.attributes & UA_SED_FREQ_CONTROL) + DPRINTFN(1, ("uaudio_process_as: FREQ_CONTROL\n")); + if (ai.attributes & UA_SED_PITCH_CONTROL) + DPRINTFN(1, ("uaudio_process_as: PITCH_CONTROL\n")); +#endif + sc->sc_mode |= (dir == UE_DIR_OUT) ? AUMODE_PLAY : AUMODE_RECORD; + + return USBD_NORMAL_COMPLETION; } #undef offs - -usbd_status -uaudio_identify_as(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc) + +Static usbd_status +uaudio_identify_as(struct uaudio_softc *sc, + const usb_config_descriptor_t *cdesc) { - usb_interface_descriptor_t *id; - usbd_status err; - char *buf; + const usb_interface_descriptor_t *id; + const char *buf; int size, offs; size = UGETW(cdesc->wTotalLength); - buf = (char *)cdesc; + buf = (const char *)cdesc; /* Locate the AudioStreaming interface descriptor. */ offs = 0; id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOSTREAM); if (id == NULL) - return (USBD_INVAL); - - sc->sc_chan.terminal = -1; - sc->sc_chan.dir = 0; + return USBD_INVAL; +#if defined(__FreeBSD__) || defined(__DragonFly__) + sc->uaudio_sndstat_flag = 0; + if (sbuf_new(&(sc->uaudio_sndstat), NULL, 4096, SBUF_AUTOEXTEND) != NULL) + sc->uaudio_sndstat_flag = 1; +#endif /* Loop through all the alternate settings. */ while (offs <= size) { - DPRINTFN(2, ("uaudio_identify: interface %d\n", - id->bInterfaceNumber)); + DPRINTFN(2, ("uaudio_identify: interface=%d offset=%d\n", + id->bInterfaceNumber, offs)); switch (id->bNumEndpoints) { case 0: DPRINTFN(2, ("uaudio_identify: AS null alt=%d\n", @@ -1373,194 +2185,340 @@ uaudio_identify_as(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc) sc->sc_nullalt = id->bAlternateSetting; break; case 1: - err = uaudio_process_as(sc, buf, &offs, size, id); +#ifdef UAUDIO_MULTIPLE_ENDPOINTS + case 2: +#endif + uaudio_process_as(sc, buf, &offs, size, id); break; default: -#ifdef USB_DEBUG kprintf("%s: ignored audio interface with %d " "endpoints\n", USBDEVNAME(sc->sc_dev), id->bNumEndpoints); -#endif break; } id = uaudio_find_iface(buf, size, &offs,UISUBCLASS_AUDIOSTREAM); if (id == NULL) break; } +#if defined(__FreeBSD__) || defined(__DragonFly__) + sbuf_finish(&(sc->uaudio_sndstat)); +#endif if (offs > size) - return (USBD_INVAL); + return USBD_INVAL; DPRINTF(("uaudio_identify_as: %d alts available\n", sc->sc_nalts)); - if (sc->sc_chan.terminal < 0) { - kprintf("%s: no useable endpoint found\n", + + if (sc->sc_mode == 0) { + kprintf("%s: no usable endpoint found\n", USBDEVNAME(sc->sc_dev)); - return (USBD_INVAL); + return USBD_INVAL; } -#ifndef NO_RECORDING - if (sc->sc_chan.dir == (AUMODE_PLAY | AUMODE_RECORD)) - sc->sc_props |= AUDIO_PROP_FULLDUPLEX; -#endif - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; } -usbd_status -uaudio_identify_ac(struct uaudio_softc *sc, usb_config_descriptor_t *cdesc) +Static usbd_status +uaudio_identify_ac(struct uaudio_softc *sc, const usb_config_descriptor_t *cdesc) { - usb_interface_descriptor_t *id; - struct usb_audio_control_descriptor *acdp; - usb_descriptor_t *dp, *dps[256]; - char *buf, *ibuf, *ibufend; - int size, offs, aclen, ndps, i; + struct io_terminal* iot; + const usb_interface_descriptor_t *id; + const struct usb_audio_control_descriptor *acdp; + const usb_descriptor_t *dp; + const struct usb_audio_output_terminal *pot; + struct terminal_list *tml; + const char *buf, *ibuf, *ibufend; + int size, offs, aclen, ndps, i, j; size = UGETW(cdesc->wTotalLength); - buf = (char *)cdesc; + buf = (const char *)cdesc; /* Locate the AudioControl interface descriptor. */ offs = 0; id = uaudio_find_iface(buf, size, &offs, UISUBCLASS_AUDIOCONTROL); if (id == NULL) - return (USBD_INVAL); + return USBD_INVAL; if (offs + sizeof *acdp > size) - return (USBD_INVAL); + return USBD_INVAL; sc->sc_ac_iface = id->bInterfaceNumber; - DPRINTFN(2,("uaudio_identify: AC interface is %d\n", sc->sc_ac_iface)); + DPRINTFN(2,("uaudio_identify_ac: AC interface is %d\n", sc->sc_ac_iface)); /* A class-specific AC interface header should follow. */ ibuf = buf + offs; - acdp = (struct usb_audio_control_descriptor *)ibuf; + acdp = (const struct usb_audio_control_descriptor *)ibuf; if (acdp->bDescriptorType != UDESC_CS_INTERFACE || acdp->bDescriptorSubtype != UDESCSUB_AC_HEADER) - return (USBD_INVAL); + return USBD_INVAL; aclen = UGETW(acdp->wTotalLength); if (offs + aclen > size) - return (USBD_INVAL); + return USBD_INVAL; if (!(usbd_get_quirks(sc->sc_udev)->uq_flags & UQ_BAD_ADC) && UGETW(acdp->bcdADC) != UAUDIO_VERSION) - return (USBD_INVAL); + return USBD_INVAL; sc->sc_audio_rev = UGETW(acdp->bcdADC); - DPRINTFN(2,("uaudio_identify: found AC header, vers=%03x, len=%d\n", + DPRINTFN(2,("uaudio_identify_ac: found AC header, vers=%03x, len=%d\n", sc->sc_audio_rev, aclen)); sc->sc_nullalt = -1; /* Scan through all the AC specific descriptors */ ibufend = ibuf + aclen; - dp = (usb_descriptor_t *)ibuf; + dp = (const usb_descriptor_t *)ibuf; ndps = 0; - memset(dps, 0, sizeof dps); + iot = kmalloc(sizeof(struct io_terminal) * 256, M_TEMP, M_NOWAIT | M_ZERO); + if (iot == NULL) { + kprintf("%s: no memory\n", __func__); + return USBD_NOMEM; + } for (;;) { ibuf += dp->bLength; if (ibuf >= ibufend) break; - dp = (usb_descriptor_t *)ibuf; - if (ibuf + dp->bLength > ibufend) - return (USBD_INVAL); + dp = (const usb_descriptor_t *)ibuf; + if (ibuf + dp->bLength > ibufend) { + kfree(iot, M_TEMP); + return USBD_INVAL; + } if (dp->bDescriptorType != UDESC_CS_INTERFACE) { - kprintf("uaudio_identify: skip desc type=0x%02x\n", + kprintf("uaudio_identify_ac: skip desc type=0x%02x\n", dp->bDescriptorType); continue; } - i = ((struct usb_audio_input_terminal *)dp)->bTerminalId; - dps[i] = dp; + i = ((const struct usb_audio_input_terminal *)dp)->bTerminalId; + iot[i].d.desc = dp; if (i > ndps) ndps = i; } ndps++; + /* construct io_terminal */ + for (i = 0; i < ndps; i++) { + dp = iot[i].d.desc; + if (dp == NULL) + continue; + if (dp->bDescriptorSubtype != UDESCSUB_AC_OUTPUT) + continue; + pot = iot[i].d.ot; + tml = uaudio_io_terminaltype(UGETW(pot->wTerminalType), iot, i); + if (tml != NULL) + kfree(tml, M_TEMP); + } + +#ifdef USB_DEBUG + for (i = 0; i < 256; i++) { + struct usb_audio_cluster cluster; + + if (iot[i].d.desc == NULL) + continue; + logprintf("id %d:\t", i); + switch (iot[i].d.desc->bDescriptorSubtype) { + case UDESCSUB_AC_INPUT: + logprintf("AC_INPUT type=%s\n", uaudio_get_terminal_name + (UGETW(iot[i].d.it->wTerminalType))); + logprintf("\t"); + cluster = uaudio_get_cluster(i, iot); + uaudio_dump_cluster(&cluster); + logprintf("\n"); + break; + case UDESCSUB_AC_OUTPUT: + logprintf("AC_OUTPUT type=%s ", uaudio_get_terminal_name + (UGETW(iot[i].d.ot->wTerminalType))); + logprintf("src=%d\n", iot[i].d.ot->bSourceId); + break; + case UDESCSUB_AC_MIXER: + logprintf("AC_MIXER src="); + for (j = 0; j < iot[i].d.mu->bNrInPins; j++) + logprintf("%d ", iot[i].d.mu->baSourceId[j]); + logprintf("\n\t"); + cluster = uaudio_get_cluster(i, iot); + uaudio_dump_cluster(&cluster); + logprintf("\n"); + break; + case UDESCSUB_AC_SELECTOR: + logprintf("AC_SELECTOR src="); + for (j = 0; j < iot[i].d.su->bNrInPins; j++) + logprintf("%d ", iot[i].d.su->baSourceId[j]); + logprintf("\n"); + break; + case UDESCSUB_AC_FEATURE: + logprintf("AC_FEATURE src=%d\n", iot[i].d.fu->bSourceId); + break; + case UDESCSUB_AC_PROCESSING: + logprintf("AC_PROCESSING src="); + for (j = 0; j < iot[i].d.pu->bNrInPins; j++) + logprintf("%d ", iot[i].d.pu->baSourceId[j]); + logprintf("\n\t"); + cluster = uaudio_get_cluster(i, iot); + uaudio_dump_cluster(&cluster); + logprintf("\n"); + break; + case UDESCSUB_AC_EXTENSION: + logprintf("AC_EXTENSION src="); + for (j = 0; j < iot[i].d.eu->bNrInPins; j++) + logprintf("%d ", iot[i].d.eu->baSourceId[j]); + logprintf("\n\t"); + cluster = uaudio_get_cluster(i, iot); + uaudio_dump_cluster(&cluster); + logprintf("\n"); + break; + default: + logprintf("unknown audio control (subtype=%d)\n", + iot[i].d.desc->bDescriptorSubtype); + } + for (j = 0; j < iot[i].inputs_size; j++) { + int k; + logprintf("\tinput%d: ", j); + tml = iot[i].inputs[j]; + if (tml == NULL) { + logprintf("NULL\n"); + continue; + } + for (k = 0; k < tml->size; k++) + logprintf("%s ", uaudio_get_terminal_name + (tml->terminals[k])); + logprintf("\n"); + } + logprintf("\toutput: "); + tml = iot[i].output; + for (j = 0; j < tml->size; j++) + logprintf("%s ", uaudio_get_terminal_name(tml->terminals[j])); + logprintf("\n"); + } +#endif + for (i = 0; i < ndps; i++) { - dp = dps[i]; + dp = iot[i].d.desc; if (dp == NULL) continue; - DPRINTF(("uaudio_identify: subtype=%d\n", - dp->bDescriptorSubtype)); + DPRINTF(("uaudio_identify_ac: id=%d subtype=%d\n", + i, dp->bDescriptorSubtype)); switch (dp->bDescriptorSubtype) { case UDESCSUB_AC_HEADER: - kprintf("uaudio_identify: unexpected AC header\n"); + kprintf("uaudio_identify_ac: unexpected AC header\n"); break; case UDESCSUB_AC_INPUT: - uaudio_add_input(sc, dp, dps); + uaudio_add_input(sc, iot, i); break; case UDESCSUB_AC_OUTPUT: - uaudio_add_output(sc, dp, dps); + uaudio_add_output(sc, iot, i); break; case UDESCSUB_AC_MIXER: - uaudio_add_mixer(sc, dp, dps); + uaudio_add_mixer(sc, iot, i); break; case UDESCSUB_AC_SELECTOR: - uaudio_add_selector(sc, dp, dps); + uaudio_add_selector(sc, iot, i); break; case UDESCSUB_AC_FEATURE: - uaudio_add_feature(sc, dp, dps); + uaudio_add_feature(sc, iot, i); break; case UDESCSUB_AC_PROCESSING: - uaudio_add_processing(sc, dp, dps); + uaudio_add_processing(sc, iot, i); break; case UDESCSUB_AC_EXTENSION: - uaudio_add_extension(sc, dp, dps); + uaudio_add_extension(sc, iot, i); break; default: - kprintf("uaudio_identify: bad AC desc subtype=0x%02x\n", + kprintf("uaudio_identify_ac: bad AC desc subtype=0x%02x\n", dp->bDescriptorSubtype); break; } } - return (USBD_NORMAL_COMPLETION); + + /* delete io_terminal */ + for (i = 0; i < 256; i++) { + if (iot[i].d.desc == NULL) + continue; + if (iot[i].inputs != NULL) { + for (j = 0; j < iot[i].inputs_size; j++) { + if (iot[i].inputs[j] != NULL) + kfree(iot[i].inputs[j], M_TEMP); + } + kfree(iot[i].inputs, M_TEMP); + } + if (iot[i].output != NULL) + kfree(iot[i].output, M_TEMP); + iot[i].d.desc = NULL; + } + kfree(iot, M_TEMP); + + return USBD_NORMAL_COMPLETION; } #if defined(__NetBSD__) || defined(__OpenBSD__) -int +Static int uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; struct mixerctl *mc; - int n, nctls; + int n, nctls, i; + sc = addr; DPRINTFN(2,("uaudio_query_devinfo: index=%d\n", mi->index)); if (sc->sc_dying) - return (EIO); - + return EIO; + n = mi->index; nctls = sc->sc_nctls; - if (n < 0 || n >= nctls) { - switch (n - nctls) { - case UAC_OUTPUT: - mi->type = AUDIO_MIXER_CLASS; - mi->mixer_class = nctls + UAC_OUTPUT; - mi->next = mi->prev = AUDIO_MIXER_LAST; - strcpy(mi->label.name, AudioCoutputs); - return (0); - case UAC_INPUT: - mi->type = AUDIO_MIXER_CLASS; - mi->mixer_class = nctls + UAC_INPUT; - mi->next = mi->prev = AUDIO_MIXER_LAST; - strcpy(mi->label.name, AudioCinputs); - return (0); - case UAC_EQUAL: - mi->type = AUDIO_MIXER_CLASS; - mi->mixer_class = nctls + UAC_EQUAL; - mi->next = mi->prev = AUDIO_MIXER_LAST; - strcpy(mi->label.name, AudioCequalization); - return (0); - default: - return (ENXIO); - } + switch (n) { + case UAC_OUTPUT: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = UAC_OUTPUT; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strlcpy(mi->label.name, AudioCoutputs, sizeof(mi->label.name)); + return 0; + case UAC_INPUT: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = UAC_INPUT; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strlcpy(mi->label.name, AudioCinputs, sizeof(mi->label.name)); + return 0; + case UAC_EQUAL: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = UAC_EQUAL; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strlcpy(mi->label.name, AudioCequalization, + sizeof(mi->label.name)); + return 0; + case UAC_RECORD: + mi->type = AUDIO_MIXER_CLASS; + mi->mixer_class = UAC_RECORD; + mi->next = mi->prev = AUDIO_MIXER_LAST; + strlcpy(mi->label.name, AudioCrecord, sizeof(mi->label.name)); + return 0; + default: + break; } + + n -= UAC_NCLASSES; + if (n < 0 || n >= nctls) + return ENXIO; + mc = &sc->sc_ctls[n]; - strncpy(mi->label.name, mc->ctlname, MAX_AUDIO_DEV_LEN); + strlcpy(mi->label.name, mc->ctlname, sizeof(mi->label.name)); mi->mixer_class = mc->class; mi->next = mi->prev = AUDIO_MIXER_LAST; /* XXX */ switch (mc->type) { case MIX_ON_OFF: mi->type = AUDIO_MIXER_ENUM; mi->un.e.num_mem = 2; - strcpy(mi->un.e.member[0].label.name, AudioNoff); + strlcpy(mi->un.e.member[0].label.name, AudioNoff, + sizeof(mi->un.e.member[0].label.name)); mi->un.e.member[0].ord = 0; - strcpy(mi->un.e.member[1].label.name, AudioNon); + strlcpy(mi->un.e.member[1].label.name, AudioNon, + sizeof(mi->un.e.member[1].label.name)); mi->un.e.member[1].ord = 1; break; + case MIX_SELECTOR: + mi->type = AUDIO_MIXER_ENUM; + mi->un.e.num_mem = mc->maxval - mc->minval + 1; + for (i = 0; i <= mc->maxval - mc->minval; i++) { + ksnprintf(mi->un.e.member[i].label.name, + sizeof(mi->un.e.member[i].label.name), + "%d", i + mc->minval); + mi->un.e.member[i].ord = i + mc->minval; + } + break; default: mi->type = AUDIO_MIXER_VALUE; strncpy(mi->un.v.units.name, mc->ctlunit, MAX_AUDIO_DEV_LEN); @@ -1568,126 +2526,131 @@ uaudio_query_devinfo(void *addr, mixer_devinfo_t *mi) mi->un.v.delta = mc->delta; break; } - return (0); + return 0; } -int +Static int uaudio_open(void *addr, int flags) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; - DPRINTF(("uaudio_open: sc=%p\n", sc)); + sc = addr; + DPRINTF(("uaudio_open: sc=%p\n", sc)); if (sc->sc_dying) - return (EIO); - - if (sc->sc_chan.terminal < 0) - return (ENXIO); + return EIO; - if ((flags & FREAD) && !(sc->sc_chan.dir & AUMODE_RECORD)) - return (EACCES); - if ((flags & FWRITE) && !(sc->sc_chan.dir & AUMODE_PLAY)) - return (EACCES); + if ((flags & FWRITE) && !(sc->sc_mode & AUMODE_PLAY)) + return EACCES; + if ((flags & FREAD) && !(sc->sc_mode & AUMODE_RECORD)) + return EACCES; - sc->sc_chan.intr = 0; - - return (0); + return 0; } /* - * Close function is called from a critical section. + * Close function is called at splaudio(). */ -void +Static void uaudio_close(void *addr) { - struct uaudio_softc *sc = addr; - - if (sc->sc_dying) - return (EIO); - - DPRINTF(("uaudio_close: sc=%p\n", sc)); - uaudio_halt_in_dma(sc); - uaudio_halt_out_dma(sc); - - sc->sc_chan.intr = 0; } -int +Static int uaudio_drain(void *addr) { - struct uaudio_softc *sc = addr; - - if (sc->sc_dying) - return (EIO); + struct uaudio_softc *sc; + sc = addr; usbd_delay_ms(sc->sc_udev, UAUDIO_NCHANBUFS * UAUDIO_NFRAMES); - return (0); + return 0; } -int +Static int uaudio_halt_out_dma(void *addr) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; + sc = addr; if (sc->sc_dying) - return (EIO); + return EIO; DPRINTF(("uaudio_halt_out_dma: enter\n")); - if (sc->sc_chan.pipe != NULL) { - uaudio_chan_close(sc, &sc->sc_chan); - sc->sc_chan.pipe = 0; - uaudio_chan_free_buffers(sc, &sc->sc_chan); + if (sc->sc_playchan.pipe != NULL) { + uaudio_chan_close(sc, &sc->sc_playchan); + sc->sc_playchan.pipe = NULL; + uaudio_chan_free_buffers(sc, &sc->sc_playchan); + sc->sc_playchan.intr = NULL; } - return (0); + return 0; } -int +Static int uaudio_halt_in_dma(void *addr) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; DPRINTF(("uaudio_halt_in_dma: enter\n")); - if (sc->sc_chan.pipe != NULL) { - uaudio_chan_close(sc, &sc->sc_chan); - sc->sc_chan.pipe = 0; - uaudio_chan_free_buffers(sc, &sc->sc_chan); + sc = addr; + if (sc->sc_recchan.pipe != NULL) { + uaudio_chan_close(sc, &sc->sc_recchan); + sc->sc_recchan.pipe = NULL; + uaudio_chan_free_buffers(sc, &sc->sc_recchan); + sc->sc_recchan.intr = NULL; } - return (0); + return 0; } -int +Static int uaudio_getdev(void *addr, struct audio_device *retp) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; DPRINTF(("uaudio_mixer_getdev:\n")); + sc = addr; if (sc->sc_dying) - return (EIO); - + return EIO; + *retp = uaudio_device; - return (0); + return 0; } /* * Make sure the block size is large enough to hold all outstanding transfers. */ -int +Static int uaudio_round_blocksize(void *addr, int blk) { - struct uaudio_softc *sc = addr; - int bpf; + struct uaudio_softc *sc; + int b; - if (sc->sc_dying) - return (EIO); + sc = addr; + DPRINTF(("uaudio_round_blocksize: blk=%d mode=%s\n", blk, + mode == AUMODE_PLAY ? "AUMODE_PLAY" : "AUMODE_RECORD")); + + /* chan.bytes_per_frame can be 0. */ + if (mode == AUMODE_PLAY || sc->sc_recchan.bytes_per_frame <= 0) { + b = param->sample_rate * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; - bpf = sc->sc_chan.bytes_per_frame + sc->sc_chan.sample_size; - /* XXX */ - bpf *= UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; + /* + * This does not make accurate value in the case + * of b % USB_FRAMES_PER_SECOND != 0 + */ + b /= USB_FRAMES_PER_SECOND; - bpf = (bpf + 15) &~ 15; + b *= param->precision / 8 * param->channels; + } else { + /* + * use wMaxPacketSize in bytes_per_frame. + * See uaudio_set_params() and uaudio_chan_init() + */ + b = sc->sc_recchan.bytes_per_frame + * UAUDIO_NFRAMES * UAUDIO_NCHANBUFS; + } - if (blk < bpf) - blk = bpf; + if (b <= 0) + b = 1; + blk = blk <= b ? b : blk / b * b; #ifdef DIAGNOSTIC if (blk <= 0) { @@ -1696,34 +2659,34 @@ uaudio_round_blocksize(void *addr, int blk) } #endif - DPRINTFN(1,("uaudio_round_blocksize: blk=%d\n", blk)); - return (blk); + DPRINTF(("uaudio_round_blocksize: resultant blk=%d\n", blk)); + return blk; } -int +Static int uaudio_get_props(void *addr) { - struct uaudio_softc *sc = addr; + return AUDIO_PROP_FULLDUPLEX | AUDIO_PROP_INDEPENDENT; - return (sc->sc_props); } #endif /* NetBSD or OpenBSD */ - -int +Static int uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue, int wIndex, int len) { usb_device_request_t req; - u_int8_t data[4]; + uint8_t data[4]; usbd_status err; int val; +#if defined(__FreeBSD__) || defined(__DragonFly__) if (sc->sc_dying) - return (EIO); + return EIO; +#endif if (wValue == -1) - return (0); + return 0; req.bmRequestType = type; req.bRequest = which; @@ -1731,12 +2694,12 @@ uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue, USETW(req.wIndex, wIndex); USETW(req.wLength, len); DPRINTFN(2,("uaudio_get: type=0x%02x req=0x%02x wValue=0x%04x " - "wIndex=0x%04x len=%d\n", + "wIndex=0x%04x len=%d\n", type, which, wValue, wIndex, len)); - err = usbd_do_request(sc->sc_udev, &req, &data); + err = usbd_do_request(sc->sc_udev, &req, data); if (err) { DPRINTF(("uaudio_get: err=%s\n", usbd_errstr(err))); - return (-1); + return -1; } switch (len) { case 1: @@ -1747,22 +2710,24 @@ uaudio_get(struct uaudio_softc *sc, int which, int type, int wValue, break; default: DPRINTF(("uaudio_get: bad length=%d\n", len)); - return (-1); + return -1; } DPRINTFN(2,("uaudio_get: val=%d\n", val)); - return (val); + return val; } -void +Static void uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue, int wIndex, int len, int val) { usb_device_request_t req; - u_int8_t data[4]; + uint8_t data[4]; usbd_status err; +#if defined(__FreeBSD__) || defined(__DragonFly__) if (sc->sc_dying) return; +#endif if (wValue == -1) return; @@ -1784,16 +2749,16 @@ uaudio_set(struct uaudio_softc *sc, int which, int type, int wValue, return; } DPRINTFN(2,("uaudio_set: type=0x%02x req=0x%02x wValue=0x%04x " - "wIndex=0x%04x len=%d, val=%d\n", + "wIndex=0x%04x len=%d, val=%d\n", type, which, wValue, wIndex, len, val & 0xffff)); - err = usbd_do_request(sc->sc_udev, &req, &data); + err = usbd_do_request(sc->sc_udev, &req, data); #ifdef USB_DEBUG if (err) DPRINTF(("uaudio_set: err=%d\n", err)); #endif } -int +Static int uaudio_signext(int type, int val) { if (!MIX_UNSIGNED(type)) { @@ -1802,22 +2767,25 @@ uaudio_signext(int type, int val) else val = (int8_t)val; } - return (val); + return val; } #if defined(__NetBSD__) || defined(__OpenBSD__) -int +Static int uaudio_value2bsd(struct mixerctl *mc, int val) { DPRINTFN(5, ("uaudio_value2bsd: type=%03x val=%d min=%d max=%d ", mc->type, val, mc->minval, mc->maxval)); - if (mc->type == MIX_ON_OFF) - val = val != 0; - else - val = ((uaudio_signext(mc->type, val) - mc->minval) * 256 + if (mc->type == MIX_ON_OFF) { + val = (val != 0); + } else if (mc->type == MIX_SELECTOR) { + if (val < mc->minval || val > mc->maxval) + val = mc->minval; + } else + val = ((uaudio_signext(mc->type, val) - mc->minval) * 255 + mc->mul/2) / mc->mul; DPRINTFN(5, ("val'=%d\n", val)); - return (val); + return val; } #endif @@ -1826,17 +2794,20 @@ uaudio_bsd2value(struct mixerctl *mc, int val) { DPRINTFN(5,("uaudio_bsd2value: type=%03x val=%d min=%d max=%d ", mc->type, val, mc->minval, mc->maxval)); - if (mc->type == MIX_ON_OFF) - val = val != 0; - else - val = (val + mc->delta/2) * mc->mul / 256 + mc->minval; + if (mc->type == MIX_ON_OFF) { + val = (val != 0); + } else if (mc->type == MIX_SELECTOR) { + if (val < mc->minval || val > mc->maxval) + val = mc->minval; + } else + val = (val + mc->delta/2) * mc->mul / 255 + mc->minval; DPRINTFN(5, ("val'=%d\n", val)); - return (val); + return val; } #if defined(__NetBSD__) || defined(__OpenBSD__) -int -uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc, +Static int +uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc, int chan) { int val; @@ -1844,11 +2815,11 @@ uaudio_ctl_get(struct uaudio_softc *sc, int which, struct mixerctl *mc, DPRINTFN(5,("uaudio_ctl_get: which=%d chan=%d\n", which, chan)); val = uaudio_get(sc, which, UT_READ_CLASS_INTERFACE, mc->wValue[chan], mc->wIndex, MIX_SIZE(mc->type)); - return (uaudio_value2bsd(mc, val)); + return uaudio_value2bsd(mc, val); } #endif -void +Static void uaudio_ctl_set(struct uaudio_softc *sc, int which, struct mixerctl *mc, int chan, int val) { @@ -1858,33 +2829,37 @@ uaudio_ctl_set(struct uaudio_softc *sc, int which, struct mixerctl *mc, } #if defined(__NetBSD__) || defined(__OpenBSD__) -int +Static int uaudio_mixer_get_port(void *addr, mixer_ctrl_t *cp) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; struct mixerctl *mc; int i, n, vals[MIX_MAX_CHAN], val; DPRINTFN(2,("uaudio_mixer_get_port: index=%d\n", cp->dev)); - + sc = addr; if (sc->sc_dying) - return (EIO); - - n = cp->dev; + return EIO; + + n = cp->dev - UAC_NCLASSES; if (n < 0 || n >= sc->sc_nctls) - return (ENXIO); + return ENXIO; mc = &sc->sc_ctls[n]; if (mc->type == MIX_ON_OFF) { if (cp->type != AUDIO_MIXER_ENUM) - return (EINVAL); + return EINVAL; + cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); + } else if (mc->type == MIX_SELECTOR) { + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; cp->un.ord = uaudio_ctl_get(sc, GET_CUR, mc, 0); } else { if (cp->type != AUDIO_MIXER_VALUE) return (EINVAL); if (cp->un.value.num_channels != 1 && cp->un.value.num_channels != mc->nchan) - return (EINVAL); + return EINVAL; for (i = 0; i < mc->nchan; i++) vals[i] = uaudio_ctl_get(sc, GET_CUR, mc, i); if (cp->un.value.num_channels == 1 && mc->nchan != 1) { @@ -1896,32 +2871,37 @@ uaudio_mixer_get_port(void *addr, mixer_ctrl_t *cp) cp->un.value.level[i] = vals[i]; } - return (0); + return 0; } - -int + +Static int uaudio_mixer_set_port(void *addr, mixer_ctrl_t *cp) { - struct uaudio_softc *sc = addr; + struct uaudio_softc *sc; struct mixerctl *mc; int i, n, vals[MIX_MAX_CHAN]; DPRINTFN(2,("uaudio_mixer_set_port: index = %d\n", cp->dev)); + sc = addr; if (sc->sc_dying) - return (EIO); - - n = cp->dev; + return EIO; + + n = cp->dev - UAC_NCLASSES; if (n < 0 || n >= sc->sc_nctls) - return (ENXIO); + return ENXIO; mc = &sc->sc_ctls[n]; if (mc->type == MIX_ON_OFF) { if (cp->type != AUDIO_MIXER_ENUM) - return (EINVAL); + return EINVAL; + uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); + } else if (mc->type == MIX_SELECTOR) { + if (cp->type != AUDIO_MIXER_ENUM) + return EINVAL; uaudio_ctl_set(sc, SET_CUR, mc, 0, cp->un.ord); } else { if (cp->type != AUDIO_MIXER_VALUE) - return (EINVAL); + return EINVAL; if (cp->un.value.num_channels == 1) for (i = 0; i < mc->nchan; i++) vals[i] = cp->un.value.level[0]; @@ -1929,150 +2909,177 @@ uaudio_mixer_set_port(void *addr, mixer_ctrl_t *cp) for (i = 0; i < mc->nchan; i++) vals[i] = cp->un.value.level[i]; else - return (EINVAL); + return EINVAL; for (i = 0; i < mc->nchan; i++) uaudio_ctl_set(sc, SET_CUR, mc, i, vals[i]); } - return (0); + return 0; } -int +Static int uaudio_trigger_input(void *addr, void *start, void *end, int blksize, void (*intr)(void *), void *arg, struct audio_params *param) { - struct uaudio_softc *sc = addr; - struct chan *ch = &sc->sc_chan; + struct uaudio_softc *sc; + struct chan *ch; usbd_status err; - int i; + int i, s; + sc = addr; if (sc->sc_dying) - return (EIO); + return EIO; DPRINTFN(3,("uaudio_trigger_input: sc=%p start=%p end=%p " "blksize=%d\n", sc, start, end, blksize)); - - uaudio_chan_set_param(ch, param, start, end, blksize); + ch = &sc->sc_recchan; + uaudio_chan_set_param(ch, start, end, blksize); DPRINTFN(3,("uaudio_trigger_input: sample_size=%d bytes/frame=%d " "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame, ch->fraction)); err = uaudio_chan_alloc_buffers(sc, ch); if (err) - return (EIO); + return EIO; err = uaudio_chan_open(sc, ch); if (err) { uaudio_chan_free_buffers(sc, ch); - return (EIO); + return EIO; } - sc->sc_chan.intr = intr; - sc->sc_chan.arg = arg; + ch->intr = intr; + ch->arg = arg; - crit_enter(); + s = splusb(); for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */ uaudio_chan_rtransfer(ch); - crit_exit(); + splx(s); - return (0); + return 0; } - -int + +Static int uaudio_trigger_output(void *addr, void *start, void *end, int blksize, void (*intr)(void *), void *arg, struct audio_params *param) { - struct uaudio_softc *sc = addr; - struct chan *ch = &sc->sc_chan; + struct uaudio_softc *sc; + struct chan *ch; usbd_status err; - int i; + int i, s; + sc = addr; if (sc->sc_dying) - return (EIO); + return EIO; DPRINTFN(3,("uaudio_trigger_output: sc=%p start=%p end=%p " "blksize=%d\n", sc, start, end, blksize)); - - uaudio_chan_set_param(ch, param, start, end, blksize); + ch = &sc->sc_playchan; + uaudio_chan_set_param(ch, start, end, blksize); DPRINTFN(3,("uaudio_trigger_output: sample_size=%d bytes/frame=%d " "fraction=0.%03d\n", ch->sample_size, ch->bytes_per_frame, ch->fraction)); err = uaudio_chan_alloc_buffers(sc, ch); if (err) - return (EIO); + return EIO; err = uaudio_chan_open(sc, ch); if (err) { uaudio_chan_free_buffers(sc, ch); - return (EIO); + return EIO; } - sc->sc_chan.intr = intr; - sc->sc_chan.arg = arg; + ch->intr = intr; + ch->arg = arg; - crit_enter(); + s = splusb(); for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */ uaudio_chan_ptransfer(ch); - crit_exit(); + splx(s); - return (0); + return 0; } #endif /* NetBSD or OpenBSD */ /* Set up a pipe for a channel. */ -usbd_status +Static usbd_status uaudio_chan_open(struct uaudio_softc *sc, struct chan *ch) { - struct as_info *as = &sc->sc_alts[sc->sc_curaltidx]; - int endpt = as->edesc->bEndpointAddress; + struct as_info *as; + int endpt; usbd_status err; +#if defined(__FreeBSD__) || defined(__DragonFly__) if (sc->sc_dying) - return (EIO); + return EIO; +#endif - DPRINTF(("uaudio_open_chan: endpt=0x%02x, speed=%d, alt=%d\n", + as = &sc->sc_alts[ch->altidx]; + endpt = as->edesc->bEndpointAddress; + DPRINTF(("uaudio_chan_open: endpt=0x%02x, speed=%d, alt=%d\n", endpt, ch->sample_rate, as->alt)); /* Set alternate interface corresponding to the mode. */ err = usbd_set_interface(as->ifaceh, as->alt); if (err) - return (err); + return err; - /* Some devices do not support this request, so ignore errors. */ -#ifdef USB_DEBUG - err = uaudio_set_speed(sc, endpt, ch->sample_rate); - if (err) - DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n", - usbd_errstr(err))); -#else - (void)uaudio_set_speed(sc, endpt, ch->sample_rate); -#endif + /* + * If just one sampling rate is supported, + * no need to call uaudio_set_speed(). + * Roland SD-90 freezes by a SAMPLING_FREQ_CONTROL request. + */ + if (as->asf1desc->bSamFreqType != 1) { + err = uaudio_set_speed(sc, endpt, ch->sample_rate); + if (err) + DPRINTF(("uaudio_chan_open: set_speed failed err=%s\n", + usbd_errstr(err))); + } - DPRINTF(("uaudio_open_chan: create pipe to 0x%02x\n", endpt)); + ch->pipe = 0; + ch->sync_pipe = 0; + DPRINTF(("uaudio_chan_open: create pipe to 0x%02x\n", endpt)); err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->pipe); - return (err); + if (err) + return err; + if (as->edesc1 != NULL) { + endpt = as->edesc1->bEndpointAddress; + DPRINTF(("uaudio_chan_open: create sync-pipe to 0x%02x\n", endpt)); + err = usbd_open_pipe(as->ifaceh, endpt, 0, &ch->sync_pipe); + } + return err; } -void +Static void uaudio_chan_close(struct uaudio_softc *sc, struct chan *ch) { - struct as_info *as = &sc->sc_alts[sc->sc_curaltidx]; + struct as_info *as; +#if defined(__FreeBSD__) || defined(__DragonFly__) if (sc->sc_dying) return ; +#endif + as = &sc->sc_alts[ch->altidx]; + as->sc_busy = 0; if (sc->sc_nullalt >= 0) { - DPRINTF(("uaudio_close_chan: set null alt=%d\n", + DPRINTF(("uaudio_chan_close: set null alt=%d\n", sc->sc_nullalt)); usbd_set_interface(as->ifaceh, sc->sc_nullalt); } - usbd_abort_pipe(ch->pipe); - usbd_close_pipe(ch->pipe); + if (ch->pipe) { + usbd_abort_pipe(ch->pipe); + usbd_close_pipe(ch->pipe); + } + if (ch->sync_pipe) { + usbd_abort_pipe(ch->sync_pipe); + usbd_close_pipe(ch->sync_pipe); + } } -usbd_status +Static usbd_status uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch) { usbd_xfer_handle xfer; @@ -2094,16 +3101,16 @@ uaudio_chan_alloc_buffers(struct uaudio_softc *sc, struct chan *ch) ch->chanbufs[i].chan = ch; } - return (USBD_NORMAL_COMPLETION); + return USBD_NORMAL_COMPLETION; bad: while (--i >= 0) - /* implicit buffer free */ + /* implicit buffer kfree */ usbd_free_xfer(ch->chanbufs[i].xfer); - return (USBD_NOMEM); + return USBD_NOMEM; } -void +Static void uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch) { int i; @@ -2112,8 +3119,8 @@ uaudio_chan_free_buffers(struct uaudio_softc *sc, struct chan *ch) usbd_free_xfer(ch->chanbufs[i].xfer); } -/* Called from a critical section. */ -void +/* Called at splusb() */ +Static void uaudio_chan_ptransfer(struct chan *ch) { struct chanbuf *cb; @@ -2134,7 +3141,7 @@ uaudio_chan_ptransfer(struct chan *ch) size = ch->bytes_per_frame; residue += ch->fraction; if (residue >= USB_FRAMES_PER_SECOND) { - if (!ch->nofrac) + if ((ch->sc->sc_altflags & UA_NOFRAC) == 0) size += ch->sample_size; residue -= USB_FRAMES_PER_SECOND; } @@ -2144,7 +3151,7 @@ uaudio_chan_ptransfer(struct chan *ch) ch->residue = residue; cb->size = total; - /* + /* * Transfer data from upper layer buffer to channel buffer, taking * care of wrapping the upper layer buffer. */ @@ -2171,21 +3178,26 @@ uaudio_chan_ptransfer(struct chan *ch) DPRINTFN(5,("uaudio_chan_transfer: ptransfer xfer=%p\n", cb->xfer)); /* Fill the request */ - usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, - UAUDIO_NFRAMES, USBD_NO_COPY, + usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, + UAUDIO_NFRAMES, USBD_NO_COPY, uaudio_chan_pintr); (void)usbd_transfer(cb->xfer); } -void +Static void uaudio_chan_pintr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { - struct chanbuf *cb = priv; - struct chan *ch = cb->chan; + struct chanbuf *cb; + struct chan *ch; u_int32_t count; +#if !defined(__DragonFly__) + int s; +#endif + cb = priv; + ch = cb->chan; /* Return if we are aborting. */ if (status == USBD_CANCELLED) return; @@ -2201,16 +3213,33 @@ uaudio_chan_pintr(usbd_xfer_handle xfer, usbd_private_handle priv, #endif ch->transferred += cb->size; +#if defined(__FreeBSD__) + /* s = spltty(); */ + s = splhigh(); + chn_intr(ch->pcm_ch); + splx(s); +#elif defined(__DragonFly__) crit_enter(); chn_intr(ch->pcm_ch); crit_exit(); +#else + s = splaudio(); + /* Call back to upper layer */ + while (ch->transferred >= ch->blksize) { + ch->transferred -= ch->blksize; + DPRINTFN(5,("uaudio_chan_pintr: call %p(%p)\n", + ch->intr, ch->arg)); + ch->intr(ch->arg); + } + splx(s); +#endif /* start next transfer */ uaudio_chan_ptransfer(ch); } -/* Called from a critical section. */ -void +/* Called at splusb() */ +Static void uaudio_chan_rtransfer(struct chan *ch) { struct chanbuf *cb; @@ -2229,13 +3258,8 @@ uaudio_chan_rtransfer(struct chan *ch) total = 0; for (i = 0; i < UAUDIO_NFRAMES; i++) { size = ch->bytes_per_frame; - residue += ch->fraction; - if (residue >= USB_FRAMES_PER_SECOND) { - if (!ch->nofrac) - size += ch->sample_size; - residue -= USB_FRAMES_PER_SECOND; - } cb->sizes[i] = size; + cb->offsets[i] = total; total += size; } ch->residue = residue; @@ -2253,21 +3277,24 @@ uaudio_chan_rtransfer(struct chan *ch) DPRINTFN(5,("uaudio_chan_rtransfer: transfer xfer=%p\n", cb->xfer)); /* Fill the request */ - usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, - UAUDIO_NFRAMES, USBD_NO_COPY, + usbd_setup_isoc_xfer(cb->xfer, ch->pipe, cb, cb->sizes, + UAUDIO_NFRAMES, USBD_NO_COPY, uaudio_chan_rintr); (void)usbd_transfer(cb->xfer); } -void +Static void uaudio_chan_rintr(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status status) { struct chanbuf *cb = priv; struct chan *ch = cb->chan; u_int32_t count; - int n; +#if !defined(__DragonFly__) + int s; +#endif + int i, n, frsize; /* Return if we are aborting. */ if (status == USBD_CANCELLED) @@ -2277,237 +3304,438 @@ uaudio_chan_rintr(usbd_xfer_handle xfer, usbd_private_handle priv, DPRINTFN(5,("uaudio_chan_rintr: count=%d, transferred=%d\n", count, ch->transferred)); - if (count < cb->size) { - /* if the device fails to keep up, copy last byte */ - u_char b = count ? cb->buffer[count-1] : 0; - while (count < cb->size) - cb->buffer[count++] = b; - } - + /* count < cb->size is normal for asynchronous source */ #ifdef DIAGNOSTIC - if (count != cb->size) { - kprintf("uaudio_chan_rintr: count(%d) != size(%d)\n", + if (count > cb->size) { + kprintf("uaudio_chan_rintr: count(%d) > size(%d)\n", count, cb->size); } #endif - /* + /* * Transfer data from channel buffer to upper layer buffer, taking * care of wrapping the upper layer buffer. */ - n = min(count, ch->end - ch->cur); - memcpy(ch->cur, cb->buffer, n); - ch->cur += n; - if (ch->cur >= ch->end) - ch->cur = ch->start; - if (count > n) { - memcpy(ch->cur, cb->buffer + n, count - n); - ch->cur += count - n; + for(i = 0; i < UAUDIO_NFRAMES; i++) { + frsize = cb->sizes[i]; + n = min(frsize, ch->end - ch->cur); + memcpy(ch->cur, cb->buffer + cb->offsets[i], n); + ch->cur += n; + if (ch->cur >= ch->end) + ch->cur = ch->start; + if (frsize > n) { + memcpy(ch->cur, cb->buffer + cb->offsets[i] + n, + frsize - n); + ch->cur += frsize - n; + } } /* Call back to upper layer */ - ch->transferred += cb->size; + ch->transferred += count; +#if defined(__FreeBSD__) + s = spltty(); + chn_intr(ch->pcm_ch); + splx(s); +#elif defined(__DragonFly__) crit_enter(); chn_intr(ch->pcm_ch); crit_exit(); +#else + s = splaudio(); + while (ch->transferred >= ch->blksize) { + ch->transferred -= ch->blksize; + DPRINTFN(5,("uaudio_chan_rintr: call %p(%p)\n", + ch->intr, ch->arg)); + ch->intr(ch->arg); + } + splx(s); +#endif /* start next transfer */ uaudio_chan_rtransfer(ch); } #if defined(__NetBSD__) || defined(__OpenBSD__) -void -uaudio_chan_set_param(struct chan *ch, struct audio_params *param, - u_char *start, u_char *end, int blksize) +Static void +uaudio_chan_init(struct chan *ch, int altidx, const struct audio_params *param, + int maxpktsize) { int samples_per_frame, sample_size; - sample_size = param->precision * param->channels / 8; - samples_per_frame = param->sample_rate / USB_FRAMES_PER_SECOND; - ch->fraction = param->sample_rate % USB_FRAMES_PER_SECOND; + ch->altidx = altidx; + sample_size = param->precision * param->factor * param->hw_channels / 8; + samples_per_frame = param->hw_sample_rate / USB_FRAMES_PER_SECOND; ch->sample_size = sample_size; - ch->sample_rate = param->sample_rate; - ch->bytes_per_frame = samples_per_frame * sample_size; + ch->sample_rate = param->hw_sample_rate; + if (maxpktsize == 0) { + ch->fraction = param->hw_sample_rate % USB_FRAMES_PER_SECOND; + ch->bytes_per_frame = samples_per_frame * sample_size; + } else { + ch->fraction = 0; + ch->bytes_per_frame = maxpktsize; + } ch->residue = 0; +} +Static void +uaudio_chan_set_param(struct chan *ch, u_char *start, u_char *end, int blksize) +{ ch->start = start; ch->end = end; ch->cur = start; ch->blksize = blksize; ch->transferred = 0; - ch->curchanbuf = 0; } -int -uaudio_set_params(void *addr, int setmode, int usemode, - struct audio_params *play, struct audio_params *rec) +Static void +uaudio_get_minmax_rates(int nalts, const struct as_info *alts, + const struct audio_params *p, int mode, + u_long *min, u_long *max) { - struct uaudio_softc *sc = addr; - int flags = sc->sc_altflags; - int factor; - int enc, i, j; - void (*swcode)(void *, u_char *buf, int cnt); - struct audio_params *p; - int mode; - - if (sc->sc_dying) - return (EIO); - - if (sc->sc_chan.pipe != NULL) - return (EBUSY); - - for (mode = AUMODE_RECORD; mode != -1; - mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { - if ((setmode & mode) == 0) + const struct usb_audio_streaming_type1_descriptor *a1d; + int i, j; + + *min = ULONG_MAX; + *max = 0; + for (i = 0; i < nalts; i++) { + a1d = alts[i].asf1desc; + if (alts[i].sc_busy) continue; - if ((sc->sc_chan.dir & mode) == 0) + if (p->hw_channels != a1d->bNrChannels) continue; + if (p->hw_precision != a1d->bBitResolution) + continue; + if (p->hw_encoding != alts[i].encoding) + continue; + if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress)) + continue; + if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + DPRINTFN(2,("uaudio_get_minmax_rates: cont %d-%d\n", + UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); + if (UA_SAMP_LO(a1d) < *min) + *min = UA_SAMP_LO(a1d); + if (UA_SAMP_HI(a1d) > *max) + *max = UA_SAMP_HI(a1d); + } else { + for (j = 0; j < a1d->bSamFreqType; j++) { + DPRINTFN(2,("uaudio_get_minmax_rates: disc #%d: %d\n", + j, UA_GETSAMP(a1d, j))); + if (UA_GETSAMP(a1d, j) < *min) + *min = UA_GETSAMP(a1d, j); + if (UA_GETSAMP(a1d, j) > *max) + *max = UA_GETSAMP(a1d, j); + } + } + } +} - p = mode == AUMODE_PLAY ? play : rec; - - factor = 1; - swcode = 0; - enc = p->encoding; +Static int +uaudio_match_alt_sub(int nalts, const struct as_info *alts, + const struct audio_params *p, int mode, u_long rate) +{ + const struct usb_audio_streaming_type1_descriptor *a1d; + int i, j; + + DPRINTF(("uaudio_match_alt_sub: search for %luHz %dch\n", + rate, p->hw_channels)); + for (i = 0; i < nalts; i++) { + a1d = alts[i].asf1desc; + if (alts[i].sc_busy) + continue; + if (p->hw_channels != a1d->bNrChannels) + continue; + if (p->hw_precision != a1d->bBitResolution) + continue; + if (p->hw_encoding != alts[i].encoding) + continue; + if (mode != UE_GET_DIR(alts[i].edesc->bEndpointAddress)) + continue; + if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { + DPRINTFN(3,("uaudio_match_alt_sub: cont %d-%d\n", + UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); + if (UA_SAMP_LO(a1d) <= rate && rate <= UA_SAMP_HI(a1d)) + return i; + } else { + for (j = 0; j < a1d->bSamFreqType; j++) { + DPRINTFN(3,("uaudio_match_alt_sub: disc #%d: %d\n", + j, UA_GETSAMP(a1d, j))); + /* XXX allow for some slack */ + if (UA_GETSAMP(a1d, j) == rate) + return i; + } + } + } + return -1; +} + +Static int +uaudio_match_alt_chan(int nalts, const struct as_info *alts, + struct audio_params *p, int mode) +{ + int i, n; + u_long min, max; + u_long rate; + + /* Exact match */ + DPRINTF(("uaudio_match_alt_chan: examine %ldHz %dch %dbit.\n", + p->sample_rate, p->hw_channels, p->hw_precision)); + i = uaudio_match_alt_sub(nalts, alts, p, mode, p->sample_rate); + if (i >= 0) + return i; + + uaudio_get_minmax_rates(nalts, alts, p, mode, &min, &max); + DPRINTF(("uaudio_match_alt_chan: min=%lu max=%lu\n", min, max)); + if (max <= 0) + return -1; + /* Search for biggers */ + n = 2; + while ((rate = p->sample_rate * n++) <= max) { + i = uaudio_match_alt_sub(nalts, alts, p, mode, rate); + if (i >= 0) { + p->hw_sample_rate = rate; + return i; + } + } + if (p->sample_rate >= min) { + i = uaudio_match_alt_sub(nalts, alts, p, mode, max); + if (i >= 0) { + p->hw_sample_rate = max; + return i; + } + } else { + i = uaudio_match_alt_sub(nalts, alts, p, mode, min); + if (i >= 0) { + p->hw_sample_rate = min; + return i; + } + } + return -1; +} + +Static int +uaudio_match_alt(int nalts, const struct as_info *alts, + struct audio_params *p, int mode) +{ + int i, n; + + mode = mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN; + i = uaudio_match_alt_chan(nalts, alts, p, mode); + if (i >= 0) + return i; + + for (n = p->channels + 1; n <= AUDIO_MAX_CHANNELS; n++) { + p->hw_channels = n; + i = uaudio_match_alt_chan(nalts, alts, p, mode); + if (i >= 0) + return i; + } + + if (p->channels != 2) + return -1; + p->hw_channels = 1; + return uaudio_match_alt_chan(nalts, alts, p, mode); +} + +Static int +uaudio_set_params(void *addr, int setmode, int usemode, + struct audio_params *play, struct audio_params *rec) +{ + struct uaudio_softc *sc; + int flags; + int factor; + int enc, i; + int paltidx, raltidx; + void (*swcode)(void *, u_char *buf, int cnt); + struct audio_params *p; + int mode; + + sc = addr; + flags = sc->sc_altflags; + paltidx = -1; + raltidx = -1; + if (sc->sc_dying) + return EIO; + + if (((usemode & AUMODE_PLAY) && sc->sc_playchan.pipe != NULL) || + ((usemode & AUMODE_RECORD) && sc->sc_recchan.pipe != NULL)) + return EBUSY; + + if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) + sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 0; + if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1) + sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 0; + + /* Some uaudio devices are unidirectional. Don't try to find a + matching mode for the unsupported direction. */ + setmode &= sc->sc_mode; + + for (mode = AUMODE_RECORD; mode != -1; + mode = mode == AUMODE_RECORD ? AUMODE_PLAY : -1) { + if ((setmode & mode) == 0) + continue; + + p = (mode == AUMODE_PLAY) ? play : rec; + + factor = 1; + swcode = 0; + enc = p->encoding; switch (enc) { case AUDIO_ENCODING_SLINEAR_BE: - if (p->precision == 16) { + /* FALLTHROUGH */ + case AUDIO_ENCODING_SLINEAR_LE: + if (enc == AUDIO_ENCODING_SLINEAR_BE + && p->precision == 16 && (flags & HAS_16)) { swcode = swap_bytes; enc = AUDIO_ENCODING_SLINEAR_LE; - } else if (p->precision == 8 && !(flags & HAS_8)) { - swcode = change_sign8; - enc = AUDIO_ENCODING_ULINEAR_LE; - } - break; - case AUDIO_ENCODING_SLINEAR_LE: - if (p->precision == 8 && !(flags & HAS_8)) { - swcode = change_sign8; - enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (p->precision == 8) { + if (flags & HAS_8) { + /* No conversion */ + } else if (flags & HAS_8U) { + swcode = change_sign8; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_16) { + factor = 2; + p->hw_precision = 16; + if (mode == AUMODE_PLAY) + swcode = linear8_to_linear16_le; + else + swcode = linear16_to_linear8_le; + } } break; case AUDIO_ENCODING_ULINEAR_BE: + /* FALLTHROUGH */ + case AUDIO_ENCODING_ULINEAR_LE: if (p->precision == 16) { - if (mode == AUMODE_PLAY) + if (enc == AUDIO_ENCODING_ULINEAR_LE) + swcode = change_sign16_le; + else if (mode == AUMODE_PLAY) swcode = swap_bytes_change_sign16_le; else swcode = change_sign16_swap_bytes_le; enc = AUDIO_ENCODING_SLINEAR_LE; - } else if (p->precision == 8 && !(flags & HAS_8U)) { - swcode = change_sign8; - enc = AUDIO_ENCODING_SLINEAR_LE; - } - break; - case AUDIO_ENCODING_ULINEAR_LE: - if (p->precision == 16) { - swcode = change_sign16_le; - enc = AUDIO_ENCODING_SLINEAR_LE; - } else if (p->precision == 8 && !(flags & HAS_8U)) { - swcode = change_sign8; - enc = AUDIO_ENCODING_SLINEAR_LE; - } - break; - case AUDIO_ENCODING_ULAW: - if (!(flags & HAS_MULAW)) { - if (mode == AUMODE_PLAY && - (flags & HAS_16)) { - swcode = mulaw_to_slinear16_le; - factor = 2; - enc = AUDIO_ENCODING_SLINEAR_LE; - } else if (flags & HAS_8U) { - if (mode == AUMODE_PLAY) - swcode = mulaw_to_ulinear8; - else - swcode = ulinear8_to_mulaw; - enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (p->precision == 8) { + if (flags & HAS_8U) { + /* No conversion */ } else if (flags & HAS_8) { - if (mode == AUMODE_PLAY) - swcode = mulaw_to_slinear8; - else - swcode = slinear8_to_mulaw; + swcode = change_sign8; enc = AUDIO_ENCODING_SLINEAR_LE; - } else - return (EINVAL); - } - break; - case AUDIO_ENCODING_ALAW: - if (!(flags & HAS_ALAW)) { - if (mode == AUMODE_PLAY && - (flags & HAS_16)) { - swcode = alaw_to_slinear16_le; + } else if (flags & HAS_16) { factor = 2; + p->hw_precision = 16; enc = AUDIO_ENCODING_SLINEAR_LE; - } else if (flags & HAS_8U) { - if (mode == AUMODE_PLAY) - swcode = alaw_to_ulinear8; - else - swcode = ulinear8_to_alaw; - enc = AUDIO_ENCODING_ULINEAR_LE; - } else if (flags & HAS_8) { if (mode == AUMODE_PLAY) - swcode = alaw_to_slinear8; + swcode = ulinear8_to_slinear16_le; else - swcode = slinear8_to_alaw; - enc = AUDIO_ENCODING_SLINEAR_LE; - } else - return (EINVAL); + swcode = slinear16_to_ulinear8_le; + } } break; + case AUDIO_ENCODING_ULAW: + if (flags & HAS_MULAW) + break; + if (flags & HAS_16) { + if (mode == AUMODE_PLAY) + swcode = mulaw_to_slinear16_le; + else + swcode = slinear16_to_mulaw_le; + factor = 2; + enc = AUDIO_ENCODING_SLINEAR_LE; + p->hw_precision = 16; + } else if (flags & HAS_8U) { + if (mode == AUMODE_PLAY) + swcode = mulaw_to_ulinear8; + else + swcode = ulinear8_to_mulaw; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_8) { + if (mode == AUMODE_PLAY) + swcode = mulaw_to_slinear8; + else + swcode = slinear8_to_mulaw; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else + return (EINVAL); + break; + case AUDIO_ENCODING_ALAW: + if (flags & HAS_ALAW) + break; + if (mode == AUMODE_PLAY && (flags & HAS_16)) { + swcode = alaw_to_slinear16_le; + factor = 2; + enc = AUDIO_ENCODING_SLINEAR_LE; + p->hw_precision = 16; + } else if (flags & HAS_8U) { + if (mode == AUMODE_PLAY) + swcode = alaw_to_ulinear8; + else + swcode = ulinear8_to_alaw; + enc = AUDIO_ENCODING_ULINEAR_LE; + } else if (flags & HAS_8) { + if (mode == AUMODE_PLAY) + swcode = alaw_to_slinear8; + else + swcode = slinear8_to_alaw; + enc = AUDIO_ENCODING_SLINEAR_LE; + } else + return (EINVAL); + break; default: return (EINVAL); } /* XXX do some other conversions... */ DPRINTF(("uaudio_set_params: chan=%d prec=%d enc=%d rate=%ld\n", - p->channels, p->precision, enc, p->sample_rate)); + p->channels, p->hw_precision, enc, p->sample_rate)); - for (i = 0; i < sc->sc_nalts; i++) { - struct usb_audio_streaming_type1_descriptor *a1d = - sc->sc_alts[i].asf1desc; - if (p->channels == a1d->bNrChannels && - p->precision == a1d->bBitResolution && - enc == sc->sc_alts[i].encoding && - (mode == AUMODE_PLAY ? UE_DIR_OUT : UE_DIR_IN) == - UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress)) { - if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { - DPRINTFN(2,("uaudio_set_params: cont %d-%d\n", - UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); - if (UA_SAMP_LO(a1d) < p->sample_rate && - p->sample_rate < UA_SAMP_HI(a1d)) - goto found; - } else { - for (j = 0; j < a1d->bSamFreqType; j++) { - DPRINTFN(2,("uaudio_set_params: disc #" - "%d: %d\n", j, UA_GETSAMP(a1d, j))); - /* XXX allow for some slack */ - if (UA_GETSAMP(a1d, j) == - p->sample_rate) - goto found; - } - } - } - } - return (EINVAL); + p->hw_encoding = enc; + i = uaudio_match_alt(sc->sc_nalts, sc->sc_alts, p, mode); + if (i < 0) + return (EINVAL); - found: p->sw_code = swcode; p->factor = factor; - if (usemode == mode) - sc->sc_curaltidx = i; + + if (mode == AUMODE_PLAY) + paltidx = i; + else + raltidx = i; } - DPRINTF(("uaudio_set_params: use altidx=%d, altno=%d\n", - sc->sc_curaltidx, - sc->sc_alts[sc->sc_curaltidx].idesc->bAlternateSetting)); - - return (0); + if ((setmode & AUMODE_PLAY)) { + /* XXX abort transfer if currently happening? */ + uaudio_chan_init(&sc->sc_playchan, paltidx, play, 0); + } + if ((setmode & AUMODE_RECORD)) { + /* XXX abort transfer if currently happening? */ + uaudio_chan_init(&sc->sc_recchan, raltidx, rec, + UGETW(sc->sc_alts[raltidx].edesc->wMaxPacketSize)); + } + + if ((usemode & AUMODE_PLAY) && sc->sc_playchan.altidx != -1) + sc->sc_alts[sc->sc_playchan.altidx].sc_busy = 1; + if ((usemode & AUMODE_RECORD) && sc->sc_recchan.altidx != -1) + sc->sc_alts[sc->sc_recchan.altidx].sc_busy = 1; + + DPRINTF(("uaudio_set_params: use altidx=p%d/r%d, altno=p%d/r%d\n", + sc->sc_playchan.altidx, sc->sc_recchan.altidx, + (sc->sc_playchan.altidx >= 0) + ?sc->sc_alts[sc->sc_playchan.altidx].idesc->bAlternateSetting + : -1, + (sc->sc_recchan.altidx >= 0) + ? sc->sc_alts[sc->sc_recchan.altidx].idesc->bAlternateSetting + : -1)); + + return 0; } #endif /* NetBSD or OpenBSD */ -usbd_status +Static usbd_status uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed) { usb_device_request_t req; - u_int8_t data[3]; + uint8_t data[3]; DPRINTFN(5,("uaudio_set_speed: endpt=%d speed=%u\n", endpt, speed)); req.bmRequestType = UT_WRITE_CLASS_ENDPOINT; @@ -2519,19 +3747,22 @@ uaudio_set_speed(struct uaudio_softc *sc, int endpt, u_int speed) data[1] = speed >> 8; data[2] = speed >> 16; - return (usbd_do_request(sc->sc_udev, &req, &data)); + return usbd_do_request(sc->sc_udev, &req, data); } -#if defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) /************************************************************/ -void -uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) +int +uaudio_init_params(struct uaudio_softc *sc, struct chan *ch, int mode) { int i, j, enc; int samples_per_frame, sample_size; - switch(ch->format & 0x0000FFFF) { + if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL)) + return (-1); + + switch(ch->format & 0x000FFFFF) { case AFMT_U8: enc = AUDIO_ENCODING_ULINEAR_LE; ch->precision = 8; @@ -2564,6 +3795,38 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) enc = AUDIO_ENCODING_ULINEAR_BE; ch->precision = 16; break; + case AFMT_S24_LE: + enc = AUDIO_ENCODING_SLINEAR_LE; + ch->precision = 24; + break; + case AFMT_S24_BE: + enc = AUDIO_ENCODING_SLINEAR_BE; + ch->precision = 24; + break; + case AFMT_U24_LE: + enc = AUDIO_ENCODING_ULINEAR_LE; + ch->precision = 24; + break; + case AFMT_U24_BE: + enc = AUDIO_ENCODING_ULINEAR_BE; + ch->precision = 24; + break; + case AFMT_S32_LE: + enc = AUDIO_ENCODING_SLINEAR_LE; + ch->precision = 32; + break; + case AFMT_S32_BE: + enc = AUDIO_ENCODING_SLINEAR_BE; + ch->precision = 32; + break; + case AFMT_U32_LE: + enc = AUDIO_ENCODING_ULINEAR_LE; + ch->precision = 32; + break; + case AFMT_U32_BE: + enc = AUDIO_ENCODING_ULINEAR_BE; + ch->precision = 32; + break; default: enc = 0; ch->precision = 16; @@ -2578,11 +3841,11 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) /* for (mode = ...... */ for (i = 0; i < sc->sc_nalts; i++) { - struct usb_audio_streaming_type1_descriptor *a1d = + const struct usb_audio_streaming_type1_descriptor *a1d = sc->sc_alts[i].asf1desc; if (ch->channels == a1d->bNrChannels && ch->precision == a1d->bBitResolution && -#if 1 +#if 0 enc == sc->sc_alts[i].encoding) { #else enc == sc->sc_alts[i].encoding && @@ -2592,9 +3855,12 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) if (a1d->bSamFreqType == UA_SAMP_CONTNUOUS) { DPRINTFN(2,("uaudio_set_params: cont %d-%d\n", UA_SAMP_LO(a1d), UA_SAMP_HI(a1d))); - if (UA_SAMP_LO(a1d) < ch->sample_rate && - ch->sample_rate < UA_SAMP_HI(a1d)) { - sc->sc_curaltidx = i; + if (UA_SAMP_LO(a1d) <= ch->sample_rate && + ch->sample_rate <= UA_SAMP_HI(a1d)) { + if (mode == AUMODE_PLAY) + sc->sc_playchan.altidx = i; + else + sc->sc_recchan.altidx = i; goto found; } } else { @@ -2604,7 +3870,10 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) /* XXX allow for some slack */ if (UA_GETSAMP(a1d, j) == ch->sample_rate) { - sc->sc_curaltidx = i; + if (mode == AUMODE_PLAY) + sc->sc_playchan.altidx = i; + else + sc->sc_recchan.altidx = i; goto found; } } @@ -2612,6 +3881,11 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) } } /* return (EINVAL); */ + if (mode == AUMODE_PLAY) + kprintf("uaudio: This device can't play in rate=%d.\n", ch->sample_rate); + else + kprintf("uaudio: This device can't record in rate=%d.\n", ch->sample_rate); + return (-1); found: #if 0 /* XXX */ @@ -2632,96 +3906,156 @@ uaudio_init_params(struct uaudio_softc *sc, struct chan *ch) ch->cur = ch->start; ch->transferred = 0; ch->curchanbuf = 0; + return (0); } -void -uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt) +struct uaudio_conversion { + uint8_t uaudio_fmt; + uint8_t uaudio_prec; + uint32_t freebsd_fmt; +}; + +const struct uaudio_conversion const accepted_conversion[] = { + {AUDIO_ENCODING_ULINEAR_LE, 8, AFMT_U8}, + {AUDIO_ENCODING_ULINEAR_LE, 16, AFMT_U16_LE}, + {AUDIO_ENCODING_ULINEAR_LE, 24, AFMT_U24_LE}, + {AUDIO_ENCODING_ULINEAR_LE, 32, AFMT_U32_LE}, + {AUDIO_ENCODING_ULINEAR_BE, 16, AFMT_U16_BE}, + {AUDIO_ENCODING_ULINEAR_BE, 24, AFMT_U24_BE}, + {AUDIO_ENCODING_ULINEAR_BE, 32, AFMT_U32_BE}, + {AUDIO_ENCODING_SLINEAR_LE, 8, AFMT_S8}, + {AUDIO_ENCODING_SLINEAR_LE, 16, AFMT_S16_LE}, + {AUDIO_ENCODING_SLINEAR_LE, 24, AFMT_S24_LE}, + {AUDIO_ENCODING_SLINEAR_LE, 32, AFMT_S32_LE}, + {AUDIO_ENCODING_SLINEAR_BE, 16, AFMT_S16_BE}, + {AUDIO_ENCODING_SLINEAR_BE, 24, AFMT_S24_BE}, + {AUDIO_ENCODING_SLINEAR_BE, 32, AFMT_S32_BE}, + {AUDIO_ENCODING_ALAW, 8, AFMT_A_LAW}, + {AUDIO_ENCODING_ULAW, 8, AFMT_MU_LAW}, + {0,0,0} +}; + +unsigned +uaudio_query_formats(device_t dev, int reqdir, unsigned maxfmt, struct pcmchan_caps *cap) { - int i, pn=0, rn=0; - int prec, dir; - u_int32_t fmt; struct uaudio_softc *sc; - - struct usb_audio_streaming_type1_descriptor *a1d; + const struct usb_audio_streaming_type1_descriptor *asf1d; + const struct uaudio_conversion *iterator; + unsigned fmtcount, foundcount; + u_int32_t fmt; + uint8_t format, numchan, subframesize, prec, dir, iscontinuous; + int freq, freq_min, freq_max; + char *numchannel_descr; + char freq_descr[64]; + int i,r; sc = device_get_softc(dev); + if (sc == NULL) + return 0; + + cap->minspeed = cap->maxspeed = 0; + foundcount = fmtcount = 0; for (i = 0; i < sc->sc_nalts; i++) { - fmt = 0; - a1d = sc->sc_alts[i].asf1desc; - prec = a1d->bBitResolution; /* precision */ + dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress); - switch (sc->sc_alts[i].encoding) { - case AUDIO_ENCODING_ULINEAR_LE: - if (prec == 8) { - fmt = AFMT_U8; - } else if (prec == 16) { - fmt = AFMT_U16_LE; - } - break; - case AUDIO_ENCODING_SLINEAR_LE: - if (prec == 8) { - fmt = AFMT_S8; - } else if (prec == 16) { - fmt = AFMT_S16_LE; - } - break; - case AUDIO_ENCODING_ULINEAR_BE: - if (prec == 16) { - fmt = AFMT_U16_BE; - } - break; - case AUDIO_ENCODING_SLINEAR_BE: - if (prec == 16) { - fmt = AFMT_S16_BE; - } - break; - case AUDIO_ENCODING_ALAW: - if (prec == 8) { - fmt = AFMT_A_LAW; - } - break; - case AUDIO_ENCODING_ULAW: - if (prec == 8) { - fmt = AFMT_MU_LAW; - } - break; - } + if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY)) + continue; - if (fmt != 0) { - if (a1d->bNrChannels == 2) { /* stereo/mono */ - fmt |= AFMT_STEREO; - } else if (a1d->bNrChannels != 1) { - fmt = 0; - } + asf1d = sc->sc_alts[i].asf1desc; + format = sc->sc_alts[i].encoding; + + numchan = asf1d->bNrChannels; + subframesize = asf1d->bSubFrameSize; + prec = asf1d->bBitResolution; /* precision */ + iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS; + + if (iscontinuous) + ksnprintf(freq_descr, sizeof(freq_descr), "continous min %d max %d", UA_SAMP_LO(asf1d), UA_SAMP_HI(asf1d)); + else + ksnprintf(freq_descr, sizeof(freq_descr), "fixed frequency (%d listed formats)", asf1d->bSamFreqType); + + if (numchan == 1) + numchannel_descr = " (mono)"; + else if (numchan == 2) + numchannel_descr = " (stereo)"; + else + numchannel_descr = ""; + + if (bootverbose) { + device_printf(dev, "uaudio_query_formats: found a native %s channel%s %s %dbit %dbytes/subframe X %d channels = %d bytes per sample\n", + (dir==UE_DIR_OUT)?"playback":"record", + numchannel_descr, freq_descr, + prec, subframesize, numchan, subframesize*numchan); } + /* + * Now start rejecting the ones that don't map to FreeBSD + */ + + if (numchan != 1 && numchan != 2) + continue; + + for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++) + if (iterator->uaudio_fmt == format && iterator->uaudio_prec == prec) + break; + + if (iterator->uaudio_fmt == 0) + continue; + + fmt = iterator->freebsd_fmt; + + if (numchan == 2) + fmt |= AFMT_STEREO; + + foundcount++; + + if (fmtcount >= maxfmt) + continue; - if (fmt != 0) { - dir= UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress); - if (dir == UE_DIR_OUT) { - pfmt[pn++] = fmt; - } else if (dir == UE_DIR_IN) { - rfmt[rn++] = fmt; + cap->fmtlist[fmtcount++] = fmt; + + if (iscontinuous) { + freq_min = UA_SAMP_LO(asf1d); + freq_max = UA_SAMP_HI(asf1d); + + if (cap->minspeed == 0 || freq_min < cap->minspeed) + cap->minspeed = freq_min; + if (cap->maxspeed == 0) + cap->maxspeed = cap->minspeed; + if (freq_max > cap->maxspeed) + cap->maxspeed = freq_max; + } else { + for (r = 0; r < asf1d->bSamFreqType; r++) { + freq = UA_GETSAMP(asf1d, r); + if (cap->minspeed == 0 || freq < cap->minspeed) + cap->minspeed = freq; + if (cap->maxspeed == 0) + cap->maxspeed = cap->minspeed; + if (freq > cap->maxspeed) + cap->maxspeed = freq; } } - - if ((pn > 8*2) || (rn > 8*2)) - break; } - pfmt[pn] = 0; - rfmt[rn] = 0; - return; + cap->fmtlist[fmtcount] = 0; + return foundcount; } void uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, u_char *end, - struct pcm_channel *pc) + struct pcm_channel *pc, int dir) { struct uaudio_softc *sc; struct chan *ch; sc = device_get_softc(dev); - ch = &sc->sc_chan; +#ifndef NO_RECORDING + if (dir == PCMDIR_PLAY) + ch = &sc->sc_playchan; + else + ch = &sc->sc_recchan; +#else + ch = &sc->sc_playchan; +#endif ch->start = start; ch->end = end; @@ -2732,42 +4066,119 @@ uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, u_char *end, } void -uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize) +uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir) { struct uaudio_softc *sc; struct chan *ch; sc = device_get_softc(dev); - ch = &sc->sc_chan; +#ifndef NO_RECORDING + if (dir == PCMDIR_PLAY) + ch = &sc->sc_playchan; + else + ch = &sc->sc_recchan; +#else + ch = &sc->sc_playchan; +#endif ch->blksize = blocksize; return; } -void -uaudio_chan_set_param_speed(device_t dev, u_int32_t speed) +int +uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir) { + const struct uaudio_conversion *iterator; struct uaudio_softc *sc; struct chan *ch; + int i, r, score, hiscore, bestspeed; sc = device_get_softc(dev); - ch = &sc->sc_chan; +#ifndef NO_RECORDING + if (reqdir == PCMDIR_PLAY) + ch = &sc->sc_playchan; + else + ch = &sc->sc_recchan; +#else + ch = &sc->sc_playchan; +#endif + /* + * We are successful if we find an endpoint that matches our selected format and it + * supports the requested speed. + */ + hiscore = 0; + bestspeed = 1; + for (i = 0; i < sc->sc_nalts; i++) { + int dir = UE_GET_DIR(sc->sc_alts[i].edesc->bEndpointAddress); + int format = sc->sc_alts[i].encoding; + const struct usb_audio_streaming_type1_descriptor *asf1d = sc->sc_alts[i].asf1desc; + int iscontinuous = asf1d->bSamFreqType == UA_SAMP_CONTNUOUS; - ch->sample_rate = speed; + if ((dir == UE_DIR_OUT) != (reqdir == PCMDIR_PLAY)) + continue; - return; + for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++) + if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff)) + continue; + if (iscontinuous) { + if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) { + ch->sample_rate = speed; + return speed; + } else if (speed < UA_SAMP_LO(asf1d)) { + score = 0xfff * speed / UA_SAMP_LO(asf1d); + if (score > hiscore) { + bestspeed = UA_SAMP_LO(asf1d); + hiscore = score; + } + } else if (speed < UA_SAMP_HI(asf1d)) { + score = 0xfff * UA_SAMP_HI(asf1d) / speed; + if (score > hiscore) { + bestspeed = UA_SAMP_HI(asf1d); + hiscore = score; + } + } + continue; + } + for (r = 0; r < asf1d->bSamFreqType; r++) { + if (speed == UA_GETSAMP(asf1d, r)) { + ch->sample_rate = speed; + return speed; + } + if (speed > UA_GETSAMP(asf1d, r)) + score = 0xfff * UA_GETSAMP(asf1d, r) / speed; + else + score = 0xfff * speed / UA_GETSAMP(asf1d, r); + if (score > hiscore) { + bestspeed = UA_GETSAMP(asf1d, r); + hiscore = score; + } + } + } + if (bestspeed != 1) { + ch->sample_rate = bestspeed; + return bestspeed; + } + + return 0; } int -uaudio_chan_getptr(device_t dev) +uaudio_chan_getptr(device_t dev, int dir) { struct uaudio_softc *sc; struct chan *ch; int ptr; sc = device_get_softc(dev); - ch = &sc->sc_chan; +#ifndef NO_RECORDING + if (dir == PCMDIR_PLAY) + ch = &sc->sc_playchan; + else + ch = &sc->sc_recchan; +#else + ch = &sc->sc_playchan; +#endif ptr = ch->cur - ch->start; @@ -2775,13 +4186,20 @@ uaudio_chan_getptr(device_t dev) } void -uaudio_chan_set_param_format(device_t dev, u_int32_t format) +uaudio_chan_set_param_format(device_t dev, u_int32_t format, int dir) { struct uaudio_softc *sc; struct chan *ch; sc = device_get_softc(dev); - ch = &sc->sc_chan; +#ifndef NO_RECORDING + if (dir == PCMDIR_PLAY) + ch = &sc->sc_playchan; + else + ch = &sc->sc_recchan; +#else + ch = &sc->sc_playchan; +#endif ch->format = format; @@ -2796,14 +4214,84 @@ uaudio_halt_out_dma(device_t dev) sc = device_get_softc(dev); DPRINTF(("uaudio_halt_out_dma: enter\n")); - if (sc->sc_chan.pipe != NULL) { - uaudio_chan_close(sc, &sc->sc_chan); - sc->sc_chan.pipe = 0; - uaudio_chan_free_buffers(sc, &sc->sc_chan); + if (sc->sc_playchan.pipe != NULL) { + uaudio_chan_close(sc, &sc->sc_playchan); + sc->sc_playchan.pipe = 0; + uaudio_chan_free_buffers(sc, &sc->sc_playchan); } return (0); } - + +int +uaudio_halt_in_dma(device_t dev) +{ + struct uaudio_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_dying) + return (EIO); + + DPRINTF(("uaudio_halt_in_dma: enter\n")); + if (sc->sc_recchan.pipe != NULL) { + uaudio_chan_close(sc, &sc->sc_recchan); + sc->sc_recchan.pipe = NULL; + uaudio_chan_free_buffers(sc, &sc->sc_recchan); +/* sc->sc_recchan.intr = NULL; */ + } + return (0); +} + +int +uaudio_trigger_input(device_t dev) +{ + struct uaudio_softc *sc; + struct chan *ch; + usbd_status err; + int i; +#if !defined(__DragonFly__) + int s; +#endif + + sc = device_get_softc(dev); + ch = &sc->sc_recchan; + + if (sc->sc_dying) + return (EIO); + +/* uaudio_chan_set_param(ch, start, end, blksize) */ + if (uaudio_init_params(sc, ch, AUMODE_RECORD)) + return (EIO); + + err = uaudio_chan_alloc_buffers(sc, ch); + if (err) + return (EIO); + + err = uaudio_chan_open(sc, ch); + if (err) { + uaudio_chan_free_buffers(sc, ch); + return (EIO); + } + +/* ch->intr = intr; + ch->arg = arg; */ + +#if defined(__DragonFly__) + crit_enter(); +#else + s = splusb(); +#endif + for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX -1 shouldn't be needed */ + uaudio_chan_rtransfer(ch); +#if defined(__DragonFly__) + crit_exit(); +#else + splx(s); +#endif + + return (0); +} + int uaudio_trigger_output(device_t dev) { @@ -2811,14 +4299,18 @@ uaudio_trigger_output(device_t dev) struct chan *ch; usbd_status err; int i; +#if !defined(__DragonFly__) + int s; +#endif sc = device_get_softc(dev); - ch = &sc->sc_chan; + ch = &sc->sc_playchan; if (sc->sc_dying) return (EIO); - uaudio_init_params(sc, ch); + if (uaudio_init_params(sc, ch, AUMODE_PLAY)) + return (EIO); err = uaudio_chan_alloc_buffers(sc, ch); if (err) @@ -2830,10 +4322,18 @@ uaudio_trigger_output(device_t dev) return (EIO); } +#if defined(__DragonFly__) crit_enter(); +#else + s = splusb(); +#endif for (i = 0; i < UAUDIO_NCHANBUFS-1; i++) /* XXX */ uaudio_chan_ptransfer(ch); +#if defined(__DragonFly__) crit_exit(); +#else + splx(s); +#endif return (0); } @@ -2858,6 +4358,39 @@ uaudio_query_mix_info(device_t dev) return mask; } +u_int32_t +uaudio_query_recsrc_info(device_t dev) +{ + int i, rec_selector_id; + u_int32_t mask = 0; + struct uaudio_softc *sc; + struct mixerctl *mc; + + sc = device_get_softc(dev); + rec_selector_id = -1; + for (i=0; i < sc->sc_nctls; i++) { + mc = &sc->sc_ctls[i]; + if (mc->ctl == SOUND_MIXER_NRDEVICES && + mc->type == MIX_SELECTOR && mc->class == UAC_RECORD) { + if (rec_selector_id == -1) { + rec_selector_id = i; + } else { + kprintf("There are many selectors. Can't recognize which selector is a record source selector.\n"); + return mask; + } + } + } + if (rec_selector_id == -1) + return mask; + mc = &sc->sc_ctls[rec_selector_id]; + for (i = mc->minval; i <= mc->maxval; i++) { + if (mc->slctrtype[i - 1] == SOUND_MIXER_NRDEVICES) + continue; + mask |= 1 << mc->slctrtype[i - 1]; + } + return mask; +} + void uaudio_mixer_set(device_t dev, unsigned type, unsigned left, unsigned right) { @@ -2871,15 +4404,161 @@ uaudio_mixer_set(device_t dev, unsigned type, unsigned left, unsigned right) if (mc->ctl == type) { if (mc->nchan == 2) { /* set Right */ - uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*256)/100); + uaudio_ctl_set(sc, SET_CUR, mc, 1, (int)(right*255)/100); } /* set Left or Mono */ - uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*256)/100); + uaudio_ctl_set(sc, SET_CUR, mc, 0, (int)(left*255)/100); } } return; } +u_int32_t +uaudio_mixer_setrecsrc(device_t dev, u_int32_t src) +{ + int i, rec_selector_id; + struct uaudio_softc *sc; + struct mixerctl *mc; + + sc = device_get_softc(dev); + rec_selector_id = -1; + for (i=0; i < sc->sc_nctls; i++) { + mc = &sc->sc_ctls[i]; + if (mc->ctl == SOUND_MIXER_NRDEVICES && + mc->type == MIX_SELECTOR && mc->class == UAC_RECORD) { + if (rec_selector_id == -1) { + rec_selector_id = i; + } else { + return src; /* Can't recognize which selector is record source selector */ + } + } + } + if (rec_selector_id == -1) + return src; + mc = &sc->sc_ctls[rec_selector_id]; + for (i = mc->minval; i <= mc->maxval; i++) { + if (src != (1 << mc->slctrtype[i - 1])) + continue; + uaudio_ctl_set(sc, SET_CUR, mc, 0, i); + return (1 << mc->slctrtype[i - 1]); + } + uaudio_ctl_set(sc, SET_CUR, mc, 0, mc->minval); + return (1 << mc->slctrtype[mc->minval - 1]); +} + +static int +uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) +{ + struct snddev_info *d; + struct snddev_channel *sce; + struct pcm_channel *c; + struct pcm_feeder *f; + int pc, rc, vc; + device_t pa_dev = device_get_parent(dev); + struct uaudio_softc *sc = device_get_softc(pa_dev); + + if (verbose < 1) + return 0; + + d = device_get_softc(dev); + if (!d) + return ENXIO; + + snd_mtxlock(d->lock); + if (SLIST_EMPTY(&d->channels)) { + sbuf_printf(s, " (mixer only)"); + snd_mtxunlock(d->lock); + return 0; + } + pc = rc = vc = 0; + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + if (c->direction == PCMDIR_PLAY) { + if (c->flags & CHN_F_VIRTUAL) + vc++; + else + pc++; + } else + rc++; + } + sbuf_printf(s, " (%dp/%dr/%dv channels%s%s)", + d->playcount, d->reccount, d->vchancount, + (d->flags & SD_F_SIMPLEX)? "" : " duplex", +#ifdef USING_DEVFS + (device_get_unit(dev) == snd_unit)? " default" : "" +#else + "" +#endif + ); + + if (sc->uaudio_sndstat_flag != 0) { + sbuf_cat(s, sbuf_data(&(sc->uaudio_sndstat))); + } + + if (verbose <= 1) { + snd_mtxunlock(d->lock); + return 0; + } + + SLIST_FOREACH(sce, &d->channels, link) { + c = sce->channel; + sbuf_printf(s, "\n\t"); + + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, + ("hosed pcm channel setup")); + + /* it would be better to indent child channels */ + sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); + sbuf_printf(s, "spd %d", c->speed); + if (c->speed != sndbuf_getspd(c->bufhard)) + sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); + sbuf_printf(s, ", fmt 0x%08x", c->format); + if (c->format != sndbuf_getfmt(c->bufhard)) + sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); + sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); + if (c->pid != -1) + sbuf_printf(s, ", pid %d", c->pid); + sbuf_printf(s, "\n\t"); + + sbuf_printf(s, "interrupts %d, ", c->interrupts); + if (c->direction == PCMDIR_REC) + sbuf_printf(s, "overruns %d, hfree %d, sfree %d", + c->xruns, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft)); + else + sbuf_printf(s, "underruns %d, ready %d", + c->xruns, sndbuf_getready(c->bufsoft)); + sbuf_printf(s, "\n\t"); + + sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); + sbuf_printf(s, " -> "); + f = c->feeder; + while (f->source != NULL) + f = f->source; + while (f != NULL) { + sbuf_printf(s, "%s", f->class->name); + if (f->desc->type == FEEDER_FMT) + sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); + if (f->desc->type == FEEDER_RATE) + sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); + if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER) + sbuf_printf(s, "(0x%08x)", f->desc->out); + sbuf_printf(s, " -> "); + f = f->parent; + } + sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); + } + snd_mtxunlock(d->lock); + + return 0; +} + +void +uaudio_sndstat_register(device_t dev) +{ + struct snddev_info *d = device_get_softc(dev); + sndstat_register(dev, d->status, uaudio_sndstat_prepare_pcm); +} + Static int audio_attach_mi(device_t dev) { diff --git a/sys/dev/sound/usb/uaudio.h b/sys/dev/sound/usb/uaudio.h index ce9d5b2975..6cb05c2882 100644 --- a/sys/dev/sound/usb/uaudio.h +++ b/sys/dev/sound/usb/uaudio.h @@ -1,7 +1,7 @@ -/* $FreeBSD: src/sys/dev/sound/usb/uaudio.h,v 1.1.2.1 2002/08/24 08:03:07 nsayer Exp $ */ -/* $DragonFly: src/sys/dev/sound/usb/uaudio.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ */ +/* $FreeBSD: src/sys/dev/sound/usb/uaudio.h,v 1.6.2.1 2005/12/30 19:55:54 netchild Exp $ */ +/* $DragonFly: src/sys/dev/sound/usb/uaudio.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ -/* +/*- * Copyright (c) 2000-2002 Hiroyuki Aizu * * Redistribution and use in source and binary forms, with or without @@ -26,12 +26,14 @@ * SUCH DAMAGE. */ +#if 0 #define NO_RECORDING /* XXX: some routines missing from uaudio.c */ +#endif /* Defined in uaudio.c, used in uaudio_pcm,c */ void uaudio_chan_set_param_pcm_dma_buff(device_t dev, u_char *start, - u_char *end, struct pcm_channel *pc); + u_char *end, struct pcm_channel *pc, int dir); int uaudio_trigger_output(device_t dev); int uaudio_halt_out_dma(device_t dev); #ifndef NO_RECORDING @@ -39,12 +41,14 @@ int uaudio_trigger_input(device_t dev); int uaudio_halt_in_dma(device_t dev); #endif void uaudio_chan_set_param(device_t, u_char *, u_char *); -void uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize); -void uaudio_chan_set_param_speed(device_t dev, u_int32_t speed); -void uaudio_chan_set_param_format(device_t dev, u_int32_t format); -int uaudio_chan_getptr(device_t dev); +void uaudio_chan_set_param_blocksize(device_t dev, u_int32_t blocksize, int dir); +int uaudio_chan_set_param_speed(device_t dev, u_int32_t speed, int reqdir); +void uaudio_chan_set_param_format(device_t dev, u_int32_t format,int dir); +int uaudio_chan_getptr(device_t dev, int); void uaudio_mixer_set(device_t dev, unsigned type, unsigned left, unsigned right); +u_int32_t uaudio_mixer_setrecsrc(device_t dev, u_int32_t src); u_int32_t uaudio_query_mix_info(device_t dev); -void uaudio_query_formats(device_t dev, u_int32_t *pfmt, u_int32_t *rfmt); - +u_int32_t uaudio_query_recsrc_info(device_t dev); +unsigned uaudio_query_formats(device_t dev, int dir, unsigned maxfmt, struct pcmchan_caps *fmt); +void uaudio_sndstat_register(device_t dev); diff --git a/sys/dev/sound/usb/uaudio_pcm.c b/sys/dev/sound/usb/uaudio_pcm.c index e779ba597d..4c2e649732 100644 --- a/sys/dev/sound/usb/uaudio_pcm.c +++ b/sys/dev/sound/usb/uaudio_pcm.c @@ -1,7 +1,7 @@ -/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.1.2.1 2002/08/24 08:03:07 nsayer Exp $ */ -/* $DragonFly: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.5 2006/12/20 18:14:41 dillon Exp $ */ +/* $FreeBSD: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.15.2.1 2005/12/30 19:55:54 netchild Exp $ */ +/* $DragonFly: src/sys/dev/sound/usb/uaudio_pcm.c,v 1.6 2007/01/04 21:47:03 corecode Exp $ */ -/* +/*- * Copyright (c) 2000-2002 Hiroyuki Aizu * * Redistribution and use in source and binary forms, with or without @@ -41,32 +41,29 @@ struct ua_chinfo { struct ua_info *parent; struct pcm_channel *channel; struct snd_dbuf *buffer; + u_char *buf; int dir, hwch; u_int32_t fmt, spd, blksz; /* XXXXX */ }; struct ua_info { device_t sc_dev; + u_int32_t bufsz; struct ua_chinfo pch, rch; - bus_dma_tag_t parent_dmat; +#define FORMAT_NUM 32 + u_int32_t ua_playfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */ + u_int32_t ua_recfmt[FORMAT_NUM*2+1]; /* FORMAT_NUM format * (stereo or mono) + endptr */ + struct pcmchan_caps ua_playcaps; + struct pcmchan_caps ua_reccaps; }; -static u_int32_t ua_playfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */ - -static struct pcmchan_caps ua_playcaps = {8000, 48000, ua_playfmt, 0}; - -static u_int32_t ua_recfmt[8*2+1]; /* 8 format * (stereo or mono) + endptr */ - -static struct pcmchan_caps ua_reccaps = {8000, 48000, ua_recfmt, 0}; - -#define UAUDIO_PCM_BUFF_SIZE 16*1024 +#define UAUDIO_DEFAULT_BUFSZ 16*1024 /************************************************************/ static void * ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { device_t pa_dev; - u_char *buf,*end; struct ua_info *sc = devinfo; struct ua_chinfo *ch = (dir == PCMDIR_PLAY)? &sc->pch : &sc->rch; @@ -74,19 +71,21 @@ ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * ch->parent = sc; ch->channel = c; ch->buffer = b; - - /* allocate PCM side DMA buffer */ - if (sndbuf_alloc(ch->buffer, sc->parent_dmat, UAUDIO_PCM_BUFF_SIZE) != 0) { - return NULL; - } + ch->dir = dir; pa_dev = device_get_parent(sc->sc_dev); - buf = end = sndbuf_getbuf(b); - end += sndbuf_getsize(b); - uaudio_chan_set_param_pcm_dma_buff(pa_dev, buf, end, ch->channel); - /* Create ua_playfmt[] & ua_recfmt[] */ - uaudio_query_formats(pa_dev, (u_int32_t *)&ua_playfmt, (u_int32_t *)&ua_recfmt); + ch->buf = kmalloc(sc->bufsz, M_DEVBUF, M_NOWAIT); + if (ch->buf == NULL) + return NULL; + if (sndbuf_setup(b, ch->buf, sc->bufsz) != 0) { + kfree(ch->buf, M_DEVBUF); + return NULL; + } + uaudio_chan_set_param_pcm_dma_buff(pa_dev, ch->buf, ch->buf+sc->bufsz, ch->channel, dir); + if (bootverbose) + device_printf(pa_dev, "%s buf %p\n", (dir == PCMDIR_PLAY)? + "play" : "rec", sndbuf_getbuf(ch->buffer)); ch->dir = dir; #ifndef NO_RECORDING @@ -100,6 +99,16 @@ ua_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel * return ch; } +static int +ua_chan_free(kobj_t obj, void *data) +{ + struct ua_chinfo *ua = data; + + if (ua->buf != NULL) + kfree(ua->buf, M_DEVBUF); + return 0; +} + static int ua_chan_setformat(kobj_t obj, void *data, u_int32_t format) { @@ -108,9 +117,12 @@ ua_chan_setformat(kobj_t obj, void *data, u_int32_t format) struct ua_chinfo *ch = data; + /* + * At this point, no need to query as we shouldn't select an unsorted format + */ ua = ch->parent; pa_dev = device_get_parent(ua->sc_dev); - uaudio_chan_set_param_format(pa_dev, format); + uaudio_chan_set_param_format(pa_dev, format, ch->dir); ch->fmt = format; return 0; @@ -119,15 +131,15 @@ ua_chan_setformat(kobj_t obj, void *data, u_int32_t format) static int ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { + struct ua_chinfo *ch; device_t pa_dev; - struct ua_info *ua; + int bestspeed; - struct ua_chinfo *ch = data; - ch->spd = speed; + ch = data; + pa_dev = device_get_parent(ch->parent->sc_dev); - ua = ch->parent; - pa_dev = device_get_parent(ua->sc_dev); - uaudio_chan_set_param_speed(pa_dev, speed); + if ((bestspeed = uaudio_chan_set_param_speed(pa_dev, speed, ch->dir))) + ch->spd = bestspeed; return ch->spd; } @@ -136,19 +148,17 @@ static int ua_chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { device_t pa_dev; - struct ua_info *ua; struct ua_chinfo *ch = data; - /* ch->blksz = blocksize; */ + struct ua_info *ua = ch->parent; + if (blocksize) { - ch->blksz = blocksize; - } else { - ch->blksz = UAUDIO_PCM_BUFF_SIZE/2; + RANGE(blocksize, 128, ua->bufsz / 2); + if (sndbuf_resize(ch->buffer, ua->bufsz/blocksize, blocksize) != 0) { + ch->blksz = blocksize; + } } - - /* XXXXX */ - ua = ch->parent; pa_dev = device_get_parent(ua->sc_dev); - uaudio_chan_set_param_blocksize(pa_dev, blocksize); + uaudio_chan_set_param_blocksize(pa_dev, blocksize, ch->dir); return ch->blksz; } @@ -195,19 +205,21 @@ ua_chan_getptr(kobj_t obj, void *data) ua = ch->parent; pa_dev = device_get_parent(ua->sc_dev); - return uaudio_chan_getptr(pa_dev); + return uaudio_chan_getptr(pa_dev, ch->dir); } static struct pcmchan_caps * ua_chan_getcaps(kobj_t obj, void *data) { - struct ua_chinfo *ch = data; + struct ua_chinfo *ch; - return (ch->dir == PCMDIR_PLAY) ? &ua_playcaps : & ua_reccaps; + ch = data; + return (ch->dir == PCMDIR_PLAY) ? &(ch->parent->ua_playcaps) : &(ch->parent->ua_reccaps); } static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_init, ua_chan_init), + KOBJMETHOD(channel_free, ua_chan_free), KOBJMETHOD(channel_setformat, ua_chan_setformat), KOBJMETHOD(channel_setspeed, ua_chan_setspeed), KOBJMETHOD(channel_setblocksize, ua_chan_setblocksize), @@ -225,13 +237,25 @@ ua_mixer_init(struct snd_mixer *m) { u_int32_t mask; device_t pa_dev; + struct snddev_info *d; struct ua_info *ua = mix_getdevinfo(m); pa_dev = device_get_parent(ua->sc_dev); + d = device_get_softc(ua->sc_dev); mask = uaudio_query_mix_info(pa_dev); + if (d && !(mask & SOUND_MIXER_PCM)) { + /* + * Emulate missing pcm mixer controller + * through FEEDER_VOLUME + */ + d->flags |= SD_F_SOFTVOL; + } mix_setdevs(m, mask); + mask = uaudio_query_recsrc_info(pa_dev); + mix_setrecdevs(m, mask); + return 0; } @@ -250,7 +274,11 @@ ua_mixer_set(struct snd_mixer *m, unsigned type, unsigned left, unsigned right) static int ua_mixer_setrecsrc(struct snd_mixer *m, u_int32_t src) { - return src; + device_t pa_dev; + struct ua_info *ua = mix_getdevinfo(m); + + pa_dev = device_get_parent(ua->sc_dev); + return uaudio_mixer_setrecsrc(pa_dev, src); } static kobj_method_t ua_mixer_methods[] = { @@ -279,7 +307,7 @@ ua_probe(device_t dev) s = "USB Audio"; device_set_desc(dev, s); - return 0; + return BUS_PROBE_DEFAULT; } static int @@ -287,50 +315,62 @@ ua_attach(device_t dev) { struct ua_info *ua; char status[SND_STATUSLEN]; - unsigned int bufsz; + device_t pa_dev; + u_int32_t nplay, nrec; + int i; - ua = (struct ua_info *)kmalloc(sizeof *ua, M_DEVBUF, M_NOWAIT); - if (!ua) + ua = (struct ua_info *)kmalloc(sizeof *ua, M_DEVBUF, M_ZERO | M_NOWAIT); + if (ua == NULL) return ENXIO; - bzero(ua, sizeof *ua); ua->sc_dev = dev; - bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_PCM_BUFF_SIZE, 65536); + pa_dev = device_get_parent(dev); - 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*/bufsz, /*nsegments*/1, - /*maxsegz*/0x3fff, /*flags*/0, - &ua->parent_dmat) != 0) { - device_printf(dev, "unable to create dma tag\n"); - goto bad; - } + ua->bufsz = pcm_getbuffersize(dev, 4096, UAUDIO_DEFAULT_BUFSZ, 65536); + if (bootverbose) + device_printf(dev, "using a default buffer size of %jd\n", (intmax_t)ua->bufsz); if (mixer_init(dev, &ua_mixer_class, ua)) { - return(ENXIO); + goto bad; } - ksnprintf(status, SND_STATUSLEN, "at addr ?"); + ksnprintf(status, SND_STATUSLEN, "at ? %s", PCM_KLDSTRING(snd_uaudio)); - if (pcm_register(dev, ua, 1, 0)) { - return(ENXIO); + ua->ua_playcaps.fmtlist = ua->ua_playfmt; + ua->ua_reccaps.fmtlist = ua->ua_recfmt; + nplay = uaudio_query_formats(pa_dev, PCMDIR_PLAY, FORMAT_NUM * 2, &ua->ua_playcaps); + nrec = uaudio_query_formats(pa_dev, PCMDIR_REC, FORMAT_NUM * 2, &ua->ua_reccaps); + + if (nplay > 1) + nplay = 1; + if (nrec > 1) + nrec = 1; + +#ifndef NO_RECORDING + if (pcm_register(dev, ua, nplay, nrec)) { +#else + if (pcm_register(dev, ua, nplay, 0)) { +#endif + goto bad; } - pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua); + sndstat_unregister(dev); + uaudio_sndstat_register(dev); + + for (i = 0; i < nplay; i++) { + pcm_addchan(dev, PCMDIR_PLAY, &ua_chan_class, ua); + } #ifndef NO_RECORDING - pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua); + for (i = 0; i < nrec; i++) { + pcm_addchan(dev, PCMDIR_REC, &ua_chan_class, ua); + } #endif pcm_setstatus(dev, status); return 0; -bad: - if (ua->parent_dmat) - bus_dma_tag_destroy(ua->parent_dmat); - kfree(ua, M_DEVBUF); +bad: kfree(ua, M_DEVBUF); return ENXIO; } @@ -345,7 +385,6 @@ ua_detach(device_t dev) return r; sc = pcm_getdevinfo(dev); - bus_dma_tag_destroy(sc->parent_dmat); kfree(sc, M_DEVBUF); return 0; @@ -368,7 +407,10 @@ static driver_t ua_pcm_driver = { PCM_SOFTC_SIZE, }; +static devclass_t pcm_devclass; + + DRIVER_MODULE(ua_pcm, uaudio, ua_pcm_driver, pcm_devclass, 0, 0); MODULE_DEPEND(ua_pcm, uaudio, 1, 1, 1); -MODULE_DEPEND(ua_pcm, snd_pcm, PCM_MINVER, PCM_PREFVER, PCM_MAXVER); +MODULE_DEPEND(ua_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); MODULE_VERSION(ua_pcm, 1); diff --git a/sys/dev/sound/usb/uaudioreg.h b/sys/dev/sound/usb/uaudioreg.h index 1e9e0bb4bf..99af52db22 100644 --- a/sys/dev/sound/usb/uaudioreg.h +++ b/sys/dev/sound/usb/uaudioreg.h @@ -1,8 +1,8 @@ -/* $NetBSD: uaudioreg.h,v 1.7 2000/12/28 00:29:58 augustss Exp $ */ -/* $FreeBSD: src/sys/dev/sound/usb/uaudioreg.h,v 1.1.2.2 2002/11/06 21:40:31 joe Exp $ */ -/* $DragonFly: src/sys/dev/sound/usb/uaudioreg.h,v 1.2 2003/06/17 04:28:31 dillon Exp $ */ +/* $NetBSD: uaudioreg.h,v 1.12 2004/11/05 19:08:29 kent Exp $ */ +/* $FreeBSD: src/sys/dev/sound/usb/uaudioreg.h,v 1.4 2005/01/06 01:43:22 imp Exp $ */ +/* $DragonFly: src/sys/dev/sound/usb/uaudioreg.h,v 1.3 2007/01/04 21:47:03 corecode Exp $ */ -/* +/*- * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * @@ -41,7 +41,6 @@ #define UAUDIO_VERSION 0x100 -#define UDESC_CS_DEVICE 0x21 #define UDESC_CS_CONFIG 0x22 #define UDESC_CS_STRING 0x23 #define UDESC_CS_INTERFACE 0x24 @@ -64,7 +63,7 @@ typedef struct { uByte bmAttributes; uWord wMaxPacketSize; uByte bInterval; - /* + /* * The following two entries are only used by the Audio Class. * And according to the specs the Audio Class is the only one * allowed to extend the endpoint descriptor. @@ -99,6 +98,9 @@ struct usb_audio_streaming_endpoint_descriptor { uByte bDescriptorType; uByte bDescriptorSubtype; uByte bmAttributes; +#define UA_SED_FREQ_CONTROL 0x01 +#define UA_SED_PITCH_CONTROL 0x02 +#define UA_SED_MAXPACKETSONLY 0x80 uByte bLockDelayUnits; uWord wLockDelay; } UPACKED; @@ -122,9 +124,29 @@ struct usb_audio_streaming_type1_descriptor { struct usb_audio_cluster { uByte bNrChannels; uWord wChannelConfig; +#define UA_CHANNEL_LEFT 0x0001 +#define UA_CHANNEL_RIGHT 0x0002 +#define UA_CHANNEL_CENTER 0x0004 +#define UA_CHANNEL_LFE 0x0008 +#define UA_CHANNEL_L_SURROUND 0x0010 +#define UA_CHANNEL_R_SURROUND 0x0020 +#define UA_CHANNEL_L_CENTER 0x0040 +#define UA_CHANNEL_R_CENTER 0x0080 +#define UA_CHANNEL_SURROUND 0x0100 +#define UA_CHANNEL_L_SIDE 0x0200 +#define UA_CHANNEL_R_SIDE 0x0400 +#define UA_CHANNEL_TOP 0x0800 uByte iChannelNames; } UPACKED; +/* Shared by all units and terminals */ +struct usb_audio_unit { + uByte bLength; + uByte bDescriptorType; + uByte bDescriptorSubtype; + uByte bUnitId; +}; + /* UDESCSUB_AC_INPUT */ struct usb_audio_input_terminal { uByte bLength; @@ -341,8 +363,11 @@ struct usb_audio_extension_unit_1 { #define UA_FMT_IEEE_FLOAT 3 #define UA_FMT_ALAW 4 #define UA_FMT_MULAW 5 +#define UA_FMT_MPEG 0x1001 +#define UA_FMT_AC3 0x1002 -#define SAMPLING_FREQ_CONTROL 0x01 +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 #define FORMAT_TYPE_UNDEFINED 0 #define FORMAT_TYPE_I 1 @@ -378,4 +403,3 @@ struct usb_audio_extension_unit_1 { #define DR_THRESHOLD_CONTROL 4 #define DR_ATTACK_TIME_CONTROL 5 #define DR_RELEASE_TIME_CONTROL 6 - diff --git a/sys/sys/bus.h b/sys/sys/bus.h index 0493500c2d..743d09a03c 100644 --- a/sys/sys/bus.h +++ b/sys/sys/bus.h @@ -24,7 +24,7 @@ * SUCH DAMAGE. * * $FreeBSD: src/sys/sys/bus.h,v 1.30.2.5 2004/03/17 17:54:25 njl Exp $ - * $DragonFly: src/sys/sys/bus.h,v 1.23 2006/10/25 20:56:03 dillon Exp $ + * $DragonFly: src/sys/sys/bus.h,v 1.24 2007/01/04 21:47:03 corecode Exp $ */ #ifndef _SYS_BUS_H_ @@ -368,6 +368,41 @@ int resource_set_string(const char *name, int unit, const char *resname, const char *value); int resource_count(void); +/** + * Some convenience defines for probe routines to return. These are just + * suggested values, and there's nothing magical about them. + * BUS_PROBE_SPECIFIC is for devices that cannot be reprobed, and that no + * possible other driver may exist (typically legacy drivers who don't fallow + * all the rules, or special needs drivers). BUS_PROBE_VENDOR is the + * suggested value that vendor supplied drivers use. This is for source or + * binary drivers that are not yet integrated into the FreeBSD tree. Its use + * in the base OS is prohibited. BUS_PROBE_DEFAULT is the normal return value + * for drivers to use. It is intended that nearly all of the drivers in the + * tree should return this value. BUS_PROBE_LOW_PRIORITY are for drivers that + * have special requirements like when there are two drivers that support + * overlapping series of hardware devices. In this case the one that supports + * the older part of the line would return this value, while the one that + * supports the newer ones would return BUS_PROBE_DEFAULT. BUS_PROBE_GENERIC + * is for drivers that wish to have a generic form and a specialized form, + * like is done with the pci bus and the acpi pci bus. BUS_PROBE_HOOVER is + * for those busses that implement a generic device place-holder for devices on + * the bus that have no more specific driver for them (aka ugen). + */ +#define BUS_PROBE_SPECIFIC 0 /* Only I can use this device */ +#if notyet +#define BUS_PROBE_VENDOR (-10) /* Vendor supplied driver */ +#define BUS_PROBE_DEFAULT (-20) /* Base OS default driver */ +#define BUS_PROBE_LOW_PRIORITY (-40) /* Older, less desirable drivers */ +#define BUS_PROBE_GENERIC (-100) /* generic driver for dev */ +#define BUS_PROBE_HOOVER (-500) /* Generic dev for all devs on bus */ +#else +#define BUS_PROBE_VENDOR 0 +#define BUS_PROBE_DEFAULT 0 +#define BUS_PROBE_LOW_PRIORITY 0 +#define BUS_PROBE_GENERIC 0 +#define BUS_PROBE_HOOVER 0 +#endif + /* * Shorthand for constructing method tables. */ diff --git a/sys/sys/selinfo.h b/sys/sys/selinfo.h index 432b823171..0150f519cd 100644 --- a/sys/sys/selinfo.h +++ b/sys/sys/selinfo.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * @(#)selinfo.h 8.2 (Berkeley) 1/4/94 - * $DragonFly: src/sys/sys/selinfo.h,v 1.1 2006/06/10 20:00:17 dillon Exp $ + * $DragonFly: src/sys/sys/selinfo.h,v 1.2 2007/01/04 21:47:03 corecode Exp $ */ #ifndef _SYS_SELINFO_H_ @@ -56,6 +56,8 @@ struct selinfo { }; #define SI_COLL 0x0001 /* collision occurred */ +#define SEL_WAITING(sel) (sel->si_pid != 0 || (sel->si_flags & SI_COLL) != 0) + struct thread; void selrecord (struct thread *selector, struct selinfo *); diff --git a/sys/sys/soundcard.h b/sys/sys/soundcard.h index aee6f2b9c7..8c07c79015 100644 --- a/sys/sys/soundcard.h +++ b/sys/sys/soundcard.h @@ -1,6 +1,8 @@ /* * soundcard.h - * + */ + +/*- * Copyright by Hannu Savolainen 1993 * Modified for the new FreeBSD sound driver by Luigi Rizzo, 1997 * @@ -27,8 +29,8 @@ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * - * $FreeBSD: src/sys/sys/soundcard.h,v 1.33.2.4 2003/06/07 21:31:56 mbr Exp $ - * $DragonFly: src/sys/sys/soundcard.h,v 1.6 2006/05/20 02:42:13 dillon Exp $ + * $FreeBSD: src/sys/sys/soundcard.h,v 1.44.2.1 2005/12/30 19:55:52 netchild Exp $ + * $DragonFly: src/sys/sys/soundcard.h,v 1.7 2007/01/04 21:47:03 corecode Exp $ */ #ifndef _SYS_SOUNDCARD_H_ @@ -90,20 +92,16 @@ #define SNDCARD_CS4232_MPU 22 #define SNDCARD_MAUI 23 #define SNDCARD_PSEUDO_MSS 24 -#define SNDCARD_AWE32 25 +#define SNDCARD_AWE32 25 #define SNDCARD_NSS 26 #define SNDCARD_UART16550 27 #define SNDCARD_OPL 28 -#ifndef _SYS_TYPES_H_ #include -#endif -#ifndef _MACHINE_ENDIAN_H_ #include -#endif -#if !defined(_IOWR) && !defined(_SYS_IOCCOM_H_) +#ifndef _IOWR #include -#endif +#endif /* !_IOWR */ /* * The first part of this file contains the new FreeBSD sound ioctl @@ -170,10 +168,8 @@ struct snd_size { #if _BYTE_ORDER == _LITTLE_ENDIAN #define AFMT_S16_NE AFMT_S16_LE /* native endian signed 16 */ -#elif _BYTE_ORDER == _BIG_ENDIAN -#define AFMT_S16_NE AFMT_S16_BE #else -#error "Byte order not implemented" +#define AFMT_S16_NE AFMT_S16_BE #endif /* @@ -185,6 +181,10 @@ struct snd_size { #define AFMT_S32_BE 0x00002000 /* Big endian signed 32-bit */ #define AFMT_U32_LE 0x00004000 /* Little endian unsigned 32-bit */ #define AFMT_U32_BE 0x00008000 /* Big endian unsigned 32-bit */ +#define AFMT_S24_LE 0x00010000 /* Little endian signed 24-bit */ +#define AFMT_S24_BE 0x00020000 /* Big endian signed 24-bit */ +#define AFMT_U24_LE 0x00040000 /* Little endian unsigned 24-bit */ +#define AFMT_U24_BE 0x00080000 /* Big endian unsigned 24-bit */ #define AFMT_STEREO 0x10000000 /* can do/want stereo */ @@ -303,6 +303,7 @@ typedef struct _snd_capabilities { #define SNDCTL_PMGR_ACCESS _IOWR('Q',16, struct patmgr_info) #define SNDCTL_SEQ_PANIC _IO ('Q',17) #define SNDCTL_SEQ_OUTOFBAND _IOW ('Q',18, struct seq_event_rec) +#define SNDCTL_SEQ_GETTIME _IOR ('Q',19, int) struct seq_event_rec { u_char arr[8]; @@ -379,7 +380,7 @@ struct patch_info { * * The low_note and high_note fields define the minimum and maximum note * frequencies for which this sample is valid. It is possible to define - * more than one samples for a instrument number at the same time. The + * more than one samples for an instrument number at the same time. The * low_note and high_note fields are used to select the most suitable one. * * The fields base_note, high_note and low_note should contain @@ -461,7 +462,7 @@ struct patmgr_info { /* Note! size must be < 4k since kmalloc() is used */ u_long key; /* Don't worry. Reserved for communication between the patch manager and the driver. */ #define PM_K_EVENT 1 /* Event from the /dev/sequencer driver */ -#define PM_K_COMMAND 2 /* Request from a application */ +#define PM_K_COMMAND 2 /* Request from an application */ #define PM_K_RESPONSE 3 /* From patmgr to application */ #define PM_ERROR 4 /* Error returned by the patmgr */ int device; @@ -697,6 +698,7 @@ struct synth_info { /* Read only */ int synth_subtype; #define FM_TYPE_ADLIB 0x00 #define FM_TYPE_OPL3 0x01 +#define MIDI_TYPE_MPU401 0x401 #define SAMPLE_TYPE_BASIC 0x10 #define SAMPLE_TYPE_GUS SAMPLE_TYPE_BASIC @@ -1028,6 +1030,14 @@ typedef struct copr_msg { #define SOUND_MIXER_READ_LINE1 MIXER_READ(SOUND_MIXER_LINE1) #define SOUND_MIXER_READ_LINE2 MIXER_READ(SOUND_MIXER_LINE2) #define SOUND_MIXER_READ_LINE3 MIXER_READ(SOUND_MIXER_LINE3) +#define SOUND_MIXER_READ_DIGITAL1 MIXER_READ(SOUND_MIXER_DIGITAL1) +#define SOUND_MIXER_READ_DIGITAL2 MIXER_READ(SOUND_MIXER_DIGITAL2) +#define SOUND_MIXER_READ_DIGITAL3 MIXER_READ(SOUND_MIXER_DIGITAL3) +#define SOUND_MIXER_READ_PHONEIN MIXER_READ(SOUND_MIXER_PHONEIN) +#define SOUND_MIXER_READ_PHONEOUT MIXER_READ(SOUND_MIXER_PHONEOUT) +#define SOUND_MIXER_READ_RADIO MIXER_READ(SOUND_MIXER_RADIO) +#define SOUND_MIXER_READ_VIDEO MIXER_READ(SOUND_MIXER_VIDEO) +#define SOUND_MIXER_READ_MONITOR MIXER_READ(SOUND_MIXER_MONITOR) /* Obsolete macros */ #define SOUND_MIXER_READ_MUTE MIXER_READ(SOUND_MIXER_MUTE) @@ -1058,12 +1068,30 @@ typedef struct copr_msg { #define SOUND_MIXER_WRITE_LINE1 MIXER_WRITE(SOUND_MIXER_LINE1) #define SOUND_MIXER_WRITE_LINE2 MIXER_WRITE(SOUND_MIXER_LINE2) #define SOUND_MIXER_WRITE_LINE3 MIXER_WRITE(SOUND_MIXER_LINE3) +#define SOUND_MIXER_WRITE_DIGITAL1 MIXER_WRITE(SOUND_MIXER_DIGITAL1) +#define SOUND_MIXER_WRITE_DIGITAL2 MIXER_WRITE(SOUND_MIXER_DIGITAL2) +#define SOUND_MIXER_WRITE_DIGITAL3 MIXER_WRITE(SOUND_MIXER_DIGITAL3) +#define SOUND_MIXER_WRITE_PHONEIN MIXER_WRITE(SOUND_MIXER_PHONEIN) +#define SOUND_MIXER_WRITE_PHONEOUT MIXER_WRITE(SOUND_MIXER_PHONEOUT) +#define SOUND_MIXER_WRITE_RADIO MIXER_WRITE(SOUND_MIXER_RADIO) +#define SOUND_MIXER_WRITE_VIDEO MIXER_WRITE(SOUND_MIXER_VIDEO) +#define SOUND_MIXER_WRITE_MONITOR MIXER_WRITE(SOUND_MIXER_MONITOR) + #define SOUND_MIXER_WRITE_MUTE MIXER_WRITE(SOUND_MIXER_MUTE) #define SOUND_MIXER_WRITE_ENHANCE MIXER_WRITE(SOUND_MIXER_ENHANCE) #define SOUND_MIXER_WRITE_LOUD MIXER_WRITE(SOUND_MIXER_LOUD) #define SOUND_MIXER_WRITE_RECSRC MIXER_WRITE(SOUND_MIXER_RECSRC) +typedef struct mixer_info { + char id[16]; + char name[32]; + int modify_counter; + int fillers[10]; +} mixer_info; + +#define SOUND_MIXER_INFO _IOR('M', 101, mixer_info) + #define LEFT_CHN 0 #define RIGHT_CHN 1 @@ -1142,7 +1170,7 @@ typedef struct copr_msg { */ #ifndef USE_SIMPLE_MACROS -void seqbuf_dump (void); /* This function must be provided by programs */ +void seqbuf_dump(void); /* This function must be provided by programs */ /* Sample seqbuf_dump() implementation: * @@ -1358,7 +1386,7 @@ void seqbuf_dump (void); /* This function must be provided by programs */ #define SEQ_PLAYAUDIO(devmask) _LOCAL_EVENT(LOCL_STARTAUDIO, devmask) /* - * Events for the level 1 interface only + * Events for the level 1 interface only */ #define SEQ_MIDIOUT(device, byte) { \ diff --git a/sys/tools/emu10k1-mkalsa.sh b/sys/tools/emu10k1-mkalsa.sh new file mode 100644 index 0000000000..1d9b034a2a --- /dev/null +++ b/sys/tools/emu10k1-mkalsa.sh @@ -0,0 +1,22 @@ +# $FreeBSD: src/sys/tools/emu10k1-mkalsa.sh,v 1.1 2004/02/05 22:51:16 peter Exp $ +# $DragonFly: src/sys/tools/emu10k1-mkalsa.sh,v 1.1 2007/01/04 21:47:03 corecode Exp $ + +GREP=${GREP:-grep} +CC=${CC:-cc} +AWK=${AWK:-awk} +MV=${MV:=mv} +RM=${RM:=rm} +IN=$1 +OUT=$2 + +trap "${RM} -f $OUT.tmp" EXIT + +$GREP -v '#include' $IN | \ +$CC -E -D__KERNEL__ -dM - | \ +$AWK -F"[ (]" ' +/define/ { + print "#ifndef " $2; + print; + print "#endif"; +}' > $OUT.tmp +${MV} -f $OUT.tmp $OUT -- 2.41.0