xref: /onnv-gate/usr/src/uts/sun/io/audio/drv/audiocs/audio_4231.c (revision 11936:54dc8a89ba0d)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
22*11936Sgdamore@opensolaris.org  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
239484Sgarrett.damore@Sun.COM  * Use is subject to license terms.
249484Sgarrett.damore@Sun.COM  */
259484Sgarrett.damore@Sun.COM 
269484Sgarrett.damore@Sun.COM 
279484Sgarrett.damore@Sun.COM /*
289484Sgarrett.damore@Sun.COM  * audiocs Audio Driver
299484Sgarrett.damore@Sun.COM  *
309484Sgarrett.damore@Sun.COM  * This Audio Driver controls the Crystal CS4231 Codec used on many SPARC
319484Sgarrett.damore@Sun.COM  * platforms. It does not support the CS4231 on Power PCs or x86 PCs. It
329484Sgarrett.damore@Sun.COM  * does support two different DMA engines, the APC and EB2. The code for
339484Sgarrett.damore@Sun.COM  * those DMA engines is split out and a well defined, but private, interface
349484Sgarrett.damore@Sun.COM  * is used to control those DMA engines.
359484Sgarrett.damore@Sun.COM  *
369484Sgarrett.damore@Sun.COM  * For some reason setting the CS4231's registers doesn't always
379484Sgarrett.damore@Sun.COM  * succeed.  Therefore every time we set a register we always read it
389484Sgarrett.damore@Sun.COM  * back to make sure it was set. If not we wait a little while and
399484Sgarrett.damore@Sun.COM  * then try again. This is all taken care of in the routines
409484Sgarrett.damore@Sun.COM  * audiocs_put_index() and audiocs_sel_index() and the macros ORIDX()
419484Sgarrett.damore@Sun.COM  * and ANDIDX(). We don't worry about the status register because it
429484Sgarrett.damore@Sun.COM  * is cleared by writing anything to it.  So it doesn't matter what
439484Sgarrett.damore@Sun.COM  * the value written is.
449484Sgarrett.damore@Sun.COM  *
459484Sgarrett.damore@Sun.COM  * This driver supports suspending and resuming. A suspend just stops playing
469484Sgarrett.damore@Sun.COM  * and recording. The play DMA buffers end up getting thrown away, but when
479484Sgarrett.damore@Sun.COM  * you shut down the machine there is a break in the audio anyway, so they
489484Sgarrett.damore@Sun.COM  * won't be missed and it isn't worth the effort to save them. When we resume
499484Sgarrett.damore@Sun.COM  * we always start playing and recording. If they aren't needed they get
509484Sgarrett.damore@Sun.COM  * shut off by the mixer.
519484Sgarrett.damore@Sun.COM  *
529484Sgarrett.damore@Sun.COM  * Power management is supported by this driver.
539484Sgarrett.damore@Sun.COM  *
549484Sgarrett.damore@Sun.COM  *	NOTE: This module depends on drv/audio being loaded first.
559484Sgarrett.damore@Sun.COM  */
569484Sgarrett.damore@Sun.COM 
579484Sgarrett.damore@Sun.COM #include <sys/modctl.h>
589484Sgarrett.damore@Sun.COM #include <sys/kmem.h>
599484Sgarrett.damore@Sun.COM #include <sys/stropts.h>
609484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
619484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
629484Sgarrett.damore@Sun.COM #include <sys/note.h>
639484Sgarrett.damore@Sun.COM #include <sys/audio/audio_driver.h>
649484Sgarrett.damore@Sun.COM #include "audio_4231.h"
659484Sgarrett.damore@Sun.COM 
669484Sgarrett.damore@Sun.COM /*
679484Sgarrett.damore@Sun.COM  * Module linkage routines for the kernel
689484Sgarrett.damore@Sun.COM  */
699484Sgarrett.damore@Sun.COM static int audiocs_ddi_attach(dev_info_t *, ddi_attach_cmd_t);
709484Sgarrett.damore@Sun.COM static int audiocs_ddi_detach(dev_info_t *, ddi_detach_cmd_t);
719484Sgarrett.damore@Sun.COM static int audiocs_ddi_power(dev_info_t *, int, int);
729484Sgarrett.damore@Sun.COM 
739484Sgarrett.damore@Sun.COM /*
749484Sgarrett.damore@Sun.COM  * Entry point routine prototypes
759484Sgarrett.damore@Sun.COM  */
76*11936Sgdamore@opensolaris.org static int audiocs_open(void *, int, unsigned *, caddr_t *);
779484Sgarrett.damore@Sun.COM static void audiocs_close(void *);
789484Sgarrett.damore@Sun.COM static int audiocs_start(void *);
799484Sgarrett.damore@Sun.COM static void audiocs_stop(void *);
809484Sgarrett.damore@Sun.COM static int audiocs_format(void *);
819484Sgarrett.damore@Sun.COM static int audiocs_channels(void *);
829484Sgarrett.damore@Sun.COM static int audiocs_rate(void *);
839484Sgarrett.damore@Sun.COM static uint64_t audiocs_count(void *);
849484Sgarrett.damore@Sun.COM static void audiocs_sync(void *, unsigned);
859484Sgarrett.damore@Sun.COM 
869484Sgarrett.damore@Sun.COM /*
879484Sgarrett.damore@Sun.COM  * Control callbacks.
889484Sgarrett.damore@Sun.COM  */
899484Sgarrett.damore@Sun.COM static int audiocs_get_value(void *, uint64_t *);
909484Sgarrett.damore@Sun.COM static int audiocs_set_ogain(void *, uint64_t);
919484Sgarrett.damore@Sun.COM static int audiocs_set_igain(void *, uint64_t);
929484Sgarrett.damore@Sun.COM static int audiocs_set_mgain(void *, uint64_t);
939484Sgarrett.damore@Sun.COM static int audiocs_set_inputs(void *, uint64_t);
949484Sgarrett.damore@Sun.COM static int audiocs_set_outputs(void *, uint64_t);
959484Sgarrett.damore@Sun.COM static int audiocs_set_micboost(void *, uint64_t);
969484Sgarrett.damore@Sun.COM 
979484Sgarrett.damore@Sun.COM /* Local Routines */
989484Sgarrett.damore@Sun.COM static int audiocs_resume(dev_info_t *);
999484Sgarrett.damore@Sun.COM static int audiocs_attach(dev_info_t *);
1009484Sgarrett.damore@Sun.COM static int audiocs_detach(dev_info_t *);
1019484Sgarrett.damore@Sun.COM static int audiocs_suspend(dev_info_t *);
1029484Sgarrett.damore@Sun.COM 
1039484Sgarrett.damore@Sun.COM static void audiocs_destroy(CS_state_t *);
1049484Sgarrett.damore@Sun.COM static int audiocs_init_state(CS_state_t *);
1059484Sgarrett.damore@Sun.COM static int audiocs_chip_init(CS_state_t *);
1069484Sgarrett.damore@Sun.COM static int audiocs_alloc_engine(CS_state_t *, int);
1079484Sgarrett.damore@Sun.COM static void audiocs_free_engine(CS_engine_t *);
1089484Sgarrett.damore@Sun.COM static void audiocs_get_ports(CS_state_t *);
1099484Sgarrett.damore@Sun.COM static void audiocs_configure_input(CS_state_t *);
1109484Sgarrett.damore@Sun.COM static void audiocs_configure_output(CS_state_t *);
1119484Sgarrett.damore@Sun.COM static CS_ctrl_t *audiocs_alloc_ctrl(CS_state_t *, uint32_t, uint64_t);
1129484Sgarrett.damore@Sun.COM static void audiocs_free_ctrl(CS_ctrl_t *);
1139484Sgarrett.damore@Sun.COM static int audiocs_add_controls(CS_state_t *);
1149484Sgarrett.damore@Sun.COM static void audiocs_del_controls(CS_state_t *);
1159484Sgarrett.damore@Sun.COM static void audiocs_power_up(CS_state_t *);
1169484Sgarrett.damore@Sun.COM static void audiocs_power_down(CS_state_t *);
1179484Sgarrett.damore@Sun.COM static int audiocs_poll_ready(CS_state_t *);
1189484Sgarrett.damore@Sun.COM #ifdef	DEBUG
1199484Sgarrett.damore@Sun.COM static void audiocs_put_index(CS_state_t *,  uint8_t, uint8_t, int);
1209484Sgarrett.damore@Sun.COM static void audiocs_sel_index(CS_state_t *, uint8_t, int);
1219484Sgarrett.damore@Sun.COM #define	SELIDX(s, idx)		audiocs_sel_index(s, idx, __LINE__)
1229484Sgarrett.damore@Sun.COM #define	PUTIDX(s, val, mask)	audiocs_put_index(s, val, mask, __LINE__)
1239484Sgarrett.damore@Sun.COM #else
1249484Sgarrett.damore@Sun.COM static void audiocs_put_index(CS_state_t *,  uint8_t, uint8_t);
1259484Sgarrett.damore@Sun.COM static void audiocs_sel_index(CS_state_t *, uint8_t);
1269484Sgarrett.damore@Sun.COM #define	SELIDX(s, idx)		audiocs_sel_index(s, idx)
1279484Sgarrett.damore@Sun.COM #define	PUTIDX(s, val, mask)	audiocs_put_index(s, val, mask)
1289484Sgarrett.damore@Sun.COM #endif
129*11936Sgdamore@opensolaris.org #define	GETIDX(s)		ddi_get8((handle), &CS4231_IDR)
1309484Sgarrett.damore@Sun.COM 
1319484Sgarrett.damore@Sun.COM #define	ORIDX(s, val, mask)						\
1329484Sgarrett.damore@Sun.COM 	PUTIDX(s,							\
1339484Sgarrett.damore@Sun.COM 	    (ddi_get8((handle), &CS4231_IDR) | (uint8_t)(val)),		\
1349484Sgarrett.damore@Sun.COM 	    (uint8_t)(mask))
1359484Sgarrett.damore@Sun.COM 
1369484Sgarrett.damore@Sun.COM #define	ANDIDX(s, val, mask)						\
1379484Sgarrett.damore@Sun.COM 	PUTIDX(s, (ddi_get8((handle), &CS4231_IDR) & (uint8_t)(val)),	\
1389484Sgarrett.damore@Sun.COM 	    (uint8_t)(mask))
1399484Sgarrett.damore@Sun.COM 
1409484Sgarrett.damore@Sun.COM static audio_engine_ops_t audiocs_engine_ops = {
1419484Sgarrett.damore@Sun.COM 	AUDIO_ENGINE_VERSION,
1429484Sgarrett.damore@Sun.COM 	audiocs_open,
1439484Sgarrett.damore@Sun.COM 	audiocs_close,
1449484Sgarrett.damore@Sun.COM 	audiocs_start,
1459484Sgarrett.damore@Sun.COM 	audiocs_stop,
1469484Sgarrett.damore@Sun.COM 	audiocs_count,
1479484Sgarrett.damore@Sun.COM 	audiocs_format,
1489484Sgarrett.damore@Sun.COM 	audiocs_channels,
1499484Sgarrett.damore@Sun.COM 	audiocs_rate,
1509484Sgarrett.damore@Sun.COM 	audiocs_sync,
151*11936Sgdamore@opensolaris.org 	NULL,
15211213Sgdamore@opensolaris.org 	NULL,
153*11936Sgdamore@opensolaris.org 	NULL,
1549484Sgarrett.damore@Sun.COM };
1559484Sgarrett.damore@Sun.COM 
1569484Sgarrett.damore@Sun.COM #define	OUTPUT_SPEAKER		0
1579484Sgarrett.damore@Sun.COM #define	OUTPUT_HEADPHONES	1
1589484Sgarrett.damore@Sun.COM #define	OUTPUT_LINEOUT		2
1599484Sgarrett.damore@Sun.COM 
1609484Sgarrett.damore@Sun.COM static const char *audiocs_outputs[] = {
1619484Sgarrett.damore@Sun.COM 	AUDIO_PORT_SPEAKER,
1629484Sgarrett.damore@Sun.COM 	AUDIO_PORT_HEADPHONES,
1639484Sgarrett.damore@Sun.COM 	AUDIO_PORT_LINEOUT,
1649484Sgarrett.damore@Sun.COM 	NULL
1659484Sgarrett.damore@Sun.COM };
1669484Sgarrett.damore@Sun.COM 
1679484Sgarrett.damore@Sun.COM #define	INPUT_MIC		0
1689484Sgarrett.damore@Sun.COM #define	INPUT_LINEIN		1
1699484Sgarrett.damore@Sun.COM #define	INPUT_STEREOMIX		2
1709484Sgarrett.damore@Sun.COM #define	INPUT_CD		3
1719484Sgarrett.damore@Sun.COM 
1729484Sgarrett.damore@Sun.COM static const char *audiocs_inputs[] = {
1739484Sgarrett.damore@Sun.COM 	AUDIO_PORT_MIC,
1749484Sgarrett.damore@Sun.COM 	AUDIO_PORT_LINEIN,
1759484Sgarrett.damore@Sun.COM 	AUDIO_PORT_STEREOMIX,
1769484Sgarrett.damore@Sun.COM 	AUDIO_PORT_CD,
1779484Sgarrett.damore@Sun.COM 	NULL
1789484Sgarrett.damore@Sun.COM };
1799484Sgarrett.damore@Sun.COM 
1809484Sgarrett.damore@Sun.COM /*
1819484Sgarrett.damore@Sun.COM  * Global variables, but viewable only by this file.
1829484Sgarrett.damore@Sun.COM  */
1839484Sgarrett.damore@Sun.COM 
1849484Sgarrett.damore@Sun.COM /* play gain array, converts linear gain to 64 steps of log10 gain */
1859484Sgarrett.damore@Sun.COM static uint8_t cs4231_atten[] = {
1869484Sgarrett.damore@Sun.COM 	0x3f,	0x3e,	0x3d,	0x3c,	0x3b,	/* [000] -> [004] */
1879484Sgarrett.damore@Sun.COM 	0x3a,	0x39,	0x38,	0x37,	0x36,	/* [005] -> [009] */
1889484Sgarrett.damore@Sun.COM 	0x35,	0x34,	0x33,	0x32,	0x31,	/* [010] -> [014] */
1899484Sgarrett.damore@Sun.COM 	0x30,	0x2f,	0x2e,	0x2d,	0x2c,	/* [015] -> [019] */
1909484Sgarrett.damore@Sun.COM 	0x2b,	0x2a,	0x29,	0x29,	0x28,	/* [020] -> [024] */
1919484Sgarrett.damore@Sun.COM 	0x28,	0x27,	0x27,	0x26,	0x26,	/* [025] -> [029] */
1929484Sgarrett.damore@Sun.COM 	0x25,	0x25,	0x24,	0x24,	0x23,	/* [030] -> [034] */
1939484Sgarrett.damore@Sun.COM 	0x23,	0x22,	0x22,	0x21,	0x21,	/* [035] -> [039] */
1949484Sgarrett.damore@Sun.COM 	0x20,	0x20,	0x1f,	0x1f,	0x1f,	/* [040] -> [044] */
1959484Sgarrett.damore@Sun.COM 	0x1e,	0x1e,	0x1e,	0x1d,	0x1d,	/* [045] -> [049] */
1969484Sgarrett.damore@Sun.COM 	0x1d,	0x1c,	0x1c,	0x1c,	0x1b,	/* [050] -> [054] */
1979484Sgarrett.damore@Sun.COM 	0x1b,	0x1b,	0x1a,	0x1a,	0x1a,	/* [055] -> [059] */
1989484Sgarrett.damore@Sun.COM 	0x1a,	0x19,	0x19,	0x19,	0x19,	/* [060] -> [064] */
1999484Sgarrett.damore@Sun.COM 	0x18,	0x18,	0x18,	0x18,	0x17,	/* [065] -> [069] */
2009484Sgarrett.damore@Sun.COM 	0x17,	0x17,	0x17,	0x16,	0x16,	/* [070] -> [074] */
2019484Sgarrett.damore@Sun.COM 	0x16,	0x16,	0x16,	0x15,	0x15,	/* [075] -> [079] */
2029484Sgarrett.damore@Sun.COM 	0x15,	0x15,	0x15,	0x14,	0x14,	/* [080] -> [084] */
2039484Sgarrett.damore@Sun.COM 	0x14,	0x14,	0x14,	0x13,	0x13,	/* [085] -> [089] */
2049484Sgarrett.damore@Sun.COM 	0x13,	0x13,	0x13,	0x12,	0x12,	/* [090] -> [094] */
2059484Sgarrett.damore@Sun.COM 	0x12,	0x12,	0x12,	0x12,	0x11,	/* [095] -> [099] */
2069484Sgarrett.damore@Sun.COM 	0x11,	0x11,	0x11,	0x11,	0x11,	/* [100] -> [104] */
2079484Sgarrett.damore@Sun.COM 	0x10,	0x10,	0x10,	0x10,	0x10,	/* [105] -> [109] */
2089484Sgarrett.damore@Sun.COM 	0x10,	0x0f,	0x0f,	0x0f,	0x0f,	/* [110] -> [114] */
2099484Sgarrett.damore@Sun.COM 	0x0f,	0x0f,	0x0e,	0x0e,	0x0e,	/* [114] -> [119] */
2109484Sgarrett.damore@Sun.COM 	0x0e,	0x0e,	0x0e,	0x0e,	0x0d,	/* [120] -> [124] */
2119484Sgarrett.damore@Sun.COM 	0x0d,	0x0d,	0x0d,	0x0d,	0x0d,	/* [125] -> [129] */
2129484Sgarrett.damore@Sun.COM 	0x0d,	0x0c,	0x0c,	0x0c,	0x0c,	/* [130] -> [134] */
2139484Sgarrett.damore@Sun.COM 	0x0c,	0x0c,	0x0c,	0x0b,	0x0b,	/* [135] -> [139] */
2149484Sgarrett.damore@Sun.COM 	0x0b,	0x0b,	0x0b,	0x0b,	0x0b,	/* [140] -> [144] */
2159484Sgarrett.damore@Sun.COM 	0x0b,	0x0a,	0x0a,	0x0a,	0x0a,	/* [145] -> [149] */
2169484Sgarrett.damore@Sun.COM 	0x0a,	0x0a,	0x0a,	0x0a,	0x09,	/* [150] -> [154] */
2179484Sgarrett.damore@Sun.COM 	0x09,	0x09,	0x09,	0x09,	0x09,	/* [155] -> [159] */
2189484Sgarrett.damore@Sun.COM 	0x09,	0x09,	0x08,	0x08,	0x08,	/* [160] -> [164] */
2199484Sgarrett.damore@Sun.COM 	0x08,	0x08,	0x08,	0x08,	0x08,	/* [165] -> [169] */
2209484Sgarrett.damore@Sun.COM 	0x08,	0x07,	0x07,	0x07,	0x07,	/* [170] -> [174] */
2219484Sgarrett.damore@Sun.COM 	0x07,	0x07,	0x07,	0x07,	0x07,	/* [175] -> [179] */
2229484Sgarrett.damore@Sun.COM 	0x06,	0x06,	0x06,	0x06,	0x06,	/* [180] -> [184] */
2239484Sgarrett.damore@Sun.COM 	0x06,	0x06,	0x06,	0x06,	0x05,	/* [185] -> [189] */
2249484Sgarrett.damore@Sun.COM 	0x05,	0x05,	0x05,	0x05,	0x05,	/* [190] -> [194] */
2259484Sgarrett.damore@Sun.COM 	0x05,	0x05,	0x05,	0x05,	0x04,	/* [195] -> [199] */
2269484Sgarrett.damore@Sun.COM 	0x04,	0x04,	0x04,	0x04,	0x04,	/* [200] -> [204] */
2279484Sgarrett.damore@Sun.COM 	0x04,	0x04,	0x04,	0x04,	0x03,	/* [205] -> [209] */
2289484Sgarrett.damore@Sun.COM 	0x03,	0x03,	0x03,	0x03,	0x03,	/* [210] -> [214] */
2299484Sgarrett.damore@Sun.COM 	0x03,	0x03,	0x03,	0x03,	0x03,	/* [215] -> [219] */
2309484Sgarrett.damore@Sun.COM 	0x02,	0x02,	0x02,	0x02,	0x02,	/* [220] -> [224] */
2319484Sgarrett.damore@Sun.COM 	0x02,	0x02,	0x02,	0x02,	0x02,	/* [225] -> [229] */
2329484Sgarrett.damore@Sun.COM 	0x02,	0x01,	0x01,	0x01,	0x01,	/* [230] -> [234] */
2339484Sgarrett.damore@Sun.COM 	0x01,	0x01,	0x01,	0x01,	0x01,	/* [235] -> [239] */
2349484Sgarrett.damore@Sun.COM 	0x01,	0x01,	0x01,	0x00,	0x00,	/* [240] -> [244] */
2359484Sgarrett.damore@Sun.COM 	0x00,	0x00,	0x00,	0x00,	0x00,	/* [245] -> [249] */
2369484Sgarrett.damore@Sun.COM 	0x00,	0x00,	0x00,	0x00,	0x00,	/* [250] -> [254] */
2379484Sgarrett.damore@Sun.COM 	0x00					/* [255] */
2389484Sgarrett.damore@Sun.COM };
2399484Sgarrett.damore@Sun.COM 
2409484Sgarrett.damore@Sun.COM /*
2419484Sgarrett.damore@Sun.COM  * STREAMS Structures
2429484Sgarrett.damore@Sun.COM  */
2439484Sgarrett.damore@Sun.COM 
2449484Sgarrett.damore@Sun.COM /*
2459484Sgarrett.damore@Sun.COM  * DDI Structures
2469484Sgarrett.damore@Sun.COM  */
2479484Sgarrett.damore@Sun.COM 
2489484Sgarrett.damore@Sun.COM /* Device operations structure */
2499484Sgarrett.damore@Sun.COM static struct dev_ops audiocs_dev_ops = {
2509484Sgarrett.damore@Sun.COM 	DEVO_REV,			/* devo_rev */
2519484Sgarrett.damore@Sun.COM 	0,				/* devo_refcnt */
2529484Sgarrett.damore@Sun.COM 	NULL,				/* devo_getinfo */
2539484Sgarrett.damore@Sun.COM 	nulldev,			/* devo_identify - obsolete */
2549484Sgarrett.damore@Sun.COM 	nulldev,			/* devo_probe - not needed */
2559484Sgarrett.damore@Sun.COM 	audiocs_ddi_attach,		/* devo_attach */
2569484Sgarrett.damore@Sun.COM 	audiocs_ddi_detach,		/* devo_detach */
2579484Sgarrett.damore@Sun.COM 	nodev,				/* devo_reset */
2589484Sgarrett.damore@Sun.COM 	NULL,				/* devi_cb_ops */
2599484Sgarrett.damore@Sun.COM 	NULL,				/* devo_bus_ops */
2609484Sgarrett.damore@Sun.COM 	audiocs_ddi_power,		/* devo_power */
2619484Sgarrett.damore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
2629484Sgarrett.damore@Sun.COM };
2639484Sgarrett.damore@Sun.COM 
2649484Sgarrett.damore@Sun.COM /* Linkage structure for loadable drivers */
2659484Sgarrett.damore@Sun.COM static struct modldrv audiocs_modldrv = {
2669484Sgarrett.damore@Sun.COM 	&mod_driverops,		/* drv_modops */
2679484Sgarrett.damore@Sun.COM 	CS4231_MOD_NAME,	/* drv_linkinfo */
2689484Sgarrett.damore@Sun.COM 	&audiocs_dev_ops	/* drv_dev_ops */
2699484Sgarrett.damore@Sun.COM };
2709484Sgarrett.damore@Sun.COM 
2719484Sgarrett.damore@Sun.COM /* Module linkage structure */
2729484Sgarrett.damore@Sun.COM static struct modlinkage audiocs_modlinkage = {
2739484Sgarrett.damore@Sun.COM 	MODREV_1,			/* ml_rev */
2749484Sgarrett.damore@Sun.COM 	(void *)&audiocs_modldrv,	/* ml_linkage */
2759484Sgarrett.damore@Sun.COM 	NULL				/* NULL terminates the list */
2769484Sgarrett.damore@Sun.COM };
2779484Sgarrett.damore@Sun.COM 
2789484Sgarrett.damore@Sun.COM 
2799484Sgarrett.damore@Sun.COM /* *******  Loadable Module Configuration Entry Points  ********************* */
2809484Sgarrett.damore@Sun.COM 
2819484Sgarrett.damore@Sun.COM /*
2829484Sgarrett.damore@Sun.COM  * _init()
2839484Sgarrett.damore@Sun.COM  *
2849484Sgarrett.damore@Sun.COM  * Description:
2859484Sgarrett.damore@Sun.COM  *	Implements _init(9E).
2869484Sgarrett.damore@Sun.COM  *
2879484Sgarrett.damore@Sun.COM  * Returns:
2889484Sgarrett.damore@Sun.COM  *	mod_install() status, see mod_install(9f)
2899484Sgarrett.damore@Sun.COM  */
2909484Sgarrett.damore@Sun.COM int
_init(void)2919484Sgarrett.damore@Sun.COM _init(void)
2929484Sgarrett.damore@Sun.COM {
2939484Sgarrett.damore@Sun.COM 	int	rv;
2949484Sgarrett.damore@Sun.COM 
2959484Sgarrett.damore@Sun.COM 	audio_init_ops(&audiocs_dev_ops, CS4231_NAME);
2969484Sgarrett.damore@Sun.COM 
2979484Sgarrett.damore@Sun.COM 	if ((rv = mod_install(&audiocs_modlinkage)) != 0) {
2989484Sgarrett.damore@Sun.COM 		audio_fini_ops(&audiocs_dev_ops);
2999484Sgarrett.damore@Sun.COM 	}
3009484Sgarrett.damore@Sun.COM 
3019484Sgarrett.damore@Sun.COM 	return (rv);
3029484Sgarrett.damore@Sun.COM }
3039484Sgarrett.damore@Sun.COM 
3049484Sgarrett.damore@Sun.COM /*
3059484Sgarrett.damore@Sun.COM  * _fini()
3069484Sgarrett.damore@Sun.COM  *
3079484Sgarrett.damore@Sun.COM  * Description:
3089484Sgarrett.damore@Sun.COM  *	Implements _fini(9E).
3099484Sgarrett.damore@Sun.COM  *
3109484Sgarrett.damore@Sun.COM  * Returns:
3119484Sgarrett.damore@Sun.COM  *	mod_remove() status, see mod_remove(9f)
3129484Sgarrett.damore@Sun.COM  */
3139484Sgarrett.damore@Sun.COM int
_fini(void)3149484Sgarrett.damore@Sun.COM _fini(void)
3159484Sgarrett.damore@Sun.COM {
3169484Sgarrett.damore@Sun.COM 	int	rv;
3179484Sgarrett.damore@Sun.COM 
3189484Sgarrett.damore@Sun.COM 	if ((rv = mod_remove(&audiocs_modlinkage)) == 0) {
3199484Sgarrett.damore@Sun.COM 		audio_fini_ops(&audiocs_dev_ops);
3209484Sgarrett.damore@Sun.COM 	}
3219484Sgarrett.damore@Sun.COM 
3229484Sgarrett.damore@Sun.COM 	return (rv);
3239484Sgarrett.damore@Sun.COM }
3249484Sgarrett.damore@Sun.COM 
3259484Sgarrett.damore@Sun.COM /*
3269484Sgarrett.damore@Sun.COM  * _info()
3279484Sgarrett.damore@Sun.COM  *
3289484Sgarrett.damore@Sun.COM  * Description:
3299484Sgarrett.damore@Sun.COM  *	Implements _info(9E).
3309484Sgarrett.damore@Sun.COM  *
3319484Sgarrett.damore@Sun.COM  * Arguments:
3329484Sgarrett.damore@Sun.COM  *	modinfo *modinfop	Pointer to the opaque modinfo structure
3339484Sgarrett.damore@Sun.COM  *
3349484Sgarrett.damore@Sun.COM  * Returns:
3359484Sgarrett.damore@Sun.COM  *	mod_info() status, see mod_info(9f)
3369484Sgarrett.damore@Sun.COM  */
3379484Sgarrett.damore@Sun.COM int
_info(struct modinfo * modinfop)3389484Sgarrett.damore@Sun.COM _info(struct modinfo *modinfop)
3399484Sgarrett.damore@Sun.COM {
3409484Sgarrett.damore@Sun.COM 	return (mod_info(&audiocs_modlinkage, modinfop));
3419484Sgarrett.damore@Sun.COM }
3429484Sgarrett.damore@Sun.COM 
3439484Sgarrett.damore@Sun.COM 
3449484Sgarrett.damore@Sun.COM /* *******  Driver Entry Points  ******************************************** */
3459484Sgarrett.damore@Sun.COM 
3469484Sgarrett.damore@Sun.COM /*
3479484Sgarrett.damore@Sun.COM  * audiocs_ddi_attach()
3489484Sgarrett.damore@Sun.COM  *
3499484Sgarrett.damore@Sun.COM  * Description:
3509484Sgarrett.damore@Sun.COM  *	Implement attach(9e).
3519484Sgarrett.damore@Sun.COM  *
3529484Sgarrett.damore@Sun.COM  * Arguments:
3539484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
3549484Sgarrett.damore@Sun.COM  *	ddi_attach_cmd_t cmd	Attach command
3559484Sgarrett.damore@Sun.COM  *
3569484Sgarrett.damore@Sun.COM  * Returns:
3579484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was initialized properly
3589484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be initialized properly
3599484Sgarrett.damore@Sun.COM  */
3609484Sgarrett.damore@Sun.COM static int
audiocs_ddi_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3619484Sgarrett.damore@Sun.COM audiocs_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3629484Sgarrett.damore@Sun.COM {
3639484Sgarrett.damore@Sun.COM 	switch (cmd) {
3649484Sgarrett.damore@Sun.COM 	case DDI_ATTACH:
3659484Sgarrett.damore@Sun.COM 		return (audiocs_attach(dip));
3669484Sgarrett.damore@Sun.COM 
3679484Sgarrett.damore@Sun.COM 	case DDI_RESUME:
3689484Sgarrett.damore@Sun.COM 		return (audiocs_resume(dip));
3699484Sgarrett.damore@Sun.COM 
3709484Sgarrett.damore@Sun.COM 	default:
3719484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
3729484Sgarrett.damore@Sun.COM 	}
3739484Sgarrett.damore@Sun.COM }
3749484Sgarrett.damore@Sun.COM 
3759484Sgarrett.damore@Sun.COM /*
3769484Sgarrett.damore@Sun.COM  * audiocs_ddi_detach()
3779484Sgarrett.damore@Sun.COM  *
3789484Sgarrett.damore@Sun.COM  * Description:
3799484Sgarrett.damore@Sun.COM  *	Implement detach(9e).
3809484Sgarrett.damore@Sun.COM  *
3819484Sgarrett.damore@Sun.COM  * Arguments:
3829484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
3839484Sgarrett.damore@Sun.COM  *	ddi_detach_cmd_t cmd	Detach command
3849484Sgarrett.damore@Sun.COM  *
3859484Sgarrett.damore@Sun.COM  * Returns:
3869484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		Success.
3879484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		Failure.
3889484Sgarrett.damore@Sun.COM  */
3899484Sgarrett.damore@Sun.COM static int
audiocs_ddi_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3909484Sgarrett.damore@Sun.COM audiocs_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3919484Sgarrett.damore@Sun.COM {
3929484Sgarrett.damore@Sun.COM 	switch (cmd) {
3939484Sgarrett.damore@Sun.COM 	case DDI_DETACH:
3949484Sgarrett.damore@Sun.COM 		return (audiocs_detach(dip));
3959484Sgarrett.damore@Sun.COM 
3969484Sgarrett.damore@Sun.COM 	case DDI_SUSPEND:
3979484Sgarrett.damore@Sun.COM 		return (audiocs_suspend(dip));
3989484Sgarrett.damore@Sun.COM 
3999484Sgarrett.damore@Sun.COM 	default:
4009484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
4019484Sgarrett.damore@Sun.COM 	}
4029484Sgarrett.damore@Sun.COM }
4039484Sgarrett.damore@Sun.COM 
4049484Sgarrett.damore@Sun.COM /*
4059484Sgarrett.damore@Sun.COM  * audiocs_ddi_power()
4069484Sgarrett.damore@Sun.COM  *
4079484Sgarrett.damore@Sun.COM  * Description:
4089484Sgarrett.damore@Sun.COM  *	Implements power(9E).
4099484Sgarrett.damore@Sun.COM  *
4109484Sgarrett.damore@Sun.COM  * Arguments:
4119484Sgarrett.damore@Sun.COM  *	def_info_t	*dip		Ptr to the device's dev_info structure
4129484Sgarrett.damore@Sun.COM  *	int		component	Which component to power up/down
4139484Sgarrett.damore@Sun.COM  *	int		level		The power level for the component
4149484Sgarrett.damore@Sun.COM  *
4159484Sgarrett.damore@Sun.COM  * Returns:
4169484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		Power level changed, we always succeed
4179484Sgarrett.damore@Sun.COM  */
4189484Sgarrett.damore@Sun.COM static int
audiocs_ddi_power(dev_info_t * dip,int component,int level)4199484Sgarrett.damore@Sun.COM audiocs_ddi_power(dev_info_t *dip, int component, int level)
4209484Sgarrett.damore@Sun.COM {
4219484Sgarrett.damore@Sun.COM 	CS_state_t		*state;
4229484Sgarrett.damore@Sun.COM 
4239484Sgarrett.damore@Sun.COM 	if (component != CS4231_COMPONENT)
4249484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
4259484Sgarrett.damore@Sun.COM 
4269484Sgarrett.damore@Sun.COM 	/* get the state structure */
4279484Sgarrett.damore@Sun.COM 	state = ddi_get_driver_private(dip);
4289484Sgarrett.damore@Sun.COM 
4299484Sgarrett.damore@Sun.COM 	ASSERT(!mutex_owned(&state->cs_lock));
4309484Sgarrett.damore@Sun.COM 
4319484Sgarrett.damore@Sun.COM 	/* make sure we have some work to do */
4329484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
4339484Sgarrett.damore@Sun.COM 
4349484Sgarrett.damore@Sun.COM 	/*
4359484Sgarrett.damore@Sun.COM 	 * We don't do anything if we're suspended.  Suspend/resume diddles
4369484Sgarrett.damore@Sun.COM 	 * with power anyway.
4379484Sgarrett.damore@Sun.COM 	 */
4389484Sgarrett.damore@Sun.COM 	if (!state->cs_suspended) {
4399484Sgarrett.damore@Sun.COM 
4409484Sgarrett.damore@Sun.COM 		/* check the level change to see what we need to do */
4419484Sgarrett.damore@Sun.COM 		if (level == CS4231_PWR_OFF && state->cs_powered) {
4429484Sgarrett.damore@Sun.COM 
4439484Sgarrett.damore@Sun.COM 			/* power down and save the state */
4449484Sgarrett.damore@Sun.COM 			audiocs_power_down(state);
4459484Sgarrett.damore@Sun.COM 			state->cs_powered = B_FALSE;
4469484Sgarrett.damore@Sun.COM 
4479484Sgarrett.damore@Sun.COM 		} else if (level == CS4231_PWR_ON && !state->cs_powered) {
4489484Sgarrett.damore@Sun.COM 
4499484Sgarrett.damore@Sun.COM 			/* power up */
4509484Sgarrett.damore@Sun.COM 			audiocs_power_up(state);
4519484Sgarrett.damore@Sun.COM 			state->cs_powered = B_TRUE;
4529484Sgarrett.damore@Sun.COM 		}
4539484Sgarrett.damore@Sun.COM 	}
4549484Sgarrett.damore@Sun.COM 
4559484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
4569484Sgarrett.damore@Sun.COM 
4579484Sgarrett.damore@Sun.COM 	ASSERT(!mutex_owned(&state->cs_lock));
4589484Sgarrett.damore@Sun.COM 
4599484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
4609484Sgarrett.damore@Sun.COM }
4619484Sgarrett.damore@Sun.COM 
4629484Sgarrett.damore@Sun.COM /* ******* Local Routines *************************************************** */
4639484Sgarrett.damore@Sun.COM 
4649484Sgarrett.damore@Sun.COM static void
audiocs_destroy(CS_state_t * state)4659484Sgarrett.damore@Sun.COM audiocs_destroy(CS_state_t *state)
4669484Sgarrett.damore@Sun.COM {
4679484Sgarrett.damore@Sun.COM 	if (state == NULL)
4689484Sgarrett.damore@Sun.COM 		return;
4699484Sgarrett.damore@Sun.COM 
4709484Sgarrett.damore@Sun.COM 	for (int i = CS4231_PLAY; i <= CS4231_REC; i++) {
4719484Sgarrett.damore@Sun.COM 		audiocs_free_engine(state->cs_engines[i]);
4729484Sgarrett.damore@Sun.COM 	}
4739484Sgarrett.damore@Sun.COM 	audiocs_del_controls(state);
4749484Sgarrett.damore@Sun.COM 
4759484Sgarrett.damore@Sun.COM 	if (state->cs_adev) {
4769484Sgarrett.damore@Sun.COM 		audio_dev_free(state->cs_adev);
4779484Sgarrett.damore@Sun.COM 	}
4789484Sgarrett.damore@Sun.COM 
4799484Sgarrett.damore@Sun.COM 	/* unmap the registers */
4809484Sgarrett.damore@Sun.COM 	CS4231_DMA_UNMAP_REGS(state);
4819484Sgarrett.damore@Sun.COM 
4829484Sgarrett.damore@Sun.COM 	/* destroy the state mutex */
4839484Sgarrett.damore@Sun.COM 	mutex_destroy(&state->cs_lock);
4849484Sgarrett.damore@Sun.COM 	kmem_free(state, sizeof (*state));
4859484Sgarrett.damore@Sun.COM }
4869484Sgarrett.damore@Sun.COM 
4879484Sgarrett.damore@Sun.COM /*
4889484Sgarrett.damore@Sun.COM  * audiocs_attach()
4899484Sgarrett.damore@Sun.COM  *
4909484Sgarrett.damore@Sun.COM  * Description:
4919484Sgarrett.damore@Sun.COM  *	Attach an instance of the CS4231 driver. This routine does the device
4929484Sgarrett.damore@Sun.COM  *	dependent attach tasks.  When it is complete it calls
4939484Sgarrett.damore@Sun.COM  *	audio_dev_register() to register with the framework.
4949484Sgarrett.damore@Sun.COM  *
4959484Sgarrett.damore@Sun.COM  * Arguments:
4969484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
4979484Sgarrett.damore@Sun.COM  *
4989484Sgarrett.damore@Sun.COM  * Returns:
4999484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was initialized properly
5009484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be initialized properly
5019484Sgarrett.damore@Sun.COM  */
5029484Sgarrett.damore@Sun.COM static int
audiocs_attach(dev_info_t * dip)5039484Sgarrett.damore@Sun.COM audiocs_attach(dev_info_t *dip)
5049484Sgarrett.damore@Sun.COM {
5059484Sgarrett.damore@Sun.COM 	CS_state_t		*state;
5069484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
5079484Sgarrett.damore@Sun.COM 
5089484Sgarrett.damore@Sun.COM 	/* allocate the state structure */
5099484Sgarrett.damore@Sun.COM 	state = kmem_zalloc(sizeof (*state), KM_SLEEP);
5109484Sgarrett.damore@Sun.COM 	state->cs_dip = dip;
5119484Sgarrett.damore@Sun.COM 	ddi_set_driver_private(dip, state);
5129484Sgarrett.damore@Sun.COM 
5139484Sgarrett.damore@Sun.COM 	/* now fill it in, initialize the state mutexs first */
514*11936Sgdamore@opensolaris.org 	mutex_init(&state->cs_lock, NULL, MUTEX_DRIVER, NULL);
5159484Sgarrett.damore@Sun.COM 
5169484Sgarrett.damore@Sun.COM 	/*
5179484Sgarrett.damore@Sun.COM 	 * audio state initialization... should always succeed,
5189484Sgarrett.damore@Sun.COM 	 * framework will message failure.
5199484Sgarrett.damore@Sun.COM 	 */
5209484Sgarrett.damore@Sun.COM 	if ((state->cs_adev = audio_dev_alloc(dip, 0)) == NULL) {
5219484Sgarrett.damore@Sun.COM 		goto error;
5229484Sgarrett.damore@Sun.COM 	}
5239484Sgarrett.damore@Sun.COM 	adev = state->cs_adev;
5249484Sgarrett.damore@Sun.COM 	audio_dev_set_description(adev, CS_DEV_CONFIG_ONBRD1);
5259484Sgarrett.damore@Sun.COM 	audio_dev_add_info(adev, "Legacy codec: Crystal Semiconductor CS4231");
5269484Sgarrett.damore@Sun.COM 
5279484Sgarrett.damore@Sun.COM 	/* initialize the audio state structures */
5289484Sgarrett.damore@Sun.COM 	if ((audiocs_init_state(state)) == DDI_FAILURE) {
5299484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "init_state() failed");
5309484Sgarrett.damore@Sun.COM 		goto error;
5319484Sgarrett.damore@Sun.COM 	}
5329484Sgarrett.damore@Sun.COM 
5339965Sgdamore@opensolaris.org 	mutex_enter(&state->cs_lock);
5349965Sgdamore@opensolaris.org 
5359484Sgarrett.damore@Sun.COM 	/* initialize the audio chip */
5369484Sgarrett.damore@Sun.COM 	if ((audiocs_chip_init(state)) == DDI_FAILURE) {
5379965Sgdamore@opensolaris.org 		mutex_exit(&state->cs_lock);
5389484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "chip_init() failed");
5399484Sgarrett.damore@Sun.COM 		goto error;
5409484Sgarrett.damore@Sun.COM 	}
5419484Sgarrett.damore@Sun.COM 	/* chip init will have powered us up */
5429484Sgarrett.damore@Sun.COM 	state->cs_powered = B_TRUE;
5439484Sgarrett.damore@Sun.COM 
5449965Sgdamore@opensolaris.org 	mutex_exit(&state->cs_lock);
5459484Sgarrett.damore@Sun.COM 
5469484Sgarrett.damore@Sun.COM 	/* finally register with framework to kick everything off */
5479484Sgarrett.damore@Sun.COM 	if (audio_dev_register(state->cs_adev) != DDI_SUCCESS) {
5489484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev, "unable to register audio dev");
5499484Sgarrett.damore@Sun.COM 	}
5509484Sgarrett.damore@Sun.COM 
5519484Sgarrett.damore@Sun.COM 	/* everything worked out, so report the device */
5529484Sgarrett.damore@Sun.COM 	ddi_report_dev(dip);
5539484Sgarrett.damore@Sun.COM 
5549484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
5559484Sgarrett.damore@Sun.COM 
5569484Sgarrett.damore@Sun.COM error:
5579484Sgarrett.damore@Sun.COM 	audiocs_destroy(state);
5589484Sgarrett.damore@Sun.COM 	return (DDI_FAILURE);
5599484Sgarrett.damore@Sun.COM }
5609484Sgarrett.damore@Sun.COM 
5619484Sgarrett.damore@Sun.COM /*
5629484Sgarrett.damore@Sun.COM  * audiocs_resume()
5639484Sgarrett.damore@Sun.COM  *
5649484Sgarrett.damore@Sun.COM  * Description:
5659484Sgarrett.damore@Sun.COM  *	Resume a suspended device instance.
5669484Sgarrett.damore@Sun.COM  *
5679484Sgarrett.damore@Sun.COM  * Arguments:
5689484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
5699484Sgarrett.damore@Sun.COM  *
5709484Sgarrett.damore@Sun.COM  * Returns:
5719484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was initialized properly
5729484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be initialized properly
5739484Sgarrett.damore@Sun.COM  */
5749484Sgarrett.damore@Sun.COM static int
audiocs_resume(dev_info_t * dip)5759484Sgarrett.damore@Sun.COM audiocs_resume(dev_info_t *dip)
5769484Sgarrett.damore@Sun.COM {
5779484Sgarrett.damore@Sun.COM 	CS_state_t		*state;
5789484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
5799484Sgarrett.damore@Sun.COM 
5809484Sgarrett.damore@Sun.COM 	/* we've already allocated the state structure so get ptr */
5819484Sgarrett.damore@Sun.COM 	state = ddi_get_driver_private(dip);
5829484Sgarrett.damore@Sun.COM 	adev = state->cs_adev;
5839484Sgarrett.damore@Sun.COM 
5849484Sgarrett.damore@Sun.COM 	ASSERT(dip == state->cs_dip);
5859484Sgarrett.damore@Sun.COM 	ASSERT(!mutex_owned(&state->cs_lock));
5869484Sgarrett.damore@Sun.COM 
5879484Sgarrett.damore@Sun.COM 	/* mark the Codec busy -- this should keep power(9e) away */
5889484Sgarrett.damore@Sun.COM 	(void) pm_busy_component(state->cs_dip, CS4231_COMPONENT);
5899484Sgarrett.damore@Sun.COM 
5909484Sgarrett.damore@Sun.COM 	/* power it up */
5919484Sgarrett.damore@Sun.COM 	audiocs_power_up(state);
5929484Sgarrett.damore@Sun.COM 	state->cs_powered = B_TRUE;
5939484Sgarrett.damore@Sun.COM 
5949965Sgdamore@opensolaris.org 	mutex_enter(&state->cs_lock);
5959965Sgdamore@opensolaris.org 
5969484Sgarrett.damore@Sun.COM 	/* initialize the audio chip */
5979484Sgarrett.damore@Sun.COM 	if ((audiocs_chip_init(state)) == DDI_FAILURE) {
5989965Sgdamore@opensolaris.org 		mutex_exit(&state->cs_lock);
5999484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "chip_init() failed");
6009484Sgarrett.damore@Sun.COM 		(void) pm_idle_component(state->cs_dip, CS4231_COMPONENT);
6019484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
6029484Sgarrett.damore@Sun.COM 	}
6039484Sgarrett.damore@Sun.COM 
6049484Sgarrett.damore@Sun.COM 	state->cs_suspended = B_FALSE;
6059484Sgarrett.damore@Sun.COM 
6069484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
6079484Sgarrett.damore@Sun.COM 
6089484Sgarrett.damore@Sun.COM 	/*
6099484Sgarrett.damore@Sun.COM 	 * We have already powered up the chip, but this alerts the
6109484Sgarrett.damore@Sun.COM 	 * framework to the fact.
6119484Sgarrett.damore@Sun.COM 	 */
6129484Sgarrett.damore@Sun.COM 	(void) pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON);
6139484Sgarrett.damore@Sun.COM 	(void) pm_idle_component(state->cs_dip, CS4231_COMPONENT);
6149484Sgarrett.damore@Sun.COM 
615*11936Sgdamore@opensolaris.org 	audio_dev_resume(state->cs_adev);
616*11936Sgdamore@opensolaris.org 
6179484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
6189484Sgarrett.damore@Sun.COM }
6199484Sgarrett.damore@Sun.COM 
6209484Sgarrett.damore@Sun.COM /*
6219484Sgarrett.damore@Sun.COM  * audiocs_detach()
6229484Sgarrett.damore@Sun.COM  *
6239484Sgarrett.damore@Sun.COM  * Description:
6249484Sgarrett.damore@Sun.COM  *	Detach an instance of the CS4231 driver.
6259484Sgarrett.damore@Sun.COM  *
6269484Sgarrett.damore@Sun.COM  * Arguments:
6279484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
6289484Sgarrett.damore@Sun.COM  *
6299484Sgarrett.damore@Sun.COM  * Returns:
6309484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was detached
6319484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be detached (busy)
6329484Sgarrett.damore@Sun.COM  */
6339484Sgarrett.damore@Sun.COM static int
audiocs_detach(dev_info_t * dip)6349484Sgarrett.damore@Sun.COM audiocs_detach(dev_info_t *dip)
6359484Sgarrett.damore@Sun.COM {
6369484Sgarrett.damore@Sun.COM 	CS_state_t		*state;
6379484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
6389484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle;
6399484Sgarrett.damore@Sun.COM 
6409484Sgarrett.damore@Sun.COM 	/* get the state structure */
6419484Sgarrett.damore@Sun.COM 	state = ddi_get_driver_private(dip);
6429484Sgarrett.damore@Sun.COM 	handle = CODEC_HANDLE;
6439484Sgarrett.damore@Sun.COM 	adev = state->cs_adev;
6449484Sgarrett.damore@Sun.COM 
6459484Sgarrett.damore@Sun.COM 	/* don't detach if still in use */
6469484Sgarrett.damore@Sun.COM 	if (audio_dev_unregister(adev) != DDI_SUCCESS) {
6479484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
6489484Sgarrett.damore@Sun.COM 	}
6499484Sgarrett.damore@Sun.COM 
6509484Sgarrett.damore@Sun.COM 	if (state->cs_powered) {
6519484Sgarrett.damore@Sun.COM 		/*
6529484Sgarrett.damore@Sun.COM 		 * Make sure the Codec and DMA engine are off.
6539484Sgarrett.damore@Sun.COM 		 */
6549484Sgarrett.damore@Sun.COM 		SELIDX(state, INTC_REG);
6559484Sgarrett.damore@Sun.COM 		ANDIDX(state, ~(INTC_PEN|INTC_CEN), INTC_VALID_MASK);
6569484Sgarrett.damore@Sun.COM 
6579484Sgarrett.damore@Sun.COM 		/* make sure the DMA engine isn't going to do anything */
6589484Sgarrett.damore@Sun.COM 		CS4231_DMA_RESET(state);
6599484Sgarrett.damore@Sun.COM 
6609484Sgarrett.damore@Sun.COM 		/*
6619484Sgarrett.damore@Sun.COM 		 * power down the device, no reason to waste power without
6629484Sgarrett.damore@Sun.COM 		 * a driver
6639484Sgarrett.damore@Sun.COM 		 */
6649484Sgarrett.damore@Sun.COM 		(void) pm_lower_power(dip, CS4231_COMPONENT, CS4231_PWR_OFF);
6659484Sgarrett.damore@Sun.COM 	}
6669484Sgarrett.damore@Sun.COM 
6679484Sgarrett.damore@Sun.COM 	audiocs_destroy(state);
6689484Sgarrett.damore@Sun.COM 
6699484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
6709484Sgarrett.damore@Sun.COM }
6719484Sgarrett.damore@Sun.COM 
6729484Sgarrett.damore@Sun.COM /*
6739484Sgarrett.damore@Sun.COM  * audiocs_suspend()
6749484Sgarrett.damore@Sun.COM  *
6759484Sgarrett.damore@Sun.COM  * Description:
6769484Sgarrett.damore@Sun.COM  *	Suspend an instance of the CS4231 driver.
6779484Sgarrett.damore@Sun.COM  *
6789484Sgarrett.damore@Sun.COM  * Arguments:
6799484Sgarrett.damore@Sun.COM  *	dev_info_t	*dip	Pointer to the device's dev_info struct
6809484Sgarrett.damore@Sun.COM  *
6819484Sgarrett.damore@Sun.COM  * Returns:
6829484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The driver was detached
6839484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The driver couldn't be detached
6849484Sgarrett.damore@Sun.COM  */
6859484Sgarrett.damore@Sun.COM static int
audiocs_suspend(dev_info_t * dip)6869484Sgarrett.damore@Sun.COM audiocs_suspend(dev_info_t *dip)
6879484Sgarrett.damore@Sun.COM {
6889484Sgarrett.damore@Sun.COM 	CS_state_t		*state;
6899484Sgarrett.damore@Sun.COM 
6909484Sgarrett.damore@Sun.COM 	/* get the state structure */
6919484Sgarrett.damore@Sun.COM 	state = ddi_get_driver_private(dip);
6929484Sgarrett.damore@Sun.COM 
6939484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
6949484Sgarrett.damore@Sun.COM 
6959484Sgarrett.damore@Sun.COM 	ASSERT(!state->cs_suspended);
6969484Sgarrett.damore@Sun.COM 
697*11936Sgdamore@opensolaris.org 	audio_dev_suspend(state->cs_adev);
698*11936Sgdamore@opensolaris.org 
6999484Sgarrett.damore@Sun.COM 	if (state->cs_powered) {
7009484Sgarrett.damore@Sun.COM 		/* now we can power down the Codec */
7019484Sgarrett.damore@Sun.COM 		audiocs_power_down(state);
7029484Sgarrett.damore@Sun.COM 		state->cs_powered = B_FALSE;
7039484Sgarrett.damore@Sun.COM 	}
7049484Sgarrett.damore@Sun.COM 	state->cs_suspended = B_TRUE;	/* stop new ops */
7059484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
7069484Sgarrett.damore@Sun.COM 
7079484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
7089484Sgarrett.damore@Sun.COM }
7099484Sgarrett.damore@Sun.COM 
7109484Sgarrett.damore@Sun.COM #define	PLAYCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY)
7119484Sgarrett.damore@Sun.COM #define	RECCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC)
7129484Sgarrett.damore@Sun.COM #define	MONCTL	(AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR)
7139484Sgarrett.damore@Sun.COM #define	PCMVOL	(PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL)
7149484Sgarrett.damore@Sun.COM #define	MAINVOL	(PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL)
7159484Sgarrett.damore@Sun.COM #define	RECVOL	(RECCTL | AUDIO_CTRL_FLAG_RECVOL)
7169484Sgarrett.damore@Sun.COM #define	MONVOL	(MONCTL | AUDIO_CTRL_FLAG_MONVOL)
7179484Sgarrett.damore@Sun.COM 
7189484Sgarrett.damore@Sun.COM /*
7199484Sgarrett.damore@Sun.COM  * audiocs_alloc_ctrl
7209484Sgarrett.damore@Sun.COM  *
7219484Sgarrett.damore@Sun.COM  * Description:
7229484Sgarrett.damore@Sun.COM  *	Allocates a control structure for the audio mixer.
7239484Sgarrett.damore@Sun.COM  *
7249484Sgarrett.damore@Sun.COM  * Arguments:
7259484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		Device soft state.
7269484Sgarrett.damore@Sun.COM  *	uint32_t	num		Control number to allocate.
7279484Sgarrett.damore@Sun.COM  *	uint64_t	val		Initial value.
7289484Sgarrett.damore@Sun.COM  *
7299484Sgarrett.damore@Sun.COM  * Returns:
7309484Sgarrett.damore@Sun.COM  *	Pointer to newly allocated CS_ctrl_t structure.
7319484Sgarrett.damore@Sun.COM  */
7329484Sgarrett.damore@Sun.COM static CS_ctrl_t *
audiocs_alloc_ctrl(CS_state_t * state,uint32_t num,uint64_t val)7339484Sgarrett.damore@Sun.COM audiocs_alloc_ctrl(CS_state_t *state, uint32_t num, uint64_t val)
7349484Sgarrett.damore@Sun.COM {
7359484Sgarrett.damore@Sun.COM 	audio_ctrl_desc_t	desc;
7369484Sgarrett.damore@Sun.COM 	audio_ctrl_wr_t		fn;
7379484Sgarrett.damore@Sun.COM 	CS_ctrl_t		*cc;
7389484Sgarrett.damore@Sun.COM 
7399484Sgarrett.damore@Sun.COM 	cc = kmem_zalloc(sizeof (*cc), KM_SLEEP);
7409484Sgarrett.damore@Sun.COM 	cc->cc_state = state;
7419484Sgarrett.damore@Sun.COM 	cc->cc_num = num;
7429484Sgarrett.damore@Sun.COM 
7439484Sgarrett.damore@Sun.COM 	bzero(&desc, sizeof (desc));
7449484Sgarrett.damore@Sun.COM 
7459484Sgarrett.damore@Sun.COM 	switch (num) {
7469484Sgarrett.damore@Sun.COM 	case CTL_VOLUME:
7479484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_VOLUME;
7489484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
7499484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = 0;
7509484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = 100;
7519484Sgarrett.damore@Sun.COM 		desc.acd_flags = PCMVOL;
7529484Sgarrett.damore@Sun.COM 		fn = audiocs_set_ogain;
7539484Sgarrett.damore@Sun.COM 		break;
7549484Sgarrett.damore@Sun.COM 
7559484Sgarrett.damore@Sun.COM 	case CTL_IGAIN:
7569484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_RECGAIN;
7579484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_STEREO;
7589484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = 0;
7599484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = 100;
7609484Sgarrett.damore@Sun.COM 		desc.acd_flags = RECVOL;
7619484Sgarrett.damore@Sun.COM 		fn = audiocs_set_igain;
7629484Sgarrett.damore@Sun.COM 		break;
7639484Sgarrett.damore@Sun.COM 
7649484Sgarrett.damore@Sun.COM 	case CTL_MGAIN:
7659484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_MONGAIN;
7669484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_MONO;
7679484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = 0;
7689484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = 100;
7699484Sgarrett.damore@Sun.COM 		desc.acd_flags = MONVOL;
7709484Sgarrett.damore@Sun.COM 		fn = audiocs_set_mgain;
7719484Sgarrett.damore@Sun.COM 		break;
7729484Sgarrett.damore@Sun.COM 
7739484Sgarrett.damore@Sun.COM 	case CTL_INPUTS:
7749484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_RECSRC;
7759484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
7769484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = state->cs_imask;
7779484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = state->cs_imask;
7789484Sgarrett.damore@Sun.COM 		desc.acd_flags = RECCTL;
7799484Sgarrett.damore@Sun.COM 		for (int i = 0; audiocs_inputs[i]; i++) {
7809484Sgarrett.damore@Sun.COM 			desc.acd_enum[i] = audiocs_inputs[i];
7819484Sgarrett.damore@Sun.COM 		}
7829484Sgarrett.damore@Sun.COM 		fn = audiocs_set_inputs;
7839484Sgarrett.damore@Sun.COM 
7849484Sgarrett.damore@Sun.COM 		break;
7859484Sgarrett.damore@Sun.COM 
7869484Sgarrett.damore@Sun.COM 	case CTL_OUTPUTS:
7879484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_OUTPUTS;
7889484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_ENUM;
7899484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = state->cs_omod;
7909484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = state->cs_omask;
7919484Sgarrett.damore@Sun.COM 		desc.acd_flags = PLAYCTL | AUDIO_CTRL_FLAG_MULTI;
7929484Sgarrett.damore@Sun.COM 		for (int i = 0; audiocs_outputs[i]; i++) {
7939484Sgarrett.damore@Sun.COM 			desc.acd_enum[i] = audiocs_outputs[i];
7949484Sgarrett.damore@Sun.COM 		}
7959484Sgarrett.damore@Sun.COM 		fn = audiocs_set_outputs;
7969484Sgarrett.damore@Sun.COM 		break;
7979484Sgarrett.damore@Sun.COM 
7989484Sgarrett.damore@Sun.COM 	case CTL_MICBOOST:
7999484Sgarrett.damore@Sun.COM 		desc.acd_name = AUDIO_CTRL_ID_MICBOOST;
8009484Sgarrett.damore@Sun.COM 		desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN;
8019484Sgarrett.damore@Sun.COM 		desc.acd_minvalue = 0;
8029484Sgarrett.damore@Sun.COM 		desc.acd_maxvalue = 1;
8039484Sgarrett.damore@Sun.COM 		desc.acd_flags = RECCTL;
8049484Sgarrett.damore@Sun.COM 		fn = audiocs_set_micboost;
8059484Sgarrett.damore@Sun.COM 		break;
8069484Sgarrett.damore@Sun.COM 	}
8079484Sgarrett.damore@Sun.COM 
8089484Sgarrett.damore@Sun.COM 	cc->cc_val = val;
8099484Sgarrett.damore@Sun.COM 	cc->cc_ctrl = audio_dev_add_control(state->cs_adev, &desc,
8109484Sgarrett.damore@Sun.COM 	    audiocs_get_value, fn, cc);
8119484Sgarrett.damore@Sun.COM 
8129484Sgarrett.damore@Sun.COM 	return (cc);
8139484Sgarrett.damore@Sun.COM }
8149484Sgarrett.damore@Sun.COM 
8159484Sgarrett.damore@Sun.COM /*
8169484Sgarrett.damore@Sun.COM  * audiocs_free_ctrl
8179484Sgarrett.damore@Sun.COM  *
8189484Sgarrett.damore@Sun.COM  * Description:
8199484Sgarrett.damore@Sun.COM  *	Frees a control and all resources associated with it.
8209484Sgarrett.damore@Sun.COM  *
8219484Sgarrett.damore@Sun.COM  * Arguments:
8229484Sgarrett.damore@Sun.COM  *	CS_ctrl_t	*cc	Pointer to control structure.
8239484Sgarrett.damore@Sun.COM  */
8249484Sgarrett.damore@Sun.COM static void
audiocs_free_ctrl(CS_ctrl_t * cc)8259484Sgarrett.damore@Sun.COM audiocs_free_ctrl(CS_ctrl_t *cc)
8269484Sgarrett.damore@Sun.COM {
8279484Sgarrett.damore@Sun.COM 	if (cc == NULL)
8289484Sgarrett.damore@Sun.COM 		return;
8299484Sgarrett.damore@Sun.COM 	if (cc->cc_ctrl)
8309484Sgarrett.damore@Sun.COM 		audio_dev_del_control(cc->cc_ctrl);
8319484Sgarrett.damore@Sun.COM 	kmem_free(cc, sizeof (*cc));
8329484Sgarrett.damore@Sun.COM }
8339484Sgarrett.damore@Sun.COM 
8349484Sgarrett.damore@Sun.COM /*
8359484Sgarrett.damore@Sun.COM  * audiocs_add_controls
8369484Sgarrett.damore@Sun.COM  *
8379484Sgarrett.damore@Sun.COM  * Description:
8389484Sgarrett.damore@Sun.COM  *	Allocates and registers all controls for this device.
8399484Sgarrett.damore@Sun.COM  *
8409484Sgarrett.damore@Sun.COM  * Arguments:
8419484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		Device soft state.
8429484Sgarrett.damore@Sun.COM  *
8439484Sgarrett.damore@Sun.COM  * Returns:
8449484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS	All controls added and registered
8459484Sgarrett.damore@Sun.COM  *	DDI_FAILURE	At least one control was not added or registered.
8469484Sgarrett.damore@Sun.COM  */
8479484Sgarrett.damore@Sun.COM static int
audiocs_add_controls(CS_state_t * state)8489484Sgarrett.damore@Sun.COM audiocs_add_controls(CS_state_t *state)
8499484Sgarrett.damore@Sun.COM {
8509484Sgarrett.damore@Sun.COM #define	ADD_CTRL(CTL, ID, VAL)						\
8519484Sgarrett.damore@Sun.COM 	state->cs_##CTL = audiocs_alloc_ctrl(state, ID, VAL);		\
8529484Sgarrett.damore@Sun.COM 	if (state->cs_##CTL == NULL) {					\
8539484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev,				\
8549484Sgarrett.damore@Sun.COM 		    "unable to allocate %s control", #ID);		\
8559484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);					\
8569484Sgarrett.damore@Sun.COM 	}
8579484Sgarrett.damore@Sun.COM 
8589484Sgarrett.damore@Sun.COM 	ADD_CTRL(ogain, CTL_VOLUME, 0x4b4b);
8599484Sgarrett.damore@Sun.COM 	ADD_CTRL(igain, CTL_IGAIN, 0x3232);
8609484Sgarrett.damore@Sun.COM 	ADD_CTRL(mgain, CTL_MGAIN, 0);
8619484Sgarrett.damore@Sun.COM 	ADD_CTRL(micboost, CTL_MICBOOST, 0);
8629484Sgarrett.damore@Sun.COM 	ADD_CTRL(outputs, CTL_OUTPUTS, (state->cs_omask & ~state->cs_omod) |
8639484Sgarrett.damore@Sun.COM 	    (1U << OUTPUT_SPEAKER));
8649484Sgarrett.damore@Sun.COM 	ADD_CTRL(inputs, CTL_INPUTS, (1U << INPUT_MIC));
8659484Sgarrett.damore@Sun.COM 
8669484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
8679484Sgarrett.damore@Sun.COM }
8689965Sgdamore@opensolaris.org 
8699484Sgarrett.damore@Sun.COM /*
8709484Sgarrett.damore@Sun.COM  * audiocs_del_controls
8719484Sgarrett.damore@Sun.COM  *
8729484Sgarrett.damore@Sun.COM  * Description:
8739484Sgarrett.damore@Sun.COM  *	Unregisters and frees all controls for this device.
8749484Sgarrett.damore@Sun.COM  *
8759484Sgarrett.damore@Sun.COM  * Arguments:
8769484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		Device soft state.
8779484Sgarrett.damore@Sun.COM  */
8789484Sgarrett.damore@Sun.COM void
audiocs_del_controls(CS_state_t * state)8799484Sgarrett.damore@Sun.COM audiocs_del_controls(CS_state_t *state)
8809484Sgarrett.damore@Sun.COM {
8819484Sgarrett.damore@Sun.COM 	audiocs_free_ctrl(state->cs_ogain);
8829484Sgarrett.damore@Sun.COM 	audiocs_free_ctrl(state->cs_igain);
8839484Sgarrett.damore@Sun.COM 	audiocs_free_ctrl(state->cs_mgain);
8849484Sgarrett.damore@Sun.COM 	audiocs_free_ctrl(state->cs_micboost);
8859484Sgarrett.damore@Sun.COM 	audiocs_free_ctrl(state->cs_inputs);
8869484Sgarrett.damore@Sun.COM 	audiocs_free_ctrl(state->cs_outputs);
8879484Sgarrett.damore@Sun.COM }
8889484Sgarrett.damore@Sun.COM 
8899484Sgarrett.damore@Sun.COM 
8909484Sgarrett.damore@Sun.COM /*
8919484Sgarrett.damore@Sun.COM  * audiocs_chip_init()
8929484Sgarrett.damore@Sun.COM  *
8939484Sgarrett.damore@Sun.COM  * Description:
8949484Sgarrett.damore@Sun.COM  *	Power up the audio core, initialize the audio Codec, prepare the chip
8959484Sgarrett.damore@Sun.COM  *	for use.
8969484Sgarrett.damore@Sun.COM  *
8979484Sgarrett.damore@Sun.COM  * Arguments:
8989484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
8999484Sgarrett.damore@Sun.COM  *
9009484Sgarrett.damore@Sun.COM  * Returns:
9019484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS			Chip initialized and ready to use
9029484Sgarrett.damore@Sun.COM  *	DDI_FAILURE			Chip not initialized and not ready
9039484Sgarrett.damore@Sun.COM  */
9049484Sgarrett.damore@Sun.COM static int
audiocs_chip_init(CS_state_t * state)9059484Sgarrett.damore@Sun.COM audiocs_chip_init(CS_state_t *state)
9069484Sgarrett.damore@Sun.COM {
9079484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle = CODEC_HANDLE;
9089484Sgarrett.damore@Sun.COM 
9099484Sgarrett.damore@Sun.COM 	/* make sure we are powered up */
9109484Sgarrett.damore@Sun.COM 	CS4231_DMA_POWER(state, CS4231_PWR_ON);
9119484Sgarrett.damore@Sun.COM 
9129484Sgarrett.damore@Sun.COM 	CS4231_DMA_RESET(state);
9139484Sgarrett.damore@Sun.COM 
9149965Sgdamore@opensolaris.org 	/* wait for the Codec before we continue */
9159965Sgdamore@opensolaris.org 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
9169965Sgdamore@opensolaris.org 		return (DDI_FAILURE);
9179965Sgdamore@opensolaris.org 	}
9189965Sgdamore@opensolaris.org 
9199484Sgarrett.damore@Sun.COM 	/* activate registers 16 -> 31 */
9209484Sgarrett.damore@Sun.COM 	SELIDX(state, MID_REG);
9219484Sgarrett.damore@Sun.COM 	ddi_put8(handle, &CS4231_IDR, MID_MODE2);
9229484Sgarrett.damore@Sun.COM 
9239484Sgarrett.damore@Sun.COM 	/* now figure out what version we have */
9249484Sgarrett.damore@Sun.COM 	SELIDX(state, VID_REG);
9259484Sgarrett.damore@Sun.COM 	if (ddi_get8(handle, &CS4231_IDR) & VID_A) {
9269484Sgarrett.damore@Sun.COM 		state->cs_revA = B_TRUE;
9279484Sgarrett.damore@Sun.COM 	} else {
9289484Sgarrett.damore@Sun.COM 		state->cs_revA = B_FALSE;
9299484Sgarrett.damore@Sun.COM 	}
9309484Sgarrett.damore@Sun.COM 
9319484Sgarrett.damore@Sun.COM 	/* get rid of annoying popping by muting the output channels */
9329484Sgarrett.damore@Sun.COM 	SELIDX(state, LDACO_REG);
9339484Sgarrett.damore@Sun.COM 	PUTIDX(state, LDACO_LDM | LDACO_MID_GAIN, LDAC0_VALID_MASK);
9349484Sgarrett.damore@Sun.COM 	SELIDX(state, RDACO_REG);
9359484Sgarrett.damore@Sun.COM 	PUTIDX(state, RDACO_RDM | RDACO_MID_GAIN, RDAC0_VALID_MASK);
9369484Sgarrett.damore@Sun.COM 
9379484Sgarrett.damore@Sun.COM 	/* initialize aux input channels to known gain values & muted */
9389484Sgarrett.damore@Sun.COM 	SELIDX(state, LAUX1_REG);
9399484Sgarrett.damore@Sun.COM 	PUTIDX(state, LAUX1_LX1M | LAUX1_UNITY_GAIN, LAUX1_VALID_MASK);
9409484Sgarrett.damore@Sun.COM 	SELIDX(state, RAUX1_REG);
9419484Sgarrett.damore@Sun.COM 	PUTIDX(state, RAUX1_RX1M | RAUX1_UNITY_GAIN, RAUX1_VALID_MASK);
9429484Sgarrett.damore@Sun.COM 	SELIDX(state, LAUX2_REG);
9439484Sgarrett.damore@Sun.COM 	PUTIDX(state, LAUX2_LX2M | LAUX2_UNITY_GAIN, LAUX2_VALID_MASK);
9449484Sgarrett.damore@Sun.COM 	SELIDX(state, RAUX2_REG);
9459484Sgarrett.damore@Sun.COM 	PUTIDX(state, RAUX2_RX2M | RAUX2_UNITY_GAIN, RAUX2_VALID_MASK);
9469484Sgarrett.damore@Sun.COM 
9479484Sgarrett.damore@Sun.COM 	/* initialize aux input channels to known gain values & muted */
9489484Sgarrett.damore@Sun.COM 	SELIDX(state, LLIC_REG);
9499484Sgarrett.damore@Sun.COM 	PUTIDX(state, LLIC_LLM | LLIC_UNITY_GAIN, LLIC_VALID_MASK);
9509484Sgarrett.damore@Sun.COM 	SELIDX(state, RLIC_REG);
9519484Sgarrett.damore@Sun.COM 	PUTIDX(state, RLIC_RLM | RLIC_UNITY_GAIN, RLIC_VALID_MASK);
9529484Sgarrett.damore@Sun.COM 
9539484Sgarrett.damore@Sun.COM 	/* program the sample rate, play and capture must be the same */
9549484Sgarrett.damore@Sun.COM 	SELIDX(state, FSDF_REG | IAR_MCE);
9559484Sgarrett.damore@Sun.COM 	PUTIDX(state, FS_48000 | PDF_LINEAR16NE | PDF_STEREO, FSDF_VALID_MASK);
9569965Sgdamore@opensolaris.org 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
9579965Sgdamore@opensolaris.org 		return (DDI_FAILURE);
9589965Sgdamore@opensolaris.org 	}
9599965Sgdamore@opensolaris.org 
9609484Sgarrett.damore@Sun.COM 	SELIDX(state, CDF_REG | IAR_MCE);
9619484Sgarrett.damore@Sun.COM 	PUTIDX(state, CDF_LINEAR16NE | CDF_STEREO, CDF_VALID_MASK);
9629965Sgdamore@opensolaris.org 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
9639965Sgdamore@opensolaris.org 		return (DDI_FAILURE);
9649965Sgdamore@opensolaris.org 	}
9659484Sgarrett.damore@Sun.COM 
9669484Sgarrett.damore@Sun.COM 	/*
9679484Sgarrett.damore@Sun.COM 	 * Set up the Codec for playback and capture disabled, dual DMA, and
9689484Sgarrett.damore@Sun.COM 	 * playback and capture DMA.
9699484Sgarrett.damore@Sun.COM 	 */
9709484Sgarrett.damore@Sun.COM 	SELIDX(state, (INTC_REG | IAR_MCE));
9719484Sgarrett.damore@Sun.COM 	PUTIDX(state, INTC_DDC | INTC_PDMA | INTC_CDMA, INTC_VALID_MASK);
9729484Sgarrett.damore@Sun.COM 	if (audiocs_poll_ready(state) == DDI_FAILURE) {
9739484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
9749484Sgarrett.damore@Sun.COM 	}
9759484Sgarrett.damore@Sun.COM 
9769484Sgarrett.damore@Sun.COM 	/*
9779484Sgarrett.damore@Sun.COM 	 * Turn on the output level bit to be 2.8 Vpp. Also, don't go to 0 on
9789484Sgarrett.damore@Sun.COM 	 * underflow.
9799484Sgarrett.damore@Sun.COM 	 */
9809484Sgarrett.damore@Sun.COM 	SELIDX(state, AFE1_REG);
9819484Sgarrett.damore@Sun.COM 	PUTIDX(state, AFE1_OLB, AFE1_VALID_MASK);
9829484Sgarrett.damore@Sun.COM 
9839484Sgarrett.damore@Sun.COM 	/* turn on the high pass filter if Rev A */
9849484Sgarrett.damore@Sun.COM 	SELIDX(state, AFE2_REG);
9859484Sgarrett.damore@Sun.COM 	if (state->cs_revA) {
9869484Sgarrett.damore@Sun.COM 		PUTIDX(state, AFE2_HPF, AFE2_VALID_MASK);
9879484Sgarrett.damore@Sun.COM 	} else {
9889484Sgarrett.damore@Sun.COM 		PUTIDX(state, 0, AFE2_VALID_MASK);
9899484Sgarrett.damore@Sun.COM 	}
9909484Sgarrett.damore@Sun.COM 
991*11936Sgdamore@opensolaris.org 
9929484Sgarrett.damore@Sun.COM 	/* clear the play and capture interrupt flags */
9939484Sgarrett.damore@Sun.COM 	SELIDX(state, AFS_REG);
9949484Sgarrett.damore@Sun.COM 	ddi_put8(handle, &CS4231_STATUS, (AFS_RESET_STATUS));
9959484Sgarrett.damore@Sun.COM 
9969484Sgarrett.damore@Sun.COM 	/* the play and record gains will be set by the audio mixer */
9979484Sgarrett.damore@Sun.COM 
9989484Sgarrett.damore@Sun.COM 	/* unmute the output */
9999484Sgarrett.damore@Sun.COM 	SELIDX(state, LDACO_REG);
10009484Sgarrett.damore@Sun.COM 	ANDIDX(state, ~LDACO_LDM, LDAC0_VALID_MASK);
10019484Sgarrett.damore@Sun.COM 	SELIDX(state, RDACO_REG);
10029484Sgarrett.damore@Sun.COM 	ANDIDX(state, ~RDACO_RDM, RDAC0_VALID_MASK);
10039484Sgarrett.damore@Sun.COM 
10049484Sgarrett.damore@Sun.COM 	/* unmute the mono speaker and mute mono in */
10059484Sgarrett.damore@Sun.COM 	SELIDX(state, MIOC_REG);
10069484Sgarrett.damore@Sun.COM 	PUTIDX(state, MIOC_MIM, MIOC_VALID_MASK);
10079484Sgarrett.damore@Sun.COM 
10089965Sgdamore@opensolaris.org 	audiocs_configure_output(state);
10099965Sgdamore@opensolaris.org 	audiocs_configure_input(state);
10109484Sgarrett.damore@Sun.COM 
10119484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
10129484Sgarrett.damore@Sun.COM }
10139484Sgarrett.damore@Sun.COM 
10149484Sgarrett.damore@Sun.COM /*
10159484Sgarrett.damore@Sun.COM  * audiocs_init_state()
10169484Sgarrett.damore@Sun.COM  *
10179484Sgarrett.damore@Sun.COM  * Description:
10189484Sgarrett.damore@Sun.COM  *	This routine initializes the audio driver's state structure and
10199484Sgarrett.damore@Sun.COM  *	maps in the registers. This also includes reading the properties.
10209484Sgarrett.damore@Sun.COM  *
10219484Sgarrett.damore@Sun.COM  *	CAUTION: This routine maps the registers and initializes a mutex.
10229484Sgarrett.damore@Sun.COM  *		 Failure cleanup is handled by cs4231_attach(). It is not
10239484Sgarrett.damore@Sun.COM  *		 handled locally by this routine.
10249484Sgarrett.damore@Sun.COM  *
10259484Sgarrett.damore@Sun.COM  * Arguments:
10269484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
10279484Sgarrett.damore@Sun.COM  *
10289484Sgarrett.damore@Sun.COM  * Returns:
10299484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS			State structure initialized
10309484Sgarrett.damore@Sun.COM  *	DDI_FAILURE			State structure not initialized
10319484Sgarrett.damore@Sun.COM  */
10329484Sgarrett.damore@Sun.COM static int
audiocs_init_state(CS_state_t * state)10339484Sgarrett.damore@Sun.COM audiocs_init_state(CS_state_t *state)
10349484Sgarrett.damore@Sun.COM {
10359484Sgarrett.damore@Sun.COM 	audio_dev_t	*adev = state->cs_adev;
10369484Sgarrett.damore@Sun.COM 	dev_info_t	*dip = state->cs_dip;
10379484Sgarrett.damore@Sun.COM 	char		*prop_str;
10389484Sgarrett.damore@Sun.COM 	char		*pm_comp[] = {
10399484Sgarrett.damore@Sun.COM 				"NAME=audiocs audio device",
10409484Sgarrett.damore@Sun.COM 				"0=off",
10419484Sgarrett.damore@Sun.COM 				"1=on" };
10429484Sgarrett.damore@Sun.COM 
10439484Sgarrett.damore@Sun.COM 	/* set up the pm-components */
10449484Sgarrett.damore@Sun.COM 	if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip,
10459484Sgarrett.damore@Sun.COM 	    "pm-components", pm_comp, 3) != DDI_PROP_SUCCESS) {
10469484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "couldn't create pm-components property");
10479484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
10489484Sgarrett.damore@Sun.COM 	}
10499484Sgarrett.damore@Sun.COM 
10509484Sgarrett.damore@Sun.COM 	/* figure out which DMA engine hardware we have */
10519484Sgarrett.damore@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
10529484Sgarrett.damore@Sun.COM 	    "dma-model", &prop_str) == DDI_PROP_SUCCESS) {
10539484Sgarrett.damore@Sun.COM 		if (strcmp(prop_str, "eb2dma") == 0) {
10549484Sgarrett.damore@Sun.COM 			state->cs_dma_engine = EB2_DMA;
10559484Sgarrett.damore@Sun.COM 			state->cs_dma_ops = &cs4231_eb2dma_ops;
10569484Sgarrett.damore@Sun.COM 		} else {
10579484Sgarrett.damore@Sun.COM 			state->cs_dma_engine = APC_DMA;
10589484Sgarrett.damore@Sun.COM 			state->cs_dma_ops = &cs4231_apcdma_ops;
10599484Sgarrett.damore@Sun.COM 		}
10609484Sgarrett.damore@Sun.COM 		ddi_prop_free(prop_str);
10619484Sgarrett.damore@Sun.COM 	} else {
10629484Sgarrett.damore@Sun.COM 		state->cs_dma_engine = APC_DMA;
10639484Sgarrett.damore@Sun.COM 		state->cs_dma_ops = &cs4231_apcdma_ops;
10649484Sgarrett.damore@Sun.COM 	}
10659484Sgarrett.damore@Sun.COM 
10669484Sgarrett.damore@Sun.COM 	/* cs_regs, cs_eb2_regs and cs_handles filled in later */
10679484Sgarrett.damore@Sun.COM 
10689484Sgarrett.damore@Sun.COM 	/* most of what's left is filled in when the registers are mapped */
10699484Sgarrett.damore@Sun.COM 
10709484Sgarrett.damore@Sun.COM 	audiocs_get_ports(state);
10719484Sgarrett.damore@Sun.COM 
10729484Sgarrett.damore@Sun.COM 	/* Allocate engines, must be done before register mapping called  */
10739484Sgarrett.damore@Sun.COM 	if ((audiocs_alloc_engine(state, CS4231_PLAY) != DDI_SUCCESS) ||
10749484Sgarrett.damore@Sun.COM 	    (audiocs_alloc_engine(state, CS4231_REC) != DDI_SUCCESS)) {
10759484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
10769484Sgarrett.damore@Sun.COM 	}
10779484Sgarrett.damore@Sun.COM 
10789484Sgarrett.damore@Sun.COM 	/* Map in the registers */
10799484Sgarrett.damore@Sun.COM 	if (CS4231_DMA_MAP_REGS(state) == DDI_FAILURE) {
10809484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
10819484Sgarrett.damore@Sun.COM 	}
10829484Sgarrett.damore@Sun.COM 
10839484Sgarrett.damore@Sun.COM 
10849484Sgarrett.damore@Sun.COM 	/* Allocate and add controls, must be done *after* registers mapped */
10859484Sgarrett.damore@Sun.COM 	if (audiocs_add_controls(state) != DDI_SUCCESS) {
10869484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
10879484Sgarrett.damore@Sun.COM 	}
10889484Sgarrett.damore@Sun.COM 
10899484Sgarrett.damore@Sun.COM 	state->cs_suspended = B_FALSE;
10909484Sgarrett.damore@Sun.COM 	state->cs_powered = B_FALSE;
10919484Sgarrett.damore@Sun.COM 
10929484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
10939484Sgarrett.damore@Sun.COM }
10949484Sgarrett.damore@Sun.COM 
10959484Sgarrett.damore@Sun.COM /*
10969484Sgarrett.damore@Sun.COM  * audiocs_get_ports()
10979484Sgarrett.damore@Sun.COM  *
10989484Sgarrett.damore@Sun.COM  * Description:
10999484Sgarrett.damore@Sun.COM  *	Get which audiocs h/w version we have and use this to
11009484Sgarrett.damore@Sun.COM  *	determine the input and output ports as well whether or not
11019484Sgarrett.damore@Sun.COM  *	the hardware has internal loopbacks or not. We also have three
11029484Sgarrett.damore@Sun.COM  *	different ways for the properties to be specified, which we
11039484Sgarrett.damore@Sun.COM  *	also need to worry about.
11049484Sgarrett.damore@Sun.COM  *
11059484Sgarrett.damore@Sun.COM  * Vers	Platform(s)	DMA eng.	audio-module**	loopback
11069484Sgarrett.damore@Sun.COM  * a    SS-4+/SS-5+	apcdma		no		no
11079484Sgarrett.damore@Sun.COM  * b	Ultra-1&2	apcdma		no		yes
11089484Sgarrett.damore@Sun.COM  * c	positron	apcdma		no		yes
11099484Sgarrett.damore@Sun.COM  * d	PPC - retired
11109484Sgarrett.damore@Sun.COM  * e	x86 - retired
11119484Sgarrett.damore@Sun.COM  * f	tazmo		eb2dma		Perigee		no
11129484Sgarrett.damore@Sun.COM  * g	tazmo		eb2dma		Quark		yes
11139484Sgarrett.damore@Sun.COM  * h	darwin+		eb2dma		no		N/A
11149484Sgarrett.damore@Sun.COM  *
11159484Sgarrett.damore@Sun.COM  * Vers	model~		aux1*		aux2*
11169484Sgarrett.damore@Sun.COM  * a	N/A		N/A		N/A
11179484Sgarrett.damore@Sun.COM  * b	N/A		N/A		N/A
11189484Sgarrett.damore@Sun.COM  * c	N/A		N/A		N/A
11199484Sgarrett.damore@Sun.COM  * d	retired
11209484Sgarrett.damore@Sun.COM  * e	retired
11219484Sgarrett.damore@Sun.COM  * f	SUNW,CS4231f	N/A		N/A
11229484Sgarrett.damore@Sun.COM  * g	SUNW,CS4231g	N/A		N/A
11239484Sgarrett.damore@Sun.COM  * h	SUNW,CS4231h	cdrom		none
11249484Sgarrett.damore@Sun.COM  *
11259484Sgarrett.damore@Sun.COM  * *   = Replaces internal-loopback for latest property type, can be
11269484Sgarrett.damore@Sun.COM  *	 set to "cdrom", "loopback", or "none".
11279484Sgarrett.damore@Sun.COM  *
11289484Sgarrett.damore@Sun.COM  * **  = For plugin audio modules only. Starting with darwin, this
11299484Sgarrett.damore@Sun.COM  *	 property is replaces by the model property.
11309484Sgarrett.damore@Sun.COM  *
11319484Sgarrett.damore@Sun.COM  * ~   = Replaces audio-module.
11329484Sgarrett.damore@Sun.COM  *
11339484Sgarrett.damore@Sun.COM  * +   = Has the capability of having a cable run from the internal
11349484Sgarrett.damore@Sun.COM  *	 CD-ROM to the audio device.
11359484Sgarrett.damore@Sun.COM  *
11369484Sgarrett.damore@Sun.COM  * N/A = Not applicable, the property wasn't created for early
11379484Sgarrett.damore@Sun.COM  *	 platforms, or the property has been retired.
11389484Sgarrett.damore@Sun.COM  *
11399484Sgarrett.damore@Sun.COM  * NOTE: Older tazmo and quark machines don't have the model property.
11409484Sgarrett.damore@Sun.COM  *
11419484Sgarrett.damore@Sun.COM  * Arguments:
11429484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
11439484Sgarrett.damore@Sun.COM  */
11449484Sgarrett.damore@Sun.COM static void
audiocs_get_ports(CS_state_t * state)11459484Sgarrett.damore@Sun.COM audiocs_get_ports(CS_state_t *state)
11469484Sgarrett.damore@Sun.COM {
11479484Sgarrett.damore@Sun.COM 	dev_info_t	*dip = state->cs_dip;
11489484Sgarrett.damore@Sun.COM 	audio_dev_t	*adev = state->cs_adev;
11499484Sgarrett.damore@Sun.COM 	char		*prop_str;
11509484Sgarrett.damore@Sun.COM 
11519484Sgarrett.damore@Sun.COM 	/* First we set the common ports, etc. */
11529484Sgarrett.damore@Sun.COM 	state->cs_omask = state->cs_omod =
11539484Sgarrett.damore@Sun.COM 	    (1U << OUTPUT_SPEAKER) |
11549484Sgarrett.damore@Sun.COM 	    (1U << OUTPUT_HEADPHONES) |
11559484Sgarrett.damore@Sun.COM 	    (1U << OUTPUT_LINEOUT);
11569484Sgarrett.damore@Sun.COM 	state->cs_imask =
11579484Sgarrett.damore@Sun.COM 	    (1U << INPUT_MIC) |
11589484Sgarrett.damore@Sun.COM 	    (1U << INPUT_LINEIN) |
11599484Sgarrett.damore@Sun.COM 	    (1U << INPUT_STEREOMIX);
11609484Sgarrett.damore@Sun.COM 
11619484Sgarrett.damore@Sun.COM 	/* now we try the new "model" property */
11629484Sgarrett.damore@Sun.COM 	if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
11639484Sgarrett.damore@Sun.COM 	    "model", &prop_str) == DDI_PROP_SUCCESS) {
11649484Sgarrett.damore@Sun.COM 		if (strcmp(prop_str, "SUNW,CS4231h") == 0) {
11659484Sgarrett.damore@Sun.COM 			/* darwin */
11669484Sgarrett.damore@Sun.COM 			audio_dev_set_version(adev, CS_DEV_VERSION_H);
11679484Sgarrett.damore@Sun.COM 			state->cs_imask |= (1U << INPUT_CD);
11689484Sgarrett.damore@Sun.COM 			state->cs_omod = (1U << OUTPUT_SPEAKER);
11699484Sgarrett.damore@Sun.COM 		} else if (strcmp(prop_str, "SUNW,CS4231g") == 0) {
11709484Sgarrett.damore@Sun.COM 			/* quark audio module */
11719484Sgarrett.damore@Sun.COM 			audio_dev_set_version(adev, CS_DEV_VERSION_G);
11729484Sgarrett.damore@Sun.COM 			/*
11739484Sgarrett.damore@Sun.COM 			 * NB: This could do SUNVTS LOOPBACK, but we
11749484Sgarrett.damore@Sun.COM 			 * don't support it for now... owing to no
11759484Sgarrett.damore@Sun.COM 			 * support in framework.
11769484Sgarrett.damore@Sun.COM 			 */
11779484Sgarrett.damore@Sun.COM 		} else if (strcmp(prop_str, "SUNW,CS4231f") == 0) {
11789484Sgarrett.damore@Sun.COM 			/* tazmo */
11799484Sgarrett.damore@Sun.COM 			audio_dev_set_version(adev, CS_DEV_VERSION_F);
11809484Sgarrett.damore@Sun.COM 		} else {
11819484Sgarrett.damore@Sun.COM 			audio_dev_set_version(adev, prop_str);
11829484Sgarrett.damore@Sun.COM 			audio_dev_warn(adev,
11839484Sgarrett.damore@Sun.COM 			    "unknown audio model: %s, some parts of "
11849484Sgarrett.damore@Sun.COM 			    "audio may not work correctly", prop_str);
11859484Sgarrett.damore@Sun.COM 		}
11869484Sgarrett.damore@Sun.COM 		ddi_prop_free(prop_str);	/* done with the property */
11879484Sgarrett.damore@Sun.COM 	} else {	/* now try the older "audio-module" property */
11889484Sgarrett.damore@Sun.COM 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
11899484Sgarrett.damore@Sun.COM 		    DDI_PROP_DONTPASS, "audio-module", &prop_str) ==
11909484Sgarrett.damore@Sun.COM 		    DDI_PROP_SUCCESS) {
11919484Sgarrett.damore@Sun.COM 			switch (*prop_str) {
11929484Sgarrett.damore@Sun.COM 			case 'Q':	/* quark audio module */
11939484Sgarrett.damore@Sun.COM 				audio_dev_set_version(adev, CS_DEV_VERSION_G);
11949484Sgarrett.damore@Sun.COM 				/* See quark comment above about SunVTS */
11959484Sgarrett.damore@Sun.COM 				break;
11969484Sgarrett.damore@Sun.COM 			case 'P':	/* tazmo */
11979484Sgarrett.damore@Sun.COM 				audio_dev_set_version(adev, CS_DEV_VERSION_F);
11989484Sgarrett.damore@Sun.COM 				break;
11999484Sgarrett.damore@Sun.COM 			default:
12009484Sgarrett.damore@Sun.COM 				audio_dev_set_version(adev, prop_str);
12019484Sgarrett.damore@Sun.COM 				audio_dev_warn(adev,
12029484Sgarrett.damore@Sun.COM 				    "unknown audio module: %s, some "
12039484Sgarrett.damore@Sun.COM 				    "parts of audio may not work correctly",
12049484Sgarrett.damore@Sun.COM 				    prop_str);
12059484Sgarrett.damore@Sun.COM 				break;
12069484Sgarrett.damore@Sun.COM 			}
12079484Sgarrett.damore@Sun.COM 			ddi_prop_free(prop_str);	/* done with the prop */
12089484Sgarrett.damore@Sun.COM 		} else {	/* now try heuristics, ;-( */
12099484Sgarrett.damore@Sun.COM 			if (ddi_prop_get_int(DDI_DEV_T_ANY, dip,
12109484Sgarrett.damore@Sun.COM 			    DDI_PROP_DONTPASS, "internal-loopback", B_FALSE)) {
12119484Sgarrett.damore@Sun.COM 				if (state->cs_dma_engine == EB2_DMA) {
12129484Sgarrett.damore@Sun.COM 					audio_dev_set_version(adev,
12139484Sgarrett.damore@Sun.COM 					    CS_DEV_VERSION_C);
12149484Sgarrett.damore@Sun.COM 				} else {
12159484Sgarrett.damore@Sun.COM 					audio_dev_set_version(adev,
12169484Sgarrett.damore@Sun.COM 					    CS_DEV_VERSION_B);
12179484Sgarrett.damore@Sun.COM 				}
12189484Sgarrett.damore@Sun.COM 				/*
12199484Sgarrett.damore@Sun.COM 				 * Again, we don't support SunVTS for these
12209484Sgarrett.damore@Sun.COM 				 * boards, although we potentially could.
12219484Sgarrett.damore@Sun.COM 				 */
12229484Sgarrett.damore@Sun.COM 			} else {
12239484Sgarrett.damore@Sun.COM 				audio_dev_set_version(adev, CS_DEV_VERSION_A);
12249484Sgarrett.damore@Sun.COM 				state->cs_imask |= (1U << INPUT_CD);
12259484Sgarrett.damore@Sun.COM 			}
12269484Sgarrett.damore@Sun.COM 		}
12279484Sgarrett.damore@Sun.COM 	}
12289484Sgarrett.damore@Sun.COM }
12299484Sgarrett.damore@Sun.COM 
12309484Sgarrett.damore@Sun.COM /*
12319484Sgarrett.damore@Sun.COM  * audiocs_power_up()
12329484Sgarrett.damore@Sun.COM  *
12339484Sgarrett.damore@Sun.COM  * Description:
12349484Sgarrett.damore@Sun.COM  *	Power up the Codec and restore the codec's registers.
12359484Sgarrett.damore@Sun.COM  *
12369484Sgarrett.damore@Sun.COM  *	NOTE: We don't worry about locking since the only routines
12379484Sgarrett.damore@Sun.COM  *		that may call us are attach() and power() Both of
12389484Sgarrett.damore@Sun.COM  *		which should be the only threads in the driver.
12399484Sgarrett.damore@Sun.COM  *
12409484Sgarrett.damore@Sun.COM  * Arguments:
12419484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
12429484Sgarrett.damore@Sun.COM  */
12439484Sgarrett.damore@Sun.COM static void
audiocs_power_up(CS_state_t * state)12449484Sgarrett.damore@Sun.COM audiocs_power_up(CS_state_t *state)
12459484Sgarrett.damore@Sun.COM {
12469484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle = CODEC_HANDLE;
12479484Sgarrett.damore@Sun.COM 	int			i;
12489484Sgarrett.damore@Sun.COM 
12499484Sgarrett.damore@Sun.COM 	/* turn on the Codec */
12509484Sgarrett.damore@Sun.COM 	CS4231_DMA_POWER(state, CS4231_PWR_ON);
12519484Sgarrett.damore@Sun.COM 
12529484Sgarrett.damore@Sun.COM 	/* reset the DMA engine(s) */
12539484Sgarrett.damore@Sun.COM 	CS4231_DMA_RESET(state);
12549484Sgarrett.damore@Sun.COM 
12559965Sgdamore@opensolaris.org 	(void) audiocs_poll_ready(state);
12569965Sgdamore@opensolaris.org 
12579484Sgarrett.damore@Sun.COM 	/*
12589484Sgarrett.damore@Sun.COM 	 * Reload the Codec's registers, the DMA engines will be
12599484Sgarrett.damore@Sun.COM 	 * taken care of when play and record start up again. But
12609484Sgarrett.damore@Sun.COM 	 * first enable registers 16 -> 31.
12619484Sgarrett.damore@Sun.COM 	 */
12629484Sgarrett.damore@Sun.COM 	SELIDX(state, MID_REG);
12639484Sgarrett.damore@Sun.COM 	PUTIDX(state, state->cs_save[MID_REG], MID_VALID_MASK);
12649484Sgarrett.damore@Sun.COM 
12659484Sgarrett.damore@Sun.COM 	for (i = 0; i < CS4231_REGS; i++) {
12669484Sgarrett.damore@Sun.COM 		/* restore Codec registers */
12679484Sgarrett.damore@Sun.COM 		SELIDX(state, (i | IAR_MCE));
12689484Sgarrett.damore@Sun.COM 		ddi_put8(handle, &CS4231_IDR, state->cs_save[i]);
12699965Sgdamore@opensolaris.org 		(void) audiocs_poll_ready(state);
12709484Sgarrett.damore@Sun.COM 	}
12719484Sgarrett.damore@Sun.COM 	/* clear MCE bit */
12729484Sgarrett.damore@Sun.COM 	SELIDX(state, 0);
12739484Sgarrett.damore@Sun.COM }
12749484Sgarrett.damore@Sun.COM 
12759484Sgarrett.damore@Sun.COM /*
12769484Sgarrett.damore@Sun.COM  * audiocs_power_down()
12779484Sgarrett.damore@Sun.COM  *
12789484Sgarrett.damore@Sun.COM  * Description:
12799484Sgarrett.damore@Sun.COM  *	Power down the Codec and save the codec's registers.
12809484Sgarrett.damore@Sun.COM  *
12819484Sgarrett.damore@Sun.COM  *	NOTE: See the note in cs4231_power_up() about locking.
12829484Sgarrett.damore@Sun.COM  *
12839484Sgarrett.damore@Sun.COM  * Arguments:
12849484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
12859484Sgarrett.damore@Sun.COM  */
12869484Sgarrett.damore@Sun.COM static void
audiocs_power_down(CS_state_t * state)12879484Sgarrett.damore@Sun.COM audiocs_power_down(CS_state_t *state)
12889484Sgarrett.damore@Sun.COM {
12899484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle;
12909484Sgarrett.damore@Sun.COM 	int			i;
12919484Sgarrett.damore@Sun.COM 
12929484Sgarrett.damore@Sun.COM 	handle = state->cs_handles.cs_codec_hndl;
12939484Sgarrett.damore@Sun.COM 
12949484Sgarrett.damore@Sun.COM 	/*
12959484Sgarrett.damore@Sun.COM 	 * We are powering down, so we don't need to do a thing with
12969484Sgarrett.damore@Sun.COM 	 * the DMA engines. However, we do need to save the Codec
12979484Sgarrett.damore@Sun.COM 	 * registers.
12989484Sgarrett.damore@Sun.COM 	 */
12999484Sgarrett.damore@Sun.COM 
13009484Sgarrett.damore@Sun.COM 	for (i = 0; i < CS4231_REGS; i++) {
13019484Sgarrett.damore@Sun.COM 		/* save Codec regs */
13029484Sgarrett.damore@Sun.COM 		SELIDX(state, i);
13039484Sgarrett.damore@Sun.COM 		state->cs_save[i] = ddi_get8(handle, &CS4231_IDR);
13049484Sgarrett.damore@Sun.COM 	}
13059484Sgarrett.damore@Sun.COM 
13069484Sgarrett.damore@Sun.COM 	/* turn off the Codec */
13079484Sgarrett.damore@Sun.COM 	CS4231_DMA_POWER(state, CS4231_PWR_OFF);
13089484Sgarrett.damore@Sun.COM 
13099484Sgarrett.damore@Sun.COM }	/* cs4231_power_down() */
13109484Sgarrett.damore@Sun.COM 
13119484Sgarrett.damore@Sun.COM /*
13129484Sgarrett.damore@Sun.COM  * audiocs_configure_input()
13139484Sgarrett.damore@Sun.COM  *
13149484Sgarrett.damore@Sun.COM  * Description:
13159484Sgarrett.damore@Sun.COM  *	Configure input properties of the mixer (e.g. igain, ports).
13169484Sgarrett.damore@Sun.COM  *
13179484Sgarrett.damore@Sun.COM  * Arguments:
13189484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
13199484Sgarrett.damore@Sun.COM  */
13209484Sgarrett.damore@Sun.COM static void
audiocs_configure_input(CS_state_t * state)13219484Sgarrett.damore@Sun.COM audiocs_configure_input(CS_state_t *state)
13229484Sgarrett.damore@Sun.COM {
13239484Sgarrett.damore@Sun.COM 	uint8_t		l, r;
13249484Sgarrett.damore@Sun.COM 	uint64_t	inputs;
13259484Sgarrett.damore@Sun.COM 	uint64_t	micboost;
13269484Sgarrett.damore@Sun.COM 
13279484Sgarrett.damore@Sun.COM 	ASSERT(mutex_owned(&state->cs_lock));
13289484Sgarrett.damore@Sun.COM 
13299484Sgarrett.damore@Sun.COM 	inputs = state->cs_inputs->cc_val;
13309484Sgarrett.damore@Sun.COM 	micboost = state->cs_micboost->cc_val;
13319484Sgarrett.damore@Sun.COM 	r = (state->cs_igain->cc_val & 0xff);
13329484Sgarrett.damore@Sun.COM 	l = ((state->cs_igain->cc_val & 0xff00) >> 8);
13339484Sgarrett.damore@Sun.COM 
13349484Sgarrett.damore@Sun.COM 	/* rescale these for our atten array */
13359484Sgarrett.damore@Sun.COM 	l = (((uint32_t)l * 255) / 100) & 0xff;
13369484Sgarrett.damore@Sun.COM 	r = (((uint32_t)r * 255) / 100) & 0xff;
13379484Sgarrett.damore@Sun.COM 
13389484Sgarrett.damore@Sun.COM 	/* we downshift by 4 bits -- igain only has 16 possible values */
13399484Sgarrett.damore@Sun.COM 	/* NB: that we do not scale here!  The SADA driver didn't do so. */
13409484Sgarrett.damore@Sun.COM 	l = l >> 4;
13419484Sgarrett.damore@Sun.COM 	r = r >> 4;
13429484Sgarrett.damore@Sun.COM 
13439484Sgarrett.damore@Sun.COM 	if (inputs & (1U << INPUT_MIC)) {
13449484Sgarrett.damore@Sun.COM 		l |= LADCI_LMIC;
13459484Sgarrett.damore@Sun.COM 		r |= RADCI_RMIC;
13469484Sgarrett.damore@Sun.COM 	}
13479484Sgarrett.damore@Sun.COM 	if (inputs & (1U << INPUT_LINEIN)) {
13489484Sgarrett.damore@Sun.COM 		l |= LADCI_LLINE;
13499484Sgarrett.damore@Sun.COM 		r |= RADCI_RLINE;
13509484Sgarrett.damore@Sun.COM 	}
13519484Sgarrett.damore@Sun.COM 	if (inputs & (1U << INPUT_CD)) {
13529484Sgarrett.damore@Sun.COM 		/* note that SunVTS also uses this */
13539484Sgarrett.damore@Sun.COM 		l |= LADCI_LAUX1;
13549484Sgarrett.damore@Sun.COM 		r |= RADCI_RAUX1;
13559484Sgarrett.damore@Sun.COM 	}
13569484Sgarrett.damore@Sun.COM 	if (inputs & (1U << INPUT_STEREOMIX)) {
13579484Sgarrett.damore@Sun.COM 		l |= LADCI_LLOOP;
13589484Sgarrett.damore@Sun.COM 		r |= RADCI_RLOOP;
13599484Sgarrett.damore@Sun.COM 	}
13609484Sgarrett.damore@Sun.COM 	if (micboost) {
13619484Sgarrett.damore@Sun.COM 		l |= LADCI_LMGE;
13629484Sgarrett.damore@Sun.COM 		r |= RADCI_RMGE;
13639484Sgarrett.damore@Sun.COM 	}
13649484Sgarrett.damore@Sun.COM 
13659484Sgarrett.damore@Sun.COM 	SELIDX(state, LADCI_REG);
13669484Sgarrett.damore@Sun.COM 	PUTIDX(state, l, LADCI_VALID_MASK);
13679484Sgarrett.damore@Sun.COM 
13689484Sgarrett.damore@Sun.COM 	SELIDX(state, RADCI_REG);
13699484Sgarrett.damore@Sun.COM 	PUTIDX(state, r, RADCI_VALID_MASK);
13709484Sgarrett.damore@Sun.COM }
13719484Sgarrett.damore@Sun.COM 
13729484Sgarrett.damore@Sun.COM /*
13739484Sgarrett.damore@Sun.COM  * audiocs_configure_output()
13749484Sgarrett.damore@Sun.COM  *
13759484Sgarrett.damore@Sun.COM  * Description:
13769484Sgarrett.damore@Sun.COM  *	Configure output properties of the mixer (e.g. ogain, mgain).
13779484Sgarrett.damore@Sun.COM  *
13789484Sgarrett.damore@Sun.COM  * Arguments:
13799484Sgarrett.damore@Sun.COM  *	CS_state_t	*state		The device's state structure
13809484Sgarrett.damore@Sun.COM  */
13819484Sgarrett.damore@Sun.COM static void
audiocs_configure_output(CS_state_t * state)13829484Sgarrett.damore@Sun.COM audiocs_configure_output(CS_state_t *state)
13839484Sgarrett.damore@Sun.COM {
13849484Sgarrett.damore@Sun.COM 	uint64_t		outputs;
13859484Sgarrett.damore@Sun.COM 	uint8_t			l, r;
13869484Sgarrett.damore@Sun.COM 	uint8_t			rmute, lmute;
13879484Sgarrett.damore@Sun.COM 	uint8_t			mgain;
13889484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle = CODEC_HANDLE;
13899484Sgarrett.damore@Sun.COM 
13909484Sgarrett.damore@Sun.COM 	rmute = lmute = 0;
13919484Sgarrett.damore@Sun.COM 
13929484Sgarrett.damore@Sun.COM 	ASSERT(mutex_owned(&state->cs_lock));
13939484Sgarrett.damore@Sun.COM 
13949484Sgarrett.damore@Sun.COM 	outputs = state->cs_outputs->cc_val;
13959484Sgarrett.damore@Sun.COM 
13969484Sgarrett.damore@Sun.COM 	/* port selection */
13979484Sgarrett.damore@Sun.COM 	SELIDX(state, MIOC_REG);
13989484Sgarrett.damore@Sun.COM 	if (outputs & (1U << OUTPUT_SPEAKER)) {
13999484Sgarrett.damore@Sun.COM 		ANDIDX(state, ~MIOC_MONO_SPKR_MUTE, MIOC_VALID_MASK);
14009484Sgarrett.damore@Sun.COM 	} else {
14019484Sgarrett.damore@Sun.COM 		ORIDX(state, MIOC_MONO_SPKR_MUTE, MIOC_VALID_MASK);
14029484Sgarrett.damore@Sun.COM 	}
14039484Sgarrett.damore@Sun.COM 	SELIDX(state, PC_REG);
14049484Sgarrett.damore@Sun.COM 	if (outputs & (1U << OUTPUT_HEADPHONES)) {
14059484Sgarrett.damore@Sun.COM 		ANDIDX(state, ~PC_HEADPHONE_MUTE, PC_VALID_MASK);
14069484Sgarrett.damore@Sun.COM 	} else {
14079484Sgarrett.damore@Sun.COM 		ORIDX(state, PC_HEADPHONE_MUTE, PC_VALID_MASK);
14089484Sgarrett.damore@Sun.COM 	}
14099484Sgarrett.damore@Sun.COM 	SELIDX(state, PC_REG);
14109484Sgarrett.damore@Sun.COM 	if (outputs & (1U << OUTPUT_LINEOUT)) {
14119484Sgarrett.damore@Sun.COM 		ANDIDX(state, ~PC_LINE_OUT_MUTE, PC_VALID_MASK);
14129484Sgarrett.damore@Sun.COM 	} else {
14139484Sgarrett.damore@Sun.COM 		ORIDX(state, PC_LINE_OUT_MUTE, PC_VALID_MASK);
14149484Sgarrett.damore@Sun.COM 	}
14159484Sgarrett.damore@Sun.COM 
14169484Sgarrett.damore@Sun.COM 	/* monitor gain */
14179484Sgarrett.damore@Sun.COM 	mgain = cs4231_atten[((state->cs_mgain->cc_val * 255) / 100) & 0xff];
14189484Sgarrett.damore@Sun.COM 	SELIDX(state, LC_REG);
14199484Sgarrett.damore@Sun.COM 	if (mgain == 0) {
14209484Sgarrett.damore@Sun.COM 		/* disable loopbacks when gain == 0 */
14219484Sgarrett.damore@Sun.COM 		PUTIDX(state, LC_OFF, LC_VALID_MASK);
14229484Sgarrett.damore@Sun.COM 	} else {
14239484Sgarrett.damore@Sun.COM 		/* we use cs4231_atten[] to linearize attenuation */
14249484Sgarrett.damore@Sun.COM 		PUTIDX(state, (mgain << 2) | LC_LBE, LC_VALID_MASK);
14259484Sgarrett.damore@Sun.COM 	}
14269484Sgarrett.damore@Sun.COM 
14279484Sgarrett.damore@Sun.COM 	/* output gain */
14289484Sgarrett.damore@Sun.COM 	l = ((state->cs_ogain->cc_val >> 8) & 0xff);
14299484Sgarrett.damore@Sun.COM 	r = (state->cs_ogain->cc_val & 0xff);
14309484Sgarrett.damore@Sun.COM 	if (l == 0) {
14319484Sgarrett.damore@Sun.COM 		lmute = LDACO_LDM;
14329484Sgarrett.damore@Sun.COM 	}
14339484Sgarrett.damore@Sun.COM 	if (r == 0) {
14349484Sgarrett.damore@Sun.COM 		rmute = RDACO_RDM;
14359484Sgarrett.damore@Sun.COM 	}
14369484Sgarrett.damore@Sun.COM 
14379484Sgarrett.damore@Sun.COM 	/* rescale these for our atten array */
14389484Sgarrett.damore@Sun.COM 	l = cs4231_atten[(((uint32_t)l * 255) / 100) & 0xff] | lmute;
14399484Sgarrett.damore@Sun.COM 	r = cs4231_atten[(((uint32_t)r * 255) / 100) & 0xff] | rmute;
14409484Sgarrett.damore@Sun.COM 
14419484Sgarrett.damore@Sun.COM 	SELIDX(state, LDACO_REG);
14429484Sgarrett.damore@Sun.COM 	PUTIDX(state, l, LDAC0_VALID_MASK);
14439484Sgarrett.damore@Sun.COM 	SELIDX(state, RDACO_REG);
14449484Sgarrett.damore@Sun.COM 	PUTIDX(state, r, RDAC0_VALID_MASK);
14459484Sgarrett.damore@Sun.COM }
14469484Sgarrett.damore@Sun.COM 
14479484Sgarrett.damore@Sun.COM /*
14489484Sgarrett.damore@Sun.COM  * audiocs_get_value()
14499484Sgarrett.damore@Sun.COM  *
14509484Sgarrett.damore@Sun.COM  * Description:
14519484Sgarrett.damore@Sun.COM  *	Get a control value
14529484Sgarrett.damore@Sun.COM  *
14539484Sgarrett.damore@Sun.COM  * Arguments:
14549484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
14559484Sgarrett.damore@Sun.COM  *	uint64_t	*valp		Pointer to store value.
14569484Sgarrett.damore@Sun.COM  *
14579484Sgarrett.damore@Sun.COM  * Returns:
14589484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been retrieved.
14599484Sgarrett.damore@Sun.COM  */
14609484Sgarrett.damore@Sun.COM static int
audiocs_get_value(void * arg,uint64_t * valp)14619484Sgarrett.damore@Sun.COM audiocs_get_value(void *arg, uint64_t *valp)
14629484Sgarrett.damore@Sun.COM {
14639484Sgarrett.damore@Sun.COM 	CS_ctrl_t		*cc = arg;
14649484Sgarrett.damore@Sun.COM 	CS_state_t		*state = cc->cc_state;
14659484Sgarrett.damore@Sun.COM 
14669484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
14679484Sgarrett.damore@Sun.COM 	*valp = cc->cc_val;
14689484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
14699484Sgarrett.damore@Sun.COM 	return (0);
14709484Sgarrett.damore@Sun.COM }
14719484Sgarrett.damore@Sun.COM 
14729484Sgarrett.damore@Sun.COM 
14739484Sgarrett.damore@Sun.COM /*
14749484Sgarrett.damore@Sun.COM  * audiocs_set_ogain()
14759484Sgarrett.damore@Sun.COM  *
14769484Sgarrett.damore@Sun.COM  * Description:
14779484Sgarrett.damore@Sun.COM  *	Set the play gain.
14789484Sgarrett.damore@Sun.COM  *
14799484Sgarrett.damore@Sun.COM  * Arguments:
14809484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
14819484Sgarrett.damore@Sun.COM  *	uint64_t	val		The gain to set (both left and right)
14829484Sgarrett.damore@Sun.COM  *
14839484Sgarrett.damore@Sun.COM  * Returns:
14849484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been set
14859484Sgarrett.damore@Sun.COM  */
14869484Sgarrett.damore@Sun.COM static int
audiocs_set_ogain(void * arg,uint64_t val)14879484Sgarrett.damore@Sun.COM audiocs_set_ogain(void *arg, uint64_t val)
14889484Sgarrett.damore@Sun.COM {
14899484Sgarrett.damore@Sun.COM 	CS_ctrl_t		*cc = arg;
14909484Sgarrett.damore@Sun.COM 	CS_state_t		*state = cc->cc_state;
14919484Sgarrett.damore@Sun.COM 
14929484Sgarrett.damore@Sun.COM 	if ((val & ~0xffff) ||
14939484Sgarrett.damore@Sun.COM 	    ((val & 0xff) > 100) ||
14949484Sgarrett.damore@Sun.COM 	    (((val & 0xff00) >> 8) > 100))
14959484Sgarrett.damore@Sun.COM 		return (EINVAL);
14969484Sgarrett.damore@Sun.COM 
14979484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
14989484Sgarrett.damore@Sun.COM 	cc->cc_val = val;
14999484Sgarrett.damore@Sun.COM 	audiocs_configure_output(state);
15009484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
15019484Sgarrett.damore@Sun.COM 	return (0);
15029484Sgarrett.damore@Sun.COM }
15039484Sgarrett.damore@Sun.COM 
15049484Sgarrett.damore@Sun.COM /*
15059484Sgarrett.damore@Sun.COM  * audiocs_set_micboost()
15069484Sgarrett.damore@Sun.COM  *
15079484Sgarrett.damore@Sun.COM  * Description:
15089484Sgarrett.damore@Sun.COM  *	Set the 20 dB microphone boost.
15099484Sgarrett.damore@Sun.COM  *
15109484Sgarrett.damore@Sun.COM  * Arguments:
15119484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
15129484Sgarrett.damore@Sun.COM  *	uint64_t	val		The 1 to enable, 0 to disable.
15139484Sgarrett.damore@Sun.COM  *
15149484Sgarrett.damore@Sun.COM  * Returns:
15159484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been set
15169484Sgarrett.damore@Sun.COM  */
15179484Sgarrett.damore@Sun.COM static int
audiocs_set_micboost(void * arg,uint64_t val)15189484Sgarrett.damore@Sun.COM audiocs_set_micboost(void *arg, uint64_t val)
15199484Sgarrett.damore@Sun.COM {
15209484Sgarrett.damore@Sun.COM 	CS_ctrl_t	*cc = arg;
15219484Sgarrett.damore@Sun.COM 	CS_state_t	*state = cc->cc_state;
15229484Sgarrett.damore@Sun.COM 
15239484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
15249484Sgarrett.damore@Sun.COM 	cc->cc_val = val ? B_TRUE : B_FALSE;
15259484Sgarrett.damore@Sun.COM 	audiocs_configure_input(state);
15269484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
15279484Sgarrett.damore@Sun.COM 	return (0);
15289484Sgarrett.damore@Sun.COM }
15299484Sgarrett.damore@Sun.COM 
15309484Sgarrett.damore@Sun.COM /*
15319484Sgarrett.damore@Sun.COM  * audiocs_set_igain()
15329484Sgarrett.damore@Sun.COM  *
15339484Sgarrett.damore@Sun.COM  * Description:
15349484Sgarrett.damore@Sun.COM  *	Set the record gain.
15359484Sgarrett.damore@Sun.COM  *
15369484Sgarrett.damore@Sun.COM  * Arguments:
15379484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
15389484Sgarrett.damore@Sun.COM  *	uint64_t	val		The gain to set (both left and right)
15399484Sgarrett.damore@Sun.COM  *
15409484Sgarrett.damore@Sun.COM  * Returns:
15419484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been set
15429484Sgarrett.damore@Sun.COM  */
15439484Sgarrett.damore@Sun.COM static int
audiocs_set_igain(void * arg,uint64_t val)15449484Sgarrett.damore@Sun.COM audiocs_set_igain(void *arg, uint64_t val)
15459484Sgarrett.damore@Sun.COM {
15469484Sgarrett.damore@Sun.COM 	CS_ctrl_t	*cc = arg;
15479484Sgarrett.damore@Sun.COM 	CS_state_t	*state = cc->cc_state;
15489484Sgarrett.damore@Sun.COM 
15499484Sgarrett.damore@Sun.COM 	if ((val & ~0xffff) ||
15509484Sgarrett.damore@Sun.COM 	    ((val & 0xff) > 100) ||
15519484Sgarrett.damore@Sun.COM 	    (((val & 0xff00) >> 8) > 100))
15529484Sgarrett.damore@Sun.COM 		return (EINVAL);
15539484Sgarrett.damore@Sun.COM 
15549484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
15559484Sgarrett.damore@Sun.COM 	cc->cc_val = val;
15569484Sgarrett.damore@Sun.COM 	audiocs_configure_input(state);
15579484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
15589484Sgarrett.damore@Sun.COM 
15599484Sgarrett.damore@Sun.COM 	return (0);
15609484Sgarrett.damore@Sun.COM }
15619484Sgarrett.damore@Sun.COM 
15629484Sgarrett.damore@Sun.COM /*
15639484Sgarrett.damore@Sun.COM  * audiocs_set_inputs()
15649484Sgarrett.damore@Sun.COM  *
15659484Sgarrett.damore@Sun.COM  * Description:
15669484Sgarrett.damore@Sun.COM  *	Set the input ports.
15679484Sgarrett.damore@Sun.COM  *
15689484Sgarrett.damore@Sun.COM  * Arguments:
15699484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
15709484Sgarrett.damore@Sun.COM  *	uint64_t	val		The mask of output ports.
15719484Sgarrett.damore@Sun.COM  *
15729484Sgarrett.damore@Sun.COM  * Returns:
15739484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been set
15749484Sgarrett.damore@Sun.COM  */
15759484Sgarrett.damore@Sun.COM static int
audiocs_set_inputs(void * arg,uint64_t val)15769484Sgarrett.damore@Sun.COM audiocs_set_inputs(void *arg, uint64_t val)
15779484Sgarrett.damore@Sun.COM {
15789484Sgarrett.damore@Sun.COM 	CS_ctrl_t	*cc = arg;
15799484Sgarrett.damore@Sun.COM 	CS_state_t	*state = cc->cc_state;
15809484Sgarrett.damore@Sun.COM 
15819484Sgarrett.damore@Sun.COM 	if (val & ~(state->cs_imask))
15829484Sgarrett.damore@Sun.COM 		return (EINVAL);
15839484Sgarrett.damore@Sun.COM 
15849484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
15859484Sgarrett.damore@Sun.COM 	cc->cc_val = val;
15869484Sgarrett.damore@Sun.COM 	audiocs_configure_input(state);
15879484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
15889484Sgarrett.damore@Sun.COM 
15899484Sgarrett.damore@Sun.COM 	return (0);
15909484Sgarrett.damore@Sun.COM }
15919484Sgarrett.damore@Sun.COM 
15929484Sgarrett.damore@Sun.COM /*
15939484Sgarrett.damore@Sun.COM  * audiocs_set_outputs()
15949484Sgarrett.damore@Sun.COM  *
15959484Sgarrett.damore@Sun.COM  * Description:
15969484Sgarrett.damore@Sun.COM  *	Set the output ports.
15979484Sgarrett.damore@Sun.COM  *
15989484Sgarrett.damore@Sun.COM  * Arguments:
15999484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
16009484Sgarrett.damore@Sun.COM  *	uint64_t	val		The mask of input ports.
16019484Sgarrett.damore@Sun.COM  *
16029484Sgarrett.damore@Sun.COM  * Returns:
16039484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been set
16049484Sgarrett.damore@Sun.COM  */
16059484Sgarrett.damore@Sun.COM static int
audiocs_set_outputs(void * arg,uint64_t val)16069484Sgarrett.damore@Sun.COM audiocs_set_outputs(void *arg, uint64_t val)
16079484Sgarrett.damore@Sun.COM {
16089484Sgarrett.damore@Sun.COM 	CS_ctrl_t	*cc = arg;
16099484Sgarrett.damore@Sun.COM 	CS_state_t	*state = cc->cc_state;
16109484Sgarrett.damore@Sun.COM 
16119484Sgarrett.damore@Sun.COM 	if ((val & ~(state->cs_omod)) !=
16129484Sgarrett.damore@Sun.COM 	    (state->cs_omask & ~state->cs_omod))
16139484Sgarrett.damore@Sun.COM 		return (EINVAL);
16149484Sgarrett.damore@Sun.COM 
16159484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
16169484Sgarrett.damore@Sun.COM 	cc->cc_val = val;
16179484Sgarrett.damore@Sun.COM 	audiocs_configure_output(state);
16189484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
16199484Sgarrett.damore@Sun.COM 
16209484Sgarrett.damore@Sun.COM 	return (0);
16219484Sgarrett.damore@Sun.COM }
16229484Sgarrett.damore@Sun.COM 
16239484Sgarrett.damore@Sun.COM /*
16249484Sgarrett.damore@Sun.COM  * audiocs_set_mgain()
16259484Sgarrett.damore@Sun.COM  *
16269484Sgarrett.damore@Sun.COM  * Description:
16279484Sgarrett.damore@Sun.COM  *	Set the monitor gain.
16289484Sgarrett.damore@Sun.COM  *
16299484Sgarrett.damore@Sun.COM  * Arguments:
16309484Sgarrett.damore@Sun.COM  *	void		*arg		The device's state structure
16319484Sgarrett.damore@Sun.COM  *	uint64_t	val		The gain to set (monoaural).)
16329484Sgarrett.damore@Sun.COM  *
16339484Sgarrett.damore@Sun.COM  * Returns:
16349484Sgarrett.damore@Sun.COM  *	0		The Codec parameter has been set
16359484Sgarrett.damore@Sun.COM  */
16369484Sgarrett.damore@Sun.COM static int
audiocs_set_mgain(void * arg,uint64_t gain)16379484Sgarrett.damore@Sun.COM audiocs_set_mgain(void *arg, uint64_t gain)
16389484Sgarrett.damore@Sun.COM {
16399484Sgarrett.damore@Sun.COM 	CS_ctrl_t	*cc = arg;
16409484Sgarrett.damore@Sun.COM 	CS_state_t	*state = cc->cc_state;
16419484Sgarrett.damore@Sun.COM 
16429484Sgarrett.damore@Sun.COM 	if (gain > 100)
16439484Sgarrett.damore@Sun.COM 		return (EINVAL);
16449484Sgarrett.damore@Sun.COM 
16459484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
16469484Sgarrett.damore@Sun.COM 	cc->cc_val = gain;
16479484Sgarrett.damore@Sun.COM 	audiocs_configure_output(state);
16489484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
16499484Sgarrett.damore@Sun.COM 
16509484Sgarrett.damore@Sun.COM 	return (0);
16519484Sgarrett.damore@Sun.COM }
16529484Sgarrett.damore@Sun.COM 
16539484Sgarrett.damore@Sun.COM /*
16549484Sgarrett.damore@Sun.COM  * audiocs_open()
16559484Sgarrett.damore@Sun.COM  *
16569484Sgarrett.damore@Sun.COM  * Description:
16579484Sgarrett.damore@Sun.COM  *	Opens a DMA engine for use.
16589484Sgarrett.damore@Sun.COM  *
16599484Sgarrett.damore@Sun.COM  * Arguments:
16609484Sgarrett.damore@Sun.COM  *	void		*arg		The DMA engine to set up
16619484Sgarrett.damore@Sun.COM  *	int		flag		Open flags
1662*11936Sgdamore@opensolaris.org  *	unsigned	*nframesp	Receives number of frames
16639484Sgarrett.damore@Sun.COM  *	caddr_t		*bufp		Receives kernel data buffer
16649484Sgarrett.damore@Sun.COM  *
16659484Sgarrett.damore@Sun.COM  * Returns:
16669484Sgarrett.damore@Sun.COM  *	0	on success
16679484Sgarrett.damore@Sun.COM  *	errno	on failure
16689484Sgarrett.damore@Sun.COM  */
16699484Sgarrett.damore@Sun.COM static int
audiocs_open(void * arg,int flag,unsigned * nframesp,caddr_t * bufp)1670*11936Sgdamore@opensolaris.org audiocs_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp)
16719484Sgarrett.damore@Sun.COM {
16729484Sgarrett.damore@Sun.COM 	CS_engine_t	*eng = arg;
16739484Sgarrett.damore@Sun.COM 	CS_state_t	*state = eng->ce_state;
16749484Sgarrett.damore@Sun.COM 	dev_info_t	*dip = state->cs_dip;
16759484Sgarrett.damore@Sun.COM 
16769484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(flag));
16779484Sgarrett.damore@Sun.COM 
16789484Sgarrett.damore@Sun.COM 	(void) pm_busy_component(dip, CS4231_COMPONENT);
16799484Sgarrett.damore@Sun.COM 	if (pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON) ==
16809484Sgarrett.damore@Sun.COM 	    DDI_FAILURE) {
16819484Sgarrett.damore@Sun.COM 
16829484Sgarrett.damore@Sun.COM 		/* match the busy call above */
16839484Sgarrett.damore@Sun.COM 		(void) pm_idle_component(dip, CS4231_COMPONENT);
16849484Sgarrett.damore@Sun.COM 
16859484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev, "power up failed");
16869484Sgarrett.damore@Sun.COM 	}
16879484Sgarrett.damore@Sun.COM 
16889484Sgarrett.damore@Sun.COM 	eng->ce_count = 0;
1689*11936Sgdamore@opensolaris.org 	*nframesp = CS4231_NFRAMES;
16909484Sgarrett.damore@Sun.COM 	*bufp = eng->ce_kaddr;
16919484Sgarrett.damore@Sun.COM 
16929484Sgarrett.damore@Sun.COM 	return (0);
16939484Sgarrett.damore@Sun.COM }
16949484Sgarrett.damore@Sun.COM 
16959484Sgarrett.damore@Sun.COM /*
16969484Sgarrett.damore@Sun.COM  * audiocs_close()
16979484Sgarrett.damore@Sun.COM  *
16989484Sgarrett.damore@Sun.COM  * Description:
16999484Sgarrett.damore@Sun.COM  *	Closes an audio DMA engine that was previously opened.  Since
17009484Sgarrett.damore@Sun.COM  *	nobody is using it, we take this opportunity to possibly power
17019484Sgarrett.damore@Sun.COM  *	down the entire device.
17029484Sgarrett.damore@Sun.COM  *
17039484Sgarrett.damore@Sun.COM  * Arguments:
17049484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to shut down
17059484Sgarrett.damore@Sun.COM  */
17069484Sgarrett.damore@Sun.COM static void
audiocs_close(void * arg)17079484Sgarrett.damore@Sun.COM audiocs_close(void *arg)
17089484Sgarrett.damore@Sun.COM {
17099484Sgarrett.damore@Sun.COM 	CS_engine_t	*eng = arg;
17109484Sgarrett.damore@Sun.COM 	CS_state_t	*state = eng->ce_state;
17119484Sgarrett.damore@Sun.COM 
17129484Sgarrett.damore@Sun.COM 	(void) pm_idle_component(state->cs_dip, CS4231_COMPONENT);
17139484Sgarrett.damore@Sun.COM }
17149484Sgarrett.damore@Sun.COM 
17159484Sgarrett.damore@Sun.COM /*
17169484Sgarrett.damore@Sun.COM  * audiocs_stop()
17179484Sgarrett.damore@Sun.COM  *
17189484Sgarrett.damore@Sun.COM  * Description:
17199484Sgarrett.damore@Sun.COM  *	This is called by the framework to stop an engine that is
17209484Sgarrett.damore@Sun.COM  *	transferring data.
17219484Sgarrett.damore@Sun.COM  *
17229484Sgarrett.damore@Sun.COM  * Arguments:
17239484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to stop
17249484Sgarrett.damore@Sun.COM  */
17259484Sgarrett.damore@Sun.COM static void
audiocs_stop(void * arg)17269484Sgarrett.damore@Sun.COM audiocs_stop(void *arg)
17279484Sgarrett.damore@Sun.COM {
1728*11936Sgdamore@opensolaris.org 	CS_engine_t		*eng = arg;
1729*11936Sgdamore@opensolaris.org 	CS_state_t		*state = eng->ce_state;
1730*11936Sgdamore@opensolaris.org 	ddi_acc_handle_t	handle = CODEC_HANDLE;
17319484Sgarrett.damore@Sun.COM 
17329484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
1733*11936Sgdamore@opensolaris.org 	/*
1734*11936Sgdamore@opensolaris.org 	 * Stop the DMA engine.
1735*11936Sgdamore@opensolaris.org 	 */
1736*11936Sgdamore@opensolaris.org 	CS4231_DMA_STOP(state, eng);
1737*11936Sgdamore@opensolaris.org 
1738*11936Sgdamore@opensolaris.org 	/*
1739*11936Sgdamore@opensolaris.org 	 * Stop the codec.
1740*11936Sgdamore@opensolaris.org 	 */
1741*11936Sgdamore@opensolaris.org 	SELIDX(state, INTC_REG);
1742*11936Sgdamore@opensolaris.org 	ANDIDX(state, ~(eng->ce_codec_en), INTC_VALID_MASK);
17439484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
17449484Sgarrett.damore@Sun.COM }
17459484Sgarrett.damore@Sun.COM 
17469484Sgarrett.damore@Sun.COM /*
17479484Sgarrett.damore@Sun.COM  * audiocs_start()
17489484Sgarrett.damore@Sun.COM  *
17499484Sgarrett.damore@Sun.COM  * Description:
17509484Sgarrett.damore@Sun.COM  *	This is called by the framework to start an engine transferring data.
17519484Sgarrett.damore@Sun.COM  *
17529484Sgarrett.damore@Sun.COM  * Arguments:
17539484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to start
17549484Sgarrett.damore@Sun.COM  *
17559484Sgarrett.damore@Sun.COM  * Returns:
17569484Sgarrett.damore@Sun.COM  *	0 	on success, an errno otherwise
17579484Sgarrett.damore@Sun.COM  */
17589484Sgarrett.damore@Sun.COM static int
audiocs_start(void * arg)17599484Sgarrett.damore@Sun.COM audiocs_start(void *arg)
17609484Sgarrett.damore@Sun.COM {
1761*11936Sgdamore@opensolaris.org 	CS_engine_t		*eng = arg;
1762*11936Sgdamore@opensolaris.org 	CS_state_t		*state = eng->ce_state;
1763*11936Sgdamore@opensolaris.org 	ddi_acc_handle_t	handle = CODEC_HANDLE;
1764*11936Sgdamore@opensolaris.org 	uint8_t			mask;
1765*11936Sgdamore@opensolaris.org 	uint8_t			value;
1766*11936Sgdamore@opensolaris.org 	uint8_t			reg;
1767*11936Sgdamore@opensolaris.org 	int			rv;
17689484Sgarrett.damore@Sun.COM 
17699484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
1770*11936Sgdamore@opensolaris.org 
1771*11936Sgdamore@opensolaris.org 	if (eng->ce_num == CS4231_PLAY) {
1772*11936Sgdamore@opensolaris.org 		/* sample rate only set on play side */
1773*11936Sgdamore@opensolaris.org 		value = FS_48000 | PDF_STEREO | PDF_LINEAR16NE;
1774*11936Sgdamore@opensolaris.org 		reg = FSDF_REG;
1775*11936Sgdamore@opensolaris.org 		mask = FSDF_VALID_MASK;
1776*11936Sgdamore@opensolaris.org 	} else {
1777*11936Sgdamore@opensolaris.org 		value = CDF_STEREO | CDF_LINEAR16NE;
1778*11936Sgdamore@opensolaris.org 		reg = CDF_REG;
1779*11936Sgdamore@opensolaris.org 		mask = CDF_VALID_MASK;
17809484Sgarrett.damore@Sun.COM 	}
1781*11936Sgdamore@opensolaris.org 	eng->ce_curoff = 0;
1782*11936Sgdamore@opensolaris.org 	eng->ce_curidx = 0;
1783*11936Sgdamore@opensolaris.org 
1784*11936Sgdamore@opensolaris.org 	SELIDX(state, reg | IAR_MCE);
1785*11936Sgdamore@opensolaris.org 	PUTIDX(state, value, mask);
1786*11936Sgdamore@opensolaris.org 
1787*11936Sgdamore@opensolaris.org 	if (audiocs_poll_ready(state) != DDI_SUCCESS) {
1788*11936Sgdamore@opensolaris.org 		rv = EIO;
1789*11936Sgdamore@opensolaris.org 	} else if (CS4231_DMA_START(state, eng) != DDI_SUCCESS) {
1790*11936Sgdamore@opensolaris.org 		rv = EIO;
1791*11936Sgdamore@opensolaris.org 	} else {
1792*11936Sgdamore@opensolaris.org 		/*
1793*11936Sgdamore@opensolaris.org 		 * Start the codec.
1794*11936Sgdamore@opensolaris.org 		 */
1795*11936Sgdamore@opensolaris.org 		SELIDX(state, INTC_REG);
1796*11936Sgdamore@opensolaris.org 		ORIDX(state, eng->ce_codec_en, INTC_VALID_MASK);
1797*11936Sgdamore@opensolaris.org 		rv = 0;
1798*11936Sgdamore@opensolaris.org 	}
1799*11936Sgdamore@opensolaris.org 
18009484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
18019484Sgarrett.damore@Sun.COM 	return (rv);
18029484Sgarrett.damore@Sun.COM }
18039484Sgarrett.damore@Sun.COM 
18049484Sgarrett.damore@Sun.COM /*
18059484Sgarrett.damore@Sun.COM  * audiocs_format()
18069484Sgarrett.damore@Sun.COM  *
18079484Sgarrett.damore@Sun.COM  * Description:
18089484Sgarrett.damore@Sun.COM  *	Called by the framework to query the format of the device.
18099484Sgarrett.damore@Sun.COM  *
18109484Sgarrett.damore@Sun.COM  * Arguments:
18119484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
18129484Sgarrett.damore@Sun.COM  *
18139484Sgarrett.damore@Sun.COM  * Returns:
18149484Sgarrett.damore@Sun.COM  *	AUDIO_FORMAT_S16_NE
18159484Sgarrett.damore@Sun.COM  */
18169484Sgarrett.damore@Sun.COM static int
audiocs_format(void * arg)18179484Sgarrett.damore@Sun.COM audiocs_format(void *arg)
18189484Sgarrett.damore@Sun.COM {
18199484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
18209484Sgarrett.damore@Sun.COM 
18219484Sgarrett.damore@Sun.COM 	return (AUDIO_FORMAT_S16_NE);
18229484Sgarrett.damore@Sun.COM }
18239484Sgarrett.damore@Sun.COM 
18249484Sgarrett.damore@Sun.COM /*
18259484Sgarrett.damore@Sun.COM  * audiocs_channels()
18269484Sgarrett.damore@Sun.COM  *
18279484Sgarrett.damore@Sun.COM  * Description:
18289484Sgarrett.damore@Sun.COM  *	Called by the framework to query the channels of the device.
18299484Sgarrett.damore@Sun.COM  *
18309484Sgarrett.damore@Sun.COM  * Arguments:
18319484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
18329484Sgarrett.damore@Sun.COM  *
18339484Sgarrett.damore@Sun.COM  * Returns:
18349484Sgarrett.damore@Sun.COM  *	2 (stereo)
18359484Sgarrett.damore@Sun.COM  */
18369484Sgarrett.damore@Sun.COM static int
audiocs_channels(void * arg)18379484Sgarrett.damore@Sun.COM audiocs_channels(void *arg)
18389484Sgarrett.damore@Sun.COM {
18399484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
18409484Sgarrett.damore@Sun.COM 
18419484Sgarrett.damore@Sun.COM 	return (2);
18429484Sgarrett.damore@Sun.COM }
18439484Sgarrett.damore@Sun.COM 
18449484Sgarrett.damore@Sun.COM /*
18459484Sgarrett.damore@Sun.COM  * audiocs_rates()
18469484Sgarrett.damore@Sun.COM  *
18479484Sgarrett.damore@Sun.COM  * Description:
18489484Sgarrett.damore@Sun.COM  *	Called by the framework to query the sample rate of the device.
18499484Sgarrett.damore@Sun.COM  *
18509484Sgarrett.damore@Sun.COM  * Arguments:
18519484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
18529484Sgarrett.damore@Sun.COM  *
18539484Sgarrett.damore@Sun.COM  * Returns:
18549484Sgarrett.damore@Sun.COM  *	48000
18559484Sgarrett.damore@Sun.COM  */
18569484Sgarrett.damore@Sun.COM static int
audiocs_rate(void * arg)18579484Sgarrett.damore@Sun.COM audiocs_rate(void *arg)
18589484Sgarrett.damore@Sun.COM {
18599484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(arg));
18609484Sgarrett.damore@Sun.COM 
18619484Sgarrett.damore@Sun.COM 	return (48000);
18629484Sgarrett.damore@Sun.COM }
18639484Sgarrett.damore@Sun.COM 
18649484Sgarrett.damore@Sun.COM /*
18659484Sgarrett.damore@Sun.COM  * audiocs_count()
18669484Sgarrett.damore@Sun.COM  *
18679484Sgarrett.damore@Sun.COM  * Description:
18689484Sgarrett.damore@Sun.COM  *	This is called by the framework to get the engine's frame counter
18699484Sgarrett.damore@Sun.COM  *
18709484Sgarrett.damore@Sun.COM  * Arguments:
18719484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to query
18729484Sgarrett.damore@Sun.COM  *
18739484Sgarrett.damore@Sun.COM  * Returns:
18749484Sgarrett.damore@Sun.COM  *	frame count for current engine
18759484Sgarrett.damore@Sun.COM  */
18769484Sgarrett.damore@Sun.COM static uint64_t
audiocs_count(void * arg)18779484Sgarrett.damore@Sun.COM audiocs_count(void *arg)
18789484Sgarrett.damore@Sun.COM {
1879*11936Sgdamore@opensolaris.org 	CS_engine_t		*eng = arg;
1880*11936Sgdamore@opensolaris.org 	CS_state_t		*state = eng->ce_state;
1881*11936Sgdamore@opensolaris.org 	uint64_t		val;
1882*11936Sgdamore@opensolaris.org 	uint32_t		off;
18839484Sgarrett.damore@Sun.COM 
18849484Sgarrett.damore@Sun.COM 	mutex_enter(&state->cs_lock);
1885*11936Sgdamore@opensolaris.org 
1886*11936Sgdamore@opensolaris.org 	off = CS4231_DMA_ADDR(state, eng);
1887*11936Sgdamore@opensolaris.org 	ASSERT(off >= eng->ce_paddr);
1888*11936Sgdamore@opensolaris.org 	off -= eng->ce_paddr;
1889*11936Sgdamore@opensolaris.org 
1890*11936Sgdamore@opensolaris.org 	/*
1891*11936Sgdamore@opensolaris.org 	 * Every now and then, we get a value that is just a wee bit
1892*11936Sgdamore@opensolaris.org 	 * too large.  This seems to be a small value related to
1893*11936Sgdamore@opensolaris.org 	 * prefetch.  Rather than believe it, we just assume the last
1894*11936Sgdamore@opensolaris.org 	 * offset in the buffer.  This should allow us to handle
1895*11936Sgdamore@opensolaris.org 	 * wraps, but without inserting bogus sample counts.
1896*11936Sgdamore@opensolaris.org 	 */
1897*11936Sgdamore@opensolaris.org 	if (off >= CS4231_BUFSZ) {
1898*11936Sgdamore@opensolaris.org 		off = CS4231_BUFSZ - 4;
1899*11936Sgdamore@opensolaris.org 	}
1900*11936Sgdamore@opensolaris.org 
1901*11936Sgdamore@opensolaris.org 	off /= 4;
1902*11936Sgdamore@opensolaris.org 
1903*11936Sgdamore@opensolaris.org 	val = (off >= eng->ce_curoff) ?
1904*11936Sgdamore@opensolaris.org 	    off - eng->ce_curoff :
1905*11936Sgdamore@opensolaris.org 	    off + CS4231_NFRAMES - eng->ce_curoff;
1906*11936Sgdamore@opensolaris.org 
1907*11936Sgdamore@opensolaris.org 	eng->ce_count += val;
1908*11936Sgdamore@opensolaris.org 	eng->ce_curoff = off;
19099484Sgarrett.damore@Sun.COM 	val = eng->ce_count;
1910*11936Sgdamore@opensolaris.org 
1911*11936Sgdamore@opensolaris.org 	/* while here, possibly reload the next address */
1912*11936Sgdamore@opensolaris.org 	CS4231_DMA_RELOAD(state, eng);
19139484Sgarrett.damore@Sun.COM 	mutex_exit(&state->cs_lock);
19149484Sgarrett.damore@Sun.COM 
19159484Sgarrett.damore@Sun.COM 	return (val);
19169484Sgarrett.damore@Sun.COM }
19179484Sgarrett.damore@Sun.COM 
19189484Sgarrett.damore@Sun.COM /*
19199484Sgarrett.damore@Sun.COM  * audiocs_sync()
19209484Sgarrett.damore@Sun.COM  *
19219484Sgarrett.damore@Sun.COM  * Description:
19229484Sgarrett.damore@Sun.COM  *	This is called by the framework to synchronize DMA caches.
19239484Sgarrett.damore@Sun.COM  *
19249484Sgarrett.damore@Sun.COM  * Arguments:
19259484Sgarrett.damore@Sun.COM  *	void	*arg		The DMA engine to sync
19269484Sgarrett.damore@Sun.COM  */
19279484Sgarrett.damore@Sun.COM static void
audiocs_sync(void * arg,unsigned nframes)19289484Sgarrett.damore@Sun.COM audiocs_sync(void *arg, unsigned nframes)
19299484Sgarrett.damore@Sun.COM {
19309484Sgarrett.damore@Sun.COM 	CS_engine_t *eng = arg;
19319484Sgarrett.damore@Sun.COM 	_NOTE(ARGUNUSED(nframes));
19329484Sgarrett.damore@Sun.COM 
19339484Sgarrett.damore@Sun.COM 	(void) ddi_dma_sync(eng->ce_dmah, 0, 0, eng->ce_syncdir);
19349484Sgarrett.damore@Sun.COM }
19359484Sgarrett.damore@Sun.COM 
19369484Sgarrett.damore@Sun.COM /*
19379484Sgarrett.damore@Sun.COM  * audiocs_alloc_engine()
19389484Sgarrett.damore@Sun.COM  *
19399484Sgarrett.damore@Sun.COM  * Description:
19409484Sgarrett.damore@Sun.COM  *	Allocates the DMA handles and the memory for the DMA engine.
19419484Sgarrett.damore@Sun.COM  *
19429484Sgarrett.damore@Sun.COM  * Arguments:
19439484Sgarrett.damore@Sun.COM  *	CS_state_t	*dip	Pointer to the device's soft state
19449484Sgarrett.damore@Sun.COM  *	int		num	Engine number, CS4231_PLAY or CS4231_REC.
19459484Sgarrett.damore@Sun.COM  *
19469484Sgarrett.damore@Sun.COM  * Returns:
19479484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		Engine initialized.
19489484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		Engine not initialized.
19499484Sgarrett.damore@Sun.COM  */
19509484Sgarrett.damore@Sun.COM int
audiocs_alloc_engine(CS_state_t * state,int num)19519484Sgarrett.damore@Sun.COM audiocs_alloc_engine(CS_state_t *state, int num)
19529484Sgarrett.damore@Sun.COM {
19539484Sgarrett.damore@Sun.COM 	unsigned		caps;
19549484Sgarrett.damore@Sun.COM 	int			dir;
19559484Sgarrett.damore@Sun.COM 	int			rc;
19569484Sgarrett.damore@Sun.COM 	audio_dev_t		*adev;
19579484Sgarrett.damore@Sun.COM 	dev_info_t		*dip;
19589484Sgarrett.damore@Sun.COM 	CS_engine_t		*eng;
19599484Sgarrett.damore@Sun.COM 	uint_t			ccnt;
19609484Sgarrett.damore@Sun.COM 	ddi_dma_cookie_t	dmac;
1961*11936Sgdamore@opensolaris.org 	size_t			bufsz;
19629484Sgarrett.damore@Sun.COM 
19639484Sgarrett.damore@Sun.COM 	static ddi_device_acc_attr_t buf_attr = {
19649484Sgarrett.damore@Sun.COM 		DDI_DEVICE_ATTR_V0,
19659484Sgarrett.damore@Sun.COM 		DDI_NEVERSWAP_ACC,
19669484Sgarrett.damore@Sun.COM 		DDI_STRICTORDER_ACC
19679484Sgarrett.damore@Sun.COM 	};
19689484Sgarrett.damore@Sun.COM 
19699484Sgarrett.damore@Sun.COM 	adev = state->cs_adev;
19709484Sgarrett.damore@Sun.COM 	dip = state->cs_dip;
19719484Sgarrett.damore@Sun.COM 
19729484Sgarrett.damore@Sun.COM 	eng = kmem_zalloc(sizeof (*eng), KM_SLEEP);
19739484Sgarrett.damore@Sun.COM 	eng->ce_state = state;
19749484Sgarrett.damore@Sun.COM 	eng->ce_started = B_FALSE;
19759484Sgarrett.damore@Sun.COM 	eng->ce_num = num;
19769484Sgarrett.damore@Sun.COM 
19779484Sgarrett.damore@Sun.COM 	switch (num) {
19789484Sgarrett.damore@Sun.COM 	case CS4231_REC:
19799484Sgarrett.damore@Sun.COM 		dir = DDI_DMA_READ;
19809484Sgarrett.damore@Sun.COM 		caps = ENGINE_INPUT_CAP;
19819484Sgarrett.damore@Sun.COM 		eng->ce_syncdir = DDI_DMA_SYNC_FORKERNEL;
19829484Sgarrett.damore@Sun.COM 		eng->ce_codec_en = INTC_CEN;
19839484Sgarrett.damore@Sun.COM 		break;
19849484Sgarrett.damore@Sun.COM 	case CS4231_PLAY:
19859484Sgarrett.damore@Sun.COM 		dir = DDI_DMA_WRITE;
19869484Sgarrett.damore@Sun.COM 		caps = ENGINE_OUTPUT_CAP;
19879484Sgarrett.damore@Sun.COM 		eng->ce_syncdir = DDI_DMA_SYNC_FORDEV;
19889484Sgarrett.damore@Sun.COM 		eng->ce_codec_en = INTC_PEN;
19899484Sgarrett.damore@Sun.COM 		break;
19909484Sgarrett.damore@Sun.COM 	default:
19919484Sgarrett.damore@Sun.COM 		kmem_free(eng, sizeof (*eng));
19929484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "bad engine number (%d)!", num);
19939484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
19949484Sgarrett.damore@Sun.COM 	}
19959484Sgarrett.damore@Sun.COM 	state->cs_engines[num] = eng;
19969484Sgarrett.damore@Sun.COM 
19979484Sgarrett.damore@Sun.COM 	/* allocate dma handle */
19989484Sgarrett.damore@Sun.COM 	rc = ddi_dma_alloc_handle(dip, CS4231_DMA_ATTR(state), DDI_DMA_SLEEP,
19999484Sgarrett.damore@Sun.COM 	    NULL, &eng->ce_dmah);
20009484Sgarrett.damore@Sun.COM 	if (rc != DDI_SUCCESS) {
20019484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc);
20029484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
20039484Sgarrett.damore@Sun.COM 	}
20049484Sgarrett.damore@Sun.COM 	/* allocate DMA buffer */
2005*11936Sgdamore@opensolaris.org 	rc = ddi_dma_mem_alloc(eng->ce_dmah, CS4231_BUFSZ, &buf_attr,
20069484Sgarrett.damore@Sun.COM 	    DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &eng->ce_kaddr,
2007*11936Sgdamore@opensolaris.org 	    &bufsz, &eng->ce_acch);
20089484Sgarrett.damore@Sun.COM 	if (rc == DDI_FAILURE) {
20099484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "dma_mem_alloc failed");
20109484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
20119484Sgarrett.damore@Sun.COM 	}
20129484Sgarrett.damore@Sun.COM 
20139484Sgarrett.damore@Sun.COM 	/* bind DMA buffer */
20149484Sgarrett.damore@Sun.COM 	rc = ddi_dma_addr_bind_handle(eng->ce_dmah, NULL,
2015*11936Sgdamore@opensolaris.org 	    eng->ce_kaddr, CS4231_BUFSZ, dir | DDI_DMA_CONSISTENT,
20169484Sgarrett.damore@Sun.COM 	    DDI_DMA_SLEEP, NULL, &dmac, &ccnt);
20179484Sgarrett.damore@Sun.COM 	if ((rc != DDI_DMA_MAPPED) || (ccnt != 1)) {
20189484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev,
20199484Sgarrett.damore@Sun.COM 		    "ddi_dma_addr_bind_handle failed: %d", rc);
20209484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
20219484Sgarrett.damore@Sun.COM 	}
20229484Sgarrett.damore@Sun.COM 
2023*11936Sgdamore@opensolaris.org 	eng->ce_paddr = dmac.dmac_address;
20249484Sgarrett.damore@Sun.COM 
20259484Sgarrett.damore@Sun.COM 	eng->ce_engine = audio_engine_alloc(&audiocs_engine_ops, caps);
20269484Sgarrett.damore@Sun.COM 	if (eng->ce_engine == NULL) {
20279484Sgarrett.damore@Sun.COM 		audio_dev_warn(adev, "audio_engine_alloc failed");
20289484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
20299484Sgarrett.damore@Sun.COM 	}
20309484Sgarrett.damore@Sun.COM 
20319484Sgarrett.damore@Sun.COM 	audio_engine_set_private(eng->ce_engine, eng);
20329484Sgarrett.damore@Sun.COM 	audio_dev_add_engine(adev, eng->ce_engine);
20339484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
20349484Sgarrett.damore@Sun.COM }
20359484Sgarrett.damore@Sun.COM 
20369484Sgarrett.damore@Sun.COM /*
20379484Sgarrett.damore@Sun.COM  * audiocs_free_engine()
20389484Sgarrett.damore@Sun.COM  *
20399484Sgarrett.damore@Sun.COM  * Description:
20409484Sgarrett.damore@Sun.COM  *	This routine fress the engine and all associated resources.
20419484Sgarrett.damore@Sun.COM  *
20429484Sgarrett.damore@Sun.COM  * Arguments:
20439484Sgarrett.damore@Sun.COM  *	CS_engine_t	*eng	Engine to free.
20449484Sgarrett.damore@Sun.COM  */
20459484Sgarrett.damore@Sun.COM void
audiocs_free_engine(CS_engine_t * eng)20469484Sgarrett.damore@Sun.COM audiocs_free_engine(CS_engine_t *eng)
20479484Sgarrett.damore@Sun.COM {
20489484Sgarrett.damore@Sun.COM 	CS_state_t	*state = eng->ce_state;
20499484Sgarrett.damore@Sun.COM 	audio_dev_t	*adev = state->cs_adev;
20509484Sgarrett.damore@Sun.COM 
20519484Sgarrett.damore@Sun.COM 	if (eng == NULL)
20529484Sgarrett.damore@Sun.COM 		return;
20539484Sgarrett.damore@Sun.COM 	if (eng->ce_engine) {
20549484Sgarrett.damore@Sun.COM 		audio_dev_remove_engine(adev, eng->ce_engine);
20559484Sgarrett.damore@Sun.COM 		audio_engine_free(eng->ce_engine);
20569484Sgarrett.damore@Sun.COM 	}
2057*11936Sgdamore@opensolaris.org 	if (eng->ce_paddr) {
20589484Sgarrett.damore@Sun.COM 		(void) ddi_dma_unbind_handle(eng->ce_dmah);
20599484Sgarrett.damore@Sun.COM 	}
20609484Sgarrett.damore@Sun.COM 	if (eng->ce_acch) {
20619484Sgarrett.damore@Sun.COM 		ddi_dma_mem_free(&eng->ce_acch);
20629484Sgarrett.damore@Sun.COM 	}
20639484Sgarrett.damore@Sun.COM 	if (eng->ce_dmah) {
20649484Sgarrett.damore@Sun.COM 		ddi_dma_free_handle(&eng->ce_dmah);
20659484Sgarrett.damore@Sun.COM 	}
20669484Sgarrett.damore@Sun.COM 	kmem_free(eng, sizeof (*eng));
20679484Sgarrett.damore@Sun.COM }
20689484Sgarrett.damore@Sun.COM 
20699484Sgarrett.damore@Sun.COM /*
20709484Sgarrett.damore@Sun.COM  * audiocs_poll_ready()
20719484Sgarrett.damore@Sun.COM  *
20729484Sgarrett.damore@Sun.COM  * Description:
20739484Sgarrett.damore@Sun.COM  *	This routine waits for the Codec to complete its initialization
20749484Sgarrett.damore@Sun.COM  *	sequence and is done with its autocalibration.
20759484Sgarrett.damore@Sun.COM  *
20769484Sgarrett.damore@Sun.COM  *	Early versions of the Codec have a bug that can take as long as
20779484Sgarrett.damore@Sun.COM  *	15 seconds to complete its initialization. For these cases we
20789484Sgarrett.damore@Sun.COM  *	use a timeout mechanism so we don't keep the machine locked up.
20799484Sgarrett.damore@Sun.COM  *
20809484Sgarrett.damore@Sun.COM  * Arguments:
20819484Sgarrett.damore@Sun.COM  *	CS_state_t	*state	The device's state structure
20829484Sgarrett.damore@Sun.COM  *
20839484Sgarrett.damore@Sun.COM  * Returns:
20849484Sgarrett.damore@Sun.COM  *	DDI_SUCCESS		The Codec is ready to continue
20859484Sgarrett.damore@Sun.COM  *	DDI_FAILURE		The Codec isn't ready to continue
20869484Sgarrett.damore@Sun.COM  */
20879484Sgarrett.damore@Sun.COM int
audiocs_poll_ready(CS_state_t * state)20889484Sgarrett.damore@Sun.COM audiocs_poll_ready(CS_state_t *state)
20899484Sgarrett.damore@Sun.COM {
20909484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle = CODEC_HANDLE;
20919484Sgarrett.damore@Sun.COM 	int			x = 0;
20929484Sgarrett.damore@Sun.COM 	uint8_t			iar;
20939484Sgarrett.damore@Sun.COM 	uint8_t			idr;
20949484Sgarrett.damore@Sun.COM 
20959484Sgarrett.damore@Sun.COM 	ASSERT(state->cs_regs != NULL);
20969484Sgarrett.damore@Sun.COM 	ASSERT(handle != NULL);
20979484Sgarrett.damore@Sun.COM 
20989484Sgarrett.damore@Sun.COM 	/* wait for the chip to initialize itself */
20999484Sgarrett.damore@Sun.COM 	iar = ddi_get8(handle, &CS4231_IAR);
21009484Sgarrett.damore@Sun.COM 
21019484Sgarrett.damore@Sun.COM 	while ((iar & IAR_INIT) && x++ < CS4231_TIMEOUT) {
21029484Sgarrett.damore@Sun.COM 		drv_usecwait(50);
21039484Sgarrett.damore@Sun.COM 		iar = ddi_get8(handle, &CS4231_IAR);
21049484Sgarrett.damore@Sun.COM 	}
21059484Sgarrett.damore@Sun.COM 
21069484Sgarrett.damore@Sun.COM 	if (x >= CS4231_TIMEOUT) {
21079484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
21089484Sgarrett.damore@Sun.COM 	}
21099484Sgarrett.damore@Sun.COM 
21109484Sgarrett.damore@Sun.COM 	x = 0;
21119484Sgarrett.damore@Sun.COM 
21129484Sgarrett.damore@Sun.COM 	/*
21139484Sgarrett.damore@Sun.COM 	 * Now wait for the chip to complete its autocalibration.
21149484Sgarrett.damore@Sun.COM 	 * Set the test register.
21159484Sgarrett.damore@Sun.COM 	 */
21169484Sgarrett.damore@Sun.COM 	SELIDX(state, ESI_REG);
21179484Sgarrett.damore@Sun.COM 
21189484Sgarrett.damore@Sun.COM 	idr = ddi_get8(handle, &CS4231_IDR);
21199484Sgarrett.damore@Sun.COM 
21209484Sgarrett.damore@Sun.COM 	while ((idr & ESI_ACI) && x++ < CS4231_TIMEOUT) {
21219484Sgarrett.damore@Sun.COM 		drv_usecwait(50);
21229484Sgarrett.damore@Sun.COM 		idr = ddi_get8(handle, &CS4231_IDR);
21239484Sgarrett.damore@Sun.COM 	}
21249484Sgarrett.damore@Sun.COM 
21259484Sgarrett.damore@Sun.COM 	if (x >= CS4231_TIMEOUT) {
21269484Sgarrett.damore@Sun.COM 		return (DDI_FAILURE);
21279484Sgarrett.damore@Sun.COM 	}
21289484Sgarrett.damore@Sun.COM 
21299484Sgarrett.damore@Sun.COM 
21309484Sgarrett.damore@Sun.COM 	return (DDI_SUCCESS);
21319484Sgarrett.damore@Sun.COM 
21329484Sgarrett.damore@Sun.COM }
21339484Sgarrett.damore@Sun.COM 
21349484Sgarrett.damore@Sun.COM /*
21359484Sgarrett.damore@Sun.COM  * audiocs_sel_index()
21369484Sgarrett.damore@Sun.COM  *
21379484Sgarrett.damore@Sun.COM  * Description:
21389484Sgarrett.damore@Sun.COM  *	Select a cs4231 register. The cs4231 has a hardware bug where a
21399484Sgarrett.damore@Sun.COM  *	register is not always selected the first time. We try and try
21409484Sgarrett.damore@Sun.COM  *	again until the proper register is selected or we time out and
21419484Sgarrett.damore@Sun.COM  *	print an error message.
21429484Sgarrett.damore@Sun.COM  *
21439484Sgarrett.damore@Sun.COM  * Arguments:
21449484Sgarrett.damore@Sun.COM  *	audiohdl_t	ahandle		Handle to this device
21459484Sgarrett.damore@Sun.COM  *	ddi_acc_handle_t handle		A handle to the device's registers
21469484Sgarrett.damore@Sun.COM  *	uint8_t		addr		The register address to program
21479484Sgarrett.damore@Sun.COM  *	int		reg		The register to select
21489484Sgarrett.damore@Sun.COM  */
21499484Sgarrett.damore@Sun.COM void
21509484Sgarrett.damore@Sun.COM #ifdef	DEBUG
audiocs_sel_index(CS_state_t * state,uint8_t reg,int n)21519484Sgarrett.damore@Sun.COM audiocs_sel_index(CS_state_t *state, uint8_t reg, int n)
21529484Sgarrett.damore@Sun.COM #else
21539484Sgarrett.damore@Sun.COM audiocs_sel_index(CS_state_t *state, uint8_t reg)
21549484Sgarrett.damore@Sun.COM #endif
21559484Sgarrett.damore@Sun.COM {
21569484Sgarrett.damore@Sun.COM 	int			x;
21579484Sgarrett.damore@Sun.COM 	uint8_t			T;
21589484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle = CODEC_HANDLE;
21599484Sgarrett.damore@Sun.COM 	uint8_t			*addr = &CS4231_IAR;
21609484Sgarrett.damore@Sun.COM 
21619484Sgarrett.damore@Sun.COM 	for (x = 0; x < CS4231_RETRIES; x++) {
21629484Sgarrett.damore@Sun.COM 		ddi_put8(handle, addr, reg);
21639484Sgarrett.damore@Sun.COM 		T = ddi_get8(handle, addr);
21649484Sgarrett.damore@Sun.COM 		if (T == reg) {
21659484Sgarrett.damore@Sun.COM 			break;
21669484Sgarrett.damore@Sun.COM 		}
21679484Sgarrett.damore@Sun.COM 		drv_usecwait(1000);
21689484Sgarrett.damore@Sun.COM 	}
21699484Sgarrett.damore@Sun.COM 
21709484Sgarrett.damore@Sun.COM 	if (x == CS4231_RETRIES) {
21719484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev,
21729484Sgarrett.damore@Sun.COM #ifdef	DEBUG
21739484Sgarrett.damore@Sun.COM 		    "line %d: Couldn't select index (0x%02x 0x%02x)", n,
21749484Sgarrett.damore@Sun.COM #else
21759484Sgarrett.damore@Sun.COM 		    "Couldn't select index (0x%02x 0x%02x)",
21769484Sgarrett.damore@Sun.COM #endif
21779484Sgarrett.damore@Sun.COM 		    T, reg);
21789484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev,
21799484Sgarrett.damore@Sun.COM 		    "audio may not work correctly until it is stopped and "
21809484Sgarrett.damore@Sun.COM 		    "restarted");
21819484Sgarrett.damore@Sun.COM 	}
21829484Sgarrett.damore@Sun.COM }
21839484Sgarrett.damore@Sun.COM 
21849484Sgarrett.damore@Sun.COM /*
21859484Sgarrett.damore@Sun.COM  * audiocs_put_index()
21869484Sgarrett.damore@Sun.COM  *
21879484Sgarrett.damore@Sun.COM  * Description:
21889484Sgarrett.damore@Sun.COM  *	Program a cs4231 register. The cs4231 has a hardware bug where a
21899484Sgarrett.damore@Sun.COM  *	register is not programmed properly the first time. We program a value,
21909484Sgarrett.damore@Sun.COM  *	then immediately read back the value and reprogram if nescessary.
21919484Sgarrett.damore@Sun.COM  *	We do this until the register is properly programmed or we time out and
21929484Sgarrett.damore@Sun.COM  *	print an error message.
21939484Sgarrett.damore@Sun.COM  *
21949484Sgarrett.damore@Sun.COM  * Arguments:
21959484Sgarrett.damore@Sun.COM  *	CS_state_t	state		Handle to this device
21969484Sgarrett.damore@Sun.COM  *	uint8_t		mask		Mask to not set reserved register bits
21979484Sgarrett.damore@Sun.COM  *	int		val		The value to program
21989484Sgarrett.damore@Sun.COM  */
21999484Sgarrett.damore@Sun.COM void
22009484Sgarrett.damore@Sun.COM #ifdef DEBUG
audiocs_put_index(CS_state_t * state,uint8_t val,uint8_t mask,int n)22019484Sgarrett.damore@Sun.COM audiocs_put_index(CS_state_t *state, uint8_t val, uint8_t mask, int n)
22029484Sgarrett.damore@Sun.COM #else
22039484Sgarrett.damore@Sun.COM audiocs_put_index(CS_state_t *state, uint8_t val, uint8_t mask)
22049484Sgarrett.damore@Sun.COM #endif
22059484Sgarrett.damore@Sun.COM {
22069484Sgarrett.damore@Sun.COM 	int			x;
22079484Sgarrett.damore@Sun.COM 	uint8_t			T;
22089484Sgarrett.damore@Sun.COM 	ddi_acc_handle_t	handle = CODEC_HANDLE;
22099484Sgarrett.damore@Sun.COM 	uint8_t			*addr = &CS4231_IDR;
22109484Sgarrett.damore@Sun.COM 
22119484Sgarrett.damore@Sun.COM 	val &= mask;
22129484Sgarrett.damore@Sun.COM 
22139484Sgarrett.damore@Sun.COM 	for (x = 0; x < CS4231_RETRIES; x++) {
22149484Sgarrett.damore@Sun.COM 		ddi_put8(handle, addr, val);
22159484Sgarrett.damore@Sun.COM 		T = ddi_get8(handle, addr);
22169484Sgarrett.damore@Sun.COM 		if (T == val) {
22179484Sgarrett.damore@Sun.COM 			break;
22189484Sgarrett.damore@Sun.COM 		}
22199484Sgarrett.damore@Sun.COM 		drv_usecwait(1000);
22209484Sgarrett.damore@Sun.COM 	}
22219484Sgarrett.damore@Sun.COM 
22229484Sgarrett.damore@Sun.COM 	if (x == CS4231_RETRIES) {
22239484Sgarrett.damore@Sun.COM #ifdef DEBUG
22249484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev,
22259484Sgarrett.damore@Sun.COM 		    "line %d: Couldn't set value (0x%02x 0x%02x)", n, T, val);
22269484Sgarrett.damore@Sun.COM #else
22279484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev,
22289484Sgarrett.damore@Sun.COM 		    "Couldn't set value (0x%02x 0x%02x)", T, val);
22299484Sgarrett.damore@Sun.COM #endif
22309484Sgarrett.damore@Sun.COM 		audio_dev_warn(state->cs_adev,
22319484Sgarrett.damore@Sun.COM 		    "audio may not work correctly until it is stopped and "
22329484Sgarrett.damore@Sun.COM 		    "restarted");
22339484Sgarrett.damore@Sun.COM 	}
22349484Sgarrett.damore@Sun.COM }
2235