- direct don't use plugins
- block force block mode (rvplayer)
- non-block force non-block mode
+ - whole-frag write only whole fragments (optimization affecting
+ playback only)
+ - no-silence do not fill silence ahead to avoid clicks
Example: echo "x11amp 128 16384" > /proc/asound/card0/pcm0p/oss
echo "squake 0 0 disable" > /proc/asound/card0/pcm0c/oss
#include <sound/asound.h>
/** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 0)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1)
/**
* definition of sequencer event types
/* misc. conditioning flags */
#define SNDRV_SEQ_PORT_FLG_GIVEN_PORT (1<<0)
+#define SNDRV_SEQ_PORT_FLG_TIMESTAMP (1<<1)
+#define SNDRV_SEQ_PORT_FLG_TIME_REAL (1<<1)
struct sndrv_seq_port_info {
struct sndrv_seq_addr addr; /* client/port numbers */
void *kernel; /* reserved for kernel use (must be NULL) */
unsigned int flags; /* misc. conditioning */
- char reserved[60]; /* for future use */
+ unsigned char time_queue; /* queue # for timestamping */
+ char reserved[59]; /* for future use */
};
SNDRV_HWDEP_IFACE_ICS2115, /* Wavetable synth */
SNDRV_HWDEP_IFACE_SSCAPE, /* Ensoniq SoundScape ISA card (MC68EC000) */
SNDRV_HWDEP_IFACE_VX, /* Digigram VX cards */
+ SNDRV_HWDEP_IFACE_MIXART, /* Digigram miXart cards */
/* Don't forget to change the following: */
- SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_VX,
+ SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_MIXART,
};
struct sndrv_hwdep_info {
unsigned long port; /* I/O port number */
struct resource *res_port;
int APS: 1, /* APS flag */
+ no_ac97: 1, /* no AC'97 */
tos_link: 1; /* tos link detected */
unsigned int audigy; /* is Audigy? */
unsigned int revision; /* chip revision */
unsigned int disable:1,
direct:1,
block:1,
- nonblock:1;
+ nonblock:1,
+ wholefrag:1,
+ nosilence:1;
unsigned int periods;
unsigned int period_size;
snd_pcm_oss_setup_t *next;
int zone_locked; /* locked time for zone */
int sample_locked; /* locked time for sample */
snd_sf_callback_t callback; /* callback functions */
- char sf_locked; /* font lock flag */
struct semaphore presets_mutex;
spinlock_t lock;
snd_util_memhdr_t *memhdr;
/* include/version.h. Generated by configure. */
-#define CONFIG_SND_VERSION "0.9.5"
-#define CONFIG_SND_DATE " (Mon Jul 21 08:59:59 2003 UTC)"
+#define CONFIG_SND_VERSION "0.9.6"
+#define CONFIG_SND_DATE " (Mon Jul 28 11:08:42 2003 UTC)"
module_exit(snd_mem_exit)
+#ifndef MODULE
+
+/* format is: snd-page-alloc=enable */
+
+static int __init snd_mem_setup(char *str)
+{
+ static unsigned __initdata nr_dev = 0;
+
+ if (nr_dev >= SNDRV_CARDS)
+ return 0;
+ (void)(get_option(&str,&enable[nr_dev]) == 2);
+ nr_dev++;
+ return 1;
+}
+
+__setup("snd-page-alloc=", snd_mem_setup);
+
+#endif
+
/*
* exports
*/
sw_params->sleep_min = 0;
sw_params->avail_min = runtime->period_size;
sw_params->xfer_align = 1;
- sw_params->silence_threshold = 0;
- sw_params->silence_size = 0;
+ if (atomic_read(&runtime->mmap_count) ||
+ (substream->oss.setup && substream->oss.setup->nosilence)) {
+ sw_params->silence_threshold = 0;
+ sw_params->silence_size = 0;
+ } else {
+ snd_pcm_uframes_t frames;
+ frames = runtime->period_size + 16;
+ if (frames > runtime->buffer_size)
+ frames = runtime->buffer_size;
+ sw_params->silence_threshold = frames;
+ sw_params->silence_size = frames;
+ }
if ((err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_SW_PARAMS, sw_params)) < 0) {
snd_printd("SW_PARAMS failed: %i\n", err);
if (err < 0)
break;
runtime = substream->runtime;
- if (*delay <= runtime->buffer_size)
+ if (*delay <= (snd_pcm_sframes_t)runtime->buffer_size)
break;
/* in case of overrun, skip whole periods like OSS/Linux driver does */
/* until avail(delay) <= buffer_size */
buf += tmp;
bytes -= tmp;
xfer += tmp;
- if (runtime->oss.buffer_used == runtime->oss.period_bytes) {
- tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
+ if (substream->oss.setup == NULL || !substream->oss.setup->wholefrag ||
+ runtime->oss.buffer_used == runtime->oss.period_bytes) {
+ tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer, runtime->oss.buffer_used, 1);
if (tmp <= 0)
return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
runtime->oss.bytes += tmp;
snd_pcm_oss_setup_t *setup = pstr->oss.setup_list;
down(&pstr->oss.setup_mutex);
while (setup) {
- snd_iprintf(buffer, "%s %u %u%s%s%s%s\n",
+ snd_iprintf(buffer, "%s %u %u%s%s%s%s%s%s\n",
setup->task_name,
setup->periods,
setup->period_size,
setup->disable ? " disable" : "",
setup->direct ? " direct" : "",
setup->block ? " block" : "",
- setup->nonblock ? " non-block" : "");
+ setup->nonblock ? " non-block" : "",
+ setup->wholefrag ? " whole-frag" : "",
+ setup->nosilence ? " no-silence" : "");
setup = setup->next;
}
up(&pstr->oss.setup_mutex);
template.block = 1;
} else if (!strcmp(str, "non-block")) {
template.nonblock = 1;
+ } else if (!strcmp(str, "whole-frag")) {
+ template.wholefrag = 1;
+ } else if (!strcmp(str, "no-silence")) {
+ template.nosilence = 1;
}
} while (*str);
if (setup == NULL) {
module_init(alsa_pcm_oss_init)
module_exit(alsa_pcm_oss_exit)
+
+#ifndef MODULE
+
+/* format is: snd-pcm-oss=dsp_map,adsp_map[,nonblock_open] */
+
+static int __init alsa_pcm_oss_setup(char *str)
+{
+ static unsigned __initdata nr_dev = 0;
+
+ if (nr_dev >= SNDRV_CARDS)
+ return 0;
+ (void)(get_option(&str,&dsp_map[nr_dev]) == 2 &&
+ get_option(&str,&adsp_map[nr_dev]) == 2);
+ (void)(get_option(&str,&nonblock_open) == 2);
+ nr_dev++;
+ return 1;
+}
+
+__setup("snd-pcm-oss=", alsa_pcm_oss_setup);
+
+#endif /* !MODULE */
return 0;
}
+/*
+ * because some cards might have rates "very close", we ignore
+ * all "resampling" requests within +-5%
+ */
+static int rate_match(unsigned int src_rate, unsigned int dst_rate)
+{
+ unsigned int low = (src_rate * 95) / 100;
+ unsigned int high = (src_rate * 105) / 100;
+ return dst_rate >= low && dst_rate <= high;
+}
+
static int snd_pcm_plugin_alloc(snd_pcm_plugin_t *plugin, snd_pcm_uframes_t frames)
{
snd_pcm_plugin_format_t *format;
plugin->buf = vmalloc(size);
plugin->buf_frames = frames;
}
- if (!plugin->buf)
+ if (!plugin->buf) {
+ plugin->buf_frames = 0;
return -ENOMEM;
+ }
c = plugin->buf_channels;
if (plugin->access == SNDRV_PCM_ACCESS_RW_INTERLEAVED) {
for (channel = 0; channel < format->channels; channel++, c++) {
+ c->frames = frames;
c->enabled = 1;
c->wanted = 0;
c->area.addr = plugin->buf;
snd_assert((size % format->channels) == 0,);
size /= format->channels;
for (channel = 0; channel < format->channels; channel++, c++) {
+ c->frames = frames;
c->enabled = 1;
c->wanted = 0;
c->area.addr = plugin->buf + (channel * size);
/* Format change (linearization) */
if ((srcformat.format != dstformat.format ||
- srcformat.rate != dstformat.rate ||
+ !rate_match(srcformat.rate, dstformat.rate) ||
srcformat.channels != dstformat.channels) &&
!snd_pcm_format_linear(srcformat.format)) {
if (snd_pcm_format_linear(dstformat.format))
ttable[v * sv + v] = FULL;
}
tmpformat.channels = dstformat.channels;
- if (srcformat.rate == dstformat.rate &&
+ if (rate_match(srcformat.rate, dstformat.rate) &&
snd_pcm_format_linear(dstformat.format))
tmpformat.format = dstformat.format;
err = snd_pcm_plugin_build_route(plug,
}
/* rate resampling */
- if (srcformat.rate != dstformat.rate) {
+ if (!rate_match(srcformat.rate, dstformat.rate)) {
tmpformat.rate = dstformat.rate;
if (srcformat.channels == dstformat.channels &&
snd_pcm_format_linear(dstformat.format))
typedef struct _snd_pcm_plugin_channel {
void *aptr; /* pointer to the allocated area */
snd_pcm_channel_area_t area;
+ snd_pcm_uframes_t frames; /* allocated frames */
unsigned int enabled:1; /* channel need to be processed */
unsigned int wanted:1; /* channel is wanted */
} snd_pcm_plugin_channel_t;
#ifdef PUT_S16_LABELS
/* dst_wid dst_endswap unsigned */
-static void *put_s16_labels[4 * 2 * 2 * 4 * 2] = {
+static void *put_s16_labels[4 * 2 * 2] = {
&&put_s16_xx12_xxx1, /* 16h -> 8h */
&&put_s16_xx12_xxx9, /* 16h ^> 8h */
&&put_s16_xx12_xxx1, /* 16h -> 8s */
#undef PUT_S16_LABELS
void *get = get_s16_labels[data->get];
void *put = put_s16_labels[data->put];
- void *get_s16_end = 0;
signed short sample = 0;
-#define GET_S16_END *get_s16_end
-#include "plugin_ops.h"
-#undef GET_S16_END
for (channel = 0; channel < plugin->src_format.channels; channel++) {
pos = data->pos;
dst_step = dst_channels[channel].area.step / 8;
src_frames1 = src_frames;
dst_frames1 = dst_frames;
- if (pos & ~R_MASK) {
- get_s16_end = &&after_get1;
- goto *get;
- after_get1:
- pos &= R_MASK;
- S1 = S2;
- S2 = sample;
- src += src_step;
- src_frames1--;
- }
while (dst_frames1-- > 0) {
if (pos & ~R_MASK) {
pos &= R_MASK;
S1 = S2;
if (src_frames1-- > 0) {
- get_s16_end = &&after_get2;
goto *get;
- after_get2:
+#define GET_S16_END after_get
+#include "plugin_ops.h"
+#undef GET_S16_END
+ after_get:
S2 = sample;
src += src_step;
}
#endif
dst_frames = rate_dst_frames(plugin, frames);
+ if (dst_frames > dst_channels[0].frames)
+ dst_frames = dst_channels[0].frames;
data = (rate_t *)plugin->extra_data;
data->func(plugin, src_channels, dst_channels, frames, dst_frames);
return dst_frames;
int sign, width, endian;
sign = !snd_pcm_format_signed(format);
width = snd_pcm_format_width(format) / 8 - 1;
+ if (width < 0 || width > 3) {
+ snd_printk(KERN_ERR "snd-pcm-oss: invalid format %d\n", format);
+ width = 0;
+ }
#ifdef SNDRV_LITTLE_ENDIAN
endian = snd_pcm_format_big_endian(format);
#else
return;
snd_assert(runtime->silence_filled <= runtime->buffer_size, return);
noise_dist = snd_pcm_playback_hw_avail(runtime) + runtime->silence_filled;
- if (noise_dist > (snd_pcm_sframes_t) runtime->silence_threshold)
+ if (noise_dist >= (snd_pcm_sframes_t) runtime->silence_threshold)
return;
frames = runtime->silence_threshold - noise_dist;
if (frames > runtime->silence_size)
frames = runtime->silence_size;
- else
- frames = runtime->silence_threshold;
} else {
if (new_hw_ptr == ULONG_MAX) { /* initialization */
runtime->silence_filled = 0;
if ((snd_pcm_sframes_t)runtime->silence_start < 0)
runtime->silence_start += runtime->boundary;
}
- frames = runtime->buffer_size;
+ frames = runtime->buffer_size - runtime->silence_filled;
}
- snd_assert(frames >= runtime->silence_filled, return);
- frames -= runtime->silence_filled;
+ snd_assert(frames <= runtime->buffer_size, return);
if (frames == 0)
return;
ofs = (runtime->silence_start + runtime->silence_filled) % runtime->buffer_size;
#include <sound/driver.h>
#include <asm/io.h>
#include <linux/time.h>
+#include <linux/init.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/info.h>
}
#endif /* CONFIG_PCI */
+
+#ifndef MODULE
+
+/* format is: snd-pcm=preallocate_dma,maximum_substreams */
+
+static int __init alsa_pcm_setup(char *str)
+{
+ (void)(get_option(&str,&preallocate_dma) == 2 &&
+ get_option(&str,&maximum_substreams) == 2);
+ return 1;
+}
+
+__setup("snd-pcm=", alsa_pcm_setup);
+
+#endif /* ifndef MODULE */
if (params->xfer_align == 0 ||
params->xfer_align % runtime->min_align != 0)
return -EINVAL;
- if ((params->silence_threshold != 0 || params->silence_size < runtime->boundary) &&
- (params->silence_threshold + params->silence_size > runtime->buffer_size))
- return -EINVAL;
+ if (params->silence_size >= runtime->boundary) {
+ if (params->silence_threshold != 0)
+ return -EINVAL;
+ } else {
+ if (params->silence_size > params->silence_threshold)
+ return -EINVAL;
+ if (params->silence_threshold > runtime->buffer_size)
+ return -EINVAL;
+ }
snd_pcm_stream_lock_irq(substream);
runtime->tstamp_mode = params->tstamp_mode;
runtime->sleep_min = params->sleep_min;
module_init(alsa_rawmidi_init)
module_exit(alsa_rawmidi_exit)
+#ifndef MODULE
+#ifdef CONFIG_SND_OSSEMUL
+/* format is: snd-rawmidi=midi_map,amidi_map */
+
+static int __init alsa_rawmidi_setup(char *str)
+{
+ static unsigned __initdata nr_dev = 0;
+
+ if (nr_dev >= SNDRV_CARDS)
+ return 0;
+ (void)(get_option(&str,&midi_map[nr_dev]) == 2 &&
+ get_option(&str,&amidi_map[nr_dev]) == 2);
+ nr_dev++;
+ return 1;
+}
+
+__setup("snd-rawmidi=", alsa_rawmidi_setup);
+#endif /* CONFIG_SND_OSSEMUL */
+#endif /* ifndef MODULE */
+
EXPORT_SYMBOL(snd_rawmidi_output_params);
EXPORT_SYMBOL(snd_rawmidi_input_params);
EXPORT_SYMBOL(snd_rawmidi_drop_output);
.stop = rtctimer_stop,
};
-int rtctimer_freq = RTC_FREQ; /* frequency */
+static int rtctimer_freq = RTC_FREQ; /* frequency */
static snd_timer_t *rtctimer;
static atomic_t rtc_inc = ATOMIC_INIT(0);
static rtc_task_t rtc_task;
MODULE_LICENSE("GPL");
+#ifndef MODULE
+/* format is: snd-rtctimer=freq */
+
+static int __init rtctimer_setup(char *str)
+{
+ (void)(get_option(&str,&rtctimer_freq) == 2);
+ return 1;
+}
+
+__setup("snd-rtctimer=", rtctimer_setup);
+#endif /* ifndef MODULE */
+
#endif /* CONFIG_RTC || CONFIG_RTC_MODULE */
}
+/*
+ * rewrite the time-stamp of the event record with the curren time
+ * of the given queue.
+ * return non-zero if updated.
+ */
+static int update_timestamp_of_queue(snd_seq_event_t *event, int queue, int real_time)
+{
+ queue_t *q;
+
+ q = queueptr(queue);
+ if (! q)
+ return 0;
+ event->queue = queue;
+ event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
+ if (real_time) {
+ event->time.time = snd_seq_timer_get_cur_time(q->timer);
+ event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
+ } else {
+ event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
+ event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
+ }
+ queuefree(q);
+ return 1;
+}
+
+
/*
* deliver an event to the specified destination.
* if filter is non-zero, client filter bitmap is tested.
goto __skip;
}
+ if (dest_port->timestamping)
+ update_timestamp_of_queue(event, dest_port->time_queue,
+ dest_port->time_real);
+
/* expand the quoted event */
if (event->type == SNDRV_SEQ_EVENT_KERNEL_QUOTE) {
quoted = 1;
}
-static void snd_seq_subs_update_event_header(subscribers_t *subs, snd_seq_event_t *event)
-{
- if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP) {
- /* convert time according to flag with subscription */
- queue_t *q;
- q = queueptr(subs->info.queue);
- if (q) {
- event->queue = subs->info.queue;
- event->flags &= ~SNDRV_SEQ_TIME_STAMP_MASK;
- if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL) {
- event->time.time = snd_seq_timer_get_cur_time(q->timer);
- event->flags |= SNDRV_SEQ_TIME_STAMP_REAL;
- } else {
- event->time.tick = snd_seq_timer_get_cur_tick(q->timer);
- event->flags |= SNDRV_SEQ_TIME_STAMP_TICK;
- }
- queuefree(q);
- }
- }
-}
-
/*
* send the event to all subscribers:
*/
list_for_each(p, &grp->list_head) {
subs = list_entry(p, subscribers_t, src_list);
event->dest = subs->info.dest;
- snd_seq_subs_update_event_header(subs, event);
+ if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
+ /* convert time according to flag with subscription */
+ update_timestamp_of_queue(event, subs->info.queue,
+ subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIME_REAL);
err = snd_seq_deliver_single_event(client, event,
0, atomic, hop);
if (err < 0)
port->midi_voices = info->midi_voices;
port->synth_voices = info->synth_voices;
+ /* timestamping */
+ port->timestamping = (info->flags & SNDRV_SEQ_PORT_FLG_TIMESTAMP) ? 1 : 0;
+ port->time_real = (info->flags & SNDRV_SEQ_PORT_FLG_TIME_REAL) ? 1 : 0;
+ port->time_queue = info->time_queue;
+
return 0;
}
info->read_use = port->c_src.count;
info->write_use = port->c_dest.count;
+ /* timestamping */
+ info->flags = 0;
+ if (port->timestamping) {
+ info->flags |= SNDRV_SEQ_PORT_FLG_TIMESTAMP;
+ if (port->time_real)
+ info->flags |= SNDRV_SEQ_PORT_FLG_TIME_REAL;
+ info->time_queue = port->time_queue;
+ }
+
return 0;
}
void *private_data;
unsigned int callback_all : 1;
unsigned int closing : 1;
+ unsigned int timestamping: 1;
+ unsigned int time_real: 1;
+ int time_queue;
/* capability, inport, output, sync */
unsigned int capability; /* port capability bits */
module_init(alsa_sound_init)
module_exit(alsa_sound_exit)
+#ifndef MODULE
+
+/* format is: snd=major,cards_limit[,device_mode] */
+
+static int __init alsa_sound_setup(char *str)
+{
+ (void)(get_option(&str,&major) == 2 &&
+ get_option(&str,&cards_limit) == 2);
+#ifdef CONFIG_DEVFS_FS
+ (void)(get_option(&str,&device_mode) == 2);
+#endif
+ return 1;
+}
+
+__setup("snd=", alsa_sound_setup);
+
+#endif /* ifndef MODULE */
+
/* sound.c */
EXPORT_SYMBOL(snd_major);
EXPORT_SYMBOL(snd_ecards_limit);
#define DEFAULT_TIMER_LIMIT 2
#endif
-int timer_limit = DEFAULT_TIMER_LIMIT;
+static int timer_limit = DEFAULT_TIMER_LIMIT;
MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
MODULE_DESCRIPTION("ALSA timer interface");
MODULE_LICENSE("GPL");
module_init(alsa_timer_init)
module_exit(alsa_timer_exit)
+#ifndef MODULE
+/* format is: snd-timer=timer_limit */
+
+static int __init alsa_timer_setup(char *str)
+{
+ (void)(get_option(&str,&timer_limit) == 2);
+ return 1;
+}
+
+__setup("snd-timer=", alsa_timer_setup);
+#endif /* ifndef MODULE */
+
EXPORT_SYMBOL(snd_timer_open);
EXPORT_SYMBOL(snd_timer_close);
EXPORT_SYMBOL(snd_timer_resolution);
#define USE_PERIODS_MAX 255
#endif
+#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
+#define USE_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+#define USE_CHANNELS_MIN 2
+#define USE_CHANNELS_MAX 2
+#define USE_RATE SNDRV_PCM_RATE_48000
+#define USE_RATE_MIN 48000
+#define USE_RATE_MAX 48000
+#endif
+
/* defaults */
#ifndef MAX_BUFFER_SIZE
#ifndef USE_FORMATS
#define USE_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
#endif
+#ifndef USE_RATE
+#define USE_RATE SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
+#define USE_RATE_MIN 5500
+#define USE_RATE_MAX 48000
+#endif
#ifndef USE_CHANNELS_MIN
#define USE_CHANNELS_MIN 1
#endif
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = USE_FORMATS,
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5500,
- .rate_max = 48000,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = MAX_BUFFER_SIZE,
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = USE_FORMATS,
- .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
- .rate_min = 5500,
- .rate_max = 48000,
+ .rates = USE_RATE,
+ .rate_min = USE_RATE_MIN,
+ .rate_max = USE_RATE_MAX,
.channels_min = USE_CHANNELS_MIN,
.channels_max = USE_CHANNELS_MAX,
.buffer_bytes_max = MAX_BUFFER_SIZE,
#endif /* CONFIG_PNP */
+static void snd_sb16_free(snd_card_t *card)
+{
+ struct snd_card_sb16 *acard = (struct snd_card_sb16 *)card->private_data;
+
+ if (acard == NULL)
+ return;
+ if (acard->fm_res) {
+ release_resource(acard->fm_res);
+ kfree_nocheck(acard->fm_res);
+ }
+}
+
static int __init snd_sb16_probe(int dev,
struct pnp_card_link *pcard,
const struct pnp_card_device_id *pid)
if (card == NULL)
return -ENOMEM;
acard = (struct snd_card_sb16 *) card->private_data;
+ card->private_free = snd_sb16_free;
#ifdef CONFIG_PNP
if (isapnp[dev]) {
if ((err = snd_card_sb16_pnp(dev, acard, pcard, pid))) {
if (fm_port[dev] > 0) {
if (snd_opl3_create(card, fm_port[dev], fm_port[dev] + 2,
- OPL3_HW_OPL3, fm_port[dev] == port[dev],
+ OPL3_HW_OPL3,
+ fm_port[dev] == port[dev] || fm_port[dev] == 0x388,
&opl3) < 0) {
snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx\n",
fm_port[dev], fm_port[dev] + 2);
return 0;
}
+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("Low Power Mixer", AC97_AD_MISC, 14, 1, 0),
+ AC97_SINGLE("Zero Fill DAC", AC97_AD_MISC, 15, 1, 0),
+};
+
+static int patch_ad1885_specific(ac97_t * ac97)
+{
+ int err;
+
+ if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
+ return err;
+ return 0;
+}
+
+static struct snd_ac97_build_ops patch_ad1885_build_ops = {
+ .build_specific = &patch_ad1885_specific
+};
+
int patch_ad1885(ac97_t * ac97)
{
unsigned short jack;
/* turn off jack sense bits D8 & D9 */
jack = snd_ac97_read(ac97, AC97_AD_JACK_SPDIF);
snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, jack | 0x0300);
+
+ ac97->build_ops = &patch_ad1885_build_ops;
return 0;
}
snd_card_free(card);
return err;
}
- if (!emu->APS) { /* APS board has not an AC97 mixer */
- if ((err = snd_emu10k1_mixer(emu)) < 0) {
- snd_card_free(card);
- return err;
- }
+ if ((err = snd_emu10k1_mixer(emu)) < 0) {
+ snd_card_free(card);
+ return err;
}
if (emu->audigy) {
if ((err = snd_emu10k1_audigy_midi(emu)) < 0) {
if (emu->serial == 0x40011102) {
emu->card_type = EMU10K1_CARD_EMUAPS;
emu->APS = 1;
+ emu->no_ac97 = 1; /* APS has no AC97 chip */
+ }
+ else if (emu->revision == 4 && emu->serial == 0x10051102) {
+ /* Audigy 2 EX has apparently no effective AC97 controls
+ * (for both input and output), so we skip the AC97 detections
+ */
+ snd_printdd(KERN_INFO "Audigy2 EX is detected. skpping ac97.\n");
+ emu->no_ac97 = 1;
}
emu->fx8010.fxbus_mask = 0x303f;
static int __devinit _snd_emu10k1_audigy_init_efx(emu10k1_t *emu)
{
- int err, i, z, gpr, tmp, playback, capture, nctl;
+ int err, i, z, gpr, nctl;
+ const int playback = 10;
+ const int capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
+ const int tmp = 0x88;
u32 ptr;
emu10k1_fx8010_code_t *icode;
emu10k1_fx8010_control_gpr_t *controls, *ctl;
strcpy(icode->name, "Audigy DSP code for ALSA");
ptr = 0;
nctl = 0;
- playback = 10;
- capture = playback + (SND_EMU10K1_PLAYBACK_CHANNELS * 2); /* we reserve 10 voices */
gpr = capture + 10;
- tmp = 0x88;
/* stop FX processor */
snd_emu10k1_ptr_write(emu, A_DBG, 0, (emu->fx8010.dbg = 0) | A_DBG_SINGLE_STEP);
- /* Wave Playback */
+ /* Wave Playback Volume */
A_OP(icode, &ptr, iMAC0, A_GPR(playback), A_C_00000000, A_GPR(gpr), A_FXBUS(FXBUS_PCM_LEFT));
A_OP(icode, &ptr, iMAC0, A_GPR(playback+1), A_C_00000000, A_GPR(gpr+1), A_FXBUS(FXBUS_PCM_RIGHT));
- snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr,
- emu->revision == 4 ? 50 : 100);
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Playback Volume", gpr, 100);
gpr += 2;
/* Wave Surround Playback */
snd_emu10k1_init_stereo_onoff_control(controls + nctl++, "Tone Control - Switch", gpr, 0);
gpr += 2;
+ /* Master volume for audigy2 */
+ if (emu->revision == 4) {
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr), A_GPR(playback+0+SND_EMU10K1_PLAYBACK_CHANNELS));
+ A_OP(icode, &ptr, iMAC0, A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS), A_C_00000000, A_GPR(gpr+1), A_GPR(playback+1+SND_EMU10K1_PLAYBACK_CHANNELS));
+ snd_emu10k1_init_stereo_control(&controls[nctl++], "Wave Master Playback Volume", gpr, 0);
+ gpr += 2;
+ }
+
/* digital outputs */
A_PUT_STEREO_OUTPUT(A_EXTOUT_FRONT_L, A_EXTOUT_FRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
A_PUT_STEREO_OUTPUT(A_EXTOUT_REAR_L, A_EXTOUT_REAR_R, playback+2 + SND_EMU10K1_PLAYBACK_CHANNELS);
A_PUT_OUTPUT(A_EXTOUT_LFE, playback+5 + SND_EMU10K1_PLAYBACK_CHANNELS);
/* analog speakers */
- if (emu->audigy && emu->revision == 4) { /* audigy2 */
+ if (emu->revision == 4) { /* audigy2 */
A_PUT_STEREO_OUTPUT(A_EXTOUT_AFRONT_L, A_EXTOUT_AFRONT_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
} else {
A_PUT_STEREO_OUTPUT(A_EXTOUT_AC97_L, A_EXTOUT_AC97_R, playback + SND_EMU10K1_PLAYBACK_CHANNELS);
emu->ac97 = NULL;
}
+/*
+ */
+static int remove_ctl(snd_card_t *card, const char *name)
+{
+ snd_ctl_elem_id_t id;
+ memset(&id, 0, sizeof(id));
+ strcpy(id.name, name);
+ id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_remove_id(card, &id);
+}
+
+static snd_kcontrol_t *ctl_find(snd_card_t *card, const char *name)
+{
+ snd_ctl_elem_id_t sid;
+ memset(&sid, 0, sizeof(sid));
+ strcpy(sid.name, name);
+ sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ return snd_ctl_find_id(card, &sid);
+}
+
+static int rename_ctl(snd_card_t *card, const char *src, const char *dst)
+{
+ snd_kcontrol_t *kctl = ctl_find(card, src);
+ if (kctl) {
+ strcpy(kctl->id.name, dst);
+ return 0;
+ }
+ return -ENOENT;
+}
+
int __devinit snd_emu10k1_mixer(emu10k1_t *emu)
{
ac97_t ac97;
snd_kcontrol_t *kctl;
snd_card_t *card = emu->card;
- if (!emu->APS) {
+ if (!emu->no_ac97) {
memset(&ac97, 0, sizeof(ac97));
ac97.write = snd_emu10k1_ac97_write;
ac97.read = snd_emu10k1_ac97_read;
ac97.private_free = snd_emu10k1_mixer_free_ac97;
if ((err = snd_ac97_mixer(emu->card, &ac97, &emu->ac97)) < 0)
return err;
+ if (emu->audigy && emu->revision == 4) {
+ /* Master/PCM controls on ac97 of Audigy2 has no effect */
+ /* FIXME: keep master volume/switch to be sure.
+ * once after we check that they play really no roles,
+ * they shall be removed.
+ */
+ rename_ctl(card, "Master Playback Switch", "AC97 Master Playback Switch");
+ rename_ctl(card, "Master Playback Volume", "AC97 Master Playback Volume");
+ /* pcm controls are removed */
+ remove_ctl(card, "PCM Playback Switch");
+ remove_ctl(card, "PCM Playback Volume");
+ }
} else {
- strcpy(emu->card->mixername, "EMU APS");
+ if (emu->APS)
+ strcpy(emu->card->mixername, "EMU APS");
+ else if (emu->audigy)
+ strcpy(emu->card->mixername, "SB Audigy");
+ else
+ strcpy(emu->card->mixername, "Emu10k1");
+ }
+
+ if (emu->audigy && emu->revision == 4) {
+ /* Audigy2 and Audigy2 EX */
+ /* use the conventional names */
+ rename_ctl(card, "Wave Playback Volume", "PCM Playback Volume");
+ rename_ctl(card, "Wave Playback Volume", "PCM Capture Volume");
+ rename_ctl(card, "Wave Master Playback Volume", "Master Playback Volume");
}
if ((kctl = emu->ctl_send_routing = snd_ctl_new1(&snd_emu10k1_send_routing_control, emu)) == NULL)
if ((err = snd_ctl_add(card, kctl)))
return err;
+ /* intiailize the routing and volume table for each pcm playback stream */
for (pcm = 0; pcm < 32; pcm++) {
emu10k1_pcm_mixer_t *mix;
int v;
mix->attn[0] = mix->attn[1] = mix->attn[2] = 0xffff;
}
- if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
- return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
- return err;
- if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
- return -ENOMEM;
- if ((err = snd_ctl_add(card, kctl)))
- return err;
+ if (! emu->APS) { /* FIXME: APS has these controls? */
+ /* sb live! and audigy */
+ if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_mask_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ if ((kctl = snd_ctl_new1(&snd_emu10k1_spdif_control, emu)) == NULL)
+ return -ENOMEM;
+ if ((err = snd_ctl_add(card, kctl)))
+ return err;
+ }
if (emu->audigy) {
if ((kctl = snd_ctl_new1(&snd_audigy_shared_spdif, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
return err;
- } else {
+ } else if (! emu->APS) {
+ /* sb live! */
if ((kctl = snd_ctl_new1(&snd_emu10k1_shared_spdif, emu)) == NULL)
return -ENOMEM;
if ((err = snd_ctl_add(card, kctl)))
if (status & IPR_CHANNELLOOP) {
int voice;
int voice_max = status & IPR_CHANNELNUMBERMASK;
- int voice_max_l;
u32 val;
emu10k1_voice_t *pvoice = emu->voices;
val = snd_emu10k1_ptr_read(emu, CLIPL, 0);
- voice_max_l = voice_max;
- if (voice_max_l >= 0x20)
- voice_max_l = 0x1f;
- for (voice = 0; voice <= voice_max_l; voice++) {
+ for (voice = 0; voice <= voice_max; voice++) {
+ if (voice == 0x20)
+ val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
if (val & 1) {
if (pvoice->use && pvoice->interrupt != NULL) {
pvoice->interrupt(emu, pvoice);
val >>= 1;
pvoice++;
}
- if (voice_max > 0x1f) {
- val = snd_emu10k1_ptr_read(emu, CLIPH, 0);
- for (; voice <= voice_max; voice++) {
- if(val & 1) {
- if (pvoice->use && pvoice->interrupt != NULL) {
- pvoice->interrupt(emu, pvoice);
- snd_emu10k1_voice_intr_ack(emu, voice);
- } else {
- snd_emu10k1_voice_intr_disable(emu, voice);
- }
- }
- val >>= 1;
- pvoice++;
- }
- }
status &= ~IPR_CHANNELLOOP;
}
status &= ~IPR_CHANNELNUMBERMASK;
status &= ~IPR_FXDSP;
}
if (status) {
- snd_printd(KERN_WARNING "emu10k1: unhandled interrupt: 0x%08x\n", status);
+ unsigned int bits;
+ snd_printk(KERN_ERR "emu10k1: unhandled interrupt: 0x%08x\n", status);
+ //make sure any interrupts we don't handle are disabled:
+ bits = INTE_FXDSPENABLE |
+ INTE_PCIERRORENABLE |
+ INTE_VOLINCRENABLE |
+ INTE_VOLDECRENABLE |
+ INTE_MUTEENABLE |
+ INTE_MICBUFENABLE |
+ INTE_ADCBUFENABLE |
+ INTE_EFXBUFENABLE |
+ INTE_GPSPDIFENABLE |
+ INTE_CDSPDIFENABLE |
+ INTE_INTERVALTIMERENB |
+ INTE_MIDITXENABLE |
+ INTE_MIDIRXENABLE;
+ if (emu->audigy)
+ bits |= INTE_A_MIDITXENABLE2 | INTE_A_MIDIRXENABLE2;
+ snd_emu10k1_intr_disable(emu, bits);
}
- outl(orig_status, emu->port + IPR); /* ack */
+ outl(orig_status, emu->port + IPR); /* ack all */
}
return IRQ_RETVAL(handled);
}
snd_pcm_runtime_t *runtime = substream->runtime;
static unsigned int i, rates[] = {
/* ATTENTION: these values depend on the definition in pcm.h! */
- 5512, 8000, 11025, 16000, 22050, 32000, 44100, 480000
+ 5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000
};
int err;
{ 0x1028, 0x0126, "Dell Optiplex GX260", AC97_TUNE_HP_ONLY },
{ 0x1734, 0x0088, "Fujitsu-Siemens D1522", AC97_TUNE_HP_ONLY },
{ 0x10f1, 0x2665, "Fujitsu-Siemens Celcius", AC97_TUNE_HP_ONLY },
+ { 0x110a, 0x0056, "Fujitsu-Siemens Scenic", AC97_TUNE_HP_ONLY },
{ 0x8086, 0x4d44, "Intel D850EMV2", AC97_TUNE_HP_ONLY },
/* { 0x4144, 0x5360, "AMD64 Motherboard", AC97_TUNE_HP_ONLY }, */ /* FIXME: this seems invalid */
{ 0x1043, 0x80b0, "ASUS P4PE Mobo", AC97_TUNE_SWAP_SURROUND },
snd_m3_pcm_pointer(snd_pcm_substream_t * subs)
{
m3_t *chip = snd_pcm_substream_chip(subs);
+ unsigned int ptr;
m3_dma_t *s = (m3_dma_t*)subs->runtime->private_data;
snd_assert(s != NULL, return 0);
- return bytes_to_frames(subs->runtime, snd_m3_get_pointer(chip, s, subs));
+
+ spin_lock(&chip->reg_lock);
+ ptr = snd_m3_get_pointer(chip, s, subs);
+ spin_unlock(&chip->reg_lock);
+ return bytes_to_frames(subs->runtime, ptr);
}
snd_ac97_set_rate(chip->ac97, AC97_PCM_LFE_DAC_RATE, runtime->rate);
snd_ac97_set_rate(chip->ac97, AC97_SPDIF, runtime->rate);
}
- if (chip->chip_type == TYPE_VIA8233A)
+ if (chip->revision == VIA_REV_8233A)
rbits = 0;
else
rbits = (0xfffff / 48000) * runtime->rate + ((0xfffff % 48000) * runtime->rate) / 48000;
fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
fmt |= runtime->channels << 4;
outb(fmt, VIADEV_REG(viadev, OFS_MULTPLAY_FORMAT));
- if (chip->chip_type == TYPE_VIA8233A)
+ if (chip->revision == VIA_REV_8233A)
slots = 0;
else {
/* set sample number to slot 3, 4, 7, 8, 6, 9 (for VIA8233/C,8235) */
if ((err = snd_via82xx_pcm_open(chip, viadev, substream)) < 0)
return err;
substream->runtime->hw.channels_max = 6;
- if (chip->chip_type == TYPE_VIA8233A)
+ if (chip->revision == VIA_REV_8233A)
snd_pcm_hw_constraint_list(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
return 0;
}
static int
lock_preset(snd_sf_list_t *sflist, int nonblock)
{
- unsigned long flags;
- spin_lock_irqsave(&sflist->lock, flags);
- if (sflist->sf_locked && nonblock) {
- spin_unlock_irqrestore(&sflist->lock, flags);
- return -EBUSY;
- }
- spin_unlock_irqrestore(&sflist->lock, flags);
- down(&sflist->presets_mutex);
- sflist->sf_locked = 1;
+ if (nonblock) {
+ if (down_trylock(&sflist->presets_mutex))
+ return -EBUSY;
+ } else
+ down(&sflist->presets_mutex);
return 0;
}
unlock_preset(snd_sf_list_t *sflist)
{
up(&sflist->presets_mutex);
- sflist->sf_locked = 0;
}
init_MUTEX(&sflist->presets_mutex);
spin_lock_init(&sflist->lock);
- sflist->sf_locked = 0;
sflist->memhdr = hdr;
if (callback)
/*
* Remove unlocked samples.
- * The soundcard should be silet before calling this function.
+ * The soundcard should be silent before calling this function.
*/
int
snd_soundfont_remove_unlocked(snd_sf_list_t *sflist)
*/
fp->attributes &= ~EP_CS_ATTR_SAMPLE_RATE;
}
+
+ /* workaround for M-Audio Audiophile USB */
+ if (dev->descriptor.idVendor == 0x0763 &&
+ dev->descriptor.idProduct == 0x2003) {
+ /* doesn't set the sample rate attribute, but supports it */
+ fp->attributes |= EP_CS_ATTR_SAMPLE_RATE;
+ }
+
/*
* plantronics headset and Griffin iMic have set adaptive-in
* although it's really not...