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