/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* audio1575 Audio Driver
*
* The driver is primarily targeted at providing audio support for
* those systems which use the Uli M1575 audio core.
*
* The M1575 audio core, in AC'97 controller mode, has independent
* channels for PCM in, PCM out, mic in, modem in, and modem out.
*
* 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. Each entry
* contains a pointer to a data buffer, control bits, and the length
* of the buffer being pointed to, where the length is expressed as
* the number of samples. This, combined with the 16-bit sample size,
* gives the actual physical length of the buffer.
*
* NOTE:
* modules being loaded first.
*/
#include "audio1575.h"
/*
* Module linkage routines for the kernel
*/
static int audio1575_ddi_quiesce(dev_info_t *);
/*
* Entry point routine prototypes
*/
static int audio1575_open(void *, int, unsigned *, caddr_t *);
static void audio1575_close(void *);
static int audio1575_start(void *);
static void audio1575_stop(void *);
static int audio1575_format(void *);
static int audio1575_channels(void *);
static int audio1575_rate(void *);
static uint64_t audio1575_count(void *);
static void audio1575_sync(void *, unsigned);
NULL,
NULL,
};
/*
* Local Routine Prototypes
*/
static int audio1575_attach(dev_info_t *);
static int audio1575_resume(dev_info_t *);
static int audio1575_detach(dev_info_t *);
static int audio1575_suspend(dev_info_t *);
static void audio1575_free_port(audio1575_port_t *);
static int audio1575_codec_sync(audio1575_state_t *);
static int audio1575_chip_init(audio1575_state_t *);
static int audio1575_map_regs(audio1575_state_t *);
static void audio1575_unmap_regs(audio1575_state_t *);
static void audio1575_pci_enable(audio1575_state_t *);
static void audio1575_pci_disable(audio1575_state_t *);
static void audio1575_destroy(audio1575_state_t *);
/*
* Global variables, but used only by this file.
*/
/*
* DDI Structures
*/
/* Device operations structure */
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
NULL, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
audio1575_ddi_attach, /* devo_attach */
audio1575_ddi_detach, /* devo_detach */
nodev, /* devo_reset */
NULL, /* devi_cb_ops */
NULL, /* devo_bus_ops */
NULL, /* devo_power */
audio1575_ddi_quiesce, /* devo_quiesce */
};
/* Linkage structure for loadable drivers */
&mod_driverops, /* drv_modops */
M1575_MOD_NAME, /* drv_linkinfo */
&audio1575_dev_ops, /* drv_dev_ops */
};
/* Module linkage structure */
MODREV_1, /* ml_rev */
(void *)&audio1575_modldrv, /* ml_linkage */
NULL /* NULL terminates the list */
};
/*
* device access attributes for register mapping
*/
};
};
/*
* DMA attributes of buffer descriptor list
*/
DMA_ATTR_V0, /* version */
0x0000000000000000LL, /* dlim_addr_lo */
0x00000000ffffffffLL, /* dlim_addr_hi */
0x000000000000ffffLL, /* DMA counter register - 64 bits */
0x0000000000000008LL, /* DMA address align must be 8-bytes */
0x0000003c, /* 1 through 64 byte burst sizes */
0x00000008, /* min xfer DMA size BDList entry */
0x00000000000ffffLL, /* max xfer size, 64K */
0x000000000001fffLL, /* seg, set to PAGESIZE */
0x00000001, /* s/g list length, no s/g */
0x00000008, /* granularity of device minxfer */
0 /* DMA flags use virtual address */
};
/*
*/
0x0000000000000000LL, /* dlim_addr_lo */
0x00000000ffffffffLL, /* dlim_addr_hi */
0x000000000001fffeLL, /* DMA counter register - 16 bits */
0x0000000000000004LL, /* DMA address align 2-byte boundary */
0x0000003c, /* 1 through 60 byte burst sizes */
0x00000004, /* min xfer DMA size BDList entry */
0x000000000001ffffLL, /* max xfer size, 64K */
0x000000000001ffffLL, /* seg, set to 64K */
0x00000001, /* s/g list length, no s/g */
0x00000004, /* granularity of device minxfer */
0 /* DMA flags use virtual address */
};
/*
* _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:
* mod_install() status, see mod_install(9f)
*/
int
_init(void)
{
int error;
}
return (error);
}
/*
* _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;
return (error);
}
/* clean up ops */
return (0);
}
/*
* _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
{
}
/* ******************* Driver Entry Points ********************************* */
/*
* audio1575_ddi_attach()
*
* Description:
* Implements the DDI attach(9e) entry point.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
* ddi_attach_cmd_t cmd Attach command
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
switch (cmd) {
case DDI_ATTACH:
return (audio1575_attach(dip));
case DDI_RESUME:
return (audio1575_resume(dip));
}
return (DDI_FAILURE);
}
/*
* audio1575_ddi_detach()
*
* Description:
* Implements the detach(9e) entry point.
*
* 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
{
switch (cmd) {
case DDI_DETACH:
return (audio1575_detach(dip));
case DDI_SUSPEND:
return (audio1575_suspend(dip));
}
return (DDI_FAILURE);
}
/*
* audio1575_ddi_quiesce()
*
* Description:
* Implements the quiesce(9e) entry point.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was quiesced
* DDI_FAILURE The driver couldn't be quiesced
*/
static int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* audio1575_open()
*
* Description:
* Opens a DMA engine for use.
*
* Arguments:
* void *arg The DMA engine to set up
* int flag Open flags
* unsigned *nframesp Receives number of frames
* caddr_t *bufp Receives kernel data buffer
*
* Returns:
* 0 on success
* errno on failure
*/
static int
{
return (0);
}
/*
* audio1575_close()
*
* Description:
* Closes an audio DMA engine that was previously opened. Since
* nobody is using it, we take this opportunity to possibly power
* down the entire device.
*
* Arguments:
* void *arg The DMA engine to shut down
*/
static void
{
}
/*
* audio1575_stop()
*
* Description:
* This is called by the framework to stop a port that is
* transferring data.
*
* Arguments:
* void *arg The DMA engine to stop
*/
static void
{
} else {
}
}
/*
* audio1575_start()
*
* Description:
* This is called by the framework to start a port transferring data.
*
* Arguments:
* void *arg The DMA engine to start
*
* Returns:
* 0 on success (never fails, errno if it did)
*/
static int
{
/* Uli FIFO madness ... */
PUT8(M1575_PCMICR_REG, 0);
/* ULi says do fifo resets here */
PUT8(M1575_PCMICR_REG, 0);
} else {
/* Uli FIFO madness ... */
/* configure the number of channels properly */
case 2:
break;
case 4:
break;
case 6:
break;
}
PUT8(M1575_PCMOCR_REG, 0);
PUT8(M1575_PCMOCR_REG, 0);
}
return (0);
}
/*
* audio1575_format()
*
* Description:
* Called by the framework to query the format for the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* AUDIO_FORMAT_S16_LE
*/
static int
{
return (AUDIO_FORMAT_S16_LE);
}
/*
* audio1575_channels()
*
* Description:
* Called by the framework to query the channels for the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* Number of channels for the device
*/
static int
{
}
/*
* audio1575_rate()
*
* Description:
* Called by the framework to query the sample rate for the device.
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* 48000
*/
static int
{
return (48000);
}
/*
* audio1575_count()
*
* Description:
* This is called by the framework to get the engine's frame counter
*
* Arguments:
* void *arg The DMA engine to query
*
* Returns:
* frame count for current engine
*/
static uint64_t
{
unsigned n;
int civoff;
int lvioff;
int picoff;
} else {
}
/*
* Read the position counters. We also take this opportunity
* to update the last valid index to the one just previous to
* the one we're working on (so we'll fully loop.)
*/
} else {
}
return (val);
}
/*
* audio1575_sync()
*
* Description:
* This is called by the framework to synchronize DMA caches.
*
* Arguments:
* void *arg The DMA engine to sync
*/
static void
{
}
/*
* audio1575_attach()
*
* Description:
* Attach an instance of the audio1575 driver. This routine does the
* device dependent attach tasks. When it is completed, it registers
* with the audio framework.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was initialized properly
* DDI_FAILURE The driver couldn't be initialized properly
*/
static int
{
const char *name;
const char *rev;
int maxch;
/* allocate the soft state structure */
/*
* We want the micboost enabled by default as well.
*/
/* allocate common audio dev structure */
goto error;
}
/* map in the audio registers */
goto error;
}
/* Enable PCI I/O and Memory Spaces */
switch (devid) {
case 0x10b95455:
name = "Uli M1575 AC'97";
rev = "M5455";
break;
default:
name = "Uli AC'97";
rev = "Unknown";
break;
}
/* set device information -- this should check PCI config space */
/*
* Override "max-channels" property to prevent configuration
* of 4 or 6 (or possibly even 8!) channel audio. The default
* is to support as many channels as the hardware can do.
*/
if (maxch < 2) {
maxch = 2;
}
/* allocate port structures */
DDI_SUCCESS) ||
goto error;
}
goto error;
}
goto error;
}
/* register with the framework */
goto error;
}
/* everything worked out, so report the device */
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* audio1575_detach()
*
* Description:
* Detach an instance of the audio1575 driver.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was detached
* DDI_FAILURE The driver couldn't be detached
*/
static int
{
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* *********************** Local Routines *************************** */
/*
* audio1575_alloc_port()
*
* Description:
* This routine allocates the DMA handles and the memory for the
* DMA engines to use. It also configures the BDL lists properly
* for use.
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
* int num M1575_PLAY or M1575_REC
* uint8_t nchan Number of channels (2 = stereo, 6 = 5.1, etc.)
*
* Returns:
* DDI_SUCCESS Registers successfully mapped
* DDI_FAILURE Registers not successfully mapped
*/
static int
{
int dir;
unsigned caps;
int rc;
dir = DDI_DMA_READ;
} else {
dir = DDI_DMA_WRITE;
}
/*
* We use one big sample area. The sample area must be larger
* than about 1.5 framework fragment sizes. (Currently 480 *
* 1.5 = 720 frames.) This is necessary to ensure that we
* don't have to involve an interrupt service routine on our
* own, to keep the last valid index updated reasonably.
*/
/* allocate dma handle */
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/* allocate DMA buffer */
if (rc == DDI_FAILURE) {
return (DDI_FAILURE);
}
/* bind DMA buffer */
"ddi_dma_addr_bind_handle failed: %d", rc);
return (DDI_FAILURE);
}
/*
* now, from here we allocate DMA memory for buffer descriptor list.
* we allocate adjacent DMA memory for all DMA engines.
*/
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* we allocate all buffer descriptors lists in continuous dma memory.
*/
if (rc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Wire up the BD list. We do this *before* binding the BD list
* so that we don't have to do an extra ddi_dma_sync.
*/
for (int i = 0; i < M1575_BD_NUMS; i++) {
/* set base address of buffer */
kaddr++;
/* set size in frames, and enable IOC interrupt */
kaddr++;
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* audio1575_free_port()
*
* Description:
* This routine unbinds the DMA cookies, frees the DMA buffers,
* deallocates the DMA handles.
*
* Arguments:
* audio1575_port_t *port The port structure for a DMA engine.
*/
static void
{
return;
}
}
}
}
if (port->samp_paddr) {
}
}
}
}
/*
* audio1575_map_regs()
*
* Description:
* The registers are mapped in.
*
* Arguments:
* dev_info_t *dip Pointer to the device's devinfo
*
* Returns:
* DDI_SUCCESS Registers successfully mapped
* DDI_FAILURE Registers not successfully mapped
*/
static int
{
/* map the M1575 Audio PCI Cfg Space */
goto error;
}
/* map the M1575 Audio registers in PCI IO Space */
goto error;
}
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
/*
* audio1575_unmap_regs()
*
* Description:
* This routine unmaps control registers.
*
* Arguments:
* audio1575_state_t *state The device's state structure
*/
static void
{
}
}
}
/*
* audio1575_chip_init()
*
* Description:
* This routine initializes the M1575 AC97 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. This routine expects that the
* PCI IO and Memory spaces have been mapped and enabled already.
* Arguments:
* audio1575_state_t *state The device's state structure
* restore from codec_shadow[]
* Returns:
* DDI_SUCCESS The hardware was initialized properly
* DDI_FAILURE The hardware couldn't be initialized properly
*/
static int
{
int i;
int j;
#ifdef __sparc
#endif
/*
* clear the interrupt control and status register
* for buggy hardware
*/
PUT32(M1575_INTRCR_REG, 0);
(void) GET32(M1575_INTRCR_REG);
(void) GET32(M1575_INTRSR_REG);
/*
* SADA only supports stereo, so we set the channel bits
* to "00" to select 2 channels.
* will also set the following:
*
* Disable double rate enable
* no SPDIF output selected
* 16 bit audio record mode
* 16 bit pcm out mode
* PCM Out 6 chan mode FL FR CEN BL BR LFE
* PCM Out 2 channel mode (00)
*/
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Reset the AC97 Codec and default to 2 channel 16 bit mode */
/* Read the System Status Reg */
/* make sure and release the blocked reset bit */
if (ssr & M1575_SSR_RSTBLK) {
/* Read the System Status Reg */
/* make sure and release the blocked reset bit */
if (ssr & M1575_SSR_RSTBLK) {
return (DDI_FAILURE);
}
/* Reset the controller */
}
/* according AC'97 spec, wait for codec reset */
for (j = 0; j < M1575_LOOP_CTR; j++) {
break;
}
}
/* codec reset failed */
if (j >= M1575_LOOP_CTR) {
"failure to reset codec");
return (DDI_FAILURE);
}
/*
* Wait for FACRDY First codec ready. The hardware can
* provide the state of
* codec ready bit on SDATA_IN[0] and as reflected in
* the Recv Tag Slot Reg.
*/
if (rtsr & M1575_RTSR_FACRDY) {
break;
} else { /* reset the status and wait for new status to set */
drv_usecwait(10);
}
}
/* if we could not reset the AC97 codec then report failure */
if (i >= M1575_LOOP_CTR) {
"no codec ready signal received");
return (DDI_FAILURE);
}
#ifdef __sparc
/* Magic code from ULi to Turn on the AC_LINK clock */
if (clk_detect != 1) {
return (DDI_FAILURE);
}
#endif
/* Magic code from Uli to Init FIFO1 and FIFO2 */
/* Make sure that PCM in and PCM out are enabled */
return (DDI_SUCCESS);
}
/*
* audio1575_dma_stop()
*
* Description:
* This routine is used to put each DMA engine into the quiet state.
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*/
static void
{
int i;
return;
}
/* pause bus master (needed for the following reset register) */
for (i = 0; i < M1575_LOOP_CTR; i++) {
break;
}
drv_usecwait(10);
}
if (i >= M1575_LOOP_CTR) {
if (!quiesce)
return;
}
/* Pause bus master (needed for the following reset register) */
PUT8(M1575_PCMICR_REG, 0);
PUT8(M1575_PCMOCR_REG, 0);
PUT8(M1575_MICICR_REG, 0);
PUT8(M1575_CSPOCR_REG, 0);
PUT8(M1575_PCMI2CR_RR, 0);
PUT8(M1575_MICI2CR_RR, 0);
/* Reset the bus master registers for all DMA engines */
/* Reset FIFOS */
/* Clear Interrupts */
/*
* clear the interrupt control and status register
*/
PUT32(M1575_INTRCR_REG, 0);
(void) GET32(M1575_INTRCR_REG);
(void) GET32(M1575_INTRSR_REG);
}
/*
* audio1575_codec_sync()
*
* Description:
* Serialize access to the AC97 audio mixer registers.
*
* Arguments:
* audio1575_state_t *state The device's state structure
*
* Returns:
* DDI_SUCCESS Ready for an I/O access to the codec
* DDI_FAILURE An I/O access is currently in progress, can't
* perform another I/O access.
*/
static int
{
/* do the Uli Shuffle ... */
for (int i = 0; i < M1575_LOOP_CTR; i++) {
/* Read the semaphore, and loop till we own it */
for (int j = 0; j < M1575_LOOP_CTR; j++) {
/* Wait for CWRSUCC 0x8 */
if (GET32(M1575_CSPSR_REG) &
return (DDI_SUCCESS);
}
drv_usecwait(1);
}
}
drv_usecwait(10);
}
return (DDI_FAILURE);
}
/*
* audio1575_write_ac97()
*
* Description:
* Set the specific AC97 Codec register.
*
* Arguments:
* void *arg The device's state structure
* uint8_t reg AC97 register number
* uint16_t data The data want to be set
*/
static void
{
int i;
return;
}
/* write the data to WRITE to the lo word of the CPR register */
/* write the address to WRITE to the hi word of the CPR register */
/* wait until command is completed sucessfully */
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Wait for Write Ready 0x01 */
break;
}
drv_usecwait(1);
}
if (i < M1575_LOOP_CTR) {
}
}
/*
* audio1575_read_ac97()
*
* Description:
* Get the specific AC97 Codec register. It also updates codec_shadow[]
* with the register value.
*
* Arguments:
* void *arg The device's state structure
* uint8_t reg AC97 register number
*
* Returns:
* Value of AC97 register. (0xffff in failure situations).
*/
static uint16_t
{
int i;
return (data);
}
/*
* at this point we have the CASR semaphore
* and the codec is r/w ready
* OR in the READ opcode into the address field
*/
/* write the address to READ to the hi word of the CPR register */
/* wait until command is completed sucessfully */
for (i = 0; i < M1575_LOOP_CTR; i++) {
/* Wait for Read Ready 0x02 */
break;
}
drv_usecwait(1);
}
if (i < M1575_LOOP_CTR) {
/* read back the data and address */
data = 0xffff;
}
}
return (data);
}
/*
* audio1575_pci_enable()
*
* Description:
* This routine Enables all PCI IO and MEMORY accesses
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*/
static void
{
}
/*
* audio1575_pci_disable()
*
* Description:
* This routine Disables all PCI IO and MEMORY accesses
*
* Arguments:
* audio1575_state_t *statep The device's state structure
*/
static void
{
return;
}
/*
* audio1575_resume()
*
* Description:
* Resume operation of the device after sleeping or hibernating.
* Note that this should never fail, even if hardware goes wonky,
* because the current PM framework will panic if it does.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was resumed
*/
static int
{
/* we've already allocated the state structure so get ptr */
/*
* Note that PM gurus say we should return
* success here. Failure of audio shouldn't
* be considered FATAL to the system. The
* upshot is that audio will not progress.
*/
return (DDI_SUCCESS);
}
/* allow ac97 operations again */
return (DDI_SUCCESS);
}
/*
* audio1575_suspend()
*
* Description:
* Suspend an instance of the audio1575 driver.
*
* Arguments:
* dev_info_t *dip Pointer to the device's dev_info struct
*
* Returns:
* DDI_SUCCESS The driver was suspended
*/
static int
{
return (DDI_SUCCESS);
}
/*
* audio1575_destroy()
*
* Description:
* This routine releases all resources held by the device instance,
* as part of either detach or a failure in attach.
*
* Arguments:
* audio1575_state_t *state The device soft state.
*/
void
{
/* stop DMA engines */
/* reset the codec */
}
/* turn off the AC_LINK clock */
}
/* Disable PCI I/O and Memory Spaces */
}
}
}