/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* audiocs Audio Driver
*
* This Audio Driver controls the Crystal CS4231 Codec used on many SPARC
* platforms. It does not support the CS4231 on Power PCs or x86 PCs. It
* does support two different DMA engines, the APC and EB2. The code for
* those DMA engines is split out and a well defined, but private, interface
* is used to control those DMA engines.
*
* For some reason setting the CS4231's registers doesn't always
* succeed. Therefore every time we set a register we always read it
* back to make sure it was set. If not we wait a little while and
* then try again. This is all taken care of in the routines
* audiocs_put_index() and audiocs_sel_index() and the macros ORIDX()
* and ANDIDX(). We don't worry about the status register because it
* is cleared by writing anything to it. So it doesn't matter what
* the value written is.
*
* This driver supports suspending and resuming. A suspend just stops playing
* and recording. The play DMA buffers end up getting thrown away, but when
* you shut down the machine there is a break in the audio anyway, so they
* won't be missed and it isn't worth the effort to save them. When we resume
* we always start playing and recording. If they aren't needed they get
* shut off by the mixer.
*
* Power management is supported by this driver.
*
*/
#include "audio_4231.h"
/*
* Module linkage routines for the kernel
*/
static int audiocs_ddi_power(dev_info_t *, int, int);
/*
* Entry point routine prototypes
*/
static int audiocs_open(void *, int, unsigned *, caddr_t *);
static void audiocs_close(void *);
static int audiocs_start(void *);
static void audiocs_stop(void *);
static int audiocs_format(void *);
static int audiocs_channels(void *);
static int audiocs_rate(void *);
static uint64_t audiocs_count(void *);
static void audiocs_sync(void *, unsigned);
/*
* Control callbacks.
*/
static int audiocs_get_value(void *, uint64_t *);
static int audiocs_set_ogain(void *, uint64_t);
static int audiocs_set_igain(void *, uint64_t);
static int audiocs_set_mgain(void *, uint64_t);
static int audiocs_set_inputs(void *, uint64_t);
static int audiocs_set_outputs(void *, uint64_t);
static int audiocs_set_micboost(void *, uint64_t);
/* Local Routines */
static int audiocs_resume(dev_info_t *);
static int audiocs_attach(dev_info_t *);
static int audiocs_detach(dev_info_t *);
static int audiocs_suspend(dev_info_t *);
static void audiocs_destroy(CS_state_t *);
static int audiocs_init_state(CS_state_t *);
static int audiocs_chip_init(CS_state_t *);
static int audiocs_alloc_engine(CS_state_t *, int);
static void audiocs_free_engine(CS_engine_t *);
static void audiocs_get_ports(CS_state_t *);
static void audiocs_configure_input(CS_state_t *);
static void audiocs_configure_output(CS_state_t *);
static void audiocs_free_ctrl(CS_ctrl_t *);
static int audiocs_add_controls(CS_state_t *);
static void audiocs_del_controls(CS_state_t *);
static void audiocs_power_up(CS_state_t *);
static void audiocs_power_down(CS_state_t *);
static int audiocs_poll_ready(CS_state_t *);
#ifdef DEBUG
#else
#endif
PUTIDX(s, \
NULL,
NULL,
NULL,
};
#define OUTPUT_SPEAKER 0
static const char *audiocs_outputs[] = {
};
#define INPUT_MIC 0
static const char *audiocs_inputs[] = {
};
/*
* Global variables, but viewable only by this file.
*/
/* play gain array, converts linear gain to 64 steps of log10 gain */
0x3f, 0x3e, 0x3d, 0x3c, 0x3b, /* [000] -> [004] */
0x3a, 0x39, 0x38, 0x37, 0x36, /* [005] -> [009] */
0x35, 0x34, 0x33, 0x32, 0x31, /* [010] -> [014] */
0x30, 0x2f, 0x2e, 0x2d, 0x2c, /* [015] -> [019] */
0x2b, 0x2a, 0x29, 0x29, 0x28, /* [020] -> [024] */
0x28, 0x27, 0x27, 0x26, 0x26, /* [025] -> [029] */
0x25, 0x25, 0x24, 0x24, 0x23, /* [030] -> [034] */
0x23, 0x22, 0x22, 0x21, 0x21, /* [035] -> [039] */
0x20, 0x20, 0x1f, 0x1f, 0x1f, /* [040] -> [044] */
0x1e, 0x1e, 0x1e, 0x1d, 0x1d, /* [045] -> [049] */
0x1d, 0x1c, 0x1c, 0x1c, 0x1b, /* [050] -> [054] */
0x1b, 0x1b, 0x1a, 0x1a, 0x1a, /* [055] -> [059] */
0x1a, 0x19, 0x19, 0x19, 0x19, /* [060] -> [064] */
0x18, 0x18, 0x18, 0x18, 0x17, /* [065] -> [069] */
0x17, 0x17, 0x17, 0x16, 0x16, /* [070] -> [074] */
0x16, 0x16, 0x16, 0x15, 0x15, /* [075] -> [079] */
0x15, 0x15, 0x15, 0x14, 0x14, /* [080] -> [084] */
0x14, 0x14, 0x14, 0x13, 0x13, /* [085] -> [089] */
0x13, 0x13, 0x13, 0x12, 0x12, /* [090] -> [094] */
0x12, 0x12, 0x12, 0x12, 0x11, /* [095] -> [099] */
0x11, 0x11, 0x11, 0x11, 0x11, /* [100] -> [104] */
0x10, 0x10, 0x10, 0x10, 0x10, /* [105] -> [109] */
0x10, 0x0f, 0x0f, 0x0f, 0x0f, /* [110] -> [114] */
0x0f, 0x0f, 0x0e, 0x0e, 0x0e, /* [114] -> [119] */
0x0e, 0x0e, 0x0e, 0x0e, 0x0d, /* [120] -> [124] */
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, /* [125] -> [129] */
0x0d, 0x0c, 0x0c, 0x0c, 0x0c, /* [130] -> [134] */
0x0c, 0x0c, 0x0c, 0x0b, 0x0b, /* [135] -> [139] */
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, /* [140] -> [144] */
0x0b, 0x0a, 0x0a, 0x0a, 0x0a, /* [145] -> [149] */
0x0a, 0x0a, 0x0a, 0x0a, 0x09, /* [150] -> [154] */
0x09, 0x09, 0x09, 0x09, 0x09, /* [155] -> [159] */
0x09, 0x09, 0x08, 0x08, 0x08, /* [160] -> [164] */
0x08, 0x08, 0x08, 0x08, 0x08, /* [165] -> [169] */
0x08, 0x07, 0x07, 0x07, 0x07, /* [170] -> [174] */
0x07, 0x07, 0x07, 0x07, 0x07, /* [175] -> [179] */
0x06, 0x06, 0x06, 0x06, 0x06, /* [180] -> [184] */
0x06, 0x06, 0x06, 0x06, 0x05, /* [185] -> [189] */
0x05, 0x05, 0x05, 0x05, 0x05, /* [190] -> [194] */
0x05, 0x05, 0x05, 0x05, 0x04, /* [195] -> [199] */
0x04, 0x04, 0x04, 0x04, 0x04, /* [200] -> [204] */
0x04, 0x04, 0x04, 0x04, 0x03, /* [205] -> [209] */
0x03, 0x03, 0x03, 0x03, 0x03, /* [210] -> [214] */
0x03, 0x03, 0x03, 0x03, 0x03, /* [215] -> [219] */
0x02, 0x02, 0x02, 0x02, 0x02, /* [220] -> [224] */
0x02, 0x02, 0x02, 0x02, 0x02, /* [225] -> [229] */
0x02, 0x01, 0x01, 0x01, 0x01, /* [230] -> [234] */
0x01, 0x01, 0x01, 0x01, 0x01, /* [235] -> [239] */
0x01, 0x01, 0x01, 0x00, 0x00, /* [240] -> [244] */
0x00, 0x00, 0x00, 0x00, 0x00, /* [245] -> [249] */
0x00, 0x00, 0x00, 0x00, 0x00, /* [250] -> [254] */
0x00 /* [255] */
};
/*
* STREAMS Structures
*/
/*
* DDI Structures
*/
/* Device operations structure */
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe - not needed */
audiocs_ddi_attach, /* devo_attach */
audiocs_ddi_detach, /* devo_detach */
nodev, /* devo_reset */
NULL, /* devi_cb_ops */
NULL, /* devo_bus_ops */
audiocs_ddi_power, /* devo_power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
/* Linkage structure for loadable drivers */
&mod_driverops, /* drv_modops */
CS4231_MOD_NAME, /* drv_linkinfo */
&audiocs_dev_ops /* drv_dev_ops */
};
/* Module linkage structure */
MODREV_1, /* ml_rev */
(void *)&audiocs_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
/* ******* Loadable Module Configuration Entry Points ********************* */
/*
* _init()
*
* Description:
* Implements _init(9E).
*
* Returns:
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int rv;
}
return (rv);
}
/*
* _fini()
*
* Description:
* Implements _fini(9E).
*
* Returns:
* mod_remove() status, see mod_remove(9f)
*/
int
_fini(void)
{
int rv;
}
return (rv);
}
/*
* _info()
*
* Description:
* Implements _info(9E).
*
* Arguments:
* modinfo *modinfop Pointer to the opaque modinfo structure
*
* Returns:
* mod_info() status, see mod_info(9f)
*/
int
{
}
/* ******* Driver Entry Points ******************************************** */
/*
* audiocs_ddi_attach()
*
* Description:
* Implement attach(9e).
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_attach_cmd_t cmd Attach command
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
switch (cmd) {
case DDI_ATTACH:
return (audiocs_attach(dip));
case DDI_RESUME:
return (audiocs_resume(dip));
default:
return (DDI_FAILURE);
}
}
/*
* audiocs_ddi_detach()
*
* Description:
* Implement detach(9e).
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_detach_cmd_t cmd Detach command
*
* Returns:
* DDI_SUCCESS Success.
* DDI_FAILURE Failure.
*/
static int
{
switch (cmd) {
case DDI_DETACH:
return (audiocs_detach(dip));
case DDI_SUSPEND:
return (audiocs_suspend(dip));
default:
return (DDI_FAILURE);
}
}
/*
* audiocs_ddi_power()
*
* Description:
* Implements power(9E).
*
* Arguments:
* def_info_t *dip Ptr to the device's dev_info structure
* int level The power level for the component
*
* Returns:
* DDI_SUCCESS Power level changed, we always succeed
*/
static int
{
if (component != CS4231_COMPONENT)
return (DDI_FAILURE);
/* get the state structure */
/* make sure we have some work to do */
/*
* with power anyway.
*/
if (!state->cs_suspended) {
/* check the level change to see what we need to do */
/* power down and save the state */
/* power up */
}
}
return (DDI_SUCCESS);
}
/* ******* Local Routines *************************************************** */
static void
{
return;
for (int i = CS4231_PLAY; i <= CS4231_REC; i++) {
}
}
/* unmap the registers */
/* destroy the state mutex */
}
/*
* audiocs_attach()
*
* Description:
* Attach an instance of the CS4231 driver. This routine does the device
* dependent attach tasks. When it is complete it calls
* audio_dev_register() to register with the framework.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
/* allocate the state structure */
/* now fill it in, initialize the state mutexs first */
/*
* audio state initialization... should always succeed,
* framework will message failure.
*/
goto error;
}
/* initialize the audio state structures */
goto error;
}
/* initialize the audio chip */
goto error;
}
/* chip init will have powered us up */
/* finally register with framework to kick everything off */
}
/* everything worked out, so report the device */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* audiocs_resume()
*
* Description:
* Resume a suspended device instance.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
/* we've already allocated the state structure so get ptr */
/* mark the Codec busy -- this should keep power(9e) away */
/* power it up */
/* initialize the audio chip */
return (DDI_FAILURE);
}
/*
* We have already powered up the chip, but this alerts the
* framework to the fact.
*/
return (DDI_SUCCESS);
}
/*
* audiocs_detach()
*
* Description:
* Detach an instance of the CS4231 driver.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was detached
* DDI_FAILURE The driver couldn't be detached (busy)
*/
static int
{
/* get the state structure */
/* don't detach if still in use */
return (DDI_FAILURE);
}
if (state->cs_powered) {
/*
* Make sure the Codec and DMA engine are off.
*/
/* make sure the DMA engine isn't going to do anything */
/*
* power down the device, no reason to waste power without
* a driver
*/
}
return (DDI_SUCCESS);
}
/*
* audiocs_suspend()
*
* Description:
* Suspend an instance of the CS4231 driver.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was detached
* DDI_FAILURE The driver couldn't be detached
*/
static int
{
/* get the state structure */
if (state->cs_powered) {
/* now we can power down the Codec */
}
return (DDI_SUCCESS);
}
/*
* audiocs_alloc_ctrl
*
* Description:
* Allocates a control structure for the audio mixer.
*
* Arguments:
* CS_state_t *state Device soft state.
* uint32_t num Control number to allocate.
* uint64_t val Initial value.
*
* Returns:
* Pointer to newly allocated CS_ctrl_t structure.
*/
static CS_ctrl_t *
{
switch (num) {
case CTL_VOLUME:
desc.acd_minvalue = 0;
break;
case CTL_IGAIN:
desc.acd_minvalue = 0;
break;
case CTL_MGAIN:
desc.acd_minvalue = 0;
break;
case CTL_INPUTS:
for (int i = 0; audiocs_inputs[i]; i++) {
}
break;
case CTL_OUTPUTS:
for (int i = 0; audiocs_outputs[i]; i++) {
}
break;
case CTL_MICBOOST:
desc.acd_minvalue = 0;
break;
}
return (cc);
}
/*
* audiocs_free_ctrl
*
* Description:
* Frees a control and all resources associated with it.
*
* Arguments:
* CS_ctrl_t *cc Pointer to control structure.
*/
static void
{
return;
}
/*
* audiocs_add_controls
*
* Description:
* Allocates and registers all controls for this device.
*
* Arguments:
* CS_state_t *state Device soft state.
*
* Returns:
* DDI_SUCCESS All controls added and registered
* DDI_FAILURE At least one control was not added or registered.
*/
static int
{
"unable to allocate %s control", #ID); \
return (DDI_FAILURE); \
}
(1U << OUTPUT_SPEAKER));
return (DDI_SUCCESS);
}
/*
* audiocs_del_controls
*
* Description:
* Unregisters and frees all controls for this device.
*
* Arguments:
* CS_state_t *state Device soft state.
*/
void
{
}
/*
* audiocs_chip_init()
*
* Description:
* Power up the audio core, initialize the audio Codec, prepare the chip
* for use.
*
* Arguments:
* CS_state_t *state The device's state structure
*
* Returns:
* DDI_SUCCESS Chip initialized and ready to use
* DDI_FAILURE Chip not initialized and not ready
*/
static int
{
/* make sure we are powered up */
/* wait for the Codec before we continue */
return (DDI_FAILURE);
}
/* activate registers 16 -> 31 */
/* now figure out what version we have */
} else {
}
/* get rid of annoying popping by muting the output channels */
/* initialize aux input channels to known gain values & muted */
/* initialize aux input channels to known gain values & muted */
/* program the sample rate, play and capture must be the same */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/*
* Set up the Codec for playback and capture disabled, dual DMA, and
* playback and capture DMA.
*/
return (DDI_FAILURE);
}
/*
* Turn on the output level bit to be 2.8 Vpp. Also, don't go to 0 on
* underflow.
*/
/* turn on the high pass filter if Rev A */
} else {
}
/* clear the play and capture interrupt flags */
/* the play and record gains will be set by the audio mixer */
/* unmute the output */
/* unmute the mono speaker and mute mono in */
return (DDI_SUCCESS);
}
/*
* audiocs_init_state()
*
* Description:
* This routine initializes the audio driver's state structure and
* maps in the registers. This also includes reading the properties.
*
* CAUTION: This routine maps the registers and initializes a mutex.
* Failure cleanup is handled by cs4231_attach(). It is not
* handled locally by this routine.
*
* Arguments:
* CS_state_t *state The device's state structure
*
* Returns:
* DDI_SUCCESS State structure initialized
* DDI_FAILURE State structure not initialized
*/
static int
{
char *prop_str;
char *pm_comp[] = {
"NAME=audiocs audio device",
"0=off",
"1=on" };
/* set up the pm-components */
return (DDI_FAILURE);
}
/* figure out which DMA engine hardware we have */
} else {
}
} else {
}
/* cs_regs, cs_eb2_regs and cs_handles filled in later */
/* most of what's left is filled in when the registers are mapped */
/* Allocate engines, must be done before register mapping called */
return (DDI_FAILURE);
}
/* Map in the registers */
return (DDI_FAILURE);
}
/* Allocate and add controls, must be done *after* registers mapped */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audiocs_get_ports()
*
* Description:
* Get which audiocs h/w version we have and use this to
* determine the input and output ports as well whether or not
* the hardware has internal loopbacks or not. We also have three
* different ways for the properties to be specified, which we
* also need to worry about.
*
* Vers Platform(s) DMA eng. audio-module** loopback
* a SS-4+/SS-5+ apcdma no no
* b Ultra-1&2 apcdma no yes
* c positron apcdma no yes
* d PPC - retired
* e x86 - retired
* f tazmo eb2dma Perigee no
* g tazmo eb2dma Quark yes
* h darwin+ eb2dma no N/A
*
* Vers model~ aux1* aux2*
* a N/A N/A N/A
* b N/A N/A N/A
* c N/A N/A N/A
* d retired
* e retired
* f SUNW,CS4231f N/A N/A
* g SUNW,CS4231g N/A N/A
* h SUNW,CS4231h cdrom none
*
* * = Replaces internal-loopback for latest property type, can be
* set to "cdrom", "loopback", or "none".
*
* ** = For plugin audio modules only. Starting with darwin, this
* property is replaces by the model property.
*
* ~ = Replaces audio-module.
*
* + = Has the capability of having a cable run from the internal
* CD-ROM to the audio device.
*
* N/A = Not applicable, the property wasn't created for early
* platforms, or the property has been retired.
*
* NOTE: Older tazmo and quark machines don't have the model property.
*
* Arguments:
* CS_state_t *state The device's state structure
*/
static void
{
char *prop_str;
/* First we set the common ports, etc. */
(1U << OUTPUT_SPEAKER) |
(1U << OUTPUT_HEADPHONES) |
(1U << OUTPUT_LINEOUT);
(1U << INPUT_MIC) |
(1U << INPUT_LINEIN) |
(1U << INPUT_STEREOMIX);
/* now we try the new "model" property */
/* darwin */
/* quark audio module */
/*
* NB: This could do SUNVTS LOOPBACK, but we
* don't support it for now... owing to no
* support in framework.
*/
/* tazmo */
} else {
"unknown audio model: %s, some parts of "
"audio may not work correctly", prop_str);
}
} else { /* now try the older "audio-module" property */
switch (*prop_str) {
case 'Q': /* quark audio module */
/* See quark comment above about SunVTS */
break;
case 'P': /* tazmo */
break;
default:
"unknown audio module: %s, some "
"parts of audio may not work correctly",
prop_str);
break;
}
} else { /* now try heuristics, ;-( */
} else {
}
/*
* Again, we don't support SunVTS for these
* boards, although we potentially could.
*/
} else {
}
}
}
}
/*
* audiocs_power_up()
*
* Description:
* Power up the Codec and restore the codec's registers.
*
* NOTE: We don't worry about locking since the only routines
* that may call us are attach() and power() Both of
* which should be the only threads in the driver.
*
* Arguments:
* CS_state_t *state The device's state structure
*/
static void
{
int i;
/* turn on the Codec */
/* reset the DMA engine(s) */
(void) audiocs_poll_ready(state);
/*
* Reload the Codec's registers, the DMA engines will be
* taken care of when play and record start up again. But
* first enable registers 16 -> 31.
*/
for (i = 0; i < CS4231_REGS; i++) {
/* restore Codec registers */
(void) audiocs_poll_ready(state);
}
/* clear MCE bit */
}
/*
* audiocs_power_down()
*
* Description:
* Power down the Codec and save the codec's registers.
*
* NOTE: See the note in cs4231_power_up() about locking.
*
* Arguments:
* CS_state_t *state The device's state structure
*/
static void
{
int i;
/*
* We are powering down, so we don't need to do a thing with
* the DMA engines. However, we do need to save the Codec
* registers.
*/
for (i = 0; i < CS4231_REGS; i++) {
/* save Codec regs */
}
/* turn off the Codec */
} /* cs4231_power_down() */
/*
* audiocs_configure_input()
*
* Description:
* Configure input properties of the mixer (e.g. igain, ports).
*
* Arguments:
* CS_state_t *state The device's state structure
*/
static void
{
uint8_t l, r;
/* rescale these for our atten array */
/* we downshift by 4 bits -- igain only has 16 possible values */
/* NB: that we do not scale here! The SADA driver didn't do so. */
l = l >> 4;
r = r >> 4;
l |= LADCI_LMIC;
r |= RADCI_RMIC;
}
l |= LADCI_LLINE;
r |= RADCI_RLINE;
}
/* note that SunVTS also uses this */
l |= LADCI_LAUX1;
r |= RADCI_RAUX1;
}
l |= LADCI_LLOOP;
r |= RADCI_RLOOP;
}
if (micboost) {
l |= LADCI_LMGE;
r |= RADCI_RMGE;
}
}
/*
* audiocs_configure_output()
*
* Description:
* Configure output properties of the mixer (e.g. ogain, mgain).
*
* Arguments:
* CS_state_t *state The device's state structure
*/
static void
{
uint8_t l, r;
/* port selection */
} else {
}
} else {
}
} else {
}
/* monitor gain */
if (mgain == 0) {
/* disable loopbacks when gain == 0 */
} else {
/* we use cs4231_atten[] to linearize attenuation */
}
/* output gain */
if (l == 0) {
}
if (r == 0) {
}
/* rescale these for our atten array */
}
/*
* audiocs_get_value()
*
* Description:
* Get a control value
*
* Arguments:
* void *arg The device's state structure
* uint64_t *valp Pointer to store value.
*
* Returns:
* 0 The Codec parameter has been retrieved.
*/
static int
{
return (0);
}
/*
* audiocs_set_ogain()
*
* Description:
* Set the play gain.
*
* Arguments:
* void *arg The device's state structure
* uint64_t val The gain to set (both left and right)
*
* Returns:
* 0 The Codec parameter has been set
*/
static int
{
if ((val & ~0xffff) ||
return (EINVAL);
return (0);
}
/*
* audiocs_set_micboost()
*
* Description:
* Set the 20 dB microphone boost.
*
* Arguments:
* void *arg The device's state structure
* uint64_t val The 1 to enable, 0 to disable.
*
* Returns:
* 0 The Codec parameter has been set
*/
static int
{
return (0);
}
/*
* audiocs_set_igain()
*
* Description:
* Set the record gain.
*
* Arguments:
* void *arg The device's state structure
* uint64_t val The gain to set (both left and right)
*
* Returns:
* 0 The Codec parameter has been set
*/
static int
{
if ((val & ~0xffff) ||
return (EINVAL);
return (0);
}
/*
* audiocs_set_inputs()
*
* Description:
* Set the input ports.
*
* Arguments:
* void *arg The device's state structure
* uint64_t val The mask of output ports.
*
* Returns:
* 0 The Codec parameter has been set
*/
static int
{
return (EINVAL);
return (0);
}
/*
* audiocs_set_outputs()
*
* Description:
* Set the output ports.
*
* Arguments:
* void *arg The device's state structure
* uint64_t val The mask of input ports.
*
* Returns:
* 0 The Codec parameter has been set
*/
static int
{
return (EINVAL);
return (0);
}
/*
* audiocs_set_mgain()
*
* Description:
* Set the monitor gain.
*
* Arguments:
* void *arg The device's state structure
* uint64_t val The gain to set (monoaural).)
*
* Returns:
* 0 The Codec parameter has been set
*/
static int
{
if (gain > 100)
return (EINVAL);
return (0);
}
/*
* audiocs_open()
*
* Description:
* Opens a DMA engine for use.
*
* Arguments:
* void *arg The DMA engine to set up
* int flag Open flags
* unsigned *nframesp Receives number of frames
* caddr_t *bufp Receives kernel data buffer
*
* Returns:
* 0 on success
* errno on failure
*/
static int
{
DDI_FAILURE) {
/* match the busy call above */
}
return (0);
}
/*
* audiocs_close()
*
* Description:
* Closes an audio DMA engine that was previously opened. Since
* nobody is using it, we take this opportunity to possibly power
* down the entire device.
*
* Arguments:
* void *arg The DMA engine to shut down
*/
static void
{
}
/*
* audiocs_stop()
*
* Description:
* This is called by the framework to stop an engine that is
* transferring data.
*
* Arguments:
* void *arg The DMA engine to stop
*/
static void
{
/*
* Stop the DMA engine.
*/
/*
* Stop the codec.
*/
}
/*
* audiocs_start()
*
* Description:
* This is called by the framework to start an engine transferring data.
*
* Arguments:
* void *arg The DMA engine to start
*
* Returns:
* 0 on success, an errno otherwise
*/
static int
{
int rv;
/* sample rate only set on play side */
} else {
}
} else {
/*
* Start the codec.
*/
rv = 0;
}
return (rv);
}
/*
* audiocs_format()
*
* Description:
* Called by the framework to query the format of the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* AUDIO_FORMAT_S16_NE
*/
static int
{
return (AUDIO_FORMAT_S16_NE);
}
/*
* audiocs_channels()
*
* Description:
* Called by the framework to query the channels of the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* 2 (stereo)
*/
static int
{
return (2);
}
/*
* audiocs_rates()
*
* Description:
* Called by the framework to query the sample rate of the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* 48000
*/
static int
{
return (48000);
}
/*
* audiocs_count()
*
* Description:
* This is called by the framework to get the engine's frame counter
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* frame count for current engine
*/
static uint64_t
{
/*
* Every now and then, we get a value that is just a wee bit
* too large. This seems to be a small value related to
* prefetch. Rather than believe it, we just assume the last
* offset in the buffer. This should allow us to handle
* wraps, but without inserting bogus sample counts.
*/
if (off >= CS4231_BUFSZ) {
}
off /= 4;
/* while here, possibly reload the next address */
return (val);
}
/*
* audiocs_sync()
*
* Description:
* This is called by the framework to synchronize DMA caches.
*
* Arguments:
* void *arg The DMA engine to sync
*/
static void
{
}
/*
* audiocs_alloc_engine()
*
* Description:
* Allocates the DMA handles and the memory for the DMA engine.
*
* Arguments:
* CS_state_t *dip Pointer to the device's soft state
* int num Engine number, CS4231_PLAY or CS4231_REC.
*
* Returns:
* DDI_SUCCESS Engine initialized.
* DDI_FAILURE Engine not initialized.
*/
int
{
unsigned caps;
int dir;
int rc;
};
switch (num) {
case CS4231_REC:
dir = DDI_DMA_READ;
break;
case CS4231_PLAY:
dir = DDI_DMA_WRITE;
break;
default:
return (DDI_FAILURE);
}
/* allocate dma handle */
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* allocate DMA buffer */
if (rc == DDI_FAILURE) {
return (DDI_FAILURE);
}
/* bind DMA buffer */
"ddi_dma_addr_bind_handle failed: %d", rc);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audiocs_free_engine()
*
* Description:
* This routine fress the engine and all associated resources.
*
* Arguments:
* CS_engine_t *eng Engine to free.
*/
void
{
return;
}
}
}
}
}
/*
* audiocs_poll_ready()
*
* Description:
* This routine waits for the Codec to complete its initialization
* sequence and is done with its autocalibration.
*
* Early versions of the Codec have a bug that can take as long as
* 15 seconds to complete its initialization. For these cases we
* use a timeout mechanism so we don't keep the machine locked up.
*
* Arguments:
* CS_state_t *state The device's state structure
*
* Returns:
* DDI_SUCCESS The Codec is ready to continue
* DDI_FAILURE The Codec isn't ready to continue
*/
int
{
int x = 0;
/* wait for the chip to initialize itself */
drv_usecwait(50);
}
if (x >= CS4231_TIMEOUT) {
return (DDI_FAILURE);
}
x = 0;
/*
* Now wait for the chip to complete its autocalibration.
* Set the test register.
*/
drv_usecwait(50);
}
if (x >= CS4231_TIMEOUT) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audiocs_sel_index()
*
* Description:
* Select a cs4231 register. The cs4231 has a hardware bug where a
* register is not always selected the first time. We try and try
* again until the proper register is selected or we time out and
* print an error message.
*
* Arguments:
* audiohdl_t ahandle Handle to this device
* ddi_acc_handle_t handle A handle to the device's registers
* uint8_t addr The register address to program
* int reg The register to select
*/
void
#ifdef DEBUG
#else
#endif
{
int x;
uint8_t T;
for (x = 0; x < CS4231_RETRIES; x++) {
if (T == reg) {
break;
}
drv_usecwait(1000);
}
if (x == CS4231_RETRIES) {
#ifdef DEBUG
"line %d: Couldn't select index (0x%02x 0x%02x)", n,
#else
"Couldn't select index (0x%02x 0x%02x)",
#endif
T, reg);
"audio may not work correctly until it is stopped and "
"restarted");
}
}
/*
* audiocs_put_index()
*
* Description:
* Program a cs4231 register. The cs4231 has a hardware bug where a
* register is not programmed properly the first time. We program a value,
* then immediately read back the value and reprogram if nescessary.
* We do this until the register is properly programmed or we time out and
* print an error message.
*
* Arguments:
* CS_state_t state Handle to this device
* uint8_t mask Mask to not set reserved register bits
* int val The value to program
*/
void
#ifdef DEBUG
#else
#endif
{
int x;
uint8_t T;
for (x = 0; x < CS4231_RETRIES; x++) {
if (T == val) {
break;
}
drv_usecwait(1000);
}
if (x == CS4231_RETRIES) {
#ifdef DEBUG
"line %d: Couldn't set value (0x%02x 0x%02x)", n, T, val);
#else
"Couldn't set value (0x%02x 0x%02x)", T, val);
#endif
"audio may not work correctly until it is stopped and "
"restarted");
}
}