--- sys/conf/files.orig Mon Nov 21 21:23:22 2005 +++ sys/conf/files Sat Dec 31 01:39:10 2005 @@ -705,6 +705,7 @@ dev/sound/isa/sbc.c optional snd_sbc isa dev/sound/isa/sndbuf_dma.c optional sound 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 @@ -727,6 +728,7 @@ 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/pci/hda/hdac.c optional snd_hda pci dev/sound/pcm/ac97.c optional sound dev/sound/pcm/ac97_patch.c optional sound dev/sound/pcm/ac97_if.m optional sound @@ -739,6 +741,7 @@ dev/sound/pcm/feeder_if.m optional sound dev/sound/pcm/feeder_fmt.c optional sound dev/sound/pcm/feeder_rate.c optional sound +dev/sound/pcm/feeder_volume.c optional sound dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound --- sys/sys/soundcard.h.orig Fri Sep 16 06:21:09 2005 +++ sys/sys/soundcard.h Fri Sep 16 06:32:00 2005 @@ -180,6 +180,10 @@ #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 */ --- sys/dev/sound/driver.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/driver.c Thu Jul 6 04:47:48 2006 @@ -54,6 +54,7 @@ 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); @@ -66,13 +67,14 @@ 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_sb8, 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); --- sys/dev/sound/isa/ad1816.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/isa/ad1816.c Thu Jul 6 04:47:48 2006 @@ -138,12 +138,16 @@ } /* 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) { @@ -575,6 +579,9 @@ switch (logical_id) { case 0x80719304: /* ADS7180 */ s = "AD1816"; + break; + case 0x50719304: /* ADS7150 */ + s = "AD1815"; break; } --- sys/dev/sound/isa/ess.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/isa/ess.c Thu Jul 6 04:47:48 2006 @@ -61,7 +61,7 @@ 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, @@ -75,7 +75,7 @@ 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; @@ -361,8 +361,11 @@ 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_dma(sc->pch.buffer, PCMTRIG_STOP); @@ -375,8 +378,11 @@ } 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_dma(sc->rch.buffer, PCMTRIG_STOP); --- sys/dev/sound/isa/mss.c.orig Mon Feb 28 07:32:21 2005 +++ sys/dev/sound/isa/mss.c Thu Jul 6 04:47:48 2006 @@ -795,11 +795,15 @@ 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 */ @@ -1111,8 +1115,16 @@ 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); --- sys/dev/sound/isa/sb16.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/isa/sb16.c Thu Jul 6 04:47:48 2006 @@ -370,23 +370,32 @@ 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) @@ -494,7 +503,7 @@ 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 @@ -570,8 +579,9 @@ sb_reset_dsp(sb); if (sb->bd_flags & BD_F_SB16X) { + /* full-duplex doesn't work! */ pprio = sb->pch.run? 1 : 0; - sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : NULL); + sndbuf_dmasetup(sb->pch.buffer, pprio? sb->drq1 : sb->drq2); sb->pch.dch = pprio? 1 : 0; sndbuf_dmasetup(sb->rch.buffer, pprio? sb->drq2 : sb->drq1); sb->rch.dch = pprio? 2 : 1; @@ -848,7 +858,7 @@ else status2[0] = '\0'; - snprintf(status, SND_STATUSLEN, "at io 0x%lx irq %ld drq %ld%s bufsz %ud %s", + snprintf(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, PCM_KLDSTRING(snd_sb16)); --- sys/dev/sound/isa/sb8.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/isa/sb8.c Thu Jul 6 04:47:48 2006 @@ -475,11 +475,17 @@ 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); @@ -564,8 +570,16 @@ 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 */ --- sys/dev/sound/isa/sbc.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/isa/sbc.c Thu Jul 6 04:47:48 2006 @@ -259,6 +259,7 @@ {0x81167316, "ESS ES1681"}, /* ESS1681 */ {0x02017316, "ESS ES1688"}, /* ESS1688 */ + {0x68097316, "ESS ES1688"}, /* ESS1688 */ {0x68187316, "ESS ES1868"}, /* ESS1868 */ {0x03007316, "ESS ES1869"}, /* ESS1869 */ {0x69187316, "ESS ES1869"}, /* ESS1869 */ --- sys/dev/sound/pci/atiixp.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/atiixp.c Sun Oct 1 22:40:35 2006 @@ -0,0 +1,1202 @@ +/*- + * 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 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("$FreeBSD: src/sys/dev/sound/pci/atiixp.c,v 1.7 2006/10/01 13:30:30 ariff Exp $"); + +#define ATI_IXP_DMA_RETRY_MAX 100 +#define ATI_IXP_DMA_CHSEGS_DEFAULT 2 + +#define ATI_IXP_BUFSZ_MIN 4096 +#define ATI_IXP_BUFSZ_MAX 65536 +#define ATI_IXP_BUFSZ_DEFAULT 16384 + +#ifdef ATI_IXP_DEBUG_VERBOSE +#undef ATI_IXP_DEBUG +#define ATI_IXP_DEBUG 1 +#endif + +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, dma_blksz; +#ifdef ATI_IXP_DEBUG + uint32_t dma_ptr, dma_prevptr; +#endif + 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 mtx *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_chinfo *); +static void atiixp_enable_dma(struct atiixp_chinfo *); +static void atiixp_disable_dma(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 __inline uint32_t atiixp_dmapos(struct atiixp_chinfo *); +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 1 + value &= ~(ATI_REG_IER_IN_XRUN_EN | ATI_REG_IER_OUT_XRUN_EN | + ATI_REG_IER_SPDF_XRUN_EN | ATI_REG_IER_SPDF_STATUS_EN); +#else + 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_chinfo *ch) +{ + atiixp_wr(ch->parent, ATI_REG_FIFO_FLUSH, ch->flush_bit); +} + +static void +atiixp_enable_dma(struct atiixp_chinfo *ch) +{ + uint32_t value; + + value = atiixp_rd(ch->parent, ATI_REG_CMD); + if (!(value & ch->enable_bit)) { + value |= ch->enable_bit; + atiixp_wr(ch->parent, ATI_REG_CMD, value); + } +} + +static void +atiixp_disable_dma(struct atiixp_chinfo *ch) +{ + uint32_t value; + + value = atiixp_rd(ch->parent, ATI_REG_CMD); + if (value & ch->enable_bit) { + value &= ~ch->enable_bit; + atiixp_wr(ch->parent, 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; + ch->dma_blksz = sc->bufsz / 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(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; + + /* XXX Force static blocksize */ + sndbuf_resize(ch->buffer, ch->dma_segs, ch->dma_blksz); + + if (ch->dma_blksz != sndbuf_getblksz(ch->buffer)) { + device_printf(sc->dev, + "%s: WARNING - dma_blksz=%u != blksz=%u !!!\n", + __func__, ch->dma_blksz, sndbuf_getblksz(ch->buffer)); + ch->dma_blksz = sndbuf_getblksz(ch->buffer); + } + + return (ch->dma_blksz); +} + +static void +atiixp_buildsgdt(struct atiixp_chinfo *ch) +{ + uint32_t addr; + int i; + + addr = sndbuf_getbufaddr(ch->buffer); + + for (i = 0; i < ch->dma_segs; i++) { + ch->sgd_table[i].addr = htole32(addr + (i * ch->dma_blksz)); + ch->sgd_table[i].status = htole16(0); + ch->sgd_table[i].size = htole16(ch->dma_blksz >> 2); + ch->sgd_table[i].next = htole32((uint32_t)ch->sgd_addr + + (((i + 1) % ch->dma_segs) * + sizeof(struct atiixp_dma_op))); + } + +#ifdef ATI_IXP_DEBUG + ch->dma_ptr = 0; + ch->dma_prevptr = 0; +#endif +} + +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(ch); + atiixp_buildsgdt(ch); + atiixp_wr(sc, ch->linkptr_bit, 0); + atiixp_enable_dma(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(ch); + atiixp_flush_dma(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 __inline uint32_t +atiixp_dmapos(struct atiixp_chinfo *ch) +{ + struct atiixp_info *sc = ch->parent; + uint32_t reg, addr, sz, retry; + volatile uint32_t ptr; + + reg = ch->dma_dt_cur_bit; + addr = sndbuf_getbufaddr(ch->buffer); + sz = ch->dma_segs * ch->dma_blksz; + retry = ATI_IXP_DMA_RETRY_MAX; + + do { + ptr = atiixp_rd(sc, reg); + if (ptr < addr) + continue; + ptr -= addr; + if (ptr < sz) { +#ifdef ATI_IXP_DEBUG + if ((ptr & ~(ch->dma_blksz - 1)) != ch->dma_ptr) { + uint32_t delta; + + delta = (sz + ptr - ch->dma_prevptr) % sz; +#ifndef ATI_IXP_DEBUG_VERBOSE + if (delta < ch->dma_blksz) +#endif + device_printf(sc->dev, + "PCMDIR_%s: incoherent DMA " + "dma_prevptr=%u ptr=%u " + "dma_ptr=%u dma_segs=%u " + "[delta=%u != dma_blksz=%u] " + "(%s)\n", + (ch->dir == PCMDIR_PLAY) ? + "PLAY" : "REC", + ch->dma_prevptr, ptr, + ch->dma_ptr, ch->dma_segs, + delta, ch->dma_blksz, + (delta < ch->dma_blksz) ? + "OVERLAPPED!" : "Ok"); + ch->dma_ptr = ptr & ~(ch->dma_blksz - 1); + } + ch->dma_prevptr = ptr; +#endif + return (ptr); + } + } while (--retry); + + device_printf(sc->dev, "PCMDIR_%s: invalid DMA pointer ptr=%u\n", + (ch->dir == PCMDIR_PLAY) ? "PLAY" : "REC", ptr); + + 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 ptr; + + atiixp_lock(sc); + ptr = atiixp_dmapos(ch); + atiixp_unlock(sc); + + return (ptr); +} + +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; + struct atiixp_chinfo *ch; + 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) { + ch = &sc->rch; +#ifdef ATI_IXP_DEBUG + ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) % + (ch->dma_segs * ch->dma_blksz); +#endif + atiixp_unlock(sc); + chn_intr(ch->channel); + atiixp_lock(sc); + } + if ((status & ATI_REG_ISR_OUT_STATUS) && sc->pch.channel) { + ch = &sc->pch; +#ifdef ATI_IXP_DEBUG + ch->dma_ptr = (ch->dma_ptr + ch->dma_blksz) % + (ch->dma_segs * ch->dma_blksz); +#endif + atiixp_unlock(sc); + chn_intr(ch->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; + do { + msleep(sc, sc->lock, PWAIT, "ixpslp", 1); + if (sc->codec_not_ready_bits) + break; + } while (--timeout); + + 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); + + snprintf(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 = malloc(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_DEFAULT; + + 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, ATI_IXP_BUFSZ_MIN, + ATI_IXP_BUFSZ_DEFAULT, ATI_IXP_BUFSZ_MAX); + + 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*/2, /*boundary*/0, + /*lowaddr*/BUS_SPACE_MAXADDR_32BIT, + /*highaddr*/BUS_SPACE_MAXADDR, + /*filter*/NULL, /*filterarg*/NULL, + /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &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, /*lockfunc*/NULL, + /*lockarg*/NULL, &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; + if (sc->st != 0 && sc->sh != 0) + atiixp_disable_interrupts(sc); + atiixp_release_resource(sc); + free(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); --- sys/dev/sound/pci/atiixp.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/atiixp.h Sun Oct 1 22:40:35 2006 @@ -0,0 +1,202 @@ +/*- + * 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.3 2006/10/01 13:30:30 ariff 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_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 --- sys/dev/sound/pci/als4000.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/pci/als4000.c Thu Jul 6 04:47:48 2006 @@ -75,6 +75,7 @@ struct resource *reg, *irq; int regid, irqid; void *ih; + struct mtx *lock; unsigned int bufsz; struct sc_chinfo pch, rch; @@ -90,7 +91,11 @@ 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 */ @@ -199,6 +204,7 @@ 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; @@ -213,9 +219,11 @@ 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; } @@ -263,9 +271,12 @@ 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; } @@ -378,7 +389,9 @@ 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); @@ -387,6 +400,7 @@ als_playback_stop(ch); break; } + snd_mtxunlock(sc->lock); return 0; } @@ -468,7 +482,9 @@ 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); @@ -477,6 +493,7 @@ als_capture_stop(ch); break; } + snd_mtxunlock(sc->lock); return 0; } @@ -578,8 +595,13 @@ 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; + } } } @@ -605,13 +627,20 @@ 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); @@ -627,6 +656,8 @@ 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; } @@ -708,6 +739,10 @@ bus_dma_tag_destroy(sc->parent_dmat); sc->parent_dmat = 0; } + if (sc->lock) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } } static int @@ -730,7 +765,7 @@ goto bad; } - if (bus_setup_intr(dev, sc->irq, INTR_TYPE_AV, als_intr, + if (snd_setup_intr(dev, sc->irq, INTR_MPSAFE, als_intr, sc, &sc->ih)) { device_printf(dev, "unable to setup interrupt\n"); goto bad; @@ -745,8 +780,8 @@ /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &sc->parent_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -768,6 +803,7 @@ return ENXIO; } + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "sound softc"); sc->dev = dev; data = pci_read_config(dev, PCIR_COMMAND, 2); @@ -851,9 +887,11 @@ { 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; } @@ -862,13 +900,17 @@ { 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; } @@ -879,6 +921,8 @@ if (sc->rch.dma_was_active) { als_capture_start(&sc->rch); } + snd_mtxunlock(sc->lock); + return 0; } --- sys/dev/sound/pci/cmi.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/pci/cmi.c Thu Jul 6 04:47:48 2006 @@ -876,8 +876,8 @@ /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, /*flags*/0, - /*lockfunc*/busdma_lock_mutex, - /*lockfunc*/&Giant, + /*lockfunc*/NULL, + /*lockfunc*/NULL, &sc->parent_dmat) != 0) { device_printf(dev, "cmi_attach: Unable to create dma tag\n"); goto bad; --- sys/dev/sound/pci/ds1.c.orig Sun Jan 30 09:00:03 2005 +++ sys/dev/sound/pci/ds1.c Thu Jul 6 04:47:48 2006 @@ -743,13 +743,17 @@ 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); @@ -830,8 +834,8 @@ 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, busdma_lock_mutex, - &Giant, &sc->control_dmat)) + NULL, NULL, memsz, 1, memsz, 0, NULL, + NULL, &sc->control_dmat)) return -1; if (bus_dmamem_alloc(sc->control_dmat, &buf, BUS_DMA_NOWAIT, &sc->map)) return -1; @@ -971,8 +975,8 @@ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/sc->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &sc->buffer_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &sc->buffer_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -986,12 +990,23 @@ 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_any(dev, SYS_RES_IRQ, &sc->irqid, RF_ACTIVE | RF_SHAREABLE); - if (!sc->irq || snd_setup_intr(dev, sc->irq, 0, ds_intr, sc, &sc->ih)) { + 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; } --- sys/dev/sound/pci/es137x.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pci/es137x.c Thu Jul 6 04:47:48 2006 @@ -61,9 +61,6 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/es137x.c,v 1.51.2.2 2005/01/30 01:00:04 imp Exp $"); -static int debug = 0; -SYSCTL_INT(_debug, OID_AUTO, es_debug, CTLFLAG_RW, &debug, 0, ""); - #define MEM_MAP_REG 0x14 /* PCI IDs of supported chips */ @@ -90,6 +87,15 @@ #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 +103,73 @@ 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 0 + struct es_info { bus_space_tag_t st; bus_space_handle_t sh; @@ -115,41 +184,35 @@ 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 mtx *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 +222,7 @@ 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 +234,67 @@ [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 +302,8 @@ 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 +313,53 @@ } 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 +376,20 @@ static int es1370_wrcodec(struct es_info *es, u_char i, u_char data) { - int wait = 100; /* 100 msec timeout */ + u_int t; + + ES_LOCK_ASSERT(es); - do { - if ((bus_space_read_4(es->st, es->sh, ES1370_REG_STATUS) & + 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); - printf("pcm: es1370_wrcodec timed out\n"); + DELAY(1); + } + device_printf(es->dev, "%s: timed out\n", __func__); return -1; } @@ -266,36 +400,82 @@ 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++; + ch->dir = dir; + ES_UNLOCK(es); if (sndbuf_alloc(ch->buffer, es->parent_dmat, ch->bufsz) != 0) 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; - + 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, sndbuf_getbufaddr(ch->buffer)); - bus_space_write_4(es->st, es->sh, ES1370_REG_DAC2_FRAMECNT & 0xff, (ch->bufsz >> 2) - 1); - } else { - bus_space_write_1(es->st, es->sh, ES1370_REG_MEMPAGE, ES1370_REG_ADC_FRAMEADR >> 8); - bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_FRAMEADR & 0xff, sndbuf_getbufaddr(ch->buffer)); - bus_space_write_4(es->st, es->sh, ES1370_REG_ADC_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 { + 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 @@ -304,16 +484,24 @@ 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; } @@ -324,11 +512,41 @@ 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 @@ -336,23 +554,41 @@ { 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; } @@ -361,37 +597,52 @@ { 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; } @@ -402,13 +653,17 @@ 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; } @@ -417,12 +672,12 @@ 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), @@ -435,7 +690,6 @@ 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), @@ -452,34 +706,78 @@ 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); - - if (intsrc & STAT_ADC) chn_intr(es->rch.channel); - if (intsrc & STAT_DAC1); - if (intsrc & STAT_DAC2) chn_intr(es->pch.channel); + 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->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 @@ -487,45 +785,65 @@ * 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) printf("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) || - (devid == CT4730_PCI_ID)) { - bus_space_write_4(es->st, es->sh, ES1370_REG_STATUS, 0x20000000); + (devid == CT5880_PCI_ID && revid == CT5880REV_CT5880_E)) { + 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); @@ -538,16 +856,21 @@ 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); } @@ -557,43 +880,34 @@ static int es1371_wrcd(kobj_t obj, void *s, int addr, u_int32_t data) { - int sl; - unsigned t, x; + uint32_t t, x, orig; struct es_info *es = (struct es_info*)s; - if (debug > 0) printf("wrcodec addr 0x%x data 0x%x\n", addr, data); - for (t = 0; t < 0x1000; t++) - if (!(bus_space_read_4(es->st, es->sh,(ES1371_REG_CODEC & CODEC_WIP)))) + if (!es_rd(es, ES1371_REG_CODEC & CODEC_WIP, 4)) break; - sl = spltty(); /* 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) - printf("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) - printf("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); - splx(sl); + es_wr(es, ES1371_REG_SMPRATE, orig, 4); return 0; } @@ -601,44 +915,42 @@ static int es1371_rdcd(kobj_t obj, void *s, int addr) { - int sl; - unsigned t, x = 0; + uint32_t t, x, orig; struct es_info *es = (struct es_info *)s; - if (debug > 0) printf("rdcodec addr 0x%x ... ", addr); - for (t = 0; t < 0x1000; t++) - if (!(x = bus_space_read_4(es->st, es->sh, ES1371_REG_CODEC) & CODEC_WIP)) + if (!(x = es_rd(es, ES1371_REG_CODEC, 4) & CODEC_WIP)) break; - if (debug > 0) printf("loop 1 t 0x%x x 0x%x ", t, x); - - sl = spltty(); /* save the current state for later */ - x = bus_space_read_4(es->st, es->sh, ES1371_REG_SMPRATE); + 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) printf("loop 2 t 0x%x x 0x%x ", t, x); - bus_space_write_4(es->st, es->sh, ES1371_REG_CODEC, - ((addr << CODEC_POADD_SHIFT) & CODEC_POADD_MASK) | CODEC_PORD); + + 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); - - splx(sl); + 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) printf("loop 3 t 0x%x 0x%x ret 0x%x\n", t, x, ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT)); + return ((x & CODEC_PIDAT_MASK) >> CODEC_PIDAT_SHIFT); } @@ -654,24 +966,24 @@ 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); - /* printf("es1371_src_write 0x%x 0x%x\n",ES1371_REG_SMPRATE,r | ES1371_SRC_RAM_WE); */ - bus_space_write_4(es->st, es->sh, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE); + es_wr(es, ES1371_REG_SMPRATE, r | ES1371_SRC_RAM_WE, 4); } static u_int @@ -679,6 +991,8 @@ { u_int n, truncm, freq, result; + ES_LOCK_ASSERT(es); + if (rate > 48000) rate = 48000; if (rate < 4000) rate = 4000; n = rate / 3000; @@ -712,36 +1026,37 @@ { 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); } - printf("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; } @@ -799,7 +1114,7 @@ case CT4730_PCI_ID: switch(pci_get_revid(dev)) { case CT4730REV_CT4730_A: - device_set_desc(dev, "Creative SB AudioPCI CT4730"); + device_set_desc(dev, "Creative SB AudioPCI CT4730/EV1938"); return 0; default: device_set_desc(dev, "Creative SB AudioPCI CT4730-?"); @@ -832,25 +1147,285 @@ } } +#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); +} + +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 /* 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"); + } 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"); + } + } + 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); + 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 /* 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 = malloc(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)) { @@ -858,58 +1433,97 @@ es->regtype = SYS_RES_MEMORY; es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid, RF_ACTIVE); - if (es->reg) { - es->st = rman_get_bustag(es->reg); - es->sh = rman_get_bushandle(es->reg); + if (es->reg) mapped++; - } } if (mapped == 0 && (data & PCIM_CMD_PORTEN)) { es->regid = PCIR_BAR(0); es->regtype = SYS_RES_IOPORT; es->reg = bus_alloc_resource_any(dev, es->regtype, &es->regid, RF_ACTIVE); - if (es->reg) { - es->st = rman_get_bustag(es->reg); - es->sh = rman_get_bushandle(es->reg); + 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 || - pci_get_devid(dev) == CT4730_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_any(dev, SYS_RES_IRQ, &es->irqid, RF_ACTIVE | RF_SHAREABLE); - if (!es->irq || snd_setup_intr(dev, es->irq, 0, es_intr, es, &es->ih)) { + 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; } @@ -919,8 +1533,8 @@ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/es->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &es->parent_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &es->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -929,19 +1543,31 @@ (es->regtype == SYS_RES_IOPORT)? "io" : "memory", 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) free(es, M_DEVBUF); return ENXIO; } @@ -953,14 +1579,14 @@ 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); free(es, M_DEVBUF); return 0; --- sys/dev/sound/pci/es137x.h.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pci/es137x.h Thu Jul 6 04:47:48 2006 @@ -167,6 +167,17 @@ #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 */ --- sys/dev/sound/pci/fm801.c.orig Mon Jun 27 04:51:17 2005 +++ sys/dev/sound/pci/fm801.c Thu Jul 6 04:47:48 2006 @@ -114,7 +114,7 @@ }; static struct pcmchan_caps fm801ch_caps = { - 4000, 48000, + 5500, 48000, fmts, 0 }; --- sys/dev/sound/pci/hda/hda_reg.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hda_reg.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,1226 @@ +/*- + * 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 $ + */ + +#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 --- sys/dev/sound/pci/hda/hdac.c.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac.c Sun Oct 1 22:29:26 2006 @@ -0,0 +1,4894 @@ +/*- + * 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. + */ + +/* + * 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 "20061017_0033" +#define HDA_WIDGET_PARSER_REV 1 + +SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pci/hda/hdac.c,v 1.8 2006/10/16 14:43:22 ariff 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) 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_U5F_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1263) +#define ASUS_A8JC_SUBVENDOR HDA_MODEL_CONSTRUCT(ASUS, 0x1153) +#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) +#define HDA_QUIRK_EAPDINV (1 << 18) + +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 }, + { "eapdinv", HDA_QUIRK_EAPDINV }, +}; +#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) + free(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; + 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 */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &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 *)malloc(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); + + 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 *)malloc(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 = 1000; + 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 = BUS_PROBE_DEFAULT; + 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 = BUS_PROBE_GENERIC; + break; + } + } + if (result == ENXIO && class == PCIC_MULTIMEDIA && + subclass == PCIS_MULTIMEDIA_HDA) { + strlcpy(desc, "Generic", sizeof(desc)); + result = BUS_PROBE_GENERIC; + } + 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(ch->b->buf); + + 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) { + 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; + } + } + } + + 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) { + uint32_t val; + + if (hdac_eapd_switch[i].hp_switch != 0) + hdac_hp_switch_handler(devinfo); + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(devinfo->codec->cad, + w->nid, val), 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 = malloc(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"); + free(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 */ + NULL, /* lockfunc */ + NULL, /* lockfuncarg */ + &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); + free(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); + free(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 *)malloc( + 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 *)malloc( + 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 }, + { ASUS_U5F_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 0 }, + { ASUS_A8JC_SUBVENDOR, HDA_CODEC_AD1986A, + HDA_QUIRK_EAPDINV, 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) { + uint32_t val; + + val = w->param.eapdbtl; + if (devinfo->function.audio.quirks & + HDA_QUIRK_EAPDINV) + val ^= HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD; + hdac_command(sc, + HDA_CMD_SET_EAPD_BTL_ENABLE(cad, w->nid, + val), 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) + printf(" childnid=%d", + ctl->childwidget->nid); + if (ctl->widget == NULL) + printf(" NULL WIDGET!"); + printf(" 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) + printf(" childnid=%d", + ctl->childwidget->nid); + printf(" 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) + printf("(nid: %2d) ", ctl->childwidget->nid); + else + printf(" "); + printf("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)) + printf(" AC3"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(cap)) + printf(" FLOAT32"); + if (HDA_PARAM_SUPP_STREAM_FORMATS_PCM(cap)) + printf(" PCM"); + printf("\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)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(cap)) + printf(" 20"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(cap)) + printf(" 24"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(cap)) + printf(" 32"); + printf("\n"); + device_printf(sc->dev, " PCM rate:"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(cap)) + printf(" 8"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(cap)) + printf(" 11"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(cap)) + printf(" 16"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(cap)) + printf(" 22"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(cap)) + printf(" 32"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(cap)) + printf(" 44"); + printf(" 48"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(cap)) + printf(" 88"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(cap)) + printf(" 96"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(cap)) + printf(" 176"); + if (HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(cap)) + printf(" 192"); + printf("\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)) + printf(" ISC"); + if (HDA_PARAM_PIN_CAP_TRIGGER_REQD(pincap)) + printf(" TRQD"); + if (HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(pincap)) + printf(" PDC"); + if (HDA_PARAM_PIN_CAP_HEADPHONE_CAP(pincap)) + printf(" HP"); + if (HDA_PARAM_PIN_CAP_OUTPUT_CAP(pincap)) + printf(" OUT"); + if (HDA_PARAM_PIN_CAP_INPUT_CAP(pincap)) + printf(" IN"); + if (HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(pincap)) + printf(" BAL"); + if (HDA_PARAM_PIN_CAP_EAPD_CAP(pincap)) + printf(" EAPD"); + if (HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(wcap)) + printf(" : UNSOL"); + printf("\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) + printf(" HP"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE) + printf(" IN"); + if (w->wclass.pin.ctrl & HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE) + printf(" OUT"); + printf("\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) + printf(" [UNKNOWN]"); + else if (cw->enable == 0) + printf(" [DISABLED]"); + if (w->nconns > 1 && w->selconn == j && w->type != + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER) + printf(" (selected)"); + printf("\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, " "); + printf(" 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) { + printf(" [recsrc: %s]", ossdevs); + } + printf("\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++) + printf(" %d", *nids); + printf("\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++) + printf(" %d", *nids); + printf("\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) + free(devinfo->widget, M_HDAC); + if (devinfo->node_type == + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO && + devinfo->function.audio.ctl != NULL) + free(devinfo->function.audio.ctl, M_HDAC); + free(devinfo, M_HDAC); + device_delete_child(sc->dev, devlist[i]); + } + if (devlist != NULL) + free(devlist, M_TEMP); + + for (i = 0; i < HDAC_CODEC_MAX; i++) { + if (sc->codecs[i] != NULL) + free(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); + free(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) +{ + const 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( + printf("\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( + printf(" %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) + free(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); + + snprintf(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), + PCM_KLDSTRING(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) + printf(" %s", hdac_quirks_tab[i].key); + } + printf("\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) + printf(" cnid=%d", ctl->childwidget->nid); + printf(" 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); --- sys/dev/sound/pci/hda/hdac.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,69 @@ +/*- + * 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 $ + */ + +#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 --- sys/dev/sound/pci/hda/hdac_private.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac_private.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,332 @@ +/*- + * 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 $ + */ + +#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 mtx *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 --- sys/dev/sound/pci/hda/hdac_reg.h.orig Thu Jan 1 07:30:00 1970 +++ sys/dev/sound/pci/hda/hdac_reg.h Sun Oct 1 22:29:26 2006 @@ -0,0 +1,266 @@ +/*- + * 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 $ + */ + +#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 --- sys/dev/sound/pci/ich.c.orig Sat May 28 17:41:40 2005 +++ sys/dev/sound/pci/ich.c Thu Jul 6 04:47:48 2006 @@ -41,11 +41,84 @@ #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 I6300ESBID 0x25a68086 /* 6300ESB 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 { @@ -93,6 +166,11 @@ 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 mtx *ich_lock; }; /* -------------------------------------------------------------------- */ @@ -106,7 +184,7 @@ /* -------------------------------------------------------------------- */ /* Hardware */ -static u_int32_t +static __inline u_int32_t ich_rd(struct sc_info *sc, int regno, int size) { switch (size) { @@ -121,7 +199,7 @@ } } -static void +static __inline void ich_wr(struct sc_info *sc, int regno, u_int32_t data, int size) { switch (size) { @@ -149,7 +227,10 @@ 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 +271,15 @@ static void ich_filldtbl(struct sc_chinfo *ch) { + struct sc_info *sc = ch->parent; u_int32_t base; int i; base = sndbuf_getbufaddr(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; - } + 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 +303,12 @@ 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 +330,7 @@ struct sc_chinfo *ch; unsigned int num; + ICH_LOCK(sc); num = sc->chnum++; ch = &sc->ch[num]; ch->num = num; @@ -283,10 +370,13 @@ return NULL; } + ICH_UNLOCK(sc); if (sndbuf_alloc(ch->buffer, sc->dmat, sc->bufsz) != 0) return NULL; + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (u_int32_t)(ch->desc_addr), 4); + ICH_UNLOCK(sc); return ch; } @@ -304,16 +394,20 @@ 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; } @@ -328,7 +422,9 @@ 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; } @@ -342,12 +438,16 @@ switch (go) { case PCMTRIG_START: ch->run = 1; + 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; } @@ -361,7 +461,9 @@ 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; @@ -399,6 +501,7 @@ 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 */ @@ -417,8 +520,11 @@ 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) @@ -439,6 +545,7 @@ (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); @@ -573,7 +680,6 @@ 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); @@ -581,134 +687,75 @@ 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) != I6300ESBID) && - (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, sc, 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 I6300ESBID: - device_set_desc(dev, "Intel 6300ESB"); - 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 0x005910de: - device_set_desc(dev, "nVidia nForce4"); - return 0; - - case 0x74451022: - device_set_desc(dev, "AMD-768"); - return 0; - - case 0x746d1022: - device_set_desc(dev, "AMD-8111"); - return 0; + int i; + uint16_t devid, vendor; - default: - return ENXIO; + 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 (-1000); + return (0); + } } + 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 = malloc(sizeof(*sc), M_DEVBUF, M_NOWAIT)) == NULL) { + if ((sc = malloc(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 { @@ -717,28 +764,25 @@ } /* + * 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 * pci config space. The driver should use MMBAR and MBBAR, * 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) == I6300ESBID || - 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_write_config(dev, PCIR_ICH_LEGACY, ICH_LEGACY_ENABLE, 1); } else { sc->nambarid = PCIR_NAMBAR; sc->nabmbarid = PCIR_NABMBAR; @@ -763,7 +807,7 @@ 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, - busdma_lock_mutex, &Giant, &sc->dmat) != 0) { + NULL, NULL, &sc->dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -771,7 +815,7 @@ 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, 0, ich_intr, sc, &sc->ih)) { + 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; } @@ -781,9 +825,37 @@ 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 */ + 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 */ @@ -832,6 +904,12 @@ 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); free(sc, M_DEVBUF); return ENXIO; } @@ -851,7 +929,9 @@ 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); free(sc, M_DEVBUF); return 0; } @@ -885,12 +965,16 @@ 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; } @@ -908,13 +992,16 @@ 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"); --- sys/dev/sound/pci/maestro.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pci/maestro.c Thu Jul 6 04:47:48 2006 @@ -785,7 +785,7 @@ AFMT_STEREO | AFMT_S16_LE, 0 }; - static struct pcmchan_caps playcaps = {2000, 96000, playfmt, 0}; + static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0}; static u_int32_t recfmt[] = { AFMT_S8, @@ -794,7 +794,7 @@ 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; --- sys/dev/sound/pci/solo.c.orig Thu Jul 6 04:47:48 2006 +++ sys/dev/sound/pci/solo.c Sun Jul 9 10:27:45 2006 @@ -39,11 +39,14 @@ #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, @@ -55,7 +58,7 @@ 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 @@ -71,7 +74,7 @@ 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; @@ -93,8 +96,21 @@ unsigned int bufsz; struct ess_chinfo pch, rch; +#if ESS18XX_MPSAFE == 1 + struct mtx *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); @@ -219,29 +235,22 @@ static void ess_setmixer(struct ess_info *sc, u_int port, u_int value) { - u_long flags; - DEB(printf("ess_setmixer: reg=%x, val=%x\n", port, value);) - flags = spltty(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); ess_wr(sc, SB_MIX_DATA, (u_char) (value & 0xff)); DELAY(10); - splx(flags); } static int ess_getmixer(struct ess_info *sc, u_int port) { int val; - u_long flags; - flags = spltty(); ess_wr(sc, SB_MIX_ADDR, (u_char) (port & 0xff)); /* Select register */ DELAY(10); val = ess_rd(sc, SB_MIX_DATA); DELAY(10); - splx(flags); return val; } @@ -296,14 +305,17 @@ 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 +340,9 @@ 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 +352,17 @@ /* 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 */ @@ -570,6 +588,7 @@ if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD) return 0; + ess_lock(sc); switch (go) { case PCMTRIG_START: ess_dmasetup(sc, ch->hwch, sndbuf_getbufaddr(ch->buffer), sndbuf_getsize(ch->buffer), ch->dir); @@ -583,6 +602,7 @@ ess_stop(ch); break; } + ess_unlock(sc); return 0; } @@ -591,8 +611,12 @@ { 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 * @@ -760,10 +784,8 @@ ess_dmapos(struct ess_info *sc, int ch) { int p = 0, i = 0, j = 0; - u_long flags; KASSERT(ch == 1 || ch == 2, ("bad ch")); - flags = spltty(); if (ch == 1) { /* @@ -787,7 +809,6 @@ } else if (ch == 2) p = port_rd(sc->io, 0x4, 2); - splx(flags); return sc->dmasz[ch - 1] - p; } @@ -841,6 +862,13 @@ sc->parent_dmat = 0; } +#if ESS18XX_MPSAFE == 1 + if (sc->lock) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } +#endif + free(sc, M_DEVBUF); } @@ -868,7 +896,14 @@ 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 @@ -911,6 +946,7 @@ 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); @@ -921,14 +957,19 @@ pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); pci_write_config(dev, ESS_PCI_CONFIG, 0, 2); - if (ess_reset_dsp(sc)) + 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: @@ -962,11 +1003,6 @@ pci_write_config(dev, ESS_PCI_DDMACONTROL, ddma, 2); pci_write_config(dev, ESS_PCI_CONFIG, 0, 2); - if (ess_reset_dsp(sc)) - goto no; - if (mixer_init(dev, &solomixer_class, sc)) - goto no; - port_wr(sc->io, 0x7, 0xb0, 1); /* enable irqs */ #ifdef ESS18XX_DUPLEX sc->duplex = 1; @@ -979,24 +1015,48 @@ #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); 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, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &sc->parent_dmat) != 0) { + /*flags*/0, +#if ESS18XX_MPSAFE == 1 + /*lockfunc*/NULL, /*lockarg*/NULL, +#else + /*lockfunc*/busdma_lock_mutex, /*lockarg*/&Giant, +#endif + &sc->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto no; } + + if (ess_reset_dsp(sc)) + goto no; + + if (sc->newspeed) + ess_setmixer(sc, 0x71, 0x2a); + + if (mixer_init(dev, &solomixer_class, sc)) + goto no; snprintf(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), --- sys/dev/sound/pci/via8233.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pci/via8233.c Thu Jul 6 04:47:48 2006 @@ -54,6 +54,7 @@ #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 NDXSCHANS 4 /* No of DXS channels */ @@ -100,12 +101,14 @@ struct ac97_info *codec; unsigned int bufsz; + int dxs_src, dma_eol_wake; 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 mtx *lock; }; static u_int32_t via_fmt[] = { @@ -119,7 +122,86 @@ 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; +} + +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 /* 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"); + 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 +} + +static __inline u_int32_t via_rd(struct via_info *via, int regno, int size) { switch (size) { @@ -134,7 +216,7 @@ } } -static void +static __inline void via_wr(struct via_info *via, int regno, u_int32_t data, int size) { @@ -253,14 +335,16 @@ { 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; } @@ -270,9 +354,11 @@ { struct via_chinfo *ch = data; struct via_info *via = ch->parent; + u_int32_t r, v; - u_int32_t r = ch->rbase + VIA8233_RP_DXS_RATEFMT; - u_int32_t v = via_rd(via, r, 4); + 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) @@ -280,6 +366,7 @@ if (format & AFMT_16BIT) v |= VIA8233_DXS_RATEFMT_16BIT; via_wr(via, r, v, 4); + snd_mtxunlock(via->lock); return 0; } @@ -301,8 +388,10 @@ 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; } @@ -316,11 +405,10 @@ 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; + if (via->codec_caps & AC97_EXTCAP_VRA) + return ac97_setrate(via->codec, AC97_REGEXT_LADCRATE, speed); + + return 48000; } static int @@ -328,14 +416,17 @@ { struct via_chinfo *ch = data; struct via_info *via = ch->parent; + u_int32_t r, v; - u_int32_t r = ch->rbase + VIA8233_RP_DXS_RATEFMT; - u_int32_t v = via_rd(via, r, 4) & ~VIA8233_DXS_RATEFMT_48K; + 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; } @@ -362,7 +453,7 @@ struct via_info *via = ch->parent; /* Controlled by ac97 registers */ - if (via->codec_caps & AC97_EXTCAP_VRA) + if (via->codec_caps & AC97_EXTCAP_VRA) return &via_vracaps; return &via_caps; } @@ -370,7 +461,17 @@ static struct pcmchan_caps * via8233dxs_getcaps(kobj_t obj, void *data) { - /* Controlled by onboard registers */ + 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; } @@ -381,7 +482,7 @@ struct via_info *via = ch->parent; /* Controlled by ac97 registers */ - if (via->codec_caps & AC97_EXTCAP_VRA) + if (via->codec_caps & AC97_EXTCAP_VRA) return &via_vracaps; return &via_caps; } @@ -393,6 +494,7 @@ via8233chan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize) { struct via_chinfo *ch = data; + sndbuf_resize(ch->buffer, SEGS_PER_CHAN, blocksize); ch->blksz = sndbuf_getblksz(ch->buffer); return ch->blksz; @@ -403,11 +505,15 @@ { struct via_chinfo *ch = data; struct via_info *via = ch->parent; + u_int32_t v, index, count; + int ptr; - 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; + 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; @@ -445,12 +551,17 @@ ch->dir = dir; 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; } @@ -472,13 +583,18 @@ * 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; } @@ -498,8 +614,11 @@ 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; } @@ -526,6 +645,7 @@ struct via_chinfo *ch = data; struct via_info *via = ch->parent; + snd_mtxlock(via->lock); switch(go) { case PCMTRIG_START: via_buildsgdt(ch); @@ -542,6 +662,7 @@ via8233chan_reset(via, ch); break; } + snd_mtxunlock(via->lock); return 0; } @@ -587,29 +708,55 @@ via_intr(void *p) { struct via_info *via = p; - int i, stat; + int i, reg, stat; /* Poll playback channels */ + snd_mtxlock(via->lock); for (i = 0; i < NDXSCHANS + NMSGDCHANS; i++) { - if (via->pch[i].rbase == 0) + if (via->pch[i].channel == NULL) continue; - stat = via->pch[i].rbase + VIA_RP_STATUS; - if (via_rd(via, stat, 1) & SGD_STATUS_INTR) { - via_wr(via, stat, SGD_STATUS_INTR, 1); + 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); } } - + /* Poll record channels */ for (i = 0; i < NWRCHANS; i++) { - if (via->rch[i].rbase == 0) + if (via->rch[i].channel == NULL) continue; - stat = via->rch[i].rbase + VIA_RP_STATUS; - if (via_rd(via, stat, 1) & SGD_STATUS_INTR) { - via_wr(via, stat, SGD_STATUS_INTR, 1); + 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); } /* @@ -639,6 +786,9 @@ case VIA8233_REV_ID_8237: device_set_desc(dev, "VIA VT8237"); return 0; + case VIA8233_REV_ID_8251: + device_set_desc(dev, "VIA VT8251"); + return 0; default: device_set_desc(dev, "VIA VT8233X"); /* Unknown */ return 0; @@ -710,65 +860,23 @@ return ENXIO; } -#ifdef SND_DYNSYSCTL -static int via8233_spdif_en; - -static int -sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS) -{ - device_t dev; - int err, new_en, r; - - new_en = via8233_spdif_en; - 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; - via8233_spdif_en = new_en; - - dev = oidp->oid_arg1; - r = pci_read_config(dev, VIA_PCI_SPDIF, 1) & ~VIA_SPDIF_EN; - if (new_en) - r |= VIA_SPDIF_EN; - pci_write_config(dev, VIA_PCI_SPDIF, r, 1); - return 0; -} -#endif /* SND_DYNSYSCTL */ - -static void -via_init_sysctls(device_t dev) -{ -#ifdef SND_DYNSYSCTL - int r; - - r = pci_read_config(dev, VIA_PCI_SPDIF, 1); - via8233_spdif_en = (r & VIA_SPDIF_EN) ? 1 : 0; - - 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"); -#endif -} - static int 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 = malloc(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_set_powerstate(dev, PCI_POWERSTATE_D0); pci_enable_busmaster(dev); - + via->regid = PCIR_BAR(0); via->reg = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &via->regid, RF_ACTIVE); @@ -785,7 +893,7 @@ 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)) { + snd_setup_intr(dev, via->irq, INTR_MPSAFE, via_intr, via, &via->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } @@ -796,8 +904,8 @@ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &via->parent_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &via->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -813,8 +921,8 @@ /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/NSEGS * sizeof(struct via_dma_op), /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &via->sgd_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &via->sgd_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -850,28 +958,82 @@ snprintf(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 (pci_get_revid(dev) == VIA8233_REV_ID_8233A) { - if (pcm_register(dev, via, NMSGDCHANS, 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; + + /* + * 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). - pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via); */ - pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via); - pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via); + 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 { - int i; - if (pcm_register(dev, via, NMSGDCHANS + NDXSCHANS, NWRCHANS)) goto bad; - for (i = 0; i < NDXSCHANS; i++) - pcm_addchan(dev, PCMDIR_PLAY, &via8233dxs_class, via); - pcm_addchan(dev, PCMDIR_PLAY, &via8233msgd_class, via); - for (i = 0; i < NWRCHANS; i++) - pcm_addchan(dev, PCMDIR_REC, &via8233wr_class, via); + 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); @@ -884,6 +1046,7 @@ 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) free(via, M_DEVBUF); return ENXIO; } @@ -904,6 +1067,7 @@ 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); free(via, M_DEVBUF); return 0; } --- sys/dev/sound/pci/via82c686.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pci/via82c686.c Thu Jul 6 04:47:48 2006 @@ -86,6 +86,7 @@ struct via_chinfo pch, rch; struct via_dma_op *sgd_table; u_int16_t codec_caps; + struct mtx *lock; }; static u_int32_t via_fmt[] = { @@ -98,7 +99,7 @@ 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) { @@ -115,7 +116,7 @@ } -static void +static __inline void via_wr(struct via_info *via, int regno, u_int32_t data, int size) { @@ -244,6 +245,7 @@ struct via_info *via = devinfo; struct via_chinfo *ch; + snd_mtxlock(via->lock); if (dir == PCMDIR_PLAY) { ch = &via->pch; ch->base = VIA_PLAY_DMAOPS_BASE; @@ -266,9 +268,11 @@ 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; } @@ -286,10 +290,12 @@ mode_set |= VIA_RPMODE_16BIT; DEB(printf("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; } @@ -342,12 +348,14 @@ ado = ch->sgd_table; DEB(printf("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, 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(printf("viachan_trigger: go=%d\n", go)); return 0; @@ -363,11 +371,13 @@ 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(printf("viachan_getptr: len / base = %x / %x\n", len, base)); @@ -417,22 +427,25 @@ via_intr(void *p) { struct via_info *via = p; - int st; /* DEB(printf("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); } /* @@ -442,8 +455,8 @@ 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 0; } return ENXIO; } @@ -468,6 +481,7 @@ 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); @@ -521,7 +535,7 @@ via->irqid = 0; 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)) { + 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; } @@ -546,8 +560,8 @@ /*highaddr*/BUS_SPACE_MAXADDR, /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/via->bufsz, /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &via->parent_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &via->parent_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -563,8 +577,8 @@ /*filter*/NULL, /*filterarg*/NULL, /*maxsize*/NSEGS * sizeof(struct via_dma_op), /*nsegments*/1, /*maxsegz*/0x3ffff, - /*flags*/0, /*lockfunc*/busdma_lock_mutex, - /*lockarg*/&Giant, &via->sgd_dmat) != 0) { + /*flags*/0, /*lockfunc*/NULL, + /*lockarg*/NULL, &via->sgd_dmat) != 0) { device_printf(dev, "unable to create dma tag\n"); goto bad; } @@ -594,6 +608,7 @@ 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) free(via, M_DEVBUF); return ENXIO; } @@ -615,6 +630,7 @@ 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); free(via, M_DEVBUF); return 0; } --- sys/dev/sound/pcm/ac97.c.orig Sun May 1 22:31:06 2005 +++ sys/dev/sound/pcm/ac97.c Thu Jul 6 04:47:48 2006 @@ -84,10 +84,8 @@ [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 }, @@ -118,6 +116,11 @@ { 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,8 +136,9 @@ { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, { 0x41445372, 0x00, 0, "AD1981A", 0 }, - { 0x41445374, 0x00, 0, "AD1981B", 0 }, + { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, { 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 }, @@ -145,7 +149,9 @@ { 0x414c4710, 0x0f, 0, "ALC200", 0 }, { 0x414c4740, 0x0f, 0, "ALC202", 0 }, { 0x414c4720, 0x0f, 0, "ALC650", 0 }, + { 0x414c4752, 0x0f, 0, "ALC250", 0 }, { 0x414c4760, 0x0f, 0, "ALC655", 0 }, + { 0x414c4770, 0x0f, 0, "ALC203", 0 }, { 0x414c4780, 0x0f, 0, "ALC658", 0 }, { 0x414c4790, 0x0f, 0, "ALC850", 0 }, { 0x43525900, 0x07, 0, "CS4297", 0 }, @@ -156,10 +162,14 @@ { 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 }, @@ -192,6 +202,7 @@ { 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 */ @@ -201,6 +212,7 @@ { 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 }, @@ -211,6 +223,11 @@ { 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 } }; @@ -283,6 +300,21 @@ 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); } @@ -452,14 +484,16 @@ */ 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 { - /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ +#if 0 + printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); +#endif return -1; } } @@ -536,8 +570,9 @@ 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); @@ -552,6 +587,16 @@ 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; @@ -610,22 +655,74 @@ 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); } - /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ +#if 0 + printf("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"); device_printf(codec->dev, "Codec features "); for (i = j = 0; i < 10; i++) if (codec->caps & (1 << i)) @@ -645,8 +742,16 @@ } } - 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; } @@ -685,15 +790,17 @@ struct ac97_info * ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { + struct snddev_info *d = device_get_softc(dev); struct ac97_info *codec; + int eapd_inv; - codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); + codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO); if (codec == NULL) return NULL; snprintf(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, M_WAITOK); + codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); if (codec->methods == NULL) { snd_mtxlock(codec->lock); snd_mtxfree(codec->lock); @@ -704,6 +811,13 @@ codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "ac97_eapd_inv", &eapd_inv) == 0) { + if (eapd_inv != 0) + codec->flags |= AC97_F_EAPD_INV; + } + if (d != NULL) + d->flags |= SD_F_AC97; return codec; } @@ -735,6 +849,7 @@ ac97mix_init(struct snd_mixer *m) { struct ac97_info *codec = mix_getdevinfo(m); + struct snddev_info *d; u_int32_t i, mask; if (codec == NULL) @@ -743,6 +858,46 @@ if (ac97_initmixer(codec)) return -1; + switch (codec->id) { + case 0x41445374: /* AD1981B */ + mask = 0; + if (codec->mix[SOUND_MIXER_OGAIN].enable) + mask |= SOUND_MASK_OGAIN; + if (codec->mix[SOUND_MIXER_PHONEOUT].enable) + mask |= SOUND_MASK_PHONEOUT; + /* Tie ogain/phone to master volume */ + if (codec->mix[SOUND_MIXER_VOLUME].enable) + mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); + else { + mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + } + break; + case 0x434d4941: /* CMI9738 */ + case 0x434d4961: /* CMI9739 */ + case 0x434d4978: /* CMI9761 */ + case 0x434d4982: /* CMI9761 */ + case 0x434d4983: /* CMI9761 */ + ac97_wrcd(codec, AC97_MIX_PCM, 0); + bzero(&codec->mix[SOUND_MIXER_PCM], + sizeof(codec->mix[SOUND_MIXER_PCM])); + d = device_get_softc(codec->dev); + if (d != NULL) + d->flags |= SD_F_SOFTPCMVOL; + /* XXX How about master volume ? */ + break; + default: + break; + } + +#if 0 + /* XXX For the sake of debugging purposes */ + mix_setparentchild(m, SOUND_MIXER_VOLUME, + SOUND_MASK_PCM | SOUND_MASK_CD); + mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); + ac97_wrcd(codec, AC97_MIX_MASTER, 0); +#endif + mask = 0; for (i = 0; i < 32; i++) mask |= codec->mix[i].enable? 1 << i : 0; @@ -822,4 +977,65 @@ return &ac97mixer_class; } +#ifdef SND_DYNSYSCTL +static int +sysctl_hw_snd_ac97_external_amplifier(SYSCTL_HANDLER_ARGS) +{ + struct ac97_info *codec; + int ea, inv, err = 0; + u_int16_t val; + + codec = oidp->oid_arg1; + if (codec == NULL || codec->id == 0 || codec->lock == NULL) + return EINVAL; + snd_mtxlock(codec->lock); + val = ac97_rdcd(codec, AC97_REG_POWER); + inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1; + ea = (val >> 15) ^ inv; + snd_mtxunlock(codec->lock); + err = sysctl_handle_int(oidp, &ea, sizeof(ea), req); + if (err == 0 && req->newptr != NULL) { + if (ea != 0 && ea != 1) + return EINVAL; + if (ea != ((val >> 15) ^ inv)) { + snd_mtxlock(codec->lock); + ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000); + snd_mtxunlock(codec->lock); + } + } + return err; +} +#endif + +int +ac97_initsys(device_t dev) +{ +#ifdef SND_DYNSYSCTL + struct ac97_info *codec; + struct snddev_info *d; + u_int16_t orig, val; + d = device_get_softc(dev); + if (d == NULL || d->mixer_dev == NULL || d->mixer_dev->si_drv1 == NULL) + return ENODEV; + if (!(d->flags & SD_F_AC97)) + return ENODEV; + codec = mix_getdevinfo(d->mixer_dev->si_drv1); + if (codec == NULL || codec->id == 0 || codec->lock == NULL) + return ENODEV; + + snd_mtxlock(codec->lock); + orig = ac97_rdcd(codec, AC97_REG_POWER); + ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000); + val = ac97_rdcd(codec, AC97_REG_POWER); + ac97_wrcd(codec, AC97_REG_POWER, orig); + snd_mtxunlock(codec->lock); + if ((val & 0x8000) == (orig & 0x8000)) + return EINVAL; + SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)), + OID_AUTO, "ac97_external_amplifier", CTLTYPE_INT | CTLFLAG_RW, + codec, sizeof(codec), sysctl_hw_snd_ac97_external_amplifier, + "I", "AC97 External Amplifier"); +#endif + return 0; +} --- sys/dev/sound/pcm/ac97.h.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/ac97.h Thu Jul 6 04:47:48 2006 @@ -81,6 +81,7 @@ #define AC97_REG_ID2 0x7e #define AC97_F_EAPD_INV 0x00000001 +#define AC97_F_RDCD_BUG 0x00000002 #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) @@ -103,4 +104,4 @@ u_int16_t ac97_rdcd(struct ac97_info *codec, int reg); void ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val); - +int ac97_initsys(device_t dev); --- sys/dev/sound/pcm/ac97_patch.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/ac97_patch.c Thu Jul 6 04:47:48 2006 @@ -46,3 +46,19 @@ ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420); } +void ad1981b_patch(struct ac97_info* codec) +{ + ac97_wrcd(codec, AC97_AD_JACK_SPDIF, + ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800); +} + +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); +} --- sys/dev/sound/pcm/ac97_patch.h.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/ac97_patch.h Thu Jul 6 04:47:48 2006 @@ -29,3 +29,5 @@ void ad1886_patch(struct ac97_info*); void ad198x_patch(struct ac97_info*); +void ad1981b_patch(struct ac97_info*); +void cmi9739_patch(struct ac97_info*); --- sys/dev/sound/pcm/buffer.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/buffer.c Thu Jul 6 04:47:48 2006 @@ -286,8 +286,12 @@ 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; } @@ -541,15 +545,28 @@ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) { + unsigned int cnt, feedcount = 0; + KASSERT(count > 0, ("can't feed 0 bytes")); if (sndbuf_getfree(to) < count) return EINVAL; - count = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from); - if (count) - sndbuf_acquire(to, to->tmpbuf, count); - /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */ + do { + cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from); + if (cnt) + sndbuf_acquire(to, to->tmpbuf, cnt); + /* the root feeder has called sndbuf_dispose(from, , bytes fetched) */ + count -= cnt; + if (count) { + if (cnt) + feedcount++; + else if (feedcount) + feedcount--; + } + } while (count && cnt); + + channel->feedcount += feedcount; return 0; } --- sys/dev/sound/pcm/channel.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/channel.c Thu Jul 6 04:47:48 2006 @@ -32,8 +32,10 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/channel.c,v 1.97.2.1 2005/01/30 01:00:04 imp 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)) @@ -41,7 +43,7 @@ #define DEB(x) x */ -static int chn_targetirqrate = 32; +int chn_targetirqrate = CHN_IRQRATE; TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate); static int @@ -51,7 +53,7 @@ val = chn_targetirqrate; err = sysctl_handle_int(oidp, &val, sizeof(val), req); - if (val < 16 || val > 512) + if (val < CHN_IRQRATE_MIN || val > CHN_IRQRATE_MAX) err = EINVAL; else chn_targetirqrate = val; @@ -59,8 +61,8 @@ return err; } SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", ""); -static int report_soft_formats = 1; + 0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "irq rate for buffering"); +int report_soft_formats = 1; SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, &report_soft_formats, 1, "report software-emulated formats"); @@ -105,7 +107,9 @@ 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; } @@ -161,7 +165,7 @@ /* * chn_dmaupdate() tracks the status of a dma transfer, - * updating pointers. It must be called at spltty(). + * updating pointers. */ static unsigned int @@ -187,13 +191,23 @@ if (c->direction == PCMDIR_PLAY) { amt = MIN(delta, sndbuf_getready(b)); + amt -= amt % sndbuf_getbps(b); if (amt > 0) sndbuf_dispose(b, NULL, amt); } else { amt = MIN(delta, sndbuf_getfree(b)); + amt -= amt % sndbuf_getbps(b); if (amt > 0) sndbuf_acquire(b, NULL, amt); } + if ((c->flags & CHN_F_TRIGGERED) && delta == 0) { + device_printf(c->dev, "WARNING: PCMDIR_%s DMA completion " + "too fast/slow ! hwptr=%u, old=%u " + "delta=%u amt=%u ready=%u free=%u\n", + (c->direction == PCMDIR_PLAY) ? "PLAY" : "REC", + hwptr, old, delta, amt, + sndbuf_getready(b), sndbuf_getfree(b)); + } return delta; } @@ -225,23 +239,31 @@ unsigned int ret, amt; CHN_LOCKASSERT(c); -/* DEB( +#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); - KASSERT(amt <= sndbuf_getsize(bs), - ("%s(%s): amt %d > source size %d, flags 0x%x", __func__, c->name, - amt, sndbuf_getsize(bs), c->flags)); - if (sndbuf_getready(bs) < amt) + DEB(if (amt > sndbuf_getsize(bs) && + sndbuf_getbps(bs) >= sndbuf_getbps(b)) { + printf("%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); @@ -351,19 +373,25 @@ 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 at spltty. */ int chn_rdfeed(struct pcm_channel *c) @@ -379,16 +407,21 @@ 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); @@ -408,8 +441,8 @@ chn_trigger(c, PCMTRIG_EMLDMARD); chn_dmaupdate(c); ret = chn_rdfeed(c); - if (ret) - printf("chn_rdfeed: %d\n", ret); + DEB(if (ret) + printf("chn_rdfeed: %d\n", ret);) } @@ -533,12 +566,15 @@ * 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->feedcount = 0; c->xruns = 0; chn_trigger(c, PCMTRIG_START); return 0; @@ -721,30 +757,162 @@ return 0; } +static struct afmtstr_table default_afmtstr_table[] = { + { "alaw", AFMT_A_LAW }, { "mulaw", AFMT_MU_LAW }, + { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, + { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, + { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, + { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, + { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, + { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, + { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, + { NULL, 0 }, +}; + +int +afmtstr_swap_sign(char *s) +{ + if (s == NULL || strlen(s) < 2) /* full length of "s8" */ + return 0; + if (*s == 's') + *s = 'u'; + else if (*s == 'u') + *s = 's'; + else + return 0; + return 1; +} + +int +afmtstr_swap_endian(char *s) +{ + if (s == NULL || strlen(s) < 5) /* full length of "s16le" */ + return 0; + if (s[3] == 'l') + s[3] = 'b'; + else if (s[3] == 'b') + s[3] = 'l'; + else + return 0; + return 1; +} + +u_int32_t +afmtstr2afmt(struct afmtstr_table *tbl, const char *s, int stereo) +{ + size_t fsz, sz; + + sz = (s == NULL) ? 0 : strlen(s); + + if (sz > 1) { + + if (tbl == NULL) + tbl = default_afmtstr_table; + + for (; tbl->fmtstr != NULL; tbl++) { + fsz = strlen(tbl->fmtstr); + if (sz < fsz) + continue; + if (strncmp(s, tbl->fmtstr, fsz) != 0) + continue; + if (fsz == sz) + return tbl->format | + ((stereo) ? AFMT_STEREO : 0); + if ((sz - fsz) < 2 || s[fsz] != ':') + break; + /* + * For now, just handle mono/stereo. + */ + if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 'm' || + s[fsz + 1] == '1')) || + strcmp(s + fsz + 1, "mono") == 0) + return tbl->format; + if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 's' || + s[fsz + 1] == '2')) || + strcmp(s + fsz + 1, "stereo") == 0) + return tbl->format | AFMT_STEREO; + break; + } + } + + return 0; +} + +u_int32_t +afmt2afmtstr(struct afmtstr_table *tbl, u_int32_t afmt, char *dst, + size_t len, int type, int stereo) +{ + u_int32_t fmt = 0; + char *fmtstr = NULL, *tag = ""; + + if (tbl == NULL) + tbl = default_afmtstr_table; + + for (; tbl->format != 0; tbl++) { + if (tbl->format == 0) + break; + if ((afmt & ~AFMT_STEREO) != tbl->format) + continue; + fmt = afmt; + fmtstr = tbl->fmtstr; + break; + } + + if (fmt != 0 && fmtstr != NULL && dst != NULL && len > 0) { + strlcpy(dst, fmtstr, len); + switch (type) { + case AFMTSTR_SIMPLE: + tag = (fmt & AFMT_STEREO) ? ":s" : ":m"; + break; + case AFMTSTR_NUM: + tag = (fmt & AFMT_STEREO) ? ":2" : ":1"; + break; + case AFMTSTR_FULL: + tag = (fmt & AFMT_STEREO) ? ":stereo" : ":mono"; + break; + case AFMTSTR_NONE: + default: + break; + } + if (strlen(tag) > 0 && ((stereo && !(fmt & AFMT_STEREO)) || \ + (!stereo && (fmt & AFMT_STEREO)))) + strlcat(dst, tag, len); + } + + return fmt; +} + int chn_reset(struct pcm_channel *c, u_int32_t fmt) { int hwspd, r; CHN_LOCKASSERT(c); + c->feedcount = 0; c->flags &= CHN_F_RESET; c->interrupts = 0; c->xruns = 0; 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); @@ -880,7 +1048,15 @@ { CHN_LOCKASSERT(c); /* should add a feeder for volume changing if channel returns -1 */ - c->volume = (left << 8) | right; + 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; } @@ -912,7 +1088,10 @@ delta = -delta; c->feederflags &= ~(1 << FEEDER_RATE); - if (delta > 500) + /* + * Used to be 500. It was too big! + */ + if (delta > feeder_rate_round) c->feederflags |= 1 << FEEDER_RATE; else sndbuf_setspd(bs, sndbuf_getspd(b)); @@ -945,6 +1124,11 @@ r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); DEB(printf("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(printf("setspeed done, r = %d\n", r)); return r; } else @@ -1002,46 +1186,89 @@ 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 irqhz, tmp, ret, maxsize, reqblksz, tmpblksz; + int irqhz, ret, maxsz, maxsize, reqblksz; CHN_LOCKASSERT(c); 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))); + DEB(if (!(sndbuf_getsize(bs) == 0 || + sndbuf_getsize(bs) >= sndbuf_getsize(b) || + sndbuf_getbps(bs) < sndbuf_getbps(b))) { + printf("%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(printf("%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(printf("%s: defaulting to (%d, %d)\n", __func__, blkcnt, blksz)); - } else { + } else { /* use previously defined value */ blkcnt = sndbuf_getblkcnt(bs); blksz = sndbuf_getblksz(bs); - DEB(printf("%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; @@ -1050,27 +1277,29 @@ } 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 */ + /* + * 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 + */ + irqhz = (sndbuf_getbps(bs) * sndbuf_getspd(bs)) / blksz; - DEB(printf("%s: soft bps %d, spd %d, irqhz == %d\n", __func__, sndbuf_getbps(bs), sndbuf_getspd(bs), irqhz)); RANGE(irqhz, 16, 512); - tmpblksz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / irqhz; - - /* round down to 2^x */ - blksz = 32; - while (blksz <= tmpblksz) - blksz <<= 1; - blksz >>= 1; - - /* round down to fit hw buffer size */ - if (sndbuf_getmaxsize(b) > 0) - RANGE(blksz, 16, sndbuf_getmaxsize(b) / 2); - else - /* virtual channels don't appear to allocate bufhard */ - RANGE(blksz, 16, CHN_2NDBUFMAXSIZE / 2); - DEB(printf("%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); @@ -1092,7 +1321,7 @@ maxsize = sndbuf_getsize(b); if (reqblksz * blkcnt > maxsize) maxsize = reqblksz * blkcnt; - if (maxsize > sndbuf_getsize(bs)) + if (sndbuf_getbps(bs) >= sndbuf_getbps(b) && maxsize > sndbuf_getsize(bs)) printf("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) { @@ -1101,18 +1330,35 @@ goto out1; } - irqhz = (sndbuf_getbps(b) * sndbuf_getspd(b)) / sndbuf_getblksz(b); - DEB(printf("got %d, irqhz == %d\n", sndbuf_getblksz(b), irqhz)); - 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", + DEB(if (!(sndbuf_getsize(bs) == 0 || + sndbuf_getsize(bs) >= sndbuf_getsize(b) || + sndbuf_getbps(bs) < sndbuf_getbps(b))) { + printf("%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)); + 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; } @@ -1133,6 +1379,7 @@ int chn_getptr(struct pcm_channel *c) { +#if 0 int hwptr; int a = (1 << c->align) - 1; @@ -1144,6 +1391,12 @@ #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 * @@ -1166,7 +1419,9 @@ /* 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; @@ -1177,8 +1432,9 @@ { 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; + char fmtstr[AFMTSTR_MAXSZ]; CHN_LOCKASSERT(c); while (chn_removefeeder(c) == 0); @@ -1198,8 +1454,13 @@ } 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 = c->format; + } 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); @@ -1216,7 +1477,14 @@ return err; } } + c->feederflags &= ~(1 << FEEDER_VOLUME); + if (c->direction == PCMDIR_PLAY && + !(c->flags & CHN_F_VIRTUAL) && + c->parentsnddev && (c->parentsnddev->flags & SD_F_SOFTPCMVOL) && + c->parentsnddev->mixer_dev) + c->feederflags |= 1 << FEEDER_VOLUME; flags = c->feederflags; + fmtlist = chn_getcaps(c)->fmtlist; DEB(printf("feederflags %x\n", flags)); @@ -1227,7 +1495,42 @@ desc.out = 0; desc.flags = 0; DEB(printf("find feeder type %d, ", type)); - fc = feeder_getclass(&desc); + if (type == FEEDER_VOLUME || type == FEEDER_RATE) { + if (c->feeder->desc->out & AFMT_32BIT) + strlcpy(fmtstr,"s32le", sizeof(fmtstr)); + else if (c->feeder->desc->out & AFMT_24BIT) + strlcpy(fmtstr, "s24le", sizeof(fmtstr)); + else { + /* + * 8bit doesn't provide enough headroom + * for proper processing without + * creating too much noises. Force to + * 16bit instead. + */ + strlcpy(fmtstr, "s16le", sizeof(fmtstr)); + } + if (!(c->feeder->desc->out & AFMT_8BIT) && + c->feeder->desc->out & AFMT_BIGENDIAN) + afmtstr_swap_endian(fmtstr); + if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && + !(c->feeder->desc->out & AFMT_SIGNED)) + afmtstr_swap_sign(fmtstr); + desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); + if (desc.in == 0) + desc.in = AFMT_S16_LE; + /* feeder_volume need stereo processing */ + if (type == FEEDER_VOLUME || + c->feeder->desc->out & AFMT_STEREO) + desc.in |= AFMT_STEREO; + desc.out = desc.in; + fc = feeder_getclass(&desc); + if (fc != NULL && fc->desc != NULL) + desc.flags = fc->desc->flags; + } else { + fc = feeder_getclass(&desc); + if (fc != NULL && fc->desc != NULL) + desc = *fc->desc; + } DEB(printf("got %p\n", fc)); if (fc == NULL) { DEB(printf("can't find required feeder type %d\n", type)); @@ -1235,19 +1538,17 @@ return EOPNOTSUPP; } - if (c->feeder->desc->out != fc->desc->in) { - DEB(printf("build fmtchain from 0x%x to 0x%x: ", 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")); + DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); + tmp[0] = desc.in; + tmp[1] = 0; + if (chn_fmtchain(c, tmp) == 0) { + DEB(printf("failed\n")); - return ENODEV; - } - DEB(printf("ok\n")); + return ENODEV; } + DEB(printf("ok\n")); - err = chn_addfeeder(c, fc, fc->desc); + err = chn_addfeeder(c, fc, &desc); if (err) { DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); @@ -1257,31 +1558,56 @@ } } - 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] = 0; - hwfmt = chn_fmtchain(c, tmp); - } else { -#if 0 - u_int32_t *x = chn_getcaps(c)->fmtlist; - printf("acceptable formats for %s:\n", c->name); - while (*x) { - printf("[0x%8x] ", *x); - x++; - } -#endif - 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))) { + uint32_t parent = SOUND_MIXER_NONE; + int vol, left, right; + + 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. + */ + if (mixer_ioctl(c->parentsnddev->mixer_dev, + MIXER_READ(SOUND_MIXER_PCM), (caddr_t)&vol, -1, NULL) != 0) + device_printf(c->dev, "Soft PCM Volume: Failed to read default value\n"); + left = vol & 0x7f; + right = (vol >> 8) & 0x7f; + if (c->parentsnddev != NULL && + c->parentsnddev->mixer_dev != NULL && + c->parentsnddev->mixer_dev->si_drv1 != NULL) + parent = mix_getparent( + c->parentsnddev->mixer_dev->si_drv1, + SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) { + vol = 100 | (100 << 8); + if (mixer_ioctl(c->parentsnddev->mixer_dev, + MIXER_READ(parent), + (caddr_t)&vol, -1, NULL) != 0) + device_printf(c->dev, "Soft Volume: Failed to read parent default value\n"); + left = (left * (vol & 0x7f)) / 100; + right = (right * ((vol >> 8) & 0x7f)) / 100; + } + CHN_LOCK(c); + chn_setvolume(c, left, right); + } + return 0; } @@ -1325,15 +1651,21 @@ */ } if (flags & CHN_N_BLOCKSIZE) { + u_int32_t fmt; int blksz; /* * scan the children, find the lowest blocksize and use that * for the hard blocksize */ + fmt = sndbuf_getfmt(c->bufsoft); blksz = sndbuf_getmaxsize(c->bufhard) / 2; SLIST_FOREACH(pce, &c->children, link) { child = pce->channel; CHN_LOCK(child); + if (fmt != sndbuf_getfmt(child->bufhard)) { + CHN_UNLOCK(child); + continue; + } if (sndbuf_getblksz(child->bufhard) < blksz) blksz = sndbuf_getblksz(child->bufhard); CHN_UNLOCK(child); --- sys/dev/sound/pcm/channel.h.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/channel.h Thu Jul 6 04:47:48 2006 @@ -55,7 +55,7 @@ u_int32_t blocks; int direction; - unsigned int interrupts, xruns; + unsigned int interrupts, xruns, feedcount; struct snd_dbuf *bufhard, *bufsoft; struct snddev_info *parentsnddev; struct pcm_channel *parentchannel; @@ -114,6 +114,29 @@ int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); +#define AFMTSTR_NONE 0 /* "s16le" */ +#define AFMTSTR_SIMPLE 1 /* "s16le:s" */ +#define AFMTSTR_NUM 2 /* "s16le:2" */ +#define AFMTSTR_FULL 3 /* "s16le:stereo" */ + +#define AFMTSTR_MAXSZ 13 /* include null terminator */ + +#define AFMTSTR_MONO_RETURN 0 +#define AFMTSTR_STEREO_RETURN 1 + +struct afmtstr_table { + char *fmtstr; + u_int32_t format; +}; + +int afmtstr_swap_sign(char *); +int afmtstr_swap_endian(char *); +u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int); +u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int); + +extern int chn_targetirqrate; +extern int report_soft_formats; + #define PCMDIR_VIRTUAL 2 #define PCMDIR_PLAY 1 #define PCMDIR_REC -1 @@ -137,16 +160,23 @@ #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 #define CHN_N_VOLUME 0x00000004 #define CHN_N_BLOCKSIZE 0x00000008 #define CHN_N_TRIGGER 0x00000010 + +#define CHN_IRQRATE 32 +#define CHN_IRQRATE_MIN 16 +#define CHN_IRQRATE_MAX 512 /* * This should be large enough to hold all pcm data between --- sys/dev/sound/pcm/dsp.c.orig Sun Jan 30 09:00:04 2005 +++ sys/dev/sound/pcm/dsp.c Thu Jul 6 04:47:48 2006 @@ -102,7 +102,7 @@ } /* - * 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. @@ -169,15 +169,20 @@ { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - intrmask_t s; u_int32_t fmt; int devtype; - int rdref; int error; + int chnum; + + if (i_dev == NULL || td == NULL) + return ENODEV; + + if ((flags & (FREAD | FWRITE)) == 0) + return EINVAL; - s = spltty(); d = dsp_get_info(i_dev); devtype = PCMDEV(i_dev); + chnum = -1; /* decide default format */ switch (devtype) { @@ -197,40 +202,29 @@ fmt = 0; break; - case SND_DEV_DSPREC: + case SND_DEV_DSPHW: + /* + * HW *specific* access without channel numbering confusion + * caused by "first come first served" by dsp_clone(). + */ fmt = AFMT_U8; - if (mode & FWRITE) { - splx(s); - return EINVAL; - } + chnum = PCMCHAN(i_dev); break; default: panic("impossible devtype %d", devtype); } - rdref = 0; - /* lock snddev so nobody else can monkey with it */ pcm_lock(d); rdch = i_dev->si_drv1; wrch = i_dev->si_drv2; - if ((dsp_get_flags(i_dev) & SD_F_SIMPLEX) && (rdch || wrch)) { - /* we're a simplex device and already open, no go */ - pcm_unlock(d); - splx(s); - return EBUSY; - } - - if (((flags & FREAD) && rdch) || ((flags & FWRITE) && wrch)) { - /* - * device already open in one or both directions that - * the opener wants; we can't handle this. - */ + 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); - splx(s); return EBUSY; } @@ -242,65 +236,51 @@ */ if (flags & FREAD) { /* open for read */ - if (devtype == SND_DEV_DSPREC) - rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, PCMCHAN(i_dev)); - else - rdch = pcm_chnalloc(d, PCMDIR_REC, td->td_proc->p_pid, -1); - if (!rdch) { - /* no channel available, exit */ - pcm_unlock(d); - splx(s); - return EBUSY; - } - /* got a channel, already locked for us */ - if (chn_reset(rdch, fmt)) { - pcm_chnrelease(rdch); - i_dev->si_drv1 = NULL; - pcm_unlock(d); - splx(s); - return ENODEV; + pcm_unlock(d); + 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 (error == 0 && (chn_reset(rdch, fmt) || + (fmt && chn_setspeed(rdch, DSP_DEFAULT_SPEED)))) + error = ENODEV; + + if (error != 0) { + if (rdch) + pcm_chnrelease(rdch); + return error; } if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; pcm_chnref(rdch, 1); CHN_UNLOCK(rdch); - rdref = 1; - /* - * Record channel created, ref'ed and unlocked - */ + pcm_lock(d); } if (flags & FWRITE) { /* open for write */ - wrch = pcm_chnalloc(d, PCMDIR_PLAY, td->td_proc->p_pid, -1); - error = 0; + 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 (!wrch) - error = EBUSY; /* XXX Right return code? */ - else if (chn_reset(wrch, fmt)) + if (error == 0 && (chn_reset(wrch, fmt) || + (fmt && chn_setspeed(wrch, DSP_DEFAULT_SPEED)))) error = ENODEV; if (error != 0) { - if (wrch) { - /* - * Free play channel - */ + if (wrch) pcm_chnrelease(wrch); - i_dev->si_drv2 = NULL; - } - if (rdref) { + if (rdch) { /* * Lock, deref and release previously created record channel */ CHN_LOCK(rdch); pcm_chnref(rdch, -1); pcm_chnrelease(rdch); - i_dev->si_drv1 = NULL; } - pcm_unlock(d); - splx(s); return error; } @@ -308,13 +288,13 @@ wrch->flags |= CHN_F_NBIO; pcm_chnref(wrch, 1); CHN_UNLOCK(wrch); + pcm_lock(d); } i_dev->si_drv1 = rdch; i_dev->si_drv2 = wrch; pcm_unlock(d); - splx(s); return 0; } @@ -323,45 +303,19 @@ { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - intrmask_t s; int refs; - s = spltty(); d = dsp_get_info(i_dev); pcm_lock(d); rdch = i_dev->si_drv1; wrch = i_dev->si_drv2; + pcm_unlock(d); - refs = 0; - - if (rdch) { - CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); - CHN_UNLOCK(rdch); - } - if (wrch) { - CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); - CHN_UNLOCK(wrch); - } - - /* - * If there are no more references, release the channels. - */ - if ((rdch || wrch) && refs == 0) { - - if (pcm_getfakechan(d)) - pcm_getfakechan(d)->flags = 0; - - i_dev->si_drv1 = NULL; - i_dev->si_drv2 = NULL; - - dsp_set_flags(i_dev, dsp_get_flags(i_dev) & ~SD_F_TRANSIENT); - - pcm_unlock(d); - + 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); @@ -369,6 +323,7 @@ } 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 @@ -381,9 +336,24 @@ chn_reset(wrch, 0); pcm_chnrelease(wrch); } - } else + + 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); - splx(s); + } return 0; } @@ -391,10 +361,8 @@ dsp_read(struct cdev *i_dev, struct uio *buf, int flag) { struct pcm_channel *rdch, *wrch; - intrmask_t s; int ret; - s = spltty(); getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD); KASSERT(rdch, ("dsp_read: nonexistant channel")); @@ -402,7 +370,6 @@ if (rdch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); - splx(s); return EINVAL; } if (!(rdch->flags & CHN_F_RUNNING)) @@ -410,7 +377,6 @@ ret = chn_read(rdch, buf); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD); - splx(s); return ret; } @@ -418,10 +384,8 @@ dsp_write(struct cdev *i_dev, struct uio *buf, int flag) { struct pcm_channel *rdch, *wrch; - intrmask_t s; int ret; - s = spltty(); getchns(i_dev, &rdch, &wrch, SD_F_PRIO_WR); KASSERT(wrch, ("dsp_write: nonexistant channel")); @@ -429,7 +393,6 @@ if (wrch->flags & (CHN_F_MAPPED | CHN_F_DEAD)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); - splx(s); return EINVAL; } if (!(wrch->flags & CHN_F_RUNNING)) @@ -437,7 +400,6 @@ ret = chn_write(wrch, buf); relchns(i_dev, rdch, wrch, SD_F_PRIO_WR); - splx(s); return ret; } @@ -446,7 +408,6 @@ { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; - intrmask_t s; int kill; int ret = 0, *arg_i = (int *)arg, tmp; @@ -456,10 +417,16 @@ */ d = dsp_get_info(i_dev); - if (IOCGROUP(cmd) == 'M') - return mixer_ioctl(d->mixer_dev, cmd, arg, mode, td); + if (IOCGROUP(cmd) == 'M') { + /* + * This is at least, a bug to bug compatible with OSS. + */ + if (d->mixer_dev != NULL) + return mixer_ioctl(d->mixer_dev, cmd, arg, -1, td); + else + return EBADF; + } - s = spltty(); getchns(i_dev, &rdch, &wrch, 0); kill = 0; @@ -469,7 +436,6 @@ kill |= 2; if (kill == 3) { relchns(i_dev, rdch, wrch, 0); - splx(s); return EINVAL; } if (kill & 1) @@ -483,13 +449,18 @@ * we start with the new ioctl interface. */ case AIONWRITE: /* how many bytes can write ? */ - CHN_LOCK(wrch); + if (wrch) { + CHN_LOCK(wrch); /* if (wrch && wrch->bufhard.dl) while (chn_wrfeed(wrch) == 0); */ - *arg_i = wrch? sndbuf_getfree(wrch->bufsoft) : 0; - CHN_UNLOCK(wrch); + *arg_i = sndbuf_getfree(wrch->bufsoft); + CHN_UNLOCK(wrch); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case AIOSSIZE: /* set the current blocksize */ @@ -534,9 +505,15 @@ { 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); - if (cmd == AIOSFMT) { + if (cmd == AIOSFMT && p->play_format != 0) { chn_setformat(wrch, p->play_format); chn_setspeed(wrch, p->play_rate); } @@ -549,7 +526,7 @@ } if (rdch) { CHN_LOCK(rdch); - if (cmd == AIOSFMT) { + if (cmd == AIOSFMT && p->rec_format != 0) { chn_setformat(rdch, p->rec_format); chn_setspeed(rdch, p->rec_rate); } @@ -630,8 +607,10 @@ */ *arg_i = sndbuf_getready(rdch->bufsoft); CHN_UNLOCK(rdch); - } else + } else { *arg_i = 0; + ret = EINVAL; + } break; case FIOASYNC: /*set/clear async i/o */ @@ -665,9 +644,14 @@ case THE_REAL_SNDCTL_DSP_GETBLKSIZE: case SNDCTL_DSP_GETBLKSIZE: chn = wrch ? wrch : rdch; - CHN_LOCK(chn); - *arg_i = sndbuf_getblksz(chn->bufsoft); - CHN_UNLOCK(chn); + 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: @@ -731,9 +715,14 @@ case SOUND_PCM_READ_RATE: chn = wrch ? wrch : rdch; - CHN_LOCK(chn); - *arg_i = chn->speed; - CHN_UNLOCK(chn); + if (chn) { + CHN_LOCK(chn); + *arg_i = chn->speed; + CHN_UNLOCK(chn); + } else { + *arg_i = 0; + ret = EINVAL; + } break; case SNDCTL_DSP_STEREO: @@ -784,16 +773,26 @@ case SOUND_PCM_READ_CHANNELS: chn = wrch ? wrch : rdch; - CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; - CHN_UNLOCK(chn); + 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 */ chn = wrch ? wrch : rdch; - CHN_LOCK(chn); - *arg_i = chn_getformats(chn); - CHN_UNLOCK(chn); + 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 */ @@ -827,6 +826,7 @@ 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; @@ -842,9 +842,12 @@ 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); @@ -852,6 +855,9 @@ 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; @@ -888,7 +894,7 @@ 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); @@ -905,7 +911,7 @@ 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); @@ -923,7 +929,7 @@ 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); @@ -942,9 +948,23 @@ case SOUND_PCM_READ_BITS: chn = wrch ? wrch : rdch; - CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_16BIT) ? 16 : 8; - CHN_UNLOCK(chn); + 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: @@ -990,7 +1010,7 @@ 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 @@ -1031,7 +1051,6 @@ break; } relchns(i_dev, rdch, wrch, 0); - splx(s); return ret; } @@ -1039,10 +1058,8 @@ dsp_poll(struct cdev *i_dev, int events, struct thread *td) { struct pcm_channel *wrch = NULL, *rdch = NULL; - intrmask_t s; int ret, e; - s = spltty(); ret = 0; getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); @@ -1058,7 +1075,6 @@ } relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - splx(s); return ret; } @@ -1066,12 +1082,10 @@ dsp_mmap(struct cdev *i_dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) { struct pcm_channel *wrch = NULL, *rdch = NULL, *c; - intrmask_t s; if (nprot & PROT_EXEC) return -1; - s = spltty(); getchns(i_dev, &rdch, &wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); #if 0 /* @@ -1084,7 +1098,6 @@ } else if (rdch && (nprot & PROT_READ)) { c = rdch; } else { - splx(s); return -1; } #else @@ -1093,13 +1106,11 @@ if (c == NULL) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - splx(s); return -1; } if (offset >= sndbuf_getsize(c->bufsoft)) { relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - splx(s); return -1; } @@ -1109,7 +1120,6 @@ *paddr = vtophys(sndbuf_getbufofs(c->bufsoft, offset)); relchns(i_dev, rdch, wrch, SD_F_PRIO_RD | SD_F_PRIO_WR); - splx(s); return 0; } @@ -1130,8 +1140,8 @@ struct snddev_info *pcm_dev; struct snddev_channel *pcm_chan; int i, unit, devtype; - int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; - char *devnames[3] = {"dsp", "dspW", "audio"}; + static int devtypes[3] = {SND_DEV_DSP, SND_DEV_DSP16, SND_DEV_AUDIO}; + static char *devnames[3] = {"dsp", "dspW", "audio"}; if (*dev != NULL) return; @@ -1173,8 +1183,9 @@ panic("Unknown devtype %d", devtype); } - if ((pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { + if ((pdev != NULL) && (pdev->si_drv1 == NULL) && (pdev->si_drv2 == NULL)) { *dev = pdev; + dev_ref(*dev); return; } } --- sys/dev/sound/pcm/fake.c.orig Sun Jan 30 09:00:05 2005 +++ sys/dev/sound/pcm/fake.c Thu Jul 6 04:47:48 2006 @@ -29,6 +29,10 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/fake.c,v 1.13.4.1 2005/01/30 01:00:05 imp 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, @@ -41,6 +45,22 @@ 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}; @@ -120,6 +140,13 @@ c = malloc(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 / format without + * querying kernel hint repetitively (see vchan_create / vchan.c). + */ + c->speed = 0; + c->format = 0; snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev)); return c; --- sys/dev/sound/pcm/feeder.c.orig Sun Jan 30 09:00:05 2005 +++ sys/dev/sound/pcm/feeder.c Thu Jul 6 04:47:48 2006 @@ -35,6 +35,11 @@ #define MAXFEEDERS 256 #undef FEEDER_DEBUG +int feeder_buffersize = FEEDBUFSZ; +TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, + &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); + struct feedertab_entry { SLIST_ENTRY(feedertab_entry) link; struct feeder_class *feederclass; @@ -72,6 +77,45 @@ SLIST_INSERT_HEAD(&feedertab, fte, link); feedercnt++; + /* initialize global variables */ + if (snd_unit < 0 || snd_unit > PCMMAXDEV) + snd_unit = 0; + + if (snd_maxautovchans < 0 || + snd_maxautovchans > SND_MAXVCHANS) + snd_maxautovchans = 0; + + if (chn_targetirqrate < CHN_IRQRATE_MIN || + chn_targetirqrate > CHN_IRQRATE_MAX) + chn_targetirqrate = CHN_IRQRATE; + + if (feeder_buffersize < FEEDBUFSZ_MIN || + feeder_buffersize > FEEDBUFSZ_MAX) + feeder_buffersize = FEEDBUFSZ; + + if (feeder_rate_ratemin < FEEDRATE_MIN || + feeder_rate_ratemax < FEEDRATE_MIN || + feeder_rate_ratemin > FEEDRATE_MAX || + feeder_rate_ratemax > FEEDRATE_MAX || + !(feeder_rate_ratemin < feeder_rate_ratemax)) { + feeder_rate_ratemin = FEEDRATE_RATEMIN; + feeder_rate_ratemax = FEEDRATE_RATEMAX; + } + + if (feeder_rate_round < FEEDRATE_ROUNDHZ_MIN || + feeder_rate_round > FEEDRATE_ROUNDHZ_MAX) + feeder_rate_round = FEEDRATE_ROUNDHZ; + + if (bootverbose) + printf("%s: snd_unit=%d snd_maxautovchans=%d " + "chn_targetirqrate=%d feeder_buffersize=%d " + "feeder_rate_ratemin=%d feeder_rate_ratemax=%d " + "feeder_rate_round=%d\n", + __func__, snd_unit, snd_maxautovchans, + chn_targetirqrate, feeder_buffersize, + feeder_rate_ratemin, feeder_rate_ratemax, + feeder_rate_round); + /* we've got our root feeder so don't veto pcm loading anymore */ pcm_veto_load = 0; @@ -259,37 +303,339 @@ return 1; } +/* + * See feeder_fmtchain() for the mumbo-jumbo ridiculous explaination + * of what the heck is this FMT_Q_* + */ +#define FMT_Q_UP 1 +#define FMT_Q_DOWN 2 +#define FMT_Q_EQ 3 +#define FMT_Q_MULTI 4 + +/* + * 14bit format scoring + * -------------------- + * + * 13 12 11 10 9 8 2 1 0 offset + * +---+---+---+---+---+---+-------------+---+---+ + * | X | X | X | X | X | X | X X X X X X | X | X | + * +---+---+---+---+---+---+-------------+---+---+ + * | | | | | | | | | + * | | | | | | | | +--> signed? + * | | | | | | | | + * | | | | | | | +------> bigendian? + * | | | | | | | + * | | | | | | +---------------> total channels + * | | | | | | + * | | | | | +------------------------> AFMT_A_LAW + * | | | | | + * | | | | +----------------------------> AFMT_MU_LAW + * | | | | + * | | | +--------------------------------> AFMT_8BIT + * | | | + * | | +------------------------------------> AFMT_16BIT + * | | + * | +----------------------------------------> AFMT_24BIT + * | + * +--------------------------------------------> AFMT_32BIT + */ +#define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) +#define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) +#define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) +#define score_val(s1) ((s1) & 0x3f00) +#define score_cse(s1) ((s1) & 0x7f) + +u_int32_t +chn_fmtscore(u_int32_t fmt) +{ + u_int32_t ret; + + ret = 0; + if (fmt & AFMT_SIGNED) + ret |= 1 << 0; + if (fmt & AFMT_BIGENDIAN) + ret |= 1 << 1; + if (fmt & AFMT_STEREO) + ret |= (2 & 0x3f) << 2; + else + ret |= (1 & 0x3f) << 2; + if (fmt & AFMT_A_LAW) + ret |= 1 << 8; + else if (fmt & AFMT_MU_LAW) + ret |= 1 << 9; + else if (fmt & AFMT_8BIT) + ret |= 1 << 10; + else if (fmt & AFMT_16BIT) + ret |= 1 << 11; + else if (fmt & AFMT_24BIT) + ret |= 1 << 12; + else if (fmt & AFMT_32BIT) + ret |= 1 << 13; + + return ret; +} + +static u_int32_t +chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) +{ + u_int32_t best, score, score2, oldscore; + int i; + + if (fmt == 0 || fmts == NULL || fmts[0] == 0) + return 0; + + if (fmtvalid(fmt, fmts)) + return fmt; + + best = 0; + score = chn_fmtscore(fmt); + oldscore = 0; + for (i = 0; fmts[i] != 0; i++) { + score2 = chn_fmtscore(fmts[i]); + if (cheq && !score_cheq(score, score2)) + continue; + if (oldscore == 0 || + (score_val(score2) == score_val(score)) || + (score_val(score2) == score_val(oldscore)) || + (score_val(score2) > score_val(oldscore) && + score_val(score2) < score_val(score)) || + (score_val(score2) < score_val(oldscore) && + score_val(score2) > score_val(score)) || + (score_val(oldscore) < score_val(score) && + score_val(score2) > score_val(oldscore))) { + if (score_val(oldscore) != score_val(score2) || + score_cse(score) == score_cse(score2) || + ((score_cse(oldscore) != score_cse(score) && + !score_endianeq(score, oldscore) && + (score_endianeq(score, score2) || + (!score_signeq(score, oldscore) && + score_signeq(score, score2)))))) { + best = fmts[i]; + oldscore = score2; + } + } + } + return best; +} + +u_int32_t +chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) +{ + return chn_fmtbestfunc(fmt, fmts, 0); +} + +u_int32_t +chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) +{ + return chn_fmtbestfunc(fmt, fmts, 1); +} + +u_int32_t +chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) +{ + u_int32_t best1, best2; + u_int32_t score, score1, score2; + + if (fmtvalid(fmt, fmts)) + return fmt; + + best1 = chn_fmtbeststereo(fmt, fmts); + best2 = chn_fmtbestbit(fmt, fmts); + + if (best1 != 0 && best2 != 0 && best1 != best2) { + if (fmt & AFMT_STEREO) + return best1; + else { + score = score_val(chn_fmtscore(fmt)); + score1 = score_val(chn_fmtscore(best1)); + score2 = score_val(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; +} + static struct pcm_feeder * feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) { - struct feedertab_entry *fte; + struct feedertab_entry *fte, *ftebest; struct pcm_feeder *try, *ret; + uint32_t fl, qout, qsrc, qdst; + int qtype; + + if (to == NULL || to[0] == 0) + return NULL; - /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */ + DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); if (fmtvalid(source->desc->out, to)) { - /* printf("got it\n"); */ + DEB(printf("got it\n")); return source; } if (maxdepth < 0) return NULL; + /* + * WARNING: THIS IS _NOT_ FOR THE FAINT HEART + * Disclaimer: I don't expect anybody could understand this + * without deep logical and mathematical analysis + * involving various unnamed probability theorem. + * + * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm + * is **extremely** difficult to digest especially when applied to + * large sets / numbers of random chains (feeders), each with + * unique characteristic providing different sets of in/out format. + * + * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: + * 1) Format chains + * 1.1 "8bit to any, not to 8bit" + * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] + * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] + * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] + * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] + * 1.2 "Any to 8bit, not from 8bit" + * 1.2.1 sign can remain consistent, e.g: s16le -> s8 + * 1.2.2 sign can be changed, e.g: s16le -> u8 + * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 + * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 + * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" + * 1.3.1 endian MUST remain consistent + * 1.3.2 sign CAN be changed + * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding + * 16bit to 24bit . + * 2) Channel chains (mono <-> stereo) + * 2.1 Both endian and sign MUST remain consistent + * 3) Endian chains (big endian <-> little endian) + * 3.1 Channels and sign MUST remain consistent + * 4) Sign chains (signed <-> unsigned) + * 4.1 Channels and endian MUST remain consistent + * + * .. and the mother of all chaining rules: + * + * Rules 0: Source and destination MUST not contain multiple selections. + * (qtype != FMT_Q_MULTI) + * + * First of all, our caller ( chn_fmtchain() ) will reduce the possible + * multiple from/to formats to a single best format using chn_fmtbest(). + * Then, using chn_fmtscore(), we determine the chaining characteristic. + * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining + * type while still adhering above chaining rules. + * + * The need for this complicated chaining procedures is inevitable, + * since currently we have more than 200 different types of FEEDER_FMT + * doing various unique format conversion. Without this (the old way), + * it is possible to generate broken chain since it doesn't do any + * sanity checking to ensure that the output format is "properly aligned" + * with the direction of conversion (quality up/down/equal). + * + * Conversion: s24le to s32le + * Possible chain: 1) s24le -> s32le (correct, optimized) + * 2) s24le -> s16le -> s32le + * (since we have feeder_24to16 and feeder_16to32) + * +-- obviously broken! + * + * Using scoring mechanisme, this will ensure that the chaining + * process do the right thing, or at least, give the best chain + * possible without causing quality (the 'Q') degradation. + */ + + qdst = chn_fmtscore(to[0]); + qsrc = chn_fmtscore(source->desc->out); + +#define score_q(s1) score_val(s1) +#define score_8bit(s1) ((s1) & 0x700) +#define score_non8bit(s1) (!score_8bit(s1)) +#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ + (score_8bit(s2) && score_non8bit(s1))) + +#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) +#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) +#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) +#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ + 0x2 : 0x0)) +#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) +#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ + ((score_cheq(s1, s2) && \ + score_endianeq(s1, s2)) ? \ + 0x1 : 0x0) | \ + ((score_cheq(s1, s2) && \ + score_signeq(s1, s2)) ? \ + 0x2 : 0x0)) + + /* Determine chaining direction and set matching flag */ + fl = 0x3fff; + if (to[1] != 0) { + qtype = FMT_Q_MULTI; + printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); + } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { + qtype = FMT_Q_DOWN; + fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); + } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { + qtype = FMT_Q_UP; + fl = FMT_Q_UP_FLAGS(qsrc, qdst); + } else { + qtype = FMT_Q_EQ; + fl = FMT_Q_EQ_FLAGS(qsrc, qdst); + } + + ftebest = NULL; + SLIST_FOREACH(fte, &feedertab, link) { if (fte->desc == NULL) continue; if (fte->desc->type != FEEDER_FMT) continue; - if (fte->desc->in == source->desc->out) { + qout = chn_fmtscore(fte->desc->out); +#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) +#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) +#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ + score_q(s3) >= score_q(s1) && \ + score_q(s3) <= score_q(s2)) +#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ + score_q(s3) <= score_q(s1) && \ + score_q(s3) >= score_q(s2)) +#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ + score_q(s1) == score_q(s2)) + if (fte->desc->in == source->desc->out && + (FMT_Q_MULTI_VALIDATE(qtype) || + (FMT_Q_FL_MATCH(fl, qout, qdst) && + (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || + FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || + FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { try = feeder_create(fte->feederclass, fte->desc); if (try) { try->source = source; - ret = chainok(try, stop)? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; + ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; if (ret != NULL) return ret; feeder_destroy(try); } + } else if (fte->desc->in == source->desc->out) { + /* XXX quality must be considered! */ + if (ftebest == NULL) + ftebest = fte; + } + } + + if (ftebest != NULL) { + try = feeder_create(ftebest->feederclass, ftebest->desc); + if (try) { + try->source = source; + ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; + if (ret != NULL) + return ret; + feeder_destroy(try); } } + /* printf("giving up %s...\n", source->class->name); */ return NULL; @@ -299,7 +645,7 @@ 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")); @@ -307,44 +653,81 @@ KASSERT(to != NULL, ("to == NULL")); KASSERT(to[0] != 0, ("to[0] == 0")); + if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) + return 0; + stop = c->feeder; + best = 0; if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { from = chn_getcaps(c)->fmtlist; + if (from[1] != 0) { + 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) { + best = chn_fmtbest(from[0], to); + if (best != 0) { + tmpto[0] = best; + tmpto[1] = 0; + to = tmpto; + } + } } - i = 0; - best = 0; - bestmax = 100; - while (from[i] != 0) { - c->feeder->desc->out = from[i]; - try = NULL; +#define FEEDER_FMTCHAIN_MAXDEPTH 8 + + try = NULL; + + if (to[0] != 0 && from[0] != 0 && + to[1] == 0 && from[1] == 0) { max = 0; - while (try == NULL && max < 8) { + best = from[0]; + c->feeder->desc->out = best; + do { try = feeder_fmtchain(to, c->feeder, stop, max); - if (try == NULL) - max++; - } - if (try != NULL && max < bestmax) { - bestmax = max; - best = from[i]; - } - while (try != NULL && try != stop) { - del = try; - try = try->source; - feeder_destroy(del); + DEB(if (try != NULL) { + printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", + __func__, from[0], to[0], max) + }); + } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); + } else { + printf("%s: Using the old-way format chaining!\n", __func__); + i = 0; + best = 0; + bestmax = 100; + while (from[i] != 0) { + c->feeder->desc->out = from[i]; + try = NULL; + max = 0; + do { + try = feeder_fmtchain(to, c->feeder, stop, max); + } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); + if (try != NULL && max < bestmax) { + bestmax = max; + best = from[i]; + } + while (try != NULL && try != stop) { + del = try; + try = try->source; + feeder_destroy(del); + } + i++; } - i++; - } - if (best == 0) - return 0; + if (best == 0) + return 0; - c->feeder->desc->out = best; - try = feeder_fmtchain(to, c->feeder, stop, bestmax); + c->feeder->desc->out = best; + try = feeder_fmtchain(to, c->feeder, stop, bestmax); + } if (try == NULL) return 0; @@ -371,7 +754,16 @@ printf("%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 @@ -434,8 +826,3 @@ }; 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); - - - - - --- sys/dev/sound/pcm/feeder.h.orig Sun Jan 30 09:00:05 2005 +++ sys/dev/sound/pcm/feeder.h Thu Jul 6 04:47:48 2006 @@ -53,6 +53,10 @@ void feeder_register(void *p); struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc); +u_int32_t 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); @@ -68,11 +72,11 @@ .desc = feeder ## _desc, \ .data = pdata, \ }; \ -SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, feeder_register, &feeder ## _class); +SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class); #define FEEDER_ROOT 1 #define FEEDER_FMT 2 -#define FEEDER_MIXER 3 +#define FEEDER_MIXER 3 #define FEEDER_RATE 4 #define FEEDER_FILTER 5 #define FEEDER_VOLUME 6 @@ -81,4 +85,27 @@ #define FEEDRATE_SRC 1 #define FEEDRATE_DST 2 +#define FEEDRATE_RATEMIN 1 +#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ +#define FEEDRATE_MIN 1 +#define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */ + +#define FEEDRATE_ROUNDHZ 25 +#define FEEDRATE_ROUNDHZ_MIN 0 +#define FEEDRATE_ROUNDHZ_MAX 500 + +/* + * Default buffer size for feeder processing. + * + * Big = less sndbuf_feed(), more memory usage. + * Small = aggresive sndbuf_feed() (perhaps too much), less memory usage. + */ +#define FEEDBUFSZ 16384 +#define FEEDBUFSZ_MIN 2048 +#define FEEDBUFSZ_MAX 131072 + +extern int feeder_rate_ratemin; +extern int feeder_rate_ratemax; +extern int feeder_rate_round; +extern int feeder_buffersize; --- sys/dev/sound/pcm/feeder_fmt.c.orig Sun Jan 30 09:00:05 2005 +++ sys/dev/sound/pcm/feeder_fmt.c Thu Jul 6 04:47:48 2006 @@ -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 @@ -22,54 +23,108 @@ * 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. + * + * *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("$FreeBSD: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.13.4.1 2005/01/30 01:00:05 imp Exp $"); MALLOC_DEFINE(M_FMTFEEDER, "fmtfeed", "pcm format feeder"); -#define FEEDBUFSZ 8192 +#define FMT_TRACE(x...) /* printf(x) */ +#define FMT_TEST(x, y...) /* if (x) FMT_TRACE(y) */ -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, +struct feed_fmt_info { + int bps, bufsz; + uint8_t *buffer; }; -static unsigned char u8_to_ulaw[] = { +/* + * 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 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, @@ -104,446 +159,1404 @@ 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_table_8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int j, sign, k; + uint8_t *tbl = (uint8_t *)f->data; + + if (count < 1) + return 0; + k = FEEDER_FEED(f->source, c, b, count, source); + j = k; + sign = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80; + while (j > 0) { + j--; + b[j] = tbl[b[j]] ^ sign; + } + return k; +} static int -feed_8to16le(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_table_16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - int i, j, k; + int i, j, sign, k; + uint8_t *tbl = (uint8_t *)f->data; + + if (count < 2) + return 0; + k = FEEDER_FEED(f->source, c, b, count >> 1, source); + i = k; + k <<= 1; + j = k; + sign = (f->desc->out & AFMT_SIGNED) ? 0x00 : 0x80; + if (f->desc->out & AFMT_BIGENDIAN) { + while (i > 0) { + b[--j] = 0; + b[--j] = tbl[b[--i]] ^ sign; + } + } else { + while (i > 0) { + b[--j] = tbl[b[--i]] ^ sign; + b[--j] = 0; + } + } + return k; +} + +static int +feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int j, sign, k; + uint8_t *tbl = (uint8_t *)f->data; - 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; + if (count < 1) + return 0; + k = FEEDER_FEED(f->source, c, b, count, source); + j = k; + sign = (f->desc->in & AFMT_SIGNED) ? 0x80 : 0x00; + while (j > 0) { + j--; + b[j] = tbl[b[j] ^ sign]; } - 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 struct pcm_feederdesc feeder_ulawto8_desc[] = { + {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_ulawto8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_8), + {0, 0} +}; +FEEDER_DECLARE(feeder_ulawto8, 0, ulaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_alawto8_desc[] = { + {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_alawto8_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_8), + {0, 0} +}; +FEEDER_DECLARE(feeder_alawto8, 0, alaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_ulawto16_desc[] = { + {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_ulawto16_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_16), + {0, 0} +}; +FEEDER_DECLARE(feeder_ulawto16, 0, ulaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_alawto16_desc[] = { + {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_alawto16_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_16), + {0, 0} }; -static kobj_method_t feeder_8to16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_8to16le), - { 0, 0 } +FEEDER_DECLARE(feeder_alawto16, 0, alaw_to_s8_tbl); + +static struct pcm_feederdesc feeder_8toulaw_desc[] = { + {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_MU_LAW, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_MU_LAW|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_8toulaw_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_xlaw), + {0, 0} +}; +FEEDER_DECLARE(feeder_8toulaw, 0, u8_to_ulaw_tbl); + +static struct pcm_feederdesc feeder_8toalaw_desc[] = { + {FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_A_LAW, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_A_LAW|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_8toalaw_methods[] = { + KOBJMETHOD(feeder_feed, feed_table_xlaw), + {0, 0} }; -FEEDER_DECLARE(feeder_8to16le, 0, NULL); +FEEDER_DECLARE(feeder_8toalaw, 0, u8_to_alaw_tbl); -/*****************************************************************************/ +/* + * All conversion done in byte level to preserve endianess. + */ static int -feed_16to8_init(struct pcm_feeder *f) +feed_fmt_init(struct pcm_feeder *f) { - f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO); - if (f->data == NULL) + struct feed_fmt_info *info; + + info = malloc(sizeof(*info), M_FMTFEEDER, M_NOWAIT | M_ZERO); + if (info == NULL) return ENOMEM; + + info->bps = 1; + info->bps <<= (f->desc->in & AFMT_STEREO) ? 1 : 0; + if (f->desc->in & AFMT_16BIT) + info->bps <<= 1; + else if (f->desc->in & AFMT_24BIT) + info->bps *= 3; + else if (f->desc->in & AFMT_32BIT) + info->bps <<= 2; + info->bufsz = feeder_buffersize - (feeder_buffersize % info->bps); + + info->buffer = malloc(info->bufsz, M_FMTFEEDER, M_NOWAIT | M_ZERO); + if (info->buffer == NULL) { + free(info, M_FMTFEEDER); + return ENOMEM; + } + + f->data = info; + return 0; } static int -feed_16to8_free(struct pcm_feeder *f) +feed_fmt_free(struct pcm_feeder *f) { - if (f->data) - free(f->data, M_FMTFEEDER); + struct feed_fmt_info *info = f->data; + + if (info) { + if (info->buffer) + free(info->buffer, M_FMTFEEDER); + free(info, M_FMTFEEDER); + } f->data = NULL; return 0; } +#define swap_sign(fdr) \ + (((((fdr)->desc->in & AFMT_SIGNED) && \ + !((fdr)->desc->out & AFMT_SIGNED)) || \ + (!((fdr)->desc->in & AFMT_SIGNED) && \ + ((fdr)->desc->out & AFMT_SIGNED))) ? 0x80 : 0x00) + +/* + * 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_8to16(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; + int sign; + + if (count < 2) + return 0; + k = FEEDER_FEED(f->source, c, b, count >> 1, source); + i = k; + k <<= 1; + j = k; + sign = swap_sign(f); + if (f->desc->out & AFMT_BIGENDIAN) { + while (i > 0) { + b[--j] = 0; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 0) { + b[--j] = b[--i] ^ sign; + b[--j] = 0; + } } - return i; + return k; } +static struct pcm_feederdesc feeder_8to16_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}, + {FEEDER_FMT, AFMT_U8, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U8, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U8, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U8|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S8, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S8|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_8to16_methods[] = { + KOBJMETHOD(feeder_feed, feed_8to16), + {0, 0} +}; +FEEDER_DECLARE(feeder_8to16, 0, NULL); -static struct pcm_feederdesc feeder_16leto8_desc[] = { +static int +feed_16to8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_fmt_info *info = f->data; + int i, j, k, sign; + uint8_t *src = info->buffer; + + if (count < 1) + return 0; + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__); + k &= ~1; + i = k; + j = k >> 1; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + i--; + b[--j] = src[--i] ^ sign; + } + } else { + while (i > 1) { + b[--j] = src[--i] ^ sign; + i--; + } + } + return k >> 1; +} +static struct pcm_feederdesc feeder_16to8_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}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_LE, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S8, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S8|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U8, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U8|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_16to8_methods[] = { + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_16to8), + {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 } +FEEDER_DECLARE(feeder_16to8, 0, NULL); + +static int +feed_16to24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k, sign; + + if (count < 3) + return 0; + 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__); + k &= ~1; + i = k; + j = (k >> 1) * 3; + k = j; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + b[--j] = 0; + b[--j] = b[--i]; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 1) { + b[--j] = b[--i] ^ sign; + b[--j] = b[--i]; + b[--j] = 0; + } + } + return k; +} +static struct pcm_feederdesc feeder_16to24_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}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_16to24_methods[] = { + KOBJMETHOD(feeder_feed, feed_16to24), + {0, 0} }; -FEEDER_DECLARE(feeder_16leto8, 1, NULL); +FEEDER_DECLARE(feeder_16to24, 0, NULL); -/*****************************************************************************/ +static int +feed_24to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_fmt_info *info = f->data; + int i, j, k, sign; + uint8_t *src = info->buffer; + + if (count < 2) + return 0; + k = (count >> 1) * 3; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__); + k -= k % 3; + i = (k / 3) << 1; + j = i; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + k--; + b[--i] = src[--k]; + b[--i] = src[--k] ^ sign; + } + } else { + while (i > 1) { + b[--i] = src[--k] ^ sign; + b[--i] = src[--k]; + k--; + } + } + return j; +} +static struct pcm_feederdesc feeder_24to16_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}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_LE, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_24to16_methods[] = { + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_24to16), + {0, 0} +}; +FEEDER_DECLARE(feeder_24to16, 1, NULL); static int -feed_monotostereo8(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) +feed_16to32(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, sign, k; - j = k - 1; - i = j * 2 + 1; - while (i > 0 && j >= 0) { - b[i--] = b[j]; - b[i--] = b[j]; - j--; + if (count < 4) + return 0; + k = FEEDER_FEED(f->source, c, b, (count & ~3) >> 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__); + k &= ~1; + i = k; + j = k << 1; + k = j; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + b[--j] = 0; + b[--j] = 0; + b[--j] = b[--i]; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 1) { + b[--j] = b[--i] ^ sign; + b[--j] = b[--i]; + b[--j] = 0; + b[--j] = 0; + } + } + return k; +} +static struct pcm_feederdesc feeder_16to32_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}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_U16_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_S16_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_16to32_methods[] = { + KOBJMETHOD(feeder_feed, feed_16to32), + {0, 0} +}; +FEEDER_DECLARE(feeder_16to32, 0, NULL); + +static int +feed_32to16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_fmt_info *info = f->data; + int i, j, k, sign; + uint8_t *src = info->buffer; + + if (count < 2) + return 0; + k = (count & ~1) << 1; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__); + k &= ~3; + i = k; + k >>= 1; + j = k; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 3) { + i -= 2; + b[--j] = src[--i]; + b[--j] = src[--i] ^ sign; + } + } else { + while (i > 3) { + b[--j] = src[--i] ^ sign; + b[--j] = src[--i]; + i -= 2; + } + } + return k; +} +static struct pcm_feederdesc feeder_32to16_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}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_LE, AFMT_S16_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_U16_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U16_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_32to16_methods[] = { + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_32to16), + {0, 0} +}; +FEEDER_DECLARE(feeder_32to16, 1, NULL); + +static int +feed_24to32(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j, k, sign; + + if (count < 4) + return 0; + k = FEEDER_FEED(f->source, c, b, (count >> 2) * 3, 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__); + k -= k % 3; + i = k; + k = (k / 3) << 2; + j = k; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 2) { + b[--j] = 0; + b[--j] = b[--i]; + b[--j] = b[--i]; + b[--j] = b[--i] ^ sign; + } + } else { + while (i > 2) { + b[--j] = b[--i] ^ sign; + b[--j] = b[--i]; + b[--j] = b[--i]; + b[--j] = 0; + } + } + return k; +} +static struct pcm_feederdesc feeder_24to32_desc[] = { + {FEEDER_FMT, AFMT_U24_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_LE, AFMT_S32_LE, 0}, + {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_LE, AFMT_U32_LE, 0}, + {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_24to32_methods[] = { + KOBJMETHOD(feeder_feed, feed_24to32), + {0, 0} +}; +FEEDER_DECLARE(feeder_24to32, 1, NULL); + +static int +feed_32to24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_fmt_info *info = f->data; + int i, j, k, sign; + uint8_t *src = info->buffer; + + if (count < 3) + return 0; + k = (count / 3) << 2; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__); + k &= ~3; + i = k; + k = (k >> 2) * 3; + j = k; + sign = swap_sign(f); + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 3) { + --i; + b[--j] = src[--i]; + b[--j] = src[--i]; + b[--j] = src[--i] ^ sign; + } + } else { + while (i > 3) { + b[--j] = src[--i] ^ sign; + b[--j] = src[--i]; + b[--j] = src[--i]; + --i; + } } - return k * 2; + return k; } +static struct pcm_feederdesc feeder_32to24_desc[] = { + {FEEDER_FMT, AFMT_U32_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_LE, AFMT_S24_LE, 0}, + {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_LE, AFMT_U24_LE, 0}, + {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_32to24_methods[] = { + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_32to24), + {0, 0} +}; +FEEDER_DECLARE(feeder_32to24, 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; + + if (count < 2) + return 0; + 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}, + {FEEDER_FMT, AFMT_MU_LAW, AFMT_MU_LAW|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_A_LAW, AFMT_A_LAW|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; + int i, j, k; + uint8_t l, m; - 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; + if (count < 4) + return 0; + k = FEEDER_FEED(f->source, c, b, (count & ~3) >> 1, source); + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; } - return k * 2; + FMT_TEST(k & 1, "%s: Bytes not 16bit aligned.\n", __func__); + k &= ~1; + i = k; + j = k << 1; + while (i > 1) { + 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 = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO); - if (f->data == NULL) - return ENOMEM; - return 0; + int i, j, k; + uint8_t l, m, n; + + if (count < 6) + return 0; + k = FEEDER_FEED(f->source, c, b, ((count / 6) * 6) >> 1, 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__); + k -= k % 3; + i = k; + j = k << 1; + while (i > 2) { + 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) - free(f->data, M_FMTFEEDER); - f->data = NULL; - return 0; + int i, j, k; + uint8_t l, m, n, o; + + if (count < 8) + return 0; + k = FEEDER_FEED(f->source, c, b, (count & ~7) >> 1, 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__); + k &= ~3; + i = k; + j = k << 1; + while (i > 3) { + 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; + struct feed_fmt_info *info = f->data; + int i, j, k; + uint8_t *src = info->buffer; - 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; + if (count < 1) + return 0; + k = count << 1; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source); + if (k < 2) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; } - return i; + FMT_TEST(k & 1, "%s: Bytes not 8bit (stereo) aligned.\n", __func__); + k &= ~1; + i = k >> 1; + j = i; + while (i > 0) { + k--; + b[--i] = src[--k]; + } + 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}, + {FEEDER_FMT, AFMT_MU_LAW|AFMT_STEREO, AFMT_MU_LAW, 0}, + {FEEDER_FMT, AFMT_A_LAW|AFMT_STEREO, AFMT_A_LAW, 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_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_stereotomono8), + {0, 0} }; -FEEDER_DECLARE(feeder_stereotomono8, 1, NULL); - -/*****************************************************************************/ - -static int -feed_stereotomono16_init(struct pcm_feeder *f) -{ - f->data = malloc(FEEDBUFSZ, M_FMTFEEDER, M_NOWAIT | M_ZERO); - if (f->data == NULL) - return ENOMEM; - return 0; -} +FEEDER_DECLARE(feeder_stereotomono8, 0, NULL); static int -feed_stereotomono16_free(struct pcm_feeder *f) +feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) { - if (f->data) - free(f->data, M_FMTFEEDER); - f->data = NULL; - return 0; -} - -static int -feed_stereotomono16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source) -{ - u_int32_t i = 0, toget = count * 2; - int j = 0, k; + struct feed_fmt_info *info = f->data; + int i, j, k; + uint8_t *src = info->buffer; - 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; + if (count < 2) + return 0; + k = (count & ~1) << 1; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__); + k &= ~3; + i = k >> 1; + j = i; + while (i > 1) { + k -= 2; + b[--i] = src[--k]; + b[--i] = src[--k]; } - return i; + 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}, + {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_stereotomono16_init), - KOBJMETHOD(feeder_free, feed_stereotomono16_free), - KOBJMETHOD(feeder_feed, feed_stereotomono16), - { 0, 0 } + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_stereotomono16), + {0, 0} }; -FEEDER_DECLARE(feeder_stereotomono16, 1, NULL); - -/*****************************************************************************/ +FEEDER_DECLARE(feeder_stereotomono16, 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_stereotomono24(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); + struct feed_fmt_info *info = f->data; + int i, j, k; + uint8_t *src = info->buffer; - while (i < j) { - t = b[i]; - b[i] = b[i + 1]; - b[i + 1] = t; - i += 2; + if (count < 3) + return 0; + k = ((count / 3) * 3) << 1; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), source); + if (k < 6) { + FMT_TRACE("%s: Not enough data (Got: %d bytes)\n", + __func__, k); + return 0; + } + FMT_TEST(k % 6, "%s: Bytes not 24bit (stereo) aligned.\n", __func__); + k -= k % 6; + i = k >> 1; + j = i; + while (i > 2) { + k -= 3; + 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 kobj_method_t feeder_endian_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - { 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_stereotomono24_methods[] = { + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_stereotomono24), + {0, 0} }; -FEEDER_DECLARE(feeder_endian, 0, NULL); - -/*****************************************************************************/ +FEEDER_DECLARE(feeder_stereotomono24, 0, NULL); static int -feed_sign(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) { - int i = 0, j = FEEDER_FEED(f->source, c, b, count, source); - intptr_t ssz = (intptr_t)f->data, ofs = ssz - 1; + struct feed_fmt_info *info = f->data; + int i, j, k; + uint8_t *src = info->buffer; - while (i < j) { - b[i + ofs] ^= 0x80; - i += ssz; + if (count < 4) + return 0; + k = (count & ~3) << 1; + k = FEEDER_FEED(f->source, c, src, min(k, info->bufsz), 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__); + k &= ~7; + i = k >> 1; + j = i; + while (i > 3) { + 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_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_stereotomono32_methods[] = { + KOBJMETHOD(feeder_init, feed_fmt_init), + KOBJMETHOD(feeder_free, feed_fmt_free), + KOBJMETHOD(feeder_feed, feed_stereotomono32), + {0, 0} +}; +FEEDER_DECLARE(feeder_stereotomono32, 0, NULL); +/* + * Channel conversion (stereo -> mono) end + */ +/* + * Sign conversion + */ +static int +feed_sign8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j; + + if (count < 1) + return 0; + j = FEEDER_FEED(f->source, c, b, count, source); + 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_sign16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j; -static struct pcm_feederdesc feeder_sign16le_desc[] = { + if (count < 2) + return 0; + j = FEEDER_FEED(f->source, c, b, count & ~1, 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__); + j &= ~1; + i = j; + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 1) { + i--; + b[--i] ^= 0x80; + } + } else { + while (i > 1) { + b[--i] ^= 0x80; + i--; + } + } + return j; +} +static struct pcm_feederdesc feeder_sign16_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}, + {FEEDER_FMT, AFMT_U16_BE, AFMT_S16_BE, 0}, + {FEEDER_FMT, AFMT_U16_BE|AFMT_STEREO, AFMT_S16_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S16_BE, AFMT_U16_BE, 0}, + {FEEDER_FMT, AFMT_S16_BE|AFMT_STEREO, AFMT_U16_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_sign16_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign16), + {0, 0} }; -static kobj_method_t feeder_sign16le_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - { 0, 0 } -}; -FEEDER_DECLARE(feeder_sign16le, -1, (void *)2); +FEEDER_DECLARE(feeder_sign16, 0, NULL); + +static int +feed_sign24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j; -/*****************************************************************************/ + if (count < 3) + return 0; + j = FEEDER_FEED(f->source, c, b, (count / 3) * 3, source); + 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__); + j -= j % 3; + i = j; + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 2) { + i -= 2; + b[--i] ^= 0x80; + } + } else { + while (i > 2) { + b[--i] ^= 0x80; + i -= 2; + } + } + return j; +} +static struct pcm_feederdesc feeder_sign24_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}, + {FEEDER_FMT, AFMT_U24_BE, AFMT_S24_BE, 0}, + {FEEDER_FMT, AFMT_U24_BE|AFMT_STEREO, AFMT_S24_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S24_BE, AFMT_U24_BE, 0}, + {FEEDER_FMT, AFMT_S24_BE|AFMT_STEREO, AFMT_U24_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_sign24_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign24), + {0, 0} +}; +FEEDER_DECLARE(feeder_sign24, 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_sign32(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; - while (i < j) { - b[i] = ((u_int8_t *)f->data)[b[i]]; - i++; + if (count < 4) + return 0; + j = FEEDER_FEED(f->source, c, b, count & ~3, 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__); + j &= ~3; + i = j; + if (f->desc->in & AFMT_BIGENDIAN) { + while (i > 3) { + i -= 3; + b[--i] ^= 0x80; + } + } else { + while (i > 3) { + b[--i] ^= 0x80; + i -= 3; + } } - return i; + return j; } +static struct pcm_feederdesc feeder_sign32_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}, + {FEEDER_FMT, AFMT_U32_BE, AFMT_S32_BE, 0}, + {FEEDER_FMT, AFMT_U32_BE|AFMT_STEREO, AFMT_S32_BE|AFMT_STEREO, 0}, + {FEEDER_FMT, AFMT_S32_BE, AFMT_U32_BE, 0}, + {FEEDER_FMT, AFMT_S32_BE|AFMT_STEREO, AFMT_U32_BE|AFMT_STEREO, 0}, + {0, 0, 0, 0}, +}; +static kobj_method_t feeder_sign32_methods[] = { + KOBJMETHOD(feeder_feed, feed_sign32), + {0, 0} +}; +FEEDER_DECLARE(feeder_sign32, 0, NULL); +/* + * Sign conversion end. + */ -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 kobj_method_t feeder_ulawtou8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } -}; -FEEDER_DECLARE(feeder_ulawtou8, 0, ulaw_to_u8); +/* + * 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; + uint8_t v; -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}, + if (count < 2) + return 0; + j = FEEDER_FEED(f->source, c, b, count & ~1, 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__); + j &= ~1; + i = j; + while (i > 1) { + 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_u8toulaw_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_u8toulaw, 0, u8_to_ulaw); +FEEDER_DECLARE(feeder_endian16, 0, NULL); -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}, -}; -static kobj_method_t feeder_alawtoulaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } -}; -FEEDER_DECLARE(feeder_alawtoulaw, 0, alaw_to_ulaw); +static int +feed_endian24(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + int i, j; + 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}, -}; -static kobj_method_t feeder_ulawtoalaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table), - { 0, 0 } + if (count < 3) + return 0; + j = FEEDER_FEED(f->source, c, b, (count / 3) * 3, source); + 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__); + j -= j % 3; + i = j; + while (i > 2) { + 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_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; + uint8_t l, m; + if (count < 4) + return 0; + j = FEEDER_FEED(f->source, c, b, count & ~3, 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__); + j &= ~3; + i = j; + while (i > 3) { + 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 + */ --- sys/dev/sound/pcm/feeder_rate.c.orig Sun Jan 30 09:00:05 2005 +++ sys/dev/sound/pcm/feeder_rate.c Thu Jul 6 04:47:48 2006 @@ -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,34 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * MAINTAINER: Orion Hodson + * 2006-02-21: + * ========== + * + * Major cleanup and overhaul to remove much redundant codes. + * Highlights: + * 1) Support for signed / unsigned 16, 24 and 32 bit, + * big / little endian, + * 2) Unlimited channels. + * + * 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,200 +60,306 @@ * 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. */ -#ifdef _KERNEL - #include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.10.6.1 2005/01/30 01:00:05 imp 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 */ - -#ifndef RATE_TRACE -#define RATE_TRACE(x...) /* printf(x) */ -#endif - -/*****************************************************************************/ - -/* 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. +/* + * Don't overflow 32bit integer, since everything is done + * within 32bit arithmetic. */ -#define ROUNDHZ 25 -#define FEEDBUFSZ 8192 -#define MINGAIN 92 - -#define RATEMIN 4000 -#define RATEMAX 48000 +#define RATE_FACTOR_MIN 1 +#define RATE_FACTOR_MAX PCM_S24_MAX +#define RATE_FACTOR_SAFE(val) (!((val) < RATE_FACTOR_MIN || \ + (val) > RATE_FACTOR_MAX)) struct feed_rate_info; -typedef int (*rate_convert_method)(struct feed_rate_info *, - uint32_t, uint32_t, int16_t *); +typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, uint8_t *, uint32_t); + +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 limit */ + uint32_t bufsz_init; /* allocated buffer size */ + uint32_t channels; /* total channels */ + uint32_t bps; /* bytes-per-sample */ + uint32_t stray; /* stray bytes */ + uint8_t *buffer; + feed_rate_converter convert; +}; -static int -convert_stereo_up(struct feed_rate_info *info, - uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst); +int feeder_rate_ratemin = FEEDRATE_RATEMIN; +int feeder_rate_ratemax = FEEDRATE_RATEMAX; +int feeder_rate_round = FEEDRATE_ROUNDHZ; + +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_round", &feeder_rate_round); 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_ratemin(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 */ + val = feeder_rate_ratemin; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (RATE_FACTOR_SAFE(val) && val < feeder_rate_ratemax) + feeder_rate_ratemin = val; + else + err = EINVAL; + 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", + "minimum allowable rate"); - rate_convert_method convert; - int16_t buffer[FEEDBUFSZ]; -}; +static int +sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS) +{ + int err, val; -#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)) + val = feeder_rate_ratemax; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (RATE_FACTOR_SAFE(val) && val > feeder_rate_ratemin) + feeder_rate_ratemax = val; + else + err = EINVAL; + 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", + "maximum allowable rate"); -static uint32_t -gcd(uint32_t x, uint32_t y) +static int +sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) { - uint32_t w; + int err, val; + + val = feeder_rate_round; + err = sysctl_handle_int(oidp, &val, sizeof(val), req); + if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX) + err = EINVAL; + else + feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ); + return err; +} +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", + "sample rate converter rounding threshold"); + +#define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ +static uint32_t \ +feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ + uint8_t *dst, uint32_t max) \ +{ \ + uint32_t ret, smpsz, bps, ch, pos, bpos, gx, gy, alpha, distance; \ + int32_t x, y; \ + int i; \ + uint8_t *src, *sx, *sy; \ + \ + ret = 0; \ + alpha = info->alpha; \ + gx = info->gx; \ + gy = info->gy; \ + pos = info->pos; \ + bpos = info->bpos; \ + src = info->buffer + pos; \ + ch = info->channels; \ + bps = info->bps; \ + smpsz = bps * ch; \ + for (;;) { \ + if (alpha < gx) { \ + alpha += gy; \ + pos += smpsz; \ + if (pos == bpos) \ + break; \ + src