110463SGarrett.Damore@Sun.COM /*
210463SGarrett.Damore@Sun.COM * CDDL HEADER START
310463SGarrett.Damore@Sun.COM *
410463SGarrett.Damore@Sun.COM * The contents of this file are subject to the terms of the
510463SGarrett.Damore@Sun.COM * Common Development and Distribution License (the "License").
610463SGarrett.Damore@Sun.COM * You may not use this file except in compliance with the License.
710463SGarrett.Damore@Sun.COM *
810463SGarrett.Damore@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910463SGarrett.Damore@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010463SGarrett.Damore@Sun.COM * See the License for the specific language governing permissions
1110463SGarrett.Damore@Sun.COM * and limitations under the License.
1210463SGarrett.Damore@Sun.COM *
1310463SGarrett.Damore@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410463SGarrett.Damore@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510463SGarrett.Damore@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610463SGarrett.Damore@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710463SGarrett.Damore@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810463SGarrett.Damore@Sun.COM *
1910463SGarrett.Damore@Sun.COM * CDDL HEADER END
2010463SGarrett.Damore@Sun.COM */
2110463SGarrett.Damore@Sun.COM
2210463SGarrett.Damore@Sun.COM /*
23*11936Sgdamore@opensolaris.org * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
2410463SGarrett.Damore@Sun.COM * Use is subject to license terms.
2510463SGarrett.Damore@Sun.COM */
2610463SGarrett.Damore@Sun.COM
2710463SGarrett.Damore@Sun.COM /*
2810463SGarrett.Damore@Sun.COM * Purpose: Driver for the Creative P16X AC97 audio controller
2910463SGarrett.Damore@Sun.COM */
3010463SGarrett.Damore@Sun.COM /*
3110463SGarrett.Damore@Sun.COM *
3210463SGarrett.Damore@Sun.COM * Copyright (C) 4Front Technologies 1996-2009.
3310463SGarrett.Damore@Sun.COM *
3410463SGarrett.Damore@Sun.COM * This software is released under CDDL 1.0 source license.
3510463SGarrett.Damore@Sun.COM * See the COPYING file included in the main directory of this source
3610463SGarrett.Damore@Sun.COM * distribution for the license terms and conditions.
3710463SGarrett.Damore@Sun.COM */
3810463SGarrett.Damore@Sun.COM
3910463SGarrett.Damore@Sun.COM #include <sys/types.h>
4010463SGarrett.Damore@Sun.COM #include <sys/modctl.h>
4110463SGarrett.Damore@Sun.COM #include <sys/kmem.h>
4210463SGarrett.Damore@Sun.COM #include <sys/conf.h>
4310463SGarrett.Damore@Sun.COM #include <sys/ddi.h>
4410463SGarrett.Damore@Sun.COM #include <sys/sunddi.h>
4510463SGarrett.Damore@Sun.COM #include <sys/pci.h>
4610463SGarrett.Damore@Sun.COM #include <sys/note.h>
4710463SGarrett.Damore@Sun.COM #include <sys/audio/audio_driver.h>
4810463SGarrett.Damore@Sun.COM #include <sys/audio/ac97.h>
4910463SGarrett.Damore@Sun.COM
5010463SGarrett.Damore@Sun.COM #include "audiop16x.h"
5110463SGarrett.Damore@Sun.COM
5210463SGarrett.Damore@Sun.COM /*
5310463SGarrett.Damore@Sun.COM * These boards use an AC'97 codec, but don't have all of the
5410463SGarrett.Damore@Sun.COM * various outputs that the AC'97 codec can offer. We just
5510463SGarrett.Damore@Sun.COM * suppress them for now.
5610463SGarrett.Damore@Sun.COM */
5710463SGarrett.Damore@Sun.COM static char *p16x_remove_ac97[] = {
5810463SGarrett.Damore@Sun.COM AUDIO_CTRL_ID_BEEP,
5910463SGarrett.Damore@Sun.COM AUDIO_CTRL_ID_VIDEO,
6010463SGarrett.Damore@Sun.COM AUDIO_CTRL_ID_MICSRC,
6110463SGarrett.Damore@Sun.COM AUDIO_CTRL_ID_SPEAKER,
6210463SGarrett.Damore@Sun.COM AUDIO_CTRL_ID_SPKSRC,
6310463SGarrett.Damore@Sun.COM NULL
6410463SGarrett.Damore@Sun.COM };
6510463SGarrett.Damore@Sun.COM
6610463SGarrett.Damore@Sun.COM static struct ddi_device_acc_attr dev_attr = {
6710463SGarrett.Damore@Sun.COM DDI_DEVICE_ATTR_V0,
6810463SGarrett.Damore@Sun.COM DDI_STRUCTURE_LE_ACC,
6910463SGarrett.Damore@Sun.COM DDI_STRICTORDER_ACC
7010463SGarrett.Damore@Sun.COM };
7110463SGarrett.Damore@Sun.COM
7210463SGarrett.Damore@Sun.COM static struct ddi_device_acc_attr buf_attr = {
7310463SGarrett.Damore@Sun.COM DDI_DEVICE_ATTR_V0,
7410463SGarrett.Damore@Sun.COM DDI_NEVERSWAP_ACC,
7510463SGarrett.Damore@Sun.COM DDI_STRICTORDER_ACC
7610463SGarrett.Damore@Sun.COM };
7710463SGarrett.Damore@Sun.COM
7810463SGarrett.Damore@Sun.COM static ddi_dma_attr_t dma_attr_buf = {
7910463SGarrett.Damore@Sun.COM DMA_ATTR_V0, /* version number */
8010463SGarrett.Damore@Sun.COM 0x00000000, /* low DMA address range */
8110463SGarrett.Damore@Sun.COM 0xffffffff, /* high DMA address range */
8210463SGarrett.Damore@Sun.COM 0xfffffffe, /* DMA counter register */
8310463SGarrett.Damore@Sun.COM 4, /* DMA address alignment */
8410463SGarrett.Damore@Sun.COM 0x3c, /* DMA burstsizes */
8510463SGarrett.Damore@Sun.COM 4, /* min effective DMA size */
8610463SGarrett.Damore@Sun.COM 0xffffffff, /* max DMA xfer size */
8710463SGarrett.Damore@Sun.COM 0xffffffff, /* segment boundary */
8810463SGarrett.Damore@Sun.COM 1, /* s/g length */
8910463SGarrett.Damore@Sun.COM 4, /* granularity of device */
9010463SGarrett.Damore@Sun.COM 0 /* Bus specific DMA flags */
9110463SGarrett.Damore@Sun.COM };
9210463SGarrett.Damore@Sun.COM
9310463SGarrett.Damore@Sun.COM static int p16x_attach(dev_info_t *);
9410463SGarrett.Damore@Sun.COM static int p16x_resume(dev_info_t *);
9510463SGarrett.Damore@Sun.COM static int p16x_detach(p16x_dev_t *);
9610463SGarrett.Damore@Sun.COM static int p16x_suspend(p16x_dev_t *);
9710463SGarrett.Damore@Sun.COM
98*11936Sgdamore@opensolaris.org static int p16x_open(void *, int, unsigned *, caddr_t *);
9910463SGarrett.Damore@Sun.COM static void p16x_close(void *);
10010463SGarrett.Damore@Sun.COM static int p16x_start(void *);
10110463SGarrett.Damore@Sun.COM static void p16x_stop(void *);
10210463SGarrett.Damore@Sun.COM static int p16x_format(void *);
10310463SGarrett.Damore@Sun.COM static int p16x_channels(void *);
10410463SGarrett.Damore@Sun.COM static int p16x_rate(void *);
10510463SGarrett.Damore@Sun.COM static uint64_t p16x_count(void *);
10610463SGarrett.Damore@Sun.COM static void p16x_sync(void *, unsigned);
10710463SGarrett.Damore@Sun.COM static void p16x_chinfo(void *, int, unsigned *, unsigned *);
10810463SGarrett.Damore@Sun.COM
10910463SGarrett.Damore@Sun.COM static uint16_t p16x_read_ac97(void *, uint8_t);
11010463SGarrett.Damore@Sun.COM static void p16x_write_ac97(void *, uint8_t, uint16_t);
11110463SGarrett.Damore@Sun.COM static int p16x_alloc_port(p16x_dev_t *, int);
11210463SGarrett.Damore@Sun.COM static void p16x_destroy(p16x_dev_t *);
11310463SGarrett.Damore@Sun.COM static void p16x_hwinit(p16x_dev_t *);
11410463SGarrett.Damore@Sun.COM
11510463SGarrett.Damore@Sun.COM static audio_engine_ops_t p16x_engine_ops = {
11610463SGarrett.Damore@Sun.COM AUDIO_ENGINE_VERSION,
11710463SGarrett.Damore@Sun.COM p16x_open,
11810463SGarrett.Damore@Sun.COM p16x_close,
11910463SGarrett.Damore@Sun.COM p16x_start,
12010463SGarrett.Damore@Sun.COM p16x_stop,
12110463SGarrett.Damore@Sun.COM p16x_count,
12210463SGarrett.Damore@Sun.COM p16x_format,
12310463SGarrett.Damore@Sun.COM p16x_channels,
12410463SGarrett.Damore@Sun.COM p16x_rate,
12510463SGarrett.Damore@Sun.COM p16x_sync,
12611213Sgdamore@opensolaris.org NULL,
12710463SGarrett.Damore@Sun.COM p16x_chinfo,
12811213Sgdamore@opensolaris.org NULL
12910463SGarrett.Damore@Sun.COM };
13010463SGarrett.Damore@Sun.COM
13110463SGarrett.Damore@Sun.COM static unsigned int
read_reg(p16x_dev_t * dev,int reg,int chn)13210463SGarrett.Damore@Sun.COM read_reg(p16x_dev_t *dev, int reg, int chn)
13310463SGarrett.Damore@Sun.COM {
13410463SGarrett.Damore@Sun.COM unsigned int val;
13510463SGarrett.Damore@Sun.COM
136*11936Sgdamore@opensolaris.org mutex_enter(&dev->mutex);
13710463SGarrett.Damore@Sun.COM OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
13810463SGarrett.Damore@Sun.COM val = INL(dev, DR); /* Data */
139*11936Sgdamore@opensolaris.org mutex_exit(&dev->mutex);
14010463SGarrett.Damore@Sun.COM
14110463SGarrett.Damore@Sun.COM return (val);
14210463SGarrett.Damore@Sun.COM }
14310463SGarrett.Damore@Sun.COM
14410463SGarrett.Damore@Sun.COM static void
write_reg(p16x_dev_t * dev,int reg,int chn,unsigned int value)14510463SGarrett.Damore@Sun.COM write_reg(p16x_dev_t *dev, int reg, int chn, unsigned int value)
14610463SGarrett.Damore@Sun.COM {
14710463SGarrett.Damore@Sun.COM
148*11936Sgdamore@opensolaris.org mutex_enter(&dev->mutex);
14910463SGarrett.Damore@Sun.COM OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
15010463SGarrett.Damore@Sun.COM OUTL(dev, value, DR); /* Data */
151*11936Sgdamore@opensolaris.org mutex_exit(&dev->mutex);
152*11936Sgdamore@opensolaris.org }
153*11936Sgdamore@opensolaris.org
154*11936Sgdamore@opensolaris.org static void
set_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)155*11936Sgdamore@opensolaris.org set_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
156*11936Sgdamore@opensolaris.org {
157*11936Sgdamore@opensolaris.org unsigned int val;
158*11936Sgdamore@opensolaris.org mutex_enter(&dev->mutex);
159*11936Sgdamore@opensolaris.org OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
160*11936Sgdamore@opensolaris.org val = INL(dev, DR); /* Data */
161*11936Sgdamore@opensolaris.org val |= mask;
162*11936Sgdamore@opensolaris.org OUTL(dev, val, DR); /* Data */
163*11936Sgdamore@opensolaris.org mutex_exit(&dev->mutex);
164*11936Sgdamore@opensolaris.org }
165*11936Sgdamore@opensolaris.org
166*11936Sgdamore@opensolaris.org static void
clear_reg_bits(p16x_dev_t * dev,int reg,int chn,unsigned int mask)167*11936Sgdamore@opensolaris.org clear_reg_bits(p16x_dev_t *dev, int reg, int chn, unsigned int mask)
168*11936Sgdamore@opensolaris.org {
169*11936Sgdamore@opensolaris.org unsigned int val;
170*11936Sgdamore@opensolaris.org mutex_enter(&dev->mutex);
171*11936Sgdamore@opensolaris.org OUTL(dev, (reg << 16) | (chn & 0xffff), PTR); /* Pointer */
172*11936Sgdamore@opensolaris.org val = INL(dev, DR); /* Data */
173*11936Sgdamore@opensolaris.org val &= ~(mask);
174*11936Sgdamore@opensolaris.org OUTL(dev, val, DR); /* Data */
175*11936Sgdamore@opensolaris.org mutex_exit(&dev->mutex);
17610463SGarrett.Damore@Sun.COM }
17710463SGarrett.Damore@Sun.COM
17810463SGarrett.Damore@Sun.COM static uint16_t
p16x_read_ac97(void * arg,uint8_t index)17910463SGarrett.Damore@Sun.COM p16x_read_ac97(void *arg, uint8_t index)
18010463SGarrett.Damore@Sun.COM {
18110463SGarrett.Damore@Sun.COM p16x_dev_t *dev = arg;
18210463SGarrett.Damore@Sun.COM uint16_t value;
18310463SGarrett.Damore@Sun.COM int i;
18410463SGarrett.Damore@Sun.COM
18510463SGarrett.Damore@Sun.COM OUTB(dev, index, AC97A);
18610463SGarrett.Damore@Sun.COM for (i = 0; i < 10000; i++)
18710463SGarrett.Damore@Sun.COM if (INB(dev, AC97A) & 0x80)
18810463SGarrett.Damore@Sun.COM break;
18910463SGarrett.Damore@Sun.COM value = INW(dev, AC97D);
19010463SGarrett.Damore@Sun.COM return (value);
19110463SGarrett.Damore@Sun.COM }
19210463SGarrett.Damore@Sun.COM
19310463SGarrett.Damore@Sun.COM static void
p16x_write_ac97(void * arg,uint8_t index,uint16_t data)19410463SGarrett.Damore@Sun.COM p16x_write_ac97(void *arg, uint8_t index, uint16_t data)
19510463SGarrett.Damore@Sun.COM {
19610463SGarrett.Damore@Sun.COM p16x_dev_t *dev = arg;
19710463SGarrett.Damore@Sun.COM unsigned int i;
19810463SGarrett.Damore@Sun.COM
19910463SGarrett.Damore@Sun.COM OUTB(dev, index, AC97A);
20010463SGarrett.Damore@Sun.COM for (i = 0; i < 10000; i++)
20110463SGarrett.Damore@Sun.COM if (INB(dev, AC97A) & 0x80)
20210463SGarrett.Damore@Sun.COM break;
20310463SGarrett.Damore@Sun.COM OUTW(dev, data, AC97D);
20410463SGarrett.Damore@Sun.COM }
20510463SGarrett.Damore@Sun.COM
20610463SGarrett.Damore@Sun.COM /*
20710463SGarrett.Damore@Sun.COM * Audio routines
20810463SGarrett.Damore@Sun.COM */
20910463SGarrett.Damore@Sun.COM
210*11936Sgdamore@opensolaris.org int
p16x_open(void * arg,int flag,uint_t * nframes,caddr_t * bufp)211*11936Sgdamore@opensolaris.org p16x_open(void *arg, int flag, uint_t *nframes, caddr_t *bufp)
21210463SGarrett.Damore@Sun.COM {
213*11936Sgdamore@opensolaris.org p16x_port_t *port = arg;
214*11936Sgdamore@opensolaris.org
215*11936Sgdamore@opensolaris.org _NOTE(ARGUNUSED(flag));
216*11936Sgdamore@opensolaris.org
217*11936Sgdamore@opensolaris.org port->count = 0;
218*11936Sgdamore@opensolaris.org *nframes = port->buf_frames;
219*11936Sgdamore@opensolaris.org *bufp = port->buf_kaddr;
220*11936Sgdamore@opensolaris.org
221*11936Sgdamore@opensolaris.org return (0);
222*11936Sgdamore@opensolaris.org }
223*11936Sgdamore@opensolaris.org
224*11936Sgdamore@opensolaris.org void
p16x_close(void * arg)225*11936Sgdamore@opensolaris.org p16x_close(void *arg)
226*11936Sgdamore@opensolaris.org {
227*11936Sgdamore@opensolaris.org _NOTE(ARGUNUSED(arg));
228*11936Sgdamore@opensolaris.org }
229*11936Sgdamore@opensolaris.org
230*11936Sgdamore@opensolaris.org int
p16x_start(void * arg)231*11936Sgdamore@opensolaris.org p16x_start(void *arg)
232*11936Sgdamore@opensolaris.org {
233*11936Sgdamore@opensolaris.org p16x_port_t *port = arg;
23410463SGarrett.Damore@Sun.COM p16x_dev_t *dev = port->dev;
23510463SGarrett.Damore@Sun.COM
236*11936Sgdamore@opensolaris.org port->offset = 0;
23710463SGarrett.Damore@Sun.COM
23810463SGarrett.Damore@Sun.COM if (port->port_num == P16X_REC) {
23910463SGarrett.Damore@Sun.COM write_reg(dev, CRFA, 0, 0);
24010463SGarrett.Damore@Sun.COM write_reg(dev, CRCAV, 0, 0);
24110463SGarrett.Damore@Sun.COM
242*11936Sgdamore@opensolaris.org /* Enable rec channel */
243*11936Sgdamore@opensolaris.org set_reg_bits(dev, SA, 0, 0x100);
24410463SGarrett.Damore@Sun.COM } else {
24510463SGarrett.Damore@Sun.COM for (int i = 0; i < 3; i++) {
24610463SGarrett.Damore@Sun.COM write_reg(dev, PTBA, i, 0);
24710463SGarrett.Damore@Sun.COM write_reg(dev, PTBS, i, 0);
24810463SGarrett.Damore@Sun.COM write_reg(dev, PTCA, i, 0);
24910463SGarrett.Damore@Sun.COM write_reg(dev, PFEA, i, 0);
25010463SGarrett.Damore@Sun.COM write_reg(dev, CPFA, i, 0);
25110463SGarrett.Damore@Sun.COM write_reg(dev, CPCAV, i, 0);
25210463SGarrett.Damore@Sun.COM }
25310463SGarrett.Damore@Sun.COM
254*11936Sgdamore@opensolaris.org /* Enable play channel */
255*11936Sgdamore@opensolaris.org set_reg_bits(dev, SA, 0, 0x7);
25610463SGarrett.Damore@Sun.COM }
25710463SGarrett.Damore@Sun.COM
25810463SGarrett.Damore@Sun.COM return (0);
25910463SGarrett.Damore@Sun.COM }
26010463SGarrett.Damore@Sun.COM
26110463SGarrett.Damore@Sun.COM void
p16x_stop(void * arg)26210463SGarrett.Damore@Sun.COM p16x_stop(void *arg)
26310463SGarrett.Damore@Sun.COM {
26410463SGarrett.Damore@Sun.COM p16x_port_t *port = arg;
26510463SGarrett.Damore@Sun.COM p16x_dev_t *dev = port->dev;
26610463SGarrett.Damore@Sun.COM
267*11936Sgdamore@opensolaris.org if (port->port_num == P16X_REC) {
268*11936Sgdamore@opensolaris.org /* Disable rec channel */
269*11936Sgdamore@opensolaris.org clear_reg_bits(dev, SA, 0, 0x100);
270*11936Sgdamore@opensolaris.org
271*11936Sgdamore@opensolaris.org } else {
272*11936Sgdamore@opensolaris.org /* Disable Play channel */
273*11936Sgdamore@opensolaris.org clear_reg_bits(dev, SA, 0, 0x7);
27410463SGarrett.Damore@Sun.COM }
27510463SGarrett.Damore@Sun.COM }
27610463SGarrett.Damore@Sun.COM
27710463SGarrett.Damore@Sun.COM int
p16x_format(void * arg)27810463SGarrett.Damore@Sun.COM p16x_format(void *arg)
27910463SGarrett.Damore@Sun.COM {
28010463SGarrett.Damore@Sun.COM _NOTE(ARGUNUSED(arg));
28110463SGarrett.Damore@Sun.COM
28210463SGarrett.Damore@Sun.COM return (AUDIO_FORMAT_S16_LE);
28310463SGarrett.Damore@Sun.COM }
28410463SGarrett.Damore@Sun.COM
28510463SGarrett.Damore@Sun.COM int
p16x_channels(void * arg)28610463SGarrett.Damore@Sun.COM p16x_channels(void *arg)
28710463SGarrett.Damore@Sun.COM {
28810463SGarrett.Damore@Sun.COM p16x_port_t *port = arg;
28910463SGarrett.Damore@Sun.COM
29010463SGarrett.Damore@Sun.COM return (port->nchan);
29110463SGarrett.Damore@Sun.COM }
29210463SGarrett.Damore@Sun.COM
29310463SGarrett.Damore@Sun.COM int
p16x_rate(void * arg)29410463SGarrett.Damore@Sun.COM p16x_rate(void *arg)
29510463SGarrett.Damore@Sun.COM {
29610463SGarrett.Damore@Sun.COM _NOTE(ARGUNUSED(arg));
29710463SGarrett.Damore@Sun.COM
29810463SGarrett.Damore@Sun.COM return (48000);
29910463SGarrett.Damore@Sun.COM }
30010463SGarrett.Damore@Sun.COM
30110463SGarrett.Damore@Sun.COM void
p16x_sync(void * arg,unsigned nframes)30210463SGarrett.Damore@Sun.COM p16x_sync(void *arg, unsigned nframes)
30310463SGarrett.Damore@Sun.COM {
30410463SGarrett.Damore@Sun.COM p16x_port_t *port = arg;
30510463SGarrett.Damore@Sun.COM _NOTE(ARGUNUSED(nframes));
30610463SGarrett.Damore@Sun.COM
30710463SGarrett.Damore@Sun.COM (void) ddi_dma_sync(port->buf_dmah, 0, 0, port->syncdir);
30810463SGarrett.Damore@Sun.COM }
30910463SGarrett.Damore@Sun.COM
31010463SGarrett.Damore@Sun.COM uint64_t
p16x_count(void * arg)31110463SGarrett.Damore@Sun.COM p16x_count(void *arg)
31210463SGarrett.Damore@Sun.COM {
31310463SGarrett.Damore@Sun.COM p16x_port_t *port = arg;
31410463SGarrett.Damore@Sun.COM p16x_dev_t *dev = port->dev;
31510463SGarrett.Damore@Sun.COM uint64_t val;
316*11936Sgdamore@opensolaris.org uint32_t offset, n;
31710463SGarrett.Damore@Sun.COM
318*11936Sgdamore@opensolaris.org if (port->port_num == P16X_PLAY) {
319*11936Sgdamore@opensolaris.org offset = read_reg(dev, CPFA, 0);
320*11936Sgdamore@opensolaris.org } else {
321*11936Sgdamore@opensolaris.org offset = read_reg(dev, CRFA, 0);
322*11936Sgdamore@opensolaris.org }
323*11936Sgdamore@opensolaris.org
324*11936Sgdamore@opensolaris.org /* get the offset, and switch to frames */
325*11936Sgdamore@opensolaris.org offset /= (2 * sizeof (uint16_t));
326*11936Sgdamore@opensolaris.org
327*11936Sgdamore@opensolaris.org if (offset >= port->offset) {
328*11936Sgdamore@opensolaris.org n = offset - port->offset;
329*11936Sgdamore@opensolaris.org } else {
330*11936Sgdamore@opensolaris.org n = offset + (port->buf_frames - port->offset);
331*11936Sgdamore@opensolaris.org }
332*11936Sgdamore@opensolaris.org port->offset = offset;
333*11936Sgdamore@opensolaris.org port->count += n;
33410463SGarrett.Damore@Sun.COM val = port->count;
33510463SGarrett.Damore@Sun.COM
33610463SGarrett.Damore@Sun.COM return (val);
33710463SGarrett.Damore@Sun.COM }
33810463SGarrett.Damore@Sun.COM
33910463SGarrett.Damore@Sun.COM static void
p16x_chinfo(void * arg,int chan,unsigned * offset,unsigned * incr)34010463SGarrett.Damore@Sun.COM p16x_chinfo(void *arg, int chan, unsigned *offset, unsigned *incr)
34110463SGarrett.Damore@Sun.COM {
34210463SGarrett.Damore@Sun.COM p16x_port_t *port = arg;
34310463SGarrett.Damore@Sun.COM unsigned mult;
34410463SGarrett.Damore@Sun.COM
34510463SGarrett.Damore@Sun.COM if (port->port_num == P16X_PLAY) {
34610463SGarrett.Damore@Sun.COM switch (chan) {
34710463SGarrett.Damore@Sun.COM case 0: /* left front */
34810463SGarrett.Damore@Sun.COM case 1: /* right front */
34910463SGarrett.Damore@Sun.COM mult = 0;
35010463SGarrett.Damore@Sun.COM break;
35110463SGarrett.Damore@Sun.COM case 2: /* center */
35210463SGarrett.Damore@Sun.COM case 3: /* lfe */
35310463SGarrett.Damore@Sun.COM mult = 2;
35410463SGarrett.Damore@Sun.COM break;
35510463SGarrett.Damore@Sun.COM case 4: /* left surround */
35610463SGarrett.Damore@Sun.COM case 5: /* right surround */
35710463SGarrett.Damore@Sun.COM mult = 1;
35810463SGarrett.Damore@Sun.COM break;
35910463SGarrett.Damore@Sun.COM }
36010463SGarrett.Damore@Sun.COM *offset = (port->buf_frames * 2 * mult) + (chan % 2);
36110463SGarrett.Damore@Sun.COM *incr = 2;
36210463SGarrett.Damore@Sun.COM } else {
36310463SGarrett.Damore@Sun.COM *offset = chan;
36410463SGarrett.Damore@Sun.COM *incr = 2;
36510463SGarrett.Damore@Sun.COM }
36610463SGarrett.Damore@Sun.COM }
36710463SGarrett.Damore@Sun.COM
36810463SGarrett.Damore@Sun.COM /* private implementation bits */
36910463SGarrett.Damore@Sun.COM
37010463SGarrett.Damore@Sun.COM int
p16x_alloc_port(p16x_dev_t * dev,int num)37110463SGarrett.Damore@Sun.COM p16x_alloc_port(p16x_dev_t *dev, int num)
37210463SGarrett.Damore@Sun.COM {
37310463SGarrett.Damore@Sun.COM p16x_port_t *port;
37410463SGarrett.Damore@Sun.COM size_t len;
37510463SGarrett.Damore@Sun.COM ddi_dma_cookie_t cookie;
37610463SGarrett.Damore@Sun.COM uint_t count;
37710463SGarrett.Damore@Sun.COM int dir;
37810463SGarrett.Damore@Sun.COM unsigned caps;
37910463SGarrett.Damore@Sun.COM audio_dev_t *adev;
38010463SGarrett.Damore@Sun.COM
38110463SGarrett.Damore@Sun.COM adev = dev->adev;
38210463SGarrett.Damore@Sun.COM port = kmem_zalloc(sizeof (*port), KM_SLEEP);
38310463SGarrett.Damore@Sun.COM dev->port[num] = port;
38410463SGarrett.Damore@Sun.COM port->dev = dev;
38510463SGarrett.Damore@Sun.COM
38610463SGarrett.Damore@Sun.COM switch (num) {
38710463SGarrett.Damore@Sun.COM case P16X_REC:
38810463SGarrett.Damore@Sun.COM port->syncdir = DDI_DMA_SYNC_FORKERNEL;
38910463SGarrett.Damore@Sun.COM caps = ENGINE_INPUT_CAP;
39010463SGarrett.Damore@Sun.COM dir = DDI_DMA_READ;
39110463SGarrett.Damore@Sun.COM port->port_num = P16X_REC;
39210463SGarrett.Damore@Sun.COM port->nchan = 2;
39310463SGarrett.Damore@Sun.COM break;
39410463SGarrett.Damore@Sun.COM case P16X_PLAY:
39510463SGarrett.Damore@Sun.COM port->syncdir = DDI_DMA_SYNC_FORDEV;
39610463SGarrett.Damore@Sun.COM caps = ENGINE_OUTPUT_CAP;
39710463SGarrett.Damore@Sun.COM dir = DDI_DMA_WRITE;
39810463SGarrett.Damore@Sun.COM port->port_num = P16X_PLAY;
39910463SGarrett.Damore@Sun.COM port->nchan = 6;
40010463SGarrett.Damore@Sun.COM break;
40110463SGarrett.Damore@Sun.COM default:
40210463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
40310463SGarrett.Damore@Sun.COM }
40410463SGarrett.Damore@Sun.COM
40510463SGarrett.Damore@Sun.COM /*
406*11936Sgdamore@opensolaris.org * NB: The device operates in pairs of dwords at a time, for
407*11936Sgdamore@opensolaris.org * performance reasons. So make sure that our buffer is
408*11936Sgdamore@opensolaris.org * arranged as a whole number of these. The value below gives
409*11936Sgdamore@opensolaris.org * a reasonably large buffer so we can support a deep
410*11936Sgdamore@opensolaris.org * playahead if we need to (and we should avoid input
411*11936Sgdamore@opensolaris.org * overruns.)
41210463SGarrett.Damore@Sun.COM */
413*11936Sgdamore@opensolaris.org port->buf_frames = 4096;
414*11936Sgdamore@opensolaris.org port->buf_size = port->buf_frames * port->nchan * sizeof (uint16_t);
41510463SGarrett.Damore@Sun.COM
41610463SGarrett.Damore@Sun.COM /* now allocate buffers */
41710463SGarrett.Damore@Sun.COM if (ddi_dma_alloc_handle(dev->dip, &dma_attr_buf, DDI_DMA_SLEEP, NULL,
41810463SGarrett.Damore@Sun.COM &port->buf_dmah) != DDI_SUCCESS) {
41910463SGarrett.Damore@Sun.COM audio_dev_warn(adev, "failed to allocate BUF handle");
42010463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
42110463SGarrett.Damore@Sun.COM }
42210463SGarrett.Damore@Sun.COM
42310463SGarrett.Damore@Sun.COM if (ddi_dma_mem_alloc(port->buf_dmah, port->buf_size,
42410463SGarrett.Damore@Sun.COM &buf_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
42510463SGarrett.Damore@Sun.COM &port->buf_kaddr, &len, &port->buf_acch) != DDI_SUCCESS) {
42610463SGarrett.Damore@Sun.COM audio_dev_warn(adev, "failed to allocate BUF memory");
42710463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
42810463SGarrett.Damore@Sun.COM }
42910463SGarrett.Damore@Sun.COM
43010463SGarrett.Damore@Sun.COM if (ddi_dma_addr_bind_handle(port->buf_dmah, NULL, port->buf_kaddr,
43110463SGarrett.Damore@Sun.COM len, DDI_DMA_CONSISTENT | dir, DDI_DMA_SLEEP, NULL, &cookie,
43210463SGarrett.Damore@Sun.COM &count) != DDI_SUCCESS) {
43310463SGarrett.Damore@Sun.COM audio_dev_warn(adev, "failed binding BUF DMA handle");
43410463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
43510463SGarrett.Damore@Sun.COM }
43610463SGarrett.Damore@Sun.COM port->buf_paddr = cookie.dmac_address;
43710463SGarrett.Damore@Sun.COM
43810463SGarrett.Damore@Sun.COM port->engine = audio_engine_alloc(&p16x_engine_ops, caps);
43910463SGarrett.Damore@Sun.COM if (port->engine == NULL) {
44010463SGarrett.Damore@Sun.COM audio_dev_warn(adev, "audio_engine_alloc failed");
44110463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
44210463SGarrett.Damore@Sun.COM }
44310463SGarrett.Damore@Sun.COM
44410463SGarrett.Damore@Sun.COM audio_engine_set_private(port->engine, port);
44510463SGarrett.Damore@Sun.COM audio_dev_add_engine(adev, port->engine);
44610463SGarrett.Damore@Sun.COM
44710463SGarrett.Damore@Sun.COM return (DDI_SUCCESS);
44810463SGarrett.Damore@Sun.COM }
44910463SGarrett.Damore@Sun.COM
45010463SGarrett.Damore@Sun.COM void
p16x_destroy(p16x_dev_t * dev)45110463SGarrett.Damore@Sun.COM p16x_destroy(p16x_dev_t *dev)
45210463SGarrett.Damore@Sun.COM {
453*11936Sgdamore@opensolaris.org mutex_destroy(&dev->mutex);
45410463SGarrett.Damore@Sun.COM
45510463SGarrett.Damore@Sun.COM for (int i = 0; i < P16X_NUM_PORT; i++) {
45610463SGarrett.Damore@Sun.COM p16x_port_t *port = dev->port[i];
45710463SGarrett.Damore@Sun.COM if (!port)
45810463SGarrett.Damore@Sun.COM continue;
45910463SGarrett.Damore@Sun.COM if (port->engine) {
46010463SGarrett.Damore@Sun.COM audio_dev_remove_engine(dev->adev, port->engine);
46110463SGarrett.Damore@Sun.COM audio_engine_free(port->engine);
46210463SGarrett.Damore@Sun.COM }
46310463SGarrett.Damore@Sun.COM if (port->buf_paddr) {
46410463SGarrett.Damore@Sun.COM (void) ddi_dma_unbind_handle(port->buf_dmah);
46510463SGarrett.Damore@Sun.COM }
46610463SGarrett.Damore@Sun.COM if (port->buf_acch) {
46710463SGarrett.Damore@Sun.COM ddi_dma_mem_free(&port->buf_acch);
46810463SGarrett.Damore@Sun.COM }
46910463SGarrett.Damore@Sun.COM if (port->buf_dmah) {
47010463SGarrett.Damore@Sun.COM ddi_dma_free_handle(&port->buf_dmah);
47110463SGarrett.Damore@Sun.COM }
47210463SGarrett.Damore@Sun.COM kmem_free(port, sizeof (*port));
47310463SGarrett.Damore@Sun.COM }
47410463SGarrett.Damore@Sun.COM
47510463SGarrett.Damore@Sun.COM if (dev->ac97 != NULL) {
47610463SGarrett.Damore@Sun.COM ac97_free(dev->ac97);
47710463SGarrett.Damore@Sun.COM }
47810463SGarrett.Damore@Sun.COM if (dev->adev != NULL) {
47910463SGarrett.Damore@Sun.COM audio_dev_free(dev->adev);
48010463SGarrett.Damore@Sun.COM }
48110463SGarrett.Damore@Sun.COM if (dev->regsh != NULL) {
48210463SGarrett.Damore@Sun.COM ddi_regs_map_free(&dev->regsh);
48310463SGarrett.Damore@Sun.COM }
48410463SGarrett.Damore@Sun.COM if (dev->pcih != NULL) {
48510463SGarrett.Damore@Sun.COM pci_config_teardown(&dev->pcih);
48610463SGarrett.Damore@Sun.COM }
48710463SGarrett.Damore@Sun.COM kmem_free(dev, sizeof (*dev));
48810463SGarrett.Damore@Sun.COM }
48910463SGarrett.Damore@Sun.COM
49010463SGarrett.Damore@Sun.COM void
p16x_hwinit(p16x_dev_t * dev)49110463SGarrett.Damore@Sun.COM p16x_hwinit(p16x_dev_t *dev)
49210463SGarrett.Damore@Sun.COM {
49310463SGarrett.Damore@Sun.COM p16x_port_t *port;
49410463SGarrett.Damore@Sun.COM uint32_t paddr;
49510463SGarrett.Damore@Sun.COM uint32_t chunksz;
49610463SGarrett.Damore@Sun.COM int i;
49710463SGarrett.Damore@Sun.COM
49810463SGarrett.Damore@Sun.COM for (i = 0; i < 3; i++) {
49910463SGarrett.Damore@Sun.COM write_reg(dev, PTBA, i, 0);
50010463SGarrett.Damore@Sun.COM write_reg(dev, PTBS, i, 0);
50110463SGarrett.Damore@Sun.COM write_reg(dev, PTCA, i, 0);
50210463SGarrett.Damore@Sun.COM write_reg(dev, PFEA, i, 0);
50310463SGarrett.Damore@Sun.COM write_reg(dev, CPFA, i, 0);
50410463SGarrett.Damore@Sun.COM write_reg(dev, CPCAV, i, 0);
50510463SGarrett.Damore@Sun.COM write_reg(dev, CRFA, i, 0);
50610463SGarrett.Damore@Sun.COM write_reg(dev, CRCAV, i, 0);
50710463SGarrett.Damore@Sun.COM }
50810463SGarrett.Damore@Sun.COM write_reg(dev, SCS0, 0, 0x02108504);
50910463SGarrett.Damore@Sun.COM write_reg(dev, SCS1, 0, 0x02108504);
51010463SGarrett.Damore@Sun.COM write_reg(dev, SCS2, 0, 0x02108504);
51110463SGarrett.Damore@Sun.COM
51210463SGarrett.Damore@Sun.COM /* set the spdif/analog combo jack to analog out */
51310463SGarrett.Damore@Sun.COM write_reg(dev, SPC, 0, 0x00000700);
51410463SGarrett.Damore@Sun.COM write_reg(dev, EA_aux, 0, 0x0001003f);
51510463SGarrett.Damore@Sun.COM
51610463SGarrett.Damore@Sun.COM port = dev->port[P16X_REC];
51710463SGarrett.Damore@Sun.COM /* Set physical address of the DMA buffer */
51810463SGarrett.Damore@Sun.COM write_reg(dev, RFBA, 0, port->buf_paddr);
51910463SGarrett.Damore@Sun.COM write_reg(dev, RFBS, 0, (port->buf_size) << 16);
52010463SGarrett.Damore@Sun.COM
52110463SGarrett.Damore@Sun.COM /* Set physical address of the DMA buffer */
52210463SGarrett.Damore@Sun.COM port = dev->port[P16X_PLAY];
52310463SGarrett.Damore@Sun.COM paddr = port->buf_paddr;
52410463SGarrett.Damore@Sun.COM chunksz = port->buf_frames * 4;
52510463SGarrett.Damore@Sun.COM write_reg(dev, PFBA, 0, paddr);
52610463SGarrett.Damore@Sun.COM write_reg(dev, PFBS, 0, chunksz << 16);
52710463SGarrett.Damore@Sun.COM paddr += chunksz;
52810463SGarrett.Damore@Sun.COM write_reg(dev, PFBA, 1, paddr);
52910463SGarrett.Damore@Sun.COM write_reg(dev, PFBS, 1, chunksz << 16);
53010463SGarrett.Damore@Sun.COM paddr += chunksz;
53110463SGarrett.Damore@Sun.COM write_reg(dev, PFBA, 2, paddr);
53210463SGarrett.Damore@Sun.COM write_reg(dev, PFBS, 2, chunksz << 16);
53310463SGarrett.Damore@Sun.COM
53410463SGarrett.Damore@Sun.COM OUTL(dev, 0x1080, GPIO); /* GPIO */
53510463SGarrett.Damore@Sun.COM /* Clear any pending interrupts */
53610463SGarrett.Damore@Sun.COM OUTL(dev, INTR_ALL, IP);
53710463SGarrett.Damore@Sun.COM OUTL(dev, 0, IE);
53810463SGarrett.Damore@Sun.COM OUTL(dev, 0x9, HC); /* Enable audio */
53910463SGarrett.Damore@Sun.COM }
54010463SGarrett.Damore@Sun.COM
54110463SGarrett.Damore@Sun.COM int
p16x_attach(dev_info_t * dip)54210463SGarrett.Damore@Sun.COM p16x_attach(dev_info_t *dip)
54310463SGarrett.Damore@Sun.COM {
54410463SGarrett.Damore@Sun.COM uint16_t vendor, device;
54510463SGarrett.Damore@Sun.COM p16x_dev_t *dev;
54610463SGarrett.Damore@Sun.COM ddi_acc_handle_t pcih;
54710463SGarrett.Damore@Sun.COM
54810463SGarrett.Damore@Sun.COM dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
54910463SGarrett.Damore@Sun.COM dev->dip = dip;
55010463SGarrett.Damore@Sun.COM ddi_set_driver_private(dip, dev);
55110463SGarrett.Damore@Sun.COM
552*11936Sgdamore@opensolaris.org mutex_init(&dev->mutex, NULL, MUTEX_DRIVER, NULL);
55310463SGarrett.Damore@Sun.COM
55410463SGarrett.Damore@Sun.COM if ((dev->adev = audio_dev_alloc(dip, 0)) == NULL) {
55510463SGarrett.Damore@Sun.COM cmn_err(CE_WARN, "audio_dev_alloc failed");
55610463SGarrett.Damore@Sun.COM goto error;
55710463SGarrett.Damore@Sun.COM }
55810463SGarrett.Damore@Sun.COM
55910463SGarrett.Damore@Sun.COM if (pci_config_setup(dip, &pcih) != DDI_SUCCESS) {
56010463SGarrett.Damore@Sun.COM audio_dev_warn(dev->adev, "pci_config_setup failed");
56110463SGarrett.Damore@Sun.COM goto error;
56210463SGarrett.Damore@Sun.COM }
56310463SGarrett.Damore@Sun.COM dev->pcih = pcih;
56410463SGarrett.Damore@Sun.COM
56510463SGarrett.Damore@Sun.COM vendor = pci_config_get16(pcih, PCI_CONF_VENID);
56610463SGarrett.Damore@Sun.COM device = pci_config_get16(pcih, PCI_CONF_DEVID);
56710463SGarrett.Damore@Sun.COM if (vendor != CREATIVE_VENDOR_ID ||
56810463SGarrett.Damore@Sun.COM device != SB_P16X_ID) {
56910463SGarrett.Damore@Sun.COM audio_dev_warn(dev->adev, "Hardware not recognized "
57010463SGarrett.Damore@Sun.COM "(vendor=%x, dev=%x)", vendor, device);
57110463SGarrett.Damore@Sun.COM goto error;
57210463SGarrett.Damore@Sun.COM }
57310463SGarrett.Damore@Sun.COM
57410463SGarrett.Damore@Sun.COM /* set PCI command register */
57510463SGarrett.Damore@Sun.COM pci_config_put16(pcih, PCI_CONF_COMM,
57610463SGarrett.Damore@Sun.COM pci_config_get16(pcih, PCI_CONF_COMM) |
57710463SGarrett.Damore@Sun.COM PCI_COMM_MAE | PCI_COMM_IO);
57810463SGarrett.Damore@Sun.COM
57910463SGarrett.Damore@Sun.COM
58010463SGarrett.Damore@Sun.COM if ((ddi_regs_map_setup(dip, 1, &dev->base, 0, 0, &dev_attr,
58110463SGarrett.Damore@Sun.COM &dev->regsh)) != DDI_SUCCESS) {
58210463SGarrett.Damore@Sun.COM audio_dev_warn(dev->adev, "failed to map registers");
58310463SGarrett.Damore@Sun.COM goto error;
58410463SGarrett.Damore@Sun.COM }
58510463SGarrett.Damore@Sun.COM
58610463SGarrett.Damore@Sun.COM audio_dev_set_description(dev->adev, "Creative Sound Blaster Live!");
58710463SGarrett.Damore@Sun.COM audio_dev_set_version(dev->adev, "SBO200");
58810463SGarrett.Damore@Sun.COM
58910463SGarrett.Damore@Sun.COM if ((p16x_alloc_port(dev, P16X_PLAY) != DDI_SUCCESS) ||
59010463SGarrett.Damore@Sun.COM (p16x_alloc_port(dev, P16X_REC) != DDI_SUCCESS)) {
59110463SGarrett.Damore@Sun.COM goto error;
59210463SGarrett.Damore@Sun.COM }
59310463SGarrett.Damore@Sun.COM
59410463SGarrett.Damore@Sun.COM p16x_hwinit(dev);
59510463SGarrett.Damore@Sun.COM
59610463SGarrett.Damore@Sun.COM dev->ac97 = ac97_allocate(dev->adev, dip,
59710463SGarrett.Damore@Sun.COM p16x_read_ac97, p16x_write_ac97, dev);
59810463SGarrett.Damore@Sun.COM if (dev->ac97 == NULL) {
59910463SGarrett.Damore@Sun.COM audio_dev_warn(dev->adev, "failed to allocate ac97 handle");
60010463SGarrett.Damore@Sun.COM goto error;
60110463SGarrett.Damore@Sun.COM }
60210463SGarrett.Damore@Sun.COM
60310463SGarrett.Damore@Sun.COM ac97_probe_controls(dev->ac97);
60410463SGarrett.Damore@Sun.COM
60510463SGarrett.Damore@Sun.COM /* remove the AC'97 controls we don't want to expose */
60610463SGarrett.Damore@Sun.COM for (int i = 0; p16x_remove_ac97[i]; i++) {
60710463SGarrett.Damore@Sun.COM ac97_ctrl_t *ctrl;
60810463SGarrett.Damore@Sun.COM ctrl = ac97_control_find(dev->ac97, p16x_remove_ac97[i]);
60910463SGarrett.Damore@Sun.COM if (ctrl != NULL) {
61010463SGarrett.Damore@Sun.COM ac97_control_unregister(ctrl);
61110463SGarrett.Damore@Sun.COM }
61210463SGarrett.Damore@Sun.COM }
61310463SGarrett.Damore@Sun.COM
61410463SGarrett.Damore@Sun.COM ac97_register_controls(dev->ac97);
61510463SGarrett.Damore@Sun.COM
61610463SGarrett.Damore@Sun.COM if (audio_dev_register(dev->adev) != DDI_SUCCESS) {
61710463SGarrett.Damore@Sun.COM audio_dev_warn(dev->adev, "unable to register with framework");
61810463SGarrett.Damore@Sun.COM goto error;
61910463SGarrett.Damore@Sun.COM }
62010463SGarrett.Damore@Sun.COM
62110463SGarrett.Damore@Sun.COM ddi_report_dev(dip);
62210463SGarrett.Damore@Sun.COM
62310463SGarrett.Damore@Sun.COM return (DDI_SUCCESS);
62410463SGarrett.Damore@Sun.COM
62510463SGarrett.Damore@Sun.COM error:
62610463SGarrett.Damore@Sun.COM p16x_destroy(dev);
62710463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
62810463SGarrett.Damore@Sun.COM }
62910463SGarrett.Damore@Sun.COM
63010463SGarrett.Damore@Sun.COM int
p16x_resume(dev_info_t * dip)63110463SGarrett.Damore@Sun.COM p16x_resume(dev_info_t *dip)
63210463SGarrett.Damore@Sun.COM {
63310463SGarrett.Damore@Sun.COM p16x_dev_t *dev;
63410463SGarrett.Damore@Sun.COM
63510463SGarrett.Damore@Sun.COM dev = ddi_get_driver_private(dip);
63610463SGarrett.Damore@Sun.COM
63710463SGarrett.Damore@Sun.COM p16x_hwinit(dev);
63810463SGarrett.Damore@Sun.COM
639*11936Sgdamore@opensolaris.org ac97_reset(dev->ac97);
64010463SGarrett.Damore@Sun.COM
641*11936Sgdamore@opensolaris.org audio_dev_resume(dev->adev);
64210463SGarrett.Damore@Sun.COM
64310463SGarrett.Damore@Sun.COM return (DDI_SUCCESS);
64410463SGarrett.Damore@Sun.COM }
64510463SGarrett.Damore@Sun.COM
64610463SGarrett.Damore@Sun.COM int
p16x_detach(p16x_dev_t * dev)64710463SGarrett.Damore@Sun.COM p16x_detach(p16x_dev_t *dev)
64810463SGarrett.Damore@Sun.COM {
64910463SGarrett.Damore@Sun.COM if (audio_dev_unregister(dev->adev) != DDI_SUCCESS)
65010463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
65110463SGarrett.Damore@Sun.COM
65210463SGarrett.Damore@Sun.COM p16x_destroy(dev);
65310463SGarrett.Damore@Sun.COM return (DDI_SUCCESS);
65410463SGarrett.Damore@Sun.COM }
65510463SGarrett.Damore@Sun.COM
65610463SGarrett.Damore@Sun.COM int
p16x_suspend(p16x_dev_t * dev)65710463SGarrett.Damore@Sun.COM p16x_suspend(p16x_dev_t *dev)
65810463SGarrett.Damore@Sun.COM {
659*11936Sgdamore@opensolaris.org audio_dev_suspend(dev->adev);
66010463SGarrett.Damore@Sun.COM
66110463SGarrett.Damore@Sun.COM return (DDI_SUCCESS);
66210463SGarrett.Damore@Sun.COM }
66310463SGarrett.Damore@Sun.COM
66410463SGarrett.Damore@Sun.COM static int p16x_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
66510463SGarrett.Damore@Sun.COM static int p16x_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
66610463SGarrett.Damore@Sun.COM static int p16x_ddi_quiesce(dev_info_t *);
66710463SGarrett.Damore@Sun.COM
66810463SGarrett.Damore@Sun.COM static struct dev_ops p16x_dev_ops = {
66910463SGarrett.Damore@Sun.COM DEVO_REV, /* rev */
67010463SGarrett.Damore@Sun.COM 0, /* refcnt */
67110463SGarrett.Damore@Sun.COM NULL, /* getinfo */
67210463SGarrett.Damore@Sun.COM nulldev, /* identify */
67310463SGarrett.Damore@Sun.COM nulldev, /* probe */
67410463SGarrett.Damore@Sun.COM p16x_ddi_attach, /* attach */
67510463SGarrett.Damore@Sun.COM p16x_ddi_detach, /* detach */
67610463SGarrett.Damore@Sun.COM nodev, /* reset */
67710463SGarrett.Damore@Sun.COM NULL, /* cb_ops */
67810463SGarrett.Damore@Sun.COM NULL, /* bus_ops */
67910463SGarrett.Damore@Sun.COM NULL, /* power */
68010463SGarrett.Damore@Sun.COM p16x_ddi_quiesce, /* quiesce */
68110463SGarrett.Damore@Sun.COM };
68210463SGarrett.Damore@Sun.COM
68310463SGarrett.Damore@Sun.COM static struct modldrv p16x_modldrv = {
68410463SGarrett.Damore@Sun.COM &mod_driverops, /* drv_modops */
68510463SGarrett.Damore@Sun.COM "Creative P16X Audio", /* linkinfo */
68610463SGarrett.Damore@Sun.COM &p16x_dev_ops, /* dev_ops */
68710463SGarrett.Damore@Sun.COM };
68810463SGarrett.Damore@Sun.COM
68910463SGarrett.Damore@Sun.COM static struct modlinkage modlinkage = {
69010463SGarrett.Damore@Sun.COM MODREV_1,
69110463SGarrett.Damore@Sun.COM { &p16x_modldrv, NULL }
69210463SGarrett.Damore@Sun.COM };
69310463SGarrett.Damore@Sun.COM
69410463SGarrett.Damore@Sun.COM int
_init(void)69510463SGarrett.Damore@Sun.COM _init(void)
69610463SGarrett.Damore@Sun.COM {
69710463SGarrett.Damore@Sun.COM int rv;
69810463SGarrett.Damore@Sun.COM
69910463SGarrett.Damore@Sun.COM audio_init_ops(&p16x_dev_ops, P16X_NAME);
70010463SGarrett.Damore@Sun.COM if ((rv = mod_install(&modlinkage)) != 0) {
70110463SGarrett.Damore@Sun.COM audio_fini_ops(&p16x_dev_ops);
70210463SGarrett.Damore@Sun.COM }
70310463SGarrett.Damore@Sun.COM return (rv);
70410463SGarrett.Damore@Sun.COM }
70510463SGarrett.Damore@Sun.COM
70610463SGarrett.Damore@Sun.COM int
_fini(void)70710463SGarrett.Damore@Sun.COM _fini(void)
70810463SGarrett.Damore@Sun.COM {
70910463SGarrett.Damore@Sun.COM int rv;
71010463SGarrett.Damore@Sun.COM
71110463SGarrett.Damore@Sun.COM if ((rv = mod_remove(&modlinkage)) == 0) {
71210463SGarrett.Damore@Sun.COM audio_fini_ops(&p16x_dev_ops);
71310463SGarrett.Damore@Sun.COM }
71410463SGarrett.Damore@Sun.COM return (rv);
71510463SGarrett.Damore@Sun.COM }
71610463SGarrett.Damore@Sun.COM
71710463SGarrett.Damore@Sun.COM int
_info(struct modinfo * modinfop)71810463SGarrett.Damore@Sun.COM _info(struct modinfo *modinfop)
71910463SGarrett.Damore@Sun.COM {
72010463SGarrett.Damore@Sun.COM return (mod_info(&modlinkage, modinfop));
72110463SGarrett.Damore@Sun.COM }
72210463SGarrett.Damore@Sun.COM
72310463SGarrett.Damore@Sun.COM int
p16x_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)72410463SGarrett.Damore@Sun.COM p16x_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
72510463SGarrett.Damore@Sun.COM {
72610463SGarrett.Damore@Sun.COM switch (cmd) {
72710463SGarrett.Damore@Sun.COM case DDI_ATTACH:
72810463SGarrett.Damore@Sun.COM return (p16x_attach(dip));
72910463SGarrett.Damore@Sun.COM
73010463SGarrett.Damore@Sun.COM case DDI_RESUME:
73110463SGarrett.Damore@Sun.COM return (p16x_resume(dip));
73210463SGarrett.Damore@Sun.COM
73310463SGarrett.Damore@Sun.COM default:
73410463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
73510463SGarrett.Damore@Sun.COM }
73610463SGarrett.Damore@Sun.COM }
73710463SGarrett.Damore@Sun.COM
73810463SGarrett.Damore@Sun.COM int
p16x_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)73910463SGarrett.Damore@Sun.COM p16x_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
74010463SGarrett.Damore@Sun.COM {
74110463SGarrett.Damore@Sun.COM p16x_dev_t *dev;
74210463SGarrett.Damore@Sun.COM
74310463SGarrett.Damore@Sun.COM dev = ddi_get_driver_private(dip);
74410463SGarrett.Damore@Sun.COM
74510463SGarrett.Damore@Sun.COM switch (cmd) {
74610463SGarrett.Damore@Sun.COM case DDI_DETACH:
74710463SGarrett.Damore@Sun.COM return (p16x_detach(dev));
74810463SGarrett.Damore@Sun.COM
74910463SGarrett.Damore@Sun.COM case DDI_SUSPEND:
75010463SGarrett.Damore@Sun.COM return (p16x_suspend(dev));
75110463SGarrett.Damore@Sun.COM
75210463SGarrett.Damore@Sun.COM default:
75310463SGarrett.Damore@Sun.COM return (DDI_FAILURE);
75410463SGarrett.Damore@Sun.COM }
75510463SGarrett.Damore@Sun.COM }
75610463SGarrett.Damore@Sun.COM
75710463SGarrett.Damore@Sun.COM int
p16x_ddi_quiesce(dev_info_t * dip)75810463SGarrett.Damore@Sun.COM p16x_ddi_quiesce(dev_info_t *dip)
75910463SGarrett.Damore@Sun.COM {
76010463SGarrett.Damore@Sun.COM p16x_dev_t *dev;
76110463SGarrett.Damore@Sun.COM
76210463SGarrett.Damore@Sun.COM dev = ddi_get_driver_private(dip);
76310463SGarrett.Damore@Sun.COM
76410463SGarrett.Damore@Sun.COM write_reg(dev, SA, 0, 0);
76510463SGarrett.Damore@Sun.COM OUTL(dev, 0x01, HC);
76610463SGarrett.Damore@Sun.COM
76710463SGarrett.Damore@Sun.COM return (DDI_SUCCESS);
76810463SGarrett.Damore@Sun.COM }
769