README revision 7c478bd95313f5f23a4c958a745db2134aa03244
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (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 2004 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
# Design Document for Metropolis (Amd8111/ALC655) Audio Driver
1. Technical overview
SADA (Solaris audio driver architecture) is the native audio framework of
Solaris. It supports quickly writing new STREAMS audio drivers by providing
a number of library modules. There are two types of library modules, the
audio support module and audio personality modules. The unique audio support
module provides driver instance, buffer and state management routines. Audio
personality modules implement the semantics of the device interface. The mixer
module, as a personality module, implements the semantics of the audio(7I) and
mixer(7I) man pages. And the another personality module, amsrc2, provides the
sample rate conversion for mixer. So a SADA driver with mixer function would
communicate with audio support module, mixer module, and amsrc2 module or
another conversion module.
1.1 Audio support module
The audio support module provides a number of common routines for managing the
instances and state information for the audio driver. Audio drivers that don't
need special open(), close(), put(), svc(), and getinfo() routines set their
qinit and dev_ops data structures as follows:
/* read queue */
static struct qinit xx_rqueue = {
audio_sup_rput, /* put procedure */
audio_sup_rsvc, /* service procedure */
audio_sup_open, /* open procedure */
audio_sup_close, /* close procedure */
NULL, /* unused */
&xx_modinfo, /* module parameters */
NULL /* module statistics */
};
/* write queue */
static struct qinit xx_wqueue = {
audio_sup_wput, /* put procedure */
audio_sup_wsvc, /* service procedure */
NULL, /* open procedure */
NULL, /* close procedure */
NULL, /* unused */
&xx_modinfo, /* module parameters */
NULL /* module statistics */
};
/* Device operations structure */
static struct dev_ops audiots_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* devo_refcnt */
audio_sup_getinfo, /* devo_getinfo */
nulldev, /* devo_identify - obsolete */
nulldev, /* devo_probe */
xx_attach, /* devo_attach */
xx_detach, /* devo_detach */
nodev, /* devo_reset */
&xx_cb_ops, /* devi_cb_ops */
NULL, /* devo_bus_ops */
xx_power /* devo_power */
};
The audio support module manages the device minor nodes for each
instance of an audio device. Minor nodes are mapped as follows
0 AUDIO /dev/audio, /dev/sound/?
1 AUDIOCTL /dev/audioctl, /dev/sound/?ctl
2 WTABLE future wave table synthesizer
3 MIDI future hardware MIDI ports
4 ATIME future audio timer, probably something like
SMPTE, which is used to sync audio and video,
there are a lot of MIDI devices that also
provide SMPTE information
5 - 7 unassigned
8 USER1 available for any use
9 USER2 available for any use
10 USER3 available for any use
11 - 127 used when the open is cloned
For example, if an app opens /dev/audio that means that minor node 0
was opened. audiosup sees this and knows that it needs to call
am_open() in the mixer, which it does. am_open() enforces the open()
semantics for /dev/audio. Assuming all is legal it checks looks for
the first clone minor node that isn't being used. It then constructs
a new device number based on this new minor number and this is
returned to the kernel. Thus when open() returns you have a cloned
node. Since the mixer doesn't make the node you don't see an entry
in /devices, it is invisible.
The number of minor numbers at the beginning is tunable, as is the
total minor numbers per instance.
1.2 Audio Mixer Module
The mixer implements the semantics of the audio(7I) and mixer(7I)
man pages. Doing so it is the module that provides /dev/audio(ctl).
The mixer supports two modes of operation, mixer on and mixer off.
When the mixer is off it supports the audio(7I) semantics and just
a few new ioctl()s from mixer(7I). Essentially it enforces only one
play and one record stream. Also, the audioctl devices represents
the hardware.
When the mixer is enabled it takes an audio stream, converts it to
32 bit signed linear PCM and saves this converted data as well as
the original data. When the audio driver needs audio to play the
mixer scans all clone channels looking for AUDIO play channels. When
one is found the audio data is retrieved and summed. When all audio
data is summed it is clipped and converted to the format that the
audio device is programmed to use. All semantics of audio(7I) and
mixer(7I) are available. One of the biggest differences is that
the audioctl channel may now represent either the audio device or the
clone channel. The rules for this is very complicated and are
explained elsewhere.
The mixer also supports record, which is essentially the play in
reverse. The biggest difference is that record doesn't save any
messages. As for play, the mixer may be on or off. If off then the
recorded audio is passed straight to the application. If on then
the mixer scans all clone channels looking for AUDIO record channels.
When one is found the audio is converted to the right format and
sent to the application. For both mixer modes the recorded audio is
queued up in a buffer before it is sent to the application. The size
of this buffer is set by the record buffer size.
1.3 Amsrc Module
This is the sample rate converted that the mixer uses when the mixer
is enabled. Currently there are just two sample rate converter modules.
However the mixer supports the audio driver author providing his/her
own. This can be for performance or audio quality reasons. In this
project we use the amsrc2 module.
2. Hardware Configuration
This driver is for the low end audio configuration of Metropols
workstation. This configuration consists of two components, controller
and codec. The controller is integrated in the southbridge, amd8111, as
a function of amd8111. And the ALC655 codec is an AC97 2.3 compatible
codec. It is a 16-bit full-duplex six-channel audio codec with fixed
48khz sampling rate.
3. Software Environment
Because SADA framework supports only mono/stereo at most, which means
SADA does not support multi-channel(3, 4, 6 channel), this driver cannot
support multi-channel unless we modify SADA framework. We plan to use
Solaris 10&9 as the target operating system for the driver.
4. The features/functions
The driver will be placed in /kernel/drv. Like other audio drivers,
the driver can be added/removed by users via "add_drv/rem_drv" tools.
5. The source code structure
The driver consists of three files, audio810.c, audio810_impl.h and
audio810.h. The audio810.c is the C source file which contains all
the C codes of the driver. The audio810.h is the header file containing
only some baud rate definitions. The audio810_impl.h also a header file
which contains hardware specific information, such as data structures.
In audio810.c file, we implement the entry functions defined by mixer
module as follows:
1) audio810_ad_set_config, /* ad_set_config() */
2) audio810_ad_set_format, /* ad_set_format() */
3) audio810_ad_start_play, /* ad_start_play() */
4) audio810_ad_pause_play, /* ad_pause_play() */
5) audio810_ad_stop_play, /* ad_stop_play() */
6) audio810_ad_start_record, /* ad_start_record() */
7) audio810_ad_stop_record, /* ad_stop_record() */
6. The implementations
In this section, we describe the design of the driver in detail.
6.1 Main data structure
6.1.1 Buffer descriptor list
According to the datasheet of amd8111, the buffer descriptor list
(BDL) allows device drivers to program DMA transfer using by the
audio controller. The BDL is an array of up to 32 entry, each of
which describes a data buffer. So we define the BDL entry structure
as follows:
struct i810_bd_entry {
uint32_t buf_base; /* the address of the buffer */
uint16_t buf_len; /* the number of samples */
uint16_t reserved:14;
uint8_t cmd_bup:1; /* interrupt if underflows */
uint8_t cmd_ioc:1; /* interrupt if completing this entry */
};
AMD8111 audio function has separated DMA engine for PCM in/out, so
we must set the two BDLs. For simplification, we allocate continuous
DMA memory for all BDLs, and define the BDL as follows,
struct i810_bd_list {
i810_bd_entry_t pcm_in[I810_BD_NUMS];
i810_bd_entry_t pcm_out[I810_BD_NUMS];
};
6.1.2 Data buffer for BDL entry
For simplification, we allocate DMA buffer for each BDL entry.
struct i810_bdlist_chunk {
caddr_t data_buf; /* virtual address of buffer */
caddr_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 */
};
6.1.3 Sample buffer
struct i810_sample_buf {
boolean_t io_started; /* start/stop state for play/record */
int avail; /* the number of available chunk(s) */
uint8_t tail; /* For CPU, 1st available BD entry */
uint8_t head; /* For CPU, 1st BD entry to reclaim */
i810_bdlist_chunk_t chunk[2]; /* 2 chunks for each buffers */
};
6.1.4 Soft state
Every instance of the hardware should have a soft state to save
instance-wide data in it.
struct audio810_state {
kmutex_t inst_lock; /* state protection lock */
ddi_iblock_cookie_t intr_iblock;
dev_info_t *dip; /* used by audio810_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 ? */
ddi_acc_handle_t pci_conf_handle; /* pci configuration space */
ddi_acc_handle_t am_regs_handle; /* for audio mixer register */
ddi_acc_handle_t bm_regs_handle; /* for bus master register */
caddr_t am_regs_base; /* base of audio mixer regs */
caddr_t bm_regs_base; /* base of bus master regs */
ddi_dma_handle_t bdl_dma_handle; /* for buffer descriptor list */
ddi_acc_handle_t bdl_acc_handle; /* access handle of bdlist */
i810_bd_list_t *bdl_virtual; /* virtual address of BDL */
i810_bd_list_t *bdl_phys; /* Physical address of BDL */
size_t bdl_size; /* real len of BDL */
i810_sample_buf_t play_buf; /* buffer for playback */
i810_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 i810_defaults; /* default state for dev */
audio_device_t i810_dev_info; /* audio device info state */
uint16_t vol_bits_mask; /* bits used to ctrl volume */
kstat_t *i810_ksp; /* kernel statistics */
uint32_t flags; /* state flags */
uint_t i810_psample_rate; /* play sample rate */
uint_t i810_pchannels; /* play channels */
uint_t i810_pprecision; /* play precision */
uint_t i810_csample_rate; /* record sample rate */
uint_t i810_cchannels; /* record channels */
uint_t i810_cprecision; /* record precision */
uint_t i810_output_port; /* current out port */
uint_t i810_input_port; /* current input port */
uint_t i810_monitor_gain; /* monitor gain */
int i810_csamples; /* pcm-in samples/interrupt */
int i810_psamples; /* pcm-out samples/intr */
uint32_t i810_res_flags; /* resource flags */
};
6.2 Interrupt handler
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. The structure i810_sample_buf (Refer to 6.1.3)
is defined for the sample buffer. This structure has two chunks(data
buffers). The software uses the head, tail and avail fields of this
structure to manipulate the FIFO. Head field indicates the first valid
BD hardware can manipulate, and tail indicates the BD after the last
valid BD, avail indicates how many buffers (chunks) are available.
There're also two hardware registers to index the FIFO, the CIV (current
index value) indicates the current BD the hardware is transferring, the
LVI (last valid index) indicates the last valid BD that contains proper
data, which the hardware should not pass over. Each time a BD is
processed, the hardware will update the CIV (plus 1), and issue an
interrupt if the cmd_ioc field of this processed BD entry is set. If
the system is busy, there can be more than one BD to be processed when
the OS has chance to handle the interrupt. Except for the initialization
at the beginning of playback/record, the driver do not modify the
content of CIV register.
CIV=3 LVI=3 (CIV is updated by hardware)
----------------------------------------------------------------
0 1 2 3 31
----------------------------------------------------------------
head=2 --> (reclaim, from head to CIV)
tail=3 --> (fill, from tail to head)
(a) before reclaiming
CIV=3 LVI=3
----------------------------------------------------------------
0 1 2 3 31
----------------------------------------------------------------
head=3 --> (reclaim, from head to CIV)
tail=3 --> (fill, from tail to head)
(b) after reclaiming/before filling
CIV=3 LVI=5(LVI is updated by software)
----------------------------------------------------------------
0 1 2 3 4 5 31
----------------------------------------------------------------
head=3 --> (reclaim, from head to CIV)
tail=5 --> (fill, from tail to head)
(c) after filling
Fig 1. Interrupt handler(one pass)
Note:
Here, we use only two BDs at any time, so when we "fill" BD,
we can't "fill" all BDs from tail to head.
tail - head <=2 , when tail>=head;
tail + 32 - head <=2, when tail<head;
Now, let us explain what the interrupt handler does. It will first
reclaim the BD(s) which had been transferred, which ranges from
"head" to (CIV - 1). This job is done in the routine
audio810_reclaim_play_buf(...). Then it invokes audio810_fill_play_buf
(...), fills audio samples into buffers pointed by the available BDs
beginning from "tail", updates the LVI to to the last processed BD
and update tail to LVI + 1, and update the avail to 0. we use 2 buffers
and let the 0, 2, 4, 6, ..., 28, 30 BDs pointing to the first buffer,
let the 1, 3, 5, ..., 29, 31 BDs pointing to the second buffer, so the
hardware and software will manipulate the two buffer in turn.