188447a05SGarrett D'Amore /*
288447a05SGarrett D'Amore  * CDDL HEADER START
388447a05SGarrett D'Amore  *
488447a05SGarrett D'Amore  * The contents of this file are subject to the terms of the
588447a05SGarrett D'Amore  * Common Development and Distribution License (the "License").
688447a05SGarrett D'Amore  * You may not use this file except in compliance with the License.
788447a05SGarrett D'Amore  *
888447a05SGarrett D'Amore  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
988447a05SGarrett D'Amore  * or http://www.opensolaris.org/os/licensing.
1088447a05SGarrett D'Amore  * See the License for the specific language governing permissions
1188447a05SGarrett D'Amore  * and limitations under the License.
1288447a05SGarrett D'Amore  *
1388447a05SGarrett D'Amore  * When distributing Covered Code, include this CDDL HEADER in each
1488447a05SGarrett D'Amore  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1588447a05SGarrett D'Amore  * If applicable, add the following below this CDDL HEADER, with the
1688447a05SGarrett D'Amore  * fields enclosed by brackets "[]" replaced with your own identifying
1788447a05SGarrett D'Amore  * information: Portions Copyright [yyyy] [name of copyright owner]
1888447a05SGarrett D'Amore  *
1988447a05SGarrett D'Amore  * CDDL HEADER END
2088447a05SGarrett D'Amore  */
2188447a05SGarrett D'Amore /*
22*68c47f65SGarrett D'Amore  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2388447a05SGarrett D'Amore  * Use is subject to license terms.
2488447a05SGarrett D'Amore  */
2588447a05SGarrett D'Amore 
2688447a05SGarrett D'Amore 
2788447a05SGarrett D'Amore /*
2888447a05SGarrett D'Amore  * audiocs Audio Driver
2988447a05SGarrett D'Amore  *
3088447a05SGarrett D'Amore  * This Audio Driver controls the Crystal CS4231 Codec used on many SPARC
3188447a05SGarrett D'Amore  * platforms. It does not support the CS4231 on Power PCs or x86 PCs. It
3288447a05SGarrett D'Amore  * does support two different DMA engines, the APC and EB2. The code for
3388447a05SGarrett D'Amore  * those DMA engines is split out and a well defined, but private, interface
3488447a05SGarrett D'Amore  * is used to control those DMA engines.
3588447a05SGarrett D'Amore  *
3688447a05SGarrett D'Amore  * For some reason setting the CS4231's registers doesn't always
3788447a05SGarrett D'Amore  * succeed.  Therefore every time we set a register we always read it
3888447a05SGarrett D'Amore  * back to make sure it was set. If not we wait a little while and
3988447a05SGarrett D'Amore  * then try again. This is all taken care of in the routines
4088447a05SGarrett D'Amore  * audiocs_put_index() and audiocs_sel_index() and the macros ORIDX()
4188447a05SGarrett D'Amore  * and ANDIDX(). We don't worry about the status register because it
4288447a05SGarrett D'Amore  * is cleared by writing anything to it.  So it doesn't matter what
4388447a05SGarrett D'Amore  * the value written is.
4488447a05SGarrett D'Amore  *
4588447a05SGarrett D'Amore  * This driver supports suspending and resuming. A suspend just stops playing
4688447a05SGarrett D'Amore  * and recording. The play DMA buffers end up getting thrown away, but when
4788447a05SGarrett D'Amore  * you shut down the machine there is a break in the audio anyway, so they
4888447a05SGarrett D'Amore  * won't be missed and it isn't worth the effort to save them. When we resume
4988447a05SGarrett D'Amore  * we always start playing and recording. If they aren't needed they get
5088447a05SGarrett D'Amore  * shut off by the mixer.
5188447a05SGarrett D'Amore  *
5288447a05SGarrett D'Amore  * Power management is supported by this driver.
5388447a05SGarrett D'Amore  *
5488447a05SGarrett D'Amore  *	NOTE: This module depends on drv/audio being loaded first.
5588447a05SGarrett D'Amore  */
5688447a05SGarrett D'Amore 
5788447a05SGarrett D'Amore #include <sys/modctl.h>
5888447a05SGarrett D'Amore #include <sys/kmem.h>
5988447a05SGarrett D'Amore #include <sys/stropts.h>
6088447a05SGarrett D'Amore #include <sys/ddi.h>
6188447a05SGarrett D'Amore #include <sys/sunddi.h>
6288447a05SGarrett D'Amore #include <sys/note.h>
6388447a05SGarrett D'Amore #include <sys/audio/audio_driver.h>
6488447a05SGarrett D'Amore #include "audio_4231.h"
6588447a05SGarrett D'Amore 
6688447a05SGarrett D'Amore /*
6788447a05SGarrett D'Amore  * Module linkage routines for the kernel
6888447a05SGarrett D'Amore  */
6988447a05SGarrett D'Amore static int audiocs_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
7088447a05SGarrett D'Amore static int audiocs_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
7188447a05SGarrett D'Amore static int audiocs_ddi_power(dev_info_t *, int, int);
7288447a05SGarrett D'Amore 
7388447a05SGarrett D'Amore /*
7488447a05SGarrett D'Amore  * Entry point routine prototypes
7588447a05SGarrett D'Amore  */
76*68c47f65SGarrett D'Amore static int audiocs_open(void *, int, unsigned *, caddr_t *);
7788447a05SGarrett D'Amore static void audiocs_close(void *);
7888447a05SGarrett D'Amore static int audiocs_start(void *);
7988447a05SGarrett D'Amore static void audiocs_stop(void *);
8088447a05SGarrett D'Amore static int audiocs_format(void *);
8188447a05SGarrett D'Amore static int audiocs_channels(void *);
8288447a05SGarrett D'Amore static int audiocs_rate(void *);
8388447a05SGarrett D'Amore static uint64_t audiocs_count(void *);
8488447a05SGarrett D'Amore static void audiocs_sync(void *, unsigned);
8588447a05SGarrett D'Amore 
8688447a05SGarrett D'Amore /*
8788447a05SGarrett D'Amore  * Control callbacks.
8888447a05SGarrett D'Amore  */
8988447a05SGarrett D'Amore static int audiocs_get_value(void *, uint64_t *);
9088447a05SGarrett D'Amore static int audiocs_set_ogain(void *, uint64_t);
9188447a05SGarrett D'Amore static int audiocs_set_igain(void *, uint64_t);
9288447a05SGarrett D'Amore static int audiocs_set_mgain(void *, uint64_t);
9388447a05SGarrett D'Amore static int audiocs_set_inputs(void *, uint64_t);
9488447a05SGarrett D'Amore static int audiocs_set_outputs(void *, uint64_t);
9588447a05SGarrett D'Amore static int audiocs_set_micboost(void *, uint64_t);
9688447a05SGarrett D'Amore 
9788447a05SGarrett D'Amore /* Local Routines */
9888447a05SGarrett D'Amore static int audiocs_resume(dev_info_t *);
9988447a05SGarrett D'Amore static int audiocs_attach(dev_info_t *);
10088447a05SGarrett D'Amore static int audiocs_detach(dev_info_t *);
10188447a05SGarrett D'Amore static int audiocs_suspend(dev_info_t *);
10288447a05SGarrett D'Amore 
10388447a05SGarrett D'Amore static void audiocs_destroy(CS_state_t *);
10488447a05SGarrett D'Amore static int audiocs_init_state(CS_state_t *);
10588447a05SGarrett D'Amore static int audiocs_chip_init(CS_state_t *);
10688447a05SGarrett D'Amore static int audiocs_alloc_engine(CS_state_t *, int);
10788447a05SGarrett D'Amore static void audiocs_free_engine(CS_engine_t *);
10888447a05SGarrett D'Amore static void audiocs_get_ports(CS_state_t *);
10988447a05SGarrett D'Amore static void audiocs_configure_input(CS_state_t *);
11088447a05SGarrett D'Amore static void audiocs_configure_output(CS_state_t *);
11188447a05SGarrett D'Amore static CS_ctrl_t *audiocs_alloc_ctrl(CS_state_t *, uint32_t, uint64_t);
11288447a05SGarrett D'Amore static void audiocs_free_ctrl(CS_ctrl_t *);
11388447a05SGarrett D'Amore static int audiocs_add_controls(CS_state_t *);
11488447a05SGarrett D'Amore static void audiocs_del_controls(CS_state_t *);
11588447a05SGarrett D'Amore static void audiocs_power_up(CS_state_t *);
11688447a05SGarrett D'Amore static void audiocs_power_down(CS_state_t *);
11788447a05SGarrett D'Amore static int audiocs_poll_ready(CS_state_t *);
11888447a05SGarrett D'Amore #ifdef	DEBUG
11988447a05SGarrett D'Amore static void audiocs_put_index(CS_state_t *,  uint8_t, uint8_t, int);
12088447a05SGarrett D'Amore static void audiocs_sel_index(CS_state_t *, uint8_t, int);
12188447a05SGarrett D'Amore #define	SELIDX(s, idx)		audiocs_sel_index(s, idx, __LINE__)
12288447a05SGarrett D'Amore #define	PUTIDX(s, val, mask)	audiocs_put_index(s, val, mask, __LINE__)
12388447a05SGarrett D'Amore #else
12488447a05SGarrett D'Amore static void audiocs_put_index(CS_state_t *,  uint8_t, uint8_t);
12588447a05SGarrett D'Amore static void audiocs_sel_index(CS_state_t *, uint8_t);
12688447a05SGarrett D'Amore #define	SELIDX(s, idx)		audiocs_sel_index(s, idx)
12788447a05SGarrett D'Amore #define	PUTIDX(s, val, mask)	audiocs_put_index(s, val, mask)
12888447a05SGarrett D'Amore #endif
129*68c47f65SGarrett D'Amore #define	GETIDX(s)		ddi_get8((handle), &CS4231_IDR)
13088447a05SGarrett D'Amore 
13188447a05SGarrett D'Amore #define	ORIDX(s, val, mask)						\
13288447a05SGarrett D'Amore 	PUTIDX(s,							\
13388447a05SGarrett D'Amore 	    (ddi_get8((handle), &CS4231_IDR) | (uint8_t)(val)),		\
13488447a05SGarrett D'Amore 	    (uint8_t)(mask))
13588447a05SGarrett D'Amore 
13688447a05SGarrett D'Amore #define	ANDIDX(s, val, mask)						\
13788447a05SGarrett D'Amore 	PUTIDX(s, (ddi_get8((handle), &CS4231_IDR) & (uint8_t)(val)),	\
13888447a05SGarrett D'Amore 	    (uint8_t)(mask))
13988447a05SGarrett D'Amore 
14088447a05SGarrett D'Amore static audio_engine_ops_t audiocs_engine_ops = {
14188447a05SGarrett D'Amore 	AUDIO_ENGINE_VERSION,
14288447a05SGarrett D'Amore 	audiocs_open,
14388447a05SGarrett D'Amore 	audiocs_close,
14488447a05SGarrett D'Amore 	audiocs_start,
14588447a05SGarrett D'Amore 	audiocs_stop,
14688447a05SGarrett D'Amore 	audiocs_count,
14788447a05SGarrett D'Amore 	audiocs_format,
14888447a05SGarrett D'Amore 	audiocs_channels,
14988447a05SGarrett D'Amore 	audiocs_rate,
15088447a05SGarrett D'Amore 	audiocs_sync,
151f9ead4a5SGarrett D'Amore 	NULL,
152*68c47f65SGarrett D'Amore 	NULL,
153*68c47f65SGarrett D'Amore 	NULL,
15488447a05SGarrett D'Amore };
15588447a05SGarrett D'Amore 
15688447a05SGarrett D'Amore #define	OUTPUT_SPEAKER		0
15788447a05SGarrett D'Amore #define	OUTPUT_HEADPHONES	1
15888447a05SGarrett D'Amore #define	OUTPUT_LINEOUT		2
15988447a05SGarrett D'Amore 
16088447a05SGarrett D'Amore static const char *audiocs_outputs[] = {
16188447a05SGarrett D'Amore 	AUDIO_PORT_SPEAKER,
16288447a05SGarrett D'Amore 	AUDIO_PORT_HEADPHONES,
16388447a05SGarrett D'Amore 	AUDIO_PORT_LINEOUT,
16488447a05SGarrett D'Amore 	NULL
16588447a05SGarrett D'Amore };
16688447a05SGarrett D'Amore 
16788447a05SGarrett D'Amore #define	INPUT_MIC		0
16888447a05SGarrett D'Amore #define	INPUT_LINEIN		1
16988447a05SGarrett D'Amore #define	INPUT_STEREOMIX		2
17088447a05SGarrett D'Amore #define	INPUT_CD		3
17188447a05SGarrett D'Amore 
17288447a05SGarrett D'Amore static const char *audiocs_inputs[] = {
17388447a05SGarrett D'Amore 	AUDIO_PORT_MIC,
17488447a05SGarrett D'Amore 	AUDIO_PORT_LINEIN,
17588447a05SGarrett D'Amore 	AUDIO_PORT_STEREOMIX,
17688447a05SGarrett D'Amore 	AUDIO_PORT_CD,
17788447a05SGarrett D'Amore 	NULL
17888447a05SGarrett D'Amore };
17988447a05SGarrett D'Amore 
18088447a05SGarrett D'Amore /*
18188447a05SGarrett D'Amore  * Global variables, but viewable only by this file.
18288447a05SGarrett D'Amore  */
18388447a05SGarrett D'Amore 
18488447a05SGarrett D'Amore /* play gain array, converts linear gain to 64 steps of log10 gain */
18588447a05SGarrett D'Amore static uint8_t cs4231_atten[] = {
18688447a05SGarrett D'Amore 	0x3f,	0x3e,	0x3d,	0x3c,	0x3b,	/* [000] -> [004] */
18788447a05SGarrett D'Amore 	0x3a,	0x39,	0x38,	0x37,	0x36,	/* [005] -> [009] */
18888447a05SGarrett D'Amore 	0x35,	0x34,	0x33,	0x32,	0x31,	/* [010] -> [014] */
18988447a05SGarrett D'Amore 	0x30,	0x2f,	0x2e,	0x2d,	0x2c,	/* [015] -> [019] */
19088447a05SGarrett D'Amore 	0x2b,	0x2a,	0x29,	0x29,	0x28,	/* [020] -> [024] */
19188447a05SGarrett D'Amore 	0x28,	0x27,	0x27,	0x26,	0x26,	/* [025] -> [029] */
19288447a05SGarrett D'Amore 	0x25,	0x25,	0x24,	0x24,	0x23,	/* [030] -> [034] */
19388447a05SGarrett D'Amore 	0x23,	0x22,	0x22,	0x21,	0x21,	/* [035] -> [039] */
19488447a05SGarrett D'Amore 	0x20,	0x20,	0x1f,	0x1f,	0x1f,	/* [040] -> [044] */
19588447a05SGarrett D'Amore 	0x1e,	0x1e,	0x1e,	0x1d,	0x1d,	/* [045] -> [049] */
19688447a05SGarrett D'Amore 	0x1d,	0x1c,	0x1c,	0x1c,	0x1b,	/* [050] -> [054] */
19788447a05SGarrett D'Amore 	0x1b,	0x1b,	0x1a,	0x1a,	0x1a,	/* [055] -> [059] */
19888447a05SGarrett D'Amore 	0x1a,	0x19,	0x19,	0x19,	0x19,	/* [060] -> [064] */
19988447a05SGarrett D'Amore 	0x18,	0x18,	0x18,	0x18,	0x17,	/* [065] -> [069] */
20088447a05SGarrett D'Amore 	0x17,	0x17,	0x17,	0x16,	0x16,	/* [070] -> [074] */
20188447a05SGarrett D'Amore 	0x16,	0x16,	0x16,	0x15,	0x15,	/* [075] -> [079] */
20288447a05SGarrett D'Amore 	0x15,	0x15,	0x15,	0x14,	0x14,	/* [080] -> [084] */
20388447a05SGarrett D'Amore 	0x14,	0x14,	0x14,	0x13,	0x13,	/* [085] -> [089] */
20488447a05SGarrett D'Amore 	0x13,	0x13,	0x13,	0x12,	0x12,	/* [090] -> [094] */
20588447a05SGarrett D'Amore 	0x12,	0x12,	0x12,	0x12,	0x11,	/* [095] -> [099] */
20688447a05SGarrett D'Amore 	0x11,	0x11,	0x11,	0x11,	0x11,	/* [100] -> [104] */
20788447a05SGarrett D'Amore 	0x10,	0x10,	0x10,	0x10,	0x10,	/* [105] -> [109] */
20888447a05SGarrett D'Amore 	0x10,	0x0f,	0x0f,	0x0f,	0x0f,	/* [110] -> [114] */
20988447a05SGarrett D'Amore 	0x0f,	0x0f,	0x0e,	0x0e,	0x0e,	/* [114] -> [119] */
21088447a05SGarrett D'Amore 	0x0e,	0x0e,	0x0e,	0x0e,	0x0d,	/* [120] -> [124] */
21188447a05SGarrett D'Amore 	0x0d,	0x0d,	0x0d,	0x0d,	0x0d,	/* [125] -> [129] */
21288447a05SGarrett D'Amore 	0x0d,	0x0c,	0x0c,	0x0c,	0x0c,	/* [130] -> [134] */
21388447a05SGarrett D'Amore 	0x0c,	0x0c,	0x0c,	0x0b,	0x0b,	/* [135] -> [139] */
21488447a05SGarrett D'Amore 	0x0b,	0x0b,	0x0b,	0x0b,	0x0b,	/* [140] -> [144] */
21588447a05SGarrett D'Amore 	0x0b,	0x0a,	0x0a,	0x0a,	0x0a,	/* [145] -> [149] */
21688447a05SGarrett D'Amore 	0x0a,	0x0a,	0x0a,	0x0a,	0x09,	/* [150] -> [154] */
21788447a05SGarrett D'Amore 	0x09,	0x09,	0x09,	0x09,	0x09,	/* [155] -> [159] */
21888447a05SGarrett D'Amore 	0x09,	0x09,	0x08,	0x08,	0x08,	/* [160] -> [164] */
21988447a05SGarrett D'Amore 	0x08,	0x08,	0x08,	0x08,	0x08,	/* [165] -> [169] */
22088447a05SGarrett D'Amore 	0x08,	0x07,	0x07,	0x07,	0x07,	/* [170] -> [174] */
22188447a05SGarrett D'Amore 	0x07,	0x07,	0x07,	0x07,	0x07,	/* [175] -> [179] */
22288447a05SGarrett D'Amore 	0x06,	0x06,	0x06,	0x06,	0x06,	/* [180] -> [184] */
22388447a05SGarrett D'Amore 	0x06,	0x06,	0x06,	0x06,	0x05,	/* [185] -> [189] */
22488447a05SGarrett D'Amore 	0x05,	0x05,	0x05,	0x05,	0x05,	/* [190] -> [194] */
22588447a05SGarrett D'Amore 	0x05,	0x05,	0x05,	0x05,	0x04,	/* [195] -> [199] */
22688447a05SGarrett D'Amore 	0x04,	0x04,	0x04,	0x04,	0x04,	/* [200] -> [204] */
22788447a05SGarrett D'Amore 	0x04,	0x04,	0x04,	0x04,	0x03,	/* [205] -> [209] */
22888447a05SGarrett D'Amore 	0x03,	0x03,	0x03,	0x03,	0x03,	/* [210] -> [214] */
22988447a05SGarrett D'Amore 	0x03,	0x03,	0x03,	0x03,	0x03,	/* [215] -> [219] */
23088447a05SGarrett D'Amore 	0x02,	0x02,	0x02,	0x02,	0x02,	/* [220] -> [224] */
23188447a05SGarrett D'Amore 	0x02,	0x02,	0x02,	0x02,	0x02,	/* [225] -> [229] */
23288447a05SGarrett D'Amore 	0x02,	0x01,	0x01,	0x01,	0x01,	/* [230] -> [234] */
23388447a05SGarrett D'Amore 	0x01,	0x01,	0x01,	0x01,	0x01,	/* [235] -> [239] */
23488447a05SGarrett D'Amore 	0x01,	0x01,	0x01,	0x00,	0x00,	/* [240] -> [244] */
23588447a05SGarrett D'Amore 	0x00,	0x00,	0x00,	0x00,	0x00,	/* [245] -> [249] */
23688447a05SGarrett D'Amore 	0x00,	0x00,	0x00,	0x00,	0x00,	/* [250] -> [254] */
23788447a05SGarrett D'Amore 	0x00					/* [255] */
23888447a05SGarrett D'Amore };
23988447a05SGarrett D'Amore 
24088447a05SGarrett D'Amore /*
24188447a05SGarrett D'Amore  * STREAMS Structures
24288447a05SGarrett D'Amore  */
24388447a05SGarrett D'Amore 
24488447a05SGarrett D'Amore /*
24588447a05SGarrett D'Amore  * DDI Structures
24688447a05SGarrett D'Amore  */
24788447a05SGarrett D'Amore 
24888447a05SGarrett D'Amore /* Device operations structure */
24988447a05SGarrett D'Amore static struct dev_ops audiocs_dev_ops = {
25088447a05SGarrett D'Amore 	DEVO_REV,			/* devo_rev */
25188447a05SGarrett D'Amore 	0,				/* devo_refcnt */
25288447a05SGarrett D'Amore 	NULL,				/* devo_getinfo */
25388447a05SGarrett D'Amore 	nulldev,			/* devo_identify - obsolete */
25488447a05SGarrett D'Amore 	nulldev,			/* devo_probe - not needed */
25588447a05SGarrett D'Amore 	audiocs_ddi_attach,		/* devo_attach */
25688447a05SGarrett D'Amore 	audiocs_ddi_detach,		/* devo_detach */
25788447a05SGarrett D'Amore 	nodev,				/* devo_reset */
25888447a05SGarrett D'Amore 	NULL,				/* devi_cb_ops */
25988447a05SGarrett D'Amore 	NULL,				/* devo_bus_ops */
26088447a05SGarrett D'Amore 	audiocs_ddi_power,		/* devo_power */
26188447a05SGarrett D'Amore 	ddi_quiesce_not_supported,	/* devo_quiesce */
26288447a05SGarrett D'Amore };
26388447a05SGarrett D'Amore 
26488447a05SGarrett D'Amore /* Linkage structure for loadable drivers */
26588447a05SGarrett D'Amore static struct modldrv audiocs_modldrv = {
26688447a05SGarrett D'Amore 	&mod_driverops,		/* drv_modops */
26788447a05SGarrett D'Amore 	CS4231_MOD_NAME,	/* drv_linkinfo */
26888447a05SGarrett D'Amore 	&audiocs_dev_ops	/* drv_dev_ops */
26988447a05SGarrett D'Amore };
27088447a05SGarrett D'Amore 
27188447a05SGarrett D'Amore /* Module linkage structure */
27288447a05SGarrett D'Amore static struct modlinkage audiocs_modlinkage = {
27388447a05SGarrett D'Amore 	MODREV_1,			/* ml_rev */
27488447a05SGarrett D'Amore 	(void *)&audiocs_modldrv,	/* ml_linkage */
27588447a05SGarrett D'Amore 	NULL				/* NULL terminates the list */
27688447a05SGarrett D'Amore };
27788447a05SGarrett D'Amore 
27888447a05SGarrett D'Amore 
27988447a05SGarrett D'Amore /* *******  Loadable Module Configuration Entry Points  ********************* */
28088447a05SGarrett D'Amore 
28188447a05SGarrett D'Amore /*
28288447a05SGarrett D'Amore  * _init()
28388447a05SGarrett D'Amore  *
28488447a05SGarrett D'Amore  * Description:
28588447a05SGarrett D'Amore  *	Implements _init(9E).
28688447a05SGarrett D'Amore  *
28788447a05SGarrett D'Amore  * Returns:
28888447a05SGarrett D'Amore  *	mod_install() status, see mod_install(9f)
28988447a05SGarrett D'Amore  */
29088447a05SGarrett D'Amore int
_init(void)29188447a05SGarrett D'Amore _init(void)
29288447a05SGarrett D'Amore {
29388447a05SGarrett D'Amore 	int	rv;
29488447a05SGarrett D'Amore 
29588447a05SGarrett D'Amore 	audio_init_ops(&audiocs_dev_ops, CS4231_NAME);
29688447a05SGarrett D'Amore 
29788447a05SGarrett D'Amore 	if ((rv = mod_install(&audiocs_modlinkage)) != 0) {
29888447a05SGarrett D'Amore 		audio_fini_ops(&audiocs_dev_ops);
29988447a05SGarrett D'Amore 	}
30088447a05SGarrett D'Amore 
30188447a05SGarrett D'Amore 	return (rv);
30288447a05SGarrett D'Amore }
30388447a05SGarrett D'Amore 
30488447a05SGarrett D'Amore /*
30588447a05SGarrett D'Amore  * _fini()
30688447a05SGarrett D'Amore  *
30788447a05SGarrett D'Amore  * Description:
30888447a05SGarrett D'Amore  *	Implements _fini(9E).
30988447a05SGarrett D'Amore  *
31088447a05SGarrett D'Amore  * Returns:
31188447a05SGarrett D'Amore  *	mod_remove() status, see mod_remove(9f)
31288447a05SGarrett D'Amore  */
31388447a05SGarrett D'Amore int
_fini(void)31488447a05SGarrett D'Amore _fini(void)
31588447a05SGarrett D'Amore {
31688447a05SGarrett D'Amore 	int	rv;
31788447a05SGarrett D'Amore 
31888447a05SGarrett D'Amore 	if ((rv = mod_remove(&audiocs_modlinkage)) == 0) {
31988447a05SGarrett D'Amore 		audio_fini_ops(&audiocs_dev_ops);
32088447a05SGarrett D'Amore 	}
32188447a05SGarrett D'Amore 
32288447a05SGarrett D'Amore 	return (rv);
32388447a05SGarrett D'Amore }
32488447a05SGarrett D'Amore 
32588447a05SGarrett D'Amore /*
32688447a05SGarrett D'Amore  * _info()
32788447a05SGarrett D'Amore  *
32888447a05SGarrett D'Amore  * Description:
32988447a05SGarrett D'Amore  *	Implements _info(9E).
33088447a05SGarrett D'Amore  *
33188447a05SGarrett D'Amore  * Arguments:
33288447a05SGarrett D'Amore  *	modinfo *modinfop	Pointer to the opaque modinfo structure
33388447a05SGarrett D'Amore  *
33488447a05SGarrett D'Amore  * Returns:
33588447a05SGarrett D'Amore  *	mod_info() status, see mod_info(9f)
33688447a05SGarrett D'Amore  */
33788447a05SGarrett D'Amore int
_info(struct modinfo * modinfop)33888447a05SGarrett D'Amore _info(struct modinfo *modinfop)
33988447a05SGarrett D'Amore {
34088447a05SGarrett D'Amore 	return (mod_info(&audiocs_modlinkage, modinfop));
34188447a05SGarrett D'Amore }
34288447a05SGarrett D'Amore 
34388447a05SGarrett D'Amore 
34488447a05SGarrett D'Amore /* *******  Driver Entry Points  ******************************************** */
34588447a05SGarrett D'Amore 
34688447a05SGarrett D'Amore /*
34788447a05SGarrett D'Amore  * audiocs_ddi_attach()
34888447a05SGarrett D'Amore  *
34988447a05SGarrett D'Amore  * Description:
35088447a05SGarrett D'Amore  *	Implement attach(9e).
35188447a05SGarrett D'Amore  *
35288447a05SGarrett D'Amore  * Arguments:
35388447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
35488447a05SGarrett D'Amore  *	ddi_attach_cmd_t cmd	Attach command
35588447a05SGarrett D'Amore  *
35688447a05SGarrett D'Amore  * Returns:
35788447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
35888447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
35988447a05SGarrett D'Amore  */
36088447a05SGarrett D'Amore static int
audiocs_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)36188447a05SGarrett D'Amore audiocs_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
36288447a05SGarrett D'Amore {
36388447a05SGarrett D'Amore 	switch (cmd) {
36488447a05SGarrett D'Amore 	case DDI_ATTACH:
36588447a05SGarrett D'Amore 		return (audiocs_attach(dip));
36688447a05SGarrett D'Amore 
36788447a05SGarrett D'Amore 	case DDI_RESUME:
36888447a05SGarrett D'Amore 		return (audiocs_resume(dip));
36988447a05SGarrett D'Amore 
37088447a05SGarrett D'Amore 	default:
37188447a05SGarrett D'Amore 		return (DDI_FAILURE);
37288447a05SGarrett D'Amore 	}
37388447a05SGarrett D'Amore }
37488447a05SGarrett D'Amore 
37588447a05SGarrett D'Amore /*
37688447a05SGarrett D'Amore  * audiocs_ddi_detach()
37788447a05SGarrett D'Amore  *
37888447a05SGarrett D'Amore  * Description:
37988447a05SGarrett D'Amore  *	Implement detach(9e).
38088447a05SGarrett D'Amore  *
38188447a05SGarrett D'Amore  * Arguments:
38288447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
38388447a05SGarrett D'Amore  *	ddi_detach_cmd_t cmd	Detach command
38488447a05SGarrett D'Amore  *
38588447a05SGarrett D'Amore  * Returns:
38688447a05SGarrett D'Amore  *	DDI_SUCCESS		Success.
38788447a05SGarrett D'Amore  *	DDI_FAILURE		Failure.
38888447a05SGarrett D'Amore  */
38988447a05SGarrett D'Amore static int
audiocs_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)39088447a05SGarrett D'Amore audiocs_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
39188447a05SGarrett D'Amore {
39288447a05SGarrett D'Amore 	switch (cmd) {
39388447a05SGarrett D'Amore 	case DDI_DETACH:
39488447a05SGarrett D'Amore 		return (audiocs_detach(dip));
39588447a05SGarrett D'Amore 
39688447a05SGarrett D'Amore 	case DDI_SUSPEND:
39788447a05SGarrett D'Amore 		return (audiocs_suspend(dip));
39888447a05SGarrett D'Amore 
39988447a05SGarrett D'Amore 	default:
40088447a05SGarrett D'Amore 		return (DDI_FAILURE);
40188447a05SGarrett D'Amore 	}
40288447a05SGarrett D'Amore }
40388447a05SGarrett D'Amore 
40488447a05SGarrett D'Amore /*
40588447a05SGarrett D'Amore  * audiocs_ddi_power()
40688447a05SGarrett D'Amore  *
40788447a05SGarrett D'Amore  * Description:
40888447a05SGarrett D'Amore  *	Implements power(9E).
40988447a05SGarrett D'Amore  *
41088447a05SGarrett D'Amore  * Arguments:
41188447a05SGarrett D'Amore  *	def_info_t	*dip		Ptr to the device's dev_info structure
41288447a05SGarrett D'Amore  *	int		component	Which component to power up/down
41388447a05SGarrett D'Amore  *	int		level		The power level for the component
41488447a05SGarrett D'Amore  *
41588447a05SGarrett D'Amore  * Returns:
41688447a05SGarrett D'Amore  *	DDI_SUCCESS		Power level changed, we always succeed
41788447a05SGarrett D'Amore  */
41888447a05SGarrett D'Amore static int
audiocs_ddi_power(dev_info_t * dip,int component,int level)41988447a05SGarrett D'Amore audiocs_ddi_power(dev_info_t *dip, int component, int level)
42088447a05SGarrett D'Amore {
42188447a05SGarrett D'Amore 	CS_state_t		*state;
42288447a05SGarrett D'Amore 
42388447a05SGarrett D'Amore 	if (component != CS4231_COMPONENT)
42488447a05SGarrett D'Amore 		return (DDI_FAILURE);
42588447a05SGarrett D'Amore 
42688447a05SGarrett D'Amore 	/* get the state structure */
42788447a05SGarrett D'Amore 	state = ddi_get_driver_private(dip);
42888447a05SGarrett D'Amore 
42988447a05SGarrett D'Amore 	ASSERT(!mutex_owned(&state->cs_lock));
43088447a05SGarrett D'Amore 
43188447a05SGarrett D'Amore 	/* make sure we have some work to do */
43288447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
43388447a05SGarrett D'Amore 
43488447a05SGarrett D'Amore 	/*
43588447a05SGarrett D'Amore 	 * We don't do anything if we're suspended.  Suspend/resume diddles
43688447a05SGarrett D'Amore 	 * with power anyway.
43788447a05SGarrett D'Amore 	 */
43888447a05SGarrett D'Amore 	if (!state->cs_suspended) {
43988447a05SGarrett D'Amore 
44088447a05SGarrett D'Amore 		/* check the level change to see what we need to do */
44188447a05SGarrett D'Amore 		if (level == CS4231_PWR_OFF && state->cs_powered) {
44288447a05SGarrett D'Amore 
44388447a05SGarrett D'Amore 			/* power down and save the state */
44488447a05SGarrett D'Amore 			audiocs_power_down(state);
44588447a05SGarrett D'Amore 			state->cs_powered = B_FALSE;
44688447a05SGarrett D'Amore 
44788447a05SGarrett D'Amore 		} else if (level == CS4231_PWR_ON && !state->cs_powered) {
44888447a05SGarrett D'Amore 
44988447a05SGarrett D'Amore 			/* power up */
45088447a05SGarrett D'Amore 			audiocs_power_up(state);
45188447a05SGarrett D'Amore 			state->cs_powered = B_TRUE;
45288447a05SGarrett D'Amore 		}
45388447a05SGarrett D'Amore 	}
45488447a05SGarrett D'Amore 
45588447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
45688447a05SGarrett D'Amore 
45788447a05SGarrett D'Amore 	ASSERT(!mutex_owned(&state->cs_lock));
45888447a05SGarrett D'Amore 
45988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
46088447a05SGarrett D'Amore }
46188447a05SGarrett D'Amore 
46288447a05SGarrett D'Amore /* ******* Local Routines *************************************************** */
46388447a05SGarrett D'Amore 
46488447a05SGarrett D'Amore static void
audiocs_destroy(CS_state_t * state)46588447a05SGarrett D'Amore audiocs_destroy(CS_state_t *state)
46688447a05SGarrett D'Amore {
46788447a05SGarrett D'Amore 	if (state == NULL)
46888447a05SGarrett D'Amore 		return;
46988447a05SGarrett D'Amore 
47088447a05SGarrett D'Amore 	for (int i = CS4231_PLAY; i <= CS4231_REC; i++) {
47188447a05SGarrett D'Amore 		audiocs_free_engine(state->cs_engines[i]);
47288447a05SGarrett D'Amore 	}
47388447a05SGarrett D'Amore 	audiocs_del_controls(state);
47488447a05SGarrett D'Amore 
47588447a05SGarrett D'Amore 	if (state->cs_adev) {
47688447a05SGarrett D'Amore 		audio_dev_free(state->cs_adev);
47788447a05SGarrett D'Amore 	}
47888447a05SGarrett D'Amore 
47988447a05SGarrett D'Amore 	/* unmap the registers */
48088447a05SGarrett D'Amore 	CS4231_DMA_UNMAP_REGS(state);
48188447a05SGarrett D'Amore 
48288447a05SGarrett D'Amore 	/* destroy the state mutex */
48388447a05SGarrett D'Amore 	mutex_destroy(&state->cs_lock);
48488447a05SGarrett D'Amore 	kmem_free(state, sizeof (*state));
48588447a05SGarrett D'Amore }
48688447a05SGarrett D'Amore 
48788447a05SGarrett D'Amore /*
48888447a05SGarrett D'Amore  * audiocs_attach()
48988447a05SGarrett D'Amore  *
49088447a05SGarrett D'Amore  * Description:
49188447a05SGarrett D'Amore  *	Attach an instance of the CS4231 driver. This routine does the device
49288447a05SGarrett D'Amore  *	dependent attach tasks.  When it is complete it calls
49388447a05SGarrett D'Amore  *	audio_dev_register() to register with the framework.
49488447a05SGarrett D'Amore  *
49588447a05SGarrett D'Amore  * Arguments:
49688447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
49788447a05SGarrett D'Amore  *
49888447a05SGarrett D'Amore  * Returns:
49988447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
50088447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
50188447a05SGarrett D'Amore  */
50288447a05SGarrett D'Amore static int
audiocs_attach(dev_info_t * dip)50388447a05SGarrett D'Amore audiocs_attach(dev_info_t *dip)
50488447a05SGarrett D'Amore {
50588447a05SGarrett D'Amore 	CS_state_t		*state;
50688447a05SGarrett D'Amore 	audio_dev_t		*adev;
50788447a05SGarrett D'Amore 
50888447a05SGarrett D'Amore 	/* allocate the state structure */
50988447a05SGarrett D'Amore 	state = kmem_zalloc(sizeof (*state), KM_SLEEP);
51088447a05SGarrett D'Amore 	state->cs_dip = dip;
51188447a05SGarrett D'Amore 	ddi_set_driver_private(dip, state);
51288447a05SGarrett D'Amore 
51388447a05SGarrett D'Amore 	/* now fill it in, initialize the state mutexs first */
514*68c47f65SGarrett D'Amore 	mutex_init(&state->cs_lock, NULL, MUTEX_DRIVER, NULL);
51588447a05SGarrett D'Amore 
51688447a05SGarrett D'Amore 	/*
51788447a05SGarrett D'Amore 	 * audio state initialization... should always succeed,
51888447a05SGarrett D'Amore 	 * framework will message failure.
51988447a05SGarrett D'Amore 	 */
52088447a05SGarrett D'Amore 	if ((state->cs_adev = audio_dev_alloc(dip, 0)) == NULL) {
52188447a05SGarrett D'Amore 		goto error;
52288447a05SGarrett D'Amore 	}
52388447a05SGarrett D'Amore 	adev = state->cs_adev;
52488447a05SGarrett D'Amore 	audio_dev_set_description(adev, CS_DEV_CONFIG_ONBRD1);
52588447a05SGarrett D'Amore 	audio_dev_add_info(adev, "Legacy codec: Crystal Semiconductor CS4231");
52688447a05SGarrett D'Amore 
52788447a05SGarrett D'Amore 	/* initialize the audio state structures */
52888447a05SGarrett D'Amore 	if ((audiocs_init_state(state)) == DDI_FAILURE) {
52988447a05SGarrett D'Amore 		audio_dev_warn(adev, "init_state() failed");
53088447a05SGarrett D'Amore 		goto error;
53188447a05SGarrett D'Amore 	}
53288447a05SGarrett D'Amore 
533f275d02fSGarrett D'Amore 	mutex_enter(&state->cs_lock);
534f275d02fSGarrett D'Amore 
53588447a05SGarrett D'Amore 	/* initialize the audio chip */
53688447a05SGarrett D'Amore 	if ((audiocs_chip_init(state)) == DDI_FAILURE) {
537f275d02fSGarrett D'Amore 		mutex_exit(&state->cs_lock);
53888447a05SGarrett D'Amore 		audio_dev_warn(adev, "chip_init() failed");
53988447a05SGarrett D'Amore 		goto error;
54088447a05SGarrett D'Amore 	}
54188447a05SGarrett D'Amore 	/* chip init will have powered us up */
54288447a05SGarrett D'Amore 	state->cs_powered = B_TRUE;
54388447a05SGarrett D'Amore 
544f275d02fSGarrett D'Amore 	mutex_exit(&state->cs_lock);
54588447a05SGarrett D'Amore 
54688447a05SGarrett D'Amore 	/* finally register with framework to kick everything off */
54788447a05SGarrett D'Amore 	if (audio_dev_register(state->cs_adev) != DDI_SUCCESS) {
54888447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev, "unable to register audio dev");
54988447a05SGarrett D'Amore 	}
55088447a05SGarrett D'Amore 
55188447a05SGarrett D'Amore 	/* everything worked out, so report the device */
55288447a05SGarrett D'Amore 	ddi_report_dev(dip);
55388447a05SGarrett D'Amore 
55488447a05SGarrett D'Amore 	return (DDI_SUCCESS);
55588447a05SGarrett D'Amore 
55688447a05SGarrett D'Amore error:
55788447a05SGarrett D'Amore 	audiocs_destroy(state);
55888447a05SGarrett D'Amore 	return (DDI_FAILURE);
55988447a05SGarrett D'Amore }
56088447a05SGarrett D'Amore 
56188447a05SGarrett D'Amore /*
56288447a05SGarrett D'Amore  * audiocs_resume()
56388447a05SGarrett D'Amore  *
56488447a05SGarrett D'Amore  * Description:
56588447a05SGarrett D'Amore  *	Resume a suspended device instance.
56688447a05SGarrett D'Amore  *
56788447a05SGarrett D'Amore  * Arguments:
56888447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
56988447a05SGarrett D'Amore  *
57088447a05SGarrett D'Amore  * Returns:
57188447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was initialized properly
57288447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be initialized properly
57388447a05SGarrett D'Amore  */
57488447a05SGarrett D'Amore static int
audiocs_resume(dev_info_t * dip)57588447a05SGarrett D'Amore audiocs_resume(dev_info_t *dip)
57688447a05SGarrett D'Amore {
57788447a05SGarrett D'Amore 	CS_state_t		*state;
57888447a05SGarrett D'Amore 	audio_dev_t		*adev;
57988447a05SGarrett D'Amore 
58088447a05SGarrett D'Amore 	/* we've already allocated the state structure so get ptr */
58188447a05SGarrett D'Amore 	state = ddi_get_driver_private(dip);
58288447a05SGarrett D'Amore 	adev = state->cs_adev;
58388447a05SGarrett D'Amore 
58488447a05SGarrett D'Amore 	ASSERT(dip == state->cs_dip);
58588447a05SGarrett D'Amore 	ASSERT(!mutex_owned(&state->cs_lock));
58688447a05SGarrett D'Amore 
58788447a05SGarrett D'Amore 	/* mark the Codec busy -- this should keep power(9e) away */
58888447a05SGarrett D'Amore 	(void) pm_busy_component(state->cs_dip, CS4231_COMPONENT);
58988447a05SGarrett D'Amore 
59088447a05SGarrett D'Amore 	/* power it up */
59188447a05SGarrett D'Amore 	audiocs_power_up(state);
59288447a05SGarrett D'Amore 	state->cs_powered = B_TRUE;
59388447a05SGarrett D'Amore 
594f275d02fSGarrett D'Amore 	mutex_enter(&state->cs_lock);
595f275d02fSGarrett D'Amore 
59688447a05SGarrett D'Amore 	/* initialize the audio chip */
59788447a05SGarrett D'Amore 	if ((audiocs_chip_init(state)) == DDI_FAILURE) {
598f275d02fSGarrett D'Amore 		mutex_exit(&state->cs_lock);
59988447a05SGarrett D'Amore 		audio_dev_warn(adev, "chip_init() failed");
60088447a05SGarrett D'Amore 		(void) pm_idle_component(state->cs_dip, CS4231_COMPONENT);
60188447a05SGarrett D'Amore 		return (DDI_FAILURE);
60288447a05SGarrett D'Amore 	}
60388447a05SGarrett D'Amore 
60488447a05SGarrett D'Amore 	state->cs_suspended = B_FALSE;
60588447a05SGarrett D'Amore 
60688447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
60788447a05SGarrett D'Amore 
60888447a05SGarrett D'Amore 	/*
60988447a05SGarrett D'Amore 	 * We have already powered up the chip, but this alerts the
61088447a05SGarrett D'Amore 	 * framework to the fact.
61188447a05SGarrett D'Amore 	 */
61288447a05SGarrett D'Amore 	(void) pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON);
61388447a05SGarrett D'Amore 	(void) pm_idle_component(state->cs_dip, CS4231_COMPONENT);
61488447a05SGarrett D'Amore 
615*68c47f65SGarrett D'Amore 	audio_dev_resume(state->cs_adev);
616*68c47f65SGarrett D'Amore 
61788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
61888447a05SGarrett D'Amore }
61988447a05SGarrett D'Amore 
62088447a05SGarrett D'Amore /*
62188447a05SGarrett D'Amore  * audiocs_detach()
62288447a05SGarrett D'Amore  *
62388447a05SGarrett D'Amore  * Description:
62488447a05SGarrett D'Amore  *	Detach an instance of the CS4231 driver.
62588447a05SGarrett D'Amore  *
62688447a05SGarrett D'Amore  * Arguments:
62788447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
62888447a05SGarrett D'Amore  *
62988447a05SGarrett D'Amore  * Returns:
63088447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was detached
63188447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be detached (busy)
63288447a05SGarrett D'Amore  */
63388447a05SGarrett D'Amore static int
audiocs_detach(dev_info_t * dip)63488447a05SGarrett D'Amore audiocs_detach(dev_info_t *dip)
63588447a05SGarrett D'Amore {
63688447a05SGarrett D'Amore 	CS_state_t		*state;
63788447a05SGarrett D'Amore 	audio_dev_t		*adev;
63888447a05SGarrett D'Amore 	ddi_acc_handle_t	handle;
63988447a05SGarrett D'Amore 
64088447a05SGarrett D'Amore 	/* get the state structure */
64188447a05SGarrett D'Amore 	state = ddi_get_driver_private(dip);
64288447a05SGarrett D'Amore 	handle = CODEC_HANDLE;
64388447a05SGarrett D'Amore 	adev = state->cs_adev;
64488447a05SGarrett D'Amore 
64588447a05SGarrett D'Amore 	/* don't detach if still in use */
64688447a05SGarrett D'Amore 	if (audio_dev_unregister(adev) != DDI_SUCCESS) {
64788447a05SGarrett D'Amore 		return (DDI_FAILURE);
64888447a05SGarrett D'Amore 	}
64988447a05SGarrett D'Amore 
65088447a05SGarrett D'Amore 	if (state->cs_powered) {
65188447a05SGarrett D'Amore 		/*
65288447a05SGarrett D'Amore 		 * Make sure the Codec and DMA engine are off.
65388447a05SGarrett D'Amore 		 */
65488447a05SGarrett D'Amore 		SELIDX(state, INTC_REG);
65588447a05SGarrett D'Amore 		ANDIDX(state, ~(INTC_PEN|INTC_CEN), INTC_VALID_MASK);
65688447a05SGarrett D'Amore 
65788447a05SGarrett D'Amore 		/* make sure the DMA engine isn't going to do anything */
65888447a05SGarrett D'Amore 		CS4231_DMA_RESET(state);
65988447a05SGarrett D'Amore 
66088447a05SGarrett D'Amore 		/*
66188447a05SGarrett D'Amore 		 * power down the device, no reason to waste power without
66288447a05SGarrett D'Amore 		 * a driver
66388447a05SGarrett D'Amore 		 */
66488447a05SGarrett D'Amore 		(void) pm_lower_power(dip, CS4231_COMPONENT, CS4231_PWR_OFF);
66588447a05SGarrett D'Amore 	}
66688447a05SGarrett D'Amore 
66788447a05SGarrett D'Amore 	audiocs_destroy(state);
66888447a05SGarrett D'Amore 
66988447a05SGarrett D'Amore 	return (DDI_SUCCESS);
67088447a05SGarrett D'Amore }
67188447a05SGarrett D'Amore 
67288447a05SGarrett D'Amore /*
67388447a05SGarrett D'Amore  * audiocs_suspend()
67488447a05SGarrett D'Amore  *
67588447a05SGarrett D'Amore  * Description:
67688447a05SGarrett D'Amore  *	Suspend an instance of the CS4231 driver.
67788447a05SGarrett D'Amore  *
67888447a05SGarrett D'Amore  * Arguments:
67988447a05SGarrett D'Amore  *	dev_info_t	*dip	Pointer to the device's dev_info struct
68088447a05SGarrett D'Amore  *
68188447a05SGarrett D'Amore  * Returns:
68288447a05SGarrett D'Amore  *	DDI_SUCCESS		The driver was detached
68388447a05SGarrett D'Amore  *	DDI_FAILURE		The driver couldn't be detached
68488447a05SGarrett D'Amore  */
68588447a05SGarrett D'Amore static int
audiocs_suspend(dev_info_t * dip)68688447a05SGarrett D'Amore audiocs_suspend(dev_info_t *dip)
68788447a05SGarrett D'Amore {
68888447a05SGarrett D'Amore 	CS_state_t		*state;
68988447a05SGarrett D'Amore 
69088447a05SGarrett D'Amore 	/* get the state structure */
69188447a05SGarrett D'Amore 	state = ddi_get_driver_private(dip);
69288447a05SGarrett D'Amore 
69388447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
69488447a05SGarrett D'Amore 
69588447a05SGarrett D'Amore 	ASSERT(!state->cs_suspended);
69688447a05SGarrett D'Amore 
697*68c47f65SGarrett D'Amore 	audio_dev_suspend(state->cs_adev);
69888447a05SGarrett D'Amore 
699*68c47f65SGarrett D'Amore 	if (state->cs_powered) {
70088447a05SGarrett D'Amore 		/* now we can power down the Codec */
70188447a05SGarrett D'Amore 		audiocs_power_down(state);
70288447a05SGarrett D'Amore 		state->cs_powered = B_FALSE;
70388447a05SGarrett D'Amore 	}
70488447a05SGarrett D'Amore 	state->cs_suspended = B_TRUE;	/* stop new ops */
70588447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
70688447a05SGarrett D'Amore 
70788447a05SGarrett D'Amore 	return (DDI_SUCCESS);
70888447a05SGarrett D'Amore }
70988447a05SGarrett D'Amore 
71088447a05SGarrett D'Amore #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
71188447a05SGarrett D'Amore #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
71288447a05SGarrett D'Amore #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
71388447a05SGarrett D'Amore #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
71488447a05SGarrett D'Amore #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
71588447a05SGarrett D'Amore #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
71688447a05SGarrett D'Amore #define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
71788447a05SGarrett D'Amore 
71888447a05SGarrett D'Amore /*
71988447a05SGarrett D'Amore  * audiocs_alloc_ctrl
72088447a05SGarrett D'Amore  *
72188447a05SGarrett D'Amore  * Description:
72288447a05SGarrett D'Amore  *	Allocates a control structure for the audio mixer.
72388447a05SGarrett D'Amore  *
72488447a05SGarrett D'Amore  * Arguments:
72588447a05SGarrett D'Amore  *	CS_state_t	*state		Device soft state.
72688447a05SGarrett D'Amore  *	uint32_t	num		Control number to allocate.
72788447a05SGarrett D'Amore  *	uint64_t	val		Initial value.
72888447a05SGarrett D'Amore  *
72988447a05SGarrett D'Amore  * Returns:
73088447a05SGarrett D'Amore  *	Pointer to newly allocated CS_ctrl_t structure.
73188447a05SGarrett D'Amore  */
73288447a05SGarrett D'Amore static CS_ctrl_t *
audiocs_alloc_ctrl(CS_state_t * state,uint32_t num,uint64_t val)73388447a05SGarrett D'Amore audiocs_alloc_ctrl(CS_state_t *state, uint32_t num, uint64_t val)
73488447a05SGarrett D'Amore {
73588447a05SGarrett D'Amore 	audio_ctrl_desc_t	desc;
73688447a05SGarrett D'Amore 	audio_ctrl_wr_t		fn;
73788447a05SGarrett D'Amore 	CS_ctrl_t		*cc;
73888447a05SGarrett D'Amore 
73988447a05SGarrett D'Amore 	cc = kmem_zalloc(sizeof (*cc), KM_SLEEP);
74088447a05SGarrett D'Amore 	cc->cc_state = state;
74188447a05SGarrett D'Amore 	cc->cc_num = num;
74288447a05SGarrett D'Amore 
74388447a05SGarrett D'Amore 	bzero(&desc, sizeof (desc));
74488447a05SGarrett D'Amore 
74588447a05SGarrett D'Amore 	switch (num) {
74688447a05SGarrett D'Amore 	case CTL_VOLUME:
74788447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
74888447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
74988447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
75088447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
75188447a05SGarrett D'Amore 		desc.acd_flags = PCMVOL;
75288447a05SGarrett D'Amore 		fn = audiocs_set_ogain;
75388447a05SGarrett D'Amore 		break;
75488447a05SGarrett D'Amore 
75588447a05SGarrett D'Amore 	case CTL_IGAIN:
75688447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_RECGAIN;
75788447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
75888447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
75988447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
76088447a05SGarrett D'Amore 		desc.acd_flags = RECVOL;
76188447a05SGarrett D'Amore 		fn = audiocs_set_igain;
76288447a05SGarrett D'Amore 		break;
76388447a05SGarrett D'Amore 
76488447a05SGarrett D'Amore 	case CTL_MGAIN:
76588447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_MONGAIN;
76688447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
76788447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
76888447a05SGarrett D'Amore 		desc.acd_maxvalue = 100;
76988447a05SGarrett D'Amore 		desc.acd_flags = MONVOL;
77088447a05SGarrett D'Amore 		fn = audiocs_set_mgain;
77188447a05SGarrett D'Amore 		break;
77288447a05SGarrett D'Amore 
77388447a05SGarrett D'Amore 	case CTL_INPUTS:
77488447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_RECSRC;
77588447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
77688447a05SGarrett D'Amore 		desc.acd_minvalue = state->cs_imask;
77788447a05SGarrett D'Amore 		desc.acd_maxvalue = state->cs_imask;
77888447a05SGarrett D'Amore 		desc.acd_flags = RECCTL;
77988447a05SGarrett D'Amore 		for (int i = 0; audiocs_inputs[i]; i++) {
78088447a05SGarrett D'Amore 			desc.acd_enum[i] = audiocs_inputs[i];
78188447a05SGarrett D'Amore 		}
78288447a05SGarrett D'Amore 		fn = audiocs_set_inputs;
78388447a05SGarrett D'Amore 
78488447a05SGarrett D'Amore 		break;
78588447a05SGarrett D'Amore 
78688447a05SGarrett D'Amore 	case CTL_OUTPUTS:
78788447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_OUTPUTS;
78888447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
78988447a05SGarrett D'Amore 		desc.acd_minvalue = state->cs_omod;
79088447a05SGarrett D'Amore 		desc.acd_maxvalue = state->cs_omask;
79188447a05SGarrett D'Amore 		desc.acd_flags = PLAYCTL | AUDIO_CTRL_FLAG_MULTI;
79288447a05SGarrett D'Amore 		for (int i = 0; audiocs_outputs[i]; i++) {
79388447a05SGarrett D'Amore 			desc.acd_enum[i] = audiocs_outputs[i];
79488447a05SGarrett D'Amore 		}
79588447a05SGarrett D'Amore 		fn = audiocs_set_outputs;
79688447a05SGarrett D'Amore 		break;
79788447a05SGarrett D'Amore 
79888447a05SGarrett D'Amore 	case CTL_MICBOOST:
79988447a05SGarrett D'Amore 		desc.acd_name = AUDIO_CTRL_ID_MICBOOST;
80088447a05SGarrett D'Amore 		desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
80188447a05SGarrett D'Amore 		desc.acd_minvalue = 0;
80288447a05SGarrett D'Amore 		desc.acd_maxvalue = 1;
80388447a05SGarrett D'Amore 		desc.acd_flags = RECCTL;
80488447a05SGarrett D'Amore 		fn = audiocs_set_micboost;
80588447a05SGarrett D'Amore 		break;
80688447a05SGarrett D'Amore 	}
80788447a05SGarrett D'Amore 
80888447a05SGarrett D'Amore 	cc->cc_val = val;
80988447a05SGarrett D'Amore 	cc->cc_ctrl = audio_dev_add_control(state->cs_adev, &desc,
81088447a05SGarrett D'Amore 	    audiocs_get_value, fn, cc);
81188447a05SGarrett D'Amore 
81288447a05SGarrett D'Amore 	return (cc);
81388447a05SGarrett D'Amore }
81488447a05SGarrett D'Amore 
81588447a05SGarrett D'Amore /*
81688447a05SGarrett D'Amore  * audiocs_free_ctrl
81788447a05SGarrett D'Amore  *
81888447a05SGarrett D'Amore  * Description:
81988447a05SGarrett D'Amore  *	Frees a control and all resources associated with it.
82088447a05SGarrett D'Amore  *
82188447a05SGarrett D'Amore  * Arguments:
82288447a05SGarrett D'Amore  *	CS_ctrl_t	*cc	Pointer to control structure.
82388447a05SGarrett D'Amore  */
82488447a05SGarrett D'Amore static void
audiocs_free_ctrl(CS_ctrl_t * cc)82588447a05SGarrett D'Amore audiocs_free_ctrl(CS_ctrl_t *cc)
82688447a05SGarrett D'Amore {
82788447a05SGarrett D'Amore 	if (cc == NULL)
82888447a05SGarrett D'Amore 		return;
82988447a05SGarrett D'Amore 	if (cc->cc_ctrl)
83088447a05SGarrett D'Amore 		audio_dev_del_control(cc->cc_ctrl);
83188447a05SGarrett D'Amore 	kmem_free(cc, sizeof (*cc));
83288447a05SGarrett D'Amore }
83388447a05SGarrett D'Amore 
83488447a05SGarrett D'Amore /*
83588447a05SGarrett D'Amore  * audiocs_add_controls
83688447a05SGarrett D'Amore  *
83788447a05SGarrett D'Amore  * Description:
83888447a05SGarrett D'Amore  *	Allocates and registers all controls for this device.
83988447a05SGarrett D'Amore  *
84088447a05SGarrett D'Amore  * Arguments:
84188447a05SGarrett D'Amore  *	CS_state_t	*state		Device soft state.
84288447a05SGarrett D'Amore  *
84388447a05SGarrett D'Amore  * Returns:
84488447a05SGarrett D'Amore  *	DDI_SUCCESS	All controls added and registered
84588447a05SGarrett D'Amore  *	DDI_FAILURE	At least one control was not added or registered.
84688447a05SGarrett D'Amore  */
84788447a05SGarrett D'Amore static int
audiocs_add_controls(CS_state_t * state)84888447a05SGarrett D'Amore audiocs_add_controls(CS_state_t *state)
84988447a05SGarrett D'Amore {
85088447a05SGarrett D'Amore #define	ADD_CTRL(CTL, ID, VAL)						\
85188447a05SGarrett D'Amore 	state->cs_##CTL = audiocs_alloc_ctrl(state, ID, VAL);		\
85288447a05SGarrett D'Amore 	if (state->cs_##CTL == NULL) {					\
85388447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev,				\
85488447a05SGarrett D'Amore 		    "unable to allocate %s control", #ID);		\
85588447a05SGarrett D'Amore 		return (DDI_FAILURE);					\
85688447a05SGarrett D'Amore 	}
85788447a05SGarrett D'Amore 
85888447a05SGarrett D'Amore 	ADD_CTRL(ogain, CTL_VOLUME, 0x4b4b);
85988447a05SGarrett D'Amore 	ADD_CTRL(igain, CTL_IGAIN, 0x3232);
86088447a05SGarrett D'Amore 	ADD_CTRL(mgain, CTL_MGAIN, 0);
86188447a05SGarrett D'Amore 	ADD_CTRL(micboost, CTL_MICBOOST, 0);
86288447a05SGarrett D'Amore 	ADD_CTRL(outputs, CTL_OUTPUTS, (state->cs_omask & ~state->cs_omod) |
86388447a05SGarrett D'Amore 	    (1U << OUTPUT_SPEAKER));
86488447a05SGarrett D'Amore 	ADD_CTRL(inputs, CTL_INPUTS, (1U << INPUT_MIC));
86588447a05SGarrett D'Amore 
86688447a05SGarrett D'Amore 	return (DDI_SUCCESS);
86788447a05SGarrett D'Amore }
868f275d02fSGarrett D'Amore 
86988447a05SGarrett D'Amore /*
87088447a05SGarrett D'Amore  * audiocs_del_controls
87188447a05SGarrett D'Amore  *
87288447a05SGarrett D'Amore  * Description:
87388447a05SGarrett D'Amore  *	Unregisters and frees all controls for this device.
87488447a05SGarrett D'Amore  *
87588447a05SGarrett D'Amore  * Arguments:
87688447a05SGarrett D'Amore  *	CS_state_t	*state		Device soft state.
87788447a05SGarrett D'Amore  */
87888447a05SGarrett D'Amore void
audiocs_del_controls(CS_state_t * state)87988447a05SGarrett D'Amore audiocs_del_controls(CS_state_t *state)
88088447a05SGarrett D'Amore {
88188447a05SGarrett D'Amore 	audiocs_free_ctrl(state->cs_ogain);
88288447a05SGarrett D'Amore 	audiocs_free_ctrl(state->cs_igain);
88388447a05SGarrett D'Amore 	audiocs_free_ctrl(state->cs_mgain);
88488447a05SGarrett D'Amore 	audiocs_free_ctrl(state->cs_micboost);
88588447a05SGarrett D'Amore 	audiocs_free_ctrl(state->cs_inputs);
88688447a05SGarrett D'Amore 	audiocs_free_ctrl(state->cs_outputs);
88788447a05SGarrett D'Amore }
88888447a05SGarrett D'Amore 
88988447a05SGarrett D'Amore 
89088447a05SGarrett D'Amore /*
89188447a05SGarrett D'Amore  * audiocs_chip_init()
89288447a05SGarrett D'Amore  *
89388447a05SGarrett D'Amore  * Description:
89488447a05SGarrett D'Amore  *	Power up the audio core, initialize the audio Codec, prepare the chip
89588447a05SGarrett D'Amore  *	for use.
89688447a05SGarrett D'Amore  *
89788447a05SGarrett D'Amore  * Arguments:
89888447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
89988447a05SGarrett D'Amore  *
90088447a05SGarrett D'Amore  * Returns:
90188447a05SGarrett D'Amore  *	DDI_SUCCESS			Chip initialized and ready to use
90288447a05SGarrett D'Amore  *	DDI_FAILURE			Chip not initialized and not ready
90388447a05SGarrett D'Amore  */
90488447a05SGarrett D'Amore static int
audiocs_chip_init(CS_state_t * state)90588447a05SGarrett D'Amore audiocs_chip_init(CS_state_t *state)
90688447a05SGarrett D'Amore {
90788447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
90888447a05SGarrett D'Amore 
90988447a05SGarrett D'Amore 	/* make sure we are powered up */
91088447a05SGarrett D'Amore 	CS4231_DMA_POWER(state, CS4231_PWR_ON);
91188447a05SGarrett D'Amore 
91288447a05SGarrett D'Amore 	CS4231_DMA_RESET(state);
91388447a05SGarrett D'Amore 
914f275d02fSGarrett D'Amore 	/* wait for the Codec before we continue */
915f275d02fSGarrett D'Amore 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
916f275d02fSGarrett D'Amore 		return (DDI_FAILURE);
917f275d02fSGarrett D'Amore 	}
918f275d02fSGarrett D'Amore 
91988447a05SGarrett D'Amore 	/* activate registers 16 -> 31 */
92088447a05SGarrett D'Amore 	SELIDX(state, MID_REG);
92188447a05SGarrett D'Amore 	ddi_put8(handle, &CS4231_IDR, MID_MODE2);
92288447a05SGarrett D'Amore 
92388447a05SGarrett D'Amore 	/* now figure out what version we have */
92488447a05SGarrett D'Amore 	SELIDX(state, VID_REG);
92588447a05SGarrett D'Amore 	if (ddi_get8(handle, &CS4231_IDR) & VID_A) {
92688447a05SGarrett D'Amore 		state->cs_revA = B_TRUE;
92788447a05SGarrett D'Amore 	} else {
92888447a05SGarrett D'Amore 		state->cs_revA = B_FALSE;
92988447a05SGarrett D'Amore 	}
93088447a05SGarrett D'Amore 
93188447a05SGarrett D'Amore 	/* get rid of annoying popping by muting the output channels */
93288447a05SGarrett D'Amore 	SELIDX(state, LDACO_REG);
93388447a05SGarrett D'Amore 	PUTIDX(state, LDACO_LDM | LDACO_MID_GAIN, LDAC0_VALID_MASK);
93488447a05SGarrett D'Amore 	SELIDX(state, RDACO_REG);
93588447a05SGarrett D'Amore 	PUTIDX(state, RDACO_RDM | RDACO_MID_GAIN, RDAC0_VALID_MASK);
93688447a05SGarrett D'Amore 
93788447a05SGarrett D'Amore 	/* initialize aux input channels to known gain values & muted */
93888447a05SGarrett D'Amore 	SELIDX(state, LAUX1_REG);
93988447a05SGarrett D'Amore 	PUTIDX(state, LAUX1_LX1M | LAUX1_UNITY_GAIN, LAUX1_VALID_MASK);
94088447a05SGarrett D'Amore 	SELIDX(state, RAUX1_REG);
94188447a05SGarrett D'Amore 	PUTIDX(state, RAUX1_RX1M | RAUX1_UNITY_GAIN, RAUX1_VALID_MASK);
94288447a05SGarrett D'Amore 	SELIDX(state, LAUX2_REG);
94388447a05SGarrett D'Amore 	PUTIDX(state, LAUX2_LX2M | LAUX2_UNITY_GAIN, LAUX2_VALID_MASK);
94488447a05SGarrett D'Amore 	SELIDX(state, RAUX2_REG);
94588447a05SGarrett D'Amore 	PUTIDX(state, RAUX2_RX2M | RAUX2_UNITY_GAIN, RAUX2_VALID_MASK);
94688447a05SGarrett D'Amore 
94788447a05SGarrett D'Amore 	/* initialize aux input channels to known gain values & muted */
94888447a05SGarrett D'Amore 	SELIDX(state, LLIC_REG);
94988447a05SGarrett D'Amore 	PUTIDX(state, LLIC_LLM | LLIC_UNITY_GAIN, LLIC_VALID_MASK);
95088447a05SGarrett D'Amore 	SELIDX(state, RLIC_REG);
95188447a05SGarrett D'Amore 	PUTIDX(state, RLIC_RLM | RLIC_UNITY_GAIN, RLIC_VALID_MASK);
95288447a05SGarrett D'Amore 
95388447a05SGarrett D'Amore 	/* program the sample rate, play and capture must be the same */
95488447a05SGarrett D'Amore 	SELIDX(state, FSDF_REG | IAR_MCE);
95588447a05SGarrett D'Amore 	PUTIDX(state, FS_48000 | PDF_LINEAR16NE | PDF_STEREO, FSDF_VALID_MASK);
956f275d02fSGarrett D'Amore 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
957f275d02fSGarrett D'Amore 		return (DDI_FAILURE);
958f275d02fSGarrett D'Amore 	}
959f275d02fSGarrett D'Amore 
96088447a05SGarrett D'Amore 	SELIDX(state, CDF_REG | IAR_MCE);
96188447a05SGarrett D'Amore 	PUTIDX(state, CDF_LINEAR16NE | CDF_STEREO, CDF_VALID_MASK);
962f275d02fSGarrett D'Amore 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
963f275d02fSGarrett D'Amore 		return (DDI_FAILURE);
964f275d02fSGarrett D'Amore 	}
96588447a05SGarrett D'Amore 
96688447a05SGarrett D'Amore 	/*
96788447a05SGarrett D'Amore 	 * Set up the Codec for playback and capture disabled, dual DMA, and
96888447a05SGarrett D'Amore 	 * playback and capture DMA.
96988447a05SGarrett D'Amore 	 */
97088447a05SGarrett D'Amore 	SELIDX(state, (INTC_REG | IAR_MCE));
97188447a05SGarrett D'Amore 	PUTIDX(state, INTC_DDC | INTC_PDMA | INTC_CDMA, INTC_VALID_MASK);
97288447a05SGarrett D'Amore 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
97388447a05SGarrett D'Amore 		return (DDI_FAILURE);
97488447a05SGarrett D'Amore 	}
97588447a05SGarrett D'Amore 
97688447a05SGarrett D'Amore 	/*
97788447a05SGarrett D'Amore 	 * Turn on the output level bit to be 2.8 Vpp. Also, don't go to 0 on
97888447a05SGarrett D'Amore 	 * underflow.
97988447a05SGarrett D'Amore 	 */
98088447a05SGarrett D'Amore 	SELIDX(state, AFE1_REG);
98188447a05SGarrett D'Amore 	PUTIDX(state, AFE1_OLB, AFE1_VALID_MASK);
98288447a05SGarrett D'Amore 
98388447a05SGarrett D'Amore 	/* turn on the high pass filter if Rev A */
98488447a05SGarrett D'Amore 	SELIDX(state, AFE2_REG);
98588447a05SGarrett D'Amore 	if (state->cs_revA) {
98688447a05SGarrett D'Amore 		PUTIDX(state, AFE2_HPF, AFE2_VALID_MASK);
98788447a05SGarrett D'Amore 	} else {
98888447a05SGarrett D'Amore 		PUTIDX(state, 0, AFE2_VALID_MASK);
98988447a05SGarrett D'Amore 	}
99088447a05SGarrett D'Amore 
991*68c47f65SGarrett D'Amore 
99288447a05SGarrett D'Amore 	/* clear the play and capture interrupt flags */
99388447a05SGarrett D'Amore 	SELIDX(state, AFS_REG);
99488447a05SGarrett D'Amore 	ddi_put8(handle, &CS4231_STATUS, (AFS_RESET_STATUS));
99588447a05SGarrett D'Amore 
99688447a05SGarrett D'Amore 	/* the play and record gains will be set by the audio mixer */
99788447a05SGarrett D'Amore 
99888447a05SGarrett D'Amore 	/* unmute the output */
99988447a05SGarrett D'Amore 	SELIDX(state, LDACO_REG);
100088447a05SGarrett D'Amore 	ANDIDX(state, ~LDACO_LDM, LDAC0_VALID_MASK);
100188447a05SGarrett D'Amore 	SELIDX(state, RDACO_REG);
100288447a05SGarrett D'Amore 	ANDIDX(state, ~RDACO_RDM, RDAC0_VALID_MASK);
100388447a05SGarrett D'Amore 
100488447a05SGarrett D'Amore 	/* unmute the mono speaker and mute mono in */
100588447a05SGarrett D'Amore 	SELIDX(state, MIOC_REG);
100688447a05SGarrett D'Amore 	PUTIDX(state, MIOC_MIM, MIOC_VALID_MASK);
100788447a05SGarrett D'Amore 
1008f275d02fSGarrett D'Amore 	audiocs_configure_output(state);
1009f275d02fSGarrett D'Amore 	audiocs_configure_input(state);
101088447a05SGarrett D'Amore 
101188447a05SGarrett D'Amore 	return (DDI_SUCCESS);
101288447a05SGarrett D'Amore }
101388447a05SGarrett D'Amore 
101488447a05SGarrett D'Amore /*
101588447a05SGarrett D'Amore  * audiocs_init_state()
101688447a05SGarrett D'Amore  *
101788447a05SGarrett D'Amore  * Description:
101888447a05SGarrett D'Amore  *	This routine initializes the audio driver's state structure and
101988447a05SGarrett D'Amore  *	maps in the registers. This also includes reading the properties.
102088447a05SGarrett D'Amore  *
102188447a05SGarrett D'Amore  *	CAUTION: This routine maps the registers and initializes a mutex.
102288447a05SGarrett D'Amore  *		 Failure cleanup is handled by cs4231_attach(). It is not
102388447a05SGarrett D'Amore  *		 handled locally by this routine.
102488447a05SGarrett D'Amore  *
102588447a05SGarrett D'Amore  * Arguments:
102688447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
102788447a05SGarrett D'Amore  *
102888447a05SGarrett D'Amore  * Returns:
102988447a05SGarrett D'Amore  *	DDI_SUCCESS			State structure initialized
103088447a05SGarrett D'Amore  *	DDI_FAILURE			State structure not initialized
103188447a05SGarrett D'Amore  */
103288447a05SGarrett D'Amore static int
audiocs_init_state(CS_state_t * state)103388447a05SGarrett D'Amore audiocs_init_state(CS_state_t *state)
103488447a05SGarrett D'Amore {
103588447a05SGarrett D'Amore 	audio_dev_t	*adev = state->cs_adev;
103688447a05SGarrett D'Amore 	dev_info_t	*dip = state->cs_dip;
103788447a05SGarrett D'Amore 	char		*prop_str;
103888447a05SGarrett D'Amore 	char		*pm_comp[] = {
103988447a05SGarrett D'Amore 				"NAME=audiocs audio device",
104088447a05SGarrett D'Amore 				"0=off",
104188447a05SGarrett D'Amore 				"1=on" };
104288447a05SGarrett D'Amore 
104388447a05SGarrett D'Amore 	/* set up the pm-components */
104488447a05SGarrett D'Amore 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
104588447a05SGarrett D'Amore 	    "pm-components", pm_comp, 3) != DDI_PROP_SUCCESS) {
104688447a05SGarrett D'Amore 		audio_dev_warn(adev, "couldn't create pm-components property");
104788447a05SGarrett D'Amore 		return (DDI_FAILURE);
104888447a05SGarrett D'Amore 	}
104988447a05SGarrett D'Amore 
105088447a05SGarrett D'Amore 	/* figure out which DMA engine hardware we have */
105188447a05SGarrett D'Amore 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
105288447a05SGarrett D'Amore 	    "dma-model", &prop_str) == DDI_PROP_SUCCESS) {
105388447a05SGarrett D'Amore 		if (strcmp(prop_str, "eb2dma") == 0) {
105488447a05SGarrett D'Amore 			state->cs_dma_engine = EB2_DMA;
105588447a05SGarrett D'Amore 			state->cs_dma_ops = &cs4231_eb2dma_ops;
105688447a05SGarrett D'Amore 		} else {
105788447a05SGarrett D'Amore 			state->cs_dma_engine = APC_DMA;
105888447a05SGarrett D'Amore 			state->cs_dma_ops = &cs4231_apcdma_ops;
105988447a05SGarrett D'Amore 		}
106088447a05SGarrett D'Amore 		ddi_prop_free(prop_str);
106188447a05SGarrett D'Amore 	} else {
106288447a05SGarrett D'Amore 		state->cs_dma_engine = APC_DMA;
106388447a05SGarrett D'Amore 		state->cs_dma_ops = &cs4231_apcdma_ops;
106488447a05SGarrett D'Amore 	}
106588447a05SGarrett D'Amore 
106688447a05SGarrett D'Amore 	/* cs_regs, cs_eb2_regs and cs_handles filled in later */
106788447a05SGarrett D'Amore 
106888447a05SGarrett D'Amore 	/* most of what's left is filled in when the registers are mapped */
106988447a05SGarrett D'Amore 
107088447a05SGarrett D'Amore 	audiocs_get_ports(state);
107188447a05SGarrett D'Amore 
107288447a05SGarrett D'Amore 	/* Allocate engines, must be done before register mapping called  */
107388447a05SGarrett D'Amore 	if ((audiocs_alloc_engine(state, CS4231_PLAY) != DDI_SUCCESS) ||
107488447a05SGarrett D'Amore 	    (audiocs_alloc_engine(state, CS4231_REC) != DDI_SUCCESS)) {
107588447a05SGarrett D'Amore 		return (DDI_FAILURE);
107688447a05SGarrett D'Amore 	}
107788447a05SGarrett D'Amore 
107888447a05SGarrett D'Amore 	/* Map in the registers */
107988447a05SGarrett D'Amore 	if (CS4231_DMA_MAP_REGS(state) == DDI_FAILURE) {
108088447a05SGarrett D'Amore 		return (DDI_FAILURE);
108188447a05SGarrett D'Amore 	}
108288447a05SGarrett D'Amore 
108388447a05SGarrett D'Amore 
108488447a05SGarrett D'Amore 	/* Allocate and add controls, must be done *after* registers mapped */
108588447a05SGarrett D'Amore 	if (audiocs_add_controls(state) != DDI_SUCCESS) {
108688447a05SGarrett D'Amore 		return (DDI_FAILURE);
108788447a05SGarrett D'Amore 	}
108888447a05SGarrett D'Amore 
108988447a05SGarrett D'Amore 	state->cs_suspended = B_FALSE;
109088447a05SGarrett D'Amore 	state->cs_powered = B_FALSE;
109188447a05SGarrett D'Amore 
109288447a05SGarrett D'Amore 	return (DDI_SUCCESS);
109388447a05SGarrett D'Amore }
109488447a05SGarrett D'Amore 
109588447a05SGarrett D'Amore /*
109688447a05SGarrett D'Amore  * audiocs_get_ports()
109788447a05SGarrett D'Amore  *
109888447a05SGarrett D'Amore  * Description:
109988447a05SGarrett D'Amore  *	Get which audiocs h/w version we have and use this to
110088447a05SGarrett D'Amore  *	determine the input and output ports as well whether or not
110188447a05SGarrett D'Amore  *	the hardware has internal loopbacks or not. We also have three
110288447a05SGarrett D'Amore  *	different ways for the properties to be specified, which we
110388447a05SGarrett D'Amore  *	also need to worry about.
110488447a05SGarrett D'Amore  *
110588447a05SGarrett D'Amore  * Vers	Platform(s)	DMA eng.	audio-module**	loopback
110688447a05SGarrett D'Amore  * a    SS-4+/SS-5+	apcdma		no		no
110788447a05SGarrett D'Amore  * b	Ultra-1&2	apcdma		no		yes
110888447a05SGarrett D'Amore  * c	positron	apcdma		no		yes
110988447a05SGarrett D'Amore  * d	PPC - retired
111088447a05SGarrett D'Amore  * e	x86 - retired
111188447a05SGarrett D'Amore  * f	tazmo		eb2dma		Perigee		no
111288447a05SGarrett D'Amore  * g	tazmo		eb2dma		Quark		yes
111388447a05SGarrett D'Amore  * h	darwin+		eb2dma		no		N/A
111488447a05SGarrett D'Amore  *
111588447a05SGarrett D'Amore  * Vers	model~		aux1*		aux2*
111688447a05SGarrett D'Amore  * a	N/A		N/A		N/A
111788447a05SGarrett D'Amore  * b	N/A		N/A		N/A
111888447a05SGarrett D'Amore  * c	N/A		N/A		N/A
111988447a05SGarrett D'Amore  * d	retired
112088447a05SGarrett D'Amore  * e	retired
112188447a05SGarrett D'Amore  * f	SUNW,CS4231f	N/A		N/A
112288447a05SGarrett D'Amore  * g	SUNW,CS4231g	N/A		N/A
112388447a05SGarrett D'Amore  * h	SUNW,CS4231h	cdrom		none
112488447a05SGarrett D'Amore  *
112588447a05SGarrett D'Amore  * *   = Replaces internal-loopback for latest property type, can be
112688447a05SGarrett D'Amore  *	 set to "cdrom", "loopback", or "none".
112788447a05SGarrett D'Amore  *
112888447a05SGarrett D'Amore  * **  = For plugin audio modules only. Starting with darwin, this
112988447a05SGarrett D'Amore  *	 property is replaces by the model property.
113088447a05SGarrett D'Amore  *
113188447a05SGarrett D'Amore  * ~   = Replaces audio-module.
113288447a05SGarrett D'Amore  *
113388447a05SGarrett D'Amore  * +   = Has the capability of having a cable run from the internal
113488447a05SGarrett D'Amore  *	 CD-ROM to the audio device.
113588447a05SGarrett D'Amore  *
113688447a05SGarrett D'Amore  * N/A = Not applicable, the property wasn't created for early
113788447a05SGarrett D'Amore  *	 platforms, or the property has been retired.
113888447a05SGarrett D'Amore  *
113988447a05SGarrett D'Amore  * NOTE: Older tazmo and quark machines don't have the model property.
114088447a05SGarrett D'Amore  *
114188447a05SGarrett D'Amore  * Arguments:
114288447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
114388447a05SGarrett D'Amore  */
114488447a05SGarrett D'Amore static void
audiocs_get_ports(CS_state_t * state)114588447a05SGarrett D'Amore audiocs_get_ports(CS_state_t *state)
114688447a05SGarrett D'Amore {
114788447a05SGarrett D'Amore 	dev_info_t	*dip = state->cs_dip;
114888447a05SGarrett D'Amore 	audio_dev_t	*adev = state->cs_adev;
114988447a05SGarrett D'Amore 	char		*prop_str;
115088447a05SGarrett D'Amore 
115188447a05SGarrett D'Amore 	/* First we set the common ports, etc. */
115288447a05SGarrett D'Amore 	state->cs_omask = state->cs_omod =
115388447a05SGarrett D'Amore 	    (1U << OUTPUT_SPEAKER) |
115488447a05SGarrett D'Amore 	    (1U << OUTPUT_HEADPHONES) |
115588447a05SGarrett D'Amore 	    (1U << OUTPUT_LINEOUT);
115688447a05SGarrett D'Amore 	state->cs_imask =
115788447a05SGarrett D'Amore 	    (1U << INPUT_MIC) |
115888447a05SGarrett D'Amore 	    (1U << INPUT_LINEIN) |
115988447a05SGarrett D'Amore 	    (1U << INPUT_STEREOMIX);
116088447a05SGarrett D'Amore 
116188447a05SGarrett D'Amore 	/* now we try the new "model" property */
116288447a05SGarrett D'Amore 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
116388447a05SGarrett D'Amore 	    "model", &prop_str) == DDI_PROP_SUCCESS) {
116488447a05SGarrett D'Amore 		if (strcmp(prop_str, "SUNW,CS4231h") == 0) {
116588447a05SGarrett D'Amore 			/* darwin */
116688447a05SGarrett D'Amore 			audio_dev_set_version(adev, CS_DEV_VERSION_H);
116788447a05SGarrett D'Amore 			state->cs_imask |= (1U << INPUT_CD);
116888447a05SGarrett D'Amore 			state->cs_omod = (1U << OUTPUT_SPEAKER);
116988447a05SGarrett D'Amore 		} else if (strcmp(prop_str, "SUNW,CS4231g") == 0) {
117088447a05SGarrett D'Amore 			/* quark audio module */
117188447a05SGarrett D'Amore 			audio_dev_set_version(adev, CS_DEV_VERSION_G);
117288447a05SGarrett D'Amore 			/*
117388447a05SGarrett D'Amore 			 * NB: This could do SUNVTS LOOPBACK, but we
117488447a05SGarrett D'Amore 			 * don't support it for now... owing to no
117588447a05SGarrett D'Amore 			 * support in framework.
117688447a05SGarrett D'Amore 			 */
117788447a05SGarrett D'Amore 		} else if (strcmp(prop_str, "SUNW,CS4231f") == 0) {
117888447a05SGarrett D'Amore 			/* tazmo */
117988447a05SGarrett D'Amore 			audio_dev_set_version(adev, CS_DEV_VERSION_F);
118088447a05SGarrett D'Amore 		} else {
118188447a05SGarrett D'Amore 			audio_dev_set_version(adev, prop_str);
118288447a05SGarrett D'Amore 			audio_dev_warn(adev,
118388447a05SGarrett D'Amore 			    "unknown audio model: %s, some parts of "
118488447a05SGarrett D'Amore 			    "audio may not work correctly", prop_str);
118588447a05SGarrett D'Amore 		}
118688447a05SGarrett D'Amore 		ddi_prop_free(prop_str);	/* done with the property */
118788447a05SGarrett D'Amore 	} else {	/* now try the older "audio-module" property */
118888447a05SGarrett D'Amore 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
118988447a05SGarrett D'Amore 		    DDI_PROP_DONTPASS, "audio-module", &prop_str) ==
119088447a05SGarrett D'Amore 		    DDI_PROP_SUCCESS) {
119188447a05SGarrett D'Amore 			switch (*prop_str) {
119288447a05SGarrett D'Amore 			case 'Q':	/* quark audio module */
119388447a05SGarrett D'Amore 				audio_dev_set_version(adev, CS_DEV_VERSION_G);
119488447a05SGarrett D'Amore 				/* See quark comment above about SunVTS */
119588447a05SGarrett D'Amore 				break;
119688447a05SGarrett D'Amore 			case 'P':	/* tazmo */
119788447a05SGarrett D'Amore 				audio_dev_set_version(adev, CS_DEV_VERSION_F);
119888447a05SGarrett D'Amore 				break;
119988447a05SGarrett D'Amore 			default:
120088447a05SGarrett D'Amore 				audio_dev_set_version(adev, prop_str);
120188447a05SGarrett D'Amore 				audio_dev_warn(adev,
120288447a05SGarrett D'Amore 				    "unknown audio module: %s, some "
120388447a05SGarrett D'Amore 				    "parts of audio may not work correctly",
120488447a05SGarrett D'Amore 				    prop_str);
120588447a05SGarrett D'Amore 				break;
120688447a05SGarrett D'Amore 			}
120788447a05SGarrett D'Amore 			ddi_prop_free(prop_str);	/* done with the prop */
120888447a05SGarrett D'Amore 		} else {	/* now try heuristics, ;-( */
120988447a05SGarrett D'Amore 			if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
121088447a05SGarrett D'Amore 			    DDI_PROP_DONTPASS, "internal-loopback", B_FALSE)) {
121188447a05SGarrett D'Amore 				if (state->cs_dma_engine == EB2_DMA) {
121288447a05SGarrett D'Amore 					audio_dev_set_version(adev,
121388447a05SGarrett D'Amore 					    CS_DEV_VERSION_C);
121488447a05SGarrett D'Amore 				} else {
121588447a05SGarrett D'Amore 					audio_dev_set_version(adev,
121688447a05SGarrett D'Amore 					    CS_DEV_VERSION_B);
121788447a05SGarrett D'Amore 				}
121888447a05SGarrett D'Amore 				/*
121988447a05SGarrett D'Amore 				 * Again, we don't support SunVTS for these
122088447a05SGarrett D'Amore 				 * boards, although we potentially could.
122188447a05SGarrett D'Amore 				 */
122288447a05SGarrett D'Amore 			} else {
122388447a05SGarrett D'Amore 				audio_dev_set_version(adev, CS_DEV_VERSION_A);
122488447a05SGarrett D'Amore 				state->cs_imask |= (1U << INPUT_CD);
122588447a05SGarrett D'Amore 			}
122688447a05SGarrett D'Amore 		}
122788447a05SGarrett D'Amore 	}
122888447a05SGarrett D'Amore }
122988447a05SGarrett D'Amore 
123088447a05SGarrett D'Amore /*
123188447a05SGarrett D'Amore  * audiocs_power_up()
123288447a05SGarrett D'Amore  *
123388447a05SGarrett D'Amore  * Description:
123488447a05SGarrett D'Amore  *	Power up the Codec and restore the codec's registers.
123588447a05SGarrett D'Amore  *
123688447a05SGarrett D'Amore  *	NOTE: We don't worry about locking since the only routines
123788447a05SGarrett D'Amore  *		that may call us are attach() and power() Both of
123888447a05SGarrett D'Amore  *		which should be the only threads in the driver.
123988447a05SGarrett D'Amore  *
124088447a05SGarrett D'Amore  * Arguments:
124188447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
124288447a05SGarrett D'Amore  */
124388447a05SGarrett D'Amore static void
audiocs_power_up(CS_state_t * state)124488447a05SGarrett D'Amore audiocs_power_up(CS_state_t *state)
124588447a05SGarrett D'Amore {
124688447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
124788447a05SGarrett D'Amore 	int			i;
124888447a05SGarrett D'Amore 
124988447a05SGarrett D'Amore 	/* turn on the Codec */
125088447a05SGarrett D'Amore 	CS4231_DMA_POWER(state, CS4231_PWR_ON);
125188447a05SGarrett D'Amore 
125288447a05SGarrett D'Amore 	/* reset the DMA engine(s) */
125388447a05SGarrett D'Amore 	CS4231_DMA_RESET(state);
125488447a05SGarrett D'Amore 
1255f275d02fSGarrett D'Amore 	(void) audiocs_poll_ready(state);
1256f275d02fSGarrett D'Amore 
125788447a05SGarrett D'Amore 	/*
125888447a05SGarrett D'Amore 	 * Reload the Codec's registers, the DMA engines will be
125988447a05SGarrett D'Amore 	 * taken care of when play and record start up again. But
126088447a05SGarrett D'Amore 	 * first enable registers 16 -> 31.
126188447a05SGarrett D'Amore 	 */
126288447a05SGarrett D'Amore 	SELIDX(state, MID_REG);
126388447a05SGarrett D'Amore 	PUTIDX(state, state->cs_save[MID_REG], MID_VALID_MASK);
126488447a05SGarrett D'Amore 
126588447a05SGarrett D'Amore 	for (i = 0; i < CS4231_REGS; i++) {
126688447a05SGarrett D'Amore 		/* restore Codec registers */
126788447a05SGarrett D'Amore 		SELIDX(state, (i | IAR_MCE));
126888447a05SGarrett D'Amore 		ddi_put8(handle, &CS4231_IDR, state->cs_save[i]);
1269f275d02fSGarrett D'Amore 		(void) audiocs_poll_ready(state);
127088447a05SGarrett D'Amore 	}
127188447a05SGarrett D'Amore 	/* clear MCE bit */
127288447a05SGarrett D'Amore 	SELIDX(state, 0);
127388447a05SGarrett D'Amore }
127488447a05SGarrett D'Amore 
127588447a05SGarrett D'Amore /*
127688447a05SGarrett D'Amore  * audiocs_power_down()
127788447a05SGarrett D'Amore  *
127888447a05SGarrett D'Amore  * Description:
127988447a05SGarrett D'Amore  *	Power down the Codec and save the codec's registers.
128088447a05SGarrett D'Amore  *
128188447a05SGarrett D'Amore  *	NOTE: See the note in cs4231_power_up() about locking.
128288447a05SGarrett D'Amore  *
128388447a05SGarrett D'Amore  * Arguments:
128488447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
128588447a05SGarrett D'Amore  */
128688447a05SGarrett D'Amore static void
audiocs_power_down(CS_state_t * state)128788447a05SGarrett D'Amore audiocs_power_down(CS_state_t *state)
128888447a05SGarrett D'Amore {
128988447a05SGarrett D'Amore 	ddi_acc_handle_t	handle;
129088447a05SGarrett D'Amore 	int			i;
129188447a05SGarrett D'Amore 
129288447a05SGarrett D'Amore 	handle = state->cs_handles.cs_codec_hndl;
129388447a05SGarrett D'Amore 
129488447a05SGarrett D'Amore 	/*
129588447a05SGarrett D'Amore 	 * We are powering down, so we don't need to do a thing with
129688447a05SGarrett D'Amore 	 * the DMA engines. However, we do need to save the Codec
129788447a05SGarrett D'Amore 	 * registers.
129888447a05SGarrett D'Amore 	 */
129988447a05SGarrett D'Amore 
130088447a05SGarrett D'Amore 	for (i = 0; i < CS4231_REGS; i++) {
130188447a05SGarrett D'Amore 		/* save Codec regs */
130288447a05SGarrett D'Amore 		SELIDX(state, i);
130388447a05SGarrett D'Amore 		state->cs_save[i] = ddi_get8(handle, &CS4231_IDR);
130488447a05SGarrett D'Amore 	}
130588447a05SGarrett D'Amore 
130688447a05SGarrett D'Amore 	/* turn off the Codec */
130788447a05SGarrett D'Amore 	CS4231_DMA_POWER(state, CS4231_PWR_OFF);
130888447a05SGarrett D'Amore 
130988447a05SGarrett D'Amore }	/* cs4231_power_down() */
131088447a05SGarrett D'Amore 
131188447a05SGarrett D'Amore /*
131288447a05SGarrett D'Amore  * audiocs_configure_input()
131388447a05SGarrett D'Amore  *
131488447a05SGarrett D'Amore  * Description:
131588447a05SGarrett D'Amore  *	Configure input properties of the mixer (e.g. igain, ports).
131688447a05SGarrett D'Amore  *
131788447a05SGarrett D'Amore  * Arguments:
131888447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
131988447a05SGarrett D'Amore  */
132088447a05SGarrett D'Amore static void
audiocs_configure_input(CS_state_t * state)132188447a05SGarrett D'Amore audiocs_configure_input(CS_state_t *state)
132288447a05SGarrett D'Amore {
132388447a05SGarrett D'Amore 	uint8_t		l, r;
132488447a05SGarrett D'Amore 	uint64_t	inputs;
132588447a05SGarrett D'Amore 	uint64_t	micboost;
132688447a05SGarrett D'Amore 
132788447a05SGarrett D'Amore 	ASSERT(mutex_owned(&state->cs_lock));
132888447a05SGarrett D'Amore 
132988447a05SGarrett D'Amore 	inputs = state->cs_inputs->cc_val;
133088447a05SGarrett D'Amore 	micboost = state->cs_micboost->cc_val;
133188447a05SGarrett D'Amore 	r = (state->cs_igain->cc_val & 0xff);
133288447a05SGarrett D'Amore 	l = ((state->cs_igain->cc_val & 0xff00) >> 8);
133388447a05SGarrett D'Amore 
133488447a05SGarrett D'Amore 	/* rescale these for our atten array */
133588447a05SGarrett D'Amore 	l = (((uint32_t)l * 255) / 100) & 0xff;
133688447a05SGarrett D'Amore 	r = (((uint32_t)r * 255) / 100) & 0xff;
133788447a05SGarrett D'Amore 
133888447a05SGarrett D'Amore 	/* we downshift by 4 bits -- igain only has 16 possible values */
133988447a05SGarrett D'Amore 	/* NB: that we do not scale here!  The SADA driver didn't do so. */
134088447a05SGarrett D'Amore 	l = l >> 4;
134188447a05SGarrett D'Amore 	r = r >> 4;
134288447a05SGarrett D'Amore 
134388447a05SGarrett D'Amore 	if (inputs & (1U << INPUT_MIC)) {
134488447a05SGarrett D'Amore 		l |= LADCI_LMIC;
134588447a05SGarrett D'Amore 		r |= RADCI_RMIC;
134688447a05SGarrett D'Amore 	}
134788447a05SGarrett D'Amore 	if (inputs & (1U << INPUT_LINEIN)) {
134888447a05SGarrett D'Amore 		l |= LADCI_LLINE;
134988447a05SGarrett D'Amore 		r |= RADCI_RLINE;
135088447a05SGarrett D'Amore 	}
135188447a05SGarrett D'Amore 	if (inputs & (1U << INPUT_CD)) {
135288447a05SGarrett D'Amore 		/* note that SunVTS also uses this */
135388447a05SGarrett D'Amore 		l |= LADCI_LAUX1;
135488447a05SGarrett D'Amore 		r |= RADCI_RAUX1;
135588447a05SGarrett D'Amore 	}
135688447a05SGarrett D'Amore 	if (inputs & (1U << INPUT_STEREOMIX)) {
135788447a05SGarrett D'Amore 		l |= LADCI_LLOOP;
135888447a05SGarrett D'Amore 		r |= RADCI_RLOOP;
135988447a05SGarrett D'Amore 	}
136088447a05SGarrett D'Amore 	if (micboost) {
136188447a05SGarrett D'Amore 		l |= LADCI_LMGE;
136288447a05SGarrett D'Amore 		r |= RADCI_RMGE;
136388447a05SGarrett D'Amore 	}
136488447a05SGarrett D'Amore 
136588447a05SGarrett D'Amore 	SELIDX(state, LADCI_REG);
136688447a05SGarrett D'Amore 	PUTIDX(state, l, LADCI_VALID_MASK);
136788447a05SGarrett D'Amore 
136888447a05SGarrett D'Amore 	SELIDX(state, RADCI_REG);
136988447a05SGarrett D'Amore 	PUTIDX(state, r, RADCI_VALID_MASK);
137088447a05SGarrett D'Amore }
137188447a05SGarrett D'Amore 
137288447a05SGarrett D'Amore /*
137388447a05SGarrett D'Amore  * audiocs_configure_output()
137488447a05SGarrett D'Amore  *
137588447a05SGarrett D'Amore  * Description:
137688447a05SGarrett D'Amore  *	Configure output properties of the mixer (e.g. ogain, mgain).
137788447a05SGarrett D'Amore  *
137888447a05SGarrett D'Amore  * Arguments:
137988447a05SGarrett D'Amore  *	CS_state_t	*state		The device's state structure
138088447a05SGarrett D'Amore  */
138188447a05SGarrett D'Amore static void
audiocs_configure_output(CS_state_t * state)138288447a05SGarrett D'Amore audiocs_configure_output(CS_state_t *state)
138388447a05SGarrett D'Amore {
138488447a05SGarrett D'Amore 	uint64_t		outputs;
138588447a05SGarrett D'Amore 	uint8_t			l, r;
138688447a05SGarrett D'Amore 	uint8_t			rmute, lmute;
138788447a05SGarrett D'Amore 	uint8_t			mgain;
138888447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
138988447a05SGarrett D'Amore 
139088447a05SGarrett D'Amore 	rmute = lmute = 0;
139188447a05SGarrett D'Amore 
139288447a05SGarrett D'Amore 	ASSERT(mutex_owned(&state->cs_lock));
139388447a05SGarrett D'Amore 
139488447a05SGarrett D'Amore 	outputs = state->cs_outputs->cc_val;
139588447a05SGarrett D'Amore 
139688447a05SGarrett D'Amore 	/* port selection */
139788447a05SGarrett D'Amore 	SELIDX(state, MIOC_REG);
139888447a05SGarrett D'Amore 	if (outputs & (1U << OUTPUT_SPEAKER)) {
139988447a05SGarrett D'Amore 		ANDIDX(state, ~MIOC_MONO_SPKR_MUTE, MIOC_VALID_MASK);
140088447a05SGarrett D'Amore 	} else {
140188447a05SGarrett D'Amore 		ORIDX(state, MIOC_MONO_SPKR_MUTE, MIOC_VALID_MASK);
140288447a05SGarrett D'Amore 	}
140388447a05SGarrett D'Amore 	SELIDX(state, PC_REG);
140488447a05SGarrett D'Amore 	if (outputs & (1U << OUTPUT_HEADPHONES)) {
140588447a05SGarrett D'Amore 		ANDIDX(state, ~PC_HEADPHONE_MUTE, PC_VALID_MASK);
140688447a05SGarrett D'Amore 	} else {
140788447a05SGarrett D'Amore 		ORIDX(state, PC_HEADPHONE_MUTE, PC_VALID_MASK);
140888447a05SGarrett D'Amore 	}
140988447a05SGarrett D'Amore 	SELIDX(state, PC_REG);
141088447a05SGarrett D'Amore 	if (outputs & (1U << OUTPUT_LINEOUT)) {
141188447a05SGarrett D'Amore 		ANDIDX(state, ~PC_LINE_OUT_MUTE, PC_VALID_MASK);
141288447a05SGarrett D'Amore 	} else {
141388447a05SGarrett D'Amore 		ORIDX(state, PC_LINE_OUT_MUTE, PC_VALID_MASK);
141488447a05SGarrett D'Amore 	}
141588447a05SGarrett D'Amore 
141688447a05SGarrett D'Amore 	/* monitor gain */
141788447a05SGarrett D'Amore 	mgain = cs4231_atten[((state->cs_mgain->cc_val * 255) / 100) & 0xff];
141888447a05SGarrett D'Amore 	SELIDX(state, LC_REG);
141988447a05SGarrett D'Amore 	if (mgain == 0) {
142088447a05SGarrett D'Amore 		/* disable loopbacks when gain == 0 */
142188447a05SGarrett D'Amore 		PUTIDX(state, LC_OFF, LC_VALID_MASK);
142288447a05SGarrett D'Amore 	} else {
142388447a05SGarrett D'Amore 		/* we use cs4231_atten[] to linearize attenuation */
142488447a05SGarrett D'Amore 		PUTIDX(state, (mgain << 2) | LC_LBE, LC_VALID_MASK);
142588447a05SGarrett D'Amore 	}
142688447a05SGarrett D'Amore 
142788447a05SGarrett D'Amore 	/* output gain */
142888447a05SGarrett D'Amore 	l = ((state->cs_ogain->cc_val >> 8) & 0xff);
142988447a05SGarrett D'Amore 	r = (state->cs_ogain->cc_val & 0xff);
143088447a05SGarrett D'Amore 	if (l == 0) {
143188447a05SGarrett D'Amore 		lmute = LDACO_LDM;
143288447a05SGarrett D'Amore 	}
143388447a05SGarrett D'Amore 	if (r == 0) {
143488447a05SGarrett D'Amore 		rmute = RDACO_RDM;
143588447a05SGarrett D'Amore 	}
143688447a05SGarrett D'Amore 
143788447a05SGarrett D'Amore 	/* rescale these for our atten array */
143888447a05SGarrett D'Amore 	l = cs4231_atten[(((uint32_t)l * 255) / 100) & 0xff] | lmute;
143988447a05SGarrett D'Amore 	r = cs4231_atten[(((uint32_t)r * 255) / 100) & 0xff] | rmute;
144088447a05SGarrett D'Amore 
144188447a05SGarrett D'Amore 	SELIDX(state, LDACO_REG);
144288447a05SGarrett D'Amore 	PUTIDX(state, l, LDAC0_VALID_MASK);
144388447a05SGarrett D'Amore 	SELIDX(state, RDACO_REG);
144488447a05SGarrett D'Amore 	PUTIDX(state, r, RDAC0_VALID_MASK);
144588447a05SGarrett D'Amore }
144688447a05SGarrett D'Amore 
144788447a05SGarrett D'Amore /*
144888447a05SGarrett D'Amore  * audiocs_get_value()
144988447a05SGarrett D'Amore  *
145088447a05SGarrett D'Amore  * Description:
145188447a05SGarrett D'Amore  *	Get a control value
145288447a05SGarrett D'Amore  *
145388447a05SGarrett D'Amore  * Arguments:
145488447a05SGarrett D'Amore  *	void		*arg		The device's state structure
145588447a05SGarrett D'Amore  *	uint64_t	*valp		Pointer to store value.
145688447a05SGarrett D'Amore  *
145788447a05SGarrett D'Amore  * Returns:
145888447a05SGarrett D'Amore  *	0		The Codec parameter has been retrieved.
145988447a05SGarrett D'Amore  */
146088447a05SGarrett D'Amore static int
audiocs_get_value(void * arg,uint64_t * valp)146188447a05SGarrett D'Amore audiocs_get_value(void *arg, uint64_t *valp)
146288447a05SGarrett D'Amore {
146388447a05SGarrett D'Amore 	CS_ctrl_t		*cc = arg;
146488447a05SGarrett D'Amore 	CS_state_t		*state = cc->cc_state;
146588447a05SGarrett D'Amore 
146688447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
146788447a05SGarrett D'Amore 	*valp = cc->cc_val;
146888447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
146988447a05SGarrett D'Amore 	return (0);
147088447a05SGarrett D'Amore }
147188447a05SGarrett D'Amore 
147288447a05SGarrett D'Amore 
147388447a05SGarrett D'Amore /*
147488447a05SGarrett D'Amore  * audiocs_set_ogain()
147588447a05SGarrett D'Amore  *
147688447a05SGarrett D'Amore  * Description:
147788447a05SGarrett D'Amore  *	Set the play gain.
147888447a05SGarrett D'Amore  *
147988447a05SGarrett D'Amore  * Arguments:
148088447a05SGarrett D'Amore  *	void		*arg		The device's state structure
148188447a05SGarrett D'Amore  *	uint64_t	val		The gain to set (both left and right)
148288447a05SGarrett D'Amore  *
148388447a05SGarrett D'Amore  * Returns:
148488447a05SGarrett D'Amore  *	0		The Codec parameter has been set
148588447a05SGarrett D'Amore  */
148688447a05SGarrett D'Amore static int
audiocs_set_ogain(void * arg,uint64_t val)148788447a05SGarrett D'Amore audiocs_set_ogain(void *arg, uint64_t val)
148888447a05SGarrett D'Amore {
148988447a05SGarrett D'Amore 	CS_ctrl_t		*cc = arg;
149088447a05SGarrett D'Amore 	CS_state_t		*state = cc->cc_state;
149188447a05SGarrett D'Amore 
149288447a05SGarrett D'Amore 	if ((val & ~0xffff) ||
149388447a05SGarrett D'Amore 	    ((val & 0xff) > 100) ||
149488447a05SGarrett D'Amore 	    (((val & 0xff00) >> 8) > 100))
149588447a05SGarrett D'Amore 		return (EINVAL);
149688447a05SGarrett D'Amore 
149788447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
149888447a05SGarrett D'Amore 	cc->cc_val = val;
149988447a05SGarrett D'Amore 	audiocs_configure_output(state);
150088447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
150188447a05SGarrett D'Amore 	return (0);
150288447a05SGarrett D'Amore }
150388447a05SGarrett D'Amore 
150488447a05SGarrett D'Amore /*
150588447a05SGarrett D'Amore  * audiocs_set_micboost()
150688447a05SGarrett D'Amore  *
150788447a05SGarrett D'Amore  * Description:
150888447a05SGarrett D'Amore  *	Set the 20 dB microphone boost.
150988447a05SGarrett D'Amore  *
151088447a05SGarrett D'Amore  * Arguments:
151188447a05SGarrett D'Amore  *	void		*arg		The device's state structure
151288447a05SGarrett D'Amore  *	uint64_t	val		The 1 to enable, 0 to disable.
151388447a05SGarrett D'Amore  *
151488447a05SGarrett D'Amore  * Returns:
151588447a05SGarrett D'Amore  *	0		The Codec parameter has been set
151688447a05SGarrett D'Amore  */
151788447a05SGarrett D'Amore static int
audiocs_set_micboost(void * arg,uint64_t val)151888447a05SGarrett D'Amore audiocs_set_micboost(void *arg, uint64_t val)
151988447a05SGarrett D'Amore {
152088447a05SGarrett D'Amore 	CS_ctrl_t	*cc = arg;
152188447a05SGarrett D'Amore 	CS_state_t	*state = cc->cc_state;
152288447a05SGarrett D'Amore 
152388447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
152488447a05SGarrett D'Amore 	cc->cc_val = val ? B_TRUE : B_FALSE;
152588447a05SGarrett D'Amore 	audiocs_configure_input(state);
152688447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
152788447a05SGarrett D'Amore 	return (0);
152888447a05SGarrett D'Amore }
152988447a05SGarrett D'Amore 
153088447a05SGarrett D'Amore /*
153188447a05SGarrett D'Amore  * audiocs_set_igain()
153288447a05SGarrett D'Amore  *
153388447a05SGarrett D'Amore  * Description:
153488447a05SGarrett D'Amore  *	Set the record gain.
153588447a05SGarrett D'Amore  *
153688447a05SGarrett D'Amore  * Arguments:
153788447a05SGarrett D'Amore  *	void		*arg		The device's state structure
153888447a05SGarrett D'Amore  *	uint64_t	val		The gain to set (both left and right)
153988447a05SGarrett D'Amore  *
154088447a05SGarrett D'Amore  * Returns:
154188447a05SGarrett D'Amore  *	0		The Codec parameter has been set
154288447a05SGarrett D'Amore  */
154388447a05SGarrett D'Amore static int
audiocs_set_igain(void * arg,uint64_t val)154488447a05SGarrett D'Amore audiocs_set_igain(void *arg, uint64_t val)
154588447a05SGarrett D'Amore {
154688447a05SGarrett D'Amore 	CS_ctrl_t	*cc = arg;
154788447a05SGarrett D'Amore 	CS_state_t	*state = cc->cc_state;
154888447a05SGarrett D'Amore 
154988447a05SGarrett D'Amore 	if ((val & ~0xffff) ||
155088447a05SGarrett D'Amore 	    ((val & 0xff) > 100) ||
155188447a05SGarrett D'Amore 	    (((val & 0xff00) >> 8) > 100))
155288447a05SGarrett D'Amore 		return (EINVAL);
155388447a05SGarrett D'Amore 
155488447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
155588447a05SGarrett D'Amore 	cc->cc_val = val;
155688447a05SGarrett D'Amore 	audiocs_configure_input(state);
155788447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
155888447a05SGarrett D'Amore 
155988447a05SGarrett D'Amore 	return (0);
156088447a05SGarrett D'Amore }
156188447a05SGarrett D'Amore 
156288447a05SGarrett D'Amore /*
156388447a05SGarrett D'Amore  * audiocs_set_inputs()
156488447a05SGarrett D'Amore  *
156588447a05SGarrett D'Amore  * Description:
156688447a05SGarrett D'Amore  *	Set the input ports.
156788447a05SGarrett D'Amore  *
156888447a05SGarrett D'Amore  * Arguments:
156988447a05SGarrett D'Amore  *	void		*arg		The device's state structure
157088447a05SGarrett D'Amore  *	uint64_t	val		The mask of output ports.
157188447a05SGarrett D'Amore  *
157288447a05SGarrett D'Amore  * Returns:
157388447a05SGarrett D'Amore  *	0		The Codec parameter has been set
157488447a05SGarrett D'Amore  */
157588447a05SGarrett D'Amore static int
audiocs_set_inputs(void * arg,uint64_t val)157688447a05SGarrett D'Amore audiocs_set_inputs(void *arg, uint64_t val)
157788447a05SGarrett D'Amore {
157888447a05SGarrett D'Amore 	CS_ctrl_t	*cc = arg;
157988447a05SGarrett D'Amore 	CS_state_t	*state = cc->cc_state;
158088447a05SGarrett D'Amore 
158188447a05SGarrett D'Amore 	if (val & ~(state->cs_imask))
158288447a05SGarrett D'Amore 		return (EINVAL);
158388447a05SGarrett D'Amore 
158488447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
158588447a05SGarrett D'Amore 	cc->cc_val = val;
158688447a05SGarrett D'Amore 	audiocs_configure_input(state);
158788447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
158888447a05SGarrett D'Amore 
158988447a05SGarrett D'Amore 	return (0);
159088447a05SGarrett D'Amore }
159188447a05SGarrett D'Amore 
159288447a05SGarrett D'Amore /*
159388447a05SGarrett D'Amore  * audiocs_set_outputs()
159488447a05SGarrett D'Amore  *
159588447a05SGarrett D'Amore  * Description:
159688447a05SGarrett D'Amore  *	Set the output ports.
159788447a05SGarrett D'Amore  *
159888447a05SGarrett D'Amore  * Arguments:
159988447a05SGarrett D'Amore  *	void		*arg		The device's state structure
160088447a05SGarrett D'Amore  *	uint64_t	val		The mask of input ports.
160188447a05SGarrett D'Amore  *
160288447a05SGarrett D'Amore  * Returns:
160388447a05SGarrett D'Amore  *	0		The Codec parameter has been set
160488447a05SGarrett D'Amore  */
160588447a05SGarrett D'Amore static int
audiocs_set_outputs(void * arg,uint64_t val)160688447a05SGarrett D'Amore audiocs_set_outputs(void *arg, uint64_t val)
160788447a05SGarrett D'Amore {
160888447a05SGarrett D'Amore 	CS_ctrl_t	*cc = arg;
160988447a05SGarrett D'Amore 	CS_state_t	*state = cc->cc_state;
161088447a05SGarrett D'Amore 
161188447a05SGarrett D'Amore 	if ((val & ~(state->cs_omod)) !=
161288447a05SGarrett D'Amore 	    (state->cs_omask & ~state->cs_omod))
161388447a05SGarrett D'Amore 		return (EINVAL);
161488447a05SGarrett D'Amore 
161588447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
161688447a05SGarrett D'Amore 	cc->cc_val = val;
161788447a05SGarrett D'Amore 	audiocs_configure_output(state);
161888447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
161988447a05SGarrett D'Amore 
162088447a05SGarrett D'Amore 	return (0);
162188447a05SGarrett D'Amore }
162288447a05SGarrett D'Amore 
162388447a05SGarrett D'Amore /*
162488447a05SGarrett D'Amore  * audiocs_set_mgain()
162588447a05SGarrett D'Amore  *
162688447a05SGarrett D'Amore  * Description:
162788447a05SGarrett D'Amore  *	Set the monitor gain.
162888447a05SGarrett D'Amore  *
162988447a05SGarrett D'Amore  * Arguments:
163088447a05SGarrett D'Amore  *	void		*arg		The device's state structure
163188447a05SGarrett D'Amore  *	uint64_t	val		The gain to set (monoaural).)
163288447a05SGarrett D'Amore  *
163388447a05SGarrett D'Amore  * Returns:
163488447a05SGarrett D'Amore  *	0		The Codec parameter has been set
163588447a05SGarrett D'Amore  */
163688447a05SGarrett D'Amore static int
audiocs_set_mgain(void * arg,uint64_t gain)163788447a05SGarrett D'Amore audiocs_set_mgain(void *arg, uint64_t gain)
163888447a05SGarrett D'Amore {
163988447a05SGarrett D'Amore 	CS_ctrl_t	*cc = arg;
164088447a05SGarrett D'Amore 	CS_state_t	*state = cc->cc_state;
164188447a05SGarrett D'Amore 
164288447a05SGarrett D'Amore 	if (gain > 100)
164388447a05SGarrett D'Amore 		return (EINVAL);
164488447a05SGarrett D'Amore 
164588447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
164688447a05SGarrett D'Amore 	cc->cc_val = gain;
164788447a05SGarrett D'Amore 	audiocs_configure_output(state);
164888447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
164988447a05SGarrett D'Amore 
165088447a05SGarrett D'Amore 	return (0);
165188447a05SGarrett D'Amore }
165288447a05SGarrett D'Amore 
165388447a05SGarrett D'Amore /*
165488447a05SGarrett D'Amore  * audiocs_open()
165588447a05SGarrett D'Amore  *
165688447a05SGarrett D'Amore  * Description:
165788447a05SGarrett D'Amore  *	Opens a DMA engine for use.
165888447a05SGarrett D'Amore  *
165988447a05SGarrett D'Amore  * Arguments:
166088447a05SGarrett D'Amore  *	void		*arg		The DMA engine to set up
166188447a05SGarrett D'Amore  *	int		flag		Open flags
1662*68c47f65SGarrett D'Amore  *	unsigned	*nframesp	Receives number of frames
166388447a05SGarrett D'Amore  *	caddr_t		*bufp		Receives kernel data buffer
166488447a05SGarrett D'Amore  *
166588447a05SGarrett D'Amore  * Returns:
166688447a05SGarrett D'Amore  *	0	on success
166788447a05SGarrett D'Amore  *	errno	on failure
166888447a05SGarrett D'Amore  */
166988447a05SGarrett D'Amore static int
audiocs_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)1670*68c47f65SGarrett D'Amore audiocs_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
167188447a05SGarrett D'Amore {
167288447a05SGarrett D'Amore 	CS_engine_t	*eng = arg;
167388447a05SGarrett D'Amore 	CS_state_t	*state = eng->ce_state;
167488447a05SGarrett D'Amore 	dev_info_t	*dip = state->cs_dip;
167588447a05SGarrett D'Amore 
167688447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(flag));
167788447a05SGarrett D'Amore 
167888447a05SGarrett D'Amore 	(void) pm_busy_component(dip, CS4231_COMPONENT);
167988447a05SGarrett D'Amore 	if (pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON) ==
168088447a05SGarrett D'Amore 	    DDI_FAILURE) {
168188447a05SGarrett D'Amore 
168288447a05SGarrett D'Amore 		/* match the busy call above */
168388447a05SGarrett D'Amore 		(void) pm_idle_component(dip, CS4231_COMPONENT);
168488447a05SGarrett D'Amore 
168588447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev, "power up failed");
168688447a05SGarrett D'Amore 	}
168788447a05SGarrett D'Amore 
168888447a05SGarrett D'Amore 	eng->ce_count = 0;
1689*68c47f65SGarrett D'Amore 	*nframesp = CS4231_NFRAMES;
169088447a05SGarrett D'Amore 	*bufp = eng->ce_kaddr;
169188447a05SGarrett D'Amore 
169288447a05SGarrett D'Amore 	return (0);
169388447a05SGarrett D'Amore }
169488447a05SGarrett D'Amore 
169588447a05SGarrett D'Amore /*
169688447a05SGarrett D'Amore  * audiocs_close()
169788447a05SGarrett D'Amore  *
169888447a05SGarrett D'Amore  * Description:
169988447a05SGarrett D'Amore  *	Closes an audio DMA engine that was previously opened.  Since
170088447a05SGarrett D'Amore  *	nobody is using it, we take this opportunity to possibly power
170188447a05SGarrett D'Amore  *	down the entire device.
170288447a05SGarrett D'Amore  *
170388447a05SGarrett D'Amore  * Arguments:
170488447a05SGarrett D'Amore  *	void	*arg		The DMA engine to shut down
170588447a05SGarrett D'Amore  */
170688447a05SGarrett D'Amore static void
audiocs_close(void * arg)170788447a05SGarrett D'Amore audiocs_close(void *arg)
170888447a05SGarrett D'Amore {
170988447a05SGarrett D'Amore 	CS_engine_t	*eng = arg;
171088447a05SGarrett D'Amore 	CS_state_t	*state = eng->ce_state;
171188447a05SGarrett D'Amore 
171288447a05SGarrett D'Amore 	(void) pm_idle_component(state->cs_dip, CS4231_COMPONENT);
171388447a05SGarrett D'Amore }
171488447a05SGarrett D'Amore 
171588447a05SGarrett D'Amore /*
171688447a05SGarrett D'Amore  * audiocs_stop()
171788447a05SGarrett D'Amore  *
171888447a05SGarrett D'Amore  * Description:
171988447a05SGarrett D'Amore  *	This is called by the framework to stop an engine that is
172088447a05SGarrett D'Amore  *	transferring data.
172188447a05SGarrett D'Amore  *
172288447a05SGarrett D'Amore  * Arguments:
172388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to stop
172488447a05SGarrett D'Amore  */
172588447a05SGarrett D'Amore static void
audiocs_stop(void * arg)172688447a05SGarrett D'Amore audiocs_stop(void *arg)
172788447a05SGarrett D'Amore {
1728*68c47f65SGarrett D'Amore 	CS_engine_t		*eng = arg;
1729*68c47f65SGarrett D'Amore 	CS_state_t		*state = eng->ce_state;
1730*68c47f65SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
173188447a05SGarrett D'Amore 
173288447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
1733*68c47f65SGarrett D'Amore 	/*
1734*68c47f65SGarrett D'Amore 	 * Stop the DMA engine.
1735*68c47f65SGarrett D'Amore 	 */
1736*68c47f65SGarrett D'Amore 	CS4231_DMA_STOP(state, eng);
1737*68c47f65SGarrett D'Amore 
1738*68c47f65SGarrett D'Amore 	/*
1739*68c47f65SGarrett D'Amore 	 * Stop the codec.
1740*68c47f65SGarrett D'Amore 	 */
1741*68c47f65SGarrett D'Amore 	SELIDX(state, INTC_REG);
1742*68c47f65SGarrett D'Amore 	ANDIDX(state, ~(eng->ce_codec_en), INTC_VALID_MASK);
174388447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
174488447a05SGarrett D'Amore }
174588447a05SGarrett D'Amore 
174688447a05SGarrett D'Amore /*
174788447a05SGarrett D'Amore  * audiocs_start()
174888447a05SGarrett D'Amore  *
174988447a05SGarrett D'Amore  * Description:
175088447a05SGarrett D'Amore  *	This is called by the framework to start an engine transferring data.
175188447a05SGarrett D'Amore  *
175288447a05SGarrett D'Amore  * Arguments:
175388447a05SGarrett D'Amore  *	void	*arg		The DMA engine to start
175488447a05SGarrett D'Amore  *
175588447a05SGarrett D'Amore  * Returns:
175688447a05SGarrett D'Amore  *	0 	on success, an errno otherwise
175788447a05SGarrett D'Amore  */
175888447a05SGarrett D'Amore static int
audiocs_start(void * arg)175988447a05SGarrett D'Amore audiocs_start(void *arg)
176088447a05SGarrett D'Amore {
1761*68c47f65SGarrett D'Amore 	CS_engine_t		*eng = arg;
1762*68c47f65SGarrett D'Amore 	CS_state_t		*state = eng->ce_state;
1763*68c47f65SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
1764*68c47f65SGarrett D'Amore 	uint8_t			mask;
1765*68c47f65SGarrett D'Amore 	uint8_t			value;
1766*68c47f65SGarrett D'Amore 	uint8_t			reg;
1767*68c47f65SGarrett D'Amore 	int			rv;
176888447a05SGarrett D'Amore 
176988447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
1770*68c47f65SGarrett D'Amore 
1771*68c47f65SGarrett D'Amore 	if (eng->ce_num == CS4231_PLAY) {
1772*68c47f65SGarrett D'Amore 		/* sample rate only set on play side */
1773*68c47f65SGarrett D'Amore 		value = FS_48000 | PDF_STEREO | PDF_LINEAR16NE;
1774*68c47f65SGarrett D'Amore 		reg = FSDF_REG;
1775*68c47f65SGarrett D'Amore 		mask = FSDF_VALID_MASK;
1776*68c47f65SGarrett D'Amore 	} else {
1777*68c47f65SGarrett D'Amore 		value = CDF_STEREO | CDF_LINEAR16NE;
1778*68c47f65SGarrett D'Amore 		reg = CDF_REG;
1779*68c47f65SGarrett D'Amore 		mask = CDF_VALID_MASK;
178088447a05SGarrett D'Amore 	}
1781*68c47f65SGarrett D'Amore 	eng->ce_curoff = 0;
1782*68c47f65SGarrett D'Amore 	eng->ce_curidx = 0;
1783*68c47f65SGarrett D'Amore 
1784*68c47f65SGarrett D'Amore 	SELIDX(state, reg | IAR_MCE);
1785*68c47f65SGarrett D'Amore 	PUTIDX(state, value, mask);
1786*68c47f65SGarrett D'Amore 
1787*68c47f65SGarrett D'Amore 	if (audiocs_poll_ready(state) != DDI_SUCCESS) {
1788*68c47f65SGarrett D'Amore 		rv = EIO;
1789*68c47f65SGarrett D'Amore 	} else if (CS4231_DMA_START(state, eng) != DDI_SUCCESS) {
1790*68c47f65SGarrett D'Amore 		rv = EIO;
1791*68c47f65SGarrett D'Amore 	} else {
1792*68c47f65SGarrett D'Amore 		/*
1793*68c47f65SGarrett D'Amore 		 * Start the codec.
1794*68c47f65SGarrett D'Amore 		 */
1795*68c47f65SGarrett D'Amore 		SELIDX(state, INTC_REG);
1796*68c47f65SGarrett D'Amore 		ORIDX(state, eng->ce_codec_en, INTC_VALID_MASK);
1797*68c47f65SGarrett D'Amore 		rv = 0;
1798*68c47f65SGarrett D'Amore 	}
1799*68c47f65SGarrett D'Amore 
180088447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
180188447a05SGarrett D'Amore 	return (rv);
180288447a05SGarrett D'Amore }
180388447a05SGarrett D'Amore 
180488447a05SGarrett D'Amore /*
180588447a05SGarrett D'Amore  * audiocs_format()
180688447a05SGarrett D'Amore  *
180788447a05SGarrett D'Amore  * Description:
180888447a05SGarrett D'Amore  *	Called by the framework to query the format of the device.
180988447a05SGarrett D'Amore  *
181088447a05SGarrett D'Amore  * Arguments:
181188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
181288447a05SGarrett D'Amore  *
181388447a05SGarrett D'Amore  * Returns:
181488447a05SGarrett D'Amore  *	AUDIO_FORMAT_S16_NE
181588447a05SGarrett D'Amore  */
181688447a05SGarrett D'Amore static int
audiocs_format(void * arg)181788447a05SGarrett D'Amore audiocs_format(void *arg)
181888447a05SGarrett D'Amore {
181988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
182088447a05SGarrett D'Amore 
182188447a05SGarrett D'Amore 	return (AUDIO_FORMAT_S16_NE);
182288447a05SGarrett D'Amore }
182388447a05SGarrett D'Amore 
182488447a05SGarrett D'Amore /*
182588447a05SGarrett D'Amore  * audiocs_channels()
182688447a05SGarrett D'Amore  *
182788447a05SGarrett D'Amore  * Description:
182888447a05SGarrett D'Amore  *	Called by the framework to query the channels of the device.
182988447a05SGarrett D'Amore  *
183088447a05SGarrett D'Amore  * Arguments:
183188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
183288447a05SGarrett D'Amore  *
183388447a05SGarrett D'Amore  * Returns:
183488447a05SGarrett D'Amore  *	2 (stereo)
183588447a05SGarrett D'Amore  */
183688447a05SGarrett D'Amore static int
audiocs_channels(void * arg)183788447a05SGarrett D'Amore audiocs_channels(void *arg)
183888447a05SGarrett D'Amore {
183988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
184088447a05SGarrett D'Amore 
184188447a05SGarrett D'Amore 	return (2);
184288447a05SGarrett D'Amore }
184388447a05SGarrett D'Amore 
184488447a05SGarrett D'Amore /*
184588447a05SGarrett D'Amore  * audiocs_rates()
184688447a05SGarrett D'Amore  *
184788447a05SGarrett D'Amore  * Description:
184888447a05SGarrett D'Amore  *	Called by the framework to query the sample rate of the device.
184988447a05SGarrett D'Amore  *
185088447a05SGarrett D'Amore  * Arguments:
185188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
185288447a05SGarrett D'Amore  *
185388447a05SGarrett D'Amore  * Returns:
185488447a05SGarrett D'Amore  *	48000
185588447a05SGarrett D'Amore  */
185688447a05SGarrett D'Amore static int
audiocs_rate(void * arg)185788447a05SGarrett D'Amore audiocs_rate(void *arg)
185888447a05SGarrett D'Amore {
185988447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(arg));
186088447a05SGarrett D'Amore 
186188447a05SGarrett D'Amore 	return (48000);
186288447a05SGarrett D'Amore }
186388447a05SGarrett D'Amore 
186488447a05SGarrett D'Amore /*
186588447a05SGarrett D'Amore  * audiocs_count()
186688447a05SGarrett D'Amore  *
186788447a05SGarrett D'Amore  * Description:
186888447a05SGarrett D'Amore  *	This is called by the framework to get the engine's frame counter
186988447a05SGarrett D'Amore  *
187088447a05SGarrett D'Amore  * Arguments:
187188447a05SGarrett D'Amore  *	void	*arg		The DMA engine to query
187288447a05SGarrett D'Amore  *
187388447a05SGarrett D'Amore  * Returns:
187488447a05SGarrett D'Amore  *	frame count for current engine
187588447a05SGarrett D'Amore  */
187688447a05SGarrett D'Amore static uint64_t
audiocs_count(void * arg)187788447a05SGarrett D'Amore audiocs_count(void *arg)
187888447a05SGarrett D'Amore {
1879*68c47f65SGarrett D'Amore 	CS_engine_t		*eng = arg;
1880*68c47f65SGarrett D'Amore 	CS_state_t		*state = eng->ce_state;
1881*68c47f65SGarrett D'Amore 	uint64_t		val;
1882*68c47f65SGarrett D'Amore 	uint32_t		off;
188388447a05SGarrett D'Amore 
188488447a05SGarrett D'Amore 	mutex_enter(&state->cs_lock);
1885*68c47f65SGarrett D'Amore 
1886*68c47f65SGarrett D'Amore 	off = CS4231_DMA_ADDR(state, eng);
1887*68c47f65SGarrett D'Amore 	ASSERT(off >= eng->ce_paddr);
1888*68c47f65SGarrett D'Amore 	off -= eng->ce_paddr;
1889*68c47f65SGarrett D'Amore 
1890*68c47f65SGarrett D'Amore 	/*
1891*68c47f65SGarrett D'Amore 	 * Every now and then, we get a value that is just a wee bit
1892*68c47f65SGarrett D'Amore 	 * too large.  This seems to be a small value related to
1893*68c47f65SGarrett D'Amore 	 * prefetch.  Rather than believe it, we just assume the last
1894*68c47f65SGarrett D'Amore 	 * offset in the buffer.  This should allow us to handle
1895*68c47f65SGarrett D'Amore 	 * wraps, but without inserting bogus sample counts.
1896*68c47f65SGarrett D'Amore 	 */
1897*68c47f65SGarrett D'Amore 	if (off >= CS4231_BUFSZ) {
1898*68c47f65SGarrett D'Amore 		off = CS4231_BUFSZ - 4;
1899*68c47f65SGarrett D'Amore 	}
1900*68c47f65SGarrett D'Amore 
1901*68c47f65SGarrett D'Amore 	off /= 4;
1902*68c47f65SGarrett D'Amore 
1903*68c47f65SGarrett D'Amore 	val = (off >= eng->ce_curoff) ?
1904*68c47f65SGarrett D'Amore 	    off - eng->ce_curoff :
1905*68c47f65SGarrett D'Amore 	    off + CS4231_NFRAMES - eng->ce_curoff;
1906*68c47f65SGarrett D'Amore 
1907*68c47f65SGarrett D'Amore 	eng->ce_count += val;
1908*68c47f65SGarrett D'Amore 	eng->ce_curoff = off;
190988447a05SGarrett D'Amore 	val = eng->ce_count;
1910*68c47f65SGarrett D'Amore 
1911*68c47f65SGarrett D'Amore 	/* while here, possibly reload the next address */
1912*68c47f65SGarrett D'Amore 	CS4231_DMA_RELOAD(state, eng);
191388447a05SGarrett D'Amore 	mutex_exit(&state->cs_lock);
191488447a05SGarrett D'Amore 
191588447a05SGarrett D'Amore 	return (val);
191688447a05SGarrett D'Amore }
191788447a05SGarrett D'Amore 
191888447a05SGarrett D'Amore /*
191988447a05SGarrett D'Amore  * audiocs_sync()
192088447a05SGarrett D'Amore  *
192188447a05SGarrett D'Amore  * Description:
192288447a05SGarrett D'Amore  *	This is called by the framework to synchronize DMA caches.
192388447a05SGarrett D'Amore  *
192488447a05SGarrett D'Amore  * Arguments:
192588447a05SGarrett D'Amore  *	void	*arg		The DMA engine to sync
192688447a05SGarrett D'Amore  */
192788447a05SGarrett D'Amore static void
audiocs_sync(void * arg,unsigned nframes)192888447a05SGarrett D'Amore audiocs_sync(void *arg, unsigned nframes)
192988447a05SGarrett D'Amore {
193088447a05SGarrett D'Amore 	CS_engine_t *eng = arg;
193188447a05SGarrett D'Amore 	_NOTE(ARGUNUSED(nframes));
193288447a05SGarrett D'Amore 
193388447a05SGarrett D'Amore 	(void) ddi_dma_sync(eng->ce_dmah, 0, 0, eng->ce_syncdir);
193488447a05SGarrett D'Amore }
193588447a05SGarrett D'Amore 
193688447a05SGarrett D'Amore /*
193788447a05SGarrett D'Amore  * audiocs_alloc_engine()
193888447a05SGarrett D'Amore  *
193988447a05SGarrett D'Amore  * Description:
194088447a05SGarrett D'Amore  *	Allocates the DMA handles and the memory for the DMA engine.
194188447a05SGarrett D'Amore  *
194288447a05SGarrett D'Amore  * Arguments:
194388447a05SGarrett D'Amore  *	CS_state_t	*dip	Pointer to the device's soft state
194488447a05SGarrett D'Amore  *	int		num	Engine number, CS4231_PLAY or CS4231_REC.
194588447a05SGarrett D'Amore  *
194688447a05SGarrett D'Amore  * Returns:
194788447a05SGarrett D'Amore  *	DDI_SUCCESS		Engine initialized.
194888447a05SGarrett D'Amore  *	DDI_FAILURE		Engine not initialized.
194988447a05SGarrett D'Amore  */
195088447a05SGarrett D'Amore int
audiocs_alloc_engine(CS_state_t * state,int num)195188447a05SGarrett D'Amore audiocs_alloc_engine(CS_state_t *state, int num)
195288447a05SGarrett D'Amore {
195388447a05SGarrett D'Amore 	unsigned		caps;
195488447a05SGarrett D'Amore 	int			dir;
195588447a05SGarrett D'Amore 	int			rc;
195688447a05SGarrett D'Amore 	audio_dev_t		*adev;
195788447a05SGarrett D'Amore 	dev_info_t		*dip;
195888447a05SGarrett D'Amore 	CS_engine_t		*eng;
195988447a05SGarrett D'Amore 	uint_t			ccnt;
196088447a05SGarrett D'Amore 	ddi_dma_cookie_t	dmac;
1961*68c47f65SGarrett D'Amore 	size_t			bufsz;
196288447a05SGarrett D'Amore 
196388447a05SGarrett D'Amore 	static ddi_device_acc_attr_t buf_attr = {
196488447a05SGarrett D'Amore 		DDI_DEVICE_ATTR_V0,
196588447a05SGarrett D'Amore 		DDI_NEVERSWAP_ACC,
196688447a05SGarrett D'Amore 		DDI_STRICTORDER_ACC
196788447a05SGarrett D'Amore 	};
196888447a05SGarrett D'Amore 
196988447a05SGarrett D'Amore 	adev = state->cs_adev;
197088447a05SGarrett D'Amore 	dip = state->cs_dip;
197188447a05SGarrett D'Amore 
197288447a05SGarrett D'Amore 	eng = kmem_zalloc(sizeof (*eng), KM_SLEEP);
197388447a05SGarrett D'Amore 	eng->ce_state = state;
197488447a05SGarrett D'Amore 	eng->ce_started = B_FALSE;
197588447a05SGarrett D'Amore 	eng->ce_num = num;
197688447a05SGarrett D'Amore 
197788447a05SGarrett D'Amore 	switch (num) {
197888447a05SGarrett D'Amore 	case CS4231_REC:
197988447a05SGarrett D'Amore 		dir = DDI_DMA_READ;
198088447a05SGarrett D'Amore 		caps = ENGINE_INPUT_CAP;
198188447a05SGarrett D'Amore 		eng->ce_syncdir = DDI_DMA_SYNC_FORKERNEL;
198288447a05SGarrett D'Amore 		eng->ce_codec_en = INTC_CEN;
198388447a05SGarrett D'Amore 		break;
198488447a05SGarrett D'Amore 	case CS4231_PLAY:
198588447a05SGarrett D'Amore 		dir = DDI_DMA_WRITE;
198688447a05SGarrett D'Amore 		caps = ENGINE_OUTPUT_CAP;
198788447a05SGarrett D'Amore 		eng->ce_syncdir = DDI_DMA_SYNC_FORDEV;
198888447a05SGarrett D'Amore 		eng->ce_codec_en = INTC_PEN;
198988447a05SGarrett D'Amore 		break;
199088447a05SGarrett D'Amore 	default:
199188447a05SGarrett D'Amore 		kmem_free(eng, sizeof (*eng));
199288447a05SGarrett D'Amore 		audio_dev_warn(adev, "bad engine number (%d)!", num);
199388447a05SGarrett D'Amore 		return (DDI_FAILURE);
199488447a05SGarrett D'Amore 	}
199588447a05SGarrett D'Amore 	state->cs_engines[num] = eng;
199688447a05SGarrett D'Amore 
199788447a05SGarrett D'Amore 	/* allocate dma handle */
199888447a05SGarrett D'Amore 	rc = ddi_dma_alloc_handle(dip, CS4231_DMA_ATTR(state), DDI_DMA_SLEEP,
199988447a05SGarrett D'Amore 	    NULL, &eng->ce_dmah);
200088447a05SGarrett D'Amore 	if (rc != DDI_SUCCESS) {
200188447a05SGarrett D'Amore 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
200288447a05SGarrett D'Amore 		return (DDI_FAILURE);
200388447a05SGarrett D'Amore 	}
200488447a05SGarrett D'Amore 	/* allocate DMA buffer */
2005*68c47f65SGarrett D'Amore 	rc = ddi_dma_mem_alloc(eng->ce_dmah, CS4231_BUFSZ, &buf_attr,
200688447a05SGarrett D'Amore 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &eng->ce_kaddr,
2007*68c47f65SGarrett D'Amore 	    &bufsz, &eng->ce_acch);
200888447a05SGarrett D'Amore 	if (rc == DDI_FAILURE) {
200988447a05SGarrett D'Amore 		audio_dev_warn(adev, "dma_mem_alloc failed");
201088447a05SGarrett D'Amore 		return (DDI_FAILURE);
201188447a05SGarrett D'Amore 	}
201288447a05SGarrett D'Amore 
201388447a05SGarrett D'Amore 	/* bind DMA buffer */
201488447a05SGarrett D'Amore 	rc = ddi_dma_addr_bind_handle(eng->ce_dmah, NULL,
2015*68c47f65SGarrett D'Amore 	    eng->ce_kaddr, CS4231_BUFSZ, dir | DDI_DMA_CONSISTENT,
201688447a05SGarrett D'Amore 	    DDI_DMA_SLEEP, NULL, &dmac, &ccnt);
201788447a05SGarrett D'Amore 	if ((rc != DDI_DMA_MAPPED) || (ccnt != 1)) {
201888447a05SGarrett D'Amore 		audio_dev_warn(adev,
201988447a05SGarrett D'Amore 		    "ddi_dma_addr_bind_handle failed: %d", rc);
202088447a05SGarrett D'Amore 		return (DDI_FAILURE);
202188447a05SGarrett D'Amore 	}
202288447a05SGarrett D'Amore 
2023*68c47f65SGarrett D'Amore 	eng->ce_paddr = dmac.dmac_address;
202488447a05SGarrett D'Amore 
202588447a05SGarrett D'Amore 	eng->ce_engine = audio_engine_alloc(&audiocs_engine_ops, caps);
202688447a05SGarrett D'Amore 	if (eng->ce_engine == NULL) {
202788447a05SGarrett D'Amore 		audio_dev_warn(adev, "audio_engine_alloc failed");
202888447a05SGarrett D'Amore 		return (DDI_FAILURE);
202988447a05SGarrett D'Amore 	}
203088447a05SGarrett D'Amore 
203188447a05SGarrett D'Amore 	audio_engine_set_private(eng->ce_engine, eng);
203288447a05SGarrett D'Amore 	audio_dev_add_engine(adev, eng->ce_engine);
203388447a05SGarrett D'Amore 	return (DDI_SUCCESS);
203488447a05SGarrett D'Amore }
203588447a05SGarrett D'Amore 
203688447a05SGarrett D'Amore /*
203788447a05SGarrett D'Amore  * audiocs_free_engine()
203888447a05SGarrett D'Amore  *
203988447a05SGarrett D'Amore  * Description:
204088447a05SGarrett D'Amore  *	This routine fress the engine and all associated resources.
204188447a05SGarrett D'Amore  *
204288447a05SGarrett D'Amore  * Arguments:
204388447a05SGarrett D'Amore  *	CS_engine_t	*eng	Engine to free.
204488447a05SGarrett D'Amore  */
204588447a05SGarrett D'Amore void
audiocs_free_engine(CS_engine_t * eng)204688447a05SGarrett D'Amore audiocs_free_engine(CS_engine_t *eng)
204788447a05SGarrett D'Amore {
204888447a05SGarrett D'Amore 	CS_state_t	*state = eng->ce_state;
204988447a05SGarrett D'Amore 	audio_dev_t	*adev = state->cs_adev;
205088447a05SGarrett D'Amore 
205188447a05SGarrett D'Amore 	if (eng == NULL)
205288447a05SGarrett D'Amore 		return;
205388447a05SGarrett D'Amore 	if (eng->ce_engine) {
205488447a05SGarrett D'Amore 		audio_dev_remove_engine(adev, eng->ce_engine);
205588447a05SGarrett D'Amore 		audio_engine_free(eng->ce_engine);
205688447a05SGarrett D'Amore 	}
2057*68c47f65SGarrett D'Amore 	if (eng->ce_paddr) {
205888447a05SGarrett D'Amore 		(void) ddi_dma_unbind_handle(eng->ce_dmah);
205988447a05SGarrett D'Amore 	}
206088447a05SGarrett D'Amore 	if (eng->ce_acch) {
206188447a05SGarrett D'Amore 		ddi_dma_mem_free(&eng->ce_acch);
206288447a05SGarrett D'Amore 	}
206388447a05SGarrett D'Amore 	if (eng->ce_dmah) {
206488447a05SGarrett D'Amore 		ddi_dma_free_handle(&eng->ce_dmah);
206588447a05SGarrett D'Amore 	}
206688447a05SGarrett D'Amore 	kmem_free(eng, sizeof (*eng));
206788447a05SGarrett D'Amore }
206888447a05SGarrett D'Amore 
206988447a05SGarrett D'Amore /*
207088447a05SGarrett D'Amore  * audiocs_poll_ready()
207188447a05SGarrett D'Amore  *
207288447a05SGarrett D'Amore  * Description:
207388447a05SGarrett D'Amore  *	This routine waits for the Codec to complete its initialization
207488447a05SGarrett D'Amore  *	sequence and is done with its autocalibration.
207588447a05SGarrett D'Amore  *
207688447a05SGarrett D'Amore  *	Early versions of the Codec have a bug that can take as long as
207788447a05SGarrett D'Amore  *	15 seconds to complete its initialization. For these cases we
207888447a05SGarrett D'Amore  *	use a timeout mechanism so we don't keep the machine locked up.
207988447a05SGarrett D'Amore  *
208088447a05SGarrett D'Amore  * Arguments:
208188447a05SGarrett D'Amore  *	CS_state_t	*state	The device's state structure
208288447a05SGarrett D'Amore  *
208388447a05SGarrett D'Amore  * Returns:
208488447a05SGarrett D'Amore  *	DDI_SUCCESS		The Codec is ready to continue
208588447a05SGarrett D'Amore  *	DDI_FAILURE		The Codec isn't ready to continue
208688447a05SGarrett D'Amore  */
208788447a05SGarrett D'Amore int
audiocs_poll_ready(CS_state_t * state)208888447a05SGarrett D'Amore audiocs_poll_ready(CS_state_t *state)
208988447a05SGarrett D'Amore {
209088447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
209188447a05SGarrett D'Amore 	int			x = 0;
209288447a05SGarrett D'Amore 	uint8_t			iar;
209388447a05SGarrett D'Amore 	uint8_t			idr;
209488447a05SGarrett D'Amore 
209588447a05SGarrett D'Amore 	ASSERT(state->cs_regs != NULL);
209688447a05SGarrett D'Amore 	ASSERT(handle != NULL);
209788447a05SGarrett D'Amore 
209888447a05SGarrett D'Amore 	/* wait for the chip to initialize itself */
209988447a05SGarrett D'Amore 	iar = ddi_get8(handle, &CS4231_IAR);
210088447a05SGarrett D'Amore 
210188447a05SGarrett D'Amore 	while ((iar & IAR_INIT) && x++ < CS4231_TIMEOUT) {
210288447a05SGarrett D'Amore 		drv_usecwait(50);
210388447a05SGarrett D'Amore 		iar = ddi_get8(handle, &CS4231_IAR);
210488447a05SGarrett D'Amore 	}
210588447a05SGarrett D'Amore 
210688447a05SGarrett D'Amore 	if (x >= CS4231_TIMEOUT) {
210788447a05SGarrett D'Amore 		return (DDI_FAILURE);
210888447a05SGarrett D'Amore 	}
210988447a05SGarrett D'Amore 
211088447a05SGarrett D'Amore 	x = 0;
211188447a05SGarrett D'Amore 
211288447a05SGarrett D'Amore 	/*
211388447a05SGarrett D'Amore 	 * Now wait for the chip to complete its autocalibration.
211488447a05SGarrett D'Amore 	 * Set the test register.
211588447a05SGarrett D'Amore 	 */
211688447a05SGarrett D'Amore 	SELIDX(state, ESI_REG);
211788447a05SGarrett D'Amore 
211888447a05SGarrett D'Amore 	idr = ddi_get8(handle, &CS4231_IDR);
211988447a05SGarrett D'Amore 
212088447a05SGarrett D'Amore 	while ((idr & ESI_ACI) && x++ < CS4231_TIMEOUT) {
212188447a05SGarrett D'Amore 		drv_usecwait(50);
212288447a05SGarrett D'Amore 		idr = ddi_get8(handle, &CS4231_IDR);
212388447a05SGarrett D'Amore 	}
212488447a05SGarrett D'Amore 
212588447a05SGarrett D'Amore 	if (x >= CS4231_TIMEOUT) {
212688447a05SGarrett D'Amore 		return (DDI_FAILURE);
212788447a05SGarrett D'Amore 	}
212888447a05SGarrett D'Amore 
212988447a05SGarrett D'Amore 
213088447a05SGarrett D'Amore 	return (DDI_SUCCESS);
213188447a05SGarrett D'Amore 
213288447a05SGarrett D'Amore }
213388447a05SGarrett D'Amore 
213488447a05SGarrett D'Amore /*
213588447a05SGarrett D'Amore  * audiocs_sel_index()
213688447a05SGarrett D'Amore  *
213788447a05SGarrett D'Amore  * Description:
213888447a05SGarrett D'Amore  *	Select a cs4231 register. The cs4231 has a hardware bug where a
213988447a05SGarrett D'Amore  *	register is not always selected the first time. We try and try
214088447a05SGarrett D'Amore  *	again until the proper register is selected or we time out and
214188447a05SGarrett D'Amore  *	print an error message.
214288447a05SGarrett D'Amore  *
214388447a05SGarrett D'Amore  * Arguments:
214488447a05SGarrett D'Amore  *	audiohdl_t	ahandle		Handle to this device
214588447a05SGarrett D'Amore  *	ddi_acc_handle_t handle		A handle to the device's registers
214688447a05SGarrett D'Amore  *	uint8_t		addr		The register address to program
214788447a05SGarrett D'Amore  *	int		reg		The register to select
214888447a05SGarrett D'Amore  */
214988447a05SGarrett D'Amore void
215088447a05SGarrett D'Amore #ifdef	DEBUG
audiocs_sel_index(CS_state_t * state,uint8_t reg,int n)215188447a05SGarrett D'Amore audiocs_sel_index(CS_state_t *state, uint8_t reg, int n)
215288447a05SGarrett D'Amore #else
215388447a05SGarrett D'Amore audiocs_sel_index(CS_state_t *state, uint8_t reg)
215488447a05SGarrett D'Amore #endif
215588447a05SGarrett D'Amore {
215688447a05SGarrett D'Amore 	int			x;
215788447a05SGarrett D'Amore 	uint8_t			T;
215888447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
215988447a05SGarrett D'Amore 	uint8_t			*addr = &CS4231_IAR;
216088447a05SGarrett D'Amore 
216188447a05SGarrett D'Amore 	for (x = 0; x < CS4231_RETRIES; x++) {
216288447a05SGarrett D'Amore 		ddi_put8(handle, addr, reg);
216388447a05SGarrett D'Amore 		T = ddi_get8(handle, addr);
216488447a05SGarrett D'Amore 		if (T == reg) {
216588447a05SGarrett D'Amore 			break;
216688447a05SGarrett D'Amore 		}
216788447a05SGarrett D'Amore 		drv_usecwait(1000);
216888447a05SGarrett D'Amore 	}
216988447a05SGarrett D'Amore 
217088447a05SGarrett D'Amore 	if (x == CS4231_RETRIES) {
217188447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev,
217288447a05SGarrett D'Amore #ifdef	DEBUG
217388447a05SGarrett D'Amore 		    "line %d: Couldn't select index (0x%02x 0x%02x)", n,
217488447a05SGarrett D'Amore #else
217588447a05SGarrett D'Amore 		    "Couldn't select index (0x%02x 0x%02x)",
217688447a05SGarrett D'Amore #endif
217788447a05SGarrett D'Amore 		    T, reg);
217888447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev,
217988447a05SGarrett D'Amore 		    "audio may not work correctly until it is stopped and "
218088447a05SGarrett D'Amore 		    "restarted");
218188447a05SGarrett D'Amore 	}
218288447a05SGarrett D'Amore }
218388447a05SGarrett D'Amore 
218488447a05SGarrett D'Amore /*
218588447a05SGarrett D'Amore  * audiocs_put_index()
218688447a05SGarrett D'Amore  *
218788447a05SGarrett D'Amore  * Description:
218888447a05SGarrett D'Amore  *	Program a cs4231 register. The cs4231 has a hardware bug where a
218988447a05SGarrett D'Amore  *	register is not programmed properly the first time. We program a value,
219088447a05SGarrett D'Amore  *	then immediately read back the value and reprogram if nescessary.
219188447a05SGarrett D'Amore  *	We do this until the register is properly programmed or we time out and
219288447a05SGarrett D'Amore  *	print an error message.
219388447a05SGarrett D'Amore  *
219488447a05SGarrett D'Amore  * Arguments:
219588447a05SGarrett D'Amore  *	CS_state_t	state		Handle to this device
219688447a05SGarrett D'Amore  *	uint8_t		mask		Mask to not set reserved register bits
219788447a05SGarrett D'Amore  *	int		val		The value to program
219888447a05SGarrett D'Amore  */
219988447a05SGarrett D'Amore void
220088447a05SGarrett D'Amore #ifdef DEBUG
audiocs_put_index(CS_state_t * state,uint8_t val,uint8_t mask,int n)220188447a05SGarrett D'Amore audiocs_put_index(CS_state_t *state, uint8_t val, uint8_t mask, int n)
220288447a05SGarrett D'Amore #else
220388447a05SGarrett D'Amore audiocs_put_index(CS_state_t *state, uint8_t val, uint8_t mask)
220488447a05SGarrett D'Amore #endif
220588447a05SGarrett D'Amore {
220688447a05SGarrett D'Amore 	int			x;
220788447a05SGarrett D'Amore 	uint8_t			T;
220888447a05SGarrett D'Amore 	ddi_acc_handle_t	handle = CODEC_HANDLE;
220988447a05SGarrett D'Amore 	uint8_t			*addr = &CS4231_IDR;
221088447a05SGarrett D'Amore 
221188447a05SGarrett D'Amore 	val &= mask;
221288447a05SGarrett D'Amore 
221388447a05SGarrett D'Amore 	for (x = 0; x < CS4231_RETRIES; x++) {
221488447a05SGarrett D'Amore 		ddi_put8(handle, addr, val);
221588447a05SGarrett D'Amore 		T = ddi_get8(handle, addr);
221688447a05SGarrett D'Amore 		if (T == val) {
221788447a05SGarrett D'Amore 			break;
221888447a05SGarrett D'Amore 		}
221988447a05SGarrett D'Amore 		drv_usecwait(1000);
222088447a05SGarrett D'Amore 	}
222188447a05SGarrett D'Amore 
222288447a05SGarrett D'Amore 	if (x == CS4231_RETRIES) {
222388447a05SGarrett D'Amore #ifdef DEBUG
222488447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev,
222588447a05SGarrett D'Amore 		    "line %d: Couldn't set value (0x%02x 0x%02x)", n, T, val);
222688447a05SGarrett D'Amore #else
222788447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev,
222888447a05SGarrett D'Amore 		    "Couldn't set value (0x%02x 0x%02x)", T, val);
222988447a05SGarrett D'Amore #endif
223088447a05SGarrett D'Amore 		audio_dev_warn(state->cs_adev,
223188447a05SGarrett D'Amore 		    "audio may not work correctly until it is stopped and "
223288447a05SGarrett D'Amore 		    "restarted");
223388447a05SGarrett D'Amore 	}
223488447a05SGarrett D'Amore }
2235