08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * CDDL HEADER START
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * The contents of this file are subject to the terms of the
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Common Development and Distribution License (the "License").
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * You may not use this file except in compliance with the License.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * or http://www.opensolaris.org/os/licensing.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * See the License for the specific language governing permissions
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * and limitations under the License.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * When distributing Covered Code, include this CDDL HEADER in each
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * If applicable, add the following below this CDDL HEADER, with the
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * fields enclosed by brackets "[]" replaced with your own identifying
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * information: Portions Copyright [yyyy] [name of copyright owner]
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * CDDL HEADER END
2c30fa4582c5d6c659e059e719c5f6163f7ef1e3Garrett D'Amore * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Purpose: Driver for CMEDIA CM8738 PCI audio controller.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * This file is part of Open Sound System
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Copyright (C) 4Front Technologies 1996-2008.
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * Note: The original 4Front driver had support SPDIF and dual dac
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * options. Dual dac support is probably not terribly useful. SPDIF
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * on the other hand might be quite useful, we just don't have a card
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * that supports it at present. Some variants of the chip are also
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * capable of jack retasking, but we're electing to punt on supporting
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * that as well, for now (we don't have any cards that would benefit
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * from this feature.)
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * Note that surround support requires the use of the second DMA
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * engine, and that the same second DMA engine is the only way one can
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * capture from SPDIF. Rather than support a lot more complexity in
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * the driver, we we will probably just punt on ever supporting
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * capture of SPDIF. (SPDIF playback should be doable, however.)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Adding back support for the advanced features would be an
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * interesting project for someone with access to suitable hardware.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Note that each variant (CMI 8338, 8738-033, -037, -055, and 8768)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * seems to have significant differences in some of the registers.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * While programming these parts for basic stereo is pretty much the
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * same on all parts, doing anything more than that can be
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * sigificantly different for each part.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore 0 /* dma_attr_flags */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amorecmpci_open(void *arg, int flag, uint_t *nframesp, caddr_t *bufp)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* reset channel */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET32(dev, REG_FUNCTRL0, port->fc0_rst_bit);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR32(dev, REG_FUNCTRL0, port->fc0_rst_bit);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* Set 48k 16-bit stereo -- these are just with all bits set. */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET32(dev, REG_FUNCTRL1, port->fc1_rate_mask);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET32(dev, REG_CHFORMAT, port->chformat_mask);
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore PUT16(dev, port->reg_bufsz, (port->bufsz / 4) - 1);
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore PUT16(dev, port->reg_fragsz, (port->bufsz / 4) - 1);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* Analog output */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* Analog capture */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET32(dev, REG_FUNCTRL0, port->fc0_rec_bit);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR32(dev, REG_FUNCTRL0, port->fc0_rec_bit);
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore /* this gives us the offset in dwords */
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore offset = (port->bufsz / 4) - (GET16(dev, port->reg_bufsz) + 1);
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore /* check for wrap - note that the count is given in dwords */
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore count = ((port->bufsz / 4) - port->offset) + offset;
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * convert dwords to frames - unfortunately this requires a
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore ((uint8_t)((((val) * MASK(nbits)) / 100)) << (8 - (nbits)))
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define LEFT(dev, ctl) min(((dev->controls[ctl].value) >> 8), 100)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define RIGHT(dev, ctl) min(((dev->controls[ctl].value) & 0xff), 100)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define MONO(dev, ctl) min(dev->controls[ctl].value, 100)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amorecmpci_setmixer(cmpci_dev_t *dev, uint8_t idx, uint8_t val)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amorecmpci_getmixer(cmpci_dev_t *dev, uint8_t idx)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* reset all mix values */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore OUTMIX_CD_R | OUTMIX_CD_L | OUTMIX_LINE_R | OUTMIX_LINE_L;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore inmix[0] = INMIX_LINE_L | INMIX_CD_L | INMIX_MIC;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore inmix[1] = INMIX_LINE_R | INMIX_CD_R | INMIX_MIC;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* program PCM volume */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* left and right are the same */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_VOICE_LEFT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_VOICE_RIGHT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* lineout/master volume - no separate mute */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_MASTER_LEFT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_MASTER_RIGHT, SCALE(right, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* speaker volume - mute in extension register, but we don't use */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_SPEAKER, SCALE(left, 2));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* mic gain */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_MIC, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* set record mic gain */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* line in */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_LINEIN_LEFT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_LINEIN_RIGHT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_CDDA_LEFT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_setmixer(dev, IDX_CDDA_RIGHT, SCALE(left, 5));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* aux - trickier because it doesn't use regular sbpro mixer */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore PUT8(dev, REG_VAUX, (((left * 15) / 100) << 4) | ((right * 15) / 100));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* maybe enable recording */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if ((left || right) && (recsrcs & (1 << SRC_LINE))) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET8(dev, REG_MIX3, MIX3_RAUXREN | MIX3_RAUXLEN);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR8(dev, REG_MIX3, MIX3_RAUXREN | MIX3_RAUXLEN);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* maybe enable monitoring */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if ((left || right) && (monsrcs & (1 << SRC_AUX))) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR8(dev, REG_MIX3, MIX3_VAUXRM | MIX3_VAUXLM);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET8(dev, REG_MIX3, MIX3_VAUXRM | MIX3_VAUXLM);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* now do the recsrcs */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET8(dev, REG_MIX2, MIX2_WAVEIN_L | MIX2_WAVEIN_R);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR8(dev, REG_MIX2, MIX2_WAVEIN_L | MIX2_WAVEIN_R);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* now the monsrcs */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* micboost */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (dev->controls[CTL_MICBOOST].value != 0) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * We don't bother to check for valid values - a bogus value
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * will give incorrect volumes, but is otherwise harmless.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define PLAYCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define RECCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define MONCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define PCMVOL (PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define MAINVOL (PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore#define RECVOL (RECCTL | AUDIO_CTRL_FLAG_RECVOL)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amorecmpci_alloc_ctrl(cmpci_dev_t *dev, uint32_t num, uint64_t val)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_enum[SRC_LINE] = AUDIO_PORT_LINEIN;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_enum[SRC_MIX] = AUDIO_PORT_STEREOMIX;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_minvalue = (1 << (SRC_MIX + 1)) - 1;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_flags = RECCTL | AUDIO_CTRL_FLAG_MULTI;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_enum[SRC_LINE] = AUDIO_PORT_LINEIN;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_minvalue = ((1 << (SRC_AUX + 1)) - 1);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore desc.acd_flags = MONCTL | AUDIO_CTRL_FLAG_MULTI;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cc->ctrl = audio_dev_add_control(dev->adev, &desc,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_alloc_ctrl(dev, CTL_LINEOUT, 90 | (90 << 8));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_alloc_ctrl(dev, CTL_LINEIN, 64 | (64 << 8));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_alloc_ctrl(dev, CTL_CD, 75 | (75 << 8));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_alloc_ctrl(dev, CTL_AUX, 75 | (75 << 8));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore cmpci_alloc_ctrl(dev, CTL_RECSRCS, (1 << SRC_MIC));
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore for (int i = 0; i < CTL_NUM; i++) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_del_control(dev->controls[i].ctrl);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* Full reset */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* reset all channels */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* disable interrupts and such */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR32(dev, REG_FUNCTRL0, FUNCTRL0_CH0_EN | FUNCTRL0_CH1_EN);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR32(dev, REG_INTCTRL, INTCTRL_CH0_EN | INTCTRL_CH1_EN);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* disable uart, joystick in Function Control Reg1 */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore CLR32(dev, REG_FUNCTRL1, FUNCTRL1_UART_EN | FUNCTRL1_JYSTK_EN);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Set DAC and ADC rates to 48 kHz - note that both rates have
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * all bits set in them, so we can do this with a simple "set".
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore FUNCTRL1_DAC_RATE_48K | FUNCTRL1_ADC_RATE_48K);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* Set 16-bit stereo -- also these are just with all bits set. */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore SET32(dev, REG_CHFORMAT, CHFORMAT_CH0_16ST | CHFORMAT_CH1_16ST);
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amorecmpci_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore static const int map8ch[] = { 0, 1, 4, 5, 2, 3, 6, 7 };
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore return (48000);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore (void) ddi_dma_sync(port->dmah, 0, 0, port->sync_dir);
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore playch = ddi_prop_get_int(DDI_DEV_T_ANY, dev->dip,
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore if ((playch % 2) || (playch < 2) || (playch > dev->maxch)) {
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore "Invalid channels property (%d), resetting to %d",
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore for (int i = 0; i < PORT_MAX; i++) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * Channel 0 is recording channel, unless we are in
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * dual DAC mode. The reason for this is simple --
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * only channel "B" (which I presume to mean channel
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * 1) supports multichannel configuration.
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * However, if we're going to use SPDIF recording,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * then recording *must* occur on channel 1. Yes, the
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore * hardware is "strange".
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore dmaflags = DDI_DMA_READ | DDI_DMA_CONSISTENT;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore port->fc1_rate_mask = FUNCTRL1_ADC_RATE_48K;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore dmaflags = DDI_DMA_WRITE | DDI_DMA_CONSISTENT;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore port->fc1_rate_mask = FUNCTRL1_DAC_RATE_48K;
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * For efficiency, we'd like to have the fragments
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * evenly divisble by 64 bytes. Since frames are
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * already evenly divisble by 4 (16-bit stereo), this
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * is adequate. For a typical configuration (175 Hz
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * requested) this will translate to 166 Hz.
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore port->bufsz = port->nframes * port->nchan * 2;
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (ddi_dma_alloc_handle(dev->dip, &dma_attr, DDI_DMA_DONTWAIT,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(adev, "ch%d: dma hdl alloc failed", i);
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore if (ddi_dma_mem_alloc(port->dmah, port->bufsz, &buf_attr,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore DDI_DMA_CONSISTENT, DDI_DMA_DONTWAIT, NULL, &port->kaddr,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(adev, "ch%d: dma mem allcoc failed", i);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (ddi_dma_addr_bind_handle(port->dmah, NULL, port->kaddr,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore rlen, dmaflags, DDI_DMA_DONTWAIT, NULL, &c, &ccnt) !=
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(adev, "ch%d: dma bind failed", i);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore port->engine = audio_engine_alloc(&cmpci_engine_ops, caps);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(adev, "ch%d: alloc engine failed", i);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_engine_set_private(port->engine, port);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (audio_dev_register(adev) != DDI_SUCCESS) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(adev, "audio_dev_register failed");
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* free up ports, including DMA resources for ports */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore for (int i = 0; i < PORT_MAX; i++) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_remove_engine(dev->adev, port->engine);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(NULL, "pci_config_setup failed");
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore vendor = pci_config_get16(pcih, PCI_CONF_VENID);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore device = pci_config_get16(pcih, PCI_CONF_DEVID);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore ((device != CMEDIA_CM8738) && (device != CMEDIA_CM8338A) &&
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(NULL, "device not recognized");
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* enable IO and Master accesses */
68c47f65208790c466e5e484f2293d3baed71c6aGarrett D'Amore mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if ((adev = audio_dev_alloc(dip, 0)) == NULL) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (ddi_regs_map_setup(dip, 1, &dev->regs, 0, 0, &acc_attr,
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(adev, "can't map registers");
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* setup some initial values */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_set_description(adev, "C-Media PCI Audio");
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * Crazy 8738 detection scheme. Reviewing multiple
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * different open sources gives multiple different
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * answers here. Its unclear how accurate this is.
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * The approach taken here is a bit conservative in
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * assigning multiple channel support, but for users
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * with newer 8768 cards should offer the best
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore * capability.
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore val = GET32(dev, REG_INTCTRL) & INTCTRL_MDL_MASK;
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore if (GET32(dev, REG_CHFORMAT & CHFORMAT_VER_MASK)) {
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore audio_dev_set_version(adev, "CMI-8738-037");
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore audio_dev_set_version(adev, "CMI-8738-033");
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore } else if ((val & INTCTRL_MDL_068) == INTCTRL_MDL_068) {
2be7def9bac9cc5b894988030377b62ee6be9c39Garrett D'Amore dev->softvol = B_TRUE; /* No hardware PCM volume */
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore } else if ((val & INTCTRL_MDL_055) == INTCTRL_MDL_055) {
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore audio_dev_set_version(adev, "CMI-8738-055");
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore } else if ((val & INTCTRL_MDL_039) == INTCTRL_MDL_039) {
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore audio_dev_set_version(adev, "CMI-8738-039");
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore audio_dev_set_version(dev->adev, "CMI-8338");
af145792def4317aeeb9d20f7772b32a35a0161fGarrett D'Amore audio_dev_set_version(dev->adev, "CMI-8338B");
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore audio_dev_warn(dev->adev, "can't init device");
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* wait one millisecond, to give reset a chance to get up */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* disable channels */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore /* disable channels */
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amorecmpci_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amorecmpci_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore if ((dev = ddi_get_driver_private(dip)) == NULL) {
08045defdf65ee890fef6e20510a093a17feb8feGarrett D'Amore 0, /* refcnt */