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