audio_4231.c revision 11936:54dc8a89ba0d
1254721Semaste/* 2254721Semaste * CDDL HEADER START 3254721Semaste * 4254721Semaste * The contents of this file are subject to the terms of the 5254721Semaste * Common Development and Distribution License (the "License"). 6254721Semaste * You may not use this file except in compliance with the License. 7254721Semaste * 8254721Semaste * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9254721Semaste * or http://www.opensolaris.org/os/licensing. 10254721Semaste * See the License for the specific language governing permissions 11254721Semaste * and limitations under the License. 12254721Semaste * 13254721Semaste * When distributing Covered Code, include this CDDL HEADER in each 14254721Semaste * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15254721Semaste * If applicable, add the following below this CDDL HEADER, with the 16254721Semaste * fields enclosed by brackets "[]" replaced with your own identifying 17254721Semaste * information: Portions Copyright [yyyy] [name of copyright owner] 18254721Semaste * 19254721Semaste * CDDL HEADER END 20254721Semaste */ 21254721Semaste/* 22254721Semaste * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 23254721Semaste * Use is subject to license terms. 24254721Semaste */ 25254721Semaste 26254721Semaste 27254721Semaste/* 28254721Semaste * audiocs Audio Driver 29254721Semaste * 30254721Semaste * This Audio Driver controls the Crystal CS4231 Codec used on many SPARC 31254721Semaste * platforms. It does not support the CS4231 on Power PCs or x86 PCs. It 32254721Semaste * does support two different DMA engines, the APC and EB2. The code for 33254721Semaste * those DMA engines is split out and a well defined, but private, interface 34254721Semaste * is used to control those DMA engines. 35254721Semaste * 36254721Semaste * For some reason setting the CS4231's registers doesn't always 37254721Semaste * succeed. Therefore every time we set a register we always read it 38254721Semaste * back to make sure it was set. If not we wait a little while and 39254721Semaste * then try again. This is all taken care of in the routines 40254721Semaste * audiocs_put_index() and audiocs_sel_index() and the macros ORIDX() 41254721Semaste * and ANDIDX(). We don't worry about the status register because it 42254721Semaste * is cleared by writing anything to it. So it doesn't matter what 43254721Semaste * the value written is. 44254721Semaste * 45254721Semaste * This driver supports suspending and resuming. A suspend just stops playing 46254721Semaste * and recording. The play DMA buffers end up getting thrown away, but when 47254721Semaste * you shut down the machine there is a break in the audio anyway, so they 48254721Semaste * won't be missed and it isn't worth the effort to save them. When we resume 49254721Semaste * we always start playing and recording. If they aren't needed they get 50254721Semaste * shut off by the mixer. 51254721Semaste * 52254721Semaste * Power management is supported by this driver. 53254721Semaste * 54254721Semaste * NOTE: This module depends on drv/audio being loaded first. 55254721Semaste */ 56254721Semaste 57254721Semaste#include <sys/modctl.h> 58254721Semaste#include <sys/kmem.h> 59254721Semaste#include <sys/stropts.h> 60254721Semaste#include <sys/ddi.h> 61254721Semaste#include <sys/sunddi.h> 62254721Semaste#include <sys/note.h> 63254721Semaste#include <sys/audio/audio_driver.h> 64254721Semaste#include "audio_4231.h" 65254721Semaste 66254721Semaste/* 67254721Semaste * Module linkage routines for the kernel 68254721Semaste */ 69254721Semastestatic int audiocs_ddi_attach(dev_info_t *, ddi_attach_cmd_t); 70254721Semastestatic int audiocs_ddi_detach(dev_info_t *, ddi_detach_cmd_t); 71254721Semastestatic int audiocs_ddi_power(dev_info_t *, int, int); 72254721Semaste 73254721Semaste/* 74254721Semaste * Entry point routine prototypes 75254721Semaste */ 76254721Semastestatic int audiocs_open(void *, int, unsigned *, caddr_t *); 77254721Semastestatic void audiocs_close(void *); 78254721Semastestatic int audiocs_start(void *); 79254721Semastestatic void audiocs_stop(void *); 80254721Semastestatic int audiocs_format(void *); 81254721Semastestatic int audiocs_channels(void *); 82254721Semastestatic int audiocs_rate(void *); 83254721Semastestatic uint64_t audiocs_count(void *); 84254721Semastestatic void audiocs_sync(void *, unsigned); 85254721Semaste 86254721Semaste/* 87254721Semaste * Control callbacks. 88254721Semaste */ 89254721Semastestatic int audiocs_get_value(void *, uint64_t *); 90254721Semastestatic int audiocs_set_ogain(void *, uint64_t); 91254721Semastestatic int audiocs_set_igain(void *, uint64_t); 92254721Semastestatic int audiocs_set_mgain(void *, uint64_t); 93254721Semastestatic int audiocs_set_inputs(void *, uint64_t); 94254721Semastestatic int audiocs_set_outputs(void *, uint64_t); 95254721Semastestatic int audiocs_set_micboost(void *, uint64_t); 96254721Semaste 97254721Semaste/* Local Routines */ 98254721Semastestatic int audiocs_resume(dev_info_t *); 99254721Semastestatic int audiocs_attach(dev_info_t *); 100254721Semastestatic int audiocs_detach(dev_info_t *); 101254721Semastestatic int audiocs_suspend(dev_info_t *); 102254721Semaste 103254721Semastestatic void audiocs_destroy(CS_state_t *); 104254721Semastestatic int audiocs_init_state(CS_state_t *); 105254721Semastestatic int audiocs_chip_init(CS_state_t *); 106254721Semastestatic int audiocs_alloc_engine(CS_state_t *, int); 107254721Semastestatic void audiocs_free_engine(CS_engine_t *); 108254721Semastestatic void audiocs_get_ports(CS_state_t *); 109254721Semastestatic void audiocs_configure_input(CS_state_t *); 110254721Semastestatic void audiocs_configure_output(CS_state_t *); 111254721Semastestatic CS_ctrl_t *audiocs_alloc_ctrl(CS_state_t *, uint32_t, uint64_t); 112254721Semastestatic void audiocs_free_ctrl(CS_ctrl_t *); 113254721Semastestatic int audiocs_add_controls(CS_state_t *); 114254721Semastestatic void audiocs_del_controls(CS_state_t *); 115254721Semastestatic void audiocs_power_up(CS_state_t *); 116254721Semastestatic void audiocs_power_down(CS_state_t *); 117254721Semastestatic int audiocs_poll_ready(CS_state_t *); 118254721Semaste#ifdef DEBUG 119254721Semastestatic void audiocs_put_index(CS_state_t *, uint8_t, uint8_t, int); 120254721Semastestatic void audiocs_sel_index(CS_state_t *, uint8_t, int); 121254721Semaste#define SELIDX(s, idx) audiocs_sel_index(s, idx, __LINE__) 122254721Semaste#define PUTIDX(s, val, mask) audiocs_put_index(s, val, mask, __LINE__) 123254721Semaste#else 124254721Semastestatic void audiocs_put_index(CS_state_t *, uint8_t, uint8_t); 125254721Semastestatic void audiocs_sel_index(CS_state_t *, uint8_t); 126254721Semaste#define SELIDX(s, idx) audiocs_sel_index(s, idx) 127254721Semaste#define PUTIDX(s, val, mask) audiocs_put_index(s, val, mask) 128254721Semaste#endif 129254721Semaste#define GETIDX(s) ddi_get8((handle), &CS4231_IDR) 130254721Semaste 131254721Semaste#define ORIDX(s, val, mask) \ 132254721Semaste PUTIDX(s, \ 133254721Semaste (ddi_get8((handle), &CS4231_IDR) | (uint8_t)(val)), \ 134254721Semaste (uint8_t)(mask)) 135254721Semaste 136254721Semaste#define ANDIDX(s, val, mask) \ 137254721Semaste PUTIDX(s, (ddi_get8((handle), &CS4231_IDR) & (uint8_t)(val)), \ 138254721Semaste (uint8_t)(mask)) 139254721Semaste 140254721Semastestatic audio_engine_ops_t audiocs_engine_ops = { 141254721Semaste AUDIO_ENGINE_VERSION, 142254721Semaste audiocs_open, 143254721Semaste audiocs_close, 144254721Semaste audiocs_start, 145254721Semaste audiocs_stop, 146254721Semaste audiocs_count, 147254721Semaste audiocs_format, 148254721Semaste audiocs_channels, 149254721Semaste audiocs_rate, 150254721Semaste audiocs_sync, 151254721Semaste NULL, 152254721Semaste NULL, 153254721Semaste NULL, 154254721Semaste}; 155254721Semaste 156254721Semaste#define OUTPUT_SPEAKER 0 157254721Semaste#define OUTPUT_HEADPHONES 1 158254721Semaste#define OUTPUT_LINEOUT 2 159254721Semaste 160254721Semastestatic const char *audiocs_outputs[] = { 161254721Semaste AUDIO_PORT_SPEAKER, 162254721Semaste AUDIO_PORT_HEADPHONES, 163254721Semaste AUDIO_PORT_LINEOUT, 164254721Semaste NULL 165254721Semaste}; 166254721Semaste 167254721Semaste#define INPUT_MIC 0 168254721Semaste#define INPUT_LINEIN 1 169254721Semaste#define INPUT_STEREOMIX 2 170254721Semaste#define INPUT_CD 3 171254721Semaste 172254721Semastestatic const char *audiocs_inputs[] = { 173254721Semaste AUDIO_PORT_MIC, 174254721Semaste AUDIO_PORT_LINEIN, 175254721Semaste AUDIO_PORT_STEREOMIX, 176254721Semaste AUDIO_PORT_CD, 177254721Semaste NULL 178254721Semaste}; 179254721Semaste 180254721Semaste/* 181254721Semaste * Global variables, but viewable only by this file. 182254721Semaste */ 183254721Semaste 184254721Semaste/* play gain array, converts linear gain to 64 steps of log10 gain */ 185254721Semastestatic uint8_t cs4231_atten[] = { 186254721Semaste 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, /* [000] -> [004] */ 187254721Semaste 0x3a, 0x39, 0x38, 0x37, 0x36, /* [005] -> [009] */ 188254721Semaste 0x35, 0x34, 0x33, 0x32, 0x31, /* [010] -> [014] */ 189254721Semaste 0x30, 0x2f, 0x2e, 0x2d, 0x2c, /* [015] -> [019] */ 190254721Semaste 0x2b, 0x2a, 0x29, 0x29, 0x28, /* [020] -> [024] */ 191254721Semaste 0x28, 0x27, 0x27, 0x26, 0x26, /* [025] -> [029] */ 192254721Semaste 0x25, 0x25, 0x24, 0x24, 0x23, /* [030] -> [034] */ 193254721Semaste 0x23, 0x22, 0x22, 0x21, 0x21, /* [035] -> [039] */ 194254721Semaste 0x20, 0x20, 0x1f, 0x1f, 0x1f, /* [040] -> [044] */ 195254721Semaste 0x1e, 0x1e, 0x1e, 0x1d, 0x1d, /* [045] -> [049] */ 196254721Semaste 0x1d, 0x1c, 0x1c, 0x1c, 0x1b, /* [050] -> [054] */ 197254721Semaste 0x1b, 0x1b, 0x1a, 0x1a, 0x1a, /* [055] -> [059] */ 198254721Semaste 0x1a, 0x19, 0x19, 0x19, 0x19, /* [060] -> [064] */ 199254721Semaste 0x18, 0x18, 0x18, 0x18, 0x17, /* [065] -> [069] */ 200254721Semaste 0x17, 0x17, 0x17, 0x16, 0x16, /* [070] -> [074] */ 201254721Semaste 0x16, 0x16, 0x16, 0x15, 0x15, /* [075] -> [079] */ 202254721Semaste 0x15, 0x15, 0x15, 0x14, 0x14, /* [080] -> [084] */ 203254721Semaste 0x14, 0x14, 0x14, 0x13, 0x13, /* [085] -> [089] */ 204254721Semaste 0x13, 0x13, 0x13, 0x12, 0x12, /* [090] -> [094] */ 205254721Semaste 0x12, 0x12, 0x12, 0x12, 0x11, /* [095] -> [099] */ 206254721Semaste 0x11, 0x11, 0x11, 0x11, 0x11, /* [100] -> [104] */ 207254721Semaste 0x10, 0x10, 0x10, 0x10, 0x10, /* [105] -> [109] */ 208254721Semaste 0x10, 0x0f, 0x0f, 0x0f, 0x0f, /* [110] -> [114] */ 209254721Semaste 0x0f, 0x0f, 0x0e, 0x0e, 0x0e, /* [114] -> [119] */ 210254721Semaste 0x0e, 0x0e, 0x0e, 0x0e, 0x0d, /* [120] -> [124] */ 211254721Semaste 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, /* [125] -> [129] */ 212254721Semaste 0x0d, 0x0c, 0x0c, 0x0c, 0x0c, /* [130] -> [134] */ 213254721Semaste 0x0c, 0x0c, 0x0c, 0x0b, 0x0b, /* [135] -> [139] */ 214254721Semaste 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, /* [140] -> [144] */ 215254721Semaste 0x0b, 0x0a, 0x0a, 0x0a, 0x0a, /* [145] -> [149] */ 216254721Semaste 0x0a, 0x0a, 0x0a, 0x0a, 0x09, /* [150] -> [154] */ 217254721Semaste 0x09, 0x09, 0x09, 0x09, 0x09, /* [155] -> [159] */ 218254721Semaste 0x09, 0x09, 0x08, 0x08, 0x08, /* [160] -> [164] */ 219254721Semaste 0x08, 0x08, 0x08, 0x08, 0x08, /* [165] -> [169] */ 220254721Semaste 0x08, 0x07, 0x07, 0x07, 0x07, /* [170] -> [174] */ 221254721Semaste 0x07, 0x07, 0x07, 0x07, 0x07, /* [175] -> [179] */ 222254721Semaste 0x06, 0x06, 0x06, 0x06, 0x06, /* [180] -> [184] */ 223254721Semaste 0x06, 0x06, 0x06, 0x06, 0x05, /* [185] -> [189] */ 224254721Semaste 0x05, 0x05, 0x05, 0x05, 0x05, /* [190] -> [194] */ 225254721Semaste 0x05, 0x05, 0x05, 0x05, 0x04, /* [195] -> [199] */ 226254721Semaste 0x04, 0x04, 0x04, 0x04, 0x04, /* [200] -> [204] */ 227254721Semaste 0x04, 0x04, 0x04, 0x04, 0x03, /* [205] -> [209] */ 228254721Semaste 0x03, 0x03, 0x03, 0x03, 0x03, /* [210] -> [214] */ 229254721Semaste 0x03, 0x03, 0x03, 0x03, 0x03, /* [215] -> [219] */ 230254721Semaste 0x02, 0x02, 0x02, 0x02, 0x02, /* [220] -> [224] */ 231254721Semaste 0x02, 0x02, 0x02, 0x02, 0x02, /* [225] -> [229] */ 232254721Semaste 0x02, 0x01, 0x01, 0x01, 0x01, /* [230] -> [234] */ 233254721Semaste 0x01, 0x01, 0x01, 0x01, 0x01, /* [235] -> [239] */ 234254721Semaste 0x01, 0x01, 0x01, 0x00, 0x00, /* [240] -> [244] */ 235254721Semaste 0x00, 0x00, 0x00, 0x00, 0x00, /* [245] -> [249] */ 236254721Semaste 0x00, 0x00, 0x00, 0x00, 0x00, /* [250] -> [254] */ 237254721Semaste 0x00 /* [255] */ 238254721Semaste}; 239254721Semaste 240254721Semaste/* 241254721Semaste * STREAMS Structures 242254721Semaste */ 243254721Semaste 244254721Semaste/* 245254721Semaste * DDI Structures 246254721Semaste */ 247254721Semaste 248254721Semaste/* Device operations structure */ 249254721Semastestatic struct dev_ops audiocs_dev_ops = { 250254721Semaste DEVO_REV, /* devo_rev */ 251254721Semaste 0, /* devo_refcnt */ 252254721Semaste NULL, /* devo_getinfo */ 253254721Semaste nulldev, /* devo_identify - obsolete */ 254254721Semaste nulldev, /* devo_probe - not needed */ 255254721Semaste audiocs_ddi_attach, /* devo_attach */ 256254721Semaste audiocs_ddi_detach, /* devo_detach */ 257254721Semaste nodev, /* devo_reset */ 258254721Semaste NULL, /* devi_cb_ops */ 259254721Semaste NULL, /* devo_bus_ops */ 260254721Semaste audiocs_ddi_power, /* devo_power */ 261254721Semaste ddi_quiesce_not_supported, /* devo_quiesce */ 262254721Semaste}; 263254721Semaste 264254721Semaste/* Linkage structure for loadable drivers */ 265254721Semastestatic struct modldrv audiocs_modldrv = { 266254721Semaste &mod_driverops, /* drv_modops */ 267254721Semaste CS4231_MOD_NAME, /* drv_linkinfo */ 268254721Semaste &audiocs_dev_ops /* drv_dev_ops */ 269254721Semaste}; 270254721Semaste 271254721Semaste/* Module linkage structure */ 272254721Semastestatic struct modlinkage audiocs_modlinkage = { 273254721Semaste MODREV_1, /* ml_rev */ 274254721Semaste (void *)&audiocs_modldrv, /* ml_linkage */ 275254721Semaste NULL /* NULL terminates the list */ 276254721Semaste}; 277254721Semaste 278254721Semaste 279254721Semaste/* ******* Loadable Module Configuration Entry Points ********************* */ 280254721Semaste 281254721Semaste/* 282254721Semaste * _init() 283254721Semaste * 284254721Semaste * Description: 285254721Semaste * Implements _init(9E). 286254721Semaste * 287254721Semaste * Returns: 288254721Semaste * mod_install() status, see mod_install(9f) 289254721Semaste */ 290254721Semasteint 291254721Semaste_init(void) 292254721Semaste{ 293254721Semaste int rv; 294254721Semaste 295254721Semaste audio_init_ops(&audiocs_dev_ops, CS4231_NAME); 296254721Semaste 297254721Semaste if ((rv = mod_install(&audiocs_modlinkage)) != 0) { 298254721Semaste audio_fini_ops(&audiocs_dev_ops); 299254721Semaste } 300254721Semaste 301254721Semaste return (rv); 302254721Semaste} 303254721Semaste 304254721Semaste/* 305254721Semaste * _fini() 306254721Semaste * 307254721Semaste * Description: 308254721Semaste * Implements _fini(9E). 309254721Semaste * 310254721Semaste * Returns: 311254721Semaste * mod_remove() status, see mod_remove(9f) 312254721Semaste */ 313254721Semasteint 314254721Semaste_fini(void) 315254721Semaste{ 316254721Semaste int rv; 317254721Semaste 318254721Semaste if ((rv = mod_remove(&audiocs_modlinkage)) == 0) { 319254721Semaste audio_fini_ops(&audiocs_dev_ops); 320254721Semaste } 321254721Semaste 322254721Semaste return (rv); 323254721Semaste} 324254721Semaste 325254721Semaste/* 326254721Semaste * _info() 327254721Semaste * 328254721Semaste * Description: 329254721Semaste * Implements _info(9E). 330254721Semaste * 331254721Semaste * Arguments: 332254721Semaste * modinfo *modinfop Pointer to the opaque modinfo structure 333254721Semaste * 334254721Semaste * Returns: 335254721Semaste * mod_info() status, see mod_info(9f) 336254721Semaste */ 337254721Semasteint 338254721Semaste_info(struct modinfo *modinfop) 339254721Semaste{ 340254721Semaste return (mod_info(&audiocs_modlinkage, modinfop)); 341254721Semaste} 342254721Semaste 343254721Semaste 344254721Semaste/* ******* Driver Entry Points ******************************************** */ 345254721Semaste 346254721Semaste/* 347254721Semaste * audiocs_ddi_attach() 348254721Semaste * 349254721Semaste * Description: 350254721Semaste * Implement attach(9e). 351254721Semaste * 352254721Semaste * Arguments: 353254721Semaste * dev_info_t *dip Pointer to the device's dev_info struct 354254721Semaste * ddi_attach_cmd_t cmd Attach command 355254721Semaste * 356254721Semaste * Returns: 357254721Semaste * DDI_SUCCESS The driver was initialized properly 358254721Semaste * DDI_FAILURE The driver couldn't be initialized properly 359254721Semaste */ 360254721Semastestatic int 361254721Semasteaudiocs_ddi_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 362254721Semaste{ 363254721Semaste switch (cmd) { 364254721Semaste case DDI_ATTACH: 365254721Semaste return (audiocs_attach(dip)); 366254721Semaste 367254721Semaste case DDI_RESUME: 368254721Semaste return (audiocs_resume(dip)); 369254721Semaste 370254721Semaste default: 371254721Semaste return (DDI_FAILURE); 372254721Semaste } 373254721Semaste} 374254721Semaste 375254721Semaste/* 376254721Semaste * audiocs_ddi_detach() 377254721Semaste * 378254721Semaste * Description: 379254721Semaste * Implement detach(9e). 380254721Semaste * 381254721Semaste * Arguments: 382254721Semaste * dev_info_t *dip Pointer to the device's dev_info struct 383254721Semaste * ddi_detach_cmd_t cmd Detach command 384254721Semaste * 385254721Semaste * Returns: 386254721Semaste * DDI_SUCCESS Success. 387254721Semaste * DDI_FAILURE Failure. 388254721Semaste */ 389254721Semastestatic int 390254721Semasteaudiocs_ddi_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 391254721Semaste{ 392254721Semaste switch (cmd) { 393254721Semaste case DDI_DETACH: 394254721Semaste return (audiocs_detach(dip)); 395254721Semaste 396254721Semaste case DDI_SUSPEND: 397254721Semaste return (audiocs_suspend(dip)); 398254721Semaste 399254721Semaste default: 400254721Semaste return (DDI_FAILURE); 401254721Semaste } 402254721Semaste} 403254721Semaste 404254721Semaste/* 405254721Semaste * audiocs_ddi_power() 406254721Semaste * 407254721Semaste * Description: 408254721Semaste * Implements power(9E). 409254721Semaste * 410254721Semaste * Arguments: 411254721Semaste * def_info_t *dip Ptr to the device's dev_info structure 412254721Semaste * int component Which component to power up/down 413254721Semaste * int level The power level for the component 414254721Semaste * 415254721Semaste * Returns: 416254721Semaste * DDI_SUCCESS Power level changed, we always succeed 417254721Semaste */ 418254721Semastestatic int 419254721Semasteaudiocs_ddi_power(dev_info_t *dip, int component, int level) 420254721Semaste{ 421254721Semaste CS_state_t *state; 422254721Semaste 423254721Semaste if (component != CS4231_COMPONENT) 424254721Semaste return (DDI_FAILURE); 425254721Semaste 426254721Semaste /* get the state structure */ 427254721Semaste state = ddi_get_driver_private(dip); 428254721Semaste 429254721Semaste ASSERT(!mutex_owned(&state->cs_lock)); 430254721Semaste 431254721Semaste /* make sure we have some work to do */ 432254721Semaste mutex_enter(&state->cs_lock); 433254721Semaste 434254721Semaste /* 435254721Semaste * We don't do anything if we're suspended. Suspend/resume diddles 436254721Semaste * with power anyway. 437254721Semaste */ 438254721Semaste if (!state->cs_suspended) { 439254721Semaste 440254721Semaste /* check the level change to see what we need to do */ 441254721Semaste if (level == CS4231_PWR_OFF && state->cs_powered) { 442254721Semaste 443254721Semaste /* power down and save the state */ 444254721Semaste audiocs_power_down(state); 445254721Semaste state->cs_powered = B_FALSE; 446254721Semaste 447254721Semaste } else if (level == CS4231_PWR_ON && !state->cs_powered) { 448254721Semaste 449254721Semaste /* power up */ 450254721Semaste audiocs_power_up(state); 451254721Semaste state->cs_powered = B_TRUE; 452254721Semaste } 453254721Semaste } 454254721Semaste 455254721Semaste mutex_exit(&state->cs_lock); 456254721Semaste 457254721Semaste ASSERT(!mutex_owned(&state->cs_lock)); 458254721Semaste 459254721Semaste return (DDI_SUCCESS); 460254721Semaste} 461254721Semaste 462254721Semaste/* ******* Local Routines *************************************************** */ 463254721Semaste 464254721Semastestatic void 465254721Semasteaudiocs_destroy(CS_state_t *state) 466254721Semaste{ 467254721Semaste if (state == NULL) 468254721Semaste return; 469254721Semaste 470254721Semaste for (int i = CS4231_PLAY; i <= CS4231_REC; i++) { 471254721Semaste audiocs_free_engine(state->cs_engines[i]); 472254721Semaste } 473254721Semaste audiocs_del_controls(state); 474254721Semaste 475254721Semaste if (state->cs_adev) { 476254721Semaste audio_dev_free(state->cs_adev); 477254721Semaste } 478254721Semaste 479254721Semaste /* unmap the registers */ 480254721Semaste CS4231_DMA_UNMAP_REGS(state); 481254721Semaste 482254721Semaste /* destroy the state mutex */ 483254721Semaste mutex_destroy(&state->cs_lock); 484254721Semaste kmem_free(state, sizeof (*state)); 485254721Semaste} 486254721Semaste 487254721Semaste/* 488254721Semaste * audiocs_attach() 489254721Semaste * 490254721Semaste * Description: 491254721Semaste * Attach an instance of the CS4231 driver. This routine does the device 492254721Semaste * dependent attach tasks. When it is complete it calls 493254721Semaste * audio_dev_register() to register with the framework. 494254721Semaste * 495254721Semaste * Arguments: 496254721Semaste * dev_info_t *dip Pointer to the device's dev_info struct 497254721Semaste * 498254721Semaste * Returns: 499254721Semaste * DDI_SUCCESS The driver was initialized properly 500254721Semaste * DDI_FAILURE The driver couldn't be initialized properly 501254721Semaste */ 502254721Semastestatic int 503254721Semasteaudiocs_attach(dev_info_t *dip) 504254721Semaste{ 505254721Semaste CS_state_t *state; 506254721Semaste audio_dev_t *adev; 507254721Semaste 508254721Semaste /* allocate the state structure */ 509254721Semaste state = kmem_zalloc(sizeof (*state), KM_SLEEP); 510254721Semaste state->cs_dip = dip; 511254721Semaste ddi_set_driver_private(dip, state); 512254721Semaste 513254721Semaste /* now fill it in, initialize the state mutexs first */ 514254721Semaste mutex_init(&state->cs_lock, NULL, MUTEX_DRIVER, NULL); 515254721Semaste 516254721Semaste /* 517254721Semaste * audio state initialization... should always succeed, 518254721Semaste * framework will message failure. 519254721Semaste */ 520254721Semaste if ((state->cs_adev = audio_dev_alloc(dip, 0)) == NULL) { 521254721Semaste goto error; 522254721Semaste } 523254721Semaste adev = state->cs_adev; 524254721Semaste audio_dev_set_description(adev, CS_DEV_CONFIG_ONBRD1); 525254721Semaste audio_dev_add_info(adev, "Legacy codec: Crystal Semiconductor CS4231"); 526254721Semaste 527254721Semaste /* initialize the audio state structures */ 528254721Semaste if ((audiocs_init_state(state)) == DDI_FAILURE) { 529254721Semaste audio_dev_warn(adev, "init_state() failed"); 530254721Semaste goto error; 531254721Semaste } 532254721Semaste 533254721Semaste mutex_enter(&state->cs_lock); 534254721Semaste 535254721Semaste /* initialize the audio chip */ 536254721Semaste if ((audiocs_chip_init(state)) == DDI_FAILURE) { 537254721Semaste mutex_exit(&state->cs_lock); 538254721Semaste audio_dev_warn(adev, "chip_init() failed"); 539254721Semaste goto error; 540254721Semaste } 541254721Semaste /* chip init will have powered us up */ 542254721Semaste state->cs_powered = B_TRUE; 543254721Semaste 544254721Semaste mutex_exit(&state->cs_lock); 545254721Semaste 546254721Semaste /* finally register with framework to kick everything off */ 547254721Semaste if (audio_dev_register(state->cs_adev) != DDI_SUCCESS) { 548254721Semaste audio_dev_warn(state->cs_adev, "unable to register audio dev"); 549254721Semaste } 550254721Semaste 551254721Semaste /* everything worked out, so report the device */ 552254721Semaste ddi_report_dev(dip); 553254721Semaste 554254721Semaste return (DDI_SUCCESS); 555254721Semaste 556254721Semasteerror: 557254721Semaste audiocs_destroy(state); 558254721Semaste return (DDI_FAILURE); 559254721Semaste} 560254721Semaste 561254721Semaste/* 562254721Semaste * audiocs_resume() 563254721Semaste * 564254721Semaste * Description: 565254721Semaste * Resume a suspended device instance. 566254721Semaste * 567254721Semaste * Arguments: 568254721Semaste * dev_info_t *dip Pointer to the device's dev_info struct 569254721Semaste * 570254721Semaste * Returns: 571254721Semaste * DDI_SUCCESS The driver was initialized properly 572254721Semaste * DDI_FAILURE The driver couldn't be initialized properly 573254721Semaste */ 574254721Semastestatic int 575254721Semasteaudiocs_resume(dev_info_t *dip) 576254721Semaste{ 577254721Semaste CS_state_t *state; 578254721Semaste audio_dev_t *adev; 579254721Semaste 580254721Semaste /* we've already allocated the state structure so get ptr */ 581254721Semaste state = ddi_get_driver_private(dip); 582254721Semaste adev = state->cs_adev; 583254721Semaste 584254721Semaste ASSERT(dip == state->cs_dip); 585254721Semaste ASSERT(!mutex_owned(&state->cs_lock)); 586254721Semaste 587254721Semaste /* mark the Codec busy -- this should keep power(9e) away */ 588254721Semaste (void) pm_busy_component(state->cs_dip, CS4231_COMPONENT); 589254721Semaste 590254721Semaste /* power it up */ 591254721Semaste audiocs_power_up(state); 592254721Semaste state->cs_powered = B_TRUE; 593254721Semaste 594254721Semaste mutex_enter(&state->cs_lock); 595254721Semaste 596254721Semaste /* initialize the audio chip */ 597254721Semaste if ((audiocs_chip_init(state)) == DDI_FAILURE) { 598254721Semaste mutex_exit(&state->cs_lock); 599254721Semaste audio_dev_warn(adev, "chip_init() failed"); 600254721Semaste (void) pm_idle_component(state->cs_dip, CS4231_COMPONENT); 601254721Semaste return (DDI_FAILURE); 602254721Semaste } 603254721Semaste 604254721Semaste state->cs_suspended = B_FALSE; 605254721Semaste 606254721Semaste mutex_exit(&state->cs_lock); 607254721Semaste 608254721Semaste /* 609254721Semaste * We have already powered up the chip, but this alerts the 610254721Semaste * framework to the fact. 611254721Semaste */ 612254721Semaste (void) pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON); 613254721Semaste (void) pm_idle_component(state->cs_dip, CS4231_COMPONENT); 614254721Semaste 615254721Semaste audio_dev_resume(state->cs_adev); 616254721Semaste 617254721Semaste return (DDI_SUCCESS); 618254721Semaste} 619254721Semaste 620254721Semaste/* 621254721Semaste * audiocs_detach() 622254721Semaste * 623254721Semaste * Description: 624254721Semaste * Detach an instance of the CS4231 driver. 625254721Semaste * 626254721Semaste * Arguments: 627254721Semaste * dev_info_t *dip Pointer to the device's dev_info struct 628254721Semaste * 629254721Semaste * Returns: 630254721Semaste * DDI_SUCCESS The driver was detached 631254721Semaste * DDI_FAILURE The driver couldn't be detached (busy) 632254721Semaste */ 633254721Semastestatic int 634254721Semasteaudiocs_detach(dev_info_t *dip) 635254721Semaste{ 636254721Semaste CS_state_t *state; 637254721Semaste audio_dev_t *adev; 638254721Semaste ddi_acc_handle_t handle; 639254721Semaste 640254721Semaste /* get the state structure */ 641254721Semaste state = ddi_get_driver_private(dip); 642254721Semaste handle = CODEC_HANDLE; 643254721Semaste adev = state->cs_adev; 644254721Semaste 645254721Semaste /* don't detach if still in use */ 646254721Semaste if (audio_dev_unregister(adev) != DDI_SUCCESS) { 647254721Semaste return (DDI_FAILURE); 648254721Semaste } 649254721Semaste 650254721Semaste if (state->cs_powered) { 651254721Semaste /* 652254721Semaste * Make sure the Codec and DMA engine are off. 653254721Semaste */ 654254721Semaste SELIDX(state, INTC_REG); 655254721Semaste ANDIDX(state, ~(INTC_PEN|INTC_CEN), INTC_VALID_MASK); 656254721Semaste 657254721Semaste /* make sure the DMA engine isn't going to do anything */ 658254721Semaste CS4231_DMA_RESET(state); 659254721Semaste 660254721Semaste /* 661254721Semaste * power down the device, no reason to waste power without 662254721Semaste * a driver 663254721Semaste */ 664254721Semaste (void) pm_lower_power(dip, CS4231_COMPONENT, CS4231_PWR_OFF); 665254721Semaste } 666254721Semaste 667254721Semaste audiocs_destroy(state); 668254721Semaste 669254721Semaste return (DDI_SUCCESS); 670254721Semaste} 671254721Semaste 672254721Semaste/* 673254721Semaste * audiocs_suspend() 674254721Semaste * 675254721Semaste * Description: 676254721Semaste * Suspend an instance of the CS4231 driver. 677254721Semaste * 678254721Semaste * Arguments: 679254721Semaste * dev_info_t *dip Pointer to the device's dev_info struct 680254721Semaste * 681254721Semaste * Returns: 682254721Semaste * DDI_SUCCESS The driver was detached 683254721Semaste * DDI_FAILURE The driver couldn't be detached 684254721Semaste */ 685254721Semastestatic int 686254721Semasteaudiocs_suspend(dev_info_t *dip) 687254721Semaste{ 688254721Semaste CS_state_t *state; 689254721Semaste 690254721Semaste /* get the state structure */ 691254721Semaste state = ddi_get_driver_private(dip); 692254721Semaste 693254721Semaste mutex_enter(&state->cs_lock); 694254721Semaste 695254721Semaste ASSERT(!state->cs_suspended); 696254721Semaste 697254721Semaste audio_dev_suspend(state->cs_adev); 698254721Semaste 699254721Semaste if (state->cs_powered) { 700254721Semaste /* now we can power down the Codec */ 701254721Semaste audiocs_power_down(state); 702254721Semaste state->cs_powered = B_FALSE; 703254721Semaste } 704254721Semaste state->cs_suspended = B_TRUE; /* stop new ops */ 705254721Semaste mutex_exit(&state->cs_lock); 706254721Semaste 707254721Semaste return (DDI_SUCCESS); 708254721Semaste} 709254721Semaste 710254721Semaste#define PLAYCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_PLAY) 711254721Semaste#define RECCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_REC) 712254721Semaste#define MONCTL (AUDIO_CTRL_FLAG_RW | AUDIO_CTRL_FLAG_MONITOR) 713254721Semaste#define PCMVOL (PLAYCTL | AUDIO_CTRL_FLAG_PCMVOL) 714254721Semaste#define MAINVOL (PLAYCTL | AUDIO_CTRL_FLAG_MAINVOL) 715254721Semaste#define RECVOL (RECCTL | AUDIO_CTRL_FLAG_RECVOL) 716254721Semaste#define MONVOL (MONCTL | AUDIO_CTRL_FLAG_MONVOL) 717254721Semaste 718254721Semaste/* 719254721Semaste * audiocs_alloc_ctrl 720254721Semaste * 721254721Semaste * Description: 722254721Semaste * Allocates a control structure for the audio mixer. 723254721Semaste * 724254721Semaste * Arguments: 725254721Semaste * CS_state_t *state Device soft state. 726254721Semaste * uint32_t num Control number to allocate. 727254721Semaste * uint64_t val Initial value. 728254721Semaste * 729254721Semaste * Returns: 730254721Semaste * Pointer to newly allocated CS_ctrl_t structure. 731254721Semaste */ 732254721Semastestatic CS_ctrl_t * 733254721Semasteaudiocs_alloc_ctrl(CS_state_t *state, uint32_t num, uint64_t val) 734254721Semaste{ 735254721Semaste audio_ctrl_desc_t desc; 736254721Semaste audio_ctrl_wr_t fn; 737254721Semaste CS_ctrl_t *cc; 738254721Semaste 739254721Semaste cc = kmem_zalloc(sizeof (*cc), KM_SLEEP); 740254721Semaste cc->cc_state = state; 741254721Semaste cc->cc_num = num; 742254721Semaste 743254721Semaste bzero(&desc, sizeof (desc)); 744254721Semaste 745254721Semaste switch (num) { 746254721Semaste case CTL_VOLUME: 747254721Semaste desc.acd_name = AUDIO_CTRL_ID_VOLUME; 748254721Semaste desc.acd_type = AUDIO_CTRL_TYPE_STEREO; 749254721Semaste desc.acd_minvalue = 0; 750254721Semaste desc.acd_maxvalue = 100; 751254721Semaste desc.acd_flags = PCMVOL; 752254721Semaste fn = audiocs_set_ogain; 753254721Semaste break; 754254721Semaste 755254721Semaste case CTL_IGAIN: 756254721Semaste desc.acd_name = AUDIO_CTRL_ID_RECGAIN; 757254721Semaste desc.acd_type = AUDIO_CTRL_TYPE_STEREO; 758254721Semaste desc.acd_minvalue = 0; 759254721Semaste desc.acd_maxvalue = 100; 760254721Semaste desc.acd_flags = RECVOL; 761254721Semaste fn = audiocs_set_igain; 762254721Semaste break; 763254721Semaste 764254721Semaste case CTL_MGAIN: 765254721Semaste desc.acd_name = AUDIO_CTRL_ID_MONGAIN; 766254721Semaste desc.acd_type = AUDIO_CTRL_TYPE_MONO; 767254721Semaste desc.acd_minvalue = 0; 768254721Semaste desc.acd_maxvalue = 100; 769254721Semaste desc.acd_flags = MONVOL; 770254721Semaste fn = audiocs_set_mgain; 771254721Semaste break; 772254721Semaste 773254721Semaste case CTL_INPUTS: 774254721Semaste desc.acd_name = AUDIO_CTRL_ID_RECSRC; 775254721Semaste desc.acd_type = AUDIO_CTRL_TYPE_ENUM; 776254721Semaste desc.acd_minvalue = state->cs_imask; 777254721Semaste desc.acd_maxvalue = state->cs_imask; 778254721Semaste desc.acd_flags = RECCTL; 779254721Semaste for (int i = 0; audiocs_inputs[i]; i++) { 780254721Semaste desc.acd_enum[i] = audiocs_inputs[i]; 781254721Semaste } 782254721Semaste fn = audiocs_set_inputs; 783254721Semaste 784254721Semaste break; 785254721Semaste 786254721Semaste case CTL_OUTPUTS: 787254721Semaste desc.acd_name = AUDIO_CTRL_ID_OUTPUTS; 788254721Semaste desc.acd_type = AUDIO_CTRL_TYPE_ENUM; 789254721Semaste desc.acd_minvalue = state->cs_omod; 790254721Semaste desc.acd_maxvalue = state->cs_omask; 791254721Semaste desc.acd_flags = PLAYCTL | AUDIO_CTRL_FLAG_MULTI; 792254721Semaste for (int i = 0; audiocs_outputs[i]; i++) { 793254721Semaste desc.acd_enum[i] = audiocs_outputs[i]; 794254721Semaste } 795254721Semaste fn = audiocs_set_outputs; 796254721Semaste break; 797254721Semaste 798254721Semaste case CTL_MICBOOST: 799254721Semaste desc.acd_name = AUDIO_CTRL_ID_MICBOOST; 800254721Semaste desc.acd_type = AUDIO_CTRL_TYPE_BOOLEAN; 801254721Semaste desc.acd_minvalue = 0; 802254721Semaste desc.acd_maxvalue = 1; 803254721Semaste desc.acd_flags = RECCTL; 804254721Semaste fn = audiocs_set_micboost; 805254721Semaste break; 806254721Semaste } 807254721Semaste 808254721Semaste cc->cc_val = val; 809254721Semaste cc->cc_ctrl = audio_dev_add_control(state->cs_adev, &desc, 810254721Semaste audiocs_get_value, fn, cc); 811254721Semaste 812254721Semaste return (cc); 813254721Semaste} 814254721Semaste 815254721Semaste/* 816254721Semaste * audiocs_free_ctrl 817254721Semaste * 818254721Semaste * Description: 819254721Semaste * Frees a control and all resources associated with it. 820254721Semaste * 821254721Semaste * Arguments: 822254721Semaste * CS_ctrl_t *cc Pointer to control structure. 823254721Semaste */ 824254721Semastestatic void 825254721Semasteaudiocs_free_ctrl(CS_ctrl_t *cc) 826254721Semaste{ 827254721Semaste if (cc == NULL) 828254721Semaste return; 829254721Semaste if (cc->cc_ctrl) 830254721Semaste audio_dev_del_control(cc->cc_ctrl); 831254721Semaste kmem_free(cc, sizeof (*cc)); 832254721Semaste} 833254721Semaste 834254721Semaste/* 835254721Semaste * audiocs_add_controls 836254721Semaste * 837254721Semaste * Description: 838254721Semaste * Allocates and registers all controls for this device. 839254721Semaste * 840254721Semaste * Arguments: 841254721Semaste * CS_state_t *state Device soft state. 842254721Semaste * 843254721Semaste * Returns: 844254721Semaste * DDI_SUCCESS All controls added and registered 845254721Semaste * DDI_FAILURE At least one control was not added or registered. 846254721Semaste */ 847254721Semastestatic int 848254721Semasteaudiocs_add_controls(CS_state_t *state) 849254721Semaste{ 850254721Semaste#define ADD_CTRL(CTL, ID, VAL) \ 851254721Semaste state->cs_##CTL = audiocs_alloc_ctrl(state, ID, VAL); \ 852254721Semaste if (state->cs_##CTL == NULL) { \ 853254721Semaste audio_dev_warn(state->cs_adev, \ 854254721Semaste "unable to allocate %s control", #ID); \ 855254721Semaste return (DDI_FAILURE); \ 856254721Semaste } 857254721Semaste 858254721Semaste ADD_CTRL(ogain, CTL_VOLUME, 0x4b4b); 859254721Semaste ADD_CTRL(igain, CTL_IGAIN, 0x3232); 860254721Semaste ADD_CTRL(mgain, CTL_MGAIN, 0); 861254721Semaste ADD_CTRL(micboost, CTL_MICBOOST, 0); 862254721Semaste ADD_CTRL(outputs, CTL_OUTPUTS, (state->cs_omask & ~state->cs_omod) | 863254721Semaste (1U << OUTPUT_SPEAKER)); 864254721Semaste ADD_CTRL(inputs, CTL_INPUTS, (1U << INPUT_MIC)); 865254721Semaste 866254721Semaste return (DDI_SUCCESS); 867254721Semaste} 868254721Semaste 869254721Semaste/* 870254721Semaste * audiocs_del_controls 871254721Semaste * 872254721Semaste * Description: 873254721Semaste * Unregisters and frees all controls for this device. 874254721Semaste * 875254721Semaste * Arguments: 876254721Semaste * CS_state_t *state Device soft state. 877254721Semaste */ 878254721Semastevoid 879254721Semasteaudiocs_del_controls(CS_state_t *state) 880254721Semaste{ 881254721Semaste audiocs_free_ctrl(state->cs_ogain); 882254721Semaste audiocs_free_ctrl(state->cs_igain); 883254721Semaste audiocs_free_ctrl(state->cs_mgain); 884254721Semaste audiocs_free_ctrl(state->cs_micboost); 885254721Semaste audiocs_free_ctrl(state->cs_inputs); 886254721Semaste audiocs_free_ctrl(state->cs_outputs); 887254721Semaste} 888254721Semaste 889254721Semaste 890254721Semaste/* 891254721Semaste * audiocs_chip_init() 892254721Semaste * 893254721Semaste * Description: 894254721Semaste * Power up the audio core, initialize the audio Codec, prepare the chip 895254721Semaste * for use. 896254721Semaste * 897254721Semaste * Arguments: 898254721Semaste * CS_state_t *state The device's state structure 899254721Semaste * 900254721Semaste * Returns: 901254721Semaste * DDI_SUCCESS Chip initialized and ready to use 902254721Semaste * DDI_FAILURE Chip not initialized and not ready 903254721Semaste */ 904254721Semastestatic int 905254721Semasteaudiocs_chip_init(CS_state_t *state) 906254721Semaste{ 907254721Semaste ddi_acc_handle_t handle = CODEC_HANDLE; 908254721Semaste 909254721Semaste /* make sure we are powered up */ 910254721Semaste CS4231_DMA_POWER(state, CS4231_PWR_ON); 911254721Semaste 912254721Semaste CS4231_DMA_RESET(state); 913254721Semaste 914254721Semaste /* wait for the Codec before we continue */ 915254721Semaste if (audiocs_poll_ready(state) == DDI_FAILURE) { 916254721Semaste return (DDI_FAILURE); 917254721Semaste } 918254721Semaste 919254721Semaste /* activate registers 16 -> 31 */ 920254721Semaste SELIDX(state, MID_REG); 921254721Semaste ddi_put8(handle, &CS4231_IDR, MID_MODE2); 922254721Semaste 923254721Semaste /* now figure out what version we have */ 924254721Semaste SELIDX(state, VID_REG); 925254721Semaste if (ddi_get8(handle, &CS4231_IDR) & VID_A) { 926254721Semaste state->cs_revA = B_TRUE; 927254721Semaste } else { 928254721Semaste state->cs_revA = B_FALSE; 929254721Semaste } 930254721Semaste 931254721Semaste /* get rid of annoying popping by muting the output channels */ 932254721Semaste SELIDX(state, LDACO_REG); 933254721Semaste PUTIDX(state, LDACO_LDM | LDACO_MID_GAIN, LDAC0_VALID_MASK); 934254721Semaste SELIDX(state, RDACO_REG); 935254721Semaste PUTIDX(state, RDACO_RDM | RDACO_MID_GAIN, RDAC0_VALID_MASK); 936254721Semaste 937254721Semaste /* initialize aux input channels to known gain values & muted */ 938254721Semaste SELIDX(state, LAUX1_REG); 939254721Semaste PUTIDX(state, LAUX1_LX1M | LAUX1_UNITY_GAIN, LAUX1_VALID_MASK); 940254721Semaste SELIDX(state, RAUX1_REG); 941254721Semaste PUTIDX(state, RAUX1_RX1M | RAUX1_UNITY_GAIN, RAUX1_VALID_MASK); 942254721Semaste SELIDX(state, LAUX2_REG); 943254721Semaste PUTIDX(state, LAUX2_LX2M | LAUX2_UNITY_GAIN, LAUX2_VALID_MASK); 944254721Semaste SELIDX(state, RAUX2_REG); 945254721Semaste PUTIDX(state, RAUX2_RX2M | RAUX2_UNITY_GAIN, RAUX2_VALID_MASK); 946254721Semaste 947254721Semaste /* initialize aux input channels to known gain values & muted */ 948254721Semaste SELIDX(state, LLIC_REG); 949254721Semaste PUTIDX(state, LLIC_LLM | LLIC_UNITY_GAIN, LLIC_VALID_MASK); 950254721Semaste SELIDX(state, RLIC_REG); 951254721Semaste PUTIDX(state, RLIC_RLM | RLIC_UNITY_GAIN, RLIC_VALID_MASK); 952254721Semaste 953254721Semaste /* program the sample rate, play and capture must be the same */ 954254721Semaste SELIDX(state, FSDF_REG | IAR_MCE); 955254721Semaste PUTIDX(state, FS_48000 | PDF_LINEAR16NE | PDF_STEREO, FSDF_VALID_MASK); 956254721Semaste if (audiocs_poll_ready(state) == DDI_FAILURE) { 957254721Semaste return (DDI_FAILURE); 958254721Semaste } 959254721Semaste 960254721Semaste SELIDX(state, CDF_REG | IAR_MCE); 961254721Semaste PUTIDX(state, CDF_LINEAR16NE | CDF_STEREO, CDF_VALID_MASK); 962254721Semaste if (audiocs_poll_ready(state) == DDI_FAILURE) { 963254721Semaste return (DDI_FAILURE); 964254721Semaste } 965254721Semaste 966254721Semaste /* 967254721Semaste * Set up the Codec for playback and capture disabled, dual DMA, and 968254721Semaste * playback and capture DMA. 969254721Semaste */ 970254721Semaste SELIDX(state, (INTC_REG | IAR_MCE)); 971254721Semaste PUTIDX(state, INTC_DDC | INTC_PDMA | INTC_CDMA, INTC_VALID_MASK); 972254721Semaste if (audiocs_poll_ready(state) == DDI_FAILURE) { 973254721Semaste return (DDI_FAILURE); 974254721Semaste } 975254721Semaste 976254721Semaste /* 977254721Semaste * Turn on the output level bit to be 2.8 Vpp. Also, don't go to 0 on 978254721Semaste * underflow. 979254721Semaste */ 980254721Semaste SELIDX(state, AFE1_REG); 981254721Semaste PUTIDX(state, AFE1_OLB, AFE1_VALID_MASK); 982254721Semaste 983254721Semaste /* turn on the high pass filter if Rev A */ 984254721Semaste SELIDX(state, AFE2_REG); 985254721Semaste if (state->cs_revA) { 986254721Semaste PUTIDX(state, AFE2_HPF, AFE2_VALID_MASK); 987254721Semaste } else { 988254721Semaste PUTIDX(state, 0, AFE2_VALID_MASK); 989254721Semaste } 990254721Semaste 991254721Semaste 992254721Semaste /* clear the play and capture interrupt flags */ 993254721Semaste SELIDX(state, AFS_REG); 994254721Semaste ddi_put8(handle, &CS4231_STATUS, (AFS_RESET_STATUS)); 995254721Semaste 996254721Semaste /* the play and record gains will be set by the audio mixer */ 997254721Semaste 998254721Semaste /* unmute the output */ 999254721Semaste SELIDX(state, LDACO_REG); 1000254721Semaste ANDIDX(state, ~LDACO_LDM, LDAC0_VALID_MASK); 1001254721Semaste SELIDX(state, RDACO_REG); 1002254721Semaste ANDIDX(state, ~RDACO_RDM, RDAC0_VALID_MASK); 1003254721Semaste 1004254721Semaste /* unmute the mono speaker and mute mono in */ 1005254721Semaste SELIDX(state, MIOC_REG); 1006254721Semaste PUTIDX(state, MIOC_MIM, MIOC_VALID_MASK); 1007254721Semaste 1008254721Semaste audiocs_configure_output(state); 1009254721Semaste audiocs_configure_input(state); 1010254721Semaste 1011254721Semaste return (DDI_SUCCESS); 1012254721Semaste} 1013254721Semaste 1014254721Semaste/* 1015254721Semaste * audiocs_init_state() 1016254721Semaste * 1017254721Semaste * Description: 1018254721Semaste * This routine initializes the audio driver's state structure and 1019254721Semaste * maps in the registers. This also includes reading the properties. 1020254721Semaste * 1021254721Semaste * CAUTION: This routine maps the registers and initializes a mutex. 1022254721Semaste * Failure cleanup is handled by cs4231_attach(). It is not 1023254721Semaste * handled locally by this routine. 1024254721Semaste * 1025254721Semaste * Arguments: 1026254721Semaste * CS_state_t *state The device's state structure 1027254721Semaste * 1028254721Semaste * Returns: 1029254721Semaste * DDI_SUCCESS State structure initialized 1030254721Semaste * DDI_FAILURE State structure not initialized 1031254721Semaste */ 1032254721Semastestatic int 1033254721Semasteaudiocs_init_state(CS_state_t *state) 1034254721Semaste{ 1035254721Semaste audio_dev_t *adev = state->cs_adev; 1036254721Semaste dev_info_t *dip = state->cs_dip; 1037254721Semaste char *prop_str; 1038254721Semaste char *pm_comp[] = { 1039254721Semaste "NAME=audiocs audio device", 1040254721Semaste "0=off", 1041254721Semaste "1=on" }; 1042254721Semaste 1043254721Semaste /* set up the pm-components */ 1044254721Semaste if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, 1045254721Semaste "pm-components", pm_comp, 3) != DDI_PROP_SUCCESS) { 1046254721Semaste audio_dev_warn(adev, "couldn't create pm-components property"); 1047254721Semaste return (DDI_FAILURE); 1048254721Semaste } 1049254721Semaste 1050254721Semaste /* figure out which DMA engine hardware we have */ 1051254721Semaste if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1052254721Semaste "dma-model", &prop_str) == DDI_PROP_SUCCESS) { 1053254721Semaste if (strcmp(prop_str, "eb2dma") == 0) { 1054254721Semaste state->cs_dma_engine = EB2_DMA; 1055254721Semaste state->cs_dma_ops = &cs4231_eb2dma_ops; 1056254721Semaste } else { 1057254721Semaste state->cs_dma_engine = APC_DMA; 1058254721Semaste state->cs_dma_ops = &cs4231_apcdma_ops; 1059254721Semaste } 1060254721Semaste ddi_prop_free(prop_str); 1061254721Semaste } else { 1062254721Semaste state->cs_dma_engine = APC_DMA; 1063254721Semaste state->cs_dma_ops = &cs4231_apcdma_ops; 1064254721Semaste } 1065254721Semaste 1066254721Semaste /* cs_regs, cs_eb2_regs and cs_handles filled in later */ 1067254721Semaste 1068254721Semaste /* most of what's left is filled in when the registers are mapped */ 1069254721Semaste 1070254721Semaste audiocs_get_ports(state); 1071254721Semaste 1072254721Semaste /* Allocate engines, must be done before register mapping called */ 1073254721Semaste if ((audiocs_alloc_engine(state, CS4231_PLAY) != DDI_SUCCESS) || 1074254721Semaste (audiocs_alloc_engine(state, CS4231_REC) != DDI_SUCCESS)) { 1075254721Semaste return (DDI_FAILURE); 1076254721Semaste } 1077254721Semaste 1078254721Semaste /* Map in the registers */ 1079254721Semaste if (CS4231_DMA_MAP_REGS(state) == DDI_FAILURE) { 1080254721Semaste return (DDI_FAILURE); 1081254721Semaste } 1082254721Semaste 1083254721Semaste 1084254721Semaste /* Allocate and add controls, must be done *after* registers mapped */ 1085254721Semaste if (audiocs_add_controls(state) != DDI_SUCCESS) { 1086254721Semaste return (DDI_FAILURE); 1087254721Semaste } 1088254721Semaste 1089254721Semaste state->cs_suspended = B_FALSE; 1090254721Semaste state->cs_powered = B_FALSE; 1091254721Semaste 1092254721Semaste return (DDI_SUCCESS); 1093254721Semaste} 1094254721Semaste 1095254721Semaste/* 1096254721Semaste * audiocs_get_ports() 1097254721Semaste * 1098254721Semaste * Description: 1099254721Semaste * Get which audiocs h/w version we have and use this to 1100254721Semaste * determine the input and output ports as well whether or not 1101254721Semaste * the hardware has internal loopbacks or not. We also have three 1102254721Semaste * different ways for the properties to be specified, which we 1103254721Semaste * also need to worry about. 1104254721Semaste * 1105254721Semaste * Vers Platform(s) DMA eng. audio-module** loopback 1106254721Semaste * a SS-4+/SS-5+ apcdma no no 1107254721Semaste * b Ultra-1&2 apcdma no yes 1108254721Semaste * c positron apcdma no yes 1109254721Semaste * d PPC - retired 1110254721Semaste * e x86 - retired 1111254721Semaste * f tazmo eb2dma Perigee no 1112254721Semaste * g tazmo eb2dma Quark yes 1113254721Semaste * h darwin+ eb2dma no N/A 1114254721Semaste * 1115254721Semaste * Vers model~ aux1* aux2* 1116254721Semaste * a N/A N/A N/A 1117254721Semaste * b N/A N/A N/A 1118254721Semaste * c N/A N/A N/A 1119254721Semaste * d retired 1120254721Semaste * e retired 1121254721Semaste * f SUNW,CS4231f N/A N/A 1122254721Semaste * g SUNW,CS4231g N/A N/A 1123254721Semaste * h SUNW,CS4231h cdrom none 1124254721Semaste * 1125254721Semaste * * = Replaces internal-loopback for latest property type, can be 1126254721Semaste * set to "cdrom", "loopback", or "none". 1127254721Semaste * 1128254721Semaste * ** = For plugin audio modules only. Starting with darwin, this 1129254721Semaste * property is replaces by the model property. 1130254721Semaste * 1131254721Semaste * ~ = Replaces audio-module. 1132254721Semaste * 1133254721Semaste * + = Has the capability of having a cable run from the internal 1134254721Semaste * CD-ROM to the audio device. 1135254721Semaste * 1136254721Semaste * N/A = Not applicable, the property wasn't created for early 1137254721Semaste * platforms, or the property has been retired. 1138254721Semaste * 1139254721Semaste * NOTE: Older tazmo and quark machines don't have the model property. 1140254721Semaste * 1141254721Semaste * Arguments: 1142254721Semaste * CS_state_t *state The device's state structure 1143254721Semaste */ 1144static void 1145audiocs_get_ports(CS_state_t *state) 1146{ 1147 dev_info_t *dip = state->cs_dip; 1148 audio_dev_t *adev = state->cs_adev; 1149 char *prop_str; 1150 1151 /* First we set the common ports, etc. */ 1152 state->cs_omask = state->cs_omod = 1153 (1U << OUTPUT_SPEAKER) | 1154 (1U << OUTPUT_HEADPHONES) | 1155 (1U << OUTPUT_LINEOUT); 1156 state->cs_imask = 1157 (1U << INPUT_MIC) | 1158 (1U << INPUT_LINEIN) | 1159 (1U << INPUT_STEREOMIX); 1160 1161 /* now we try the new "model" property */ 1162 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1163 "model", &prop_str) == DDI_PROP_SUCCESS) { 1164 if (strcmp(prop_str, "SUNW,CS4231h") == 0) { 1165 /* darwin */ 1166 audio_dev_set_version(adev, CS_DEV_VERSION_H); 1167 state->cs_imask |= (1U << INPUT_CD); 1168 state->cs_omod = (1U << OUTPUT_SPEAKER); 1169 } else if (strcmp(prop_str, "SUNW,CS4231g") == 0) { 1170 /* quark audio module */ 1171 audio_dev_set_version(adev, CS_DEV_VERSION_G); 1172 /* 1173 * NB: This could do SUNVTS LOOPBACK, but we 1174 * don't support it for now... owing to no 1175 * support in framework. 1176 */ 1177 } else if (strcmp(prop_str, "SUNW,CS4231f") == 0) { 1178 /* tazmo */ 1179 audio_dev_set_version(adev, CS_DEV_VERSION_F); 1180 } else { 1181 audio_dev_set_version(adev, prop_str); 1182 audio_dev_warn(adev, 1183 "unknown audio model: %s, some parts of " 1184 "audio may not work correctly", prop_str); 1185 } 1186 ddi_prop_free(prop_str); /* done with the property */ 1187 } else { /* now try the older "audio-module" property */ 1188 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 1189 DDI_PROP_DONTPASS, "audio-module", &prop_str) == 1190 DDI_PROP_SUCCESS) { 1191 switch (*prop_str) { 1192 case 'Q': /* quark audio module */ 1193 audio_dev_set_version(adev, CS_DEV_VERSION_G); 1194 /* See quark comment above about SunVTS */ 1195 break; 1196 case 'P': /* tazmo */ 1197 audio_dev_set_version(adev, CS_DEV_VERSION_F); 1198 break; 1199 default: 1200 audio_dev_set_version(adev, prop_str); 1201 audio_dev_warn(adev, 1202 "unknown audio module: %s, some " 1203 "parts of audio may not work correctly", 1204 prop_str); 1205 break; 1206 } 1207 ddi_prop_free(prop_str); /* done with the prop */ 1208 } else { /* now try heuristics, ;-( */ 1209 if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, 1210 DDI_PROP_DONTPASS, "internal-loopback", B_FALSE)) { 1211 if (state->cs_dma_engine == EB2_DMA) { 1212 audio_dev_set_version(adev, 1213 CS_DEV_VERSION_C); 1214 } else { 1215 audio_dev_set_version(adev, 1216 CS_DEV_VERSION_B); 1217 } 1218 /* 1219 * Again, we don't support SunVTS for these 1220 * boards, although we potentially could. 1221 */ 1222 } else { 1223 audio_dev_set_version(adev, CS_DEV_VERSION_A); 1224 state->cs_imask |= (1U << INPUT_CD); 1225 } 1226 } 1227 } 1228} 1229 1230/* 1231 * audiocs_power_up() 1232 * 1233 * Description: 1234 * Power up the Codec and restore the codec's registers. 1235 * 1236 * NOTE: We don't worry about locking since the only routines 1237 * that may call us are attach() and power() Both of 1238 * which should be the only threads in the driver. 1239 * 1240 * Arguments: 1241 * CS_state_t *state The device's state structure 1242 */ 1243static void 1244audiocs_power_up(CS_state_t *state) 1245{ 1246 ddi_acc_handle_t handle = CODEC_HANDLE; 1247 int i; 1248 1249 /* turn on the Codec */ 1250 CS4231_DMA_POWER(state, CS4231_PWR_ON); 1251 1252 /* reset the DMA engine(s) */ 1253 CS4231_DMA_RESET(state); 1254 1255 (void) audiocs_poll_ready(state); 1256 1257 /* 1258 * Reload the Codec's registers, the DMA engines will be 1259 * taken care of when play and record start up again. But 1260 * first enable registers 16 -> 31. 1261 */ 1262 SELIDX(state, MID_REG); 1263 PUTIDX(state, state->cs_save[MID_REG], MID_VALID_MASK); 1264 1265 for (i = 0; i < CS4231_REGS; i++) { 1266 /* restore Codec registers */ 1267 SELIDX(state, (i | IAR_MCE)); 1268 ddi_put8(handle, &CS4231_IDR, state->cs_save[i]); 1269 (void) audiocs_poll_ready(state); 1270 } 1271 /* clear MCE bit */ 1272 SELIDX(state, 0); 1273} 1274 1275/* 1276 * audiocs_power_down() 1277 * 1278 * Description: 1279 * Power down the Codec and save the codec's registers. 1280 * 1281 * NOTE: See the note in cs4231_power_up() about locking. 1282 * 1283 * Arguments: 1284 * CS_state_t *state The device's state structure 1285 */ 1286static void 1287audiocs_power_down(CS_state_t *state) 1288{ 1289 ddi_acc_handle_t handle; 1290 int i; 1291 1292 handle = state->cs_handles.cs_codec_hndl; 1293 1294 /* 1295 * We are powering down, so we don't need to do a thing with 1296 * the DMA engines. However, we do need to save the Codec 1297 * registers. 1298 */ 1299 1300 for (i = 0; i < CS4231_REGS; i++) { 1301 /* save Codec regs */ 1302 SELIDX(state, i); 1303 state->cs_save[i] = ddi_get8(handle, &CS4231_IDR); 1304 } 1305 1306 /* turn off the Codec */ 1307 CS4231_DMA_POWER(state, CS4231_PWR_OFF); 1308 1309} /* cs4231_power_down() */ 1310 1311/* 1312 * audiocs_configure_input() 1313 * 1314 * Description: 1315 * Configure input properties of the mixer (e.g. igain, ports). 1316 * 1317 * Arguments: 1318 * CS_state_t *state The device's state structure 1319 */ 1320static void 1321audiocs_configure_input(CS_state_t *state) 1322{ 1323 uint8_t l, r; 1324 uint64_t inputs; 1325 uint64_t micboost; 1326 1327 ASSERT(mutex_owned(&state->cs_lock)); 1328 1329 inputs = state->cs_inputs->cc_val; 1330 micboost = state->cs_micboost->cc_val; 1331 r = (state->cs_igain->cc_val & 0xff); 1332 l = ((state->cs_igain->cc_val & 0xff00) >> 8); 1333 1334 /* rescale these for our atten array */ 1335 l = (((uint32_t)l * 255) / 100) & 0xff; 1336 r = (((uint32_t)r * 255) / 100) & 0xff; 1337 1338 /* we downshift by 4 bits -- igain only has 16 possible values */ 1339 /* NB: that we do not scale here! The SADA driver didn't do so. */ 1340 l = l >> 4; 1341 r = r >> 4; 1342 1343 if (inputs & (1U << INPUT_MIC)) { 1344 l |= LADCI_LMIC; 1345 r |= RADCI_RMIC; 1346 } 1347 if (inputs & (1U << INPUT_LINEIN)) { 1348 l |= LADCI_LLINE; 1349 r |= RADCI_RLINE; 1350 } 1351 if (inputs & (1U << INPUT_CD)) { 1352 /* note that SunVTS also uses this */ 1353 l |= LADCI_LAUX1; 1354 r |= RADCI_RAUX1; 1355 } 1356 if (inputs & (1U << INPUT_STEREOMIX)) { 1357 l |= LADCI_LLOOP; 1358 r |= RADCI_RLOOP; 1359 } 1360 if (micboost) { 1361 l |= LADCI_LMGE; 1362 r |= RADCI_RMGE; 1363 } 1364 1365 SELIDX(state, LADCI_REG); 1366 PUTIDX(state, l, LADCI_VALID_MASK); 1367 1368 SELIDX(state, RADCI_REG); 1369 PUTIDX(state, r, RADCI_VALID_MASK); 1370} 1371 1372/* 1373 * audiocs_configure_output() 1374 * 1375 * Description: 1376 * Configure output properties of the mixer (e.g. ogain, mgain). 1377 * 1378 * Arguments: 1379 * CS_state_t *state The device's state structure 1380 */ 1381static void 1382audiocs_configure_output(CS_state_t *state) 1383{ 1384 uint64_t outputs; 1385 uint8_t l, r; 1386 uint8_t rmute, lmute; 1387 uint8_t mgain; 1388 ddi_acc_handle_t handle = CODEC_HANDLE; 1389 1390 rmute = lmute = 0; 1391 1392 ASSERT(mutex_owned(&state->cs_lock)); 1393 1394 outputs = state->cs_outputs->cc_val; 1395 1396 /* port selection */ 1397 SELIDX(state, MIOC_REG); 1398 if (outputs & (1U << OUTPUT_SPEAKER)) { 1399 ANDIDX(state, ~MIOC_MONO_SPKR_MUTE, MIOC_VALID_MASK); 1400 } else { 1401 ORIDX(state, MIOC_MONO_SPKR_MUTE, MIOC_VALID_MASK); 1402 } 1403 SELIDX(state, PC_REG); 1404 if (outputs & (1U << OUTPUT_HEADPHONES)) { 1405 ANDIDX(state, ~PC_HEADPHONE_MUTE, PC_VALID_MASK); 1406 } else { 1407 ORIDX(state, PC_HEADPHONE_MUTE, PC_VALID_MASK); 1408 } 1409 SELIDX(state, PC_REG); 1410 if (outputs & (1U << OUTPUT_LINEOUT)) { 1411 ANDIDX(state, ~PC_LINE_OUT_MUTE, PC_VALID_MASK); 1412 } else { 1413 ORIDX(state, PC_LINE_OUT_MUTE, PC_VALID_MASK); 1414 } 1415 1416 /* monitor gain */ 1417 mgain = cs4231_atten[((state->cs_mgain->cc_val * 255) / 100) & 0xff]; 1418 SELIDX(state, LC_REG); 1419 if (mgain == 0) { 1420 /* disable loopbacks when gain == 0 */ 1421 PUTIDX(state, LC_OFF, LC_VALID_MASK); 1422 } else { 1423 /* we use cs4231_atten[] to linearize attenuation */ 1424 PUTIDX(state, (mgain << 2) | LC_LBE, LC_VALID_MASK); 1425 } 1426 1427 /* output gain */ 1428 l = ((state->cs_ogain->cc_val >> 8) & 0xff); 1429 r = (state->cs_ogain->cc_val & 0xff); 1430 if (l == 0) { 1431 lmute = LDACO_LDM; 1432 } 1433 if (r == 0) { 1434 rmute = RDACO_RDM; 1435 } 1436 1437 /* rescale these for our atten array */ 1438 l = cs4231_atten[(((uint32_t)l * 255) / 100) & 0xff] | lmute; 1439 r = cs4231_atten[(((uint32_t)r * 255) / 100) & 0xff] | rmute; 1440 1441 SELIDX(state, LDACO_REG); 1442 PUTIDX(state, l, LDAC0_VALID_MASK); 1443 SELIDX(state, RDACO_REG); 1444 PUTIDX(state, r, RDAC0_VALID_MASK); 1445} 1446 1447/* 1448 * audiocs_get_value() 1449 * 1450 * Description: 1451 * Get a control value 1452 * 1453 * Arguments: 1454 * void *arg The device's state structure 1455 * uint64_t *valp Pointer to store value. 1456 * 1457 * Returns: 1458 * 0 The Codec parameter has been retrieved. 1459 */ 1460static int 1461audiocs_get_value(void *arg, uint64_t *valp) 1462{ 1463 CS_ctrl_t *cc = arg; 1464 CS_state_t *state = cc->cc_state; 1465 1466 mutex_enter(&state->cs_lock); 1467 *valp = cc->cc_val; 1468 mutex_exit(&state->cs_lock); 1469 return (0); 1470} 1471 1472 1473/* 1474 * audiocs_set_ogain() 1475 * 1476 * Description: 1477 * Set the play gain. 1478 * 1479 * Arguments: 1480 * void *arg The device's state structure 1481 * uint64_t val The gain to set (both left and right) 1482 * 1483 * Returns: 1484 * 0 The Codec parameter has been set 1485 */ 1486static int 1487audiocs_set_ogain(void *arg, uint64_t val) 1488{ 1489 CS_ctrl_t *cc = arg; 1490 CS_state_t *state = cc->cc_state; 1491 1492 if ((val & ~0xffff) || 1493 ((val & 0xff) > 100) || 1494 (((val & 0xff00) >> 8) > 100)) 1495 return (EINVAL); 1496 1497 mutex_enter(&state->cs_lock); 1498 cc->cc_val = val; 1499 audiocs_configure_output(state); 1500 mutex_exit(&state->cs_lock); 1501 return (0); 1502} 1503 1504/* 1505 * audiocs_set_micboost() 1506 * 1507 * Description: 1508 * Set the 20 dB microphone boost. 1509 * 1510 * Arguments: 1511 * void *arg The device's state structure 1512 * uint64_t val The 1 to enable, 0 to disable. 1513 * 1514 * Returns: 1515 * 0 The Codec parameter has been set 1516 */ 1517static int 1518audiocs_set_micboost(void *arg, uint64_t val) 1519{ 1520 CS_ctrl_t *cc = arg; 1521 CS_state_t *state = cc->cc_state; 1522 1523 mutex_enter(&state->cs_lock); 1524 cc->cc_val = val ? B_TRUE : B_FALSE; 1525 audiocs_configure_input(state); 1526 mutex_exit(&state->cs_lock); 1527 return (0); 1528} 1529 1530/* 1531 * audiocs_set_igain() 1532 * 1533 * Description: 1534 * Set the record gain. 1535 * 1536 * Arguments: 1537 * void *arg The device's state structure 1538 * uint64_t val The gain to set (both left and right) 1539 * 1540 * Returns: 1541 * 0 The Codec parameter has been set 1542 */ 1543static int 1544audiocs_set_igain(void *arg, uint64_t val) 1545{ 1546 CS_ctrl_t *cc = arg; 1547 CS_state_t *state = cc->cc_state; 1548 1549 if ((val & ~0xffff) || 1550 ((val & 0xff) > 100) || 1551 (((val & 0xff00) >> 8) > 100)) 1552 return (EINVAL); 1553 1554 mutex_enter(&state->cs_lock); 1555 cc->cc_val = val; 1556 audiocs_configure_input(state); 1557 mutex_exit(&state->cs_lock); 1558 1559 return (0); 1560} 1561 1562/* 1563 * audiocs_set_inputs() 1564 * 1565 * Description: 1566 * Set the input ports. 1567 * 1568 * Arguments: 1569 * void *arg The device's state structure 1570 * uint64_t val The mask of output ports. 1571 * 1572 * Returns: 1573 * 0 The Codec parameter has been set 1574 */ 1575static int 1576audiocs_set_inputs(void *arg, uint64_t val) 1577{ 1578 CS_ctrl_t *cc = arg; 1579 CS_state_t *state = cc->cc_state; 1580 1581 if (val & ~(state->cs_imask)) 1582 return (EINVAL); 1583 1584 mutex_enter(&state->cs_lock); 1585 cc->cc_val = val; 1586 audiocs_configure_input(state); 1587 mutex_exit(&state->cs_lock); 1588 1589 return (0); 1590} 1591 1592/* 1593 * audiocs_set_outputs() 1594 * 1595 * Description: 1596 * Set the output ports. 1597 * 1598 * Arguments: 1599 * void *arg The device's state structure 1600 * uint64_t val The mask of input ports. 1601 * 1602 * Returns: 1603 * 0 The Codec parameter has been set 1604 */ 1605static int 1606audiocs_set_outputs(void *arg, uint64_t val) 1607{ 1608 CS_ctrl_t *cc = arg; 1609 CS_state_t *state = cc->cc_state; 1610 1611 if ((val & ~(state->cs_omod)) != 1612 (state->cs_omask & ~state->cs_omod)) 1613 return (EINVAL); 1614 1615 mutex_enter(&state->cs_lock); 1616 cc->cc_val = val; 1617 audiocs_configure_output(state); 1618 mutex_exit(&state->cs_lock); 1619 1620 return (0); 1621} 1622 1623/* 1624 * audiocs_set_mgain() 1625 * 1626 * Description: 1627 * Set the monitor gain. 1628 * 1629 * Arguments: 1630 * void *arg The device's state structure 1631 * uint64_t val The gain to set (monoaural).) 1632 * 1633 * Returns: 1634 * 0 The Codec parameter has been set 1635 */ 1636static int 1637audiocs_set_mgain(void *arg, uint64_t gain) 1638{ 1639 CS_ctrl_t *cc = arg; 1640 CS_state_t *state = cc->cc_state; 1641 1642 if (gain > 100) 1643 return (EINVAL); 1644 1645 mutex_enter(&state->cs_lock); 1646 cc->cc_val = gain; 1647 audiocs_configure_output(state); 1648 mutex_exit(&state->cs_lock); 1649 1650 return (0); 1651} 1652 1653/* 1654 * audiocs_open() 1655 * 1656 * Description: 1657 * Opens a DMA engine for use. 1658 * 1659 * Arguments: 1660 * void *arg The DMA engine to set up 1661 * int flag Open flags 1662 * unsigned *nframesp Receives number of frames 1663 * caddr_t *bufp Receives kernel data buffer 1664 * 1665 * Returns: 1666 * 0 on success 1667 * errno on failure 1668 */ 1669static int 1670audiocs_open(void *arg, int flag, unsigned *nframesp, caddr_t *bufp) 1671{ 1672 CS_engine_t *eng = arg; 1673 CS_state_t *state = eng->ce_state; 1674 dev_info_t *dip = state->cs_dip; 1675 1676 _NOTE(ARGUNUSED(flag)); 1677 1678 (void) pm_busy_component(dip, CS4231_COMPONENT); 1679 if (pm_raise_power(dip, CS4231_COMPONENT, CS4231_PWR_ON) == 1680 DDI_FAILURE) { 1681 1682 /* match the busy call above */ 1683 (void) pm_idle_component(dip, CS4231_COMPONENT); 1684 1685 audio_dev_warn(state->cs_adev, "power up failed"); 1686 } 1687 1688 eng->ce_count = 0; 1689 *nframesp = CS4231_NFRAMES; 1690 *bufp = eng->ce_kaddr; 1691 1692 return (0); 1693} 1694 1695/* 1696 * audiocs_close() 1697 * 1698 * Description: 1699 * Closes an audio DMA engine that was previously opened. Since 1700 * nobody is using it, we take this opportunity to possibly power 1701 * down the entire device. 1702 * 1703 * Arguments: 1704 * void *arg The DMA engine to shut down 1705 */ 1706static void 1707audiocs_close(void *arg) 1708{ 1709 CS_engine_t *eng = arg; 1710 CS_state_t *state = eng->ce_state; 1711 1712 (void) pm_idle_component(state->cs_dip, CS4231_COMPONENT); 1713} 1714 1715/* 1716 * audiocs_stop() 1717 * 1718 * Description: 1719 * This is called by the framework to stop an engine that is 1720 * transferring data. 1721 * 1722 * Arguments: 1723 * void *arg The DMA engine to stop 1724 */ 1725static void 1726audiocs_stop(void *arg) 1727{ 1728 CS_engine_t *eng = arg; 1729 CS_state_t *state = eng->ce_state; 1730 ddi_acc_handle_t handle = CODEC_HANDLE; 1731 1732 mutex_enter(&state->cs_lock); 1733 /* 1734 * Stop the DMA engine. 1735 */ 1736 CS4231_DMA_STOP(state, eng); 1737 1738 /* 1739 * Stop the codec. 1740 */ 1741 SELIDX(state, INTC_REG); 1742 ANDIDX(state, ~(eng->ce_codec_en), INTC_VALID_MASK); 1743 mutex_exit(&state->cs_lock); 1744} 1745 1746/* 1747 * audiocs_start() 1748 * 1749 * Description: 1750 * This is called by the framework to start an engine transferring data. 1751 * 1752 * Arguments: 1753 * void *arg The DMA engine to start 1754 * 1755 * Returns: 1756 * 0 on success, an errno otherwise 1757 */ 1758static int 1759audiocs_start(void *arg) 1760{ 1761 CS_engine_t *eng = arg; 1762 CS_state_t *state = eng->ce_state; 1763 ddi_acc_handle_t handle = CODEC_HANDLE; 1764 uint8_t mask; 1765 uint8_t value; 1766 uint8_t reg; 1767 int rv; 1768 1769 mutex_enter(&state->cs_lock); 1770 1771 if (eng->ce_num == CS4231_PLAY) { 1772 /* sample rate only set on play side */ 1773 value = FS_48000 | PDF_STEREO | PDF_LINEAR16NE; 1774 reg = FSDF_REG; 1775 mask = FSDF_VALID_MASK; 1776 } else { 1777 value = CDF_STEREO | CDF_LINEAR16NE; 1778 reg = CDF_REG; 1779 mask = CDF_VALID_MASK; 1780 } 1781 eng->ce_curoff = 0; 1782 eng->ce_curidx = 0; 1783 1784 SELIDX(state, reg | IAR_MCE); 1785 PUTIDX(state, value, mask); 1786 1787 if (audiocs_poll_ready(state) != DDI_SUCCESS) { 1788 rv = EIO; 1789 } else if (CS4231_DMA_START(state, eng) != DDI_SUCCESS) { 1790 rv = EIO; 1791 } else { 1792 /* 1793 * Start the codec. 1794 */ 1795 SELIDX(state, INTC_REG); 1796 ORIDX(state, eng->ce_codec_en, INTC_VALID_MASK); 1797 rv = 0; 1798 } 1799 1800 mutex_exit(&state->cs_lock); 1801 return (rv); 1802} 1803 1804/* 1805 * audiocs_format() 1806 * 1807 * Description: 1808 * Called by the framework to query the format of the device. 1809 * 1810 * Arguments: 1811 * void *arg The DMA engine to query 1812 * 1813 * Returns: 1814 * AUDIO_FORMAT_S16_NE 1815 */ 1816static int 1817audiocs_format(void *arg) 1818{ 1819 _NOTE(ARGUNUSED(arg)); 1820 1821 return (AUDIO_FORMAT_S16_NE); 1822} 1823 1824/* 1825 * audiocs_channels() 1826 * 1827 * Description: 1828 * Called by the framework to query the channels of the device. 1829 * 1830 * Arguments: 1831 * void *arg The DMA engine to query 1832 * 1833 * Returns: 1834 * 2 (stereo) 1835 */ 1836static int 1837audiocs_channels(void *arg) 1838{ 1839 _NOTE(ARGUNUSED(arg)); 1840 1841 return (2); 1842} 1843 1844/* 1845 * audiocs_rates() 1846 * 1847 * Description: 1848 * Called by the framework to query the sample rate of the device. 1849 * 1850 * Arguments: 1851 * void *arg The DMA engine to query 1852 * 1853 * Returns: 1854 * 48000 1855 */ 1856static int 1857audiocs_rate(void *arg) 1858{ 1859 _NOTE(ARGUNUSED(arg)); 1860 1861 return (48000); 1862} 1863 1864/* 1865 * audiocs_count() 1866 * 1867 * Description: 1868 * This is called by the framework to get the engine's frame counter 1869 * 1870 * Arguments: 1871 * void *arg The DMA engine to query 1872 * 1873 * Returns: 1874 * frame count for current engine 1875 */ 1876static uint64_t 1877audiocs_count(void *arg) 1878{ 1879 CS_engine_t *eng = arg; 1880 CS_state_t *state = eng->ce_state; 1881 uint64_t val; 1882 uint32_t off; 1883 1884 mutex_enter(&state->cs_lock); 1885 1886 off = CS4231_DMA_ADDR(state, eng); 1887 ASSERT(off >= eng->ce_paddr); 1888 off -= eng->ce_paddr; 1889 1890 /* 1891 * Every now and then, we get a value that is just a wee bit 1892 * too large. This seems to be a small value related to 1893 * prefetch. Rather than believe it, we just assume the last 1894 * offset in the buffer. This should allow us to handle 1895 * wraps, but without inserting bogus sample counts. 1896 */ 1897 if (off >= CS4231_BUFSZ) { 1898 off = CS4231_BUFSZ - 4; 1899 } 1900 1901 off /= 4; 1902 1903 val = (off >= eng->ce_curoff) ? 1904 off - eng->ce_curoff : 1905 off + CS4231_NFRAMES - eng->ce_curoff; 1906 1907 eng->ce_count += val; 1908 eng->ce_curoff = off; 1909 val = eng->ce_count; 1910 1911 /* while here, possibly reload the next address */ 1912 CS4231_DMA_RELOAD(state, eng); 1913 mutex_exit(&state->cs_lock); 1914 1915 return (val); 1916} 1917 1918/* 1919 * audiocs_sync() 1920 * 1921 * Description: 1922 * This is called by the framework to synchronize DMA caches. 1923 * 1924 * Arguments: 1925 * void *arg The DMA engine to sync 1926 */ 1927static void 1928audiocs_sync(void *arg, unsigned nframes) 1929{ 1930 CS_engine_t *eng = arg; 1931 _NOTE(ARGUNUSED(nframes)); 1932 1933 (void) ddi_dma_sync(eng->ce_dmah, 0, 0, eng->ce_syncdir); 1934} 1935 1936/* 1937 * audiocs_alloc_engine() 1938 * 1939 * Description: 1940 * Allocates the DMA handles and the memory for the DMA engine. 1941 * 1942 * Arguments: 1943 * CS_state_t *dip Pointer to the device's soft state 1944 * int num Engine number, CS4231_PLAY or CS4231_REC. 1945 * 1946 * Returns: 1947 * DDI_SUCCESS Engine initialized. 1948 * DDI_FAILURE Engine not initialized. 1949 */ 1950int 1951audiocs_alloc_engine(CS_state_t *state, int num) 1952{ 1953 unsigned caps; 1954 int dir; 1955 int rc; 1956 audio_dev_t *adev; 1957 dev_info_t *dip; 1958 CS_engine_t *eng; 1959 uint_t ccnt; 1960 ddi_dma_cookie_t dmac; 1961 size_t bufsz; 1962 1963 static ddi_device_acc_attr_t buf_attr = { 1964 DDI_DEVICE_ATTR_V0, 1965 DDI_NEVERSWAP_ACC, 1966 DDI_STRICTORDER_ACC 1967 }; 1968 1969 adev = state->cs_adev; 1970 dip = state->cs_dip; 1971 1972 eng = kmem_zalloc(sizeof (*eng), KM_SLEEP); 1973 eng->ce_state = state; 1974 eng->ce_started = B_FALSE; 1975 eng->ce_num = num; 1976 1977 switch (num) { 1978 case CS4231_REC: 1979 dir = DDI_DMA_READ; 1980 caps = ENGINE_INPUT_CAP; 1981 eng->ce_syncdir = DDI_DMA_SYNC_FORKERNEL; 1982 eng->ce_codec_en = INTC_CEN; 1983 break; 1984 case CS4231_PLAY: 1985 dir = DDI_DMA_WRITE; 1986 caps = ENGINE_OUTPUT_CAP; 1987 eng->ce_syncdir = DDI_DMA_SYNC_FORDEV; 1988 eng->ce_codec_en = INTC_PEN; 1989 break; 1990 default: 1991 kmem_free(eng, sizeof (*eng)); 1992 audio_dev_warn(adev, "bad engine number (%d)!", num); 1993 return (DDI_FAILURE); 1994 } 1995 state->cs_engines[num] = eng; 1996 1997 /* allocate dma handle */ 1998 rc = ddi_dma_alloc_handle(dip, CS4231_DMA_ATTR(state), DDI_DMA_SLEEP, 1999 NULL, &eng->ce_dmah); 2000 if (rc != DDI_SUCCESS) { 2001 audio_dev_warn(adev, "ddi_dma_alloc_handle failed: %d", rc); 2002 return (DDI_FAILURE); 2003 } 2004 /* allocate DMA buffer */ 2005 rc = ddi_dma_mem_alloc(eng->ce_dmah, CS4231_BUFSZ, &buf_attr, 2006 DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &eng->ce_kaddr, 2007 &bufsz, &eng->ce_acch); 2008 if (rc == DDI_FAILURE) { 2009 audio_dev_warn(adev, "dma_mem_alloc failed"); 2010 return (DDI_FAILURE); 2011 } 2012 2013 /* bind DMA buffer */ 2014 rc = ddi_dma_addr_bind_handle(eng->ce_dmah, NULL, 2015 eng->ce_kaddr, CS4231_BUFSZ, dir | DDI_DMA_CONSISTENT, 2016 DDI_DMA_SLEEP, NULL, &dmac, &ccnt); 2017 if ((rc != DDI_DMA_MAPPED) || (ccnt != 1)) { 2018 audio_dev_warn(adev, 2019 "ddi_dma_addr_bind_handle failed: %d", rc); 2020 return (DDI_FAILURE); 2021 } 2022 2023 eng->ce_paddr = dmac.dmac_address; 2024 2025 eng->ce_engine = audio_engine_alloc(&audiocs_engine_ops, caps); 2026 if (eng->ce_engine == NULL) { 2027 audio_dev_warn(adev, "audio_engine_alloc failed"); 2028 return (DDI_FAILURE); 2029 } 2030 2031 audio_engine_set_private(eng->ce_engine, eng); 2032 audio_dev_add_engine(adev, eng->ce_engine); 2033 return (DDI_SUCCESS); 2034} 2035 2036/* 2037 * audiocs_free_engine() 2038 * 2039 * Description: 2040 * This routine fress the engine and all associated resources. 2041 * 2042 * Arguments: 2043 * CS_engine_t *eng Engine to free. 2044 */ 2045void 2046audiocs_free_engine(CS_engine_t *eng) 2047{ 2048 CS_state_t *state = eng->ce_state; 2049 audio_dev_t *adev = state->cs_adev; 2050 2051 if (eng == NULL) 2052 return; 2053 if (eng->ce_engine) { 2054 audio_dev_remove_engine(adev, eng->ce_engine); 2055 audio_engine_free(eng->ce_engine); 2056 } 2057 if (eng->ce_paddr) { 2058 (void) ddi_dma_unbind_handle(eng->ce_dmah); 2059 } 2060 if (eng->ce_acch) { 2061 ddi_dma_mem_free(&eng->ce_acch); 2062 } 2063 if (eng->ce_dmah) { 2064 ddi_dma_free_handle(&eng->ce_dmah); 2065 } 2066 kmem_free(eng, sizeof (*eng)); 2067} 2068 2069/* 2070 * audiocs_poll_ready() 2071 * 2072 * Description: 2073 * This routine waits for the Codec to complete its initialization 2074 * sequence and is done with its autocalibration. 2075 * 2076 * Early versions of the Codec have a bug that can take as long as 2077 * 15 seconds to complete its initialization. For these cases we 2078 * use a timeout mechanism so we don't keep the machine locked up. 2079 * 2080 * Arguments: 2081 * CS_state_t *state The device's state structure 2082 * 2083 * Returns: 2084 * DDI_SUCCESS The Codec is ready to continue 2085 * DDI_FAILURE The Codec isn't ready to continue 2086 */ 2087int 2088audiocs_poll_ready(CS_state_t *state) 2089{ 2090 ddi_acc_handle_t handle = CODEC_HANDLE; 2091 int x = 0; 2092 uint8_t iar; 2093 uint8_t idr; 2094 2095 ASSERT(state->cs_regs != NULL); 2096 ASSERT(handle != NULL); 2097 2098 /* wait for the chip to initialize itself */ 2099 iar = ddi_get8(handle, &CS4231_IAR); 2100 2101 while ((iar & IAR_INIT) && x++ < CS4231_TIMEOUT) { 2102 drv_usecwait(50); 2103 iar = ddi_get8(handle, &CS4231_IAR); 2104 } 2105 2106 if (x >= CS4231_TIMEOUT) { 2107 return (DDI_FAILURE); 2108 } 2109 2110 x = 0; 2111 2112 /* 2113 * Now wait for the chip to complete its autocalibration. 2114 * Set the test register. 2115 */ 2116 SELIDX(state, ESI_REG); 2117 2118 idr = ddi_get8(handle, &CS4231_IDR); 2119 2120 while ((idr & ESI_ACI) && x++ < CS4231_TIMEOUT) { 2121 drv_usecwait(50); 2122 idr = ddi_get8(handle, &CS4231_IDR); 2123 } 2124 2125 if (x >= CS4231_TIMEOUT) { 2126 return (DDI_FAILURE); 2127 } 2128 2129 2130 return (DDI_SUCCESS); 2131 2132} 2133 2134/* 2135 * audiocs_sel_index() 2136 * 2137 * Description: 2138 * Select a cs4231 register. The cs4231 has a hardware bug where a 2139 * register is not always selected the first time. We try and try 2140 * again until the proper register is selected or we time out and 2141 * print an error message. 2142 * 2143 * Arguments: 2144 * audiohdl_t ahandle Handle to this device 2145 * ddi_acc_handle_t handle A handle to the device's registers 2146 * uint8_t addr The register address to program 2147 * int reg The register to select 2148 */ 2149void 2150#ifdef DEBUG 2151audiocs_sel_index(CS_state_t *state, uint8_t reg, int n) 2152#else 2153audiocs_sel_index(CS_state_t *state, uint8_t reg) 2154#endif 2155{ 2156 int x; 2157 uint8_t T; 2158 ddi_acc_handle_t handle = CODEC_HANDLE; 2159 uint8_t *addr = &CS4231_IAR; 2160 2161 for (x = 0; x < CS4231_RETRIES; x++) { 2162 ddi_put8(handle, addr, reg); 2163 T = ddi_get8(handle, addr); 2164 if (T == reg) { 2165 break; 2166 } 2167 drv_usecwait(1000); 2168 } 2169 2170 if (x == CS4231_RETRIES) { 2171 audio_dev_warn(state->cs_adev, 2172#ifdef DEBUG 2173 "line %d: Couldn't select index (0x%02x 0x%02x)", n, 2174#else 2175 "Couldn't select index (0x%02x 0x%02x)", 2176#endif 2177 T, reg); 2178 audio_dev_warn(state->cs_adev, 2179 "audio may not work correctly until it is stopped and " 2180 "restarted"); 2181 } 2182} 2183 2184/* 2185 * audiocs_put_index() 2186 * 2187 * Description: 2188 * Program a cs4231 register. The cs4231 has a hardware bug where a 2189 * register is not programmed properly the first time. We program a value, 2190 * then immediately read back the value and reprogram if nescessary. 2191 * We do this until the register is properly programmed or we time out and 2192 * print an error message. 2193 * 2194 * Arguments: 2195 * CS_state_t state Handle to this device 2196 * uint8_t mask Mask to not set reserved register bits 2197 * int val The value to program 2198 */ 2199void 2200#ifdef DEBUG 2201audiocs_put_index(CS_state_t *state, uint8_t val, uint8_t mask, int n) 2202#else 2203audiocs_put_index(CS_state_t *state, uint8_t val, uint8_t mask) 2204#endif 2205{ 2206 int x; 2207 uint8_t T; 2208 ddi_acc_handle_t handle = CODEC_HANDLE; 2209 uint8_t *addr = &CS4231_IDR; 2210 2211 val &= mask; 2212 2213 for (x = 0; x < CS4231_RETRIES; x++) { 2214 ddi_put8(handle, addr, val); 2215 T = ddi_get8(handle, addr); 2216 if (T == val) { 2217 break; 2218 } 2219 drv_usecwait(1000); 2220 } 2221 2222 if (x == CS4231_RETRIES) { 2223#ifdef DEBUG 2224 audio_dev_warn(state->cs_adev, 2225 "line %d: Couldn't set value (0x%02x 0x%02x)", n, T, val); 2226#else 2227 audio_dev_warn(state->cs_adev, 2228 "Couldn't set value (0x%02x 0x%02x)", T, val); 2229#endif 2230 audio_dev_warn(state->cs_adev, 2231 "audio may not work correctly until it is stopped and " 2232 "restarted"); 2233 } 2234} 2235