Commit b793b71f authored by Jaroslav Kysela's avatar Jaroslav Kysela

Merge suse.cz:/home/perex/bk/linux-sound/linux-2.5

into suse.cz:/home/perex/bk/linux-sound/linux-sound
parents 6e9aed30 700cb4e5
......@@ -692,6 +692,15 @@ Module parameters
The power-management is supported.
Module snd-mixart
-----------------
Module for Digigram miXart8 soundcards.
Module supports multiple cards.
Note: One miXart8 board will be represented as 4 alsa cards.
See MIXART.txt for details.
Module snd-mpu401
-----------------
......
Alsa driver for Digigram miXart8 and miXart8AES/EBU soundcards
Digigram <alsa@digigram.com>
GENERAL
=======
The miXart8 is a multichannel audio processing and mixing soundcard
that has 4 stereo audio inputs and 4 stereo audio outputs.
The miXart8AES/EBU is the same with a add-on card that offers further
4 digital stereo audio inputs and outputs.
Furthermore the add-on card offers external clock synchronisation
(AES/EBU, Word Clock, Time Code and Video Synchro)
The mainboard has a PowerPC that offers onboard mpeg encoding and
decoding, samplerate conversions and various effects.
The driver don't work properly at all until the certain firmwares
are loaded, i.e. no PCM nor mixer devices will appear.
Use the mixartloader that can be found in the alsa-tools package.
VERSION 0.1.0
=============
One miXart8 board will be represented as 4 alsa cards, each with 1
stereo analog capture 'pcm0c' and 1 stereo analog playback 'pcm0p' device.
With a miXart8AES/EBU there is in addition 1 stereo digital input
'pcm1c' and 1 stereo digital output 'pcm1p' per card.
Formats
-------
U8, S16_LE, S16_BE, S24_3LE, S24_3BE, FLOAT_LE, FLOAT_BE
Sample rates : 8000 - 48000 Hz continously
Playback
--------
For instance the playback devices are configured to have max. 4
substreams performing hardware mixing. This could be changed to a
maximum of 24 substreams if wished.
Mono files will be played on the left and right channel. Each channel
can be muted for each stream to use 8 analog/digital outputs seperately.
Capture
-------
There is one substream per capture device. For instance only stereo
formats are supported.
Mixer
-----
<Master> and <Master Capture> : analog volume control of playback and capture PCM.
<PCM 0-3> and <PCM Capture> : digital volume control of each analog substream.
<AES 0-3> and <AES Capture> : digital volume control of each AES/EBU substream.
<Monitoring> : Loopback from 'pcm0c' to 'pcm0p' with digital volume
and mute control.
Rem : for best audio quality try to keep a 0 attenuation on the PCM
and AES volume controls which is set by 219 in the range from 0 to 255
(about 86% with alsamixer)
NOT YET IMPLEMENTED
===================
- external clock support (AES/EBU, Word Clock, Time Code, Video Sync)
- MPEG audio formats
- mono record
- on-board effects and samplerate conversions
- linked streams
FIRMWARE
========
For loading the firmware automatically after the module is loaded, use
the post-install command. For example, add the following entry to
/etc/modprobe.conf for miXart driver:
install snd-mixart /sbin/modprobe --first-time -i snd-mixart && \
/usr/bin/mixartloader
(for 2.2/2.4 kernels, add "post-install snd-mixart /usr/bin/vxloader" to
/etc/modules.conf, instead.)
The firmware binaries are installed on /usr/share/alsa/firmware
(or /usr/local/share/alsa/firmware, depending to the prefix option of
configure). There will be a miXart.conf file, which define the dsp image
files.
The firmware files are copyright by Digigram SA
COPYRIGHT
=========
Copyright (c) 2003 Digigram SA <alsa@digigram.com>
Distributalbe under GPL.
......@@ -50,6 +50,7 @@ typedef struct _snd_pcm_oss_runtime {
unsigned int maxfrags;
unsigned int subdivision; /* requested subdivision */
size_t period_bytes; /* requested period size */
size_t period_frames; /* period frames for poll */
size_t period_ptr; /* actual write pointer to period */
unsigned int periods;
size_t buffer_bytes; /* requested buffer size */
......
......@@ -107,5 +107,6 @@ obj-$(CONFIG_SND_VXPOCKET) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-h
obj-$(CONFIG_SND_VXP440) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
obj-$(CONFIG_SND_VX222) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
obj-$(CONFIG_SND_BT87X) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o
obj-$(CONFIG_SND_MIXART) += snd-pcm.o snd-timer.o snd-page-alloc.o snd.o snd-hwdep.o
obj-m := $(sort $(obj-m))
......@@ -133,6 +133,15 @@ static long snd_pcm_oss_bytes(snd_pcm_substream_t *substream, long frames)
return (runtime->oss.buffer_bytes * frames) / buffer_size;
}
static long snd_pcm_alsa_frames(snd_pcm_substream_t *substream, long bytes)
{
snd_pcm_runtime_t *runtime = substream->runtime;
snd_pcm_uframes_t buffer_size = snd_pcm_lib_buffer_bytes(substream);
if (buffer_size == runtime->oss.buffer_bytes)
return bytes_to_frames(runtime, bytes);
return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
}
static int snd_pcm_oss_format_from(int format)
{
switch (format) {
......@@ -254,6 +263,7 @@ static int snd_pcm_oss_period_size(snd_pcm_substream_t *substream,
snd_assert(oss_period_size >= 16, return -EINVAL);
runtime->oss.period_bytes = oss_period_size;
runtime->oss.period_frames = 1;
runtime->oss.periods = oss_periods;
return 0;
}
......@@ -511,6 +521,8 @@ static int snd_pcm_oss_change_params(snd_pcm_substream_t *substream)
if (runtime->dma_area)
snd_pcm_format_set_silence(runtime->format, runtime->dma_area, bytes_to_samples(runtime, runtime->dma_bytes));
runtime->oss.period_frames = snd_pcm_alsa_frames(substream, oss_period_size);
err = 0;
failure:
if (sw_params)
......@@ -2098,7 +2110,7 @@ static int snd_pcm_oss_playback_ready(snd_pcm_substream_t *substream)
if (atomic_read(&runtime->mmap_count))
return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
else
return snd_pcm_playback_ready(substream);
return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
}
static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
......@@ -2107,7 +2119,7 @@ static int snd_pcm_oss_capture_ready(snd_pcm_substream_t *substream)
if (atomic_read(&runtime->mmap_count))
return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
else
return snd_pcm_capture_ready(substream);
return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
}
static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
......
......@@ -327,8 +327,9 @@ static void snd_pcm_substream_proc_hw_params_read(snd_info_entry_t *entry, snd_i
snd_iprintf(buffer, "OSS format: %s\n", snd_pcm_oss_format_name(runtime->oss.format));
snd_iprintf(buffer, "OSS channels: %u\n", runtime->oss.channels);
snd_iprintf(buffer, "OSS rate: %u\n", runtime->oss.rate);
snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);
snd_iprintf(buffer, "OSS period bytes: %lu\n", (unsigned long)runtime->oss.period_bytes);
snd_iprintf(buffer, "OSS periods: %u\n", runtime->oss.periods);
snd_iprintf(buffer, "OSS period frames: %lu\n", (unsigned long)runtime->oss.period_frames);
}
#endif
snd_pcm_stream_unlock_irq(substream);
......
......@@ -491,7 +491,7 @@ snd_seq_oss_midi_reset(seq_oss_devinfo_t *dp, int dev)
}
}
}
snd_seq_oss_midi_close(dp, dev);
// snd_seq_oss_midi_close(dp, dev);
snd_use_lock_free(&mdev->use_lock);
}
......
......@@ -410,6 +410,8 @@ snd_seq_oss_synth_reset(seq_oss_devinfo_t *dp, int dev)
if (midi_synth_dev.opened <= 0)
return;
snd_seq_oss_midi_reset(dp, info->midi_mapped);
/* reopen the device */
snd_seq_oss_midi_close(dp, dev);
if (snd_seq_oss_midi_open(dp, info->midi_mapped,
dp->file_mode) < 0) {
midi_synth_dev.opened--;
......
......@@ -171,10 +171,12 @@ int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock)
{
snd_seq_event_cell_t *cell;
unsigned long flags;
wait_queue_t wait;
snd_assert(f != NULL, return -EINVAL);
*cellp = NULL;
init_waitqueue_entry(&wait, current);
spin_lock_irqsave(&f->lock, flags);
while ((cell = fifo_cell_out(f)) == NULL) {
if (nonblock) {
......@@ -182,17 +184,19 @@ int snd_seq_fifo_cell_out(fifo_t *f, snd_seq_event_cell_t **cellp, int nonblock)
spin_unlock_irqrestore(&f->lock, flags);
return -EAGAIN;
}
spin_unlock(&f->lock);
interruptible_sleep_on(&f->input_sleep);
spin_lock(&f->lock);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&f->input_sleep, &wait);
spin_unlock_irq(&f->lock);
schedule();
spin_lock_irq(&f->lock);
remove_wait_queue(&f->input_sleep, &wait);
if (signal_pending(current)) {
spin_unlock_irqrestore(&f->lock, flags);
return -ERESTARTSYS;
}
}
*cellp = cell;
spin_unlock_irqrestore(&f->lock, flags);
*cellp = cell;
return 0;
}
......
......@@ -220,12 +220,14 @@ int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock,
snd_seq_event_cell_t *cell;
unsigned long flags;
int err = -EAGAIN;
wait_queue_t wait;
if (pool == NULL)
return -EINVAL;
*cellp = NULL;
init_waitqueue_entry(&wait, current);
spin_lock_irqsave(&pool->lock, flags);
if (pool->ptr == NULL) { /* not initialized */
snd_printd("seq: pool is not initialized\n");
......@@ -234,9 +236,12 @@ int snd_seq_cell_alloc(pool_t *pool, snd_seq_event_cell_t **cellp, int nonblock,
}
while (pool->free == NULL && ! nonblock && ! pool->closing) {
spin_unlock(&pool->lock);
interruptible_sleep_on(&pool->output_sleep);
spin_lock(&pool->lock);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&pool->output_sleep, &wait);
spin_unlock_irq(&pool->lock);
schedule();
spin_lock_irq(&pool->lock);
remove_wait_queue(&pool->output_sleep, &wait);
/* interrupted? */
if (signal_pending(current)) {
err = -ERESTARTSYS;
......
......@@ -355,11 +355,12 @@ int vx_send_msg_nolock(vx_core_t *chip, struct vx_rmh *rmh)
*/
int vx_send_msg(vx_core_t *chip, struct vx_rmh *rmh)
{
unsigned long flags;
int err;
spin_lock_bh(&chip->lock);
spin_lock_irqsave(&chip->lock, flags);
err = vx_send_msg_nolock(chip, rmh);
spin_unlock_bh(&chip->lock);
spin_unlock_irqrestore(&chip->lock, flags);
return err;
}
......@@ -414,11 +415,12 @@ int vx_send_rih_nolock(vx_core_t *chip, int cmd)
*/
int vx_send_rih(vx_core_t *chip, int cmd)
{
unsigned long flags;
int err;
spin_lock_bh(&chip->lock);
spin_lock_irqsave(&chip->lock, flags);
err = vx_send_rih_nolock(chip, cmd);
spin_unlock_bh(&chip->lock);
spin_unlock_irqrestore(&chip->lock, flags);
return err;
}
......
......@@ -919,7 +919,7 @@ int snd_vx_mixer_new(vx_core_t *chip)
if ((err = snd_ctl_add(card, snd_ctl_new1(&vx_control_iec958, chip))) < 0)
return err;
/* VU, peak, saturation meters */
for (c = 0; c < 1; c++) {
for (c = 0; c < 2; c++) {
static char *dir[2] = { "Output", "Input" };
for (i = 0; i < chip->hw->num_ins; i++) {
int val = (i * 2) | (c << 8);
......
......@@ -561,7 +561,7 @@ static snd_pcm_hardware_t vx_pcm_playback_hw = {
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5000,
.rate_max = 48000,
.channels_min = 2,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
.period_bytes_min = 126,
......@@ -958,7 +958,7 @@ static snd_pcm_hardware_t vx_pcm_capture_hw = {
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 5000,
.rate_max = 48000,
.channels_min = 2,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (128*1024),
.period_bytes_min = 126,
......
......@@ -55,6 +55,12 @@ config SND_KORG1212
help
Say 'Y' or 'M' to include support for Korg 1212IO.
config SND_MIXART
tristate "Digigram miXart"
depends on SND
help
Say 'Y' or 'M' to include support for Digigram miXart soundcard.
config SND_NM256
tristate "NeoMagic NM256AV/ZX"
depends on SND
......
......@@ -38,4 +38,16 @@ obj-$(CONFIG_SND_RME96) += snd-rme96.o
obj-$(CONFIG_SND_SONICVIBES) += snd-sonicvibes.o
obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/ vx222/
obj-$(CONFIG_SND) += \
ac97/ \
ali5451/ \
cs46xx/ \
emu10k1/ \
ice1712/ \
korg1212/ \
mixart/ \
nm256/ \
rme9652/ \
trident/ \
ymfpci/ \
vx222/
......@@ -100,6 +100,7 @@ static const ac97_codec_id_t snd_ac97_codec_ids[] = {
{ 0x41445361, 0xffffffff, "AD1886", patch_ad1886, NULL },
{ 0x41445362, 0xffffffff, "AD1887", patch_ad1881, NULL },
{ 0x41445363, 0xffffffff, "AD1886A", patch_ad1881, NULL },
{ 0x41445368, 0xffffffff, "AD1888", patch_ad1888, NULL },
{ 0x41445370, 0xffffffff, "AD1980", patch_ad1980, NULL },
{ 0x41445372, 0xffffffff, "AD1981A", patch_ad1981a, NULL },
{ 0x41445374, 0xffffffff, "AD1981B", patch_ad1981b, NULL },
......@@ -1526,38 +1527,40 @@ static int snd_ac97_modem_build(snd_card_t * card, ac97_t * ac97)
return 0;
}
static int snd_ac97_test_rate(ac97_t *ac97, int reg, int rate)
static int snd_ac97_test_rate(ac97_t *ac97, int reg, int shadow_reg, int rate)
{
unsigned short val;
unsigned int tmp;
tmp = ((unsigned int)rate * ac97->bus->clock) / 48000;
snd_ac97_write_cache(ac97, reg, tmp & 0xffff);
if (shadow_reg)
snd_ac97_write_cache(ac97, shadow_reg, tmp & 0xffff);
val = snd_ac97_read(ac97, reg);
return val == (tmp & 0xffff);
}
static void snd_ac97_determine_rates(ac97_t *ac97, int reg, unsigned int *r_result)
static void snd_ac97_determine_rates(ac97_t *ac97, int reg, int shadow_reg, unsigned int *r_result)
{
unsigned int result = 0;
/* test a non-standard rate */
if (snd_ac97_test_rate(ac97, reg, 11000))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11000))
result |= SNDRV_PCM_RATE_CONTINUOUS;
/* let's try to obtain standard rates */
if (snd_ac97_test_rate(ac97, reg, 8000))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 8000))
result |= SNDRV_PCM_RATE_8000;
if (snd_ac97_test_rate(ac97, reg, 11025))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 11025))
result |= SNDRV_PCM_RATE_11025;
if (snd_ac97_test_rate(ac97, reg, 16000))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 16000))
result |= SNDRV_PCM_RATE_16000;
if (snd_ac97_test_rate(ac97, reg, 22050))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 22050))
result |= SNDRV_PCM_RATE_22050;
if (snd_ac97_test_rate(ac97, reg, 32000))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 32000))
result |= SNDRV_PCM_RATE_32000;
if (snd_ac97_test_rate(ac97, reg, 44100))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 44100))
result |= SNDRV_PCM_RATE_44100;
if (snd_ac97_test_rate(ac97, reg, 48000))
if (snd_ac97_test_rate(ac97, reg, shadow_reg, 48000))
result |= SNDRV_PCM_RATE_48000;
*r_result = result;
}
......@@ -1866,8 +1869,8 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97)
if (ac97->ext_id & 0x0189) /* L/R, MIC, SDAC, LDAC VRA support */
snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);
if (ac97->ext_id & AC97_EI_VRA) { /* VRA support */
snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]);
snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]);
snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, 0, &ac97->rates[AC97_RATES_FRONT_DAC]);
snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, 0, &ac97->rates[AC97_RATES_ADC]);
} else {
ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
......@@ -1884,16 +1887,16 @@ int snd_ac97_mixer(ac97_bus_t * bus, ac97_t * _ac97, ac97_t ** rac97)
SNDRV_PCM_RATE_32000;
}
if (ac97->ext_id & AC97_EI_VRM) { /* MIC VRA support */
snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]);
snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, 0, &ac97->rates[AC97_RATES_MIC_ADC]);
} else {
ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
}
if (ac97->ext_id & AC97_EI_SDAC) { /* SDAC support */
snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
ac97->scaps |= AC97_SCAP_SURROUND_DAC;
}
if (ac97->ext_id & AC97_EI_LDAC) { /* LDAC support */
snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
}
/* additional initializations */
......@@ -2112,6 +2115,8 @@ static int swap_headphone(ac97_t *ac97, int remove_master)
{
/* FIXME: error checks.. */
if (remove_master) {
if (ctl_find(ac97, "Headphone Playback Switch") == NULL)
return 0;
snd_ac97_remove_ctl(ac97, "Master Playback Switch");
snd_ac97_remove_ctl(ac97, "Master Playback Volume");
} else {
......
......@@ -652,7 +652,7 @@ int patch_ad1881(ac97_t * ac97)
static const snd_kcontrol_new_t snd_ac97_controls_ad1885[] = {
AC97_SINGLE("Digital Mono Direct", AC97_AD_MISC, 11, 1, 0),
AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0),
/* AC97_SINGLE("Digital Audio Mode", AC97_AD_MISC, 12, 1, 0), */ /* seems problematic */
AC97_SINGLE("Low Power Mixer", AC97_AD_MISC, 14, 1, 0),
AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0),
};
......@@ -682,6 +682,9 @@ int patch_ad1885(ac97_t * ac97)
jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF);
snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300);
/* set default */
snd_ac97_write_cache(ac97, AC97_AD_MISC, 0x0404);
ac97->build_ops = &patch_ad1885_build_ops;
return 0;
}
......@@ -799,7 +802,7 @@ int patch_ad1981b(ac97_t *ac97)
return 0;
}
static int snd_ac97_ad1980_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
static int snd_ac97_ad1888_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 1;
......@@ -808,7 +811,7 @@ static int snd_ac97_ad1980_lohpsel_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_i
return 0;
}
static int snd_ac97_ad1980_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
static int snd_ac97_ad1888_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
......@@ -818,7 +821,7 @@ static int snd_ac97_ad1980_lohpsel_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
return 0;
}
static int snd_ac97_ad1980_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
static int snd_ac97_ad1888_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
......@@ -829,7 +832,7 @@ static int snd_ac97_ad1980_lohpsel_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
AC97_AD198X_LOSEL | AC97_AD198X_HPSEL, val);
}
static int snd_ac97_ad1980_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
static int snd_ac97_ad1888_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
static char *texts[3] = {"Off", "6 -> 4", "6 -> 2"};
......@@ -842,7 +845,7 @@ static int snd_ac97_ad1980_downmix_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_i
return 0;
}
static int snd_ac97_ad1980_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
static int snd_ac97_ad1888_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t* ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
......@@ -855,7 +858,7 @@ static int snd_ac97_ad1980_downmix_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
return 0;
}
static int snd_ac97_ad1980_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
static int snd_ac97_ad1888_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
unsigned short val;
......@@ -871,51 +874,47 @@ static int snd_ac97_ad1980_downmix_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_va
AC97_AD198X_DMIX0 | AC97_AD198X_DMIX1, val);
}
static const snd_kcontrol_new_t snd_ac97_ad1980_controls[] = {
static const snd_kcontrol_new_t snd_ac97_ad1888_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Exchange Front/Surround",
.info = snd_ac97_ad1980_lohpsel_info,
.get = snd_ac97_ad1980_lohpsel_get,
.put = snd_ac97_ad1980_lohpsel_put
.info = snd_ac97_ad1888_lohpsel_info,
.get = snd_ac97_ad1888_lohpsel_get,
.put = snd_ac97_ad1888_lohpsel_put
},
AC97_SINGLE("Spread Front to Surround and Center/LFE", AC97_AD_MISC, 7, 1, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Downmix",
.info = snd_ac97_ad1980_downmix_info,
.get = snd_ac97_ad1980_downmix_get,
.put = snd_ac97_ad1980_downmix_put
.info = snd_ac97_ad1888_downmix_info,
.get = snd_ac97_ad1888_downmix_get,
.put = snd_ac97_ad1888_downmix_put
},
AC97_SINGLE("Surround Jack as Input", AC97_AD_MISC, 12, 1, 0),
AC97_SINGLE("Center/LFE Jack as Input", AC97_AD_MISC, 11, 1, 0),
};
static int patch_ad1980_specific(ac97_t *ac97)
static int patch_ad1888_specific(ac97_t *ac97)
{
int err;
/* rename 0x04 as "Master" and 0x02 as "Master Surround" */
snd_ac97_rename_ctl(ac97, "Master Playback Switch", "Master Surround Playback Switch");
snd_ac97_rename_ctl(ac97, "Master Playback Volume", "Master Surround Playback Volume");
snd_ac97_rename_ctl(ac97, "Headphone Playback Switch", "Master Playback Switch");
snd_ac97_rename_ctl(ac97, "Headphone Playback Volume", "Master Playback Volume");
if ((err = patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1)) < 0)
return err;
return patch_build_controls(ac97, snd_ac97_ad1980_controls, ARRAY_SIZE(snd_ac97_ad1980_controls));
return patch_build_controls(ac97, snd_ac97_ad1888_controls, ARRAY_SIZE(snd_ac97_ad1888_controls));
}
static struct snd_ac97_build_ops patch_ad1980_build_ops = {
static struct snd_ac97_build_ops patch_ad1888_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1980_specific
.build_specific = patch_ad1888_specific
};
int patch_ad1980(ac97_t * ac97)
int patch_ad1888(ac97_t * ac97)
{
unsigned short misc;
patch_ad1881(ac97);
ac97->build_ops = &patch_ad1980_build_ops;
ac97->build_ops = &patch_ad1888_build_ops;
/* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */
/* it seems that most vendors connect line-out connector to headphone out of AC'97 */
/* AD-compatible mode */
......@@ -930,6 +929,27 @@ int patch_ad1980(ac97_t * ac97)
return 0;
}
static int patch_ad1980_specific(ac97_t *ac97)
{
int err;
if ((err = patch_ad1888_specific(ac97)) < 0)
return err;
return patch_build_controls(ac97, &snd_ac97_ad198x_2cmic, 1);
}
static struct snd_ac97_build_ops patch_ad1980_build_ops = {
.build_post_spdif = patch_ad198x_post_spdif,
.build_specific = patch_ad1980_specific
};
int patch_ad1980(ac97_t * ac97)
{
patch_ad1888(ac97);
ac97->build_ops = &patch_ad1980_build_ops;
return 0;
}
static const snd_kcontrol_new_t snd_ac97_ad1985_controls[] = {
AC97_SINGLE("Center/LFE Jack as Mic", AC97_AD_SERIAL_CFG, 9, 1, 0),
AC97_SINGLE("Exchange Center/LFE", AC97_AD_SERIAL_CFG, 3, 1, 0)
......@@ -1014,8 +1034,7 @@ static int snd_ac97_alc650_mic_gpio_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_v
ucontrol->value.integer.value[0] ? (1 << 10) : 0);
if (change) {
/* GPIO0 write for mic */
snd_ac97_update_bits(ac97, 0x76, 0x01,
ucontrol->value.integer.value[0] ? 0 : 0x01);
snd_ac97_update_bits(ac97, 0x76, 0x01, 0x01);
/* GPIO0 high for mic */
snd_ac97_update_bits(ac97, 0x78, 0x100,
ucontrol->value.integer.value[0] ? 0 : 0x100);
......@@ -1092,10 +1111,7 @@ int patch_alc650(ac97_t * ac97)
mic_off = snd_ac97_read(ac97, AC97_ALC650_MULTICH) & (1 << 10);
/* GPIO0 direction */
val = snd_ac97_read(ac97, AC97_ALC650_GPIO_SETUP);
if (mic_off)
val &= ~0x01;
else
val |= 0x01;
val |= 0x01;
snd_ac97_write_cache(ac97, AC97_ALC650_GPIO_SETUP, val);
val = snd_ac97_read(ac97, AC97_ALC650_GPIO_STATUS);
if (mic_off)
......@@ -1111,10 +1127,36 @@ int patch_alc650(ac97_t * ac97)
return 0;
}
static int snd_ac97_alc655_mic_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = (ac97->regs[AC97_ALC650_MULTICH] >> 10) & 1;
return 0;
}
static int snd_ac97_alc655_mic_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t * ucontrol)
{
ac97_t *ac97 = snd_kcontrol_chip(kcontrol);
int change;
snd_ac97_update_bits(ac97, 0x7a, 1 << 12, /* misc control; vrefout disable */
ucontrol->value.integer.value[0] ? (1 << 12) : 0);
change = snd_ac97_update_bits(ac97, AC97_ALC650_MULTICH, 1 << 10,
ucontrol->value.integer.value[0] ? (1 << 10) : 0);
return change;
}
static const snd_kcontrol_new_t snd_ac97_controls_alc655[] = {
AC97_SINGLE("Duplicate Front", AC97_ALC650_MULTICH, 0, 1, 0),
AC97_SINGLE("Line-In As Surround", AC97_ALC650_MULTICH, 9, 1, 0),
AC97_SINGLE("Mic As Center/LFE", AC97_ALC650_MULTICH, 10, 1, 0),
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic As Center/LFE",
.info = snd_ac97_info_single,
.get = snd_ac97_alc655_mic_get,
.put = snd_ac97_alc655_mic_put,
},
};
static int alc655_iec958_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
......@@ -1187,15 +1229,21 @@ static struct snd_ac97_build_ops patch_alc655_ops = {
int patch_alc655(ac97_t * ac97)
{
unsigned int val;
ac97->spec.dev_flags = (ac97->id == 0x414c4780); /* ALC658 */
ac97->build_ops = &patch_alc655_ops;
/* enable spdif in */
snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK,
snd_ac97_read(ac97, AC97_ALC650_MULTICH) | 0x8000);
snd_ac97_write_cache(ac97, AC97_ALC650_CLOCK,
snd_ac97_read(ac97, AC97_ALC650_CLOCK) | 0x02);
/* adjust default values */
val = snd_ac97_read(ac97, 0x7a); /* misc control */
val |= (1 << 1); /* spdif input pin */
val &= ~(1 << 12); /* vref enable */
snd_ac97_write_cache(ac97, 0x7a, val);
val = snd_ac97_read(ac97, AC97_ALC650_MULTICH);
val |= (1 << 15); /* enable spdif in */
val &= ~(1 << 10); /* disable center on mic */
snd_ac97_write_cache(ac97, AC97_ALC650_MULTICH, val);
/* full DAC volume */
snd_ac97_write_cache(ac97, AC97_ALC650_SURR_DAC_VOL, 0x0808);
......
......@@ -41,6 +41,7 @@ int patch_ad1819(ac97_t * ac97);
int patch_ad1881(ac97_t * ac97);
int patch_ad1885(ac97_t * ac97);
int patch_ad1886(ac97_t * ac97);
int patch_ad1888(ac97_t * ac97);
int patch_ad1980(ac97_t * ac97);
int patch_ad1981a(ac97_t * ac97);
int patch_ad1981b(ac97_t * ac97);
......
......@@ -2375,6 +2375,7 @@ static int __devinit snd_ice1712_create(snd_card_t * card,
ice->omni = omni ? 1 : 0;
spin_lock_init(&ice->reg_lock);
init_MUTEX(&ice->gpio_mutex);
init_MUTEX(&ice->open_mutex);
ice->gpio.set_mask = snd_ice1712_set_gpio_mask;
ice->gpio.set_dir = snd_ice1712_set_gpio_dir;
ice->gpio.set_data = snd_ice1712_set_gpio_data;
......
......@@ -335,6 +335,9 @@ struct _snd_ice1712 {
unsigned short hoontech_boxconfig[4];
unsigned int cur_rate; /* current rate */
struct semaphore open_mutex;
snd_pcm_substream_t *pcm_reserved[4];
unsigned int akm_codecs;
akm4xxx_t *akm;
struct snd_ice1712_spdif spdif;
......
......@@ -237,6 +237,18 @@ static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id, struct pt_regs *r
if (ice->capture_pro_substream)
snd_pcm_period_elapsed(ice->capture_pro_substream);
}
if (mtstat & VT1724_MULTI_PDMA1) {
if (ice->playback_con_substream_ds[0])
snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]);
}
if (mtstat & VT1724_MULTI_PDMA2) {
if (ice->playback_con_substream_ds[1])
snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]);
}
if (mtstat & VT1724_MULTI_PDMA3) {
if (ice->playback_con_substream_ds[2])
snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]);
}
if (mtstat & VT1724_MULTI_PDMA4) {
if (ice->playback_con_substream)
snd_pcm_period_elapsed(ice->playback_con_substream);
......@@ -282,16 +294,6 @@ static snd_pcm_hw_constraint_list_t hw_constraints_rates_192 = {
.mask = 0,
};
static unsigned int hw_channels[] = {
2, 4, 6, 8
};
static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
.count = ARRAY_SIZE(hw_channels),
.list = hw_channels,
.mask = 0,
};
static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
{
ice1712_t *ice = snd_pcm_substream_chip(substream);
......@@ -300,21 +302,16 @@ static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
struct list_head *pos;
snd_pcm_substream_t *s;
what = 0;
snd_pcm_group_for_each(pos, substream) {
s = snd_pcm_group_substream_entry(pos);
what |= (unsigned long)(s->runtime->private_data);
snd_pcm_trigger_done(s, substream);
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
what = 0;
snd_pcm_group_for_each(pos, substream) {
s = snd_pcm_group_substream_entry(pos);
if (s == ice->playback_pro_substream)
what |= VT1724_PDMA0_PAUSE;
else if (s == ice->capture_pro_substream)
what |= VT1724_RDMA0_PAUSE;
else if (s == ice->playback_con_substream)
what |= VT1724_PDMA4_PAUSE;
else if (s == ice->capture_con_substream)
what |= VT1724_RDMA1_PAUSE;
}
spin_lock(&ice->reg_lock);
old = inb(ICEMT1724(ice, DMA_PAUSE));
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
......@@ -327,24 +324,6 @@ static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_STOP:
what = 0;
s = substream;
snd_pcm_group_for_each(pos, substream) {
s = snd_pcm_group_substream_entry(pos);
if (s == ice->playback_pro_substream) {
what |= VT1724_PDMA0_START;
snd_pcm_trigger_done(s, substream);
} else if (s == ice->capture_pro_substream) {
what |= VT1724_RDMA0_START;
snd_pcm_trigger_done(s, substream);
} else if (s == ice->playback_con_substream) {
what |= VT1724_PDMA4_START;
snd_pcm_trigger_done(s, substream);
} else if (s == ice->capture_con_substream) {
what |= VT1724_RDMA1_START;
snd_pcm_trigger_done(s, substream);
}
}
spin_lock(&ice->reg_lock);
old = inb(ICEMT1724(ice, DMA_CONTROL));
if (cmd == SNDRV_PCM_TRIGGER_START)
......@@ -364,8 +343,10 @@ static int snd_vt1724_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
/*
*/
#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|VT1724_PDMA4_START)
#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|VT1724_PDMA4_PAUSE)
#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\
VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START)
#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\
VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)
static void snd_vt1724_set_pro_rate(ice1712_t *ice, unsigned int rate, int force)
{
......@@ -448,13 +429,52 @@ static int snd_vt1724_pcm_hw_params(snd_pcm_substream_t * substream,
snd_pcm_hw_params_t * hw_params)
{
ice1712_t *ice = snd_pcm_substream_chip(substream);
int i, chs;
chs = params_channels(hw_params);
down(&ice->open_mutex);
/* mark surround channels */
if (substream == ice->playback_pro_substream) {
chs = chs / 2 - 1;
for (i = 0; i < chs; i++) {
if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
up(&ice->open_mutex);
return -EBUSY;
}
ice->pcm_reserved[i] = substream;
}
for (; i < 3; i++) {
if (ice->pcm_reserved[i] == substream)
ice->pcm_reserved[i] = NULL;
}
} else {
for (i = 0; i < 3; i++) {
if (ice->playback_con_substream_ds[i] == substream) {
if (ice->pcm_reserved[i] && ice->pcm_reserved[i] != substream) {
up(&ice->open_mutex);
return -EBUSY;
}
ice->pcm_reserved[i] = substream;
break;
}
}
}
up(&ice->open_mutex);
snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
}
static int snd_vt1724_pcm_hw_free(snd_pcm_substream_t * substream)
{
ice1712_t *ice = snd_pcm_substream_chip(substream);
int i;
down(&ice->open_mutex);
/* unmark surround channels */
for (i = 0; i < 3; i++)
if (ice->pcm_reserved[i] == substream)
ice->pcm_reserved[i] = NULL;
up(&ice->open_mutex);
return snd_pcm_lib_free_pages(substream);
}
......@@ -593,14 +613,14 @@ static snd_pcm_hardware_t snd_vt1724_playback_pro =
.rate_max = 192000,
.channels_min = 2,
.channels_max = 8,
.buffer_bytes_max = (1UL << 21), /* 18bits dword */
.buffer_bytes_max = (1UL << 21), /* 19bits dword */
.period_bytes_min = 8 * 4 * 2, /* FIXME: constraints needed */
.period_bytes_max = (1UL << 21),
.periods_min = 1,
.periods_min = 2,
.periods_max = 1024,
};
static snd_pcm_hardware_t snd_vt1724_capture_pro =
static snd_pcm_hardware_t snd_vt1724_2ch_stereo =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
......@@ -612,10 +632,10 @@ static snd_pcm_hardware_t snd_vt1724_capture_pro =
.rate_max = 192000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = (256*1024),
.buffer_bytes_max = (1UL << 18), /* 16bits dword */
.period_bytes_min = 2 * 4 * 2,
.period_bytes_max = (256*1024),
.periods_min = 1,
.period_bytes_max = (1UL << 18),
.periods_min = 2,
.periods_max = 1024,
};
......@@ -628,7 +648,9 @@ static int snd_vt1724_playback_pro_open(snd_pcm_substream_t * substream)
{
snd_pcm_runtime_t *runtime = substream->runtime;
ice1712_t *ice = snd_pcm_substream_chip(substream);
int chs;
runtime->private_data = (void*)VT1724_PDMA0_START; /* irq/status/trigger bit */
ice->playback_pro_substream = substream;
runtime->hw = snd_vt1724_playback_pro;
snd_pcm_set_sync(substream);
......@@ -639,7 +661,17 @@ static int snd_vt1724_playback_pro_open(snd_pcm_substream_t * substream)
else
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
down(&ice->open_mutex);
/* calculate the currently available channels */
for (chs = 0; chs < 3; chs++) {
if (ice->pcm_reserved[chs])
break;
}
chs = (chs + 1) * 2;
runtime->hw.channels_max = chs;
if (chs > 2) /* channels must be even */
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
up(&ice->open_mutex);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
VT1724_BUFFER_ALIGN);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
......@@ -652,8 +684,9 @@ static int snd_vt1724_capture_pro_open(snd_pcm_substream_t * substream)
ice1712_t *ice = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
runtime->private_data = (void*)VT1724_RDMA0_START; /* irq/status/trigger bit */
ice->capture_pro_substream = substream;
runtime->hw = snd_vt1724_capture_pro;
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
if ((ice->eeprom.data[ICE_EEP2_ACLINK] & 0x80) &&
......@@ -735,25 +768,6 @@ static int __devinit snd_vt1724_pcm_profi(ice1712_t * ice, int device)
* SPDIF PCM
*/
static snd_pcm_hardware_t snd_vt1724_playback_spdif =
{
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
.rate_min = 4000,
.rate_max = 192000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = (256*1024),
.period_bytes_min = 2 * 4 * 2,
.period_bytes_max = (256*1024),
.periods_min = 1,
.periods_max = 1024,
};
const static struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
.addr = VT1724_MT_PDMA4_ADDR,
.size = VT1724_MT_PDMA4_SIZE,
......@@ -795,8 +809,9 @@ static int snd_vt1724_playback_spdif_open(snd_pcm_substream_t *substream)
ice1712_t *ice = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
runtime->private_data = (void*)VT1724_PDMA4_START; /* irq/status/trigger bit */
ice->playback_con_substream = substream;
runtime->hw = snd_vt1724_playback_spdif;
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
......@@ -820,8 +835,9 @@ static int snd_vt1724_capture_spdif_open(snd_pcm_substream_t *substream)
ice1712_t *ice = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
runtime->private_data = (void*)VT1724_RDMA1_START; /* irq/status/trigger bit */
ice->capture_con_substream = substream;
runtime->hw = snd_vt1724_playback_spdif;
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_96);
......@@ -902,6 +918,127 @@ static int __devinit snd_vt1724_pcm_spdif(ice1712_t * ice, int device)
}
/*
* independent surround PCMs
*/
const static struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = {
{
.addr = VT1724_MT_PDMA1_ADDR,
.size = VT1724_MT_PDMA1_SIZE,
.count = VT1724_MT_PDMA1_COUNT,
.start = VT1724_PDMA1_START,
.pause = VT1724_PDMA1_PAUSE,
},
{
.addr = VT1724_MT_PDMA2_ADDR,
.size = VT1724_MT_PDMA2_SIZE,
.count = VT1724_MT_PDMA2_COUNT,
.start = VT1724_PDMA2_START,
.pause = VT1724_PDMA2_PAUSE,
},
{
.addr = VT1724_MT_PDMA3_ADDR,
.size = VT1724_MT_PDMA3_SIZE,
.count = VT1724_MT_PDMA3_COUNT,
.start = VT1724_PDMA3_START,
.pause = VT1724_PDMA3_PAUSE,
},
};
static int snd_vt1724_playback_indep_prepare(snd_pcm_substream_t * substream)
{
ice1712_t *ice = snd_pcm_substream_chip(substream);
unsigned char val;
spin_lock(&ice->reg_lock);
val = 3 - substream->number;
if (inb(ICEMT1724(ice, BURST)) < val)
outb(val, ICEMT1724(ice, BURST));
spin_unlock(&ice->reg_lock);
return snd_vt1724_pcm_prepare(substream, &vt1724_playback_dma_regs[substream->number]);
}
static snd_pcm_uframes_t snd_vt1724_playback_indep_pointer(snd_pcm_substream_t * substream)
{
return snd_vt1724_pcm_pointer(substream, &vt1724_playback_dma_regs[substream->number]);
}
static int snd_vt1724_playback_indep_open(snd_pcm_substream_t *substream)
{
ice1712_t *ice = snd_pcm_substream_chip(substream);
snd_pcm_runtime_t *runtime = substream->runtime;
down(&ice->open_mutex);
/* already used by PDMA0? */
if (ice->pcm_reserved[substream->number]) {
up(&ice->open_mutex);
return -EBUSY; /* FIXME: should handle blocking mode properly */
}
up(&ice->open_mutex);
runtime->private_data = (void*)(1 << (substream->number + 4));
ice->playback_con_substream_ds[substream->number] = substream;
runtime->hw = snd_vt1724_2ch_stereo;
snd_pcm_set_sync(substream);
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates_192);
return 0;
}
static int snd_vt1724_playback_indep_close(snd_pcm_substream_t * substream)
{
ice1712_t *ice = snd_pcm_substream_chip(substream);
if (PRO_RATE_RESET)
snd_vt1724_set_pro_rate(ice, PRO_RATE_DEFAULT, 0);
ice->playback_con_substream_ds[substream->number] = NULL;
ice->pcm_reserved[substream->number] = NULL;
return 0;
}
static snd_pcm_ops_t snd_vt1724_playback_indep_ops = {
.open = snd_vt1724_playback_indep_open,
.close = snd_vt1724_playback_indep_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_vt1724_pcm_hw_params,
.hw_free = snd_vt1724_pcm_hw_free,
.prepare = snd_vt1724_playback_indep_prepare,
.trigger = snd_vt1724_pcm_trigger,
.pointer = snd_vt1724_playback_indep_pointer,
};
static int __devinit snd_vt1724_pcm_indep(ice1712_t * ice, int device)
{
snd_pcm_t *pcm;
int play;
int err;
play = ice->num_total_dacs / 2 - 1;
if (play <= 0)
return 0;
err = snd_pcm_new(ice->card, "ICE1724 Surrounds", device, play, 0, &pcm);
if (err < 0)
return err;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_vt1724_playback_indep_ops);
pcm->private_data = ice;
pcm->info_flags = 0;
strcpy(pcm->name, "ICE1724 Surround PCM");
snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
ice->pcm_ds = pcm;
return 0;
}
/*
* Mixer section
*/
......@@ -1808,6 +1945,7 @@ static int __devinit snd_vt1724_create(snd_card_t * card,
ice->vt1724 = 1;
spin_lock_init(&ice->reg_lock);
init_MUTEX(&ice->gpio_mutex);
init_MUTEX(&ice->open_mutex);
ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
ice->gpio.set_data = snd_vt1724_set_gpio_data;
......@@ -1932,6 +2070,11 @@ static int __devinit snd_vt1724_probe(struct pci_dev *pci,
return err;
}
if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) {
snd_card_free(card);
return err;
}
if ((err = snd_vt1724_ac97_mixer(ice)) < 0) {
snd_card_free(card);
return err;
......
......@@ -143,6 +143,9 @@ MODULE_PARM_SYNTAX(mpu_port, SNDRV_ENABLED ",allows:{{0},{0x330},{0x300}},dialog
#ifndef PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO
#define PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO 0x00da
#endif
#ifndef PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO
#define PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO 0x00ea
#endif
enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI, DEVICE_NFORCE };
......@@ -443,6 +446,7 @@ static struct pci_device_id snd_intel8x0_ids[] = {
{ 0x10de, 0x01b1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE */
{ 0x10de, 0x006a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE2 */
{ 0x10de, 0x00da, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* NFORCE3 */
{ 0x10de, 0x00ea, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_NFORCE }, /* CK8S */
{ 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD8111 */
{ 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL }, /* AMD768 */
{ 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI }, /* Ali5455 */
......@@ -804,10 +808,19 @@ static irqreturn_t snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs
spin_lock(&chip->reg_lock);
status = igetdword(chip, chip->int_sta_reg);
if ((status & chip->int_sta_mask) == 0) {
if (status)
static int err_count = 10;
if (status) {
/* ack */
iputdword(chip, chip->int_sta_reg, status);
status ^= igetdword(chip, chip->int_sta_reg);
}
spin_unlock(&chip->reg_lock);
return IRQ_NONE;
if (status && err_count) {
err_count--;
snd_printd("intel8x0: unknown IRQ bits 0x%x (sta_mask=0x%x)\n",
status, chip->int_sta_mask);
}
return IRQ_RETVAL(status);
}
for (i = 0; i < chip->bdbars_count; i++) {
......@@ -1017,6 +1030,7 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substrea
{
intel8x0_t *chip = snd_pcm_substream_chip(substream);
ichdev_t *ichdev = get_ichdev(substream);
unsigned long flags;
size_t ptr1, ptr;
ptr1 = igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << chip->pcm_pos_shift;
......@@ -1024,7 +1038,9 @@ static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substrea
ptr = ichdev->fragsize1 - ptr1;
else
ptr = 0;
spin_lock_irqsave(&chip->reg_lock, flags);
ptr += ichdev->position;
spin_unlock_irqrestore(&chip->reg_lock, flags);
if (ptr >= ichdev->size)
return 0;
return bytes_to_frames(substream->runtime, ptr);
......@@ -1739,6 +1755,12 @@ static struct ac97_quirk ac97_quirks[] __devinitdata = {
.name = "Intel ICH5/AD1985",
.type = AC97_TUNE_AD_SHARING
},
{
.vendor = 0x8086,
.device = 0x4856,
.name = "Intel D845WN (82801BA)",
.type = AC97_TUNE_SWAP_HP
},
{
.vendor = 0x8086,
.device = 0x4d44,
......@@ -2567,6 +2589,7 @@ static struct shortname_table {
{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia nForce" },
{ PCI_DEVICE_ID_NVIDIA_MCP2_AUDIO, "NVidia nForce2" },
{ PCI_DEVICE_ID_NVIDIA_MCP3_AUDIO, "NVidia nForce3" },
{ PCI_DEVICE_ID_NVIDIA_CK8S_AUDIO, "NVidia CK8S" },
{ 0x746d, "AMD AMD8111" },
{ 0x7445, "AMD AMD768" },
{ 0x5455, "ALi M5455" },
......
#
# Makefile for ALSA
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
#
snd-mixart-objs := mixart.o mixart_core.o mixart_hwdep.o mixart_mixer.o
obj-$(CONFIG_SND_MIXART) += snd-mixart.o
/*
* Driver for Digigram miXart soundcards
*
* main file with alsa callbacks
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/driver.h>
#include <linux/init.h>
#include <sound/core.h>
#define SNDRV_GET_ID
#include <sound/initval.h>
#include <sound/info.h>
#include <sound/control.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "mixart.h"
#include "mixart_hwdep.h"
#include "mixart_core.h"
#include "mixart_mixer.h"
#define CARD_NAME "miXart"
MODULE_AUTHOR("Digigram <alsa@digigram.com>");
MODULE_DESCRIPTION("Digigram " CARD_NAME);
MODULE_LICENSE("GPL");
MODULE_CLASSES("{sound}");
MODULE_DEVICES("{{Digigram," CARD_NAME "}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
#define chip_t mixart_t
MODULE_PARM(index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(index, "Index value for Digigram " CARD_NAME " soundcard.");
MODULE_PARM_SYNTAX(index, SNDRV_INDEX_DESC);
MODULE_PARM(id, "1-" __MODULE_STRING(SNDRV_CARDS) "s");
MODULE_PARM_DESC(id, "ID string for Digigram " CARD_NAME " soundcard.");
MODULE_PARM_SYNTAX(id, SNDRV_ID_DESC);
MODULE_PARM(enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
MODULE_PARM_SYNTAX(enable, SNDRV_ENABLE_DESC);
/*
*/
static struct pci_device_id snd_mixart_ids[] = {
{ 0x1057, 0x0003, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* MC8240 */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_mixart_ids);
static int mixart_set_pipe_state(mixart_mgr_t *mgr, mixart_pipe_t* pipe, int start)
{
mixart_group_state_req_t group_state;
mixart_group_state_resp_t group_state_resp;
mixart_msg_t request;
int err;
u32 system_msg_uid;
switch(pipe->status) {
case PIPE_RUNNING:
case PIPE_CLOCK_SET:
if(start) return 0; /* already started */
break;
case PIPE_STOPPED:
if(!start) return 0; /* already stopped */
break;
default:
snd_printk(KERN_ERR "error mixart_set_pipe_state called with wrong pipe->status!\n");
return -EINVAL; /* function called with wrong pipe status */
}
system_msg_uid = 0x12345678; /* the event ! (take care: the MSB and two LSB's have to be 0) */
/* wait on the last MSG_SYSTEM_SEND_SYNCHRO_CMD command to be really finished */
request.message_id = MSG_SYSTEM_WAIT_SYNCHRO_CMD;
request.uid = (mixart_uid_t){0,0};
request.data = &system_msg_uid;
request.size = sizeof(system_msg_uid);
err = snd_mixart_send_msg_wait_notif(mgr, &request, system_msg_uid);
if(err) {
snd_printk(KERN_ERR "error : MSG_SYSTEM_WAIT_SYNCHRO_CMD was not notified !\n");
return err;
}
/* start or stop the pipe (1 pipe) */
memset(&group_state, 0, sizeof(group_state));
group_state.pipe_count = 1;
group_state.pipe_uid[0] = pipe->group_uid;
if(start)
request.message_id = MSG_STREAM_START_STREAM_GRP_PACKET;
else
request.message_id = MSG_STREAM_STOP_STREAM_GRP_PACKET;
request.uid = pipe->group_uid; /*(mixart_uid_t){0,0};*/
request.data = &group_state;
request.size = sizeof(group_state);
err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
if(err) {
snd_printk(KERN_ERR "error MSG_STREAM_ST***_STREAM_GRP_PACKET\n");
return err;
}
if(group_state_resp.txx_status != 0) {
snd_printk(KERN_ERR "error status MSG_STREAM_ST***_STREAM_GRP_PACKET (%x)!\n", group_state_resp.txx_status);
return -EINVAL;
}
if(start) {
u32 stat;
group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
err = snd_mixart_send_msg(mgr, &request, sizeof(group_state_resp), &group_state_resp);
if(err) {
snd_printk(KERN_ERR "error MSG_STREAM_START_STREAM_GRP_PACKET !\n");
return err;
}
if(group_state_resp.txx_status != 0) {
snd_printk(KERN_ERR "error status MSG_STREAM_START_STREAM_GRP_PACKET (%x)!\n", group_state_resp.txx_status);
return -EINVAL;
}
/* in case of start send a synchro top */
request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
request.uid = (mixart_uid_t){0,0};
request.data = NULL;
request.size = 0;
err = snd_mixart_send_msg(mgr, &request, sizeof(stat), &stat);
if(err) {
snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD!\n");
return err;
}
if(stat) {
snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD stat=%x!\n", stat);
}
pipe->status = PIPE_RUNNING;
}
else /* !start */
pipe->status = PIPE_STOPPED;
return 0;
}
static int mixart_set_clock(mixart_mgr_t *mgr, mixart_pipe_t *pipe, unsigned int rate)
{
mixart_msg_t request;
mixart_clock_properties_t clock_properties;
switch(pipe->status) {
case PIPE_CLOCK_SET:
break;
case PIPE_RUNNING:
if(rate != 0)
break;
default:
if(rate == 0)
return 0; /* nothing to do */
else {
snd_printk(KERN_ERR "error mixart_set_clock(%d) called with wrong pipe->status !\n", rate);
return -EINVAL;
}
}
memset(&clock_properties, 0, sizeof(clock_properties));
clock_properties.clock_generic_type = (rate != 0) ? CGT_INTERNAL_CLOCK : CGT_NO_CLOCK;
clock_properties.clock_mode = CM_STANDALONE;
clock_properties.frequency = rate;
clock_properties.nb_callers = 1; /* only one entry in uid_caller ! */
clock_properties.uid_caller[0] = pipe->group_uid;
snd_printdd("mixart_set_clock to %d kHz\n", rate);
request.message_id = MSG_CLOCK_SET_PROPERTIES;
request.uid = mgr->uid_console_manager;
request.data = &clock_properties;
request.size = sizeof(clock_properties);
/* we are not allowed to wait for the response, so simply set rate */
/* TODO : error has to be handled later in the tasklet! */
if(rate) pipe->status = PIPE_CLOCK_SET;
else pipe->status = PIPE_RUNNING;
return snd_mixart_send_msg_nonblock(mgr, &request);
}
/*
* Allocate or reference output pipe for analog IOs (pcmp0/1)
*/
mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring)
{
int stream_count;
mixart_pipe_t *pipe;
mixart_msg_t request;
if(capture) {
if (pcm_number == MIXART_PCM_ANALOG) {
pipe = &(chip->pipe_in_ana); /* analog inputs */
} else {
pipe = &(chip->pipe_in_dig); /* digital inputs */
}
request.message_id = MSG_STREAM_ADD_OUTPUT_GROUP;
stream_count = MIXART_CAPTURE_STREAMS;
} else {
if (pcm_number == MIXART_PCM_ANALOG) {
pipe = &(chip->pipe_out_ana); /* analog outputs */
} else {
pipe = &(chip->pipe_out_dig); /* digital outputs */
}
request.message_id = MSG_STREAM_ADD_INPUT_GROUP;
stream_count = MIXART_PLAYBACK_STREAMS;
}
/* a new stream is opened and there are already all streams in use */
if( (monitoring == 0) && (pipe->references >= stream_count) ) {
return NULL;
}
/* pipe is not yet defined */
if( pipe->status == PIPE_UNDEFINED ) {
int err, i;
mixart_streaming_group_t streaming_group_resp;
mixart_streaming_group_req_t streaming_group_req;
snd_printdd("add_ref_pipe audio chip(%d) pcm(%d)\n", chip->chip_idx, pcm_number);
request.uid = (mixart_uid_t){0,0}; /* should be StreamManagerUID, but zero is OK if there is only one ! */
request.data = &streaming_group_req;
request.size = sizeof(streaming_group_req);
memset(&streaming_group_req, 0, sizeof(streaming_group_req));
streaming_group_req.stream_count = stream_count;
streaming_group_req.channel_count = 2;
streaming_group_req.latency = 256;
streaming_group_req.connector = pipe->uid_left_connector; /* the left connector */
for (i=0; i<stream_count; i++) {
int j;
/* we don't yet know the format, so config 16 bit pcm audio for instance */
streaming_group_req.stream_info[i].size_max_byte_frame = 1024;
streaming_group_req.stream_info[i].size_max_sample_frame = 256;
streaming_group_req.stream_info[i].nb_bytes_max_per_sample = MIXART_FLOAT_P__4_0_TO_HEX; /* is 4.0f */
/* find the right bufferinfo_array */
j = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (pcm_number * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS)) + i;
if(capture) j += MIXART_PLAYBACK_STREAMS; /* in the array capture is behind playback */
streaming_group_req.flow_entry[i] = j;
chip->mgr->flowinfo_array[j].bufferinfo_array_phy_address = (u32)chip->mgr->bufferinfo_physaddr + (j * sizeof(mixart_bufferinfo_t));
chip->mgr->flowinfo_array[j].bufferinfo_count = 1; /* 1 will set the miXart to ring-buffer mode ! */
chip->mgr->bufferinfo_array[j].buffer_address = 0; /* buffer is not yet allocated */
chip->mgr->bufferinfo_array[j].available_length = 0; /* buffer is not yet allocated */
/* construct the identifier of the stream buffer received in the interrupts ! */
chip->mgr->bufferinfo_array[j].buffer_id = (chip->chip_idx << MIXART_NOTIFY_CARD_OFFSET) + (pcm_number << MIXART_NOTIFY_PCM_OFFSET ) + i;
if(capture) {
chip->mgr->bufferinfo_array[j].buffer_id |= MIXART_NOTIFY_CAPT_MASK;
}
}
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(streaming_group_resp), &streaming_group_resp);
if((err < 0) || (streaming_group_resp.status != 0)) {
snd_printk(KERN_ERR "message MSG_STREAM_ADD_**PUT_GROUP return error: err(%x) status(%x)!\n", err, streaming_group_resp.status);
return NULL;
}
pipe->group_uid = streaming_group_resp.group; /* id of the pipe, as returned by embedded */
pipe->stream_count = streaming_group_resp.stream_count;
/* pipe->stream_uid[i] = streaming_group_resp.stream[i].stream_uid; */
pipe->status = PIPE_STOPPED;
}
if(monitoring) pipe->monitoring = 1;
else pipe->references++;
return pipe;
}
int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring)
{
int err = 0;
if(pipe->status == PIPE_UNDEFINED)
return 0;
if(monitoring)
pipe->monitoring = 0;
else
pipe->references--;
if((pipe->references <= 0) && (pipe->monitoring == 0)) {
mixart_msg_t request;
mixart_delete_group_resp_t delete_resp;
/* release the clock */
err = mixart_set_clock( mgr, pipe, 0);
if( err < 0 ) {
snd_printk(KERN_ERR "mixart_set_clock(0) return error!\n");
}
/* stop the pipe */
err = mixart_set_pipe_state(mgr, pipe, 0);
if( err < 0 ) {
snd_printk(KERN_ERR "error stopping pipe!\n");
}
request.message_id = MSG_STREAM_DELETE_GROUP;
request.uid = (mixart_uid_t){0,0};
request.data = &pipe->group_uid; /* the streaming group ! */
request.size = sizeof(pipe->group_uid);
/* delete the pipe */
err = snd_mixart_send_msg(mgr, &request, sizeof(delete_resp), &delete_resp);
if ((err < 0) || (delete_resp.status != 0)) {
snd_printk(KERN_ERR "error MSG_STREAM_DELETE_GROUP err(%x), status(%x)\n", err, delete_resp.status);
}
pipe->group_uid = (mixart_uid_t){0,0};
pipe->stream_count = 0;
pipe->status = PIPE_UNDEFINED;
}
return err;
}
static int mixart_set_stream_state(mixart_stream_t *stream, int start)
{
mixart_t *chip;
mixart_stream_state_req_t stream_state_req;
mixart_msg_t request;
if(!stream->substream)
return -EINVAL;
memset(&stream_state_req, 0, sizeof(stream_state_req));
stream_state_req.stream_count = 1;
stream_state_req.stream_info.stream_desc.uid_pipe = stream->pipe->group_uid;
stream_state_req.stream_info.stream_desc.stream_idx = stream->substream->number;
if (stream->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
request.message_id = start ? MSG_STREAM_START_INPUT_STAGE_PACKET : MSG_STREAM_STOP_INPUT_STAGE_PACKET;
else
request.message_id = start ? MSG_STREAM_START_OUTPUT_STAGE_PACKET : MSG_STREAM_STOP_OUTPUT_STAGE_PACKET;
request.uid = (mixart_uid_t){0,0};
request.data = &stream_state_req;
request.size = sizeof(stream_state_req);
stream->abs_period_elapsed = 0; /* reset stream pos */
stream->buf_periods = 0;
stream->buf_period_frag = 0;
chip = snd_pcm_substream_chip(stream->substream);
return snd_mixart_send_msg_nonblock(chip->mgr, &request);
}
/*
* Trigger callback
*/
static int snd_mixart_trigger(snd_pcm_substream_t *subs, int cmd)
{
mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
snd_printdd("SNDRV_PCM_TRIGGER_START\n");
// snd_printk(KERN_DEBUG "hw_avail = %d\n", snd_pcm_playback_hw_avail(subs->runtime));
/* START_STREAM */
if( mixart_set_stream_state(stream, 1) )
return -EINVAL;
stream->status = MIXART_STREAM_STATUS_RUNNING;
break;
case SNDRV_PCM_TRIGGER_STOP:
/* STOP_STREAM */
if( mixart_set_stream_state(stream, 0) )
return -EINVAL;
/* TODO : mixart drains data transefered in advance -> mute stream ? */
stream->status = MIXART_STREAM_STATUS_OPEN;
snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
/* TODO */
stream->status = MIXART_STREAM_STATUS_PAUSE;
snd_printdd("SNDRV_PCM_PAUSE_PUSH\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
/* TODO */
stream->status = MIXART_STREAM_STATUS_RUNNING;
snd_printdd("SNDRV_PCM_PAUSE_RELEASE\n");
break;
default:
return -EINVAL;
}
return 0;
}
/*
* prepare callback for all pcms
*/
static int snd_mixart_prepare(snd_pcm_substream_t *subs)
{
mixart_t *chip = snd_pcm_substream_chip(subs);
mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
/* TODO de faon non bloquante, rappliquer les hw_params (rate, bits, codec) */
snd_printdd("snd_mixart_prepare\n");
/* only the first stream can choose the sample rate */
/* the further opened streams will be limited to its frequency (see open) */
if(chip->mgr->ref_count_rate == 1)
chip->mgr->sample_rate = subs->runtime->rate;
/* set the clock only once (first stream) on the same pipe */
if(stream->pipe->references == 1) {
if( mixart_set_clock(chip->mgr, stream->pipe, subs->runtime->rate) )
return -EINVAL;
}
return 0;
}
static int mixart_set_format(mixart_stream_t *stream, snd_pcm_format_t format)
{
int err;
mixart_t *chip;
mixart_msg_t request;
mixart_stream_param_desc_t stream_param;
mixart_return_uid_t resp;
chip = snd_pcm_substream_chip(stream->substream);
memset(&stream_param, 0, sizeof(stream_param));
stream_param.coding_type = CT_LINEAR;
stream_param.number_of_channel = stream->channels;
stream_param.sampling_freq = chip->mgr->sample_rate;
if(stream_param.sampling_freq == 0)
stream_param.sampling_freq = 44100; /* if frequency not yet defined, use some default */
switch(format){
case SNDRV_PCM_FORMAT_U8:
stream_param.sample_type = ST_INTEGER_8;
stream_param.sample_size = 8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
stream_param.sample_type = ST_INTEGER_16LE;
stream_param.sample_size = 16;
break;
case SNDRV_PCM_FORMAT_S16_BE:
stream_param.sample_type = ST_INTEGER_16BE;
stream_param.sample_size = 16;
break;
case SNDRV_PCM_FORMAT_S24_3LE:
stream_param.sample_type = ST_INTEGER_24LE;
stream_param.sample_size = 24;
break;
case SNDRV_PCM_FORMAT_S24_3BE:
stream_param.sample_type = ST_INTEGER_24BE;
stream_param.sample_size = 24;
break;
case SNDRV_PCM_FMTBIT_FLOAT_LE:
stream_param.sample_type = ST_FLOATING_POINT_32LE;
stream_param.sample_size = 32;
break;
case SNDRV_PCM_FMTBIT_FLOAT_BE:
stream_param.sample_type = ST_FLOATING_POINT_32BE;
stream_param.sample_size = 32;
break;
default:
snd_printk(KERN_DEBUG "error use default SNDRV_PCM_FORMAT_S16_LE\n");
stream_param.sample_type = ST_INTEGER_16LE;
stream_param.sample_size = 16;
}
snd_printdd("set SNDRV_PCM_FORMAT sample_type(%d) sample_size(%d) freq(%d) channels(%d)\n",
stream_param.sample_type, stream_param.sample_size, stream_param.sampling_freq, stream->channels);
/* TODO: what else to configure ? */
/* stream_param.samples_per_frame = 2; */
/* stream_param.bytes_per_frame = 4; */
/* stream_param.bytes_per_sample = 2; */
stream_param.pipe_count = 1; /* set to 1 */
stream_param.stream_count = 1; /* set to 1 */
stream_param.stream_desc[0].uid_pipe = stream->pipe->group_uid;
stream_param.stream_desc[0].stream_idx = stream->substream->number;
request.message_id = MSG_STREAM_SET_INPUT_STAGE_PARAM;
request.uid = (mixart_uid_t){0,0};
request.data = &stream_param;
request.size = sizeof(stream_param);
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
if((err < 0) || resp.error_code) {
snd_printk(KERN_DEBUG "MSG_STREAM_SET_INPUT_STAGE_PARAM err=%x; resp=%x\n", err, resp.error_code);
return -EINVAL;
}
return 0;
}
/*
* HW_PARAMS callback for all pcms
*/
static int snd_mixart_hw_params(snd_pcm_substream_t *subs,
snd_pcm_hw_params_t *hw)
{
mixart_t *chip = snd_pcm_substream_chip(subs);
mixart_mgr_t *mgr = chip->mgr;
mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
snd_pcm_format_t format;
int err;
int channels;
/* set up channels */
channels = params_channels(hw);
/* set up format for the stream */
format = params_format(hw);
down(&mgr->setup_mutex);
/* update the stream levels */
if( stream->pcm_number <= MIXART_PCM_DIGITAL ) {
int is_aes = stream->pcm_number > MIXART_PCM_ANALOG;
if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK )
mixart_update_playback_stream_level(chip, is_aes, subs->number);
else
mixart_update_capture_stream_level( chip, is_aes);
}
stream->channels = channels;
/* set the format to the board */
err = mixart_set_format(stream, format);
if(err < 0) {
snd_printk(KERN_DEBUG "mixart_set_format() returned error (%x)\n", err);
return err;
}
/* allocate buffer */
err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
if (err > 0) {
int i = (chip->chip_idx * MIXART_MAX_STREAM_PER_CARD) + (stream->pcm_number * (MIXART_PLAYBACK_STREAMS+MIXART_CAPTURE_STREAMS)) + subs->number;
if( subs->stream == SNDRV_PCM_STREAM_CAPTURE ) {
i += MIXART_PLAYBACK_STREAMS; /* in array capture is behind playback */
}
mgr->bufferinfo_array[i].buffer_address = subs->runtime->dma_addr;
mgr->bufferinfo_array[i].available_length = subs->runtime->dma_bytes;
/* mgr->bufferinfo_array[i].buffer_id is already defined */
snd_printdd("snd_mixart_hw_params(pcm %d) : dma_addr(%x) dma_bytes(%x) subs-number(%d)\n", i, subs->runtime->dma_addr, subs->runtime->dma_bytes, subs->number);
}
up(&mgr->setup_mutex);
return err;
}
static int snd_mixart_hw_free(snd_pcm_substream_t *subs)
{
snd_pcm_lib_free_pages(subs);
return 0;
}
/*
* TODO CONFIGURATION SPACE for all pcms, mono pcm must update channels_max
*/
static snd_pcm_hardware_t snd_mixart_analog_caps =
{
.info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_PAUSE),
.formats = ( SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (32*1024),
.period_bytes_min = 256, /* 256 frames U8 mono*/
.period_bytes_max = (16*1024),
.periods_min = 2,
.periods_max = (32*1024/256),
};
static snd_pcm_hardware_t snd_mixart_digital_caps =
{
.info = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_PAUSE),
.formats = ( SNDRV_PCM_FMTBIT_U8 |
SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE ),
.rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
.rate_min = 32000,
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
.buffer_bytes_max = (32*1024),
.period_bytes_min = 256, /* 256 frames U8 mono*/
.period_bytes_max = (16*1024),
.periods_min = 2,
.periods_max = (32*1024/256),
};
static int snd_mixart_playback_open(snd_pcm_substream_t *subs)
{
mixart_t *chip = snd_pcm_substream_chip(subs);
mixart_mgr_t *mgr = chip->mgr;
snd_pcm_runtime_t *runtime = subs->runtime;
snd_pcm_t *pcm = subs->pcm;
mixart_stream_t *stream;
mixart_pipe_t *pipe;
int err = 0;
int pcm_number;
down(&mgr->setup_mutex);
if ( pcm == chip->pcm ) {
pcm_number = MIXART_PCM_ANALOG;
runtime->hw = snd_mixart_analog_caps;
} else {
snd_assert ( pcm == chip->pcm_dig );
pcm_number = MIXART_PCM_DIGITAL;
runtime->hw = snd_mixart_digital_caps;
}
snd_printdd("snd_mixart_playback_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
/* get stream info */
stream = &(chip->playback_stream[pcm_number][subs->number]);
if (stream->status != MIXART_STREAM_STATUS_FREE){
/* streams in use */
snd_printk(KERN_ERR "snd_mixart_playback_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
err = -EBUSY;
goto _exit_open;
}
/* get pipe pointer (out pipe) */
pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 0, 0);
if (pipe == NULL) {
err = -EINVAL;
goto _exit_open;
}
/* start the pipe if necessary */
err = mixart_set_pipe_state(chip->mgr, pipe, 1);
if( err < 0 ) {
snd_printk(KERN_ERR "error starting pipe!\n");
snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
err = -EINVAL;
goto _exit_open;
}
stream->pipe = pipe;
stream->pcm_number = pcm_number;
stream->status = MIXART_STREAM_STATUS_OPEN;
stream->substream = subs;
stream->channels = 0; /* not configured yet */
runtime->private_data = stream;
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
/* if a sample rate is already used, another stream cannot change */
if(mgr->ref_count_rate++) {
if(mgr->sample_rate) {
runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
}
}
_exit_open:
up(&mgr->setup_mutex);
return err;
}
static int snd_mixart_capture_open(snd_pcm_substream_t *subs)
{
mixart_t *chip = snd_pcm_substream_chip(subs);
mixart_mgr_t *mgr = chip->mgr;
snd_pcm_runtime_t *runtime = subs->runtime;
snd_pcm_t *pcm = subs->pcm;
mixart_stream_t *stream;
mixart_pipe_t *pipe;
int err = 0;
int pcm_number;
down(&mgr->setup_mutex);
if ( pcm == chip->pcm ) {
pcm_number = MIXART_PCM_ANALOG;
runtime->hw = snd_mixart_analog_caps;
} else {
snd_assert ( pcm == chip->pcm_dig );
pcm_number = MIXART_PCM_DIGITAL;
runtime->hw = snd_mixart_digital_caps;
}
runtime->hw.channels_min = 2; /* for instance, no mono */
snd_printdd("snd_mixart_capture_open C%d/P%d/Sub%d\n", chip->chip_idx, pcm_number, subs->number);
/* get stream info */
stream = &(chip->capture_stream[pcm_number]);
if (stream->status != MIXART_STREAM_STATUS_FREE){
/* streams in use */
snd_printk(KERN_ERR "snd_mixart_capture_open C%d/P%d/Sub%d in use\n", chip->chip_idx, pcm_number, subs->number);
err = -EBUSY;
goto _exit_open;
}
/* get pipe pointer (in pipe) */
pipe = snd_mixart_add_ref_pipe(chip, pcm_number, 1, 0);
if (pipe == NULL) {
err = -EINVAL;
goto _exit_open;
}
/* start the pipe if necessary */
err = mixart_set_pipe_state(chip->mgr, pipe, 1);
if( err < 0 ) {
snd_printk(KERN_ERR "error starting pipe!\n");
snd_mixart_kill_ref_pipe(chip->mgr, pipe, 0);
err = -EINVAL;
goto _exit_open;
}
stream->pipe = pipe;
stream->pcm_number = pcm_number;
stream->status = MIXART_STREAM_STATUS_OPEN;
stream->substream = subs;
stream->channels = 0; /* not configured yet */
runtime->private_data = stream;
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 64);
/* if a sample rate is already used, another stream cannot change */
if(mgr->ref_count_rate++) {
if(mgr->sample_rate) {
runtime->hw.rate_min = runtime->hw.rate_max = mgr->sample_rate;
}
}
_exit_open:
up(&mgr->setup_mutex);
return err;
}
static int snd_mixart_close(snd_pcm_substream_t *subs)
{
mixart_t *chip = snd_pcm_substream_chip(subs);
mixart_mgr_t *mgr = chip->mgr;
mixart_stream_t *stream = (mixart_stream_t*)subs->runtime->private_data;
down(&mgr->setup_mutex);
snd_printdd("snd_mixart_close C%d/P%d/Sub%d\n", chip->chip_idx, stream->pcm_number, subs->number);
/* sample rate released */
if(--mgr->ref_count_rate == 0) {
mgr->sample_rate = 0;
}
/* delete pipe */
if (snd_mixart_kill_ref_pipe(mgr, stream->pipe, 0 ) < 0) {
snd_printk(KERN_ERR "error snd_mixart_kill_ref_pipe C%dP%d\n", chip->chip_idx, stream->pcm_number);
}
stream->pipe = NULL;
stream->status = MIXART_STREAM_STATUS_FREE;
stream->substream = NULL;
up(&mgr->setup_mutex);
return 0;
}
static snd_pcm_uframes_t snd_mixart_stream_pointer(snd_pcm_substream_t * subs)
{
snd_pcm_runtime_t *runtime = subs->runtime;
mixart_stream_t *stream = (mixart_stream_t*)runtime->private_data;
return (snd_pcm_uframes_t)((stream->buf_periods * runtime->period_size) + stream->buf_period_frag);
}
static snd_pcm_ops_t snd_mixart_playback_ops = {
.open = snd_mixart_playback_open,
.close = snd_mixart_close,
.ioctl = snd_pcm_lib_ioctl,
.prepare = snd_mixart_prepare,
.hw_params = snd_mixart_hw_params,
.hw_free = snd_mixart_hw_free,
.trigger = snd_mixart_trigger,
.pointer = snd_mixart_stream_pointer,
};
static snd_pcm_ops_t snd_mixart_capture_ops = {
.open = snd_mixart_capture_open,
.close = snd_mixart_close,
.ioctl = snd_pcm_lib_ioctl,
.prepare = snd_mixart_prepare,
.hw_params = snd_mixart_hw_params,
.hw_free = snd_mixart_hw_free,
.trigger = snd_mixart_trigger,
.pointer = snd_mixart_stream_pointer,
};
static void preallocate_buffers(mixart_t *chip, snd_pcm_t *pcm)
{
snd_pcm_substream_t *subs;
int stream;
for (stream = 0; stream < 2; stream++) {
int idx = 0;
for (subs = pcm->streams[stream].substream; subs; subs = subs->next, idx++)
/* set up the unique device id with the chip index */
subs->dma_device.id = subs->pcm->device << 16 |
subs->stream << 8 | (subs->number + 1) |
(chip->chip_idx + 1) << 24;
}
snd_pcm_lib_preallocate_pci_pages_for_all(chip->mgr->pci, pcm, 32*1024, 32*1024);
}
/*
*/
static int snd_mixart_pcm_analog(mixart_t *chip)
{
int err;
snd_pcm_t *pcm;
char name[32];
sprintf(name, "miXart analog %d", chip->chip_idx);
if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_ANALOG,
MIXART_PLAYBACK_STREAMS,
MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
snd_printk(KERN_ERR "cannot create the analog pcm %d\n", chip->chip_idx);
return err;
}
pcm->private_data = chip;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
pcm->info_flags = 0;
strcpy(pcm->name, name);
preallocate_buffers(chip, pcm);
chip->pcm = pcm;
return 0;
}
/*
*/
static int snd_mixart_pcm_digital(mixart_t *chip)
{
int err;
snd_pcm_t *pcm;
char name[32];
sprintf(name, "miXart AES/EBU %d", chip->chip_idx);
if ((err = snd_pcm_new(chip->card, name, MIXART_PCM_DIGITAL,
MIXART_PLAYBACK_STREAMS,
MIXART_CAPTURE_STREAMS, &pcm)) < 0) {
snd_printk(KERN_ERR "cannot create the digital pcm %d\n", chip->chip_idx);
return err;
}
pcm->private_data = chip;
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_mixart_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mixart_capture_ops);
pcm->info_flags = 0;
strcpy(pcm->name, name);
preallocate_buffers(chip, pcm);
chip->pcm_dig = pcm;
return 0;
}
static int snd_mixart_chip_free(mixart_t *chip)
{
snd_magic_kfree(chip);
return 0;
}
static int snd_mixart_chip_dev_free(snd_device_t *device)
{
mixart_t *chip = snd_magic_cast(mixart_t, device->device_data, return -ENXIO);
return snd_mixart_chip_free(chip);
}
/*
*/
static int __devinit snd_mixart_create(mixart_mgr_t *mgr, snd_card_t *card, int idx)
{
int err;
mixart_t *chip;
static snd_device_ops_t ops = {
.dev_free = snd_mixart_chip_dev_free,
};
mgr->chip[idx] = chip = snd_magic_kcalloc(mixart_t, 0, GFP_KERNEL);
if (! chip) {
snd_printk(KERN_ERR "cannot allocate chip\n");
return -ENOMEM;
}
chip->card = card;
chip->chip_idx = idx;
chip->mgr = mgr;
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
snd_mixart_chip_free(chip);
return err;
}
if (idx == 0) {
/* create a DSP loader only on first cardX*/
err = snd_mixart_hwdep_new(mgr);
if (err < 0)
return err;
}
snd_card_set_dev(card, &mgr->pci->dev);
return 0;
}
int snd_mixart_create_pcm(mixart_t* chip)
{
int err;
err = snd_mixart_pcm_analog(chip);
if (err < 0)
return err;
if(chip->mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
err = snd_mixart_pcm_digital(chip);
if (err < 0)
return err;
}
return err;
}
/*
* release all the cards assigned to a manager instance
*/
static int snd_mixart_free(mixart_mgr_t *mgr)
{
unsigned int i;
for (i = 0; i < mgr->num_cards; i++) {
if (mgr->chip[i])
snd_card_free(mgr->chip[i]->card);
}
/* stop mailbox */
snd_mixart_exit_mailbox(mgr);
/* release irq */
if (mgr->irq >= 0)
free_irq(mgr->irq, (void *)mgr);
/* reset board if some firmware was loaded */
if(mgr->hwdep->dsp_loaded) {
snd_mixart_reset_board(mgr);
snd_printdd("reset miXart !\n");
}
/* release the i/o ports */
for (i = 0; i < 2; i++) {
if (mgr->mem[i].virt)
iounmap((void *)mgr->mem[i].virt);
if (mgr->mem[i].res) {
release_resource(mgr->mem[i].res);
kfree_nocheck(mgr->mem[i].res);
}
}
/* free flowarray */
if(mgr->flowinfo_array) {
snd_free_pci_pages(mgr->pci, PAGE_ALIGN(MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)),
mgr->flowinfo_array, mgr->flowinfo_physaddr);
}
/* free bufferarray */
if(mgr->bufferinfo_array) {
snd_free_pci_pages(mgr->pci, PAGE_ALIGN(MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)),
mgr->bufferinfo_array, mgr->bufferinfo_physaddr);
}
snd_magic_kfree(mgr);
return 0;
}
/*
* proc interface
*/
static long long snd_mixart_BA0_llseek(snd_info_entry_t *entry,
void *private_file_data,
struct file *file,
long long offset,
int orig)
{
offset = offset & ~3; /* 4 bytes aligned */
switch(orig) {
case 0: /* SEEK_SET */
file->f_pos = offset;
break;
case 1: /* SEEK_CUR */
file->f_pos += offset;
break;
case 2: /* SEEK_END, offset is negative */
file->f_pos = MIXART_BA0_SIZE + offset;
break;
default:
return -EINVAL;
}
if(file->f_pos > MIXART_BA0_SIZE)
file->f_pos = MIXART_BA0_SIZE;
return file->f_pos;
}
static long long snd_mixart_BA1_llseek(snd_info_entry_t *entry,
void *private_file_data,
struct file *file,
long long offset,
int orig)
{
offset = offset & ~3; /* 4 bytes aligned */
switch(orig) {
case 0: /* SEEK_SET */
file->f_pos = offset;
break;
case 1: /* SEEK_CUR */
file->f_pos += offset;
break;
case 2: /* SEEK_END, offset is negative */
file->f_pos = MIXART_BA1_SIZE + offset;
break;
default:
return -EINVAL;
}
if(file->f_pos > MIXART_BA1_SIZE)
file->f_pos = MIXART_BA1_SIZE;
return file->f_pos;
}
/*
mixart_BA0 proc interface for BAR 0 - read callback
*/
static long snd_mixart_BA0_read(snd_info_entry_t *entry, void *file_private_data,
struct file *file, char *buf, long count)
{
mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO);
count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
if(count <= 0)
return 0;
if(file->f_pos + count > MIXART_BA0_SIZE)
count = (long)(MIXART_BA0_SIZE - file->f_pos);
if(copy_to_user_fromio(buf, MIXART_MEM( mgr, file->f_pos ), count))
return -EFAULT;
file->f_pos += count;
return count;
}
/*
mixart_BA1 proc interface for BAR 1 - read callback
*/
static long snd_mixart_BA1_read(snd_info_entry_t *entry, void *file_private_data,
struct file *file, char *buf, long count)
{
mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, entry->private_data, return -ENXIO);
count = count & ~3; /* make sure the read size is a multiple of 4 bytes */
if(count <= 0)
return 0;
if(file->f_pos + count > MIXART_BA1_SIZE)
count = (long)(MIXART_BA1_SIZE - file->f_pos);
if(copy_to_user_fromio(buf, MIXART_REG( mgr, file->f_pos ), count))
return -EFAULT;
file->f_pos += count;
return count;
}
static struct snd_info_entry_ops snd_mixart_proc_ops_BA0 = {
.read = snd_mixart_BA0_read,
.llseek = snd_mixart_BA0_llseek
};
static struct snd_info_entry_ops snd_mixart_proc_ops_BA1 = {
.read = snd_mixart_BA1_read,
.llseek = snd_mixart_BA1_llseek
};
static void snd_mixart_proc_read(snd_info_entry_t *entry,
snd_info_buffer_t * buffer)
{
mixart_t *chip = snd_magic_cast(mixart_t, entry->private_data, return);
u32 ref;
snd_iprintf(buffer, "Digigram miXart (alsa card %d)\n\n", chip->chip_idx);
/* stats available when embedded OS is running */
if (chip->mgr->hwdep->dsp_loaded & ( 1 << MIXART_MOTHERBOARD_ELF_INDEX)) {
snd_iprintf(buffer, "- hardware -\n");
switch (chip->mgr->board_type ) {
case MIXART_DAUGHTER_TYPE_NONE : snd_iprintf(buffer, "\tmiXart8 (no daughter board)\n\n"); break;
case MIXART_DAUGHTER_TYPE_AES : snd_iprintf(buffer, "\tmiXart8 AES/EBU\n\n"); break;
case MIXART_DAUGHTER_TYPE_COBRANET : snd_iprintf(buffer, "\tmiXart8 Cobranet\n\n"); break;
default: snd_iprintf(buffer, "\tUNKNOWN!\n\n"); break;
}
snd_iprintf(buffer, "- system load -\n");
/* get perf reference */
ref = readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET));
if (ref) {
u32 mailbox = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET)) / ref;
u32 streaming = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET)) / ref;
u32 interr = 100 * readl_be( MIXART_MEM( chip->mgr, MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET)) / ref;
snd_iprintf(buffer, "\tstreaming : %d\n", streaming);
snd_iprintf(buffer, "\tmailbox : %d\n", mailbox);
snd_iprintf(buffer, "\tinterrups handling : %d\n\n", interr);
}
} /* endif elf loaded */
}
static void __devinit snd_mixart_proc_init(mixart_t *chip)
{
snd_info_entry_t *entry;
/* text interface to read perf and temp meters */
if (! snd_card_proc_new(chip->card, "board_info", &entry)) {
entry->private_data = chip;
entry->c.text.read_size = 1024;
entry->c.text.read = snd_mixart_proc_read;
}
if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = chip->mgr;
entry->c.ops = &snd_mixart_proc_ops_BA0;
entry->size = MIXART_BA0_SIZE;
}
if (! snd_card_proc_new(chip->card, "mixart_BA1", &entry)) {
entry->content = SNDRV_INFO_CONTENT_DATA;
entry->private_data = chip->mgr;
entry->c.ops = &snd_mixart_proc_ops_BA1;
entry->size = MIXART_BA1_SIZE;
}
}
/* end of proc interface */
/*
* probe function - creates the card manager
*/
static int __devinit snd_mixart_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
mixart_mgr_t *mgr;
unsigned int i;
int err;
size_t size;
/*
*/
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (! enable[dev]) {
dev++;
return -ENOENT;
}
/* enable PCI device */
if ((err = pci_enable_device(pci)) < 0)
return err;
pci_set_master(pci);
/* check if we can restrict PCI DMA transfers to 32 bits */
if (!pci_dma_supported(pci, 0xffffffff)) {
snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
return -ENXIO;
}
pci_set_dma_mask(pci, 0xffffffff);
/*
*/
mgr = snd_magic_kcalloc(mixart_mgr_t, 0, GFP_KERNEL);
if (! mgr)
return -ENOMEM;
mgr->pci = pci;
mgr->irq = -1;
/* resource assignment */
for (i = 0; i < 2; i++) {
static int memory_sizes[2] = {
MIXART_BA0_SIZE, /* 16M */
MIXART_BA1_SIZE /* 4 k */
};
mgr->mem[i].phys = pci_resource_start(pci, i);
mgr->mem[i].res = request_mem_region(mgr->mem[i].phys, memory_sizes[i], CARD_NAME);
if (! mgr->mem[i].res) {
snd_printk(KERN_ERR "unable to grab memory 0x%lx\n", mgr->mem[i].phys);
snd_mixart_free(mgr);
return -EBUSY;
}
mgr->mem[i].virt = (unsigned long)ioremap_nocache(mgr->mem[i].phys, memory_sizes[i]);
}
if (request_irq(pci->irq, snd_mixart_interrupt, SA_INTERRUPT|SA_SHIRQ, CARD_NAME, (void *)mgr)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_mixart_free(mgr);
return -EBUSY;
}
mgr->irq = pci->irq;
sprintf(mgr->shortname, "Digigram miXart");
sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, irq %i", mgr->shortname, mgr->mem[0].phys, mgr->mem[1].phys, mgr->irq);
/* ISR spinlock */
mgr->lock = SPIN_LOCK_UNLOCKED;
/* init mailbox */
mgr->msg_fifo_readptr = 0;
mgr->msg_fifo_writeptr = 0;
mgr->msg_lock = SPIN_LOCK_UNLOCKED;
init_MUTEX(&mgr->msg_mutex);
/* init setup mutex*/
init_MUTEX(&mgr->setup_mutex);
/* init message taslket */
tasklet_init( &mgr->msg_taskq, snd_mixart_msg_tasklet, (unsigned long) mgr);
/* card assignment */
mgr->num_cards = MIXART_MAX_CARDS; /* 4 FIXME: configurable? */
for (i = 0; i < mgr->num_cards; i++) {
snd_card_t *card;
char tmpid[16];
int idx;
if (index[dev] < 0)
idx = index[dev];
else
idx = index[dev] + i;
snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev], i);
card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
if (! card) {
snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
snd_mixart_free(mgr);
return -ENOMEM;
}
strcpy(card->driver, CARD_NAME);
sprintf(card->shortname, "%s [PCM #%d]", mgr->shortname, i);
sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
if ((err = snd_mixart_create(mgr, card, i)) < 0) {
snd_mixart_free(mgr);
return err;
}
if(i==0) {
/* init proc interface only for chip0 */
snd_mixart_proc_init(mgr->chip[i]);
}
if ((err = snd_card_register(card)) < 0) {
snd_mixart_free(mgr);
return err;
}
}
/* init firmware status (mgr->hwdep->dsp_loaded reset in hwdep_new) */
mgr->board_type = MIXART_DAUGHTER_TYPE_NONE;
/* create array of streaminfo */
size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_flowinfo_t)) );
mgr->flowinfo_array = snd_malloc_pci_pages(mgr->pci, size, &mgr->flowinfo_physaddr);
if(!mgr->flowinfo_array) {
snd_mixart_free(mgr);
return -ENOMEM;
}
/* init streaminfo_array */
memset(mgr->flowinfo_array, 0, size);
/* create array of bufferinfo */
size = PAGE_ALIGN( (MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS * sizeof(mixart_bufferinfo_t)) );
mgr->bufferinfo_array = snd_malloc_pci_pages(mgr->pci, size, &mgr->bufferinfo_physaddr);
if(!mgr->bufferinfo_array) {
snd_mixart_free(mgr);
return -ENOMEM;
}
/* init bufferinfo_array */
memset(mgr->bufferinfo_array, 0, size);
pci_set_drvdata(pci, mgr);
dev++;
return 0;
}
static void __devexit snd_mixart_remove(struct pci_dev *pci)
{
snd_mixart_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
}
static struct pci_driver driver = {
.name = "Digigram miXart",
.id_table = snd_mixart_ids,
.probe = snd_mixart_probe,
.remove = __devexit_p(snd_mixart_remove),
};
static int __init alsa_card_mixart_init(void)
{
int err;
if ((err = pci_module_init(&driver)) < 0) {
#ifdef MODULE
snd_printk(KERN_ERR "Digigram miXart soundcard not found or device busy\n");
#endif
return err;
}
return 0;
}
static void __exit alsa_card_mixart_exit(void)
{
pci_unregister_driver(&driver);
}
module_init(alsa_card_mixart_init)
module_exit(alsa_card_mixart_exit)
#ifndef MODULE
/* format is: snd-mixart=enable,index,id */
static int __init alsa_card_mixart_setup(char *str)
{
static unsigned __initdata nr_dev = 0;
if (nr_dev >= SNDRV_CARDS)
return 0;
(void)(get_option(&str,&enable[nr_dev]) == 2 &&
get_option(&str,&index[nr_dev]) == 2 &&
get_id(&str,&id[nr_dev]) == 2);
nr_dev++;
return 1;
}
__setup("snd-mixart=", alsa_card_mixart_setup);
#endif /* ifndef MODULE */
/*
* Driver for Digigram miXart soundcards
*
* main header file
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SOUND_MIXART_H
#define __SOUND_MIXART_H
#include <sound/pcm.h>
#define MIXART_DRIVER_VERSION 0x000100 /* 0.1.0 */
/*
*/
#define mixart_t_magic 0xa17a3e01
#define mixart_mgr_t_magic 0xa17a3e02
typedef struct snd_mixart mixart_t;
typedef struct snd_mixart_mgr mixart_mgr_t;
typedef struct snd_mixart_stream mixart_stream_t;
typedef struct snd_mixart_pipe mixart_pipe_t;
typedef struct mixart_bufferinfo mixart_bufferinfo_t;
typedef struct mixart_flowinfo mixart_flowinfo_t;
typedef struct mixart_uid mixart_uid_t;
struct mixart_uid
{
u32 object_id;
u32 desc;
};
struct mem_area {
unsigned long phys;
unsigned long virt;
struct resource *res;
};
typedef struct mixart_route mixart_route_t;
struct mixart_route {
unsigned char connected;
unsigned char phase_inv;
int volume;
};
/* firmware status codes */
#define MIXART_MOTHERBOARD_XLX_INDEX 0
#define MIXART_MOTHERBOARD_ELF_INDEX 1
#define MIXART_AESEBUBOARD_XLX_INDEX 2
#define MIXART_HARDW_FILES_MAX_INDEX 3 /* xilinx, elf, AESEBU xilinx */
#define MIXART_MAX_CARDS 4
#define MSG_FIFO_SIZE 16
#define MIXART_MAX_PHYS_CONNECTORS (MIXART_MAX_CARDS * 2 * 2) /* 4 * stereo * (analog+digital) */
struct snd_mixart_mgr {
unsigned int num_cards;
mixart_t *chip[MIXART_MAX_CARDS];
struct pci_dev *pci;
int irq;
/* memory-maps */
struct mem_area mem[2];
/* share the name */
char shortname[32]; /* short name of this soundcard */
char longname[80]; /* name of this soundcard */
/* message tasklet */
struct tasklet_struct msg_taskq;
/* one and only blocking message or notification may be pending */
u32 pending_event;
wait_queue_head_t msg_sleep;
/* messages stored for tasklet */
u32 msg_fifo[MSG_FIFO_SIZE];
int msg_fifo_readptr;
int msg_fifo_writeptr;
spinlock_t lock; /* interrupt spinlock */
spinlock_t msg_lock; /* mailbox spinlock */
struct semaphore msg_mutex; /* mutex for blocking_requests */
struct semaphore setup_mutex; /* mutex used in hw_params, open and close */
/* hardware interface */
snd_hwdep_t *hwdep;
unsigned int board_type; /* read from embedded once elf file is loaded, 250 = miXart8, 251 = with AES, 252 = with Cobranet */
mixart_flowinfo_t* flowinfo_array;
dma_addr_t flowinfo_physaddr;
mixart_bufferinfo_t* bufferinfo_array;
dma_addr_t bufferinfo_physaddr;
mixart_uid_t uid_console_manager;
int sample_rate;
int ref_count_rate;
struct semaphore mixer_mutex; /* mutex for mixer */
};
#define MIXART_STREAM_STATUS_FREE 0
#define MIXART_STREAM_STATUS_OPEN 1
#define MIXART_STREAM_STATUS_RUNNING 2
#define MIXART_STREAM_STATUS_DRAINING 3
#define MIXART_STREAM_STATUS_PAUSE 4
#define MIXART_PLAYBACK_STREAMS 4
#define MIXART_CAPTURE_STREAMS 1
#define MIXART_PCM_ANALOG 0
#define MIXART_PCM_DIGITAL 1
#define MIXART_PCM_TOTAL 2
#define MIXART_MAX_STREAM_PER_CARD (MIXART_PCM_TOTAL * (MIXART_PLAYBACK_STREAMS + MIXART_CAPTURE_STREAMS) )
#define MIXART_NOTIFY_CARD_MASK 0xF000
#define MIXART_NOTIFY_CARD_OFFSET 12
#define MIXART_NOTIFY_PCM_MASK 0x0F00
#define MIXART_NOTIFY_PCM_OFFSET 8
#define MIXART_NOTIFY_CAPT_MASK 0x0080
#define MIXART_NOTIFY_SUBS_MASK 0x007F
struct snd_mixart_stream {
snd_pcm_substream_t *substream;
mixart_pipe_t *pipe;
int pcm_number;
int status; /* nothing, running, draining */
u64 abs_period_elapsed; /* last absolute stream position where period_elapsed was called (multiple of runtime->period_size) */
u32 buf_periods; /* periods counter in the buffer (< runtime->periods) */
u32 buf_period_frag; /* defines with buf_period_pos the exact position in the buffer (< runtime->period_size) */
int channels;
};
enum mixart_pipe_status {
PIPE_UNDEFINED,
PIPE_STOPPED,
PIPE_RUNNING,
PIPE_CLOCK_SET
};
struct snd_mixart_pipe {
mixart_uid_t group_uid; /* id of the pipe, as returned by embedded */
int stream_count;
mixart_uid_t uid_left_connector; /* UID's for the audio connectors */
mixart_uid_t uid_right_connector;
enum mixart_pipe_status status;
int references; /* number of subs openned */
int monitoring; /* pipe used for monitoring issue */
};
struct snd_mixart {
snd_card_t *card;
mixart_mgr_t *mgr;
int chip_idx; /* zero based */
snd_hwdep_t *hwdep; /* DSP loader, only for the first card */
snd_pcm_t *pcm; /* PCM analog i/o */
snd_pcm_t *pcm_dig; /* PCM digital i/o */
/* allocate stereo pipe for instance */
mixart_pipe_t pipe_in_ana;
mixart_pipe_t pipe_out_ana;
/* if AES/EBU daughter board is available, additional pipes possible on pcm_dig */
mixart_pipe_t pipe_in_dig;
mixart_pipe_t pipe_out_dig;
mixart_stream_t playback_stream[MIXART_PCM_TOTAL][MIXART_PLAYBACK_STREAMS]; /* 0 = pcm, 1 = pcm_dig */
mixart_stream_t capture_stream[MIXART_PCM_TOTAL]; /* 0 = pcm, 1 = pcm_dig */
/* UID's for the physical io's */
mixart_uid_t uid_out_analog_physio;
mixart_uid_t uid_in_analog_physio;
int analog_playback_active[2]; /* Mixer : Master Playback active (!mute) */
int analog_playback_volume[2]; /* Mixer : Master Playback Volume */
int analog_capture_volume[2]; /* Mixer : Master Capture Volume */
int digital_playback_active[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Active [(analog+AES output)*streams][stereo]*/
int digital_playback_volume[2*MIXART_PLAYBACK_STREAMS][2]; /* Mixer : Digital Playback Volume [(analog+AES output)*streams][stereo]*/
int digital_capture_volume[2][2]; /* Mixer : Digital Capture Volume [analog+AES output][stereo] */
int monitoring_active[2]; /* Mixer : Monitoring Active */
int monitoring_volume[2]; /* Mixer : Monitoring Volume */
};
struct mixart_bufferinfo
{
u32 buffer_address;
u32 reserved[5];
u32 available_length;
u32 buffer_id;
};
struct mixart_flowinfo
{
u32 bufferinfo_array_phy_address;
u32 reserved[11];
u32 bufferinfo_count;
u32 capture;
};
/* exported */
int snd_mixart_create_pcm(mixart_t* chip);
mixart_pipe_t* snd_mixart_add_ref_pipe( mixart_t *chip, int pcm_number, int capture, int monitoring);
int snd_mixart_kill_ref_pipe( mixart_mgr_t *mgr, mixart_pipe_t *pipe, int monitoring);
#endif /* __SOUND_MIXART_H */
/*
* Driver for Digigram miXart soundcards
*
* low level interface with interrupt handling and mail box implementation
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/driver.h>
#include <sound/core.h>
#include "mixart.h"
#include "mixart_hwdep.h"
#include "mixart_core.h"
#define MSG_TIMEOUT_JIFFIES (400 * HZ) / 1000 /* 400 ms */
#define MSG_DESCRIPTOR_SIZE 0x24
#define MSG_HEADER_SIZE (MSG_DESCRIPTOR_SIZE + 4)
#define MSG_DEFAULT_SIZE 512
#define MSG_TYPE_MASK 0x00000003 /* mask for following types */
#define MSG_TYPE_NOTIFY 0 /* embedded -> driver (only notification, do not get_msg() !) */
#define MSG_TYPE_COMMAND 1 /* driver <-> embedded (a command has no answer) */
#define MSG_TYPE_REQUEST 2 /* driver -> embedded (request will get an answer back) */
#define MSG_TYPE_ANSWER 3 /* embedded -> driver */
#define MSG_CANCEL_NOTIFY_MASK 0x80000000 /* this bit is set for a notification that has been canceled */
static int retrieve_msg_frame(mixart_mgr_t *mgr, u32 *msg_frame)
{
/* read the message frame fifo */
u32 headptr, tailptr;
tailptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_POST_HEAD));
if (tailptr == headptr)
return 0; /* no message posted */
snd_assert( tailptr >= MSG_OUTBOUND_POST_STACK, return 0); /* error */
snd_assert( tailptr < (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE), return 0); /* error */
*msg_frame = readl_be(MIXART_MEM(mgr, tailptr));
/* increment the tail index */
tailptr += 4;
if( tailptr >= (MSG_OUTBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
tailptr = MSG_OUTBOUND_POST_STACK;
writel_be(tailptr, MIXART_MEM(mgr, MSG_OUTBOUND_POST_TAIL));
return 1;
}
static int get_msg(mixart_mgr_t *mgr, mixart_msg_t *resp, u32 msg_frame_address )
{
unsigned long flags;
u32 headptr, i;
u32 size;
int err;
spin_lock_irqsave(&mgr->msg_lock, flags);
err = 0;
/* copy message descriptor from miXart to driver */
size = readl_be(MIXART_MEM(mgr, msg_frame_address)); /* size of descriptor + response */
resp->message_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 4)); /* dwMessageID */
resp->uid.object_id = readl_be(MIXART_MEM(mgr, msg_frame_address + 8)); /* uidDest */
resp->uid.desc = readl_be(MIXART_MEM(mgr, msg_frame_address + 12)); /* */
if( (size < MSG_DESCRIPTOR_SIZE) || (resp->size < (size - MSG_DESCRIPTOR_SIZE))) {
err = -EINVAL;
snd_printk(KERN_ERR "problem with response size = %d\n", size);
goto _clean_exit;
}
size -= MSG_DESCRIPTOR_SIZE;
memcpy_fromio(resp->data, MIXART_MEM(mgr, msg_frame_address + MSG_HEADER_SIZE ), size);
resp->size = size;
/* swap if necessary */
#ifndef __BIG_ENDIAN
size /= 4; /* u32 size */
for(i=0; i < size; i++) {
((u32*)resp->data)[i] = be32_to_cpu(((u32*)resp->data)[i]);
}
#endif
/*
* free message frame address
*/
headptr = readl_be(MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
if( (headptr < MSG_OUTBOUND_FREE_STACK) || ( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
err = -EINVAL;
goto _clean_exit;
}
/* give address back to outbound fifo */
writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
/* increment the outbound free head */
headptr += 4;
if( headptr >= (MSG_OUTBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
headptr = MSG_OUTBOUND_FREE_STACK;
writel_be(headptr, MIXART_MEM(mgr, MSG_OUTBOUND_FREE_HEAD));
_clean_exit:
spin_unlock_irqrestore(&mgr->msg_lock, flags);
return err;
}
/*
* send a message to miXart. return: the msg_frame used for this message
*/
static int send_msg( mixart_mgr_t *mgr,
mixart_msg_t *msg,
int max_answersize,
int mark_pending,
u32 *msg_event)
{
unsigned long flags;
u32 headptr, tailptr;
u32 msg_frame_address;
int err, i;
snd_assert(msg->size % 4 == 0, return -EINVAL);
spin_lock_irqsave(&mgr->msg_lock, flags);
err = 0;
/* get message frame address */
tailptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_FREE_HEAD));
if (tailptr == headptr) {
snd_printk(KERN_ERR "error: no message frame available\n");
err = -EBUSY;
goto _clean_exit;
}
if( (tailptr < MSG_INBOUND_FREE_STACK) || (tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE))) {
err = -EINVAL;
goto _clean_exit;
}
msg_frame_address = readl_be(MIXART_MEM(mgr, tailptr));
writel(0, MIXART_MEM(mgr, tailptr)); /* set address to zero on this fifo position */
/* increment the inbound free tail */
tailptr += 4;
if( tailptr >= (MSG_INBOUND_FREE_STACK+MSG_BOUND_STACK_SIZE) )
tailptr = MSG_INBOUND_FREE_STACK;
writel_be(tailptr, MIXART_MEM(mgr, MSG_INBOUND_FREE_TAIL));
/* TODO : use memcpy_toio() with intermediate buffer to copy the message */
/* copy message descriptor to card memory */
writel_be( msg->size + MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address) ); /* size of descriptor + request */
writel_be( msg->message_id , MIXART_MEM(mgr, msg_frame_address + 4) ); /* dwMessageID */
writel_be( msg->uid.object_id, MIXART_MEM(mgr, msg_frame_address + 8) ); /* uidDest */
writel_be( msg->uid.desc, MIXART_MEM(mgr, msg_frame_address + 12) ); /* */
writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 16) ); /* SizeHeader */
writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 20) ); /* OffsetDLL_T16 */
writel_be( msg->size, MIXART_MEM(mgr, msg_frame_address + 24) ); /* SizeDLL_T16 */
writel_be( MSG_DESCRIPTOR_SIZE, MIXART_MEM(mgr, msg_frame_address + 28) ); /* OffsetDLL_DRV */
writel_be( 0, MIXART_MEM(mgr, msg_frame_address + 32) ); /* SizeDLL_DRV */
writel_be( MSG_DESCRIPTOR_SIZE + max_answersize, MIXART_MEM(mgr, msg_frame_address + 36) ); /* dwExpectedAnswerSize */
/* copy message data to card memory */
for( i=0; i < msg->size; i+=4 ) {
writel_be( *(u32*)(msg->data + i), MIXART_MEM(mgr, MSG_HEADER_SIZE + msg_frame_address + i) );
}
if( mark_pending ) {
if( *msg_event ) {
/* the pending event is the notification we wait for ! */
mgr->pending_event = *msg_event;
}
else {
/* the pending event is the answer we wait for (same address than the request)! */
mgr->pending_event = msg_frame_address;
/* copy address back to caller */
*msg_event = msg_frame_address;
}
}
/* mark the frame as a request (will have an answer) */
msg_frame_address |= MSG_TYPE_REQUEST;
/* post the frame */
headptr = readl_be(MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
if( (headptr < MSG_INBOUND_POST_STACK) || (headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE))) {
err = -EINVAL;
goto _clean_exit;
}
writel_be(msg_frame_address, MIXART_MEM(mgr, headptr));
/* increment the inbound post head */
headptr += 4;
if( headptr >= (MSG_INBOUND_POST_STACK+MSG_BOUND_STACK_SIZE) )
headptr = MSG_INBOUND_POST_STACK;
writel_be(headptr, MIXART_MEM(mgr, MSG_INBOUND_POST_HEAD));
_clean_exit:
spin_unlock_irqrestore(&mgr->msg_lock, flags);
return 0;
}
int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data)
{
mixart_msg_t resp;
u32 msg_frame = 0; /* set to 0, so it's no notification to wait for, but the answer */
int err;
wait_queue_t wait;
long timeout;
down(&mgr->msg_mutex);
init_waitqueue_head(&mgr->msg_sleep);
init_waitqueue_entry(&wait, current);
current->state = TASK_UNINTERRUPTIBLE;
add_wait_queue(&mgr->msg_sleep, &wait);
/* send the message */
err = send_msg(mgr, request, max_resp_size, 1, &msg_frame); /* send and mark the answer pending */
if(err) {
current->state = TASK_RUNNING;
remove_wait_queue(&mgr->msg_sleep, &wait);
up(&mgr->msg_mutex);
return err;
}
timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
current->state = TASK_RUNNING;
remove_wait_queue(&mgr->msg_sleep, &wait);
if (! timeout) {
/* error - no ack */
up(&mgr->msg_mutex);
snd_printk(KERN_ERR "error: no reponse on msg %x\n", msg_frame);
return -EIO;
}
/* retrieve the answer into the same mixart_msg_t */
resp.message_id = 0;
resp.uid = (mixart_uid_t){0,0};
resp.data = resp_data;
resp.size = max_resp_size;
err = get_msg(mgr, &resp, msg_frame);
if( request->message_id != resp.message_id )
snd_printk(KERN_ERR "REPONSE ERROR!\n");
up(&mgr->msg_mutex);
return err;
}
int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event)
{
int err;
wait_queue_t wait;
long timeout;
snd_assert(notif_event != 0, return -EINVAL);
snd_assert((notif_event & MSG_TYPE_MASK) == MSG_TYPE_NOTIFY, return -EINVAL);
snd_assert((notif_event & MSG_CANCEL_NOTIFY_MASK) == 0, return -EINVAL);
down(&mgr->msg_mutex);
init_waitqueue_head(&mgr->msg_sleep);
init_waitqueue_entry(&wait, current);
current->state = TASK_UNINTERRUPTIBLE;
add_wait_queue(&mgr->msg_sleep, &wait);
/* send the message */
err = send_msg(mgr, request, MSG_DEFAULT_SIZE, 1, &notif_event); /* send and mark the notification event pending */
if(err) {
current->state = TASK_RUNNING;
remove_wait_queue(&mgr->msg_sleep, &wait);
up(&mgr->msg_mutex);
return err;
}
timeout = schedule_timeout(MSG_TIMEOUT_JIFFIES);
current->state = TASK_RUNNING;
remove_wait_queue(&mgr->msg_sleep, &wait);
if (! timeout) {
/* error - no ack */
up(&mgr->msg_mutex);
snd_printk(KERN_ERR "error: notification %x not received\n", notif_event);
return -EIO;
}
up(&mgr->msg_mutex);
return 0;
}
int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request)
{
u32 message_frame;
/* just send the message (do not mark it as a pending one) */
return send_msg(mgr, request, MSG_DEFAULT_SIZE, 0, &message_frame);
/* the answer will be handled by snd_mixart_msg_tasklet() */
}
/* common buffer of tasklet and interrupt to send/receive messages */
static u32 mixart_msg_data[MSG_DEFAULT_SIZE / 4];
void snd_mixart_msg_tasklet( unsigned long arg)
{
mixart_mgr_t *mgr = ( mixart_mgr_t*)(arg);
mixart_msg_t resp;
u32 msg, addr, type;
int err;
spin_lock(&mgr->lock);
while (mgr->msg_fifo_readptr != mgr->msg_fifo_writeptr) {
msg = mgr->msg_fifo[mgr->msg_fifo_readptr];
mgr->msg_fifo_readptr++;
mgr->msg_fifo_readptr %= MSG_FIFO_SIZE;
/* process the message ... */
addr = msg & ~MSG_TYPE_MASK;
type = msg & MSG_TYPE_MASK;
switch (type) {
case MSG_TYPE_ANSWER:
/* answer to a message on that we did not wait for (send_msg_nonblock) */
resp.message_id = 0;
resp.data = mixart_msg_data;
resp.size = sizeof(mixart_msg_data);
err = get_msg(mgr, &resp, addr);
if( err < 0 ) {
snd_printk(KERN_ERR "tasklet: error(%d) reading mf %x\n", err, msg);
break;
}
switch(resp.message_id) {
case MSG_STREAM_START_INPUT_STAGE_PACKET:
case MSG_STREAM_START_OUTPUT_STAGE_PACKET:
case MSG_STREAM_STOP_INPUT_STAGE_PACKET:
case MSG_STREAM_STOP_OUTPUT_STAGE_PACKET:
if(mixart_msg_data[0])
snd_printdd("tasklet : MSG_STREAM_ST***_***PUT_STAGE_PACKET txx_status(%x)\n", mixart_msg_data[0]);
break;
case MSG_CLOCK_CHECK_PROPERTIES:
case MSG_CLOCK_SET_PROPERTIES:
if(mixart_msg_data[0])
snd_printdd("tasklet : MSG_CLOCK_***_PROPERTIES txx_status(%x) clock_mode(%x)\n", mixart_msg_data[0], mixart_msg_data[1]);
break;
case MSG_SYSTEM_WAIT_SYNCHRO_CMD:
if(mixart_msg_data[0])
snd_printdd("tasklet : MSG_SYSTEM_WAIT_SYNCHRO_CMD txx_status(%x)\n", mixart_msg_data[0]);
break;
default:
snd_printdd("tasklet received mf(%x) : msg_id(%x) uid(%x, %x) size(%d)\n",
msg, resp.message_id, resp.uid.object_id, resp.uid.desc, resp.size);
break;
}
break;
case MSG_TYPE_NOTIFY:
/* msg contains no address ! do not get_msg() ! */
case MSG_TYPE_COMMAND:
/* get_msg() necessary */
default:
snd_printk(KERN_ERR "tasklet doesn't know what to do with message %x\n", msg);
} /* switch type */
} /* while there is a msg in fifo */
spin_unlock(&mgr->lock);
}
irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, dev_id, return IRQ_NONE);
int err;
mixart_msg_t resp;
u32 msg;
u32 it_reg;
spin_lock(&mgr->lock);
it_reg = readl_le(MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET));
if( !(it_reg & MIXART_OIDI) ) {
/* this device did not cause the interrupt */
spin_unlock(&mgr->lock);
return IRQ_NONE;
}
/* mask all interrupts */
writel_le(MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG(mgr, MIXART_PCI_OMIMR_OFFSET));
/* outdoorbell register clear */
it_reg = readl(MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
writel(it_reg, MIXART_REG(mgr, MIXART_PCI_ODBR_OFFSET));
/* clear interrupt */
writel_le( MIXART_OIDI, MIXART_REG(mgr, MIXART_PCI_OMISR_OFFSET) );
/* process interrupt */
while (retrieve_msg_frame(mgr, &msg)) {
switch (msg & MSG_TYPE_MASK) {
case MSG_TYPE_COMMAND:
resp.message_id = 0;
resp.data = mixart_msg_data;
resp.size = sizeof(mixart_msg_data);
err = get_msg(mgr, &resp, msg & ~MSG_TYPE_MASK);
if( err < 0 ) {
snd_printk(KERN_ERR "interrupt: error(%d) reading mf %x\n", err, msg);
break;
}
if(resp.message_id == MSG_SERVICES_TIMER_NOTIFY) {
int i;
mixart_timer_notify_t *notify = (mixart_timer_notify_t*)mixart_msg_data;
for(i=0; i<notify->stream_count; i++) {
u32 buffer_id = notify->streams[i].buffer_id;
unsigned int chip_number = (buffer_id & MIXART_NOTIFY_CARD_MASK) >> MIXART_NOTIFY_CARD_OFFSET; /* card0 to 3 */
unsigned int pcm_number = (buffer_id & MIXART_NOTIFY_PCM_MASK ) >> MIXART_NOTIFY_PCM_OFFSET; /* pcm0 to 3 */
unsigned int sub_number = buffer_id & MIXART_NOTIFY_SUBS_MASK; /* 0 to MIXART_PLAYBACK_STREAMS */
unsigned int is_capture = ((buffer_id & MIXART_NOTIFY_CAPT_MASK) != 0); /* playback == 0 / capture == 1 */
mixart_t *chip = mgr->chip[chip_number];
mixart_stream_t *stream;
if ((chip_number >= mgr->num_cards) || (pcm_number >= MIXART_PCM_TOTAL) || (sub_number >= MIXART_PLAYBACK_STREAMS)) {
snd_printk(KERN_DEBUG "ERROR buffer_id (%x) pos(%d)\n", buffer_id, notify->streams[i].sample_pos_low_part);
break;
}
if (is_capture)
stream = &chip->capture_stream[pcm_number];
else
stream = &chip->playback_stream[pcm_number][sub_number];
if (stream->substream && (stream->status == MIXART_STREAM_STATUS_RUNNING)) {
snd_pcm_runtime_t *runtime = stream->substream->runtime;
int elapsed = 0;
u64 sample_count = ((u64)notify->streams[i].sample_pos_high_part) << 32;
sample_count |= notify->streams[i].sample_pos_low_part;
while (1) {
u64 new_elapse_pos = stream->abs_period_elapsed + runtime->period_size;
if (new_elapse_pos > sample_count) {
break; /* while */
}
else {
elapsed = 1;
stream->buf_periods++;
if (stream->buf_periods >= runtime->periods)
stream->buf_periods = 0;
stream->abs_period_elapsed = new_elapse_pos;
}
}
stream->buf_period_frag = (u32)( sample_count - stream->abs_period_elapsed );
if(elapsed) {
spin_unlock(&mgr->lock);
snd_pcm_period_elapsed(stream->substream);
spin_lock(&mgr->lock);
}
}
}
break;
}
if(resp.message_id == MSG_SERVICES_REPORT_TRACES) {
if(resp.size > 1) {
#ifndef __BIG_ENDIAN
/* Traces are text: the swapped msg_data has to be swapped back ! */
int i;
for(i=0; i<(resp.size/4); i++) {
(mixart_msg_data)[i] = cpu_to_be32((mixart_msg_data)[i]);
}
#endif
((char*)mixart_msg_data)[resp.size - 1] = 0;
snd_printdd("MIXART TRACE : %s\n", (char*)mixart_msg_data);
}
break;
}
snd_printdd("command %x not handled\n", resp.message_id);
break;
case MSG_TYPE_NOTIFY:
if(msg & MSG_CANCEL_NOTIFY_MASK) {
msg &= ~MSG_CANCEL_NOTIFY_MASK;
snd_printk(KERN_ERR "canceled notification %x !\n", msg);
}
/* no break, continue ! */
case MSG_TYPE_ANSWER:
/* answer or notification to a message we are waiting for*/
if( (msg & ~MSG_TYPE_MASK) == mgr->pending_event ) {
wake_up(&mgr->msg_sleep);
mgr->pending_event = 0;
}
/* answer to a message we did't want to wait for */
else {
mgr->msg_fifo[mgr->msg_fifo_writeptr] = msg;
mgr->msg_fifo_writeptr++;
mgr->msg_fifo_writeptr %= MSG_FIFO_SIZE;
tasklet_hi_schedule(&mgr->msg_taskq);
}
break;
case MSG_TYPE_REQUEST:
default:
snd_printdd("interrupt received request %x\n", msg);
/* TODO : are there things to do here ? */
break;
} /* switch on msg type */
} /* while there are msgs */
/* allow interrupt again */
writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
spin_unlock(&mgr->lock);
return IRQ_HANDLED;
}
void snd_mixart_init_mailbox(mixart_mgr_t *mgr)
{
writel( 0, MIXART_MEM( mgr, MSG_HOST_RSC_PROTECTION ) );
writel( 0, MIXART_MEM( mgr, MSG_AGENT_RSC_PROTECTION ) );
/* allow outbound messagebox to generate interrupts */
if(mgr->irq >= 0) {
writel_le( MIXART_ALLOW_OUTBOUND_DOORBELL, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
}
return;
}
void snd_mixart_exit_mailbox(mixart_mgr_t *mgr)
{
/* no more interrupts on outbound messagebox */
writel_le( MIXART_HOST_ALL_INTERRUPT_MASKED, MIXART_REG( mgr, MIXART_PCI_OMIMR_OFFSET));
return;
}
void snd_mixart_reset_board(mixart_mgr_t *mgr)
{
/* reset miXart */
writel_be( 1, MIXART_REG(mgr, MIXART_BA1_BRUTAL_RESET_OFFSET) );
return;
}
/*
* Driver for Digigram miXart soundcards
*
* low level interface with interrupt handling and mail box implementation
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SOUND_MIXART_CORE_H
#define __SOUND_MIXART_CORE_H
enum mixart_message_id {
MSG_CONNECTOR_GET_AUDIO_INFO = 0x050008,
MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
MSG_CONSOLE_MANAGER = 0x070000,
MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
MSG_STREAM_ADD_INPUT_GROUP = 0x130000,
MSG_STREAM_ADD_OUTPUT_GROUP = 0x130001,
MSG_STREAM_DELETE_GROUP = 0x130004,
MSG_STREAM_START_STREAM_GRP_PACKET = 0x130006,
MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130007,
MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130008,
MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130009,
MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x13000A,
MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x13000B,
MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F,
MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010,
MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017,
MSG_SYSTEM_FIRST_ID = 0x160000,
MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
MSG_SYSTEM_ENUM_PLAY_CONNECTOR = 0x160017,
MSG_SYSTEM_ENUM_RECORD_CONNECTOR = 0x160018,
MSG_SYSTEM_WAIT_SYNCHRO_CMD = 0x16002C,
MSG_SYSTEM_SEND_SYNCHRO_CMD = 0x16002D,
MSG_SERVICES_TIMER_NOTIFY = 0x1D0404,
MSG_SERVICES_REPORT_TRACES = 0x1D0700,
MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
MSG_CLOCK_SET_PROPERTIES = 0x200002,
};
typedef struct mixart_msg mixart_msg_t;
struct mixart_msg
{
u32 message_id;
mixart_uid_t uid;
void* data;
size_t size;
};
/* structs used to communicate with miXart */
typedef struct mixart_enum_connector_resp mixart_enum_connector_resp_t;
struct mixart_enum_connector_resp
{
u32 error_code;
u32 first_uid_offset;
u32 uid_count;
u32 current_uid_index;
mixart_uid_t uid[MIXART_MAX_PHYS_CONNECTORS];
} __attribute__((packed));
/* used for following struct */
#define MIXART_FLOAT_P_22_0_TO_HEX 0x41b00000 /* 22.0f */
#define MIXART_FLOAT_M_20_0_TO_HEX 0xc1a00000 /* -20.0f */
#define MIXART_FLOAT____0_0_TO_HEX 0x00000000 /* 0.0f */
typedef struct mixart_audio_info_req mixart_audio_info_req_t;
struct mixart_audio_info_req
{
u32 line_max_level; /* float */
u32 micro_max_level; /* float */
u32 cd_max_level; /* float */
} __attribute__((packed));
typedef struct mixart_analog_hw_info mixart_analog_hw_info_t;
struct mixart_analog_hw_info
{
u32 is_present;
u32 hw_connection_type;
u32 max_level; /* float */
u32 min_var_level; /* float */
u32 max_var_level; /* float */
u32 step_var_level; /* float */
u32 fix_gain; /* float */
u32 zero_var; /* float */
} __attribute__((packed));
typedef struct mixart_digital_hw_info mixart_digital_hw_info_t;
struct mixart_digital_hw_info
{
u32 hw_connection_type;
u32 presence;
u32 clock;
u32 reserved;
} __attribute__((packed));
typedef struct mixart_analog_info mixart_analog_info_t;
struct mixart_analog_info
{
u32 type_mask;
mixart_analog_hw_info_t micro_info;
mixart_analog_hw_info_t line_info;
mixart_analog_hw_info_t cd_info;
u32 analog_level_present;
} __attribute__((packed));
typedef struct mixart_digital_info mixart_digital_info_t;
struct mixart_digital_info
{
u32 type_mask;
mixart_digital_hw_info_t aes_info;
mixart_digital_hw_info_t adat_info;
} __attribute__((packed));
typedef struct mixart_audio_info mixart_audio_info_t;
struct mixart_audio_info
{
u32 clock_type_mask;
mixart_analog_info_t analog_info;
mixart_digital_info_t digital_info;
} __attribute__((packed));
typedef struct mixart_audio_info_resp mixart_audio_info_resp_t;
struct mixart_audio_info_resp
{
u32 txx_status;
mixart_audio_info_t info;
} __attribute__((packed));
/* used for nb_bytes_max_per_sample */
#define MIXART_FLOAT_P__4_0_TO_HEX 0x40800000 /* +4.0f */
#define MIXART_FLOAT_P__8_0_TO_HEX 0x41000000 /* +8.0f */
typedef struct mixart_stream_info mixart_stream_info_t;
struct mixart_stream_info
{
u32 size_max_byte_frame;
u32 size_max_sample_frame;
u32 nb_bytes_max_per_sample; /* float */
} __attribute__((packed));
/* MSG_STREAM_ADD_INPUT_GROUP */
/* MSG_STREAM_ADD_OUTPUT_GROUP */
typedef struct mixart_streaming_group_req mixart_streaming_group_req_t;
struct mixart_streaming_group_req
{
u32 stream_count;
u32 channel_count;
u32 user_grp_number;
u32 first_phys_audio;
u32 latency;
mixart_stream_info_t stream_info[32];
mixart_uid_t connector;
u32 flow_entry[32];
} __attribute__((packed));
typedef struct mixart_stream_desc mixart_stream_desc_t;
struct mixart_stream_desc
{
mixart_uid_t stream_uid;
u32 stream_desc;
} __attribute__((packed));
typedef struct mixart_streaming_group mixart_streaming_group_t;
struct mixart_streaming_group
{
u32 status;
mixart_uid_t group;
u32 pipe_desc;
u32 stream_count;
mixart_stream_desc_t stream[32];
} __attribute__((packed));
/* MSG_STREAM_DELETE_GROUP */
/* request : mixart_uid_t group */
typedef struct mixart_delete_group_resp mixart_delete_group_resp_t;
struct mixart_delete_group_resp
{
u32 status;
u32 unused[2];
} __attribute__((packed));
/* MSG_STREAM_START_INPUT_STAGE_PACKET = 0x130000 + 7,
MSG_STREAM_START_OUTPUT_STAGE_PACKET = 0x130000 + 8,
MSG_STREAM_STOP_INPUT_STAGE_PACKET = 0x130000 + 10,
MSG_STREAM_STOP_OUTPUT_STAGE_PACKET = 0x130000 + 11,
*/
typedef struct mixart_fx_couple_uid mixart_fx_couple_uid_t;
struct mixart_fx_couple_uid
{
mixart_uid_t uid_fx_code;
mixart_uid_t uid_fx_data;
} __attribute__((packed));
typedef struct mixart_txx_stream_desc mixart_txx_stream_desc_t;
struct mixart_txx_stream_desc
{
mixart_uid_t uid_pipe;
u32 stream_idx;
u32 fx_number;
mixart_fx_couple_uid_t uid_fx[4];
} __attribute__((packed));
typedef struct mixart_flow_info mixart_flow_info_t;
struct mixart_flow_info
{
mixart_txx_stream_desc_t stream_desc;
u32 flow_entry;
u32 flow_phy_addr;
} __attribute__((packed));
typedef struct mixart_stream_state_req mixart_stream_state_req_t;
struct mixart_stream_state_req
{
u32 delayed;
u64 scheduler;
u32 reserved4np[3];
u32 stream_count; /* set to 1 for instance */
mixart_flow_info_t stream_info; /* could be an array[stream_count] */
} __attribute__((packed));
/* MSG_STREAM_START_STREAM_GRP_PACKET = 0x130000 + 6
MSG_STREAM_STOP_STREAM_GRP_PACKET = 0x130000 + 9
*/
typedef struct mixart_group_state_req mixart_group_state_req_t;
struct mixart_group_state_req
{
u32 delayed;
u64 scheduler;
u32 reserved4np[2];
u32 pipe_count; /* set to 1 for instance */
mixart_uid_t pipe_uid[1]; /* could be an array[pipe_count] */
} __attribute__((packed));
typedef struct mixart_group_state_resp mixart_group_state_resp_t;
struct mixart_group_state_resp
{
u32 txx_status;
u64 scheduler;
} __attribute__((packed));
/* Structures used by the MSG_SERVICES_TIMER_NOTIFY command */
typedef struct mixart_sample_pos mixart_sample_pos_t;
struct mixart_sample_pos
{
u32 buffer_id;
u32 validity;
u32 sample_pos_high_part;
u32 sample_pos_low_part;
} __attribute__((packed));
typedef struct mixart_timer_notify mixart_timer_notify_t;
struct mixart_timer_notify
{
u32 stream_count;
mixart_sample_pos_t streams[MIXART_MAX_STREAM_PER_CARD * MIXART_MAX_CARDS];
} __attribute__((packed));
/* MSG_CONSOLE_GET_CLOCK_UID = 0x070003,
*/
/* request is a uid with desc = MSG_CONSOLE_MANAGER | cardindex */
typedef struct mixart_return_uid mixart_return_uid_t;
struct mixart_return_uid
{
u32 error_code;
mixart_uid_t uid;
} __attribute__((packed));
/* MSG_CLOCK_CHECK_PROPERTIES = 0x200001,
MSG_CLOCK_SET_PROPERTIES = 0x200002,
*/
enum mixart_clock_generic_type {
CGT_NO_CLOCK,
CGT_INTERNAL_CLOCK,
CGT_PROGRAMMABLE_CLOCK,
CGT_INTERNAL_ENSLAVED_CLOCK,
CGT_EXTERNAL_CLOCK,
CGT_CURRENT_CLOCK
};
enum mixart_clock_mode {
CM_UNDEFINED,
CM_MASTER,
CM_SLAVE,
CM_STANDALONE,
CM_NOT_CONCERNED
};
typedef struct mixart_clock_properties mixart_clock_properties_t;
struct mixart_clock_properties
{
u32 error_code;
u32 validation_mask;
u32 frequency;
u32 reference_frequency;
u32 clock_generic_type;
u32 clock_mode;
mixart_uid_t uid_clock_source;
mixart_uid_t uid_event_source;
u32 event_mode;
u32 synchro_signal_presence;
u32 format;
u32 board_mask;
u32 nb_callers; /* set to 1 (see below) */
mixart_uid_t uid_caller[1];
} __attribute__((packed));
typedef struct mixart_clock_properties_resp mixart_clock_properties_resp_t;
struct mixart_clock_properties_resp
{
u32 status;
u32 clock_mode;
} __attribute__((packed));
/* MSG_STREAM_SET_INPUT_STAGE_PARAM = 0x13000F */
/* MSG_STREAM_SET_OUTPUT_STAGE_PARAM = 0x130010 */
enum mixart_coding_type {
CT_NOT_DEFINED,
CT_LINEAR,
CT_MPEG_L1,
CT_MPEG_L2,
CT_MPEG_L3,
CT_MPEG_L3_LSF,
CT_GSM
};
enum mixart_sample_type {
ST_NOT_DEFINED,
ST_FLOATING_POINT_32BE,
ST_FLOATING_POINT_32LE,
ST_FLOATING_POINT_64BE,
ST_FLOATING_POINT_64LE,
ST_FIXED_POINT_8,
ST_FIXED_POINT_16BE,
ST_FIXED_POINT_16LE,
ST_FIXED_POINT_24BE,
ST_FIXED_POINT_24LE,
ST_FIXED_POINT_32BE,
ST_FIXED_POINT_32LE,
ST_INTEGER_8,
ST_INTEGER_16BE,
ST_INTEGER_16LE,
ST_INTEGER_24BE,
ST_INTEGER_24LE,
ST_INTEGER_32BE,
ST_INTEGER_32LE
};
typedef struct mixart_stream_param_desc mixart_stream_param_desc_t;
struct mixart_stream_param_desc
{
u32 coding_type; /* use enum mixart_coding_type */
u32 sample_type; /* use enum mixart_sample_type */
union {
struct {
u32 linear_endian_ness;
u32 linear_bits;
u32 is_signed;
u32 is_float;
} linear_format_info;
struct {
u32 mpeg_layer;
u32 mpeg_mode;
u32 mpeg_mode_extension;
u32 mpeg_pre_emphasis;
u32 mpeg_has_padding_bit;
u32 mpeg_has_crc;
u32 mpeg_has_extension;
u32 mpeg_is_original;
u32 mpeg_has_copyright;
} mpeg_format_info;
} format_info;
u32 delayed;
u64 scheduler;
u32 sample_size;
u32 has_header;
u32 has_suffix;
u32 has_bitrate;
u32 samples_per_frame;
u32 bytes_per_frame;
u32 bytes_per_sample;
u32 sampling_freq;
u32 number_of_channel;
u32 stream_number;
u32 buffer_size;
u32 differed_time;
u32 reserved4np[3];
u32 pipe_count; /* set to 1 (array size !) */
u32 stream_count; /* set to 1 (array size !) */
mixart_txx_stream_desc_t stream_desc[1]; /* only one stream per command, but this could be an array */
} __attribute__((packed));
/* MSG_CONNECTOR_GET_OUT_AUDIO_LEVEL = 0x050009,
*/
typedef struct mixart_get_out_audio_level mixart_get_out_audio_level_t;
struct mixart_get_out_audio_level
{
u32 txx_status;
u32 digital_level; /* float */
u32 analog_level; /* float */
u32 monitor_level; /* float */
u32 mute;
u32 monitor_mute1;
u32 monitor_mute2;
} __attribute__((packed));
/* MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL = 0x05000A,
*/
/* used for valid_mask below */
#define MIXART_AUDIO_LEVEL_ANALOG_MASK 0x01
#define MIXART_AUDIO_LEVEL_DIGITAL_MASK 0x02
#define MIXART_AUDIO_LEVEL_MONITOR_MASK 0x04
#define MIXART_AUDIO_LEVEL_MUTE_MASK 0x08
#define MIXART_AUDIO_LEVEL_MUTE_M1_MASK 0x10
#define MIXART_AUDIO_LEVEL_MUTE_M2_MASK 0x20
typedef struct mixart_set_out_audio_level mixart_set_out_audio_level_t;
struct mixart_set_out_audio_level
{
u32 delayed;
u64 scheduler;
u32 valid_mask1;
u32 valid_mask2;
u32 digital_level; /* float */
u32 analog_level; /* float */
u32 monitor_level; /* float */
u32 mute;
u32 monitor_mute1;
u32 monitor_mute2;
u32 reserved4np;
} __attribute__((packed));
/* MSG_SYSTEM_ENUM_PHYSICAL_IO = 0x16000E,
*/
#define MIXART_MAX_PHYS_IO (MIXART_MAX_CARDS * 2 * 2) /* 4 * (analog+digital) * (playback+capture) */
typedef struct mixart_uid_enumeration mixart_uid_enumeration_t;
struct mixart_uid_enumeration
{
u32 error_code;
u32 first_uid_offset;
u32 nb_uid;
u32 current_uid_index;
mixart_uid_t uid[MIXART_MAX_PHYS_IO];
} __attribute__((packed));
/* MSG_PHYSICALIO_SET_LEVEL = 0x0F0008,
MSG_PHYSICALIO_GET_LEVEL = 0x0F000C,
*/
typedef struct mixart_io_channel_level mixart_io_channel_level_t;
struct mixart_io_channel_level
{
u32 analog_level; /* float */
u32 unused[2];
} __attribute__((packed));
typedef struct mixart_io_level mixart_io_level_t;
struct mixart_io_level
{
s32 channel; /* 0=left, 1=right, -1=both, -2=both same */
mixart_io_channel_level_t level[2];
} __attribute__((packed));
/* MSG_STREAM_SET_IN_AUDIO_LEVEL = 0x130015,
*/
typedef struct mixart_in_audio_level_info mixart_in_audio_level_info_t;
struct mixart_in_audio_level_info
{
mixart_uid_t connector;
u32 valid_mask1;
u32 valid_mask2;
u32 digital_level;
u32 analog_level;
} __attribute__((packed));
typedef struct mixart_set_in_audio_level_req mixart_set_in_audio_level_req_t;
struct mixart_set_in_audio_level_req
{
u32 delayed;
u64 scheduler;
u32 audio_count; /* set to <= 2 */
u32 reserved4np;
mixart_in_audio_level_info_t level[2];
} __attribute__((packed));
/* response is a 32 bit status */
/* MSG_STREAM_SET_OUT_STREAM_LEVEL = 0x130017,
*/
/* defines used for valid_mask1 */
#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 0x01
#define MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO2 0x02
#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO1 0x04
#define MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2 0x08
#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_1 0x10
#define MIXART_OUT_STREAM_SET_LEVEL_STREAM_2 0x20
#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_1 0x40
#define MIXART_OUT_STREAM_SET_LEVEL_MUTE_2 0x80
typedef struct mixart_out_stream_level_info mixart_out_stream_level_info_t;
struct mixart_out_stream_level_info
{
u32 valid_mask1;
u32 valid_mask2;
u32 left_to_out1_level;
u32 left_to_out2_level;
u32 right_to_out1_level;
u32 right_to_out2_level;
u32 digital_level1;
u32 digital_level2;
u32 mute1;
u32 mute2;
} __attribute__((packed));
typedef struct mixart_set_out_stream_level mixart_set_out_stream_level_t;
struct mixart_set_out_stream_level
{
mixart_txx_stream_desc_t desc;
mixart_out_stream_level_info_t out_level;
} __attribute__((packed));
typedef struct mixart_set_out_stream_level_req mixart_set_out_stream_level_req_t;
struct mixart_set_out_stream_level_req
{
u32 delayed;
u64 scheduler;
u32 reserved4np[2];
u32 nb_of_stream; /* set to 1 */
mixart_set_out_stream_level_t stream_level; /* could be an array */
} __attribute__((packed));
/* response to this request is a u32 status value */
/* exported */
void snd_mixart_init_mailbox(mixart_mgr_t *mgr);
void snd_mixart_exit_mailbox(mixart_mgr_t *mgr);
int snd_mixart_send_msg(mixart_mgr_t *mgr, mixart_msg_t *request, int max_resp_size, void *resp_data);
int snd_mixart_send_msg_wait_notif(mixart_mgr_t *mgr, mixart_msg_t *request, u32 notif_event);
int snd_mixart_send_msg_nonblock(mixart_mgr_t *mgr, mixart_msg_t *request);
irqreturn_t snd_mixart_interrupt(int irq, void *dev_id, struct pt_regs *regs);
void snd_mixart_msg_tasklet( unsigned long arg);
void snd_mixart_reset_board(mixart_mgr_t *mgr);
#endif /* __SOUND_MIXART_CORE_H */
/*
* Driver for Digigram miXart soundcards
*
* hwdep device manager
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/driver.h>
#include <sound/core.h>
#include "mixart.h"
#include "mixart_mixer.h"
#include "mixart_core.h"
#include "mixart_hwdep.h"
/* miXart hwdep interface id string */
#define SND_MIXART_HWDEP_ID "miXart Loader"
static int mixart_hwdep_open(snd_hwdep_t *hw, struct file *file)
{
return 0;
}
static int mixart_hwdep_release(snd_hwdep_t *hw, struct file *file)
{
return 0;
}
/**
* wait for a value on a peudo register, exit with a timeout
*
* @param mgr pointer to miXart manager structure
* @param offset unsigned pseudo_register base + offset of value
* @param value value
* @param timeout timeout in centisenconds
*/
static int mixart_wait_nice_for_register_value(mixart_mgr_t *mgr, u32 offset, int is_egal, u32 value, unsigned long timeout)
{
unsigned long end_time = jiffies + (timeout * HZ / 100);
u32 read;
do { /* we may take too long time in this loop.
* so give controls back to kernel if needed.
*/
cond_resched();
read = readl_be( MIXART_MEM( mgr, offset ));
if(is_egal) {
if(read == value) return 0;
}
else { /* wait for different value */
if(read != value) return 0;
}
} while ( time_after_eq(end_time, jiffies) );
return -EBUSY;
}
/*
structures needed to upload elf code packets
*/
typedef struct snd_mixart_elf32_ehdr snd_mixart_elf32_ehdr_t;
struct snd_mixart_elf32_ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u32 e_entry;
u32 e_phoff;
u32 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
};
typedef struct snd_mixart_elf32_phdr snd_mixart_elf32_phdr_t;
struct snd_mixart_elf32_phdr {
u32 p_type;
u32 p_offset;
u32 p_vaddr;
u32 p_paddr;
u32 p_filesz;
u32 p_memsz;
u32 p_flags;
u32 p_align;
};
static int mixart_load_elf(mixart_mgr_t *mgr, snd_hwdep_dsp_image_t *dsp )
{
char elf32_magic_number[4] = {0x7f,'E','L','F'};
snd_mixart_elf32_ehdr_t elf_header;
int i;
if ( copy_from_user(&elf_header, dsp->image , sizeof(snd_mixart_elf32_ehdr_t)) )
return -EFAULT;
for( i=0; i<4; i++ )
if ( elf32_magic_number[i] != elf_header.e_ident[i] )
return -EINVAL;
if( elf_header.e_phoff != 0 ) {
snd_mixart_elf32_phdr_t elf_programheader;
for( i=0; i < be16_to_cpu(elf_header.e_phnum); i++ ) {
u32 pos = be32_to_cpu(elf_header.e_phoff) + (u32)(i * be16_to_cpu(elf_header.e_phentsize));
if( copy_from_user( &elf_programheader, dsp->image + pos, sizeof(elf_programheader) ) )
return -EFAULT;
if(elf_programheader.p_type != 0) {
if( elf_programheader.p_filesz != 0 ) {
if(copy_from_user_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),
dsp->image + be32_to_cpu( elf_programheader.p_offset ),
be32_to_cpu( elf_programheader.p_filesz )))
return -EFAULT;
}
}
}
}
return 0;
}
static int mixart_hwdep_dsp_status(snd_hwdep_t *hw, snd_hwdep_dsp_status_t *info)
{
mixart_mgr_t *mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO);
strcpy(info->id, "miXart");
info->num_dsps = MIXART_HARDW_FILES_MAX_INDEX;
if (mgr->hwdep->dsp_loaded & (1 << MIXART_MOTHERBOARD_ELF_INDEX))
info->chip_ready = 1;
info->version = MIXART_DRIVER_VERSION;
return 0;
}
/*
* get basic information and init miXart
*/
/* audio IDs for request to the board */
#define MIXART_FIRST_ANA_AUDIO_ID 0
#define MIXART_FIRST_DIG_AUDIO_ID 8
static int mixart_enum_connectors(mixart_mgr_t *mgr)
{
u32 k;
int err;
mixart_msg_t request;
mixart_enum_connector_resp_t connector;
mixart_audio_info_req_t audio_info_req;
mixart_audio_info_resp_t audio_info;
audio_info_req.line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;
audio_info_req.micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;
audio_info_req.cd_max_level = MIXART_FLOAT____0_0_TO_HEX;
request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;
request.uid = (mixart_uid_t){0,0}; /* board num = 0 */
request.data = NULL;
request.size = 0;
err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector);
if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");
return -EINVAL;
}
for(k=0; k < connector.uid_count; k++) {
mixart_pipe_t* pipe;
if(k < MIXART_FIRST_DIG_AUDIO_ID) {
pipe = &mgr->chip[k/2]->pipe_out_ana;
} else {
pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;
}
if(k & 1) {
pipe->uid_right_connector = connector.uid[k]; /* odd */
} else {
pipe->uid_left_connector = connector.uid[k]; /* even */
}
/* snd_printk(KERN_DEBUG "playback connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */
/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
request.uid = connector.uid[k];
request.data = &audio_info_req;
request.size = sizeof(audio_info_req);
err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info);
if( err < 0 ) {
snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
return err;
}
/*snd_printk(KERN_DEBUG "play analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/
}
request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;
request.uid = (mixart_uid_t){0,0}; /* board num = 0 */
request.data = NULL;
request.size = 0;
err = snd_mixart_send_msg(mgr, &request, sizeof(connector), &connector);
if((err < 0) || (connector.error_code) || (connector.uid_count > MIXART_MAX_PHYS_CONNECTORS)) {
snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");
return -EINVAL;
}
for(k=0; k < connector.uid_count; k++) {
mixart_pipe_t* pipe;
if(k < MIXART_FIRST_DIG_AUDIO_ID) {
pipe = &mgr->chip[k/2]->pipe_in_ana;
} else {
pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;
}
if(k & 1) {
pipe->uid_right_connector = connector.uid[k]; /* odd */
} else {
pipe->uid_left_connector = connector.uid[k]; /* even */
}
/* snd_printk(KERN_DEBUG "capture connector[%d].object_id = %x\n", k, connector.uid[k].object_id); */
/* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */
request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;
request.uid = connector.uid[k];
request.data = &audio_info_req;
request.size = sizeof(audio_info_req);
err = snd_mixart_send_msg(mgr, &request, sizeof(audio_info), &audio_info);
if( err < 0 ) {
snd_printk(KERN_ERR "error MSG_CONNECTOR_GET_AUDIO_INFO\n");
return err;
}
/*snd_printk(KERN_DEBUG "rec analog_info.analog_level_present = %x\n", audio_info.info.analog_info.analog_level_present);*/
}
return 0;
}
static int mixart_enum_physio(mixart_mgr_t *mgr)
{
u32 k;
int err;
mixart_msg_t request;
mixart_uid_t get_console_mgr;
mixart_return_uid_t console_mgr;
mixart_uid_enumeration_t phys_io;
/* get the uid for the console manager */
get_console_mgr.object_id = 0;
get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */
request.message_id = MSG_CONSOLE_GET_CLOCK_UID;
request.uid = get_console_mgr;
request.data = &get_console_mgr;
request.size = sizeof(get_console_mgr);
err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);
if( (err < 0) || (console_mgr.error_code != 0) ) {
snd_printk(KERN_DEBUG "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n", console_mgr.error_code);
return -EINVAL;
}
/* used later for clock issues ! */
mgr->uid_console_manager = console_mgr.uid;
request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;
request.uid = (mixart_uid_t){0,0};
request.data = &console_mgr.uid;
request.size = sizeof(console_mgr.uid);
err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);
if( (err < 0) || ( phys_io.error_code != 0 ) ) {
snd_printk(KERN_ERR "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n", err, phys_io.error_code );
return -EINVAL;
}
snd_assert(phys_io.nb_uid >= (MIXART_MAX_CARDS * 2), return -EINVAL); /* min 2 phys io per card (analog in + analog out) */
for(k=0; k<mgr->num_cards; k++) {
mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];
mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k];
}
return 0;
}
static int mixart_first_init(mixart_mgr_t *mgr)
{
u32 k;
int err;
mixart_msg_t request;
if((err = mixart_enum_connectors(mgr)) < 0) return err;
if((err = mixart_enum_physio(mgr)) < 0) return err;
/* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */
/* though why not here */
request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;
request.uid = (mixart_uid_t){0,0};
request.data = NULL;
request.size = 0;
/* this command has no data. response is a 32 bit status */
err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);
if( (err < 0) || (k != 0) ) {
snd_printk(KERN_ERR "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");
return err == 0 ? -EINVAL : err;
}
return 0;
}
/* firmware base addresses (when hard coded) */
#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000
static int mixart_hwdep_dsp_load(snd_hwdep_t *hw, snd_hwdep_dsp_image_t *dsp)
{
mixart_mgr_t* mgr = snd_magic_cast(mixart_mgr_t, hw->private_data, return -ENXIO);
int err, card_index;
u32 status_xilinx, status_elf, status_daught;
u32 val;
/* read motherboard xilinx status */
status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
/* read elf status */
status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
/* read daughterboard xilinx status */
status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
/* motherboard xilinx status 5 will say that the board is performing a reset */
if( status_xilinx == 5 ) {
snd_printk( KERN_ERR "miXart is resetting !\n");
return -EAGAIN; /* try again later */
}
switch (dsp->index) {
case MIXART_MOTHERBOARD_XLX_INDEX:
/* xilinx already loaded ? */
if( status_xilinx == 4 ) {
snd_printk( KERN_DEBUG "xilinx is already loaded !\n");
return 0;
}
/* the status should be 0 == "idle" */
if( status_xilinx != 0 ) {
snd_printk( KERN_ERR "xilinx load error ! status = %d\n", status_xilinx);
return -EIO; /* modprob -r may help ? */
}
/* check xilinx validity */
snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL);
snd_assert(dsp->length % 4 == 0, return -EINVAL);
/* set xilinx status to copying */
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
/* setup xilinx base address */
writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));
/* setup code size for xilinx file */
writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));
/* copy xilinx code */
if (copy_from_user_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->image, dsp->length))
return -EFAULT;
/* set xilinx status to copy finished */
writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));
/* return, because no further processing needed */
return 0;
case MIXART_MOTHERBOARD_ELF_INDEX:
if( status_elf == 4 ) {
snd_printk( KERN_DEBUG "elf file already loaded !\n");
return 0;
}
/* the status should be 0 == "idle" */
if( status_elf != 0 ) {
snd_printk( KERN_ERR "elf load error ! status = %d\n", status_elf);
return -EIO; /* modprob -r may help ? */
}
/* wait for xilinx status == 4 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */
if (err < 0) {
snd_printk( KERN_ERR "xilinx was not loaded or could not be started\n");
return err;
}
/* init some data on the card */
writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */
writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */
/* set elf status to copying */
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
/* process the copying of the elf packets */
err = mixart_load_elf( mgr, dsp);
if (err < 0) return err;
/* set elf status to copy finished */
writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));
/* wait for elf status == 4 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */
if (err < 0) {
snd_printk( KERN_ERR "elf could not be started\n");
return err;
}
/* miXart waits at this point on the pointer to the flow table */
writel_be( (u32)mgr->flowinfo_physaddr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */
return 0; /* return, another xilinx file has to be loaded before */
case MIXART_AESEBUBOARD_XLX_INDEX:
default:
/* elf and xilinx should be loaded */
if( (status_elf != 4) || (status_xilinx != 4) ) {
printk( KERN_ERR "xilinx or elf not successfully loaded\n");
return -EIO; /* modprob -r may help ? */
}
/* wait for daughter detection != 0 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */
if (err < 0) {
snd_printk( KERN_ERR "error starting elf file\n");
return err;
}
/* the board type can now be retrieved */
mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));
if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)
break; /* no daughter board; the file does not have to be loaded, continue after the switch */
/* only if aesebu daughter board presence (elf code must run) */
if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )
return -EINVAL;
/* daughter should be idle */
if( status_daught != 0 ) {
printk( KERN_ERR "daughter load error ! status = %d\n", status_daught);
return -EIO; /* modprob -r may help ? */
}
/* check daughterboard xilinx validity */
snd_assert(((u32*)(dsp->image))[0]==0xFFFFFFFF, return -EINVAL);
snd_assert(dsp->length % 4 == 0, return -EINVAL);
/* inform mixart about the size of the file */
writel_be( dsp->length, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));
/* set daughterboard status to 1 */
writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
/* wait for status == 2 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */
if (err < 0) {
snd_printk( KERN_ERR "daughter board load error\n");
return err;
}
/* get the address where to write the file */
val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));
snd_assert(val != 0, return -EINVAL);
/* copy daughterboard xilinx code */
if (copy_from_user_toio( MIXART_MEM( mgr, val), dsp->image, dsp->length))
return -EFAULT;
/* set daughterboard status to 4 */
writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));
/* continue with init */
break;
} /* end of switch file index*/
/* wait for daughter status == 3 */
err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */
if (err < 0) {
snd_printk( KERN_ERR "daughter board could not be initialised\n");
return err;
}
/* init mailbox (communication with embedded) */
snd_mixart_init_mailbox(mgr);
/* first communication with embedded */
err = mixart_first_init(mgr);
if (err < 0) {
snd_printk( KERN_ERR "miXart could not be set up\n");
return err;
}
/* create devices and mixer in accordance with HW options*/
for (card_index = 0; card_index < mgr->num_cards; card_index++) {
mixart_t *chip = mgr->chip[card_index];
if ((err = snd_mixart_create_pcm(chip)) < 0)
return err;
if (card_index == 0) {
if ((err = snd_mixart_create_mixer(chip->mgr)) < 0)
return err;
}
if ((err = snd_card_register(chip->card)) < 0)
return err;
};
snd_printdd("miXart firmware downloaded and successfully set up\n");
return 0;
}
int snd_mixart_hwdep_new(mixart_mgr_t *mgr)
{
int err;
snd_hwdep_t *hw;
/* only create hwdep interface for first cardX (see "index" module parameter)*/
if ((err = snd_hwdep_new(mgr->chip[0]->card, SND_MIXART_HWDEP_ID, 0, &hw)) < 0)
return err;
hw->iface = SNDRV_HWDEP_IFACE_MIXART;
hw->private_data = mgr;
hw->ops.open = mixart_hwdep_open;
hw->ops.release = mixart_hwdep_release;
hw->ops.dsp_status = mixart_hwdep_dsp_status;
hw->ops.dsp_load = mixart_hwdep_dsp_load;
hw->exclusive = 1;
sprintf(hw->name, SND_MIXART_HWDEP_ID);
mgr->hwdep = hw;
mgr->hwdep->dsp_loaded = 0;
return 0;
}
/*
* Driver for Digigram miXart soundcards
*
* definitions and makros for basic card access
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SOUND_MIXART_HWDEP_H
#define __SOUND_MIXART_HWDEP_H
#include <sound/hwdep.h>
#define readl_be(x) be32_to_cpu(__raw_readl(x))
#define writel_be(data,addr) __raw_writel(cpu_to_be32(data),addr)
#define readl_le(x) le32_to_cpu(__raw_readl(x))
#define writel_le(data,addr) __raw_writel(cpu_to_le32(data),addr)
#define MIXART_MEM(mgr,x) ((mgr)->mem[0].virt + (x))
#define MIXART_REG(mgr,x) ((mgr)->mem[1].virt + (x))
/* Daughter board Type */
#define DAUGHTER_TYPE_MASK 0x0F
#define DAUGHTER_VER_MASK 0xF0
#define DAUGHTER_TYPEVER_MASK (DAUGHTER_TYPE_MASK|DAUGHTER_VER_MASK)
#define MIXART_DAUGHTER_TYPE_NONE 0x00
#define MIXART_DAUGHTER_TYPE_COBRANET 0x08
#define MIXART_DAUGHTER_TYPE_AES 0x0E
#define MIXART_BA0_SIZE (16 * 1024 * 1024) /* 16M */
#define MIXART_BA1_SIZE (4 * 1024) /* 4k */
/*
* -----------BAR 0 --------------------------------------------------------------------------------------------------------
*/
#define MIXART_PSEUDOREG 0x2000 /* base address for pseudoregister */
#define MIXART_PSEUDOREG_BOARDNUMBER MIXART_PSEUDOREG+0 /* board number */
/* perfmeter (available when elf loaded)*/
#define MIXART_PSEUDOREG_PERF_STREAM_LOAD_OFFSET MIXART_PSEUDOREG+0x70 /* streaming load */
#define MIXART_PSEUDOREG_PERF_SYSTEM_LOAD_OFFSET MIXART_PSEUDOREG+0x78 /* system load (reference)*/
#define MIXART_PSEUDOREG_PERF_MAILBX_LOAD_OFFSET MIXART_PSEUDOREG+0x7C /* mailbox load */
#define MIXART_PSEUDOREG_PERF_INTERR_LOAD_OFFSET MIXART_PSEUDOREG+0x74 /* interrupt handling load */
/* motherboard xilinx loader info */
#define MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x9C /* 0x00600000 */
#define MIXART_PSEUDOREG_MXLX_SIZE_OFFSET MIXART_PSEUDOREG+0xA0 /* xilinx size in bytes */
#define MIXART_PSEUDOREG_MXLX_STATUS_OFFSET MIXART_PSEUDOREG+0xA4 /* status = EMBEBBED_STAT_XXX */
/* elf loader info */
#define MIXART_PSEUDOREG_ELF_STATUS_OFFSET MIXART_PSEUDOREG+0xB0 /* status = EMBEBBED_STAT_XXX */
/*
* after the elf code is loaded, and the flowtable info was passed to it,
* the driver polls on this address, until it shows 1 (presence) or 2 (absence)
* once it is non-zero, the daughter board type may be read
*/
#define MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET MIXART_PSEUDOREG+0x990
/* Global info structure */
#define MIXART_PSEUDOREG_DBRD_TYPE_OFFSET MIXART_PSEUDOREG+0x994 /* Type and version of daughterboard */
/* daughterboard xilinx loader info */
#define MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET MIXART_PSEUDOREG+0x998 /* get the address here where to write the file */
#define MIXART_PSEUDOREG_DXLX_SIZE_OFFSET MIXART_PSEUDOREG+0x99C /* xilinx size in bytes */
#define MIXART_PSEUDOREG_DXLX_STATUS_OFFSET MIXART_PSEUDOREG+0x9A0 /* status = EMBEBBED_STAT_XXX */
/* */
#define MIXART_FLOWTABLE_PTR 0x3000 /* pointer to flow table */
/* mailbox addresses */
/* message DRV -> EMB */
#define MSG_INBOUND_POST_HEAD 0x010008 /* DRV posts MF + increment4 */
#define MSG_INBOUND_POST_TAIL 0x01000C /* EMB gets MF + increment4 */
/* message EMB -> DRV */
#define MSG_OUTBOUND_POST_TAIL 0x01001C /* DRV gets MF + increment4 */
#define MSG_OUTBOUND_POST_HEAD 0x010018 /* EMB posts MF + increment4 */
/* Get Free Frames */
#define MSG_INBOUND_FREE_TAIL 0x010004 /* DRV gets MFA + increment4 */
#define MSG_OUTBOUND_FREE_TAIL 0x010014 /* EMB gets MFA + increment4 */
/* Put Free Frames */
#define MSG_OUTBOUND_FREE_HEAD 0x010010 /* DRV puts MFA + increment4 */
#define MSG_INBOUND_FREE_HEAD 0x010000 /* EMB puts MFA + increment4 */
/* firmware addresses of the message fifos */
#define MSG_BOUND_STACK_SIZE 0x004000 /* size of each following stack */
/* posted messages */
#define MSG_OUTBOUND_POST_STACK 0x108000 /* stack of messages to the DRV */
#define MSG_INBOUND_POST_STACK 0x104000 /* stack of messages to the EMB */
/* available empty messages */
#define MSG_OUTBOUND_FREE_STACK 0x10C000 /* stack of free enveloped for EMB */
#define MSG_INBOUND_FREE_STACK 0x100000 /* stack of free enveloped for DRV */
/* defines for mailbox message frames */
#define MSG_FRAME_OFFSET 0x64
#define MSG_FRAME_SIZE 0x6400
#define MSG_FRAME_NUMBER 32
#define MSG_FROM_AGENT_ITMF_OFFSET (MSG_FRAME_OFFSET + (MSG_FRAME_SIZE * MSG_FRAME_NUMBER))
#define MSG_TO_AGENT_ITMF_OFFSET (MSG_FROM_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
#define MSG_HOST_RSC_PROTECTION (MSG_TO_AGENT_ITMF_OFFSET + MSG_FRAME_SIZE)
#define MSG_AGENT_RSC_PROTECTION (MSG_HOST_RSC_PROTECTION + 4)
/*
* -----------BAR 1 --------------------------------------------------------------------------------------------------------
*/
/* interrupt addresses and constants */
#define MIXART_PCI_OMIMR_OFFSET 0x34 /* outbound message interrupt mask register */
#define MIXART_PCI_OMISR_OFFSET 0x30 /* outbound message interrupt status register */
#define MIXART_PCI_ODBR_OFFSET 0x60 /* outbound doorbell register */
#define MIXART_BA1_BRUTAL_RESET_OFFSET 0x68 /* write 1 in LSBit to reset board */
#define MIXART_HOST_ALL_INTERRUPT_MASKED 0x02B /* 0000 0010 1011 */
#define MIXART_ALLOW_OUTBOUND_DOORBELL 0x023 /* 0000 0010 0011 */
#define MIXART_OIDI 0x008 /* 0000 0000 1000 */
/* exported */
int snd_mixart_hwdep_new(mixart_mgr_t *mgr);
#endif /* __SOUND_MIXART_HWDEP_H */
/*
* Driver for Digigram miXart soundcards
*
* mixer callbacks
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <sound/driver.h>
#include <linux/time.h>
#include <linux/init.h>
#include <sound/core.h>
#include "mixart.h"
#include "mixart_core.h"
#include "mixart_hwdep.h"
#include <sound/control.h>
#include "mixart_mixer.h"
#define chip_t mixart_t
static u32 mixart_analog_level[256] = {
0xc2c00000, /* [000] -96.0 dB */
0xc2bf0000, /* [001] -95.5 dB */
0xc2be0000, /* [002] -95.0 dB */
0xc2bd0000, /* [003] -94.5 dB */
0xc2bc0000, /* [004] -94.0 dB */
0xc2bb0000, /* [005] -93.5 dB */
0xc2ba0000, /* [006] -93.0 dB */
0xc2b90000, /* [007] -92.5 dB */
0xc2b80000, /* [008] -92.0 dB */
0xc2b70000, /* [009] -91.5 dB */
0xc2b60000, /* [010] -91.0 dB */
0xc2b50000, /* [011] -90.5 dB */
0xc2b40000, /* [012] -90.0 dB */
0xc2b30000, /* [013] -89.5 dB */
0xc2b20000, /* [014] -89.0 dB */
0xc2b10000, /* [015] -88.5 dB */
0xc2b00000, /* [016] -88.0 dB */
0xc2af0000, /* [017] -87.5 dB */
0xc2ae0000, /* [018] -87.0 dB */
0xc2ad0000, /* [019] -86.5 dB */
0xc2ac0000, /* [020] -86.0 dB */
0xc2ab0000, /* [021] -85.5 dB */
0xc2aa0000, /* [022] -85.0 dB */
0xc2a90000, /* [023] -84.5 dB */
0xc2a80000, /* [024] -84.0 dB */
0xc2a70000, /* [025] -83.5 dB */
0xc2a60000, /* [026] -83.0 dB */
0xc2a50000, /* [027] -82.5 dB */
0xc2a40000, /* [028] -82.0 dB */
0xc2a30000, /* [029] -81.5 dB */
0xc2a20000, /* [030] -81.0 dB */
0xc2a10000, /* [031] -80.5 dB */
0xc2a00000, /* [032] -80.0 dB */
0xc29f0000, /* [033] -79.5 dB */
0xc29e0000, /* [034] -79.0 dB */
0xc29d0000, /* [035] -78.5 dB */
0xc29c0000, /* [036] -78.0 dB */
0xc29b0000, /* [037] -77.5 dB */
0xc29a0000, /* [038] -77.0 dB */
0xc2990000, /* [039] -76.5 dB */
0xc2980000, /* [040] -76.0 dB */
0xc2970000, /* [041] -75.5 dB */
0xc2960000, /* [042] -75.0 dB */
0xc2950000, /* [043] -74.5 dB */
0xc2940000, /* [044] -74.0 dB */
0xc2930000, /* [045] -73.5 dB */
0xc2920000, /* [046] -73.0 dB */
0xc2910000, /* [047] -72.5 dB */
0xc2900000, /* [048] -72.0 dB */
0xc28f0000, /* [049] -71.5 dB */
0xc28e0000, /* [050] -71.0 dB */
0xc28d0000, /* [051] -70.5 dB */
0xc28c0000, /* [052] -70.0 dB */
0xc28b0000, /* [053] -69.5 dB */
0xc28a0000, /* [054] -69.0 dB */
0xc2890000, /* [055] -68.5 dB */
0xc2880000, /* [056] -68.0 dB */
0xc2870000, /* [057] -67.5 dB */
0xc2860000, /* [058] -67.0 dB */
0xc2850000, /* [059] -66.5 dB */
0xc2840000, /* [060] -66.0 dB */
0xc2830000, /* [061] -65.5 dB */
0xc2820000, /* [062] -65.0 dB */
0xc2810000, /* [063] -64.5 dB */
0xc2800000, /* [064] -64.0 dB */
0xc27e0000, /* [065] -63.5 dB */
0xc27c0000, /* [066] -63.0 dB */
0xc27a0000, /* [067] -62.5 dB */
0xc2780000, /* [068] -62.0 dB */
0xc2760000, /* [069] -61.5 dB */
0xc2740000, /* [070] -61.0 dB */
0xc2720000, /* [071] -60.5 dB */
0xc2700000, /* [072] -60.0 dB */
0xc26e0000, /* [073] -59.5 dB */
0xc26c0000, /* [074] -59.0 dB */
0xc26a0000, /* [075] -58.5 dB */
0xc2680000, /* [076] -58.0 dB */
0xc2660000, /* [077] -57.5 dB */
0xc2640000, /* [078] -57.0 dB */
0xc2620000, /* [079] -56.5 dB */
0xc2600000, /* [080] -56.0 dB */
0xc25e0000, /* [081] -55.5 dB */
0xc25c0000, /* [082] -55.0 dB */
0xc25a0000, /* [083] -54.5 dB */
0xc2580000, /* [084] -54.0 dB */
0xc2560000, /* [085] -53.5 dB */
0xc2540000, /* [086] -53.0 dB */
0xc2520000, /* [087] -52.5 dB */
0xc2500000, /* [088] -52.0 dB */
0xc24e0000, /* [089] -51.5 dB */
0xc24c0000, /* [090] -51.0 dB */
0xc24a0000, /* [091] -50.5 dB */
0xc2480000, /* [092] -50.0 dB */
0xc2460000, /* [093] -49.5 dB */
0xc2440000, /* [094] -49.0 dB */
0xc2420000, /* [095] -48.5 dB */
0xc2400000, /* [096] -48.0 dB */
0xc23e0000, /* [097] -47.5 dB */
0xc23c0000, /* [098] -47.0 dB */
0xc23a0000, /* [099] -46.5 dB */
0xc2380000, /* [100] -46.0 dB */
0xc2360000, /* [101] -45.5 dB */
0xc2340000, /* [102] -45.0 dB */
0xc2320000, /* [103] -44.5 dB */
0xc2300000, /* [104] -44.0 dB */
0xc22e0000, /* [105] -43.5 dB */
0xc22c0000, /* [106] -43.0 dB */
0xc22a0000, /* [107] -42.5 dB */
0xc2280000, /* [108] -42.0 dB */
0xc2260000, /* [109] -41.5 dB */
0xc2240000, /* [110] -41.0 dB */
0xc2220000, /* [111] -40.5 dB */
0xc2200000, /* [112] -40.0 dB */
0xc21e0000, /* [113] -39.5 dB */
0xc21c0000, /* [114] -39.0 dB */
0xc21a0000, /* [115] -38.5 dB */
0xc2180000, /* [116] -38.0 dB */
0xc2160000, /* [117] -37.5 dB */
0xc2140000, /* [118] -37.0 dB */
0xc2120000, /* [119] -36.5 dB */
0xc2100000, /* [120] -36.0 dB */
0xc20e0000, /* [121] -35.5 dB */
0xc20c0000, /* [122] -35.0 dB */
0xc20a0000, /* [123] -34.5 dB */
0xc2080000, /* [124] -34.0 dB */
0xc2060000, /* [125] -33.5 dB */
0xc2040000, /* [126] -33.0 dB */
0xc2020000, /* [127] -32.5 dB */
0xc2000000, /* [128] -32.0 dB */
0xc1fc0000, /* [129] -31.5 dB */
0xc1f80000, /* [130] -31.0 dB */
0xc1f40000, /* [131] -30.5 dB */
0xc1f00000, /* [132] -30.0 dB */
0xc1ec0000, /* [133] -29.5 dB */
0xc1e80000, /* [134] -29.0 dB */
0xc1e40000, /* [135] -28.5 dB */
0xc1e00000, /* [136] -28.0 dB */
0xc1dc0000, /* [137] -27.5 dB */
0xc1d80000, /* [138] -27.0 dB */
0xc1d40000, /* [139] -26.5 dB */
0xc1d00000, /* [140] -26.0 dB */
0xc1cc0000, /* [141] -25.5 dB */
0xc1c80000, /* [142] -25.0 dB */
0xc1c40000, /* [143] -24.5 dB */
0xc1c00000, /* [144] -24.0 dB */
0xc1bc0000, /* [145] -23.5 dB */
0xc1b80000, /* [146] -23.0 dB */
0xc1b40000, /* [147] -22.5 dB */
0xc1b00000, /* [148] -22.0 dB */
0xc1ac0000, /* [149] -21.5 dB */
0xc1a80000, /* [150] -21.0 dB */
0xc1a40000, /* [151] -20.5 dB */
0xc1a00000, /* [152] -20.0 dB */
0xc19c0000, /* [153] -19.5 dB */
0xc1980000, /* [154] -19.0 dB */
0xc1940000, /* [155] -18.5 dB */
0xc1900000, /* [156] -18.0 dB */
0xc18c0000, /* [157] -17.5 dB */
0xc1880000, /* [158] -17.0 dB */
0xc1840000, /* [159] -16.5 dB */
0xc1800000, /* [160] -16.0 dB */
0xc1780000, /* [161] -15.5 dB */
0xc1700000, /* [162] -15.0 dB */
0xc1680000, /* [163] -14.5 dB */
0xc1600000, /* [164] -14.0 dB */
0xc1580000, /* [165] -13.5 dB */
0xc1500000, /* [166] -13.0 dB */
0xc1480000, /* [167] -12.5 dB */
0xc1400000, /* [168] -12.0 dB */
0xc1380000, /* [169] -11.5 dB */
0xc1300000, /* [170] -11.0 dB */
0xc1280000, /* [171] -10.5 dB */
0xc1200000, /* [172] -10.0 dB */
0xc1180000, /* [173] -9.5 dB */
0xc1100000, /* [174] -9.0 dB */
0xc1080000, /* [175] -8.5 dB */
0xc1000000, /* [176] -8.0 dB */
0xc0f00000, /* [177] -7.5 dB */
0xc0e00000, /* [178] -7.0 dB */
0xc0d00000, /* [179] -6.5 dB */
0xc0c00000, /* [180] -6.0 dB */
0xc0b00000, /* [181] -5.5 dB */
0xc0a00000, /* [182] -5.0 dB */
0xc0900000, /* [183] -4.5 dB */
0xc0800000, /* [184] -4.0 dB */
0xc0600000, /* [185] -3.5 dB */
0xc0400000, /* [186] -3.0 dB */
0xc0200000, /* [187] -2.5 dB */
0xc0000000, /* [188] -2.0 dB */
0xbfc00000, /* [189] -1.5 dB */
0xbf800000, /* [190] -1.0 dB */
0xbf000000, /* [191] -0.5 dB */
0x00000000, /* [192] 0.0 dB */
0x3f000000, /* [193] 0.5 dB */
0x3f800000, /* [194] 1.0 dB */
0x3fc00000, /* [195] 1.5 dB */
0x40000000, /* [196] 2.0 dB */
0x40200000, /* [197] 2.5 dB */
0x40400000, /* [198] 3.0 dB */
0x40600000, /* [199] 3.5 dB */
0x40800000, /* [200] 4.0 dB */
0x40900000, /* [201] 4.5 dB */
0x40a00000, /* [202] 5.0 dB */
0x40b00000, /* [203] 5.5 dB */
0x40c00000, /* [204] 6.0 dB */
0x40d00000, /* [205] 6.5 dB */
0x40e00000, /* [206] 7.0 dB */
0x40f00000, /* [207] 7.5 dB */
0x41000000, /* [208] 8.0 dB */
0x41080000, /* [209] 8.5 dB */
0x41100000, /* [210] 9.0 dB */
0x41180000, /* [211] 9.5 dB */
0x41200000, /* [212] 10.0 dB */
0x41280000, /* [213] 10.5 dB */
0x41300000, /* [214] 11.0 dB */
0x41380000, /* [215] 11.5 dB */
0x41400000, /* [216] 12.0 dB */
0x41480000, /* [217] 12.5 dB */
0x41500000, /* [218] 13.0 dB */
0x41580000, /* [219] 13.5 dB */
0x41600000, /* [220] 14.0 dB */
0x41680000, /* [221] 14.5 dB */
0x41700000, /* [222] 15.0 dB */
0x41780000, /* [223] 15.5 dB */
0x41800000, /* [224] 16.0 dB */
0x41840000, /* [225] 16.5 dB */
0x41880000, /* [226] 17.0 dB */
0x418c0000, /* [227] 17.5 dB */
0x41900000, /* [228] 18.0 dB */
0x41940000, /* [229] 18.5 dB */
0x41980000, /* [230] 19.0 dB */
0x419c0000, /* [231] 19.5 dB */
0x41a00000, /* [232] 20.0 dB */
0x41a40000, /* [233] 20.5 dB */
0x41a80000, /* [234] 21.0 dB */
0x41ac0000, /* [235] 21.5 dB */
0x41b00000, /* [236] 22.0 dB */
0x41b40000, /* [237] 22.5 dB */
0x41b80000, /* [238] 23.0 dB */
0x41bc0000, /* [239] 23.5 dB */
0x41c00000, /* [240] 24.0 dB */
0x41c40000, /* [241] 24.5 dB */
0x41c80000, /* [242] 25.0 dB */
0x41cc0000, /* [243] 25.5 dB */
0x41d00000, /* [244] 26.0 dB */
0x41d40000, /* [245] 26.5 dB */
0x41d80000, /* [246] 27.0 dB */
0x41dc0000, /* [247] 27.5 dB */
0x41e00000, /* [248] 28.0 dB */
0x41e40000, /* [249] 28.5 dB */
0x41e80000, /* [250] 29.0 dB */
0x41ec0000, /* [251] 29.5 dB */
0x41f00000, /* [252] 30.0 dB */
0x41f40000, /* [253] 30.5 dB */
0x41f80000, /* [254] 31.0 dB */
0x41fc0000, /* [255] 31.5 dB */
};
#define MIXART_ANALOG_CAPTURE_LEVEL_MIN 0 /* -96.0 dB + 8.0 dB = -88.0 dB */
#define MIXART_ANALOG_CAPTURE_LEVEL_MAX 255 /* 31.5 dB + 8.0 dB = 39.5 dB */
#define MIXART_ANALOG_CAPTURE_ZERO_LEVEL 176 /* -8.0 dB + 8.0 dB = 0.0 dB */
#define MIXART_ANALOG_PLAYBACK_LEVEL_MIN 0 /* -96.0 dB + 1.5 dB = -94.5 dB (possible is down to (-114.0+1.5)dB) */
#define MIXART_ANALOG_PLAYBACK_LEVEL_MAX 192 /* 0.0 dB + 1.5 dB = 1.5 dB */
#define MIXART_ANALOG_PLAYBACK_ZERO_LEVEL 189 /* -1.5 dB + 1.5 dB = 0.0 dB */
static int mixart_update_analog_audio_level(mixart_t* chip, int is_capture)
{
int i, err;
mixart_msg_t request;
mixart_io_level_t io_level;
mixart_return_uid_t resp;
memset(&io_level, 0, sizeof(io_level));
io_level.channel = -1; /* left and right */
for(i=0; i<2; i++) {
if(is_capture) {
io_level.level[i].analog_level = mixart_analog_level[chip->analog_capture_volume[i]];
} else {
if(chip->analog_playback_active[i])
io_level.level[i].analog_level = mixart_analog_level[chip->analog_playback_volume[i]];
else
io_level.level[i].analog_level = mixart_analog_level[MIXART_ANALOG_PLAYBACK_LEVEL_MIN];
}
}
if(is_capture) request.uid = chip->uid_in_analog_physio;
else request.uid = chip->uid_out_analog_physio;
request.message_id = MSG_PHYSICALIO_SET_LEVEL;
request.data = &io_level;
request.size = sizeof(io_level);
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
if((err<0) || (resp.error_code)) {
snd_printk(KERN_DEBUG "error MSG_PHYSICALIO_SET_LEVEL card(%d) is_capture(%d) error_code(%x)\n", chip->chip_idx, is_capture, resp.error_code);
return -EINVAL;
}
return 0;
}
/*
* analog level control
*/
static int mixart_analog_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
if(kcontrol->private_value == 0) { /* playback */
uinfo->value.integer.min = MIXART_ANALOG_PLAYBACK_LEVEL_MIN; /* -96 dB */
uinfo->value.integer.max = MIXART_ANALOG_PLAYBACK_LEVEL_MAX; /* 0 dB */
} else { /* capture */
uinfo->value.integer.min = MIXART_ANALOG_CAPTURE_LEVEL_MIN; /* -96 dB */
uinfo->value.integer.max = MIXART_ANALOG_CAPTURE_LEVEL_MAX; /* 31.5 dB */
}
return 0;
}
static int mixart_analog_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
down(&chip->mgr->mixer_mutex);
if(kcontrol->private_value == 0) { /* playback */
ucontrol->value.integer.value[0] = chip->analog_playback_volume[0];
ucontrol->value.integer.value[1] = chip->analog_playback_volume[1];
} else { /* capture */
ucontrol->value.integer.value[0] = chip->analog_capture_volume[0];
ucontrol->value.integer.value[1] = chip->analog_capture_volume[1];
}
up(&chip->mgr->mixer_mutex);
return 0;
}
static int mixart_analog_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int is_capture, i;
down(&chip->mgr->mixer_mutex);
is_capture = (kcontrol->private_value != 0);
for(i=0; i<2; i++) {
int new_volume = ucontrol->value.integer.value[i];
int* stored_volume = is_capture ? &chip->analog_capture_volume[i] : &chip->analog_playback_volume[i];
if(*stored_volume != new_volume) {
*stored_volume = new_volume;
changed = 1;
}
}
if(changed) mixart_update_analog_audio_level(chip, is_capture);
up(&chip->mgr->mixer_mutex);
return changed;
}
static snd_kcontrol_new_t mixart_control_analog_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* name will be filled later */
.info = mixart_analog_vol_info,
.get = mixart_analog_vol_get,
.put = mixart_analog_vol_put,
};
/* shared */
static int mixart_sw_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
uinfo->count = 2;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = 1;
return 0;
}
static int mixart_audio_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
down(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->analog_playback_active[0];
ucontrol->value.integer.value[1] = chip->analog_playback_active[1];
up(&chip->mgr->mixer_mutex);
return 0;
}
static int mixart_audio_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int i, changed = 0;
down(&chip->mgr->mixer_mutex);
for(i=0; i<2; i++) {
if(chip->analog_playback_active[i] != ucontrol->value.integer.value[i]) {
chip->analog_playback_active[i] = ucontrol->value.integer.value[i];
changed = 1;
}
}
if(changed) mixart_update_analog_audio_level(chip, 0); /* update playback levels */
up(&chip->mgr->mixer_mutex);
return changed;
}
static snd_kcontrol_new_t mixart_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = mixart_sw_info, /* shared */
.get = mixart_audio_sw_get,
.put = mixart_audio_sw_put
};
static u32 mixart_digital_level[256] = {
0x00000000, /* [000] = 0.00e+000 = mute if <= -109.5dB */
0x366e1c7a, /* [001] = 3.55e-006 = pow(10.0, 0.05 * -109.0dB) */
0x367c3860, /* [002] = 3.76e-006 = pow(10.0, 0.05 * -108.5dB) */
0x36859525, /* [003] = 3.98e-006 = pow(10.0, 0.05 * -108.0dB) */
0x368d7f74, /* [004] = 4.22e-006 = pow(10.0, 0.05 * -107.5dB) */
0x3695e1d4, /* [005] = 4.47e-006 = pow(10.0, 0.05 * -107.0dB) */
0x369ec362, /* [006] = 4.73e-006 = pow(10.0, 0.05 * -106.5dB) */
0x36a82ba8, /* [007] = 5.01e-006 = pow(10.0, 0.05 * -106.0dB) */
0x36b222a0, /* [008] = 5.31e-006 = pow(10.0, 0.05 * -105.5dB) */
0x36bcb0c1, /* [009] = 5.62e-006 = pow(10.0, 0.05 * -105.0dB) */
0x36c7defd, /* [010] = 5.96e-006 = pow(10.0, 0.05 * -104.5dB) */
0x36d3b6d3, /* [011] = 6.31e-006 = pow(10.0, 0.05 * -104.0dB) */
0x36e0424e, /* [012] = 6.68e-006 = pow(10.0, 0.05 * -103.5dB) */
0x36ed8c14, /* [013] = 7.08e-006 = pow(10.0, 0.05 * -103.0dB) */
0x36fb9f6c, /* [014] = 7.50e-006 = pow(10.0, 0.05 * -102.5dB) */
0x37054423, /* [015] = 7.94e-006 = pow(10.0, 0.05 * -102.0dB) */
0x370d29a5, /* [016] = 8.41e-006 = pow(10.0, 0.05 * -101.5dB) */
0x371586f0, /* [017] = 8.91e-006 = pow(10.0, 0.05 * -101.0dB) */
0x371e631b, /* [018] = 9.44e-006 = pow(10.0, 0.05 * -100.5dB) */
0x3727c5ac, /* [019] = 1.00e-005 = pow(10.0, 0.05 * -100.0dB) */
0x3731b69a, /* [020] = 1.06e-005 = pow(10.0, 0.05 * -99.5dB) */
0x373c3e53, /* [021] = 1.12e-005 = pow(10.0, 0.05 * -99.0dB) */
0x374765c8, /* [022] = 1.19e-005 = pow(10.0, 0.05 * -98.5dB) */
0x3753366f, /* [023] = 1.26e-005 = pow(10.0, 0.05 * -98.0dB) */
0x375fba4f, /* [024] = 1.33e-005 = pow(10.0, 0.05 * -97.5dB) */
0x376cfc07, /* [025] = 1.41e-005 = pow(10.0, 0.05 * -97.0dB) */
0x377b06d5, /* [026] = 1.50e-005 = pow(10.0, 0.05 * -96.5dB) */
0x3784f352, /* [027] = 1.58e-005 = pow(10.0, 0.05 * -96.0dB) */
0x378cd40b, /* [028] = 1.68e-005 = pow(10.0, 0.05 * -95.5dB) */
0x37952c42, /* [029] = 1.78e-005 = pow(10.0, 0.05 * -95.0dB) */
0x379e030e, /* [030] = 1.88e-005 = pow(10.0, 0.05 * -94.5dB) */
0x37a75fef, /* [031] = 2.00e-005 = pow(10.0, 0.05 * -94.0dB) */
0x37b14ad5, /* [032] = 2.11e-005 = pow(10.0, 0.05 * -93.5dB) */
0x37bbcc2c, /* [033] = 2.24e-005 = pow(10.0, 0.05 * -93.0dB) */
0x37c6ecdd, /* [034] = 2.37e-005 = pow(10.0, 0.05 * -92.5dB) */
0x37d2b65a, /* [035] = 2.51e-005 = pow(10.0, 0.05 * -92.0dB) */
0x37df32a3, /* [036] = 2.66e-005 = pow(10.0, 0.05 * -91.5dB) */
0x37ec6c50, /* [037] = 2.82e-005 = pow(10.0, 0.05 * -91.0dB) */
0x37fa6e9b, /* [038] = 2.99e-005 = pow(10.0, 0.05 * -90.5dB) */
0x3804a2b3, /* [039] = 3.16e-005 = pow(10.0, 0.05 * -90.0dB) */
0x380c7ea4, /* [040] = 3.35e-005 = pow(10.0, 0.05 * -89.5dB) */
0x3814d1cc, /* [041] = 3.55e-005 = pow(10.0, 0.05 * -89.0dB) */
0x381da33c, /* [042] = 3.76e-005 = pow(10.0, 0.05 * -88.5dB) */
0x3826fa6f, /* [043] = 3.98e-005 = pow(10.0, 0.05 * -88.0dB) */
0x3830df51, /* [044] = 4.22e-005 = pow(10.0, 0.05 * -87.5dB) */
0x383b5a49, /* [045] = 4.47e-005 = pow(10.0, 0.05 * -87.0dB) */
0x3846743b, /* [046] = 4.73e-005 = pow(10.0, 0.05 * -86.5dB) */
0x38523692, /* [047] = 5.01e-005 = pow(10.0, 0.05 * -86.0dB) */
0x385eab48, /* [048] = 5.31e-005 = pow(10.0, 0.05 * -85.5dB) */
0x386bdcf1, /* [049] = 5.62e-005 = pow(10.0, 0.05 * -85.0dB) */
0x3879d6bc, /* [050] = 5.96e-005 = pow(10.0, 0.05 * -84.5dB) */
0x38845244, /* [051] = 6.31e-005 = pow(10.0, 0.05 * -84.0dB) */
0x388c2971, /* [052] = 6.68e-005 = pow(10.0, 0.05 * -83.5dB) */
0x3894778d, /* [053] = 7.08e-005 = pow(10.0, 0.05 * -83.0dB) */
0x389d43a4, /* [054] = 7.50e-005 = pow(10.0, 0.05 * -82.5dB) */
0x38a6952c, /* [055] = 7.94e-005 = pow(10.0, 0.05 * -82.0dB) */
0x38b0740f, /* [056] = 8.41e-005 = pow(10.0, 0.05 * -81.5dB) */
0x38bae8ac, /* [057] = 8.91e-005 = pow(10.0, 0.05 * -81.0dB) */
0x38c5fbe2, /* [058] = 9.44e-005 = pow(10.0, 0.05 * -80.5dB) */
0x38d1b717, /* [059] = 1.00e-004 = pow(10.0, 0.05 * -80.0dB) */
0x38de2440, /* [060] = 1.06e-004 = pow(10.0, 0.05 * -79.5dB) */
0x38eb4de8, /* [061] = 1.12e-004 = pow(10.0, 0.05 * -79.0dB) */
0x38f93f3a, /* [062] = 1.19e-004 = pow(10.0, 0.05 * -78.5dB) */
0x39040206, /* [063] = 1.26e-004 = pow(10.0, 0.05 * -78.0dB) */
0x390bd472, /* [064] = 1.33e-004 = pow(10.0, 0.05 * -77.5dB) */
0x39141d84, /* [065] = 1.41e-004 = pow(10.0, 0.05 * -77.0dB) */
0x391ce445, /* [066] = 1.50e-004 = pow(10.0, 0.05 * -76.5dB) */
0x39263027, /* [067] = 1.58e-004 = pow(10.0, 0.05 * -76.0dB) */
0x3930090d, /* [068] = 1.68e-004 = pow(10.0, 0.05 * -75.5dB) */
0x393a7753, /* [069] = 1.78e-004 = pow(10.0, 0.05 * -75.0dB) */
0x394583d2, /* [070] = 1.88e-004 = pow(10.0, 0.05 * -74.5dB) */
0x395137ea, /* [071] = 2.00e-004 = pow(10.0, 0.05 * -74.0dB) */
0x395d9d8a, /* [072] = 2.11e-004 = pow(10.0, 0.05 * -73.5dB) */
0x396abf37, /* [073] = 2.24e-004 = pow(10.0, 0.05 * -73.0dB) */
0x3978a814, /* [074] = 2.37e-004 = pow(10.0, 0.05 * -72.5dB) */
0x3983b1f8, /* [075] = 2.51e-004 = pow(10.0, 0.05 * -72.0dB) */
0x398b7fa6, /* [076] = 2.66e-004 = pow(10.0, 0.05 * -71.5dB) */
0x3993c3b2, /* [077] = 2.82e-004 = pow(10.0, 0.05 * -71.0dB) */
0x399c8521, /* [078] = 2.99e-004 = pow(10.0, 0.05 * -70.5dB) */
0x39a5cb5f, /* [079] = 3.16e-004 = pow(10.0, 0.05 * -70.0dB) */
0x39af9e4d, /* [080] = 3.35e-004 = pow(10.0, 0.05 * -69.5dB) */
0x39ba063f, /* [081] = 3.55e-004 = pow(10.0, 0.05 * -69.0dB) */
0x39c50c0b, /* [082] = 3.76e-004 = pow(10.0, 0.05 * -68.5dB) */
0x39d0b90a, /* [083] = 3.98e-004 = pow(10.0, 0.05 * -68.0dB) */
0x39dd1726, /* [084] = 4.22e-004 = pow(10.0, 0.05 * -67.5dB) */
0x39ea30db, /* [085] = 4.47e-004 = pow(10.0, 0.05 * -67.0dB) */
0x39f81149, /* [086] = 4.73e-004 = pow(10.0, 0.05 * -66.5dB) */
0x3a03621b, /* [087] = 5.01e-004 = pow(10.0, 0.05 * -66.0dB) */
0x3a0b2b0d, /* [088] = 5.31e-004 = pow(10.0, 0.05 * -65.5dB) */
0x3a136a16, /* [089] = 5.62e-004 = pow(10.0, 0.05 * -65.0dB) */
0x3a1c2636, /* [090] = 5.96e-004 = pow(10.0, 0.05 * -64.5dB) */
0x3a2566d5, /* [091] = 6.31e-004 = pow(10.0, 0.05 * -64.0dB) */
0x3a2f33cd, /* [092] = 6.68e-004 = pow(10.0, 0.05 * -63.5dB) */
0x3a399570, /* [093] = 7.08e-004 = pow(10.0, 0.05 * -63.0dB) */
0x3a44948c, /* [094] = 7.50e-004 = pow(10.0, 0.05 * -62.5dB) */
0x3a503a77, /* [095] = 7.94e-004 = pow(10.0, 0.05 * -62.0dB) */
0x3a5c9112, /* [096] = 8.41e-004 = pow(10.0, 0.05 * -61.5dB) */
0x3a69a2d7, /* [097] = 8.91e-004 = pow(10.0, 0.05 * -61.0dB) */
0x3a777ada, /* [098] = 9.44e-004 = pow(10.0, 0.05 * -60.5dB) */
0x3a83126f, /* [099] = 1.00e-003 = pow(10.0, 0.05 * -60.0dB) */
0x3a8ad6a8, /* [100] = 1.06e-003 = pow(10.0, 0.05 * -59.5dB) */
0x3a9310b1, /* [101] = 1.12e-003 = pow(10.0, 0.05 * -59.0dB) */
0x3a9bc784, /* [102] = 1.19e-003 = pow(10.0, 0.05 * -58.5dB) */
0x3aa50287, /* [103] = 1.26e-003 = pow(10.0, 0.05 * -58.0dB) */
0x3aaec98e, /* [104] = 1.33e-003 = pow(10.0, 0.05 * -57.5dB) */
0x3ab924e5, /* [105] = 1.41e-003 = pow(10.0, 0.05 * -57.0dB) */
0x3ac41d56, /* [106] = 1.50e-003 = pow(10.0, 0.05 * -56.5dB) */
0x3acfbc31, /* [107] = 1.58e-003 = pow(10.0, 0.05 * -56.0dB) */
0x3adc0b51, /* [108] = 1.68e-003 = pow(10.0, 0.05 * -55.5dB) */
0x3ae91528, /* [109] = 1.78e-003 = pow(10.0, 0.05 * -55.0dB) */
0x3af6e4c6, /* [110] = 1.88e-003 = pow(10.0, 0.05 * -54.5dB) */
0x3b02c2f2, /* [111] = 2.00e-003 = pow(10.0, 0.05 * -54.0dB) */
0x3b0a8276, /* [112] = 2.11e-003 = pow(10.0, 0.05 * -53.5dB) */
0x3b12b782, /* [113] = 2.24e-003 = pow(10.0, 0.05 * -53.0dB) */
0x3b1b690d, /* [114] = 2.37e-003 = pow(10.0, 0.05 * -52.5dB) */
0x3b249e76, /* [115] = 2.51e-003 = pow(10.0, 0.05 * -52.0dB) */
0x3b2e5f8f, /* [116] = 2.66e-003 = pow(10.0, 0.05 * -51.5dB) */
0x3b38b49f, /* [117] = 2.82e-003 = pow(10.0, 0.05 * -51.0dB) */
0x3b43a669, /* [118] = 2.99e-003 = pow(10.0, 0.05 * -50.5dB) */
0x3b4f3e37, /* [119] = 3.16e-003 = pow(10.0, 0.05 * -50.0dB) */
0x3b5b85e0, /* [120] = 3.35e-003 = pow(10.0, 0.05 * -49.5dB) */
0x3b6887cf, /* [121] = 3.55e-003 = pow(10.0, 0.05 * -49.0dB) */
0x3b764f0e, /* [122] = 3.76e-003 = pow(10.0, 0.05 * -48.5dB) */
0x3b8273a6, /* [123] = 3.98e-003 = pow(10.0, 0.05 * -48.0dB) */
0x3b8a2e77, /* [124] = 4.22e-003 = pow(10.0, 0.05 * -47.5dB) */
0x3b925e89, /* [125] = 4.47e-003 = pow(10.0, 0.05 * -47.0dB) */
0x3b9b0ace, /* [126] = 4.73e-003 = pow(10.0, 0.05 * -46.5dB) */
0x3ba43aa2, /* [127] = 5.01e-003 = pow(10.0, 0.05 * -46.0dB) */
0x3badf5d1, /* [128] = 5.31e-003 = pow(10.0, 0.05 * -45.5dB) */
0x3bb8449c, /* [129] = 5.62e-003 = pow(10.0, 0.05 * -45.0dB) */
0x3bc32fc3, /* [130] = 5.96e-003 = pow(10.0, 0.05 * -44.5dB) */
0x3bcec08a, /* [131] = 6.31e-003 = pow(10.0, 0.05 * -44.0dB) */
0x3bdb00c0, /* [132] = 6.68e-003 = pow(10.0, 0.05 * -43.5dB) */
0x3be7facc, /* [133] = 7.08e-003 = pow(10.0, 0.05 * -43.0dB) */
0x3bf5b9b0, /* [134] = 7.50e-003 = pow(10.0, 0.05 * -42.5dB) */
0x3c02248a, /* [135] = 7.94e-003 = pow(10.0, 0.05 * -42.0dB) */
0x3c09daac, /* [136] = 8.41e-003 = pow(10.0, 0.05 * -41.5dB) */
0x3c1205c6, /* [137] = 8.91e-003 = pow(10.0, 0.05 * -41.0dB) */
0x3c1aacc8, /* [138] = 9.44e-003 = pow(10.0, 0.05 * -40.5dB) */
0x3c23d70a, /* [139] = 1.00e-002 = pow(10.0, 0.05 * -40.0dB) */
0x3c2d8c52, /* [140] = 1.06e-002 = pow(10.0, 0.05 * -39.5dB) */
0x3c37d4dd, /* [141] = 1.12e-002 = pow(10.0, 0.05 * -39.0dB) */
0x3c42b965, /* [142] = 1.19e-002 = pow(10.0, 0.05 * -38.5dB) */
0x3c4e4329, /* [143] = 1.26e-002 = pow(10.0, 0.05 * -38.0dB) */
0x3c5a7bf1, /* [144] = 1.33e-002 = pow(10.0, 0.05 * -37.5dB) */
0x3c676e1e, /* [145] = 1.41e-002 = pow(10.0, 0.05 * -37.0dB) */
0x3c7524ac, /* [146] = 1.50e-002 = pow(10.0, 0.05 * -36.5dB) */
0x3c81d59f, /* [147] = 1.58e-002 = pow(10.0, 0.05 * -36.0dB) */
0x3c898712, /* [148] = 1.68e-002 = pow(10.0, 0.05 * -35.5dB) */
0x3c91ad39, /* [149] = 1.78e-002 = pow(10.0, 0.05 * -35.0dB) */
0x3c9a4efc, /* [150] = 1.88e-002 = pow(10.0, 0.05 * -34.5dB) */
0x3ca373af, /* [151] = 2.00e-002 = pow(10.0, 0.05 * -34.0dB) */
0x3cad2314, /* [152] = 2.11e-002 = pow(10.0, 0.05 * -33.5dB) */
0x3cb76563, /* [153] = 2.24e-002 = pow(10.0, 0.05 * -33.0dB) */
0x3cc24350, /* [154] = 2.37e-002 = pow(10.0, 0.05 * -32.5dB) */
0x3ccdc614, /* [155] = 2.51e-002 = pow(10.0, 0.05 * -32.0dB) */
0x3cd9f773, /* [156] = 2.66e-002 = pow(10.0, 0.05 * -31.5dB) */
0x3ce6e1c6, /* [157] = 2.82e-002 = pow(10.0, 0.05 * -31.0dB) */
0x3cf49003, /* [158] = 2.99e-002 = pow(10.0, 0.05 * -30.5dB) */
0x3d0186e2, /* [159] = 3.16e-002 = pow(10.0, 0.05 * -30.0dB) */
0x3d0933ac, /* [160] = 3.35e-002 = pow(10.0, 0.05 * -29.5dB) */
0x3d1154e1, /* [161] = 3.55e-002 = pow(10.0, 0.05 * -29.0dB) */
0x3d19f169, /* [162] = 3.76e-002 = pow(10.0, 0.05 * -28.5dB) */
0x3d231090, /* [163] = 3.98e-002 = pow(10.0, 0.05 * -28.0dB) */
0x3d2cba15, /* [164] = 4.22e-002 = pow(10.0, 0.05 * -27.5dB) */
0x3d36f62b, /* [165] = 4.47e-002 = pow(10.0, 0.05 * -27.0dB) */
0x3d41cd81, /* [166] = 4.73e-002 = pow(10.0, 0.05 * -26.5dB) */
0x3d4d494a, /* [167] = 5.01e-002 = pow(10.0, 0.05 * -26.0dB) */
0x3d597345, /* [168] = 5.31e-002 = pow(10.0, 0.05 * -25.5dB) */
0x3d6655c3, /* [169] = 5.62e-002 = pow(10.0, 0.05 * -25.0dB) */
0x3d73fbb4, /* [170] = 5.96e-002 = pow(10.0, 0.05 * -24.5dB) */
0x3d813856, /* [171] = 6.31e-002 = pow(10.0, 0.05 * -24.0dB) */
0x3d88e078, /* [172] = 6.68e-002 = pow(10.0, 0.05 * -23.5dB) */
0x3d90fcbf, /* [173] = 7.08e-002 = pow(10.0, 0.05 * -23.0dB) */
0x3d99940e, /* [174] = 7.50e-002 = pow(10.0, 0.05 * -22.5dB) */
0x3da2adad, /* [175] = 7.94e-002 = pow(10.0, 0.05 * -22.0dB) */
0x3dac5156, /* [176] = 8.41e-002 = pow(10.0, 0.05 * -21.5dB) */
0x3db68738, /* [177] = 8.91e-002 = pow(10.0, 0.05 * -21.0dB) */
0x3dc157fb, /* [178] = 9.44e-002 = pow(10.0, 0.05 * -20.5dB) */
0x3dcccccd, /* [179] = 1.00e-001 = pow(10.0, 0.05 * -20.0dB) */
0x3dd8ef67, /* [180] = 1.06e-001 = pow(10.0, 0.05 * -19.5dB) */
0x3de5ca15, /* [181] = 1.12e-001 = pow(10.0, 0.05 * -19.0dB) */
0x3df367bf, /* [182] = 1.19e-001 = pow(10.0, 0.05 * -18.5dB) */
0x3e00e9f9, /* [183] = 1.26e-001 = pow(10.0, 0.05 * -18.0dB) */
0x3e088d77, /* [184] = 1.33e-001 = pow(10.0, 0.05 * -17.5dB) */
0x3e10a4d3, /* [185] = 1.41e-001 = pow(10.0, 0.05 * -17.0dB) */
0x3e1936ec, /* [186] = 1.50e-001 = pow(10.0, 0.05 * -16.5dB) */
0x3e224b06, /* [187] = 1.58e-001 = pow(10.0, 0.05 * -16.0dB) */
0x3e2be8d7, /* [188] = 1.68e-001 = pow(10.0, 0.05 * -15.5dB) */
0x3e361887, /* [189] = 1.78e-001 = pow(10.0, 0.05 * -15.0dB) */
0x3e40e2bb, /* [190] = 1.88e-001 = pow(10.0, 0.05 * -14.5dB) */
0x3e4c509b, /* [191] = 2.00e-001 = pow(10.0, 0.05 * -14.0dB) */
0x3e586bd9, /* [192] = 2.11e-001 = pow(10.0, 0.05 * -13.5dB) */
0x3e653ebb, /* [193] = 2.24e-001 = pow(10.0, 0.05 * -13.0dB) */
0x3e72d424, /* [194] = 2.37e-001 = pow(10.0, 0.05 * -12.5dB) */
0x3e809bcc, /* [195] = 2.51e-001 = pow(10.0, 0.05 * -12.0dB) */
0x3e883aa8, /* [196] = 2.66e-001 = pow(10.0, 0.05 * -11.5dB) */
0x3e904d1c, /* [197] = 2.82e-001 = pow(10.0, 0.05 * -11.0dB) */
0x3e98da02, /* [198] = 2.99e-001 = pow(10.0, 0.05 * -10.5dB) */
0x3ea1e89b, /* [199] = 3.16e-001 = pow(10.0, 0.05 * -10.0dB) */
0x3eab8097, /* [200] = 3.35e-001 = pow(10.0, 0.05 * -9.5dB) */
0x3eb5aa1a, /* [201] = 3.55e-001 = pow(10.0, 0.05 * -9.0dB) */
0x3ec06dc3, /* [202] = 3.76e-001 = pow(10.0, 0.05 * -8.5dB) */
0x3ecbd4b4, /* [203] = 3.98e-001 = pow(10.0, 0.05 * -8.0dB) */
0x3ed7e89b, /* [204] = 4.22e-001 = pow(10.0, 0.05 * -7.5dB) */
0x3ee4b3b6, /* [205] = 4.47e-001 = pow(10.0, 0.05 * -7.0dB) */
0x3ef240e2, /* [206] = 4.73e-001 = pow(10.0, 0.05 * -6.5dB) */
0x3f004dce, /* [207] = 5.01e-001 = pow(10.0, 0.05 * -6.0dB) */
0x3f07e80b, /* [208] = 5.31e-001 = pow(10.0, 0.05 * -5.5dB) */
0x3f0ff59a, /* [209] = 5.62e-001 = pow(10.0, 0.05 * -5.0dB) */
0x3f187d50, /* [210] = 5.96e-001 = pow(10.0, 0.05 * -4.5dB) */
0x3f21866c, /* [211] = 6.31e-001 = pow(10.0, 0.05 * -4.0dB) */
0x3f2b1896, /* [212] = 6.68e-001 = pow(10.0, 0.05 * -3.5dB) */
0x3f353bef, /* [213] = 7.08e-001 = pow(10.0, 0.05 * -3.0dB) */
0x3f3ff911, /* [214] = 7.50e-001 = pow(10.0, 0.05 * -2.5dB) */
0x3f4b5918, /* [215] = 7.94e-001 = pow(10.0, 0.05 * -2.0dB) */
0x3f5765ac, /* [216] = 8.41e-001 = pow(10.0, 0.05 * -1.5dB) */
0x3f642905, /* [217] = 8.91e-001 = pow(10.0, 0.05 * -1.0dB) */
0x3f71adf9, /* [218] = 9.44e-001 = pow(10.0, 0.05 * -0.5dB) */
0x3f800000, /* [219] = 1.00e+000 = pow(10.0, 0.05 * 0.0dB) */
0x3f8795a0, /* [220] = 1.06e+000 = pow(10.0, 0.05 * 0.5dB) */
0x3f8f9e4d, /* [221] = 1.12e+000 = pow(10.0, 0.05 * 1.0dB) */
0x3f9820d7, /* [222] = 1.19e+000 = pow(10.0, 0.05 * 1.5dB) */
0x3fa12478, /* [223] = 1.26e+000 = pow(10.0, 0.05 * 2.0dB) */
0x3faab0d5, /* [224] = 1.33e+000 = pow(10.0, 0.05 * 2.5dB) */
0x3fb4ce08, /* [225] = 1.41e+000 = pow(10.0, 0.05 * 3.0dB) */
0x3fbf84a6, /* [226] = 1.50e+000 = pow(10.0, 0.05 * 3.5dB) */
0x3fcaddc8, /* [227] = 1.58e+000 = pow(10.0, 0.05 * 4.0dB) */
0x3fd6e30d, /* [228] = 1.68e+000 = pow(10.0, 0.05 * 4.5dB) */
0x3fe39ea9, /* [229] = 1.78e+000 = pow(10.0, 0.05 * 5.0dB) */
0x3ff11b6a, /* [230] = 1.88e+000 = pow(10.0, 0.05 * 5.5dB) */
0x3fff64c1, /* [231] = 2.00e+000 = pow(10.0, 0.05 * 6.0dB) */
0x40074368, /* [232] = 2.11e+000 = pow(10.0, 0.05 * 6.5dB) */
0x400f4735, /* [233] = 2.24e+000 = pow(10.0, 0.05 * 7.0dB) */
0x4017c496, /* [234] = 2.37e+000 = pow(10.0, 0.05 * 7.5dB) */
0x4020c2bf, /* [235] = 2.51e+000 = pow(10.0, 0.05 * 8.0dB) */
0x402a4952, /* [236] = 2.66e+000 = pow(10.0, 0.05 * 8.5dB) */
0x40346063, /* [237] = 2.82e+000 = pow(10.0, 0.05 * 9.0dB) */
0x403f1082, /* [238] = 2.99e+000 = pow(10.0, 0.05 * 9.5dB) */
0x404a62c2, /* [239] = 3.16e+000 = pow(10.0, 0.05 * 10.0dB) */
0x405660bd, /* [240] = 3.35e+000 = pow(10.0, 0.05 * 10.5dB) */
0x406314a0, /* [241] = 3.55e+000 = pow(10.0, 0.05 * 11.0dB) */
0x40708933, /* [242] = 3.76e+000 = pow(10.0, 0.05 * 11.5dB) */
0x407ec9e1, /* [243] = 3.98e+000 = pow(10.0, 0.05 * 12.0dB) */
0x4086f161, /* [244] = 4.22e+000 = pow(10.0, 0.05 * 12.5dB) */
0x408ef052, /* [245] = 4.47e+000 = pow(10.0, 0.05 * 13.0dB) */
0x4097688d, /* [246] = 4.73e+000 = pow(10.0, 0.05 * 13.5dB) */
0x40a06142, /* [247] = 5.01e+000 = pow(10.0, 0.05 * 14.0dB) */
0x40a9e20e, /* [248] = 5.31e+000 = pow(10.0, 0.05 * 14.5dB) */
0x40b3f300, /* [249] = 5.62e+000 = pow(10.0, 0.05 * 15.0dB) */
0x40be9ca5, /* [250] = 5.96e+000 = pow(10.0, 0.05 * 15.5dB) */
0x40c9e807, /* [251] = 6.31e+000 = pow(10.0, 0.05 * 16.0dB) */
0x40d5debc, /* [252] = 6.68e+000 = pow(10.0, 0.05 * 16.5dB) */
0x40e28aeb, /* [253] = 7.08e+000 = pow(10.0, 0.05 * 17.0dB) */
0x40eff755, /* [254] = 7.50e+000 = pow(10.0, 0.05 * 17.5dB) */
0x40fe2f5e, /* [255] = 7.94e+000 = pow(10.0, 0.05 * 18.0dB) */
};
#define MIXART_DIGITAL_LEVEL_MIN 0 /* -109.5 dB */
#define MIXART_DIGITAL_LEVEL_MAX 255 /* 18.0 dB */
#define MIXART_DIGITAL_ZERO_LEVEL 219 /* 0.0 dB */
int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx)
{
int err, i;
int volume[2];
mixart_msg_t request;
mixart_set_out_stream_level_req_t set_level;
u32 status;
mixart_pipe_t *pipe;
memset(&set_level, 0, sizeof(set_level));
set_level.nb_of_stream = 1;
set_level.stream_level.desc.stream_idx = idx;
if(is_aes) {
pipe = &chip->pipe_out_dig; /* AES playback */
idx += MIXART_PLAYBACK_STREAMS;
} else {
pipe = &chip->pipe_out_ana; /* analog playback */
}
/* only when pipe exists ! */
if(pipe->status == PIPE_UNDEFINED)
return 0;
set_level.stream_level.desc.uid_pipe = pipe->group_uid;
for(i=0; i<2; i++) {
if(chip->digital_playback_active[idx][i])
volume[i] = chip->digital_playback_volume[idx][i];
else
volume[i] = MIXART_DIGITAL_LEVEL_MIN;
}
set_level.stream_level.out_level.valid_mask1 = MIXART_OUT_STREAM_SET_LEVEL_LEFT_AUDIO1 | MIXART_OUT_STREAM_SET_LEVEL_RIGHT_AUDIO2;
set_level.stream_level.out_level.left_to_out1_level = mixart_digital_level[volume[0]];
set_level.stream_level.out_level.right_to_out2_level = mixart_digital_level[volume[1]];
request.message_id = MSG_STREAM_SET_OUT_STREAM_LEVEL;
request.uid = (mixart_uid_t){0,0};
request.data = &set_level;
request.size = sizeof(set_level);
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
if((err<0) || status) {
snd_printk(KERN_DEBUG "error MSG_STREAM_SET_OUT_STREAM_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
return -EINVAL;
}
return 0;
}
int mixart_update_capture_stream_level(mixart_t* chip, int is_aes)
{
int err, i, idx;
mixart_pipe_t* pipe;
mixart_msg_t request;
mixart_set_in_audio_level_req_t set_level;
u32 status;
if(is_aes) {
idx = 1;
pipe = &chip->pipe_in_dig;
} else {
idx = 0;
pipe = &chip->pipe_in_ana;
}
/* only when pipe exists ! */
if(pipe->status == PIPE_UNDEFINED)
return 0;
memset(&set_level, 0, sizeof(set_level));
set_level.audio_count = 2;
set_level.level[0].connector = pipe->uid_left_connector;
set_level.level[1].connector = pipe->uid_right_connector;
for(i=0; i<2; i++) {
set_level.level[i].valid_mask1 = MIXART_AUDIO_LEVEL_DIGITAL_MASK;
set_level.level[i].digital_level = mixart_digital_level[chip->digital_capture_volume[idx][i]];
}
request.message_id = MSG_STREAM_SET_IN_AUDIO_LEVEL;
request.uid = (mixart_uid_t){0,0};
request.data = &set_level;
request.size = sizeof(set_level);
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(status), &status);
if((err<0) || status) {
snd_printk(KERN_DEBUG "error MSG_STREAM_SET_IN_AUDIO_LEVEL card(%d) status(%x)\n", chip->chip_idx, status);
return -EINVAL;
}
return 0;
}
/* shared */
static int mixart_digital_vol_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
uinfo->count = 2;
uinfo->value.integer.min = MIXART_DIGITAL_LEVEL_MIN; /* -109.5 dB */
uinfo->value.integer.max = MIXART_DIGITAL_LEVEL_MAX; /* 18.0 dB */
return 0;
}
#define MIXART_VOL_REC_MASK 1
#define MIXART_VOL_AES_MASK 2
static int mixart_pcm_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int *stored_volume;
int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
down(&chip->mgr->mixer_mutex);
if(is_capture) {
if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
} else {
snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */
}
ucontrol->value.integer.value[0] = stored_volume[0];
ucontrol->value.integer.value[1] = stored_volume[1];
up(&chip->mgr->mixer_mutex);
return 0;
}
static int mixart_pcm_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int changed = 0;
int is_capture = kcontrol->private_value & MIXART_VOL_REC_MASK;
int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
int* stored_volume;
int i;
down(&chip->mgr->mixer_mutex);
if(is_capture) {
if(is_aes) stored_volume = chip->digital_capture_volume[1]; /* AES capture */
else stored_volume = chip->digital_capture_volume[0]; /* analog capture */
} else {
snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
if(is_aes) stored_volume = chip->digital_playback_volume[MIXART_PLAYBACK_STREAMS + idx]; /* AES playback */
else stored_volume = chip->digital_playback_volume[idx]; /* analog playback */
}
for(i=0; i<2; i++) {
if(stored_volume[i] != ucontrol->value.integer.value[i]) {
stored_volume[i] = ucontrol->value.integer.value[i];
changed = 1;
}
}
if(changed) {
if(is_capture) mixart_update_capture_stream_level(chip, is_aes);
else mixart_update_playback_stream_level(chip, is_aes, idx);
}
up(&chip->mgr->mixer_mutex);
return changed;
}
static snd_kcontrol_new_t snd_mixart_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* name will be filled later */
/* count will be filled later */
.info = mixart_digital_vol_info, /* shared */
.get = mixart_pcm_vol_get,
.put = mixart_pcm_vol_put,
};
static int mixart_pcm_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
down(&chip->mgr->mixer_mutex);
if(kcontrol->private_value & MIXART_VOL_AES_MASK) /* AES playback */
idx += MIXART_PLAYBACK_STREAMS;
ucontrol->value.integer.value[0] = chip->digital_playback_active[idx][0];
ucontrol->value.integer.value[1] = chip->digital_playback_active[idx][1];
up(&chip->mgr->mixer_mutex);
return 0;
}
static int mixart_pcm_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int is_aes = kcontrol->private_value & MIXART_VOL_AES_MASK;
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id); /* index */
int i, j;
snd_assert ( idx < MIXART_PLAYBACK_STREAMS );
down(&chip->mgr->mixer_mutex);
j = idx;
if(is_aes) j += MIXART_PLAYBACK_STREAMS;
for(i=0; i<2; i++) {
if(chip->digital_playback_active[j][i] != ucontrol->value.integer.value[i]) {
chip->digital_playback_active[j][i] = ucontrol->value.integer.value[i];
changed = 1;
}
}
if(changed) mixart_update_playback_stream_level(chip, is_aes, idx);
up(&chip->mgr->mixer_mutex);
return changed;
}
static snd_kcontrol_new_t mixart_control_pcm_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* name will be filled later */
.count = MIXART_PLAYBACK_STREAMS,
.info = mixart_sw_info, /* shared */
.get = mixart_pcm_sw_get,
.put = mixart_pcm_sw_put
};
static int mixart_update_monitoring(mixart_t* chip, int channel)
{
int err;
mixart_msg_t request;
mixart_set_out_audio_level_t audio_level;
u32 resp;
if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
return -EINVAL; /* no pipe defined */
if(!channel) request.uid = chip->pipe_out_ana.uid_left_connector;
else request.uid = chip->pipe_out_ana.uid_right_connector;
request.message_id = MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL;
request.data = &audio_level;
request.size = sizeof(audio_level);
memset(&audio_level, 0, sizeof(audio_level));
audio_level.valid_mask1 = MIXART_AUDIO_LEVEL_MONITOR_MASK | MIXART_AUDIO_LEVEL_MUTE_M1_MASK;
audio_level.monitor_level = mixart_digital_level[chip->monitoring_volume[channel!=0]];
audio_level.monitor_mute1 = !chip->monitoring_active[channel!=0];
err = snd_mixart_send_msg(chip->mgr, &request, sizeof(resp), &resp);
if((err<0) || resp) {
snd_printk(KERN_DEBUG "error MSG_CONNECTOR_SET_OUT_AUDIO_LEVEL card(%d) resp(%x)\n", chip->chip_idx, resp);
return -EINVAL;
}
return 0;
}
/*
* monitoring level control
*/
static int mixart_monitor_vol_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
down(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->monitoring_volume[0];
ucontrol->value.integer.value[1] = chip->monitoring_volume[1];
up(&chip->mgr->mixer_mutex);
return 0;
}
static int mixart_monitor_vol_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int i;
down(&chip->mgr->mixer_mutex);
for(i=0; i<2; i++) {
if(chip->monitoring_volume[i] != ucontrol->value.integer.value[i]) {
chip->monitoring_volume[i] = ucontrol->value.integer.value[i];
mixart_update_monitoring(chip, i);
changed = 1;
}
}
up(&chip->mgr->mixer_mutex);
return changed;
}
static snd_kcontrol_new_t mixart_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Volume",
.info = mixart_digital_vol_info, /* shared */
.get = mixart_monitor_vol_get,
.put = mixart_monitor_vol_put,
};
/*
* monitoring switch control
*/
static int mixart_monitor_sw_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
down(&chip->mgr->mixer_mutex);
ucontrol->value.integer.value[0] = chip->monitoring_active[0];
ucontrol->value.integer.value[1] = chip->monitoring_active[1];
up(&chip->mgr->mixer_mutex);
return 0;
}
static int mixart_monitor_sw_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
{
mixart_t *chip = snd_kcontrol_chip(kcontrol);
int changed = 0;
int i;
down(&chip->mgr->mixer_mutex);
for(i=0; i<2; i++) {
if(chip->monitoring_active[i] != ucontrol->value.integer.value[i]) {
chip->monitoring_active[i] = ucontrol->value.integer.value[i];
changed |= (1<<i); /* mask 0x01 ans 0x02 */
}
}
if(changed) {
/* allocate or release resources for monitoring */
int allocate = chip->monitoring_active[0] || chip->monitoring_active[1];
if(allocate) {
snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 0, 1); /* allocate the playback pipe for monitoring */
snd_mixart_add_ref_pipe( chip, MIXART_PCM_ANALOG, 1, 1); /* allocate the capture pipe for monitoring */
}
if(changed & 0x01) mixart_update_monitoring(chip, 0);
if(changed & 0x02) mixart_update_monitoring(chip, 1);
if(!allocate) {
snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_in_ana, 1); /* release the capture pipe for monitoring */
snd_mixart_kill_ref_pipe( chip->mgr, &chip->pipe_out_ana, 1); /* release the playback pipe for monitoring */
}
}
up(&chip->mgr->mixer_mutex);
return (changed != 0);
}
static snd_kcontrol_new_t mixart_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Switch",
.info = mixart_sw_info, /* shared */
.get = mixart_monitor_sw_get,
.put = mixart_monitor_sw_put
};
static void mixart_reset_audio_levels(mixart_t *chip)
{
/* analog volumes can be set even if there is no pipe */
mixart_update_analog_audio_level(chip, 0);
/* analog levels for capture only on the first two chips */
if(chip->chip_idx < 2) {
mixart_update_analog_audio_level(chip, 1);
}
return;
}
int snd_mixart_create_mixer(mixart_mgr_t *mgr)
{
mixart_t *chip;
int err, i;
init_MUTEX(&mgr->mixer_mutex); /* can be in another place */
for(i=0; i<mgr->num_cards; i++) {
snd_kcontrol_new_t temp;
chip = mgr->chip[i];
/* analog output level control */
temp = mixart_control_analog_level;
temp.name = "Master Playback Volume";
temp.private_value = 0; /* playback */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
/* output mute controls */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_output_switch, chip))) < 0)
return err;
/* analog input level control only on first two chips !*/
if(i<2) {
temp = mixart_control_analog_level;
temp.name = "Master Capture Volume";
temp.private_value = 1; /* capture */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
}
temp = snd_mixart_pcm_vol;
temp.name = "PCM Playback Volume";
temp.count = MIXART_PLAYBACK_STREAMS;
temp.private_value = 0; /* playback analog */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
temp.name = "PCM Capture Volume";
temp.count = 1;
temp.private_value = MIXART_VOL_REC_MASK; /* capture analog */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
temp.name = "AES Playback Volume";
temp.count = MIXART_PLAYBACK_STREAMS;
temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
temp.name = "AES Capture Volume";
temp.count = 0;
temp.private_value = MIXART_VOL_REC_MASK | MIXART_VOL_AES_MASK; /* capture AES/EBU */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
}
temp = mixart_control_pcm_switch;
temp.name = "PCM Playback Switch";
temp.private_value = 0; /* playback analog */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
if(mgr->board_type == MIXART_DAUGHTER_TYPE_AES) {
temp.name = "AES Playback Switch";
temp.private_value = MIXART_VOL_AES_MASK; /* playback AES/EBU */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&temp, chip))) < 0)
return err;
}
/* monitoring */
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_vol, chip))) < 0)
return err;
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&mixart_control_monitor_sw, chip))) < 0)
return err;
/* init all mixer data and program the master volumes/switches */
mixart_reset_audio_levels(chip);
}
return 0;
}
/*
* Driver for Digigram miXart soundcards
*
* include file for mixer
*
* Copyright (c) 2003 by Digigram <alsa@digigram.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __SOUND_MIXART_MIXER_H
#define __SOUND_MIXART_MIXER_H
/* exported */
int mixart_update_playback_stream_level(mixart_t* chip, int is_aes, int idx);
int mixart_update_capture_stream_level(mixart_t* chip, int is_aes);
int snd_mixart_create_mixer(mixart_mgr_t* mgr);
#endif /* __SOUND_MIXART_MIXER_H */
......@@ -922,12 +922,10 @@ static int snd_via8233_playback_prepare(snd_pcm_substream_t *substream)
chip->no_vra ? 48000 : runtime->rate);
snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
}
#if 0
if (chip->revision == VIA_REV_8233A)
rbits = 0;
if (runtime->rate == 48000)
rbits = 0xfffff;
else
#endif
rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000;
rbits = (0x100000 / 48000) * runtime->rate + ((0x100000 % 48000) * runtime->rate) / 48000;
snd_assert((rbits & ~0xfffff) == 0, return -EINVAL);
snd_via82xx_channel_reset(chip, viadev);
snd_via82xx_set_table_ptr(chip, viadev);
......@@ -1989,11 +1987,12 @@ static int __devinit check_dxs_list(struct pci_dev *pci)
{ .vendor = 0x1106, .device = 0x4161, .action = VIA_DXS_NO_VRA }, /* ASRock K7VT2 */
{ .vendor = 0x1297, .device = 0xa232, .action = VIA_DXS_ENABLE }, /* Shuttle ?? */
{ .vendor = 0x1297, .device = 0xc160, .action = VIA_DXS_ENABLE }, /* Shuttle SK41G */
{ .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_ENABLE }, /* Gigabyte GA-7VAXP */
{ .vendor = 0x1458, .device = 0xa002, .action = VIA_DXS_NO_VRA }, /* Gigabyte GA-7VAXP (FIXME: or DXS_ENABLE?) */
{ .vendor = 0x147b, .device = 0x1401, .action = VIA_DXS_ENABLE }, /* ABIT KD7(-RAID) */
{ .vendor = 0x14ff, .device = 0x0403, .action = VIA_DXS_ENABLE }, /* Twinhead mobo */
{ .vendor = 0x1462, .device = 0x3800, .action = VIA_DXS_ENABLE }, /* MSI KT266 */
{ .vendor = 0x1462, .device = 0x7120, .action = VIA_DXS_ENABLE }, /* MSI KT4V */
{ .vendor = 0x1462, .device = 0x5901, .action = VIA_DXS_NO_VRA }, /* MSI KT6 Delta-SR */
{ .vendor = 0x1631, .device = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
{ .vendor = 0x1695, .device = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
{ .vendor = 0x1849, .device = 0x3059, .action = VIA_DXS_NO_VRA }, /* ASRock K7VM2 */
......
......@@ -117,7 +117,7 @@ struct audioformat {
struct list_head list;
snd_pcm_format_t format; /* format type */
unsigned int channels; /* # channels */
unsigned int nonaudio: 1; /* non-audio (type II) */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int frame_size; /* samples per frame for non-audio */
int iface; /* interface number */
unsigned char altsetting; /* corresponding alternate setting */
......@@ -171,7 +171,7 @@ struct snd_usb_substream {
unsigned int curpacksize; /* current packet size in bytes (for capture) */
unsigned int curframesize; /* current packet size in frames (for capture) */
unsigned int fill_max: 1; /* fill max packet size always */
unsigned int nonaudio: 1; /* Type II format (MPEG, AC3) */
unsigned int fmt_type; /* USB audio format type (1-3) */
unsigned int running: 1; /* running status */
......@@ -201,6 +201,7 @@ struct snd_usb_stream {
snd_usb_audio_t *chip;
snd_pcm_t *pcm;
int pcm_index;
unsigned int fmt_type; /* USB audio format type (1-3) */
snd_usb_substream_t substream[2];
struct list_head list;
};
......@@ -477,7 +478,7 @@ static int prepare_playback_urb(snd_usb_substream_t *subs,
subs->transfer_sched += counts;
if (subs->transfer_sched >= runtime->period_size) {
subs->transfer_sched -= runtime->period_size;
if (subs->nonaudio) {
if (subs->fmt_type == USB_FORMAT_TYPE_II) {
if (subs->transfer_sched > 0) {
/* FIXME: fill-max mode is not supported yet */
offs -= subs->transfer_sched;
......@@ -894,7 +895,7 @@ static int init_substream_urbs(snd_usb_substream_t *subs, unsigned int period_by
u->subs = subs;
u->transfer = 0;
u->packets = npacks[i];
if (subs->nonaudio)
if (subs->fmt_type == USB_FORMAT_TYPE_II)
u->packets++; /* for transfer delimiter */
if (! is_playback) {
/* allocate a capture buffer per urb */
......@@ -1588,7 +1589,7 @@ static int setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs)
runtime->hw.channels_min = fp->channels;
if (runtime->hw.channels_max < fp->channels)
runtime->hw.channels_max = fp->channels;
if (fp->nonaudio && fp->frame_size > 0) {
if (fp->fmt_type == USB_FORMAT_TYPE_II && fp->frame_size > 0) {
/* FIXME: there might be more than one audio formats... */
runtime->hw.period_bytes_min = runtime->hw.period_bytes_max =
fp->frame_size;
......@@ -1895,7 +1896,7 @@ static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat
subs->formats |= 1ULL << fp->format;
subs->endpoint = fp->endpoint;
subs->num_formats++;
subs->nonaudio = fp->nonaudio;
subs->fmt_type = fp->fmt_type;
}
......@@ -1954,17 +1955,12 @@ static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audiofor
list_for_each(p, &chip->pcm_list) {
as = list_entry(p, snd_usb_stream_t, list);
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
if (! subs->endpoint)
break;
continue;
if (subs->endpoint == fp->endpoint) {
if (fp->nonaudio) {
if (!subs->nonaudio || subs->formats != (1ULL << fp->format))
continue; /* non-linear formats are handled exclusively */
} else {
if (subs->nonaudio)
continue;
}
list_add_tail(&fp->list, &subs->fmt_list);
subs->num_formats++;
subs->formats |= 1ULL << fp->format;
......@@ -1974,6 +1970,8 @@ static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audiofor
/* look for an empty stream */
list_for_each(p, &chip->pcm_list) {
as = list_entry(p, snd_usb_stream_t, list);
if (as->fmt_type != fp->fmt_type)
continue;
subs = &as->substream[stream];
if (subs->endpoint)
continue;
......@@ -1991,6 +1989,7 @@ static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audiofor
memset(as, 0, sizeof(*as));
as->pcm_index = chip->pcm_devs;
as->chip = chip;
as->fmt_type = fp->fmt_type;
err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
......@@ -2216,7 +2215,6 @@ static int parse_audio_format_ii(struct usb_device *dev, struct audioformat *fp,
break;
}
fp->channels = 1;
fp->nonaudio = 1;
brate = combine_word(&fmt[4]); /* fmt[4,5] : wMaxBitRate (in kbps) */
framesize = combine_word(&fmt[6]); /* fmt[6,7]: wSamplesPerFrame */
snd_printd(KERN_INFO "found format II with max.bitrate = %d, frame size=%d\n", brate, framesize);
......@@ -2242,6 +2240,7 @@ static int parse_audio_format(struct usb_device *dev, struct audioformat *fp,
dev->devnum, fp->iface, fp->altsetting, fmt[3]);
return -1;
}
fp->fmt_type = fmt[3];
if (err < 0)
return err;
#if 1
......
......@@ -501,6 +501,32 @@ YAMAHA_DEVICE(0x5008, "01V96"),
}
}
},
{
/*
* This quirk is for the "Advanced Driver" mode. If off, the UA-3FX
* is standard compliant, but has only 16-bit PCM.
*/
USB_DEVICE(0x0582, 0x0050),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "EDIROL",
.product_name = "UA-3FX",
.ifnum = QUIRK_ANY_INTERFACE,
.type = QUIRK_COMPOSITE,
.data = & (const snd_usb_audio_quirk_t[]) {
{
.ifnum = 1,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = 2,
.type = QUIRK_AUDIO_STANDARD_INTERFACE
},
{
.ifnum = -1
}
}
}
},
{
USB_DEVICE(0x0582, 0x0052),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
......@@ -688,4 +714,14 @@ YAMAHA_DEVICE(0x5008, "01V96"),
},
{
USB_DEVICE_VENDOR_SPEC(0x0ccd, 0x0013),
.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
.vendor_name = "Terratec",
.product_name = "PHASE 26",
.ifnum = 3,
.type = QUIRK_MIDI_STANDARD_INTERFACE
}
},
#undef USB_DEVICE_VENDOR_SPEC
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment