diff options
author | fl147353 <none@none> | 2006-09-07 22:40:17 -0700 |
---|---|---|
committer | fl147353 <none@none> | 2006-09-07 22:40:17 -0700 |
commit | c525fe66b825cb3e34dd0431b0ef810f2b209048 (patch) | |
tree | 013dc7e9884ad7a84c1eb274e2a4d3336c06dc9e /usr/src | |
parent | 2eaf37a59c94a691cd7df7e3f167779057f92caf (diff) | |
download | illumos-gate-c525fe66b825cb3e34dd0431b0ef810f2b209048.tar.gz |
6456599 audioixp driver needs to move from usr/closed to usr/src
Diffstat (limited to 'usr/src')
-rw-r--r-- | usr/src/pkgdefs/etc/exception_list_sparc | 5 | ||||
-rw-r--r-- | usr/src/uts/common/Makefile.files | 2 | ||||
-rw-r--r-- | usr/src/uts/common/Makefile.rules | 7 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.c | 3389 | ||||
-rw-r--r-- | usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.conf | 79 | ||||
-rw-r--r-- | usr/src/uts/common/sys/Makefile | 1 | ||||
-rw-r--r-- | usr/src/uts/common/sys/audio/audioixp.h | 77 | ||||
-rw-r--r-- | usr/src/uts/common/sys/audio/impl/audioixp_impl.h | 459 | ||||
-rw-r--r-- | usr/src/uts/i86pc/Makefile.i86pc.shared | 2 | ||||
-rw-r--r-- | usr/src/uts/i86pc/audioixp/Makefile | 95 | ||||
-rw-r--r-- | usr/src/uts/sparc/Makefile.sparc.shared | 1 | ||||
-rw-r--r-- | usr/src/uts/sparc/audioixp/Makefile | 108 | ||||
-rw-r--r-- | usr/src/uts/sparc/audioixp/audioixp.wlcmd | 45 | ||||
-rw-r--r-- | usr/src/uts/sparc/audioixp/audioixp_with_sada.wlcmd | 130 | ||||
-rw-r--r-- | usr/src/uts/sparc/warlock/Makefile | 2 |
15 files changed, 4394 insertions, 8 deletions
diff --git a/usr/src/pkgdefs/etc/exception_list_sparc b/usr/src/pkgdefs/etc/exception_list_sparc index 96dc6e3a74..2eeae9517a 100644 --- a/usr/src/pkgdefs/etc/exception_list_sparc +++ b/usr/src/pkgdefs/etc/exception_list_sparc @@ -784,11 +784,6 @@ kernel/drv/audio810.conf sparc kernel/drv/sparcv9/audiohd sparc kernel/drv/audiohd.conf sparc # -# ATI IXP audio driver doesn't ship in sparc platform. -# -kernel/drv/sparcv9/audioixp sparc -kernel/drv/audioixp.conf sparc -# # VIA VT823x audio driver doesn't ship in sparc platform. # kernel/drv/sparcv9/audiovia823x sparc diff --git a/usr/src/uts/common/Makefile.files b/usr/src/uts/common/Makefile.files index 12f2e58e0d..b022fcd0c9 100644 --- a/usr/src/uts/common/Makefile.files +++ b/usr/src/uts/common/Makefile.files @@ -375,6 +375,8 @@ AUDIO810_OBJS += audio810.o AUDIOHD_OBJS += audiohd.o +AUDIOIXP_OBJS += audioixp.o + AUDIOTS_OBJS += audiots.o CARDBUS_OBJS += cardbus.o cardbus_hp.o cardbus_cfg.o diff --git a/usr/src/uts/common/Makefile.rules b/usr/src/uts/common/Makefile.rules index fcfce9adb2..69e32b7ee5 100644 --- a/usr/src/uts/common/Makefile.rules +++ b/usr/src/uts/common/Makefile.rules @@ -440,6 +440,10 @@ $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/audio/sada/drv/audiohd/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) +$(OBJS_DIR)/%.o: $(UTSBASE)/common/io/audio/sada/drv/audioixp/%.c + $(COMPILE.c) -o $@ $< + $(CTFCONVERT_O) + $(OBJS_DIR)/%.o: $(UTSBASE)/common/io/audio/sada/drv/audiots/%.c $(COMPILE.c) -o $@ $< $(CTFCONVERT_O) @@ -1184,6 +1188,9 @@ $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/sada/drv/audio810/%.c $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/sada/drv/audiohd/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) +$(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/sada/drv/audioixp/%.c + @($(LHEAD) $(LINT.c) $< $(LTAIL)) + $(LINTS_DIR)/%.ln: $(UTSBASE)/common/io/audio/sada/drv/audiots/%.c @($(LHEAD) $(LINT.c) $< $(LTAIL)) diff --git a/usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.c b/usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.c new file mode 100644 index 0000000000..2dbca76b43 --- /dev/null +++ b/usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.c @@ -0,0 +1,3389 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * audioixp Audio Driver + * + * This driver supports audio hardware integrated in ATI IXP400 chipset. + * + * This driver uses the mixer Audio Personality Module to implement + * audio(7I) and mixer(7I) semantics. Both play and record are single + * streaming. + * + * The IXP400 audio core is an AC'97 controller, which has independent + * channels for PCM in, PCM out. The AC'97 controller is a PCI bus master + * with scatter/gather support. Each channel has a DMA engine. Currently, + * we use only the PCM in and PCM out channels. Each DMA engine uses one + * buffer descriptor list. And the buffer descriptor list is an array + * of up to 32 entries, each of which describes a data buffer. You dont need + * to use all these entries. Each entry contains a pointer to a data buffer, + * status, length of the buffer being pointed to and the pointer to the next + * entry. Length of the buffer is in number of bytes. Interrupt will be + * triggered each time a entry is processed by hardware. + * + * We use the BD list (buffer descriptor list) as a round-robin FIFO. + * Both the software and hardware loop around the BD list. For playback, + * the software writes to the buffers pointed by the BD entries of BD + * list, and the hardware sends the data in the buffers out. For record, + * the process is reversed. So we define the struct, audioixp_sample_buf, + * to handle BD. The software uses the head, tail and avail fields of + * this structure to manipulate the FIFO. The head field indicates the + * first valid BD hardware can manipulate. The tail field indicates the + * BD after the last valid BD. And the avail field indicates how many + * buffers are available. Two DMA buffers are allocated for both playback + * and record, and two BD entries are used. When processing interrupt, + * the current hardware pointer will be check to tell which buffer is + * being processed. It's possible for the hardware to interrupt twice + * for one buffer, this logic is handled in the routine + * audioixp_chunk_processed. + * + * Every time we program AC97 codec, we save the value in codec_shadow[]. + * This means that register state information is saved for power management + * shutdown (we'll support this later). When the codec is started back up + * we use this saved state to restore codec's state in audioixp_chip_init(). + * + * System power management is not yet supported by the driver. + * + * NOTE: + * This driver depends on the misc/audiosup, misc/amsrc2 and + * misc/mixer modules being loaded first. + */ +#include <sys/types.h> +#include <sys/modctl.h> +#include <sys/kmem.h> +#include <sys/conf.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <sys/pci.h> +#include <sys/note.h> +#include <sys/audio.h> +#include <sys/audiovar.h> +#include <sys/audio/audio_trace.h> +#include <sys/audio/audio_support.h> +#include <sys/audio/audio_src.h> +#include <sys/mixer.h> +#include <sys/audio/audio_mixer.h> +#include <sys/audio/am_src2.h> +#include <sys/audio/ac97.h> +#include <sys/audio/impl/audioixp_impl.h> +#include <sys/audio/audioixp.h> + +/* + * Module linkage routines for the kernel + */ +static int audioixp_getinfo(dev_info_t *, ddi_info_cmd_t, void*, void**); +static int audioixp_attach(dev_info_t *, ddi_attach_cmd_t); +static int audioixp_detach(dev_info_t *, ddi_detach_cmd_t); + +/* + * Entry point routine prototypes + */ +static int audioixp_ad_set_config(audiohdl_t, int, int, int, int, int); +static int audioixp_ad_set_format(audiohdl_t, int, int, int, int, int, int); +static int audioixp_ad_start_play(audiohdl_t, int); +static void audioixp_ad_pause_play(audiohdl_t, int); +static void audioixp_ad_stop_play(audiohdl_t, int); +static int audioixp_ad_start_record(audiohdl_t, int); +static void audioixp_ad_stop_record(audiohdl_t, int); + +/* + * interrupt handler + */ +static uint_t audioixp_intr(caddr_t); + +/* + * Local Routine Prototypes + */ +static int audioixp_codec_sync(audioixp_state_t *); +static int audioixp_write_ac97(audioixp_state_t *, int, uint16_t); +static int audioixp_read_ac97(audioixp_state_t *, int, uint16_t *); +static int audioixp_and_ac97(audioixp_state_t *, int, uint16_t); +static int audioixp_or_ac97(audioixp_state_t *, int, uint16_t); +static int audioixp_reset_ac97(audioixp_state_t *); +static int audioixp_init_state(audioixp_state_t *, dev_info_t *); +static int audioixp_map_regs(dev_info_t *, audioixp_state_t *); +static void audioixp_unmap_regs(audioixp_state_t *); +static int audioixp_alloc_sample_buf(audioixp_state_t *, int, int); +static void audioixp_free_sample_buf(audioixp_state_t *, + audioixp_sample_buf_t *); +static void audioixp_setup_bdl(audioixp_state_t *); +static void audioixp_start_dma(audioixp_state_t *, int); +static void audioixp_stop_dma(audioixp_state_t *, int); +static int audioixp_chip_init(audioixp_state_t *, int); +static void audioixp_chip_fini(audioixp_state_t *); +static int audioixp_chunk_processed(audioixp_state_t *, int); +static int audioixp_fill_play_buf(audioixp_state_t *); +static int audioixp_prepare_record_buf(audioixp_state_t *); +static void audioixp_reclaim_play_buf(audioixp_state_t *); +static void audioixp_reclaim_record_buf(audioixp_state_t *); +static int audioixp_set_gain(audioixp_state_t *, int, int, int); +static int audioixp_set_port(audioixp_state_t *, int, int); +static int audioixp_set_monitor_gain(audioixp_state_t *, int); + +/* + * Global variables, but used only by this file. + */ + +/* anchor for soft state structures */ +static void *audioixp_statep; + +/* driver name, so we don't have to call ddi_driver_name() or hard code strs */ +static char *audioixp_name = IXP_NAME; + +/* + * STREAMS structures + */ + +/* STREAMS driver id and limit value struct */ +static struct module_info audioixp_modinfo = { + IXP_IDNUM, /* module ID number */ + IXP_NAME, /* module name */ + IXP_MINPACKET, /* minimum packet size */ + IXP_MAXPACKET, /* maximum packet size */ + IXP_HIWATER, /* high water mark */ + IXP_LOWATER, /* low water mark */ +}; + +/* STREAMS queue processing procedures structures */ +/* read queue */ +static struct qinit audioixp_rqueue = { + audio_sup_rput, /* put procedure */ + audio_sup_rsvc, /* service procedure */ + audio_sup_open, /* open procedure */ + audio_sup_close, /* close procedure */ + NULL, /* unused */ + &audioixp_modinfo, /* module parameters */ + NULL /* module statistics */ +}; + +/* write queue */ +static struct qinit audioixp_wqueue = { + audio_sup_wput, /* write procedure */ + audio_sup_wsvc, /* service procedure */ + NULL, /* open procedure */ + NULL, /* close procedure */ + NULL, /* unused */ + &audioixp_modinfo, /* module parameters */ + NULL /* module statistics */ +}; + +/* STREAMS entity declaration structure */ +static struct streamtab audioixp_str_info = { + &audioixp_rqueue, /* read queue */ + &audioixp_wqueue, /* write queue */ + NULL, /* mux lower read queue */ + NULL, /* mux lower write queue */ +}; + +/* + * DDI Structures + */ + +/* Entry points structure */ +static struct cb_ops audioixp_cb_ops = { + nulldev, /* cb_open */ + nulldev, /* cb_close */ + nodev, /* cb_strategy */ + nodev, /* cb_print */ + nodev, /* cb_dump */ + nodev, /* cb_read */ + nodev, /* cb_write */ + nodev, /* cb_ioctl */ + nodev, /* cb_devmap */ + nodev, /* cb_mmap */ + nodev, /* cb_segmap */ + nochpoll, /* cb_chpoll */ + ddi_prop_op, /* cb_prop_op */ + &audioixp_str_info, /* cb_str */ + D_NEW | D_MP | D_64BIT, /* cb_flag */ + CB_REV, /* cb_rev */ + nodev, /* cb_aread */ + nodev, /* cb_awrite */ +}; + +/* Device operations structure */ +static struct dev_ops audioixp_dev_ops = { + DEVO_REV, /* devo_rev */ + 0, /* devo_refcnt */ + audioixp_getinfo, /* devo_getinfo */ + nulldev, /* devo_identify - obsolete */ + nulldev, /* devo_probe */ + audioixp_attach, /* devo_attach */ + audioixp_detach, /* devo_detach */ + nodev, /* devo_reset */ + &audioixp_cb_ops, /* devi_cb_ops */ + NULL, /* devo_bus_ops */ + NULL /* devo_power */ +}; + +/* Linkage structure for loadable drivers */ +static struct modldrv audioixp_modldrv = { + &mod_driverops, /* drv_modops */ + IXP_MOD_NAME" %I%", /* drv_linkinfo */ + &audioixp_dev_ops, /* drv_dev_ops */ +}; + +/* Module linkage structure */ +static struct modlinkage audioixp_modlinkage = { + MODREV_1, /* ml_rev */ + (void *)&audioixp_modldrv, /* ml_linkage */ + NULL /* NULL terminates the list */ +}; + +static uint_t audioixp_mixer_srs[] = { + IXP_SAMPR8000, IXP_SAMPR48000, 0 +}; + +static uint_t audioixp_min_compat_srs[] = { + IXP_SAMPR48000, 0 +}; + +static uint_t audioixp_compat_srs [] = { + IXP_SAMPR8000, IXP_SAMPR11025, IXP_SAMPR16000, + IXP_SAMPR22050, IXP_SAMPR24000, IXP_SAMPR32000, + IXP_SAMPR44100, IXP_SAMPR48000, 0 +}; + +static am_ad_sample_rates_t audioixp_mixer_sample_rates = { + MIXER_SRS_FLAG_SR_LIMITS, + audioixp_mixer_srs +}; + +static am_ad_sample_rates_t audioixp_compat_sample_rates = { + MIXER_SRS_FLAG_SR_NOT_LIMITS, + audioixp_compat_srs +}; + +/* Some codec, only support 48K sample rate */ +static am_ad_sample_rates_t audioixp_min_compat_sample_rates = { + MIXER_SRS_FLAG_SR_NOT_LIMITS, + audioixp_min_compat_srs +}; + +static uint_t audioixp_channels[] = { + AUDIO_CHANNELS_STEREO, + 0 +}; + +static am_ad_cap_comb_t audioixp_combinations[] = { + { AUDIO_PRECISION_16, AUDIO_ENCODING_LINEAR }, + { 0 } +}; + +/* + * device access attributes for register mapping + */ +static struct ddi_device_acc_attr dev_attr = { + DDI_DEVICE_ATTR_V0, + DDI_STRUCTURE_LE_ACC, + DDI_STRICTORDER_ACC +}; + +/* + * DMA attributes of buffer descriptor list + */ +static ddi_dma_attr_t bdlist_dma_attr = { + DMA_ATTR_V0, /* version */ + 0, /* addr_lo */ + 0xffffffff, /* addr_hi */ + 0x0000ffff, /* count_max */ + 8, /* align, BDL must be aligned on a 8-byte boundary */ + 0x3c, /* burstsize */ + 8, /* minxfer, set to the size of a BDlist entry */ + 0x0000ffff, /* maxxfer */ + 0x00000fff, /* seg, set to the RAM pagesize of intel platform */ + 1, /* sgllen, there's no scatter-gather list */ + 8, /* granular, set to the value of minxfer */ + 0 /* flags, use virtual address */ +}; + +/* + * DMA attributes of buffers to be used to receive/send audio data + */ +static ddi_dma_attr_t sample_buf_dma_attr = { + DMA_ATTR_V0, + 0, /* addr_lo */ + 0xffffffff, /* addr_hi */ + 0x0001fffe, /* count_max */ + 2, /* align, data buffer is aligned on a 2-byte boundary */ + 0x3c, /* burstsize */ + 4, /* minxfer, set to the size of a sample data */ + 0x0001ffff, /* maxxfer */ + 0x0001ffff, /* seg */ + 1, /* sgllen, no scatter-gather */ + 4, /* granular, set to the value of minxfer */ + 0, /* flags, use virtual address */ +}; + +static am_ad_entry_t audioixp_entry = { + NULL, /* ad_setup() */ + NULL, /* ad_teardown() */ + audioixp_ad_set_config, /* ad_set_config() */ + audioixp_ad_set_format, /* ad_set_format() */ + audioixp_ad_start_play, /* ad_start_play() */ + audioixp_ad_pause_play, /* ad_pause_play() */ + audioixp_ad_stop_play, /* ad_stop_play() */ + audioixp_ad_start_record, /* ad_start_record() */ + audioixp_ad_stop_record, /* ad_stop_record() */ + NULL, /* ad_ioctl() */ + NULL /* ad_iocdata() */ +}; + +/* + * _init() + * + * Description: + * Driver initialization, called when driver is first loaded. + * This is how access is initially given to all the static structures. + * + * Arguments: + * None + * + * Returns: + * ddi_soft_state_init() status, see ddi_soft_state_init(9f), or + * mod_install() status, see mod_install(9f) + */ +int +_init(void) +{ + int error; + + ATRACE("in audioixp _init()", NULL); + + if ((error = ddi_soft_state_init(&audioixp_statep, + sizeof (audioixp_state_t), 1)) != 0) { + ATRACE("audioixp ddi_soft_state_init() failed", + audioixp_statep); + return (error); + } + + if ((error = mod_install(&audioixp_modlinkage)) != 0) { + ddi_soft_state_fini(&audioixp_statep); + } + + ATRACE("audioixp _init() audioixp_statep", audioixp_statep); + ATRACE("audioixp _init() returning", error); + + return (error); + +} /* _init() */ + +/* + * _fini() + * + * Description: + * Module de-initialization, called when the driver is to be unloaded. + * + * Arguments: + * None + * + * Returns: + * mod_remove() status, see mod_remove(9f) + */ +int +_fini(void) +{ + int error; + + ATRACE("in audioixp _fini()", audioixp_statep); + + if ((error = mod_remove(&audioixp_modlinkage)) != 0) { + return (error); + } + + ddi_soft_state_fini(&audioixp_statep); + + ATRACE_32("audioixp _fini() returning", error); + + return (0); + +} /* _fini() */ + +/* + * _info() + * + * Description: + * Module information, returns information about the driver. + * + * Arguments: + * modinfo *modinfop Pointer to the opaque modinfo structure + * + * Returns: + * mod_info() status, see mod_info(9f) + */ +int +_info(struct modinfo *modinfop) +{ + int error; + + ATRACE("in audioixp _info()", NULL); + + error = mod_info(&audioixp_modlinkage, modinfop); + + ATRACE_32("audioixp _info() returning", error); + + return (error); + +} /* _info() */ + + +/* ******************* Driver Entry Points ********************************* */ + +/* + * audioixp_getinfo() + */ +static int +audioixp_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, + void *arg, void **result) +{ + audioixp_state_t *state; + int instance; + int error; + + error = DDI_FAILURE; + ATRACE("in audioixp_getinfo()", dip); + + switch (cmd) { + case DDI_INFO_DEVT2DEVINFO: + instance = audio_sup_devt_to_instance((dev_t)arg); + if ((state = ddi_get_soft_state(audioixp_statep, + instance)) != NULL) { + *result = state->dip; + error = DDI_SUCCESS; + } else { + *result = NULL; + } + break; + + case DDI_INFO_DEVT2INSTANCE: + *result = (void*)(uintptr_t) + audio_sup_devt_to_instance((dev_t)arg); + error = DDI_SUCCESS; + break; + + default: + break; + } + + return (error); + +} /* audioixp_getinfo() */ + +/* + * audioixp_attach() + * + * Description: + * Attach an instance of the audioixp driver. This routine does + * the device dependent attach tasks. When it is completed, it calls + * audio_sup_register() and am_attach() so they may do their work. + * + * NOTE: mutex_init() no longer needs a name string, so set + * to NULL to save kernel space. + * + * 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 +audioixp_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) +{ + int instance; + uint16_t cmdeg; + audioixp_state_t *statep; + audio_sup_reg_data_t data; + + ATRACE("in audioixp_attach()", dip); + + instance = ddi_get_instance(dip); + + ATRACE("audioixp_attach() audioixp_statep", + audioixp_statep); + + switch (cmd) { + case DDI_ATTACH: + break; + + /* + * now, no suspend/resume supported. we'll do it in the future. + */ + case DDI_RESUME: + ATRACE("audioixp_attach() DDI_RESUME", NULL); + audio_sup_log(NULL, CE_WARN, + "%s%d: audioixp_attach() resume is not supported yet", + audioixp_name, instance); + return (DDI_FAILURE); + + default: + audio_sup_log(NULL, CE_WARN, + "!%s%d: audioixp_attach() unknown command: 0x%x", + audioixp_name, instance, cmd); + return (DDI_FAILURE); + } + + /* we don't support high level interrupts in the driver */ + if (ddi_intr_hilevel(dip, 0) != 0) { + audio_sup_log(NULL, CE_WARN, + "!%s%d: audioixp_attach()" + " unsupported high level interrupt", + audioixp_name, instance); + return (DDI_FAILURE); + } + + /* allocate the soft state structure */ + if (ddi_soft_state_zalloc(audioixp_statep, instance) != + DDI_SUCCESS) { + audio_sup_log(NULL, CE_WARN, + "!%s%d: audioixp_attach() soft state allocate failed", + audioixp_name, instance); + return (DDI_FAILURE); + } + + if ((statep = ddi_get_soft_state(audioixp_statep, instance)) == + NULL) { + audio_sup_log(NULL, CE_WARN, + "!%s%d: audioixp_attach() soft state failed", + audioixp_name, instance); + goto error_state; + } + + data.asrd_version = AUDIOSUP_VERSION; + data.asrd_key = NULL; + if ((statep->audio_handle = audio_sup_register(dip, &data)) == NULL) { + audio_sup_log(NULL, CE_WARN, + "!%s%d: audioixp_attach() audio_sup_register() failed", + audioixp_name, instance); + goto error_state; + } + + /* save private state */ + audio_sup_set_private(statep->audio_handle, statep); + + if ((audioixp_init_state(statep, dip)) != AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() init state structure failed"); + goto error_audiosup; + } + + /* map in the registers, allocate DMA buffers, etc. */ + if (audioixp_map_regs(dip, statep) != AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() couldn't map registers"); + goto error_destroy; + } + + /* set PCI command register */ + cmdeg = pci_config_get16(statep->pci_conf_handle, PCI_CONF_COMM); + pci_config_put16(statep->pci_conf_handle, PCI_CONF_COMM, + cmdeg | PCI_COMM_IO | PCI_COMM_MAE); + + if (audioixp_alloc_sample_buf(statep, IXP_DMA_PCM_OUT, + statep->play_buf_size) == AUDIO_FAILURE) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() couldn't allocate play sample " + "buffers"); + goto error_unmap; + } + + if (audioixp_alloc_sample_buf(statep, IXP_DMA_PCM_IN, + statep->record_buf_size) == AUDIO_FAILURE) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() couldn't allocate record sample " + "buffers"); + goto error_dealloc_play; + } + + audioixp_setup_bdl(statep); + + /* set up kernel statistics */ + if ((statep->ixp_ksp = kstat_create(IXP_NAME, instance, + IXP_NAME, "controller", KSTAT_TYPE_INTR, 1, + KSTAT_FLAG_PERSISTENT)) != NULL) { + kstat_install(statep->ixp_ksp); + } + + /* set up the interrupt handler */ + if (ddi_add_intr(dip, 0, &statep->intr_iblock, + (ddi_idevice_cookie_t *)NULL, audioixp_intr, (caddr_t)statep) != + DDI_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() bad interrupt specification "); + goto error_kstat; + } + + if (audioixp_chip_init(statep, IXP_INIT_NO_RESTORE) != + AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() failed to init chip"); + goto error_intr; + } + + /* call the mixer attach() routine */ + if (am_attach(statep->audio_handle, cmd, &statep->ad_info) != + AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_attach() am_attach() failed"); + goto error_intr; + } + + ddi_report_dev(dip); + + return (DDI_SUCCESS); + +error_intr: + ddi_remove_intr(dip, 0, statep->intr_iblock); + +error_kstat: + if (statep->ixp_ksp) { + kstat_delete(statep->ixp_ksp); + } + +error_dealloc: + audioixp_free_sample_buf(statep, &statep->record_buf); + +error_dealloc_play: + audioixp_free_sample_buf(statep, &statep->play_buf); + +error_unmap: + audioixp_unmap_regs(statep); + +error_destroy: + ATRACE("audioixp_attach() error_destroy", statep); + mutex_destroy(&statep->inst_lock); + +error_audiosup: + ATRACE("audioixp_attach() error_audiosup", statep); + (void) audio_sup_unregister(statep->audio_handle); + +error_state: + ATRACE("audioixp_attach() error_state", statep); + ddi_soft_state_free(audioixp_statep, instance); + + ATRACE("audioixp_attach() returning failure", NULL); + + return (DDI_FAILURE); + +} /* audioixp_attach() */ + +/* + * audioixp_detach() + * + * Description: + * Detach an instance of the audioixp driver. After the Codec is + * detached, we call am_detach() and audio_sup_register() so they may + * do their work. + * + * Arguments: + * dev_info_t *dip Pointer to the device's dev_info struct + * ddi_detach_cmd_t cmd Detach command + * + * Returns: + * DDI_SUCCESS The driver was detached + * DDI_FAILURE The driver couldn't be detached + */ +static int +audioixp_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) +{ + audioixp_state_t *statep; + int instance; + + instance = ddi_get_instance(dip); + + ATRACE_32("audioixp_detach() instance", instance); + ATRACE("audioixp_detach() audioixp_statep", + audioixp_statep); + + if ((statep = ddi_get_soft_state(audioixp_statep, instance)) == + NULL) { + audio_sup_log(NULL, CE_WARN, + "!%s%d: audioixp_detach() get soft state failed", + audioixp_name, instance); + return (DDI_FAILURE); + } + + switch (cmd) { + case DDI_DETACH: + break; + + /* + * now, no suspend/resume supported. we'll do it in the future. + */ + case DDI_SUSPEND: + ATRACE("audioixp_detach() SUSPEND", statep); + audio_sup_log(statep->audio_handle, CE_WARN, + "audioixp_detach() suspend is not supported yet"); + return (DDI_FAILURE); + + default: + ATRACE("audioixp_detach() unknown command", cmd); + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_detach() unknown command: 0x%x", cmd); + return (DDI_FAILURE); + } + + audioixp_chip_fini(statep); + + /* stop DMA engines */ + mutex_enter(&statep->inst_lock); + audioixp_stop_dma(statep, AUDIO_PLAY); + audioixp_stop_dma(statep, AUDIO_RECORD); + mutex_exit(&statep->inst_lock); + + /* remove the interrupt handler */ + ddi_remove_intr(dip, 0, statep->intr_iblock); + + /* free DMA memory */ + audioixp_free_sample_buf(statep, &statep->play_buf); + audioixp_free_sample_buf(statep, &statep->record_buf); + + /* free the kernel statistics structure */ + if (statep->ixp_ksp) { + kstat_delete(statep->ixp_ksp); + } + + /* detach audio mixer */ + (void) am_detach(statep->audio_handle, cmd); + + /* + * call the audio support module's detach routine to remove this + * driver completely from the audio driver architecture. + */ + (void) audio_sup_unregister(statep->audio_handle); + + mutex_destroy(&statep->inst_lock); + + audioixp_unmap_regs(statep); + + ddi_soft_state_free(audioixp_statep, instance); + + return (DDI_SUCCESS); + +} /* audioixp_detach */ + +/* + * audioixp_intr() + * + * Description: + * Interrupt service routine for both play and record. For play we + * get the next buffers worth of audio. For record we send it on to + * the mixer. + * + * There's a hardware pointer which indicate memory location where + * the hardware is processing. We check this pointer to decide whether + * to handle the buffer and how many buffers should be handled. + * Refer to ATI IXP400/450 Register Reference Manual, page 193,194. + * + * Arguments: + * caddr_t arg Pointer to the interrupting device's state + * structure + * + * Returns: + * DDI_INTR_CLAIMED Interrupt claimed and processed + * DDI_INTR_UNCLAIMED Interrupt not claimed, and thus ignored + */ +static uint_t +audioixp_intr(caddr_t arg) +{ + audioixp_state_t *statep; + uint32_t sr; + int intr_claimed = DDI_INTR_UNCLAIMED; + + statep = (audioixp_state_t *)arg; + mutex_enter(&statep->inst_lock); + + sr = IXP_AM_GET32(IXP_AUDIO_INT); + + /* PCM in interrupt */ + if (sr & IXP_AUDIO_INT_IN_DMA) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_IN_DMA); + + if (statep->flags & IXP_DMA_RECD_STARTED) { + audioixp_reclaim_record_buf(statep); + } + } + + /* PCM out interrupt */ + if (sr & IXP_AUDIO_INT_OUT_DMA) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_OUT_DMA); + + if (statep->flags & IXP_DMA_PLAY_STARTED) { + audioixp_reclaim_play_buf(statep); + (void) audioixp_fill_play_buf(statep); + } + } + + /* system is too busy to process the input stream, ignore it */ + if (sr & IXP_AUDIO_INT_IN_DMA_OVERFLOW) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_IN_DMA_OVERFLOW); + } + + /* System is too busy, ignore it */ + if (sr & IXP_AUDIO_INT_OUT_DMA_UNDERFLOW) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_OUT_DMA_UNDERFLOW); + } + + if (sr & IXP_AUDIO_INT_CODEC0_NOT_READY) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_CODEC0_NOT_READY); + statep -> ixp_codec_not_ready_bits |= + IXP_AUDIO_INT_CODEC0_NOT_READY; + } + + if (sr & IXP_AUDIO_INT_CODEC1_NOT_READY) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_CODEC1_NOT_READY); + statep -> ixp_codec_not_ready_bits |= + IXP_AUDIO_INT_CODEC1_NOT_READY; + } + + if (sr & IXP_AUDIO_INT_CODEC2_NOT_READY) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_CODEC2_NOT_READY); + statep -> ixp_codec_not_ready_bits |= + IXP_AUDIO_INT_CODEC2_NOT_READY; + } + + if (sr & IXP_AUDIO_INT_NEW_FRAME) { + intr_claimed = DDI_INTR_CLAIMED; + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AUDIO_INT_NEW_FRAME); + statep -> ixp_codec_not_ready_bits |= IXP_AUDIO_INT_NEW_FRAME; + } + + if (intr_claimed == DDI_INTR_UNCLAIMED) { + mutex_exit(&statep->inst_lock); + return (DDI_INTR_UNCLAIMED); + } + + /* update the kernel interrupt statistics */ + if (statep->ixp_ksp) { + IXP_KIOP(statep)->intrs[KSTAT_INTR_HARD]++; + } + + mutex_exit(&statep->inst_lock); + + ATRACE("audioixp_intr() done", statep); + + return (DDI_INTR_CLAIMED); + +} /* audioixp_intr() */ + +/* *********************** Mixer Entry Point Routines ******************* */ +/* + * audioixp_ad_set_config() + * + * Description: + * This routine is used to set new Codec parameters, except the data + * format which has it's own routine. If the Codec doesn't support a + * particular parameter and it is asked to set it then we return + * AUDIO_FAILURE. + * + * Arguments: + * audiohdl_t ahandle Handle to this device + * int stream Stream number for multi-stream Codecs, + * which is not how we program the device + * for now. + * int command The configuration to set + * int dir AUDIO_PLAY or AUDIO_RECORD, if + * direction is important + * int arg1 Argument #1 + * int arg2 Argument #2, not always needed + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The Codec parameter has not been set, + * or the parameter couldn't be set + */ +static int +audioixp_ad_set_config(audiohdl_t ahandle, int stream, int command, + int dir, int arg1, int arg2) +{ + audioixp_state_t *statep; + int rc = AUDIO_SUCCESS; + + ATRACE_32("audioixp_ad_set_config() stream", stream); + ATRACE_32("audioixp_ad_set_config() command", command); + ATRACE_32("audioixp_ad_set_config() dir", dir); + ATRACE_32("audioixp_ad_set_config() arg1", arg1); + ATRACE_32("audioixp_ad_set_config() arg2", arg2); + + /* get the soft state structure */ + statep = audio_sup_get_private(ahandle); + ASSERT(statep); + + mutex_enter(&statep->inst_lock); + switch (command) { + case AM_SET_GAIN: + /* + * Set the gain for a channel. The audio mixer calculates the + * impact, if any, of balance on gain. + * + * AUDIO_MIN_GAIN <= gain <= AUDIO_MAX_GAIN + * + * arg1 --> gain + * arg2 --> channel #, 0 == left, 1 == right + */ + rc = audioixp_set_gain(statep, dir, arg1, arg2); + break; + + case AM_SET_PORT: + /* + * Enable/disable the input or output ports. The audio mixer + * enforces exclusiveness of in ports, as well as which ports + * are modifiable. We just turn on the ports that match the + * bits. + * + * arg1 --> port bit pattern + * arg2 --> not used + */ + rc = audioixp_set_port(statep, dir, arg1); + break; + + case AM_SET_MONITOR_GAIN: + /* + * Set the loopback monitor gain. + * + * AUDIO_MIN_GAIN <= gain <= AUDIO_MAX_GAIN + * + * dir ---> N/A + * arg1 --> gain + * arg2 --> not used + */ + rc = audioixp_set_monitor_gain(statep, arg1); + break; + + case AM_OUTPUT_MUTE: + /* + * Mute or enable the output. + * + * dir ---> N/A + * arg1 --> ~0 == mute, 0 == enable + * arg2 --> not used + */ + if (arg1) { /* mute */ + if (statep->swap_out == B_FALSE) { + (void) audioixp_or_ac97(statep, + AC97_MASTER_VOLUME_REGISTER, MVR_MUTE); + } else { + (void) audioixp_or_ac97(statep, + AC97_EXTENDED_LRS_VOLUME_REGISTER, + AD1980_SURR_MUTE); + } + (void) audioixp_or_ac97(statep, + AC97_HEADPHONE_VOLUME_REGISTER, HPVR_MUTE); + (void) audioixp_or_ac97(statep, + AC97_MONO_MASTER_VOLUME_REGSITER, MMVR_MUTE); + + } else { /* not muted */ + + /* by setting the port we unmute only active ports */ + (void) audioixp_set_port(statep, + AUDIO_PLAY, statep->ixp_output_port); + } + break; + + case AM_MIC_BOOST: + /* + * Enable or disable the mic's 20 dB boost preamplifier. + * + * dir ---> N/A + * arg1 --> ~0 == enable, 0 == disabled + * arg2 --> not used + */ + if (arg1) { /* enable */ + (void) audioixp_or_ac97(statep, + AC97_MIC_VOLUME_REGISTER, MICVR_20dB_BOOST); + statep->ad_info.ad_add_mode |= AM_ADD_MODE_MIC_BOOST; + } else { /* disable */ + (void) audioixp_and_ac97(statep, + AC97_MIC_VOLUME_REGISTER, + (uint16_t)~MICVR_20dB_BOOST); + statep->ad_info.ad_add_mode &= + ~AM_ADD_MODE_MIC_BOOST; + } + break; + + default: + /* + * We let default catch commands we don't support, as well + * as bad commands. + * + * AM_SET_GAIN_BAL + * AM_SET_MONO_MIC + * AM_BASS_BOOST + * AM_MID_BOOST + * AM_TREBLE_BOOST + * AM_LOUDNESS + */ + rc = AUDIO_FAILURE; + ATRACE_32("audioixp_ad_set_config() unsupported command", + command); + break; + } + mutex_exit(&statep->inst_lock); + + ATRACE_32("audioixp_ad_set_config() returning", rc); + + return (rc); + +} /* audioixp_ad_set_config() */ + +/* + * audioixp_ad_set_format() + * + * Description: + * This routine is used to set a new audio control data format. + * + * Arguments: + * audiohdl_t ahandle Handle to this device + * int stream Stream number + * int dir AUDIO_PLAY or AUDIO_RECORD + * int sample_rate Data sample rate + * int channels Number of channels, 2 + * int precision Bits per sample, 16 + * int encoding Encoding method, linear + * + * Returns: + * AUDIO_SUCCESS The Codec data format has been set + * AUDIO_FAILURE The Codec data format has not been set, or the + * data format couldn't be set + */ +static int +audioixp_ad_set_format(audiohdl_t ahandle, int stream, int dir, + int sample_rate, int channels, int precision, int encoding) +{ + audioixp_state_t *statep; + uint16_t val; + uint32_t slot; + uint32_t cmd; + + ASSERT(precision == AUDIO_PRECISION_16); + ASSERT(channels == AUDIO_CHANNELS_STEREO); + ASSERT(encoding == AUDIO_ENCODING_LINEAR); + + ATRACE_32("audioixp_ad_set_format() stream", stream); + ATRACE_32("audioixp_ad_set_format() dir", dir); + ATRACE_32("audioixp_ad_set_format() sample_rate", sample_rate); + ATRACE_32("audioixp_ad_set_format() channels", channels); + ATRACE_32("audioixp_ad_set_format() precision", precision); + ATRACE_32("audioixp_ad_set_format() encoding", encoding); + + /* get the soft state structure */ + statep = audio_sup_get_private(ahandle); + ASSERT(statep); + + mutex_enter(&statep->inst_lock); + + if (statep->var_sr == B_FALSE) { + /* codec doesn't support variable sample rate */ + + if (sample_rate != IXP_SAMPR48000) { + audio_sup_log(statep->audio_handle, CE_NOTE, + "!audioixp_ad_set_format() bad sample" + " rate %d\n", sample_rate); + mutex_exit(&statep->inst_lock); + return (AUDIO_FAILURE); + } + } else { + switch (sample_rate) { + case IXP_SAMPR8000: break; + case IXP_SAMPR11025: break; + case IXP_SAMPR16000: break; + case IXP_SAMPR22050: break; + case IXP_SAMPR24000: break; + case IXP_SAMPR32000: break; + case IXP_SAMPR44100: break; + case IXP_SAMPR48000: break; + default: + ATRACE_32("audioixp_ad_set_format() bad SR", + sample_rate); + mutex_exit(&statep->inst_lock); + return (AUDIO_FAILURE); + } + } + + if (dir == AUDIO_PLAY) { + + (void) audioixp_write_ac97(statep, + AC97_EXTENDED_FRONT_DAC_RATE_REGISTER, sample_rate); + + /* + * Some codecs before ac97 2.2, such as YMF753 produced by + * Yamaha LSI, don't have the AC'97 registers indexed range + * from 0x2c to 0x34. So we assume this kind of codec + * supports fixed 48k sample rate. + */ + if (statep->var_sr == B_TRUE) { + (void) audioixp_read_ac97(statep, + AC97_EXTENDED_FRONT_DAC_RATE_REGISTER, &val); + if (val != sample_rate) { + ATRACE_32("audioixp_ad_set_format()" + " bad out SR", sample_rate); + audio_sup_log(statep->audio_handle, CE_NOTE, + "!audioixp_ad_set_format() bad out" + " SR %d\n", sample_rate); + mutex_exit(&statep->inst_lock); + return (AUDIO_FAILURE); + } + } + + slot = IXP_AM_GET32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD); + slot |= IXP_AUDIO_OUT_DMA_SLOT_3 + | IXP_AUDIO_OUT_DMA_SLOT_4; + slot &= ~ (IXP_AUDIO_OUT_DMA_SLOT_5 + |IXP_AUDIO_OUT_DMA_SLOT_6 + |IXP_AUDIO_OUT_DMA_SLOT_7 + |IXP_AUDIO_OUT_DMA_SLOT_8 + |IXP_AUDIO_OUT_DMA_SLOT_9 + |IXP_AUDIO_OUT_DMA_SLOT_10 + |IXP_AUDIO_OUT_DMA_SLOT_11 + |IXP_AUDIO_OUT_DMA_SLOT_12); + + IXP_AM_PUT32(IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD, slot); + + cmd = IXP_AM_GET32(IXP_AUDIO_CMD); + cmd |= IXP_AUDIO_CMD_INTER_OUT; + IXP_AM_PUT32(IXP_AUDIO_CMD, cmd); + + statep->ixp_psample_rate = sample_rate; + statep->ixp_pchannels = channels; + statep->ixp_pprecision = precision; + } else { + (void) audioixp_write_ac97(statep, + AC97_EXTENDED_LR_DAC_RATE_REGISTER, sample_rate); + + /* + * Some codecs before ac97 2.2, such as YMF753 produced by + * Yamaha LSI, don't have the AC'97 registers indexed range + * from 0x2c to 0x34. So we assume this kind of codec + * supports fixed 48k sample rate. + */ + if (statep->var_sr == B_TRUE) { + (void) audioixp_read_ac97(statep, + AC97_EXTENDED_LR_DAC_RATE_REGISTER, &val); + if (val != sample_rate) { + ATRACE_32("audioixp_ad_set_format() bad" + " in SR", sample_rate); + audio_sup_log(statep->audio_handle, CE_NOTE, + "!audioixp_ad_set_format() bad in" + " SR %d\n", sample_rate); + mutex_exit(&statep->inst_lock); + return (AUDIO_FAILURE); + } + } + + cmd = IXP_AM_GET32(IXP_AUDIO_CMD); + cmd |= IXP_AUDIO_CMD_INTER_IN; + IXP_AM_PUT32(IXP_AUDIO_CMD, cmd); + + statep->ixp_csample_rate = sample_rate; + statep->ixp_cchannels = channels; + statep->ixp_cprecision = precision; + } + +done: + mutex_exit(&statep->inst_lock); + + ATRACE_32("audioixp_ad_set_format() returning success", 0); + + return (AUDIO_SUCCESS); + +} /* audioixp_ad_set_format() */ + +/* + * audioixp_ad_start_play() + * + * Description: + * This routine starts the playback DMA engine + * + * Arguments: + * audiohdl_t ahandle Handle to this device + * int stream Stream number for multi-stream Codecs, + * which is not how we program the device + * for now. + * Returns: + * AUDIO_SUCCESS Playing started/restarted + * AUDIO_FAILURE Play not started/restarted, no audio to play + */ +static int +audioixp_ad_start_play(audiohdl_t ahandle, int stream) +{ + audioixp_state_t *statep; + int rc = AUDIO_SUCCESS; + + + ATRACE_32("audioixp_ad_start_play() stream", stream); + statep = audio_sup_get_private(ahandle); + ASSERT(statep); + + mutex_enter(&statep->inst_lock); + + if (statep->flags & IXP_DMA_PLAY_PAUSED) { + statep->flags |= IXP_DMA_PLAY_STARTED; + statep->flags &= ~IXP_DMA_PLAY_PAUSED; + audioixp_start_dma(statep, AUDIO_PLAY); + IXP_AM_UPDATE32( + IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_OUT, + IXP_AUDIO_CMD_EN_OUT); + goto done; + } + + if (statep->flags & IXP_DMA_PLAY_STARTED) { + goto done; + } + + audioixp_start_dma(statep, AUDIO_PLAY); + rc = audioixp_fill_play_buf(statep); + if (rc == AUDIO_SUCCESS) { + statep->flags |= IXP_DMA_PLAY_STARTED; + } + +done: + mutex_exit(&statep->inst_lock); + return (rc); + +} /* audioixp_ad_start_play() */ + +/* + * audioixp_ad_pause_play() + * + * Description: + * This routine pauses the play DMA engine. + * + * Arguments: + * audiohdl_t ahandle Handle to this device + * int stream Stream number for multi-stream Codecs, + * which is not how we program the device + * for now. + * + * Returns: + * void + */ +static void +audioixp_ad_pause_play(audiohdl_t ahandle, int stream) +{ + audioixp_state_t *statep; + + statep = audio_sup_get_private(ahandle); + + ASSERT(statep); + ATRACE("audioixp_ad_pause_play() ", ahandle); + ATRACE_32("audioixp_ad_pause_play() stream", stream); + + mutex_enter(&statep->inst_lock); + + if ((statep->flags & IXP_DMA_PLAY_STARTED) == 0) { + mutex_exit(&statep->inst_lock); + return; + } + IXP_AM_UPDATE32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT, 0); + statep->flags |= IXP_DMA_PLAY_PAUSED; + + mutex_exit(&statep->inst_lock); + +} /* audioixp_ad_pause_play() */ + +/* + * audioixp_ad_stop_play() + * + * Description: + * This routine stops the playback DMA engine. + * + * Arguments: + * audiohdl_t ahandle Handle for this driver + * int stream Stream number for multi-stream Codecs, + * which is not how we program the device + * for now. + * + * Returns: + * void + */ +static void +audioixp_ad_stop_play(audiohdl_t ahandle, int stream) +{ + audioixp_state_t *statep; + audioixp_sample_buf_t *buf; + + ATRACE("audioixp_ad_stop_play() ", ahandle); + ATRACE_32("audioixp_ad_stop_play() stream", stream); + statep = audio_sup_get_private(ahandle); + ASSERT(statep); + + mutex_enter(&statep->inst_lock); + + IXP_AM_UPDATE32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_OUT, 0); + audioixp_stop_dma(statep, AUDIO_PLAY); + + buf = &statep->play_buf; + buf->io_started = B_FALSE; + statep->flags &= ~(IXP_DMA_PLAY_STARTED + | IXP_DMA_PLAY_PAUSED | IXP_DMA_PLAY_EMPTY); + + mutex_exit(&statep->inst_lock); + +} /* audioixp_ad_stop_play() */ + +/* + * audioixp_ad_start_record() + * + * Description: + * This routine starts the PCM in DMA engine + * + * Arguments: + * audiohdl_t ahandle Handle to this device + * int stream Stream number for multi-stream Codecs, + * which isn't going to apply for record + * + * Returns: + * AUDIO_SUCCESS Recording successfully started + * AUDIO_FAILURE Record not started + */ +static int +audioixp_ad_start_record(audiohdl_t ahandle, int stream) +{ + audioixp_state_t *statep; + int rc; + + ATRACE("audioixp_ad_start_record() ", ahandle); + ATRACE_32("audioixp_ad_start_record() stream", stream); + statep = audio_sup_get_private(ahandle); + ASSERT(statep); + + mutex_enter(&statep->inst_lock); + + if (statep->flags & IXP_DMA_RECD_STARTED) { + mutex_exit(&statep->inst_lock); + return (AUDIO_SUCCESS); + } + + audioixp_start_dma(statep, AUDIO_RECORD); + rc = audioixp_prepare_record_buf(statep); + if (rc == AUDIO_SUCCESS) { + statep->flags |= IXP_DMA_RECD_STARTED; + } + + mutex_exit(&statep->inst_lock); + + return (rc); + +} /* audioixp_ad_start_record() */ + +/* + * audioixp_ad_stop_record() + * + * Description: + * This routine stops the PCM in DMA engine + * + * Arguments: + * audiohdl_t ahandle Handle for this driver + * int stream Stream number for multi-stream + * Codecs, which isn't going to apply + * for record + * + * Returns: + * void + */ +static void +audioixp_ad_stop_record(audiohdl_t ahandle, int stream) +{ + audioixp_state_t *statep; + audioixp_sample_buf_t *buf; + + ATRACE("audioixp_ad_stop_record() ", ahandle); + ATRACE_32("audioixp_ad_stop_record() stream", stream); + statep = audio_sup_get_private(ahandle); + ASSERT(statep); + + mutex_enter(&statep->inst_lock); + statep->flags &= ~IXP_DMA_RECD_STARTED; + + buf = &statep->record_buf; + buf->io_started = B_FALSE; + + IXP_AM_UPDATE32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_EN_IN, 0); + audioixp_stop_dma(statep, AUDIO_RECORD); + + mutex_exit(&statep->inst_lock); + +} /* audioixp_ad_stop_record() */ + +/* *********************** Local Routines *************************** */ + +/* + * audioixp_init_state() + * + * Description: + * This routine initializes the audio driver's state structure + * + * CAUTION: This routine cannot allocate resources, unless it frees + * them before returning for an error. Also, error_destroy: + * in audioixp_attach() would need to be fixed as well. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * dev_info_t *dip Pointer to the device's + * dev_info struct + * + * Returns: + * AUDIO_SUCCESS State structure initialized + * AUDIO_FAILURE State structure not initialized + */ +static int +audioixp_init_state(audioixp_state_t *statep, dev_info_t *dip) +{ + int rints; + int pints; + int cdrom; + int mode; + + ATRACE("audioixp_init_state()", NULL); + + statep->dip = dip; + statep->vol_bits_mask = 5; + + cdrom = ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "cdrom", 0); + + /* get the mode from the .conf file */ + if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "mixer-mode", 1)) { + mode = AM_MIXER_MODE; + } else { + mode = AM_COMPAT_MODE; + } + + pints = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "play-interrupts", IXP_INTS); + if (pints > IXP_MAX_INTS) { + ATRACE_32("audioixp_init_state() " + "play interrupt rate too high, resetting", pints); + audio_sup_log(statep->audio_handle, CE_NOTE, + "audioixp_init_state() " + "play interrupt rate set too high, %d, resetting to %d", + pints, IXP_INTS); + pints = IXP_INTS; + } else if (pints < IXP_MIN_INTS) { + ATRACE_32("audioixp_init_state() " + "play interrupt rate too low, resetting", pints); + audio_sup_log(statep->audio_handle, CE_NOTE, + "audioixp_init_state() " + "play interrupt rate set too low, %d, resetting to %d", + pints, IXP_INTS); + pints = IXP_INTS; + } + rints = ddi_prop_get_int(DDI_DEV_T_ANY, dip, + DDI_PROP_DONTPASS, "record-interrupts", IXP_INTS); + if (rints > IXP_MAX_INTS) { + ATRACE_32("audioixp_init_state() " + "record interrupt rate too high, resetting", rints); + audio_sup_log(statep->audio_handle, CE_NOTE, + "audioixp_init_state() " + "record interrupt rate set too high, %d, resetting to " + "%d", + rints, IXP_INTS); + rints = IXP_INTS; + } else if (rints < IXP_MIN_INTS) { + ATRACE_32("audioixp_init_state() " + "record interrupt rate too low, resetting", rints); + audio_sup_log(statep->audio_handle, CE_NOTE, + "audioixp_init_state() " + "record interrupt rate set too low, %d, resetting to " + "%d", + rints, IXP_INTS); + rints = IXP_INTS; + } + + /* fill in the device default state */ + statep->ixp_defaults.play.sample_rate = IXP_DEFAULT_SR; + statep->ixp_defaults.play.channels = IXP_DEFAULT_CH; + statep->ixp_defaults.play.precision = IXP_DEFAULT_PREC; + statep->ixp_defaults.play.encoding = IXP_DEFAULT_ENC; + statep->ixp_defaults.play.gain = IXP_DEFAULT_PGAIN; + statep->ixp_defaults.play.port = + AUDIO_SPEAKER | AUDIO_LINE_OUT; + statep->ixp_defaults.play.avail_ports = + AUDIO_SPEAKER | AUDIO_LINE_OUT; + statep->ixp_defaults.play.mod_ports = + AUDIO_SPEAKER | AUDIO_LINE_OUT; + statep->ixp_defaults.play.buffer_size = IXP_BSIZE; + statep->ixp_defaults.play.balance = IXP_DEFAULT_BAL; + + statep->ixp_defaults.record.sample_rate = IXP_DEFAULT_SR; + statep->ixp_defaults.record.channels = IXP_DEFAULT_CH; + statep->ixp_defaults.record.precision = IXP_DEFAULT_PREC; + statep->ixp_defaults.record.encoding = IXP_DEFAULT_ENC; + statep->ixp_defaults.record.gain = IXP_DEFAULT_PGAIN; + statep->ixp_defaults.record.port = AUDIO_MICROPHONE; + statep->ixp_defaults.record.avail_ports = + AUDIO_MICROPHONE|AUDIO_LINE_IN|AUDIO_CODEC_LOOPB_IN; + statep->ixp_defaults.record.mod_ports = + AUDIO_MICROPHONE|AUDIO_LINE_IN|AUDIO_CODEC_LOOPB_IN; + statep->ixp_defaults.record.buffer_size = IXP_BSIZE; + statep->ixp_defaults.record.balance = IXP_DEFAULT_BAL; + + statep->ixp_defaults.monitor_gain = IXP_DEFAULT_MONITOR_GAIN; + statep->ixp_defaults.output_muted = B_FALSE; + statep->ixp_defaults.ref_cnt = B_FALSE; + statep->ixp_defaults.hw_features = + AUDIO_HWFEATURE_DUPLEX | AUDIO_HWFEATURE_PLAY | + AUDIO_HWFEATURE_IN2OUT | AUDIO_HWFEATURE_RECORD; + statep->ixp_defaults.sw_features = AUDIO_SWFEATURE_MIXER; + + if (cdrom) { + statep->ixp_defaults.record.avail_ports |= AUDIO_CD; + statep->ixp_defaults.record.mod_ports |= AUDIO_CD; + } + + statep->ixp_psample_rate = + statep->ixp_defaults.play.sample_rate; + statep->ixp_pchannels = + statep->ixp_defaults.play.channels; + statep->ixp_pprecision = + statep->ixp_defaults.play.precision; + statep->ixp_csample_rate = + statep->ixp_defaults.record.sample_rate; + statep->ixp_cchannels = + statep->ixp_defaults.record.channels; + statep->ixp_cprecision = + statep->ixp_defaults.record.precision; + + /* + * fill in the ad_info structure + */ + statep->ad_info.ad_mode = mode; + statep->ad_info.ad_int_vers = AM_VERSION; + statep->ad_info.ad_add_mode = NULL; + statep->ad_info.ad_codec_type = AM_TRAD_CODEC; + statep->ad_info.ad_defaults = &statep->ixp_defaults; + statep->ad_info.ad_play_comb = audioixp_combinations; + statep->ad_info.ad_rec_comb = audioixp_combinations; + statep->ad_info.ad_entry = &audioixp_entry; + statep->ad_info.ad_dev_info = &statep->ixp_dev_info; + statep->ad_info.ad_diag_flags = AM_DIAG_INTERNAL_LOOP; + statep->ad_info.ad_diff_flags = + AM_DIFF_SR | AM_DIFF_CH | AM_DIFF_PREC | AM_DIFF_ENC; + statep->ad_info.ad_assist_flags = AM_ASSIST_MIC; + statep->ad_info.ad_misc_flags = AM_MISC_RP_EXCL | AM_MISC_MONO_DUP; + statep->ad_info.ad_num_mics = 1; + + /* play capabilities */ + statep->ad_info.ad_play.ad_mixer_srs = + audioixp_mixer_sample_rates; + statep->ad_info.ad_play.ad_compat_srs = + audioixp_compat_sample_rates; + statep->ad_info.ad_play.ad_conv = &am_src2; + statep->ad_info.ad_play.ad_sr_info = NULL; + statep->ad_info.ad_play.ad_chs = audioixp_channels; + statep->ad_info.ad_play.ad_int_rate = pints; + statep->ad_info.ad_play.ad_max_chs = IXP_MAX_OUT_CHANNELS; + statep->ad_info.ad_play.ad_bsize = IXP_BSIZE; + + /* record capabilities */ + statep->ad_info.ad_record.ad_mixer_srs = + audioixp_mixer_sample_rates; + statep->ad_info.ad_record.ad_compat_srs = + audioixp_compat_sample_rates; + statep->ad_info.ad_record.ad_conv = &am_src2; + statep->ad_info.ad_record.ad_sr_info = NULL; + statep->ad_info.ad_record.ad_chs = audioixp_channels; + statep->ad_info.ad_record.ad_int_rate = rints; + statep->ad_info.ad_record.ad_max_chs = IXP_MAX_CHANNELS; + statep->ad_info.ad_record.ad_bsize = IXP_BSIZE; + + if (ddi_get_iblock_cookie(dip, (uint_t)0, &statep->intr_iblock) != + DDI_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_init_state() cannot get iblock cookie"); + return (AUDIO_FAILURE); + } + mutex_init(&statep->inst_lock, NULL, MUTEX_DRIVER, statep->intr_iblock); + + /* fill in device info strings */ + (void) strcpy(statep->ixp_dev_info.name, IXP_DEV_NAME); + (void) strcpy(statep->ixp_dev_info.config, IXP_DEV_CONFIG); + (void) strcpy(statep->ixp_dev_info.version, IXP_DEV_VERSION); + + statep->play_buf_size = IXP_SAMPR48000 * AUDIO_CHANNELS_STEREO * + (AUDIO_PRECISION_16 >> AUDIO_PRECISION_SHIFT) / pints; + statep->play_buf_size += IXP_MOD_SIZE - + (statep->play_buf_size % IXP_MOD_SIZE); + statep->record_buf_size = IXP_SAMPR48000 * AUDIO_CHANNELS_STEREO * + (AUDIO_PRECISION_16 >> AUDIO_PRECISION_SHIFT) / rints; + statep->record_buf_size += IXP_MOD_SIZE - 1; + statep->record_buf_size -= statep->record_buf_size % IXP_MOD_SIZE; + + return (AUDIO_SUCCESS); + +} /* audioixp_init_state() */ + +/* + * audioixp_map_regs() + * + * Description: + * This routine allocates the DMA handles and the memory for the + * DMA engines to use. Finally, the registers are mapped in. + * + * CAUTION: Make sure all errors call audio_sup_log(). + * + * Arguments: + * dev_info_t *dip Pointer to the device's devinfo + * audioixp_state_t *state The device's state structure + * + * Returns: + * AUDIO_SUCCESS Registers successfully mapped + * AUDIO_FAILURE Registers not successfully mapped + */ +static int +audioixp_map_regs(dev_info_t *dip, audioixp_state_t *statep) +{ + ddi_dma_cookie_t cookie; + uint_t count; + + ATRACE("audioixp_map_regs()", statep); + + statep->ixp_res_flags = 0; + + /* map PCI config space */ + if (pci_config_setup(statep->dip, &statep->pci_conf_handle) == + DDI_FAILURE) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_map_regs() configuration " + "memory mapping failed"); + goto error; + } + statep->ixp_res_flags |= IXP_RS_PCI_REGS; + + /* map audio mixer register */ + if ((ddi_regs_map_setup(dip, IXP_IO_AM_REGS, + (caddr_t *)&statep->am_regs_base, 0, 0, + &dev_attr, &statep->am_regs_handle)) != DDI_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_map_regs() audio mixer " + "memory mapping failed"); + goto error; + } + statep->ixp_res_flags |= IXP_RS_AM_REGS; + + /* + * now, from here we allocate DMA memory for buffer descriptor list. + * we allocate adjacent DMA memory for all DMA engines. + */ + if (ddi_dma_alloc_handle(dip, &bdlist_dma_attr, DDI_DMA_SLEEP, + (caddr_t)0, &statep->bdl_dma_handle) != DDI_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_map_regs() ddi_dma_alloc_handle(bdlist)" + " failed"); + goto error; + } + statep->ixp_res_flags |= IXP_RS_DMA_BDL_HANDLE; + + if (ddi_dma_mem_alloc(statep->bdl_dma_handle, + sizeof (audioixp_bd_list_t), &dev_attr, DDI_DMA_CONSISTENT, + DDI_DMA_SLEEP, NULL, (caddr_t *)&statep->bdl_virtual, + &statep->bdl_size, &statep->bdl_acc_handle) != DDI_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_map_regs() ddi_dma_mem_alloc(bdlist) " + "failed"); + goto error; + } + statep->ixp_res_flags |= IXP_RS_DMA_BDL_MEM; + + if (ddi_dma_addr_bind_handle(statep->bdl_dma_handle, NULL, + (caddr_t)statep->bdl_virtual, statep->bdl_size, + DDI_DMA_RDWR|DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &cookie, + &count) != DDI_DMA_MAPPED) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_map_regs() addr_bind_handle failed"); + goto error; + } + + /* + * there are some bugs in the DDI framework and it is possible to + * get multiple cookies + */ + if (count != 1) { + (void) ddi_dma_unbind_handle(statep->bdl_dma_handle); + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_map_regs() addr_bind_handle failed," + " cookies > 1"); + goto error; + } + + statep->bdl_phys = + (audioixp_bd_list_t *)(long)(cookie.dmac_address); + statep->ixp_res_flags |= IXP_RS_DMA_BDL_BIND; + + return (AUDIO_SUCCESS); + +error: + audioixp_unmap_regs(statep); + + return (AUDIO_FAILURE); + +} /* audioixp_map_regs() */ + +/* + * audioixp_unmap_regs() + * + * Description: + * This routine unbinds the play and record DMA handles, frees + * the DMA buffers and then unmaps control registers. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * void + */ +static void +audioixp_unmap_regs(audioixp_state_t *statep) +{ + if (statep->ixp_res_flags & IXP_RS_DMA_BDL_BIND) { + statep->ixp_res_flags &= ~IXP_RS_DMA_BDL_BIND; + (void) ddi_dma_unbind_handle(statep->bdl_dma_handle); + } + + if (statep->ixp_res_flags & IXP_RS_DMA_BDL_MEM) { + statep->ixp_res_flags &= ~IXP_RS_DMA_BDL_MEM; + ddi_dma_mem_free(&statep->bdl_acc_handle); + } + + if (statep->ixp_res_flags & IXP_RS_DMA_BDL_HANDLE) { + statep->ixp_res_flags &= ~IXP_RS_DMA_BDL_HANDLE; + ddi_dma_free_handle(&statep->bdl_dma_handle); + } + + if (statep->ixp_res_flags & IXP_RS_AM_REGS) { + statep->ixp_res_flags &= ~IXP_RS_AM_REGS; + ddi_regs_map_free(&statep->am_regs_handle); + } + + if (statep->ixp_res_flags & IXP_RS_PCI_REGS) { + statep->ixp_res_flags &= ~IXP_RS_PCI_REGS; + pci_config_teardown(&statep->pci_conf_handle); + } + +} /* audioixp_unmap_regs() */ + +/* + * audioixp_alloc_sample_buf() + * + * Description: + * This routine allocates DMA buffers for the sample buffer. It + * allocates two DMA chunks (buffers) to the specified DMA engine + * (sample buffer structure). The two data chunks will be bound + * to the buffer descriptor entries of corresponding buffer + * descriptor list, and be used to transfer audio sample data to + * and from the audio controller. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int which Which sample buffer, PCM in or PCM out + * IXP_DMA_PCM_IN ---PCM in DMA engine + * IXP_DMA_PCM_OUT---PCM out DMA engine + * int len The length of the DMA buffers + * + * Returns: + * AUDIO_SUCCESS Allocating DMA buffers successfully + * AUDIO_FAILURE Failed to allocate dma buffers + */ + +static int +audioixp_alloc_sample_buf(audioixp_state_t *statep, int which, int len) +{ + audioixp_sample_buf_t *buf; + audioixp_bdlist_chunk_t *chunk; + ddi_dma_cookie_t cookie; + uint_t count; + int i; + + if (which == IXP_DMA_PCM_OUT) { + buf = &statep->play_buf; + } else { + ASSERT(which == IXP_DMA_PCM_IN); + buf = &statep->record_buf; + } + + for (i = 0; i < 2; i++) { + chunk = &(buf->chunk[i]); + + if (ddi_dma_alloc_handle(statep->dip, &sample_buf_dma_attr, + DDI_DMA_SLEEP, NULL, &chunk->dma_handle) != + DDI_SUCCESS) { + goto error; + } + + if (ddi_dma_mem_alloc(chunk->dma_handle, len, &dev_attr, + DDI_DMA_STREAMING, DDI_DMA_SLEEP, NULL, + &chunk->data_buf, &chunk->real_len, + &chunk->acc_handle) != DDI_SUCCESS) { + ddi_dma_free_handle(&chunk->dma_handle); + goto error; + } + + if (ddi_dma_addr_bind_handle(chunk->dma_handle, NULL, + chunk->data_buf, chunk->real_len, DDI_DMA_WRITE, + DDI_DMA_SLEEP, NULL, &cookie, &count) != + DDI_DMA_MAPPED) { + ddi_dma_mem_free(&chunk->acc_handle); + ddi_dma_free_handle(&chunk->dma_handle); + goto error; + } + + /* + * there some bugs in the DDI framework and it is possible to + * get multiple cookies + */ + if (count != 1) { + (void) ddi_dma_unbind_handle(chunk->dma_handle); + ddi_dma_mem_free(&chunk->acc_handle); + ddi_dma_free_handle(&chunk->dma_handle); + goto error; + } + + chunk->addr_phy = (uint32_t)cookie.dmac_address; + } + + return (AUDIO_SUCCESS); + +error: + if (i != 0) { + (void) ddi_dma_unbind_handle((buf->chunk[0].dma_handle)); + ddi_dma_mem_free(&(buf->chunk[0].acc_handle)); + ddi_dma_free_handle(&(buf->chunk[0].dma_handle)); + } + + return (AUDIO_FAILURE); + +} /* audioixp_alloc_sample_buf() */ + +/* + * audioixp_free_sample_buf() + * + * Description: + * This routine frees the DMA buffers of the sample buffer. The DMA + * buffers were allocated by calling audioixp_alloc_sample_buf(). + * + * Arguments: + * audioixp_state_t *state The device's state structure + * audioixp_sample_buf_t *buf The sample buffer structure + * + * Returns: + * void + */ +static void +audioixp_free_sample_buf(audioixp_state_t *statep, + audioixp_sample_buf_t *buf) +{ + audioixp_bdlist_chunk_t *chunk; + int i; + + ATRACE("audioixp_free_sample_buf() audioixp_statep", statep); + + for (i = 0; i < 2; i++) { + chunk = &(buf->chunk[i]); + (void) ddi_dma_unbind_handle(chunk->dma_handle); + ddi_dma_mem_free(&chunk->acc_handle); + chunk->acc_handle = 0; + ddi_dma_free_handle(&chunk->dma_handle); + } + +} /* audioixp_free_sample_buf() */ + + +/* + * audioixp_setup_bdl() + * + * Description: + * This routine setup the buf descriptor list. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * void + */ + +static void audioixp_setup_bdl(audioixp_state_t *statep) +{ + int i; + audioixp_bd_entry_t *bd_p; + + /* setup playback bdlist */ + for (i = 0; i < IXP_BD_NUMS; i ++) { + bd_p = &(((audioixp_bd_list_t *)(statep->bdl_virtual)) + ->pcm_out[i]); + bd_p->buf_base = statep->play_buf.chunk[i&IXP_CHUNK_MASK] + .addr_phy; + bd_p->status = 0; + bd_p->buf_len = 0; + bd_p->next = (uintptr_t)&(((audioixp_bd_list_t *) + (statep->bdl_phys))->pcm_out[(i+1)%IXP_BD_NUMS]); + } + + /* setup record bdlist */ + for (i = 0; i < IXP_BD_NUMS; i ++) { + bd_p = &(((audioixp_bd_list_t *)(statep->bdl_virtual)) + ->pcm_in[i]); + bd_p->buf_base = statep->record_buf.chunk[i&IXP_CHUNK_MASK] + .addr_phy; + bd_p->status = 0; + bd_p->buf_len = 0; + bd_p->next = (uintptr_t)&(((audioixp_bd_list_t *) + (statep->bdl_phys))->pcm_in[(i+1)%IXP_BD_NUMS]); + } +} /* audioixp_setup_bdl() */ + +/* + * audioixp_start_dma() + * + * Description: + * This routine is used to put each DMA engine into working state. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * void + */ +static void +audioixp_start_dma(audioixp_state_t *statep, int dir) +{ + + ASSERT(dir == AUDIO_PLAY || dir == AUDIO_RECORD); + + if (dir == AUDIO_PLAY) { + IXP_AM_PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_OUT); + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_OUT_DMA, + IXP_AUDIO_CMD_EN_OUT_DMA); + } else { + IXP_AM_PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_IN); + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_IN_DMA, + IXP_AUDIO_CMD_EN_IN_DMA); + } + +} /* audioixp_start_dma() */ + +/* + * audioixp_stop_dma() + * + * Description: + * This routine is used to put each DMA engine into the quiet state. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * void + */ +static void +audioixp_stop_dma(audioixp_state_t *statep, int dir) +{ + + ASSERT(dir == AUDIO_PLAY || dir == AUDIO_RECORD); + + IXP_AM_PUT32(IXP_AUDIO_FIFO_FLUSH, IXP_AUDIO_FIFO_FLUSH_IN); + + if (dir == AUDIO_PLAY) { + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_OUT_DMA, + 0); + } else { + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_IN_DMA, + 0); + } + +} /* audioixp_stop_dma() */ + + +/* + * audioixp_codec_ready () + * + * Description: + * This routine checks the state of codecs. This routine is called by + * chip_init before interrupt is enabled. It enables interrupt first, + * then waits a moment for interrupt handler to set the flag according + * to the hardware configuration. Then it checks the flag to confirm + * that primary codec is ready. The original value of interrupt enable + * register is restored. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * AUDIO_SUCCESS codec is ready + * AUDIO_FAILURE codec is not ready + */ +static int +audioixp_codec_ready(audioixp_state_t *statep) +{ + uint32_t old_reg; + + old_reg = IXP_AM_GET32(IXP_AUDIO_INT_EN); + + IXP_AM_UPDATE32(IXP_AUDIO_INT_EN, + IXP_AUDIO_INT_EN_CODEC0_NOT_READY + | IXP_AUDIO_INT_EN_CODEC1_NOT_READY + | IXP_AUDIO_INT_EN_CODEC2_NOT_READY, + IXP_AUDIO_INT_EN_CODEC0_NOT_READY + | IXP_AUDIO_INT_EN_CODEC1_NOT_READY + | IXP_AUDIO_INT_EN_CODEC2_NOT_READY); + + drv_usecwait(1000); + IXP_AM_PUT32(IXP_AUDIO_INT_EN, old_reg); + + if (statep->ixp_codec_not_ready_bits & IXP_AUDIO_INT_CODEC0_NOT_READY) { + audio_sup_log(NULL, CE_WARN, "primary codec not ready"); + return (AUDIO_FAILURE); + } + + return (AUDIO_SUCCESS); +} + +/* + * audioixp_codec_sync() + * + * Description: + * Serialize access to the AC97 audio mixer registers. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * AUDIO_SUCCESS Ready for an I/O access to the codec + * AUDIO_FAILURE An I/O access is currently in progress, can't + * perform another I/O access. + */ +static int +audioixp_codec_sync(audioixp_state_t *statep) +{ + int i; + uint32_t cmd; + + for (i = 0; i < 300; i++) { + cmd = IXP_AM_GET32(IXP_AUDIO_OUT_PHY_ADDR_DATA); + if (!(cmd & IXP_AUDIO_OUT_PHY_EN)) { + return (AUDIO_SUCCESS); + } + drv_usecwait(10); + } + + return (AUDIO_FAILURE); + +} /* audioixp_codec_sync() */ + + +/* + * audioixp_read_ac97() + * + * Description: + * Get the specific AC97 Codec register. It also updates codec_shadow[] + * with the register value. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int reg AC97 register number + * uint16_t *data The data to be returned + * + * Returns: + * AUDIO_SUCCESS Reading the codec register successfully + * AUDIO_FAILURE Failed to read the register + */ +static int +audioixp_read_ac97(audioixp_state_t *statep, int reg, uint16_t *data) +{ + uint32_t value; + uint32_t result; + int i; + + if (audioixp_codec_sync(statep) != AUDIO_SUCCESS) { + *data = 0xffff; + return (AUDIO_FAILURE); + } + + value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC + | IXP_AUDIO_OUT_PHY_READ + | IXP_AUDIO_OUT_PHY_EN + | ((reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT) + & IXP_AUDIO_OUT_PHY_ADDR_MASK); + IXP_AM_PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value); + + if (audioixp_codec_sync(statep) != AUDIO_SUCCESS) { + *data = 0xffff; + return (AUDIO_FAILURE); + } + + for (i = 0; i < 300; i++) { + result = IXP_AM_GET32(IXP_AUDIO_IN_PHY_ADDR_DATA); + if (result & IXP_AUDIO_IN_PHY_READY) { + *data = (result & IXP_AUDIO_IN_PHY_DATA_MASK) + >> IXP_AUDIO_IN_PHY_DATA_SHIFT; + statep->codec_shadow[IXP_CODEC_REG(reg)] = *data; + return (AUDIO_SUCCESS); + } + drv_usecwait(10); + } + + *data = 0xffff; + return (AUDIO_FAILURE); + +} /* audioixp_read_ac97() */ + +/* + * audioixp_write_ac97() + * + * Description: + * Set the specific AC97 Codec register. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int reg AC97 register number + * uint16_t data The data want to be set + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The Codec parameter has not been set + */ +static int +audioixp_write_ac97(audioixp_state_t *statep, int reg, uint16_t data) +{ + uint16_t tmp; + uint32_t value; + + if (audioixp_codec_sync(statep) != AUDIO_SUCCESS) { + return (AUDIO_FAILURE); + } + + value = IXP_AUDIO_OUT_PHY_PRIMARY_CODEC + | IXP_AUDIO_OUT_PHY_WRITE + | IXP_AUDIO_OUT_PHY_EN + | ((reg << IXP_AUDIO_OUT_PHY_ADDR_SHIFT) + & IXP_AUDIO_OUT_PHY_ADDR_MASK) + | ((data << IXP_AUDIO_OUT_PHY_DATA_SHIFT) + & IXP_AUDIO_OUT_PHY_DATA_MASK); + IXP_AM_PUT32(IXP_AUDIO_OUT_PHY_ADDR_DATA, value); + + (void) audioixp_read_ac97(statep, reg, &tmp); + + return (AUDIO_SUCCESS); + +} /* audioixp_write_ac97() */ + +/* + * audioixp_and_ac97() + * + * Description: + * Logically AND the value with the specified ac97 codec register + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int reg AC97 register number + * uint16_t data The value to AND + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The Codec parameter has not been set + */ +static int +audioixp_and_ac97(audioixp_state_t *statep, int reg, uint16_t data) +{ + data &= statep->codec_shadow[IXP_CODEC_REG(reg)]; + if (audioixp_write_ac97(statep, reg, data) != AUDIO_SUCCESS) { + return (AUDIO_FAILURE); + } + + return (AUDIO_SUCCESS); + +} /* audioixp_and_ac97() */ + +/* + * audioixp_or_ac97() + * + * Description: + * Logically OR the value with the specified ac97 codec register + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int reg AC97 register number + * uint16_t data The value to OR + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The Codec parameter has not been set + */ +static int +audioixp_or_ac97(audioixp_state_t *statep, int reg, uint16_t data) +{ + data |= statep->codec_shadow[IXP_CODEC_REG(reg)]; + if (audioixp_write_ac97(statep, reg, data) != AUDIO_SUCCESS) { + return (AUDIO_FAILURE); + } + + return (AUDIO_SUCCESS); + +} /* audioixp_or_ac97() */ + +/* + * audioixp_reset_ac97() + * + * Description: + * Reset AC97 Codec register. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * AUDIO_SUCCESS Reset the codec successfully + * AUDIO_FAILURE Failed to reset the codec + */ +static int +audioixp_reset_ac97(audioixp_state_t *statep) +{ + uint32_t cmd; + int i; + + IXP_AM_UPDATE32(IXP_AUDIO_CMD, IXP_AUDIO_CMD_POWER_DOWN, 0); + drv_usecwait(10); + + /* register reset */ + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_AC_SOFT_RESET, + IXP_AUDIO_CMD_AC_SOFT_RESET); + + drv_usecwait(10); + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_AC_SOFT_RESET, + 0); + + /* cold reset */ + for (i = 0; i < 300; i++) { + cmd = IXP_AM_GET32(IXP_AUDIO_CMD); + if (cmd & IXP_AUDIO_CMD_AC_ACTIVE) { + cmd |= IXP_AUDIO_CMD_AC_RESET; + IXP_AM_PUT32(IXP_AUDIO_CMD, cmd); + return (AUDIO_SUCCESS); + } + cmd &= ~IXP_AUDIO_CMD_AC_RESET; + IXP_AM_PUT32(IXP_AUDIO_CMD, cmd); + (void) IXP_AM_GET32(IXP_AUDIO_CMD); + drv_usecwait(10); + cmd |= IXP_AUDIO_CMD_AC_RESET; + IXP_AM_PUT32(IXP_AUDIO_CMD, cmd); + drv_usecwait(10); + } + + return (AUDIO_FAILURE); + +} /* audioixp_reset_ac97() */ + +/* + * audioixp_chunk_processed() + * + * Description: + * This routine returns the count of chunk processed. It's called by + * audioixp_reclaim_play_buf and audioixp_reclaim_record_buf + * This routine compares the current hw_point value with its last value, + * there're two cases: + * case 1: new pointer is bigger than the last one and smaller than the + * last one + len of the last chunk, which mean the current + * chunk has not been finished, return 0. + * case 2: the hw_pointer return to the old value, which means both chunks + * have been processed,return 2 + * case 3: one chunk is processed, return 1. + * + * Arguments: + * audioixp_state_t *statep The device's state structure + * int dir AUDIO_PLAY or AUDIO_RECORD, if + * direction is important + * + * Returns: + * count of chunk processed + */ +static int +audioixp_chunk_processed(audioixp_state_t *statep, int dir) +{ + audioixp_sample_buf_t *buf; + uint32_t hw_pointer; + int result; + audioixp_bd_entry_t *bd; + int i; + int retry_count; + + ASSERT(mutex_owned(&statep->inst_lock)); + + retry_count = 0; + while (++retry_count < 100) { + if (dir == AUDIO_PLAY) { + buf = &statep->play_buf; + hw_pointer = IXP_AM_GET32(IXP_AUDIO_OUT_DMA_DT_CUR); + } else { + buf = &statep->record_buf; + hw_pointer = IXP_AM_GET32(IXP_AUDIO_IN_DMA_DT_CUR); + } + + for (i = 0; i < IXP_BD_NUMS; i ++) { + if (dir == AUDIO_PLAY) + bd = &statep->bdl_virtual->pcm_out[i]; + else + bd = &statep->bdl_virtual->pcm_in[i]; + + if (hw_pointer >= bd->buf_base && + hw_pointer < bd->buf_base + bd->buf_len*4) + break; + } + + if (i < IXP_BD_NUMS) + break; + } + + /* + * cannot get valid hw_pointer, return 0 without updating + * last_hw_pointer + */ + if (retry_count == 100) { + cmn_err(CE_WARN, "!bad hw_pointer, hw_pointer=0x%08x", + hw_pointer); + for (i = 0; i < IXP_BD_NUMS; i ++) { + if (dir == AUDIO_PLAY) + bd = &statep->bdl_virtual->pcm_out[i]; + else + bd = &statep->bdl_virtual->pcm_in[i]; + + cmn_err(CE_WARN, "!bd[%d], base=0x%08x, len=0x%x", + i, bd->buf_base, bd->buf_len); + } + return (0); + } + + if (buf->last_hw_pointer >= bd->buf_base && /* case 1 */ + hw_pointer > buf->last_hw_pointer && + hw_pointer < bd->buf_base + bd->buf_len * 4) + result = 0; + else if (buf->last_hw_pointer == hw_pointer) /* case 2 */ + result = 2; + else /* case 3 */ + result = 1; + + buf->last_hw_pointer = hw_pointer; + + return (result); + +} /* audioixp_chunk_processed() */ + +/* + * audioixp_fill_play_buf() + * + * Description: + * This routine is called by audioixp_ad_start_play() and the + * interrupt handler. It fills playback samples into the DMA memory, + * sets the BDL entries, and starts the playback DMA engine. + * + * Arguments: + * audioixp_state_t *statep The device's state structure + * + * Returns: + * AUDIO_SUCCESS Starting PCM out engine successfully + * AUDIO_FAILURE Failed to start PCM out engine. + */ +static int +audioixp_fill_play_buf(audioixp_state_t *statep) +{ + audioixp_bdlist_chunk_t *chunk; + audioixp_sample_buf_t *buf; + audioixp_bd_entry_t *bdesc; + int samples; + int rs; + + buf = &statep->play_buf; + + if (!buf->io_started) { + /* + * ready to start PCM out engine + */ + IXP_AM_PUT32( + IXP_AUDIO_OUT_DMA_LINK_P, + (uint32_t)(uintptr_t)statep->bdl_phys->pcm_out | + IXP_AUDIO_OUT_DMA_LINK_P_EN); + + buf->next = 0; + buf->avail = 2; /* have only two buffers for playback */ + buf->last_hw_pointer = statep->bdl_virtual->pcm_out[0].buf_base; + } + + if (buf->avail == 0) { + return (AUDIO_SUCCESS); + } + + samples = statep->ixp_psample_rate * statep->ixp_pchannels / + statep->ad_info.ad_play.ad_int_rate; + + /* if not an even number of samples we panic! */ + if ((samples & 1) != 0) { + samples++; + } + + while (buf->avail > 0) { + chunk = &(buf->chunk[buf->next & 1]); + mutex_exit(&statep->inst_lock); + rs = am_get_audio(statep->audio_handle, + (char *)(chunk->data_buf), AUDIO_NO_CHANNEL, samples); + + mutex_enter(&statep->inst_lock); + + if (((statep->flags & IXP_DMA_PLAY_STARTED) == 0) && + (buf->io_started)) { + return (AUDIO_FAILURE); + } + + if (rs <= 0) { + if (statep->flags & IXP_DMA_PLAY_EMPTY) { + + /* + * Clear the flag so if audio is restarted while + * in am_play_shutdown() we can detect it and + * not mess things up. + */ + statep->flags &= ~IXP_DMA_PLAY_STARTED; + + /* shutdown the mixer */ + mutex_exit(&statep->inst_lock); + am_play_shutdown(statep->audio_handle, NULL); + mutex_enter(&statep->inst_lock); + + /* + * Make sure playing wasn't restarted when lock + * lost if reopened, should return success + */ + if (statep->flags & IXP_DMA_PLAY_STARTED) { + return (AUDIO_SUCCESS); + } + + /* Finished playing, then stop it */ + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_OUT, + 0); + + buf->io_started = B_FALSE; + + /* clr the flags getting ready for next start */ + statep->flags &= ~(IXP_DMA_PLAY_PAUSED | + IXP_DMA_PLAY_EMPTY); + + return (AUDIO_FAILURE); + } else { + /* + * this time, we use one BD entry with empty + * buffer next time we shut down, if no sound + * again + */ + statep->flags |= IXP_DMA_PLAY_EMPTY; + } + } else { + /* we got at least one sample */ + statep->flags &= ~IXP_DMA_PLAY_EMPTY; + (void) ddi_dma_sync(chunk->dma_handle, 0, rs<<1, + DDI_DMA_SYNC_FORDEV); + } + + /* put the samples into buffer descriptor list entry */ + bdesc = &(statep->bdl_virtual->pcm_out[buf->next]); + bdesc->buf_len = (uint16_t)rs>>1; /* in dword */ + + buf->avail --; + buf->next ++; + buf->next %= IXP_BD_NUMS; + } + + /* start PCM out engine */ + if (!buf->io_started) { + IXP_AM_UPDATE32( + IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_OUT, + IXP_AUDIO_CMD_EN_OUT); + + buf->io_started = B_TRUE; + } + + return (AUDIO_SUCCESS); + +} /* audioixp_fill_play_buf() */ + +/* + * audioixp_reclaim_play_buf() + * + * Description: + * When the audio controller finishes fetching the data from DMA + * buffers, this routine will be called by interrupt handler to + * reclaim the DMA buffers. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * void + */ +static void +audioixp_reclaim_play_buf(audioixp_state_t *statep) +{ + + audioixp_sample_buf_t *buf; + + buf = &statep->play_buf; + buf->avail += audioixp_chunk_processed(statep, AUDIO_PLAY); + + return; + +} /* audioixp_reclaim_play_buf() */ + +/* + * audioixp_prepare_record_buf() + * + * Description: + * This routine is called by audioixp_ad_start_record(). It prepares + * DMA memory for PCM in engine, sets the buffer descriptor entries for PCM + * in engine, and starts PCM in engine for recording. + * + * Arguments: + * audioixp_state_t *statep The device's state structure + * + * Returns: + * AUDIO_SUCCESS Started PCM in engine successfully + * AUDIO_FAILURE Failed to start PCM in engine. + * + */ +static int +audioixp_prepare_record_buf(audioixp_state_t *statep) +{ + audioixp_sample_buf_t *buf; + audioixp_bd_entry_t *bdesc; + int samples; + int i; + + buf = &statep->record_buf; + + if (!buf->io_started) { + + /* buffer base */ + IXP_AM_PUT32(IXP_AUDIO_IN_DMA_LINK_P, + (uint32_t)(uintptr_t)statep->bdl_phys->pcm_in | + IXP_AUDIO_IN_DMA_LINK_P_EN); + buf->next = 0; + buf->avail = 2; + buf->last_hw_pointer = statep->bdl_virtual->pcm_in[0].buf_base; + } + + if (buf->avail == 0) { + return (AUDIO_SUCCESS); + } + + samples = statep->ixp_csample_rate * statep->ixp_cchannels / + statep->ad_info.ad_record.ad_int_rate; + + /* if not an even number of samples we panic! */ + if ((samples & 1) != 0) { + samples++; + } + + statep->ixp_csamples = samples; + for (i = 0; i < 2; i ++) { + samples = statep->ixp_csamples; + bdesc = &(statep->bdl_virtual->pcm_in[i]); + bdesc->buf_len = (uint16_t)samples >> 1; /* in dword */ + buf->avail --; + } + + if (!buf->io_started) { + buf->io_started = B_TRUE; + IXP_AM_UPDATE32(IXP_AUDIO_CMD, + IXP_AUDIO_CMD_EN_IN, + IXP_AUDIO_CMD_EN_IN); + } + + return (AUDIO_SUCCESS); + +} /* audioixp_prepare_record_buf() */ + +/* + * audioixp_reclaim_record_buf() + * + * Description: + * This routine is called by the interrupt handler. It sends the PCM + * samples (record data) up to the mixer module by calling am_send_audio(), + * and reclaims the buffer descriptor entries for PCM in engine. + * + * Arguments: + * audioixp_state_t *statep The device's state structure + * + * Returns: + * void + */ +static void +audioixp_reclaim_record_buf(audioixp_state_t *statep) +{ + audioixp_bdlist_chunk_t *chunk; + audioixp_sample_buf_t *buf; + int samples; + + buf = &statep->record_buf; + samples = statep->ixp_csamples; + + buf->avail += audioixp_chunk_processed(statep, AUDIO_RECORD); + + while (buf->avail > 0) { + chunk = &buf->chunk[buf->next & 1]; + (void) ddi_dma_sync(chunk->dma_handle, 0, + samples<<1, DDI_DMA_SYNC_FORCPU); + mutex_exit(&statep->inst_lock); + am_send_audio(statep->audio_handle, chunk->data_buf, + AUDIO_NO_CHANNEL, samples); + mutex_enter(&statep->inst_lock); + buf->avail --; + buf->next ++; + buf->next %= IXP_BD_NUMS; + + if ((statep->flags & IXP_DMA_RECD_STARTED) == 0) { + break; + } + } +} /* audioixp_reclaim_record_buf() */ + +/* + * audioixp_set_gain() + * + * Description: + * Set the play/record gain. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int dir AUDIO_PLAY or AUDIO_RECORD, if + * direction is important + * int arg1 The gain to set + * int arg2 The channel, 0 == left + * or 1 == right + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The gain has not been set + */ +static int +audioixp_set_gain(audioixp_state_t *statep, int dir, int gain, + int channel) +{ + uint16_t tmp; + uint16_t channel_gain; + int regidx; + int max_channel_gain; + + if (gain > AUDIO_MAX_GAIN) { + gain = AUDIO_MAX_GAIN; + } else if (gain < AUDIO_MIN_GAIN) { + gain = AUDIO_MIN_GAIN; + } + + max_channel_gain = (1<<PCMOVR_GAIN_BITS)-1; + channel_gain = 31-gain*max_channel_gain/AUDIO_MAX_GAIN; + + if (dir == AUDIO_PLAY) { + if (statep->swap_out == B_TRUE) { + regidx = AC97_EXTENDED_LRS_VOLUME_REGISTER; + } else { + regidx = AC97_PCM_OUT_VOLUME_REGISTER; + } + (void) audioixp_read_ac97(statep, regidx, &tmp); + + if (channel == 0) { /* left channel */ + tmp &= PCMOVR_RIGHT_GAIN_MASK; + tmp |= (channel_gain << 8); + } else { /* right channel */ + tmp &= PCMOVR_LEFT_GAIN_MASK; + tmp |= channel_gain; + } + + (void) audioixp_write_ac97(statep, regidx, tmp); + } else { + ASSERT(dir == AUDIO_RECORD); + + (void) audioixp_read_ac97(statep, + AC97_RECORD_GAIN_REGISTER, &tmp); + + if (channel == 0) { /* left channel */ + tmp &= ~RGR_LEFT_MASK; + tmp |= gain & RGR_LEFT_MASK; + } else { + /* right channel */ + ASSERT(channel == 1); + tmp &= ~RGR_RIGHT_MASK; + tmp |= gain & RGR_RIGHT_MASK; + } + (void) audioixp_write_ac97(statep, + AC97_RECORD_GAIN_REGISTER, tmp); + } + + return (AUDIO_SUCCESS); + +} /* audioixp_set_gain() */ + +/* + * audioixp_set_port() + * + * Description: + * Set the play/record port. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * which is not how we program + * the device for now. + * int dir AUDIO_PLAY or AUDIO_RECORD, + * if direction is important + * int port The port to set + * AUDIO_SPEAKER output to built-in speaker + * + * AUDIO_MICROPHONE input from microphone + * AUDIO_LINE_IN input from line in + * AUDIO_CODEC_LOOPB_IN input from Codec + * internal loopback + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The port could not been set + */ +static int +audioixp_set_port(audioixp_state_t *statep, int dir, int port) +{ + uint16_t tmp; + + if (dir == AUDIO_PLAY) { /* output port */ + tmp = 0; + if (port == IXP_PORT_UNMUTE) { + port = statep->ixp_output_port; + } + + if (port & AUDIO_SPEAKER) { + (void) audioixp_and_ac97(statep, + AC97_MONO_MASTER_VOLUME_REGSITER, + (uint16_t)~MVR_MUTE); + tmp |= AUDIO_SPEAKER; + } else { + (void) audioixp_or_ac97(statep, + AC97_MONO_MASTER_VOLUME_REGSITER, MVR_MUTE); + } + + if (port & AUDIO_LINE_OUT) { + if (statep->swap_out == B_FALSE) { + (void) audioixp_and_ac97(statep, + AC97_MASTER_VOLUME_REGISTER, + (uint16_t)~MVR_MUTE); + } else { + (void) audioixp_and_ac97(statep, + AC97_EXTENDED_LRS_VOLUME_REGISTER, + (uint16_t)~AD1980_SURR_MUTE); + } + tmp |= AUDIO_LINE_OUT; + } else { + if (statep->swap_out == B_FALSE) { + (void) audioixp_or_ac97(statep, + AC97_MASTER_VOLUME_REGISTER, MVR_MUTE); + } else { + (void) audioixp_or_ac97(statep, + AC97_EXTENDED_LRS_VOLUME_REGISTER, + AD1980_SURR_MUTE); + } + } + + if (port & AUDIO_HEADPHONE) { + (void) audioixp_and_ac97(statep, + AC97_HEADPHONE_VOLUME_REGISTER, + (uint16_t)~MVR_MUTE); + tmp |= AUDIO_HEADPHONE; + } else { + (void) audioixp_or_ac97(statep, + AC97_HEADPHONE_VOLUME_REGISTER, MVR_MUTE); + } + + ATRACE_32("audioixp_set_port() out port", tmp); + statep->ixp_output_port = tmp; + if (tmp != port) { + ATRACE_32("audioixp_set_port() bad out port", port); + return (AUDIO_FAILURE); + } + + } else { /* input port */ + ASSERT(dir == AUDIO_RECORD); + + switch (port) { + case AUDIO_NONE: + /* set to an unused input */ + tmp = RSCR_R_PHONE | RSCR_L_PHONE; + + /* mute the master record input */ + (void) audioixp_or_ac97(statep, + AC97_RECORD_GAIN_REGISTER, RGR_MUTE); + + if (statep->ixp_monitor_gain) { + if (statep->ixp_input_port == + AUDIO_MICROPHONE) { + (void) audioixp_or_ac97(statep, + AC97_MIC_VOLUME_REGISTER, + MICVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_LINE_IN) { + (void) audioixp_or_ac97(statep, + AC97_LINE_IN_VOLUME_REGISTER, + LIVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_CD) { + (void) audioixp_or_ac97(statep, + AC97_CD_VOLUME_REGISTER, + CDVR_MUTE); + } + } + break; + + case AUDIO_MICROPHONE: + /* set to the mic input */ + tmp = RSCR_R_MIC | RSCR_L_MIC; + + if (statep->ixp_monitor_gain) { + if (statep->ixp_input_port == + AUDIO_LINE_IN) { + (void) audioixp_or_ac97(statep, + AC97_LINE_IN_VOLUME_REGISTER, + LIVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_CD) { + (void) audioixp_or_ac97(statep, + AC97_CD_VOLUME_REGISTER, + CDVR_MUTE); + } + (void) audioixp_write_ac97(statep, + AC97_MIC_VOLUME_REGISTER, + statep->ixp_monitor_gain); + } + break; + + case AUDIO_LINE_IN: + /* set to the line in input */ + tmp = RSCR_R_LINE_IN | RSCR_L_LINE_IN; + + /* see if we need to update monitor loopback */ + if (statep->ixp_monitor_gain) { + if (statep->ixp_input_port == + AUDIO_MICROPHONE) { + (void) audioixp_or_ac97(statep, + AC97_MIC_VOLUME_REGISTER, + MICVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_CD) { + (void) audioixp_or_ac97(statep, + AC97_CD_VOLUME_REGISTER, + CDVR_MUTE); + } + (void) audioixp_write_ac97(statep, + AC97_LINE_IN_VOLUME_REGISTER, + statep->ixp_monitor_gain); + } + break; + + case AUDIO_CD: + /* set to the line in input */ + tmp = RSCR_R_CD|RSCR_L_CD; + + /* see if we need to update monitor loopback */ + if (statep->ixp_monitor_gain) { + if (statep->ixp_input_port == + AUDIO_MICROPHONE) { + (void) audioixp_or_ac97(statep, + AC97_MIC_VOLUME_REGISTER, + MICVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_LINE_IN) { + (void) audioixp_or_ac97(statep, + AC97_LINE_IN_VOLUME_REGISTER, + LIVR_MUTE); + } + (void) audioixp_write_ac97(statep, + AC97_CD_VOLUME_REGISTER, + statep->ixp_monitor_gain); + } + break; + + case AUDIO_CODEC_LOOPB_IN: + /* set to the loopback input */ + tmp = RSCR_R_STEREO_MIX | RSCR_L_STEREO_MIX; + + if (statep->ixp_monitor_gain) { + if (statep->ixp_input_port == + AUDIO_LINE_IN) { + (void) audioixp_or_ac97(statep, + AC97_LINE_IN_VOLUME_REGISTER, + LIVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_MICROPHONE) { + (void) audioixp_or_ac97(statep, + AC97_MIC_VOLUME_REGISTER, + MICVR_MUTE); + } else if (statep->ixp_input_port == + AUDIO_CD) { + (void) audioixp_or_ac97(statep, + AC97_CD_VOLUME_REGISTER, + CDVR_MUTE); + } + } + break; + + default: + ATRACE_32("audioixp_set_port bad in port", port); + return (AUDIO_FAILURE); + } + + /* select the input */ + (void) audioixp_write_ac97(statep, + AC97_RECORD_SELECT_CTRL_REGISTER, tmp); + if ((port != AUDIO_NONE) && + (statep->codec_shadow[IXP_CODEC_REG( + AC97_RECORD_GAIN_REGISTER)] & RGR_MUTE)) { + (void) audioixp_and_ac97(statep, + AC97_RECORD_GAIN_REGISTER, + (uint16_t)~RGR_MUTE); + } + statep->ixp_input_port = port; + } + + ATRACE_32("audioixp_set_port() returning", 0); + return (AUDIO_SUCCESS); + +} /* audioixp_set_port() */ + +/* + * audioixp_set_monitor_gain() + * + * Description: + * Set the monitor gain. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int gain The gain to set + * + * Returns: + * AUDIO_SUCCESS The Codec parameter has been set + * AUDIO_FAILURE The gain has not been set + */ +static int +audioixp_set_monitor_gain(audioixp_state_t *statep, int gain) +{ + uint16_t tmp_short; + int rc = AUDIO_SUCCESS; + + ATRACE("in audioixp_set_monitor_gain()", statep); + + if (gain > AUDIO_MAX_GAIN) { /* sanity check */ + gain = AUDIO_MAX_GAIN; + } + + if (gain == 0) { + /* disable loopbacks when gain == 0 */ + tmp_short = MVR_MUTE; + } else { + /* Adjust the value of gain to the requirement of AC'97 */ + tmp_short = AUDIO_MAX_GAIN - gain; + tmp_short = ((tmp_short << statep->vol_bits_mask) - tmp_short) / + AUDIO_MAX_GAIN; + tmp_short |= (((tmp_short << statep->vol_bits_mask) - + tmp_short) / AUDIO_MAX_GAIN) << 8; + } + + switch (statep->ixp_input_port) { + case AUDIO_NONE: + /* + * It is possible to set the value of gain before any input + * is selected. So, we just save the gain and then return + * SUCCESS. + */ + break; + + case AUDIO_MICROPHONE: + /* + * MIC input has 20dB boost, we just preserve it + */ + tmp_short |= + statep->codec_shadow[IXP_CODEC_REG( + AC97_MIC_VOLUME_REGISTER)] & MICVR_20dB_BOOST; + (void) audioixp_write_ac97(statep, + AC97_MIC_VOLUME_REGISTER, tmp_short); + break; + + case AUDIO_LINE_IN: + (void) audioixp_write_ac97(statep, + AC97_LINE_IN_VOLUME_REGISTER, tmp_short); + break; + + case AUDIO_CD: + (void) audioixp_write_ac97(statep, + AC97_CD_VOLUME_REGISTER, tmp_short); + break; + + case AUDIO_CODEC_LOOPB_IN: + /* we already are getting the loopback, so done */ + rc = AUDIO_SUCCESS; + goto done; + + default: + /* this should never happen! */ + ATRACE("audioixp_ad_set_config() monitor gain bad device", + NULL); + rc = AUDIO_FAILURE; + goto done; + } + + if (gain == 0) { + statep->ixp_monitor_gain = 0; + } else { + statep->ixp_monitor_gain = tmp_short; + } + +done: + ATRACE_32("audioixp_set_monitor_gain()", rc); + + return (rc); + +} /* audioixp_set_monitor_gain() */ + +/* + * audioixp_chip_init() + * + * Description: + * This routine initializes ATI IXP audio controller and the AC97 + * codec. The AC97 codec registers are programmed from codec_shadow[]. + * If we are not doing a restore, we initialize codec_shadow[], otherwise + * we use the current values of shadow + * + * Arguments: + * audioixp_state_t *state The device's state structure + * int restore If IXP_INIT_RESTORE then + * restore from codec_shadow[] + * Returns: + * AUDIO_SUCCESS The hardware was initialized properly + * AUDIO_FAILURE The hardware couldn't be initialized properly + */ +static int +audioixp_chip_init(audioixp_state_t *statep, int restore) +{ + uint16_t *shadow; + int i; + int j; + uint16_t xid; + uint16_t vid1; + uint16_t vid2; + uint16_t sr; + uint16_t tmp; + + /* + * put the audio controller into quiet state, everything off + */ + audioixp_stop_dma(statep, AUDIO_PLAY); + audioixp_stop_dma(statep, AUDIO_RECORD); + + /* AC97 reset */ + if (audioixp_reset_ac97(statep) != AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_chip_init() AC97 codec reset failed"); + return (AUDIO_FAILURE); + } + + if (audioixp_codec_ready(statep) != AUDIO_SUCCESS) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!audioixp_chip_init() AC97 codec not ready"); + return (AUDIO_FAILURE); + } + + shadow = statep->codec_shadow; + + if (restore == IXP_INIT_NO_RESTORE) { + for (i = 0; i <= IXP_LAST_AC_REG; i += 2) { + (void) audioixp_read_ac97(statep, i, + &(shadow[IXP_CODEC_REG(i)])); + } + + /* 02h - set master line out volume, muted, 0dB */ + shadow[IXP_CODEC_REG(AC97_MASTER_VOLUME_REGISTER)] = + MVR_MUTE; + + /* 04h - set alternate line out volume, muted, 0dB */ + shadow[IXP_CODEC_REG(AC97_HEADPHONE_VOLUME_REGISTER)] = + HPVR_MUTE; + + /* 06h - set master mono volume, muted, 0dB */ + shadow[IXP_CODEC_REG(AC97_MONO_MASTER_VOLUME_REGSITER)] = + MMVR_MUTE; + + /* 08h - set master tone control to no modification */ + shadow[IXP_CODEC_REG(AC97_MASTER_TONE_CONTROL_REGISTER)] = + MTCR_BASS_BYPASS|MTCR_TREBLE_BYPASS; + + /* + * 0ah - turn pc beep mute off, 0dB + * + * AC'97 Spec does define the optional PC Beep support, that is, + * the BIOS (dependent on hardware design) can use the audio + * hardware for the beep, especially on some laptops, in order + * to save cost. So we have to turn the pc_beep mute off, that + * is, enable the PC Beep support. + */ + shadow[IXP_CODEC_REG(AC97_PC_BEEP_REGISTER)] = + PCBR_0dB_ATTEN; + + /* 0ch - set phone input, mute, 0dB attenuation */ + shadow[IXP_CODEC_REG(AC97_PHONE_VOLUME_REGISTER)] = + PVR_MUTE|PVR_0dB_GAIN; + + /* 0eh - set mic input, mute, 0dB attenuation */ + shadow[IXP_CODEC_REG(AC97_MIC_VOLUME_REGISTER)] = + MICVR_MUTE|MICVR_0dB_GAIN; + + /* 10h - set line input, mute, 0dB attenuation */ + shadow[IXP_CODEC_REG(AC97_LINE_IN_VOLUME_REGISTER)] = + LIVR_MUTE|LIVR_RIGHT_0dB_GAIN|LIVR_LEFT_0dB_GAIN; + + /* 12h - set cd input, mute, 0dB attenuation */ + shadow[IXP_CODEC_REG(AC97_CD_VOLUME_REGISTER)] = + CDVR_MUTE|CDVR_RIGHT_0dB_GAIN|CDVR_LEFT_0dB_GAIN; + + /* 14h - set video input, mute, 0dB attenuation */ + shadow[IXP_CODEC_REG(AC97_VIDEO_VOLUME_REGISTER)] = + VIDVR_MUTE|VIDVR_RIGHT_0dB_GAIN|VIDVR_LEFT_0dB_GAIN; + + /* 16h - set aux input, mute, 0dB attenuation */ + shadow[IXP_CODEC_REG(AC97_AUX_VOLUME_REGISTER)] = + AUXVR_MUTE|AUXVR_RIGHT_0dB_GAIN|AUXVR_LEFT_0dB_GAIN; + + /* 18h - set PCM out input, NOT muted, 0dB gain */ + shadow[IXP_CODEC_REG(AC97_PCM_OUT_VOLUME_REGISTER)] = + PCMOVR_RIGHT_0dB_GAIN|PCMOVR_LEFT_0dB_GAIN; + + /* 1ah - set input device as mic */ + shadow[IXP_CODEC_REG(AC97_RECORD_SELECT_CTRL_REGISTER)] = + RSCR_R_MIC|RSCR_L_MIC; + + /* 1ch - set record gain to 0dB and not muted */ + shadow[IXP_CODEC_REG(AC97_RECORD_GAIN_REGISTER)] = + RGR_RIGHT_0db_GAIN|RGR_LEFT_0db_GAIN; + + /* 1eh - set record mic gain to 0dB and not muted */ + shadow[IXP_CODEC_REG(AC97_RECORD_GAIN_MIC_REGISTER)] = + RGMR_0db_GAIN; + + /* 20h - set GP register, mic 1, everything else off */ + shadow[IXP_CODEC_REG(AC97_GENERAL_PURPOSE_REGISTER)] = + GPR_MS_MIC1|GPR_MONO_MIX_IN; + + /* 22h - set 3D control to NULL */ + shadow[IXP_CODEC_REG(AC97_THREE_D_CONTROL_REGISTER)] = + TDCR_NULL; + + /* + * The rest we ignore, most are reserved. + */ + + } + + if (restore == IXP_INIT_RESTORE) { + /* Restore from saved values */ + shadow[IXP_CODEC_REG(AC97_MASTER_VOLUME_REGISTER)] = + MVR_MUTE; + shadow[IXP_CODEC_REG(AC97_HEADPHONE_VOLUME_REGISTER)] = + HPVR_MUTE; + shadow[IXP_CODEC_REG(AC97_MONO_MASTER_VOLUME_REGSITER)] = + MMVR_MUTE; + shadow[IXP_CODEC_REG(AC97_PCM_OUT_VOLUME_REGISTER)] = + PCMOVR_MUTE; + } + + /* Now we set the AC97 codec registers to the saved values */ + for (i = 2; i <= IXP_LAST_AC_REG; i += 2) + (void) audioixp_write_ac97(statep, i, + shadow[IXP_CODEC_REG(i)]); + + (void) audioixp_read_ac97(statep, AC97_RESET_REGISTER, &tmp); + if (tmp & RR_HEADPHONE_SUPPORT) { + statep->ixp_defaults.play.port |= AUDIO_HEADPHONE; + statep->ixp_defaults.play.avail_ports |= AUDIO_HEADPHONE; + statep->ixp_defaults.play.mod_ports |= AUDIO_HEADPHONE; + } + + /* + * Most vendors connect the surr-out of ad1980/ad1985 codecs to the + * line-out jack. So far we haven't found which vendors don't + * do that. So we assume that all vendors swap the surr-out + * and the line-out outputs. So we need swap the two outputs. + * But we still internally process the "ad198x-swap-output" + * property. If someday some vendors do not swap the outputs, + * we would set "ad198x-swap-output = 0" in the + * /kernel/drv/audioixp.conf file, and unload and reload the + * audioixp driver (or reboot). + */ + (void) audioixp_read_ac97(statep, AC97_VENDOR_ID1_REGISTER, &vid1); + (void) audioixp_read_ac97(statep, AC97_VENDOR_ID2_REGISTER, &vid2); + if (vid1 == AD1980_VID1 && + (vid2 == AD1980_VID2 || vid2 == AD1985_VID2)) { + if (ddi_prop_get_int(DDI_DEV_T_ANY, statep->dip, + DDI_PROP_DONTPASS, "ad198x-swap-output", 1) == 1) { + statep->swap_out = B_TRUE; + (void) audioixp_read_ac97(statep, CODEC_AD_REG_MISC, + &tmp); + (void) audioixp_write_ac97(statep, + CODEC_AD_REG_MISC, + tmp | AD1980_MISC_LOSEL | AD1980_MISC_HPSEL); + } + } + + /* + * check if the codec implements 6 bit volume register, + * but the ALC202 does not strictly obey the AC'97 Spec + * and it only supports 5 bit volume register, so we + * skip the check for it as a workaround. + */ + if (!(vid1 == ALC202_VID1 && vid2 == ALC202_VID2)) { + (void) audioixp_write_ac97(statep, + AC97_MASTER_VOLUME_REGISTER, MVR_MUTE | + MVR_RIGHT_OPTIONAL_MASK | MVR_LEFT_OPTIONAL_MASK); + + (void) audioixp_read_ac97(statep, + AC97_MASTER_VOLUME_REGISTER, &tmp); + + if ((tmp & 0x7fff) != (MVR_RIGHT_MASK | MVR_LEFT_MASK)) { + statep->vol_bits_mask = 6; + } + } + + /* resume the master volume to the max */ + (void) audioixp_write_ac97(statep, AC97_MASTER_VOLUME_REGISTER, + MVR_MUTE); + + /* + * if the codec chip does not support variable sample rate, + * we set the sample rate to 48K + */ + (void) audioixp_read_ac97(statep, AC97_EXTENDED_AUDIO_REGISTER, + &xid); + audio_sup_log(statep->audio_handle, CE_NOTE, + "!%s%d: xid=0x%04x, vid1=0x%04x, vid2=0x%04x", + audioixp_name, ddi_get_instance(statep->dip), xid, vid1, vid2); + if (!(xid & EAR_VRA)) { + statep->var_sr = B_FALSE; + statep->ad_info.ad_record.ad_compat_srs = + audioixp_min_compat_sample_rates; + statep->ad_info.ad_play.ad_compat_srs = + audioixp_min_compat_sample_rates; + statep->ixp_defaults.play.sample_rate = + IXP_SAMPR48000; + statep->ixp_defaults.record.sample_rate = + IXP_SAMPR48000; + } else { /* variable sample rate supported */ + statep->var_sr = B_TRUE; + + /* set variable rate mode */ + (void) audioixp_write_ac97(statep, + AC97_EXTENDED_AUDIO_STAT_CTRL_REGISTER, EASCR_VRA); + + /* check the sample rates supported */ + for (i = 0, j = 0; audioixp_compat_srs[i] != 0; i++) { + (void) audioixp_write_ac97(statep, + AC97_EXTENDED_FRONT_DAC_RATE_REGISTER, + audioixp_compat_srs[i]); + (void) audioixp_read_ac97(statep, + AC97_EXTENDED_FRONT_DAC_RATE_REGISTER, &sr); + + if (sr == audioixp_compat_srs[i]) { + if (i != j) { + audioixp_compat_srs[j] = + audioixp_compat_srs[i]; + } + j++; + } + } + + if (j < 1) { + audio_sup_log(statep->audio_handle, CE_WARN, + "!No standard sample rate is supported"); + return (AUDIO_FAILURE); + } + audioixp_compat_srs[j] = 0; + + /* + * if the configuration doesn't support 8K sample rate, + * we modify the default value to the first. + */ + for (i = 0; audioixp_compat_srs[i] != 0; i++) { + if (audioixp_compat_srs[i] == IXP_SAMPR8000) { + break; + } + } + if (audioixp_compat_srs[i] != IXP_SAMPR8000) { + statep->ixp_defaults.play.sample_rate = + audioixp_compat_srs[0]; + statep->ixp_defaults.record.sample_rate = + audioixp_compat_srs[0]; + } + } + + /* enable interrupts */ + IXP_AM_PUT32(IXP_AUDIO_INT, 0xffffffff); + IXP_AM_PUT32( + IXP_AUDIO_INT_EN, + IXP_AUDIO_INT_EN_IN_DMA_OVERFLOW | + IXP_AUDIO_INT_EN_STATUS | + IXP_AUDIO_INT_EN_OUT_DMA_UNDERFLOW); + return (AUDIO_SUCCESS); + +} /* audioixp_chip_init() */ + +/* + * audioixp_chip_fini() + * + * Description: + * This routine disables hardware interrupts. + * + * Arguments: + * audioixp_state_t *state The device's state structure + * + * Returns: + * void + */ +static void audioixp_chip_fini(audioixp_state_t *statep) +{ + IXP_AM_PUT32(IXP_AUDIO_INT, IXP_AM_GET32(IXP_AUDIO_INT)); + IXP_AM_PUT32(IXP_AUDIO_INT_EN, 0); +} /* audioixp_chip_fini() */ diff --git a/usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.conf b/usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.conf new file mode 100644 index 0000000000..c40d2031d5 --- /dev/null +++ b/usr/src/uts/common/io/audio/sada/drv/audioixp/audioixp.conf @@ -0,0 +1,79 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# +# Configuration file for the audioixp audio driver. +# +# WARNING: This is an UNSTABLE configuration file. Its contents +# may change at any time. + +# mixer-mode determines the mode the mixer audio personality module +# sets for this driver when it is loaded. The mixerctl(1) command may +# be used to change modes on the fly. +# +# The compat mode (mixer disabled) may not work as expected on all systems, +# please refer to the audioixp(7D) manpage. +# +# mixer-mode=1; <-- mixer enabled +# mixer-mode=0; <-- mixer disabled, old audio(7I) behavior +# + +mixer-mode=1; + +# +# Uncomment cdrom to enable the audioixp driver to use the CDROM analog +# input. +# +# cdrom=1; + +# +# play-interrupts sets the number of interrupts per second when playing. +# This affects the resolution of various things, such as sample counts. +# record-interrupts does the same for record interrupts. +# +# These may be tuned to get more accurate information by increasing the +# count. However, the larger the interrupts per second the larger the +# load on the system. So use this capability cautiously. The audioixp +# driver enforces a maximum and minimum count. +# +# It should also be understood that not all interrupt rates are legal. +# The hardware is restricted to DMA buffers being allocated on certain +# boundaries. If those boundaries are violated the driver will not be +# loaded and an error message is entered into the messages log +# +play-interrupts=175; +record-interrupts=175; + +# +# Uncomment reset-configuration to cause the audioixp driver's state to +# be reset to the default when the driver is loaded. Otherwise this state +# is retained across driver unload/reload cycles, but not across reboots. +# +#reset-configuration=1; + +# +# We need to change the priority so that we aren't high level interrupt. +# +interrupt-priorities=9; diff --git a/usr/src/uts/common/sys/Makefile b/usr/src/uts/common/sys/Makefile index f371f4ab63..f6bcef9c5c 100644 --- a/usr/src/uts/common/sys/Makefile +++ b/usr/src/uts/common/sys/Makefile @@ -47,6 +47,7 @@ i386_HDRS= \ asy.h \ audio/audio810.h \ audio/audiohd.h \ + audio/audioixp.h \ bmc_intf.h \ fd_debug.h \ fdc.h \ diff --git a/usr/src/uts/common/sys/audio/audioixp.h b/usr/src/uts/common/sys/audio/audioixp.h new file mode 100644 index 0000000000..3d0d6ed17a --- /dev/null +++ b/usr/src/uts/common/sys/audio/audioixp.h @@ -0,0 +1,77 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_AUDIOIXP_H_ +#define _SYS_AUDIOIXP_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Header file for the audioixp device driver + */ + +/* + * Values returned by the AUDIO_GETDEV ioctl() + */ +#define IXP_DEV_NAME "SUNW,audioixp" +#define IXP_DEV_CONFIG "onboard1" +#define IXP_DEV_VERSION "a" + +/* + * Driver supported configuration information + */ +#define IXP_NAME "audioixp" +#define IXP_MOD_NAME "mixer audio driver" + +#define IXP_SAMPR8000 (8000) +#define IXP_SAMPR11025 (11025) +#define IXP_SAMPR16000 (16000) +#define IXP_SAMPR22050 (22050) +#define IXP_SAMPR24000 (24000) +#define IXP_SAMPR32000 (32000) +#define IXP_SAMPR44100 (44100) +#define IXP_SAMPR48000 (48000) + +#define IXP_DEFAULT_SR IXP_SAMPR8000 +#define IXP_DEFAULT_CH AUDIO_CHANNELS_MONO +#define IXP_DEFAULT_PREC AUDIO_PRECISION_8 +#define IXP_DEFAULT_ENC AUDIO_ENCODING_ULAW +#define IXP_DEFAULT_PGAIN (AUDIO_MAX_GAIN * 3 / 4) +#define IXP_DEFAULT_RGAIN (127) +#define IXP_DEFAULT_MONITOR_GAIN (0) +#define IXP_DEFAULT_BAL AUDIO_MID_BALANCE +#define IXP_INTS (175) /* default interrupt rate */ +#define IXP_MIN_INTS (24) /* minimum interrupt rate */ +#define IXP_MAX_INTS (500) /* maximum interrupt rate */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_AUDIOIXP_H_ */ diff --git a/usr/src/uts/common/sys/audio/impl/audioixp_impl.h b/usr/src/uts/common/sys/audio/impl/audioixp_impl.h new file mode 100644 index 0000000000..4ce55d8e16 --- /dev/null +++ b/usr/src/uts/common/sys/audio/impl/audioixp_impl.h @@ -0,0 +1,459 @@ +/* + * 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 + * or http://www.opensolaris.org/os/licensing. + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _SYS_AUDIOIXP_IMPL_H_ +#define _SYS_AUDIOIXP_IMPL_H_ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _KERNEL + +#define IXP_CONFIG_REGS (0) /* PCI configure register */ +#define IXP_IO_AM_REGS (1) /* PCI base register 0x10 */ + +#define IXP_IDNUM (0x6175) +#define IXP_MINPACKET (0) +#define IXP_MAXPACKET (1*1024) +#define IXP_HIWATER (64*1024) +#define IXP_LOWATER (32*1024) + +#define IXP_DMA_PCM_IN (1) +#define IXP_DMA_PCM_OUT (2) + +#define IXP_KIOP(X) ((kstat_intr_t *)(X->ixp_ksp->ks_data)) + +/* + * PCI configuration registers and bits + */ +#define IXP_PCI_REG_VID (0x00) +#define IXP_PCI_VID (0x1002) + +#define IXP_PCI_REG_DID (0x02) +#define IXP_PCI_DID (0x4370) + +#define IXP_PCI_REG_CMD (0x04) +#define IXP_PCI_CMD_MEM_ACC_EN (0x0002) +#define IXP_PCI_CMD_MASTER_EN (0x0004) +#define IXP_PCI_CMD_MEM_WR_INVAL_EN (0x0010) +#define IXP_PCI_CMD_PARITY_ERR_RESP (0x0040) +#define IXP_PCI_CMD_SERR_EN (0x0100) +#define IXP_PCI_CMD_INTA_EN (0x0400) + +#define IXP_PCI_REG_STS (0x06) +#define IXP_PCI_STS_INTA (0x0008) +#define IXP_PCI_STS_CAP_LIST (0x0010) +#define IXP_PCI_STS_66M_CAP (0x0020) +#define IXP_PCI_STS_FAST_B2B_CAP (0x0080) +#define IXP_PCI_STS_MASTER_PARITY_ERROR (0x0100) +#define IXP_PCI_STS_RECEIVED_TARGET_ABORT (0x1000) +#define IXP_PCI_STS_RECEIVED_MASTER_ABORT (0x2000) +#define IXP_PCI_STS_SERR (0x4000) +#define IXP_PCI_STS_PARITY_ERR (0x8000) + +#define IXP_PCI_REG_REV_ID_CLS_CODE (0x08) +#define IXP_PCI_REV_ID_400 (0x00) +#define IXP_PCI_REV_ID_450 (0x80) +#define IXP_PCI_CLS_CODE (0x040100) + +#define IXP_PCI_REG_CACHE_LINE_SIZE (0x0c) +#define IXP_PCI_REG_LATENCY_TIMER (0x0d) +#define IXP_PCI_REG_HEADER_TYPE (0x0e) +#define IXP_PCI_REG_BUILTIN_SELF_TEST (0x0f) +#define IXP_PCI_REG_BA0 (0x10) +#define IXP_PCI_REG_BA1 (0x14) +#define IXP_PCI_REG_BA2 (0x18) +#define IXP_PCI_REG_BA3 (0x1c) +#define IXP_PCI_REG_BA4 (0x20) +#define IXP_PCI_REG_BA5 (0x24) +#define IXP_PCI_REG_CARDBUS_CIS_POINTER (0x28) +#define IXP_PCI_REG_SUB_ID (0x2c) +#define IXP_PCI_REG_EX_ROM_BA (0x30) +#define IXP_PCI_REG_CAP_P (0x34) +#define IXP_PCI_REG_INT_LINE (0x3c) +#define IXP_PCI_REG_INT_PIN (0x3d) +#define IXP_PCI_REG_MIN_GRANT (0x3e) +#define IXP_PCI_REG_MAX_LATENCY (0x3f) +#define IXP_PCI_REG_MSI_CAP_REG_SET_ID (0x40) +#define IXP_PCI_REG_MSI_MSG_CTRL (0x42) +#define IXP_PCI_REG_MSI_MSG_ADDR (0x44) +#define IXP_PCI_REG_MSI_MSG_DATA (0x48) +#define IXP_PCI_REG_MSI_PROGRAM_WEIGHT (0x4c) +#define IXP_PCI_REG_UNMASK_LATENCY_TIMER_EXPIRATION (0x50) + +/* + * Audio controller registers and bits + */ +#define IXP_AUDIO_INT (0x00) +#define IXP_AUDIO_INT_IN_DMA_OVERFLOW (1U<<0) +#define IXP_AUDIO_INT_IN_DMA (1U<<1) +#define IXP_AUDIO_INT_OUT_DMA_UNDERFLOW (1U<<2) +#define IXP_AUDIO_INT_OUT_DMA (1U<<3) +#define IXP_AUDIO_INT_CODEC0_NOT_READY (1U<<10) +#define IXP_AUDIO_INT_CODEC1_NOT_READY (1U<<11) +#define IXP_AUDIO_INT_CODEC2_NOT_READY (1U<<12) +#define IXP_AUDIO_INT_NEW_FRAME (1U<<13) + +#define IXP_AUDIO_INT_EN (0x04) +#define IXP_AUDIO_INT_EN_IN_DMA_OVERFLOW (1U<<0) +#define IXP_AUDIO_INT_EN_STATUS (1U<<1) +#define IXP_AUDIO_INT_EN_OUT_DMA_UNDERFLOW (1U<<2) +#define IXP_AUDIO_INT_EN_CODEC0_NOT_READY (1U<<10) +#define IXP_AUDIO_INT_EN_CODEC1_NOT_READY (1U<<11) +#define IXP_AUDIO_INT_EN_CODEC2_NOT_READY (1U<<12) +#define IXP_AUDIO_INT_EN_NEW_FRAME (1U<<13) + +#define IXP_AUDIO_CMD (0x08) +#define IXP_AUDIO_CMD_POWER_DOWN (1U<<0) +#define IXP_AUDIO_CMD_EN_IN (1U<<1) +#define IXP_AUDIO_CMD_EN_OUT (1U<<2) +#define IXP_AUDIO_CMD_EN_IN_DMA (1U<<8) +#define IXP_AUDIO_CMD_EN_OUT_DMA (1U<<9) +#define IXP_AUDIO_CMD_INTER_IN (1U<<21) +#define IXP_AUDIO_CMD_INTER_OUT (1U<<22) +#define IXP_AUDIO_CMD_BURST_EN (1U<<25) +#define IXP_AUDIO_CMD_AC_ACTIVE (1U<<28) +#define IXP_AUDIO_CMD_AC_SOFT_RESET (1U<<29) +#define IXP_AUDIO_CMD_AC_SYNC (1U<<30) +#define IXP_AUDIO_CMD_AC_RESET (1U<<31) + +#define IXP_AUDIO_OUT_PHY_ADDR_DATA (0x0c) +#define IXP_AUDIO_OUT_PHY_PRIMARY_CODEC (0u) +#define IXP_AUDIO_OUT_PHY_SECOND_CODEC (1u) +#define IXP_AUDIO_OUT_PHY_THIRD_CODEC (2u) +#define IXP_AUDIO_OUT_PHY_READ (1u<<2) +#define IXP_AUDIO_OUT_PHY_WRITE (0u) +#define IXP_AUDIO_OUT_PHY_EN (1u<<8) +#define IXP_AUDIO_OUT_PHY_ADDR_SHIFT (9) +#define IXP_AUDIO_OUT_PHY_ADDR_MASK (0x7fu<<9) +#define IXP_AUDIO_OUT_PHY_DATA_SHIFT (16) +#define IXP_AUDIO_OUT_PHY_DATA_MASK (0xffffu<<16) + +#define IXP_AUDIO_IN_PHY_ADDR_DATA (0x10) +#define IXP_AUDIO_IN_PHY_READY (1u<<8) +#define IXP_AUDIO_IN_PHY_ADDR_SHIFT (9) +#define IXP_AUDIO_IN_PHY_ADDR_MASK (0x7fu<<9) +#define IXP_AUDIO_IN_PHY_DATA_SHIFT (16) +#define IXP_AUDIO_IN_PHY_DATA_MASK (0xffffu<<16) + +#define IXP_AUDIO_SLOTREQ (0x14) +#define IXP_AUDIO_COUNTER (0x18) +#define IXP_AUDIO_IN_FIFO_THREASHOLD (0x1c) +#define IXP_AUDIO_IN_DMA_LINK_P (0x20) +#define IXP_AUDIO_IN_DMA_LINK_P_EN (1u<<0) + +#define IXP_AUDIO_IN_DMA_DT_START (0x24) +#define IXP_AUDIO_IN_DMA_DT_NEXT (0x28) +#define IXP_AUDIO_IN_DMA_DT_CUR (0x2c) +#define IXP_AUDIO_IN_DT_SIZE_FIFO_INFO (0x30) + +#define IXP_AUDIO_OUT_DMA_SLOT_EN_THRESHOLD (0x34) +#define IXP_AUDIO_OUT_DMA_SLOT_3 (1U<<0) +#define IXP_AUDIO_OUT_DMA_SLOT_4 (1U<<1) +#define IXP_AUDIO_OUT_DMA_SLOT_5 (1U<<2) +#define IXP_AUDIO_OUT_DMA_SLOT_6 (1U<<3) +#define IXP_AUDIO_OUT_DMA_SLOT_7 (1U<<4) +#define IXP_AUDIO_OUT_DMA_SLOT_8 (1U<<5) +#define IXP_AUDIO_OUT_DMA_SLOT_9 (1U<<6) +#define IXP_AUDIO_OUT_DMA_SLOT_10 (1U<<7) +#define IXP_AUDIO_OUT_DMA_SLOT_11 (1U<<8) +#define IXP_AUDIO_OUT_DMA_SLOT_12 (1U<<9) +#define IXP_AUDIO_OUT_DMA_THRESHOLD_MASK (0x7fU<<11) +#define IXP_AUDIO_OUT_DMA_THRESHOLD_SHIFT (11) + +#define IXP_AUDIO_OUT_DMA_LINK_P (0x38) +#define IXP_AUDIO_OUT_DMA_LINK_P_EN (1U<<0) + +#define IXP_AUDIO_OUT_DMA_DT_START (0x3c) +#define IXP_AUDIO_OUT_DMA_DT_NEXT (0x40) +#define IXP_AUDIO_OUT_DMA_DT_CUR (0x44) +#define IXP_AUDIO_OUT_DT_SIZE_USED_FREE (0x48) +#define IXP_AUDIO_SPDIF_CMD (0x4c) +#define IXP_AUDIO_SPDIF_LINK_P (0x50) +#define IXP_AUDIO_SPDIF_DT_START (0x54) +#define IXP_AUDIO_SPDIF_DT_NEXT (0x58) +#define IXP_AUDIO_SPDIF_DT_CUR (0x5c) +#define IXP_AUDIO_SPDIF_DT_SIZE_FIFO_INFO (0x60) +#define IXP_AUDIO_MODEM_MIRROR (0x7c) +#define IXP_AUDIO_AUDIO_MIRROR (0x80) +#define IXP_AUDIO_6CH_RECORDER_EN (0x84) +#define IXP_AUDIO_FIFO_FLUSH (0x88) +#define IXP_AUDIO_FIFO_FLUSH_OUT (1u<<0) +#define IXP_AUDIO_FIFO_FLUSH_IN (1u<<1) + +#define IXP_AUDIO_OUT_FIFO_INFO (0x8c) +#define IXP_AUDIO_SPDIF_STATUS_BITS_REG1 (0x90) +#define IXP_AUDIO_SPDIF_STATUS_BITS_REG2 (0x94) +#define IXP_AUDIO_SPDIF_STATUS_BITS_REG3 (0x98) +#define IXP_AUDIO_SPDIF_STATUS_BITS_REG4 (0x9c) +#define IXP_AUDIO_SPDIF_STATUS_BITS_REG5 (0xa0) +#define IXP_AUDIO_SPDIF_STATUS_BITS_REG6 (0xa4) +#define IXP_AUDIO_PHY_SEMA (0xa8) + +#define IXP_INIT_NO_RESTORE (1) +#define IXP_INIT_RESTORE (0) +#define IXP_CODEC_REG(r) ((r) >> 1) + +/* + * AC97 status and link control registers are located + * in PCI configuration space. + */ +#define IXP_REG_GSR 0x40 +#define IXP_REG_GCR 0x41 + +/* AC link interface status register */ +#define IXP_GSR_PRI_READY 0x01 +#define IXP_GSR_SEC_READY 0x04 +#define IXP_GSR_TRI_READY 0x10 +#define IXP_GSR_FOUR_READY 0x20 + +#define IXP_LAST_AC_REG (0x3a) + +/* AC link interface control register */ +#define IXP_GCR_ENAC97 0x80 +#define IXP_GCR_RST 0x40 +#define IXP_GCR_RSYNCHI 0x20 +#define IXP_GCR_SDO 0x10 +#define IXP_GCR_VSR 0x08 +#define IXP_GCR_3D_AUDIO_CHANNEL 0x04 + +/* + * Macro for AD1980 codec + */ +#define AD1980_VID1 0x4144 +#define AD1980_VID2 0x5370 +#define AD1985_VID2 0x5375 +#define CODEC_AD_REG_MISC 0x76 /* offset of ad1980 misc control reg */ +#define AD1980_MISC_LOSEL 0x0020 /* Line-out amplifier output selector */ +#define AD1980_MISC_HPSEL 0x0400 /* HP-out amplifier output selector */ +#define AD1980_SURR_MUTE 0x8080 /* Mute for surround volume register */ + +/* + * Macro for ALC202 codec + */ +#define ALC202_VID1 0x414c +#define ALC202_VID2 0x4740 + +/* audioixp_state_t.flags defines */ +#define IXP_DMA_PLAY_STARTED 0x00000001 +#define IXP_DMA_PLAY_PAUSED 0x00000002 +#define IXP_DMA_PLAY_EMPTY 0x00000004 +#define IXP_DMA_RECD_STARTED 0x00000010 + +#define IXP_BD_NUMS (2) + +/* we always have 2 chunks */ +#define IXP_CHUNK_MASK (0x1l) + +#define IXP_BSIZE (8*1024) + +#define IXP_MAX_CHANNELS (200) /* force max # chs */ +#define IXP_MAX_HW_CHANNELS (32) +#define IXP_MAX_IN_CHANNELS (1) +#define IXP_MAX_OUT_CHANNELS \ + (IXP_MAX_HW_CHANNELS - IXP_MAX_IN_CHANNELS) +#define IXP_IN_STREAM (31) +#define IXP_PORT_UNMUTE (0xffffffff) + +#define IXP_MOD_SIZE (32) +#define IXP_PLAY_BUF_SZ (1024) +#define IXP_RECORD_BUF_SZ (1024) +#define IXP_BUF_MIN (512) +#define IXP_BUF_MAX (8192) + +/* + * chunk buffer + */ +struct audioixp_bdlist_chunk { + caddr_t data_buf; /* virtual address of buffer */ + uint32_t addr_phy; /* physical address of buffer */ + ddi_dma_handle_t dma_handle; /* dma handle */ + ddi_acc_handle_t acc_handle; /* access handle */ + size_t real_len; /* real len */ +}; +typedef struct audioixp_bdlist_chunk audioixp_bdlist_chunk_t; + +/* + * sample buffer + */ +struct audioixp_sample_buf { + boolean_t io_started; /* start/stop state for play/record */ + uint8_t avail; /* the number of available chunk(s) */ + uint8_t next; /* next bd entry to process */ + audioixp_bdlist_chunk_t + chunk[2]; /* 2 chunks for each buffers */ + uint32_t last_hw_pointer; +}; +typedef struct audioixp_sample_buf audioixp_sample_buf_t; + + +/* + * buffer descriptor list entry, see datasheet + */ +struct audioixp_bd_entry { + uint32_t buf_base; /* the address of the buffer */ + uint16_t status; /* status of the buffer */ + uint16_t buf_len; /* size of the buffer in DWORD */ + uint32_t next; /* physical addr of next bd_entry */ +}; +typedef struct audioixp_bd_entry audioixp_bd_entry_t; + + +/* + * we allocate all buffer descriptors lists in continuous dma memory, + * so just define the struct + */ +struct audioixp_bd_list { + audioixp_bd_entry_t pcm_in[IXP_BD_NUMS]; + audioixp_bd_entry_t pcm_out[IXP_BD_NUMS]; +}; +typedef struct audioixp_bd_list audioixp_bd_list_t; + + +/* + * audioixp_state_t -per instance state and operation data + */ +struct audioixp_state { + kmutex_t inst_lock; /* state protection lock */ + ddi_iblock_cookie_t intr_iblock; + dev_info_t *dip; /* used by audioixp_getinfo() */ + audiohdl_t audio_handle; /* audio handle */ + am_ad_info_t ad_info; /* audio device info state */ + uint16_t codec_shadow[64]; + /* shadow of AC97 registers */ + boolean_t var_sr; /* variable sample rate ? */ + boolean_t swap_out; /* swap line-out and sur-out */ + ddi_acc_handle_t pci_conf_handle; /* pci configuration space */ + ddi_acc_handle_t am_regs_handle; /* for audio mixer register */ + caddr_t am_regs_base; /* base of audio mixer regs */ + + ddi_dma_handle_t bdl_dma_handle; /* for buffer descriptor list */ + ddi_acc_handle_t bdl_acc_handle; /* access handle of bdlist */ + audioixp_bd_list_t *bdl_virtual; /* virtual address of BDL */ + audioixp_bd_list_t *bdl_phys; /* Physical address of BDL */ + size_t bdl_size; /* real len of BDL */ + + audioixp_sample_buf_t play_buf; /* buffer for playback */ + audioixp_sample_buf_t record_buf; /* buffer for record */ + int play_buf_size; /* the size of play buffer */ + int record_buf_size; /* size of in buffer */ + + audio_info_t ixp_defaults; /* default state for dev */ + audio_device_t ixp_dev_info; /* audio device info state */ + uint16_t vol_bits_mask; /* bits used to ctrl volume */ + + kstat_t *ixp_ksp; /* kernel statistics */ + uint32_t flags; /* state flags */ + + uint_t ixp_psample_rate; /* play sample rate */ + uint_t ixp_pchannels; /* play channels */ + uint_t ixp_pprecision; /* play precision */ + uint_t ixp_csample_rate; /* record sample rate */ + uint_t ixp_cchannels; /* record channels */ + uint_t ixp_cprecision; /* record precision */ + uint_t ixp_output_port; /* current out port */ + uint_t ixp_input_port; /* current input port */ + uint_t ixp_monitor_gain; /* monitor gain */ + int ixp_csamples; /* pcm-in samples/intr */ + int ixp_psamples; /* pcm-out samples/intr */ + + uint32_t ixp_res_flags; /* resource flags */ + uint32_t ixp_codec_not_ready_bits; /* for codec detect */ +}; +typedef struct audioixp_state audioixp_state_t; + +/* bits of audioixp_state_t.IXP_res_flags */ +#define IXP_RS_PCI_REGS (1u<<0) +#define IXP_RS_AM_REGS (1u<<1) +#define IXP_RS_DMA_BDL_HANDLE (1u<<2) +#define IXP_RS_DMA_BDL_MEM (1u<<3) +#define IXP_RS_DMA_BDL_BIND (1u<<4) + +/* + * Useful bit twiddlers + */ +#define IXP_BM_GET8(reg) \ + pci_config_get8(statep->pci_conf_handle, reg) + +#define IXP_BM_GET16(reg) \ + pci_config_get16(statep->pci_conf_handle, reg) + +#define IXP_BM_GET32(reg) \ + pci_config_get32(statep->pci_conf_handle, reg) + +#define IXP_BM_PUT8(reg, val) \ + pci_config_put8(statep->pci_conf_handle, reg, val) + +#define IXP_BM_PUT16(reg, val) \ + pci_config_put16(statep->pci_conf_handle, reg, val) + +#define IXP_BM_PUT32(reg, val) \ + pci_config_put32(statep->pci_conf_handle, reg, val) + +#define IXP_AM_GET32(reg) \ + ddi_get32(statep->am_regs_handle, \ + (void *)((char *)statep->am_regs_base + (reg))) + +#define IXP_AM_PUT32(reg, val) \ + ddi_put32(statep->am_regs_handle, \ + (void *)((char *)statep->am_regs_base + (reg)), (val)) + +#define IXP_AM_UPDATE8(reg, mask, value) \ + { \ + int8_t tmp; \ + tmp = IXP_AM_GET8((reg)); \ + tmp &= ~(mask); \ + tmp |= (value); \ + IXP_PUT8((reg), (tmp)); \ + } + +#define IXP_AM_UPDATE16(reg, mask, value) \ + { \ + int16_t tmp; \ + tmp = IXP_AM_GET16((reg)); \ + tmp &= ~(mask); \ + tmp |= (value); \ + IXP_PUT16((reg), (tmp)); \ + } + +#define IXP_AM_UPDATE32(reg, mask, value) \ + { \ + int32_t tmp; \ + tmp = IXP_AM_GET32((reg)); \ + tmp &= ~(mask); \ + tmp |= (value); \ + IXP_AM_PUT32((reg), (tmp)); \ + } + +#endif /* _KERNEL */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SYS_AUDIOIXP_IMPL_H_ */ diff --git a/usr/src/uts/i86pc/Makefile.i86pc.shared b/usr/src/uts/i86pc/Makefile.i86pc.shared index 47cf310002..f88512aacb 100644 --- a/usr/src/uts/i86pc/Makefile.i86pc.shared +++ b/usr/src/uts/i86pc/Makefile.i86pc.shared @@ -252,6 +252,7 @@ DRV_KMODS += kb8042 DRV_KMODS += pci-ide DRV_KMODS += audio810 DRV_KMODS += audiohd +DRV_KMODS += audioixp DRV_KMODS_32 += dnet DRV_KMODS += logi @@ -270,7 +271,6 @@ DRV_KMODS += pcic $(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x $(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens -$(CLOSED_BUILD)CLOSED_DRV_KMODS += audioixp $(CLOSED_BUILD)CLOSED_DRV_KMODS += bmc $(CLOSED_BUILD)CLOSED_DRV_KMODS += bscbus $(CLOSED_BUILD)CLOSED_DRV_KMODS += bscv diff --git a/usr/src/uts/i86pc/audioixp/Makefile b/usr/src/uts/i86pc/audioixp/Makefile new file mode 100644 index 0000000000..3d5ef5f16d --- /dev/null +++ b/usr/src/uts/i86pc/audioixp/Makefile @@ -0,0 +1,95 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# uts/i86pc/audioixp/Makefile +# +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of high definition audio +# driver (audioixp) kernel module. +# +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = audioixp +OBJECTS = $(AUDIOIXP_OBJS:%=$(OBJS_DIR)/%) +LINTS = $(AUDIOIXP_OBJS:%.o=$(LINTS_DIR)/%.ln) +ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) +CONF_SRCDIR = $(UTSBASE)/common/io/audio/sada/drv/audioixp + +# +# Include common rules. +# +include $(UTSBASE)/i86pc/Makefile.i86pc + +# +# Overrides, lint pass one enforcement +# +CFLAGS += $(CCVERBOSE) +DEBUG_FLGS = +$(NOT_RELEASE_BUILD)DEBUG_DEFS += $(DEBUG_FLGS) + +# +# Depends on misc/audiosup +# +LDFLAGS += -dy -Nmisc/amsrc2 -Nmisc/audiosup -Nmisc/mixer + +# +# Define targets +# +ALL_TARGET = $(BINARY) $(SRC_CONFILE) +LINT_TARGET = $(MODULE).lint +INSTALL_TARGET = $(BINARY) $(ROOTMODULE) $(ROOT_CONFFILE) + +# +# Default build targets. +# +.KEEP_STATE: + +def: $(DEF_DEPS) + +all: $(ALL_DEPS) + +clean: $(CLEAN_DEPS) + +clobber: $(CLOBBER_DEPS) + +lint: $(LINT_DEPS) + +modlintlib: $(MODLINTLIB_DEPS) + +clean.lint: $(CLEAN_LINT_DEPS) + +install: $(INSTALL_DEPS) + +# +# Include common targets. +# +include $(UTSBASE)/i86pc/Makefile.targ diff --git a/usr/src/uts/sparc/Makefile.sparc.shared b/usr/src/uts/sparc/Makefile.sparc.shared index c149117361..20798ec01d 100644 --- a/usr/src/uts/sparc/Makefile.sparc.shared +++ b/usr/src/uts/sparc/Makefile.sparc.shared @@ -280,7 +280,6 @@ DRV_KMODS += socal DRV_KMODS += sgen $(CLOSED_BUILD)CLOSED_DRV_KMODS += audioens -$(CLOSED_BUILD)CLOSED_DRV_KMODS += audioixp $(CLOSED_BUILD)CLOSED_DRV_KMODS += audiovia823x $(CLOSED_BUILD)CLOSED_DRV_KMODS += chxge $(CLOSED_BUILD)CLOSED_DRV_KMODS += dad diff --git a/usr/src/uts/sparc/audioixp/Makefile b/usr/src/uts/sparc/audioixp/Makefile new file mode 100644 index 0000000000..bb2905b397 --- /dev/null +++ b/usr/src/uts/sparc/audioixp/Makefile @@ -0,0 +1,108 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# uts/sparc/audioixp/Makefile +# +# ident "%Z%%M% %I% %E% SMI" +# +# This makefile drives the production of hight definition audio +# driver (audioixp) kernel module. +# +# +# Path to the base of the uts directory tree (usually /usr/src/uts). +# +UTSBASE = ../.. + +# +# Define the module and object file sets. +# +MODULE = audioixp +WARLOCK_OBJECTS = $(AUDIOIXP_OBJS:%.o=%.ll) +WARLOCK_OK = $(MODULE).ok + +# +# Include common rules. +# +include $(UTSBASE)/sparc/Makefile.sparc + +def: warlock + +clean: $(CLEAN_DEPS); \ + $(RM) $(WARLOCK_OBJECTS) $(WARLOCK_OK) + +clobber: $(CLOBBER_DEPS); \ + $(RM) $(WARLOCK_OBJECTS) $(WARLOCK_OK) + +# +# Include common targets. +# +include $(UTSBASE)/sparc/Makefile.targ + +# +# Defines for local commands. +# +SCCS = sccs +TEST = test +WLCC = wlcc +TOUCH = touch +WARLOCK = warlock + +# +# Warlock targets +# +# NOTE: there will be warnings about q_lock which is the simulated +# rwlock of the taskq framework +# + +%.wlcmd: + $(TEST) -f $@ || $(SCCS) get $@ + +warlock: $(WARLOCK_OK) + +$(WARLOCK_OK): $(WARLOCK_OBJECTS) warlock_ddi.files \ + warlock_audiosup.files warlock_mixer.files warlock_amsrc2.files + $(WARLOCK) -c audioixp_with_sada.wlcmd $(WARLOCK_OBJECTS) \ + ../audiosup/audio_support.ll ../amsrc2/am_src2.ll \ + ../mixer/am_main.ll ../mixer/am_ad.ll ../mixer/am_ioctl.ll \ + -l ../warlock/ddi_dki_impl.ll + $(TOUCH) $(WARLOCK_OK) + +%.ll: $(UTSBASE)/common/io/audio/sada/drv/audioixp/%.c + $(WLCC) $(CPPFLAGS) -DDEBUG -o $@ $< + +warlock_standalone: $(WARLOCK_OBJECTS) warlock_ddi.files audioixp.wlcmd + $(WARLOCK) -c audioixp.wlcmd $(WARLOCK_OBJECTS) \ + -l ../warlock/ddi_dki_impl.ll + +warlock_ddi.files: + @cd ../warlock; pwd; $(MAKE) warlock + +warlock_audiosup.files: + @cd ../audiosup; pwd; $(MAKE) warlock + +warlock_amsrc2.files: + @cd ../amsrc2; pwd; $(MAKE) warlock + +warlock_mixer.files: + @cd ../mixer; pwd; $(MAKE) warlock diff --git a/usr/src/uts/sparc/audioixp/audioixp.wlcmd b/usr/src/uts/sparc/audioixp/audioixp.wlcmd new file mode 100644 index 0000000000..c20eff9301 --- /dev/null +++ b/usr/src/uts/sparc/audioixp/audioixp.wlcmd @@ -0,0 +1,45 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# warlock command file + +one audioixp_state + +root audioixp_ad_pause_play +root audioixp_ad_set_config +root audioixp_ad_set_format +root audioixp_ad_start_play +root audioixp_ad_start_record +root audioixp_ad_stop_play +root audioixp_ad_stop_record + +add bus_ops::bus_add_eventcall targets warlock_dummy +add bus_ops::bus_get_eventcookie targets warlock_dummy +add bus_ops::bus_intr_ctl targets warlock_dummy +add bus_ops::bus_post_event targets warlock_dummy +add bus_ops::bus_remove_eventcall targets warlock_dummy +add bus_ops::bus_config targets warlock_dummy +add bus_ops::bus_unconfig targets warlock_dummy diff --git a/usr/src/uts/sparc/audioixp/audioixp_with_sada.wlcmd b/usr/src/uts/sparc/audioixp/audioixp_with_sada.wlcmd new file mode 100644 index 0000000000..e097a6b99d --- /dev/null +++ b/usr/src/uts/sparc/audioixp/audioixp_with_sada.wlcmd @@ -0,0 +1,130 @@ +# +# 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 +# or http://www.opensolaris.org/os/licensing. +# 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 2006 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +#pragma ident "%Z%%M% %I% %E% SMI" +# +# warlock command file + +one audioixp_state +one audio_ch +one audio_apm_info +one audio_state +one am_apm_private + +# unused functions +root audio_sup_attach +root audio_sup_ch_to_minor +root audio_sup_close +root audio_sup_construct_minor +root audio_sup_detach +root audio_sup_devt_to_ch_type +root audio_sup_devt_to_instance +root audio_sup_free_persist_state +root audio_sup_get_channel_number +root audio_sup_get_dip +root audio_sup_get_info +root audio_sup_get_max_chs +root audio_sup_get_persist_state +root audio_sup_get_qptr_data +root audio_sup_get_qptr_instance +root audio_sup_free_qptr +root audio_sup_open +root audio_sup_rput +root audio_sup_rsvc +root audio_sup_save_audio_data +root audio_sup_set_persist_state +root audio_sup_set_qptr +root audio_sup_update_persist_key +root audio_sup_wput +root audio_sup_wsvc +root audio_sup_getinfo +root audio_sup_restore_state +root audio_sup_save_state + + +# src stuff, warlock with src +root am_get_src_data +root am_hw_state_change +root am_set_src_data +root am_restore_state +root am_save_state + +# threads +root am_diag_loopback_task +root am_get_chinfo_task +root am_get_mode_task +root am_getinfo_task +root am_mixer_task_acknack +root am_mixerctl_getinfo_task +root am_mixerctl_setinfo_task +root am_multiple_open_task +root am_sample_rate_task +root am_set_chinfo_task +root am_set_mode_task +root am_setinfo_task +root am_single_open_task +root am_hw_task + +# interrupt handler + +# function pointers +add audio_ch::ch_rput targets am_rput +add audio_ch::ch_rsvc targets am_rsvc +add audio_ch::ch_wput targets am_wput +add audio_ch::ch_wsvc targets am_wsvc +add audio_apm_info::apm_open targets am_open_audio +add audio_apm_info::apm_close targets am_close_audio +add audio_apm_info::apm_open targets am_open_audioctl +add audio_apm_info::apm_close targets am_close_audioctl +add audio_apm_info::apm_restore_state targets am_restore_state +add audio_apm_info::apm_save_state targets am_save_state + + +add am_ad_entry::ad_set_config targets audioixp_ad_set_config +add am_ad_entry::ad_set_format targets audioixp_ad_set_format +add am_ad_entry::ad_start_play targets audioixp_ad_start_play +add am_ad_entry::ad_pause_play targets audioixp_ad_pause_play +add am_ad_entry::ad_stop_play targets audioixp_ad_stop_play +add am_ad_entry::ad_start_record targets audioixp_ad_start_record +add am_ad_entry::ad_stop_record targets audioixp_ad_stop_record +add am_ad_entry::ad_ioctl targets warlock_dummy +add am_ad_entry::ad_iocdata targets warlock_dummy + + +add bus_ops::bus_add_eventcall targets warlock_dummy +add bus_ops::bus_get_eventcookie targets warlock_dummy +add bus_ops::bus_intr_ctl targets warlock_dummy +add bus_ops::bus_post_event targets warlock_dummy +add bus_ops::bus_remove_eventcall targets warlock_dummy +add bus_ops::bus_config targets warlock_dummy +add bus_ops::bus_unconfig targets warlock_dummy + +add am_ad_src_entry::ad_src_adjust targets am_src2_adjust +add am_ad_src_entry::ad_src_convert targets am_src2_convert +add am_ad_src_entry::ad_src_exit targets am_src2_exit +add am_ad_src_entry::ad_src_init targets am_src2_init +add am_ad_src_entry::ad_src_size targets am_src2_size +add am_ad_src_entry::ad_src_update targets am_src2_update + +assert order audio_state::as_lock audio_ch::ch_lock audioixp_state::inst_lock diff --git a/usr/src/uts/sparc/warlock/Makefile b/usr/src/uts/sparc/warlock/Makefile index 894ca7b290..29c6b736ce 100644 --- a/usr/src/uts/sparc/warlock/Makefile +++ b/usr/src/uts/sparc/warlock/Makefile @@ -119,10 +119,10 @@ warlock.audio: @cd ../audio1575; rm -f *.ll *.ok; $(MAKE) warlock @cd ../audio810; rm -f *.ll *.ok; $(MAKE) warlock @cd ../audiohd; rm -f *.ll *.ok; $(MAKE) warlock + @cd ../audioixp; rm -f *.ll *.ok; $(MAKE) warlock @cd $(CLOSED)/uts/sparc/audiovia823x; rm -f *.ll *.ok; $(MAKE) warlock @cd ../audiocs; rm -f *.ll *.ok; $(MAKE) warlock @cd $(CLOSED)/uts/sparc/audioens; rm -f *.ll *.ok; $(MAKE) warlock - @cd $(CLOSED)/uts/sparc/audioixp; rm -f *.ll *.ok; $(MAKE) warlock @cd ../audiots; rm -f *.ll *.ok; $(MAKE) warlock @cd ../usb_ac; rm -f *.ll *.ok; $(MAKE) warlock @cd ../usb_as; rm -f *.ll *.ok; $(MAKE) warlock |