Merge master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa

* master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa: (28 commits)
  [ALSA] Kconfig SND_SEQUENCER_OSS help text fix
  [ALSA] Add Aux input switch control for Aureon Universe
  [ALSA] pcxhr - Fix the crash with REV01 board
  [ALSA] sound/pci/hda: use create_singlethread_workqueue()
  [ALSA] hda-intel - Add support of ATI SB600
  [ALSA] cs4281 - Fix the check of timeout in probe
  [ALSA] cs4281 - Fix the check of right channel
  [ALSA] Test volume resolution of usb audio at initialization
  [ALSA] maestro3.c: fix BUG, optimization
  [ALSA] HDA/Realtek: multiple input mux definitions and pin mode additions
  [ALSA] AdLib FM card driver
  [ALSA] Fix / clean up PCM-OSS setup hooks
  [ALSA] Clean up PCM codes (take 2)
  [ALSA] Tiny clean up of PCM codes
  [ALSA] ISA drivers bailing on first !enable[i]
  [ALSA] Remove obsolete kfree_nocheck call
  [ALSA] Remove obsolete kfree_nocheck call
  [ALSA] Add snd-als300 driver for Avance Logic ALS300/ALS300+ soundcards
  [ALSA] Add snd-riptide driver for Conexant Riptide chip
  [ALSA] hda-codec - Fix noisy output wtih AD1986A 3stack model
  ...
This commit is contained in:
Linus Torvalds 2006-04-02 13:08:49 -07:00
commit f27f0a045b
37 changed files with 5660 additions and 474 deletions

View file

@ -120,6 +120,34 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
enable - enable card
- Default: enabled, for PCI and ISA PnP cards
Module snd-adlib
----------------
Module for AdLib FM cards.
port - port # for OPL chip
This module supports multiple cards. It does not support autoprobe, so
the port must be specified. For actual AdLib FM cards it will be 0x388.
Note that this card does not have PCM support and no mixer; only FM
synthesis.
Make sure you have "sbiload" from the alsa-tools package available and,
after loading the module, find out the assigned ALSA sequencer port
number through "sbiload -l". Example output:
Port Client name Port name
64:0 OPL2 FM synth OPL2 FM Port
Load the std.sb and drums.sb patches also supplied by sbiload:
sbiload -p 64:0 std.sb drums.sb
If you use this driver to drive an OPL3, you can use std.o3 and drums.o3
instead. To have the card produce sound, use aplaymidi from alsa-utils:
aplaymidi -p 64:0 foo.mid
Module snd-ad1816a
------------------
@ -190,6 +218,15 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-als300
-----------------
Module for Avance Logic ALS300 and ALS300+
This module supports multiple cards.
The power-management is supported.
Module snd-als4000
------------------
@ -701,6 +738,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
uniwill 3-jack
F1734 2-jack
lg LG laptop (m1 express dual)
lg-lw LG LW20 laptop
test for testing/debugging purpose, almost all controls can be
adjusted. Appearing only when compiled with
$CONFIG_SND_DEBUG=y
@ -1013,6 +1051,23 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-miro
---------------
Module for Miro soundcards: miroSOUND PCM 1 pro,
miroSOUND PCM 12,
miroSOUND PCM 20 Radio.
port - Port # (0x530,0x604,0xe80,0xf40)
irq - IRQ # (5,7,9,10,11)
dma1 - 1st dma # (0,1,3)
dma2 - 2nd dma # (0,1)
mpu_port - MPU-401 port # (0x300,0x310,0x320,0x330)
mpu_irq - MPU-401 irq # (5,7,9,10)
fm_port - FM Port # (0x388)
wss - enable WSS mode
ide - enable onboard ide support
Module snd-mixart
-----------------
@ -1202,6 +1257,20 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
The power-management is supported.
Module snd-riptide
------------------
Module for Conexant Riptide chip
joystick_port - Joystick port # (default: 0x200)
mpu_port - MPU401 port # (default: 0x330)
opl3_port - OPL3 port # (default: 0x388)
This module supports multiple cards.
The driver requires the firmware loader support on kernel.
You need to install the firmware file "riptide.hex" to the standard
firmware path (e.g. /lib/firmware).
Module snd-rme32
----------------

View file

@ -170,7 +170,7 @@ static inline void snd_power_change_state(struct snd_card *card, unsigned int st
}
/* init.c */
int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file *file);
int snd_power_wait(struct snd_card *card, unsigned int power_state);
#else /* ! CONFIG_PM */

View file

@ -369,6 +369,7 @@ struct snd_pcm_substream {
/* -- assigned files -- */
struct snd_pcm_file *file;
struct file *ffile;
void (*pcm_release)(struct snd_pcm_substream *);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
/* -- OSS things -- */
struct snd_pcm_oss_substream oss;
@ -381,13 +382,10 @@ struct snd_pcm_substream {
struct snd_info_entry *proc_prealloc_entry;
/* misc flags */
unsigned int no_mmap_ctrl: 1;
unsigned int hw_opened: 1;
};
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL || ((substream)->oss.file != NULL))
#else
#define SUBSTREAM_BUSY(substream) ((substream)->file != NULL)
#endif
struct snd_pcm_str {
@ -460,7 +458,6 @@ int snd_pcm_info_user(struct snd_pcm_substream *substream,
struct snd_pcm_info __user *info);
int snd_pcm_status(struct snd_pcm_substream *substream,
struct snd_pcm_status *status);
int snd_pcm_prepare(struct snd_pcm_substream *substream);
int snd_pcm_start(struct snd_pcm_substream *substream);
int snd_pcm_stop(struct snd_pcm_substream *substream, int status);
int snd_pcm_drain_done(struct snd_pcm_substream *substream);
@ -468,11 +465,13 @@ int snd_pcm_drain_done(struct snd_pcm_substream *substream);
int snd_pcm_suspend(struct snd_pcm_substream *substream);
int snd_pcm_suspend_all(struct snd_pcm *pcm);
#endif
int snd_pcm_kernel_playback_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
int snd_pcm_kernel_capture_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream, unsigned int cmd, void *arg);
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct snd_pcm_substream **rsubstream);
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream, struct file *file,
struct snd_pcm_substream **rsubstream);
void snd_pcm_release_substream(struct snd_pcm_substream *substream);
int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream, struct file *file,
struct snd_pcm_substream **rsubstream);
void snd_pcm_detach_substream(struct snd_pcm_substream *substream);
void snd_pcm_vma_notify_data(void *client, void *data);
int snd_pcm_mmap_data(struct snd_pcm_substream *substream, struct file *file, struct vm_area_struct *area);

View file

@ -69,8 +69,7 @@ struct snd_pcm_oss_file {
struct snd_pcm_oss_substream {
unsigned oss: 1; /* oss mode */
struct snd_pcm_oss_setup *setup; /* active setup */
struct snd_pcm_oss_file *file;
struct snd_pcm_oss_setup setup; /* active setup */
};
struct snd_pcm_oss_stream {

View file

@ -92,8 +92,9 @@ config SND_SEQUENCER_OSS
Many programs still use the OSS API, so say Y.
To compile this driver as a module, choose M here: the module
will be called snd-seq-oss.
If you choose M in "Sequencer support" (SND_SEQUENCER),
this will be compiled as a module. The module will be called
snd-seq-oss.
config SND_RTCTIMER
tristate "RTC Timer support"

View file

@ -664,7 +664,7 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl,
if (copy_from_user(&info, _info, sizeof(info)))
return -EFAULT;
snd_power_lock(ctl->card);
result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0, NULL);
result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
if (result >= 0)
result = snd_ctl_elem_info(ctl, &info);
snd_power_unlock(ctl->card);
@ -718,7 +718,7 @@ static int snd_ctl_elem_read_user(struct snd_card *card,
return -EFAULT;
}
snd_power_lock(card);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (result >= 0)
result = snd_ctl_elem_read(card, control);
snd_power_unlock(card);
@ -783,7 +783,7 @@ static int snd_ctl_elem_write_user(struct snd_ctl_file *file,
}
card = file->card;
snd_power_lock(card);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (result >= 0)
result = snd_ctl_elem_write(card, file, control);
snd_power_unlock(card);

View file

@ -109,7 +109,7 @@ static int snd_ctl_elem_info_compat(struct snd_ctl_file *ctl,
goto error;
snd_power_lock(ctl->card);
err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0, NULL);
err = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
if (err >= 0)
err = snd_ctl_elem_info(ctl, data);
snd_power_unlock(ctl->card);
@ -294,7 +294,7 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
goto error;
snd_power_lock(card);
err = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (err >= 0)
err = snd_ctl_elem_read(card, data);
snd_power_unlock(card);
@ -320,7 +320,7 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
goto error;
snd_power_lock(card);
err = snd_power_wait(card, SNDRV_CTL_POWER_D0, NULL);
err = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (err >= 0)
err = snd_ctl_elem_write(card, file, data);
snd_power_unlock(card);

View file

@ -722,13 +722,12 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
* snd_power_wait - wait until the power-state is changed.
* @card: soundcard structure
* @power_state: expected power state
* @file: file structure for the O_NONBLOCK check (optional)
*
* Waits until the power-state is changed.
*
* Note: the power lock must be active before call.
*/
int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file *file)
int snd_power_wait(struct snd_card *card, unsigned int power_state)
{
wait_queue_t wait;
int result = 0;
@ -745,12 +744,6 @@ int snd_power_wait(struct snd_card *card, unsigned int power_state, struct file
}
if (snd_power_get_state(card) == power_state)
break;
#if 0 /* block all devices */
if (file && (file->f_flags & O_NONBLOCK)) {
result = -EAGAIN;
break;
}
#endif
set_current_state(TASK_UNINTERRUPTIBLE);
snd_power_unlock(card);
schedule_timeout(30 * HZ);

View file

@ -208,9 +208,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
oss_buffer_size = runtime->oss.mmap_bytes;
}
if (substream->oss.setup &&
substream->oss.setup->period_size > 16)
oss_period_size = substream->oss.setup->period_size;
if (substream->oss.setup.period_size > 16)
oss_period_size = substream->oss.setup.period_size;
else if (runtime->oss.fragshift) {
oss_period_size = 1 << runtime->oss.fragshift;
if (oss_period_size > oss_buffer_size / 2)
@ -252,10 +251,8 @@ static int snd_pcm_oss_period_size(struct snd_pcm_substream *substream,
oss_periods = oss_buffer_size / oss_period_size;
if (substream->oss.setup) {
if (substream->oss.setup->periods > 1)
oss_periods = substream->oss.setup->periods;
}
if (substream->oss.setup.periods > 1)
oss_periods = substream->oss.setup.periods;
s = snd_pcm_hw_param_value_max(slave_params, SNDRV_PCM_HW_PARAM_PERIODS, NULL);
if (runtime->oss.maxfrags && s > runtime->oss.maxfrags)
@ -341,12 +338,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
goto failure;
}
if (atomic_read(&runtime->mmap_count)) {
if (atomic_read(&runtime->mmap_count))
direct = 1;
} else {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
direct = (setup != NULL && setup->direct);
}
else
direct = substream->oss.setup.direct;
_snd_pcm_hw_params_any(sparams);
_snd_pcm_hw_param_setinteger(sparams, SNDRV_PCM_HW_PARAM_PERIODS);
@ -482,7 +477,7 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
1 : runtime->period_size;
sw_params->xfer_align = 1;
if (atomic_read(&runtime->mmap_count) ||
(substream->oss.setup && substream->oss.setup->nosilence)) {
substream->oss.setup.nosilence) {
sw_params->silence_threshold = 0;
sw_params->silence_size = 0;
} else {
@ -843,7 +838,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
buf += tmp;
bytes -= tmp;
xfer += tmp;
if ((substream->oss.setup != NULL && substream->oss.setup->partialfrag) ||
if (substream->oss.setup.partialfrag ||
runtime->oss.buffer_used == runtime->oss.period_bytes) {
tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr,
runtime->oss.buffer_used - runtime->oss.period_ptr, 1);
@ -959,12 +954,12 @@ static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK];
if (substream != NULL) {
snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
substream->runtime->oss.prepare = 1;
}
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
if (substream != NULL) {
snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
substream->runtime->oss.prepare = 1;
}
return 0;
@ -979,7 +974,7 @@ static int snd_pcm_oss_post(struct snd_pcm_oss_file *pcm_oss_file)
if (substream != NULL) {
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
return err;
snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_START, NULL);
}
/* note: all errors from the start action are ignored */
/* OSS apps do not know, how to handle them */
@ -1108,7 +1103,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
__direct:
saved_f_flags = substream->ffile->f_flags;
substream->ffile->f_flags &= ~O_NONBLOCK;
err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DRAIN, NULL);
substream->ffile->f_flags = saved_f_flags;
if (err < 0)
return err;
@ -1120,7 +1115,7 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
return err;
runtime = substream->runtime;
err = snd_pcm_kernel_capture_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
if (err < 0)
return err;
runtime->oss.buffer_used = 0;
@ -1214,12 +1209,10 @@ static int snd_pcm_oss_get_formats(struct snd_pcm_oss_file *pcm_oss_file)
if ((err = snd_pcm_oss_get_active_substream(pcm_oss_file, &substream)) < 0)
return err;
if (atomic_read(&substream->runtime->mmap_count)) {
if (atomic_read(&substream->runtime->mmap_count))
direct = 1;
} else {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
direct = (setup != NULL && setup->direct);
}
else
direct = substream->oss.setup.direct;
if (!direct)
return AFMT_MU_LAW | AFMT_U8 |
AFMT_S16_LE | AFMT_S16_BE |
@ -1437,7 +1430,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
err = snd_pcm_kernel_playback_ioctl(psubstream, cmd, NULL);
err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
if (err < 0)
return err;
}
@ -1458,7 +1451,7 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
cmd = SNDRV_PCM_IOCTL_DROP;
runtime->oss.prepare = 1;
}
err = snd_pcm_kernel_capture_ioctl(csubstream, cmd, NULL);
err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
if (err < 0)
return err;
}
@ -1495,7 +1488,7 @@ static int snd_pcm_oss_get_odelay(struct snd_pcm_oss_file *pcm_oss_file)
runtime = substream->runtime;
if (runtime->oss.params || runtime->oss.prepare)
return 0;
err = snd_pcm_kernel_playback_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DELAY, &delay);
if (err == -EPIPE)
delay = 0; /* hack for broken OSS applications */
else if (err < 0)
@ -1555,8 +1548,7 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
} else {
delay = snd_pcm_oss_bytes(substream, delay);
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
if (setup && setup->buggyptr)
if (substream->oss.setup.buggyptr)
info.blocks = (runtime->oss.buffer_bytes - delay - fixup) / runtime->oss.period_bytes;
else
info.blocks = (delay + fixup) / runtime->oss.period_bytes;
@ -1638,37 +1630,46 @@ static int snd_pcm_oss_get_mapbuf(struct snd_pcm_oss_file *pcm_oss_file, int str
return -EINVAL;
}
static struct snd_pcm_oss_setup *snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream, const char *task_name)
static const char *strip_task_path(const char *path)
{
const char *ptr, *ptrl = NULL;
for (ptr = path; *ptr; ptr++) {
if (*ptr == '/')
ptrl = ptr + 1;
}
return ptrl;
}
static void snd_pcm_oss_look_for_setup(struct snd_pcm *pcm, int stream,
const char *task_name,
struct snd_pcm_oss_setup *rsetup)
{
const char *ptr, *ptrl;
struct snd_pcm_oss_setup *setup;
mutex_lock(&pcm->streams[stream].oss.setup_mutex);
for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
if (!strcmp(setup->task_name, task_name)) {
mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
return setup;
do {
for (setup = pcm->streams[stream].oss.setup_list; setup;
setup = setup->next) {
if (!strcmp(setup->task_name, task_name))
goto out;
}
}
ptr = ptrl = task_name;
while (*ptr) {
if (*ptr == '/')
ptrl = ptr + 1;
ptr++;
}
if (ptrl == task_name) {
goto __not_found;
return NULL;
}
for (setup = pcm->streams[stream].oss.setup_list; setup; setup = setup->next) {
if (!strcmp(setup->task_name, ptrl)) {
mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
return setup;
}
}
__not_found:
} while ((task_name = strip_task_path(task_name)) != NULL);
out:
if (setup)
*rsetup = *setup;
mutex_unlock(&pcm->streams[stream].oss.setup_mutex);
return NULL;
}
static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
runtime = substream->runtime;
vfree(runtime->oss.buffer);
runtime->oss.buffer = NULL;
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
snd_pcm_oss_plugin_clear(substream);
#endif
substream->oss.oss = 0;
}
static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
@ -1678,7 +1679,11 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
struct snd_pcm_runtime *runtime;
substream->oss.oss = 1;
substream->oss.setup = setup;
substream->oss.setup = *setup;
if (setup->nonblock)
substream->ffile->f_flags |= O_NONBLOCK;
else
substream->ffile->f_flags &= ~O_NONBLOCK;
runtime = substream->runtime;
runtime->oss.params = 1;
runtime->oss.trigger = 1;
@ -1697,18 +1702,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
runtime->oss.fragshift = 0;
runtime->oss.maxfrags = 0;
runtime->oss.subdivision = 0;
}
static void snd_pcm_oss_release_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
runtime = substream->runtime;
vfree(runtime->oss.buffer);
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
snd_pcm_oss_plugin_clear(substream);
#endif
substream->oss.file = NULL;
substream->oss.oss = 0;
substream->pcm_release = snd_pcm_oss_release_substream;
}
static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
@ -1717,23 +1711,8 @@ static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)
snd_assert(pcm_oss_file != NULL, return -ENXIO);
for (cidx = 0; cidx < 2; ++cidx) {
struct snd_pcm_substream *substream = pcm_oss_file->streams[cidx];
struct snd_pcm_runtime *runtime;
if (substream == NULL)
continue;
runtime = substream->runtime;
snd_pcm_stream_lock_irq(substream);
if (snd_pcm_running(substream))
snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
snd_pcm_stream_unlock_irq(substream);
if (substream->ffile != NULL) {
if (substream->ops->hw_free != NULL)
substream->ops->hw_free(substream);
substream->ops->close(substream);
substream->ffile = NULL;
}
snd_pcm_oss_release_substream(substream);
snd_pcm_release_substream(substream);
if (substream)
snd_pcm_release_substream(substream);
}
kfree(pcm_oss_file);
return 0;
@ -1743,12 +1722,11 @@ static int snd_pcm_oss_open_file(struct file *file,
struct snd_pcm *pcm,
struct snd_pcm_oss_file **rpcm_oss_file,
int minor,
struct snd_pcm_oss_setup *psetup,
struct snd_pcm_oss_setup *csetup)
struct snd_pcm_oss_setup *setup)
{
int err = 0;
int idx, err;
struct snd_pcm_oss_file *pcm_oss_file;
struct snd_pcm_substream *psubstream = NULL, *csubstream = NULL;
struct snd_pcm_substream *substream;
unsigned int f_mode = file->f_mode;
snd_assert(rpcm_oss_file != NULL, return -EINVAL);
@ -1761,73 +1739,31 @@ static int snd_pcm_oss_open_file(struct file *file,
if ((f_mode & (FMODE_WRITE|FMODE_READ)) == (FMODE_WRITE|FMODE_READ) &&
(pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX))
f_mode = FMODE_WRITE;
if ((f_mode & FMODE_WRITE) && !(psetup && psetup->disable)) {
if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&psubstream)) < 0) {
for (idx = 0; idx < 2; idx++) {
if (setup[idx].disable)
continue;
if (idx == SNDRV_PCM_STREAM_PLAYBACK) {
if (! (f_mode & FMODE_WRITE))
continue;
} else {
if (! (f_mode & FMODE_READ))
continue;
}
err = snd_pcm_open_substream(pcm, idx, file, &substream);
if (err < 0) {
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
pcm_oss_file->streams[SNDRV_PCM_STREAM_PLAYBACK] = psubstream;
}
if ((f_mode & FMODE_READ) && !(csetup && csetup->disable)) {
if ((err = snd_pcm_open_substream(pcm, SNDRV_PCM_STREAM_CAPTURE,
&csubstream)) < 0) {
if (!(f_mode & FMODE_WRITE) || err != -ENODEV) {
snd_pcm_oss_release_file(pcm_oss_file);
return err;
} else {
csubstream = NULL;
}
}
pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE] = csubstream;
pcm_oss_file->streams[idx] = substream;
snd_pcm_oss_init_substream(substream, &setup[idx], minor);
}
if (psubstream == NULL && csubstream == NULL) {
if (! pcm_oss_file->streams[0] && pcm_oss_file->streams[1]) {
snd_pcm_oss_release_file(pcm_oss_file);
return -EINVAL;
}
if (psubstream != NULL) {
psubstream->oss.file = pcm_oss_file;
err = snd_pcm_hw_constraints_init(psubstream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraint_init failed\n");
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
if ((err = psubstream->ops->open(psubstream)) < 0) {
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
psubstream->ffile = file;
err = snd_pcm_hw_constraints_complete(psubstream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraint_complete failed\n");
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
snd_pcm_oss_init_substream(psubstream, psetup, minor);
}
if (csubstream != NULL) {
csubstream->oss.file = pcm_oss_file;
err = snd_pcm_hw_constraints_init(csubstream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraint_init failed\n");
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
if ((err = csubstream->ops->open(csubstream)) < 0) {
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
csubstream->ffile = file;
err = snd_pcm_hw_constraints_complete(csubstream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraint_complete failed\n");
snd_pcm_oss_release_file(pcm_oss_file);
return err;
}
snd_pcm_oss_init_substream(csubstream, csetup, minor);
}
file->private_data = pcm_oss_file;
*rpcm_oss_file = pcm_oss_file;
@ -1852,7 +1788,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
char task_name[32];
struct snd_pcm *pcm;
struct snd_pcm_oss_file *pcm_oss_file;
struct snd_pcm_oss_setup *psetup = NULL, *csetup = NULL;
struct snd_pcm_oss_setup setup[2];
int nonblock;
wait_queue_t wait;
@ -1873,23 +1809,15 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
err = -EFAULT;
goto __error;
}
memset(setup, 0, sizeof(*setup));
if (file->f_mode & FMODE_WRITE)
psetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK, task_name);
snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_PLAYBACK,
task_name, &setup[0]);
if (file->f_mode & FMODE_READ)
csetup = snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE, task_name);
snd_pcm_oss_look_for_setup(pcm, SNDRV_PCM_STREAM_CAPTURE,
task_name, &setup[1]);
nonblock = !!(file->f_flags & O_NONBLOCK);
if (psetup && !psetup->disable) {
if (psetup->nonblock)
nonblock = 1;
else if (psetup->block)
nonblock = 0;
} else if (csetup && !csetup->disable) {
if (csetup->nonblock)
nonblock = 1;
else if (csetup->block)
nonblock = 0;
}
if (!nonblock)
nonblock = nonblock_open;
@ -1898,7 +1826,7 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
mutex_lock(&pcm->open_mutex);
while (1) {
err = snd_pcm_oss_open_file(file, pcm, &pcm_oss_file,
iminor(inode), psetup, csetup);
iminor(inode), setup);
if (err >= 0)
break;
if (err == -EAGAIN) {
@ -2312,13 +2240,8 @@ static void snd_pcm_oss_proc_read(struct snd_info_entry *entry,
static void snd_pcm_oss_proc_free_setup_list(struct snd_pcm_str * pstr)
{
unsigned int idx;
struct snd_pcm_substream *substream;
struct snd_pcm_oss_setup *setup, *setupn;
for (idx = 0, substream = pstr->substream;
idx < pstr->substream_count; idx++, substream = substream->next)
substream->oss.setup = NULL;
for (setup = pstr->oss.setup_list, pstr->oss.setup_list = NULL;
setup; setup = setupn) {
setupn = setup->next;
@ -2379,21 +2302,28 @@ static void snd_pcm_oss_proc_write(struct snd_info_entry *entry,
}
} while (*str);
if (setup == NULL) {
setup = kmalloc(sizeof(struct snd_pcm_oss_setup), GFP_KERNEL);
if (setup) {
if (pstr->oss.setup_list == NULL) {
pstr->oss.setup_list = setup;
} else {
for (setup1 = pstr->oss.setup_list; setup1->next; setup1 = setup1->next);
setup1->next = setup;
}
template.task_name = kstrdup(task_name, GFP_KERNEL);
} else {
setup = kmalloc(sizeof(*setup), GFP_KERNEL);
if (! setup) {
buffer->error = -ENOMEM;
mutex_lock(&pstr->oss.setup_mutex);
return;
}
if (pstr->oss.setup_list == NULL)
pstr->oss.setup_list = setup;
else {
for (setup1 = pstr->oss.setup_list;
setup1->next; setup1 = setup1->next);
setup1->next = setup;
}
template.task_name = kstrdup(task_name, GFP_KERNEL);
if (! template.task_name) {
kfree(setup);
buffer->error = -ENOMEM;
mutex_lock(&pstr->oss.setup_mutex);
return;
}
}
if (setup)
*setup = template;
*setup = template;
mutex_unlock(&pstr->oss.setup_mutex);
}
}

View file

@ -777,8 +777,9 @@ static void snd_pcm_tick_timer_func(unsigned long data)
snd_pcm_tick_elapsed(substream);
}
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct snd_pcm_substream **rsubstream)
int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_str * pstr;
struct snd_pcm_substream *substream;
@ -793,7 +794,7 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
*rsubstream = NULL;
snd_assert(pcm != NULL, return -ENXIO);
pstr = &pcm->streams[stream];
if (pstr->substream == NULL)
if (pstr->substream == NULL || pstr->substream_count == 0)
return -ENODEV;
card = pcm->card;
@ -807,8 +808,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
}
up_read(&card->controls_rwsem);
if (pstr->substream_count == 0)
return -ENODEV;
switch (stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
if (pcm->info_flags & SNDRV_PCM_INFO_HALF_DUPLEX) {
@ -874,12 +873,13 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
substream->runtime = runtime;
substream->private_data = pcm->private_data;
substream->ffile = file;
pstr->substream_opened++;
*rsubstream = substream;
return 0;
}
void snd_pcm_release_substream(struct snd_pcm_substream *substream)
void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
substream->file = NULL;
@ -1111,8 +1111,6 @@ EXPORT_SYMBOL(snd_pcm_link_rwlock);
EXPORT_SYMBOL(snd_pcm_suspend);
EXPORT_SYMBOL(snd_pcm_suspend_all);
#endif
EXPORT_SYMBOL(snd_pcm_kernel_playback_ioctl);
EXPORT_SYMBOL(snd_pcm_kernel_capture_ioctl);
EXPORT_SYMBOL(snd_pcm_kernel_ioctl);
EXPORT_SYMBOL(snd_pcm_mmap_data);
#if SNDRV_PCM_INFO_MMAP_IOMEM

View file

@ -2299,19 +2299,7 @@ snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const v
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
snd_assert(substream->ffile != NULL, return -ENXIO);
nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if (substream->oss.oss) {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
if (setup != NULL) {
if (setup->nonblock)
nonblock = 1;
else if (setup->block)
nonblock = 0;
}
}
#endif
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&
runtime->channels > 1)
@ -2374,19 +2362,7 @@ snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
snd_assert(substream->ffile != NULL, return -ENXIO);
nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if (substream->oss.oss) {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
if (setup != NULL) {
if (setup->nonblock)
nonblock = 1;
else if (setup->block)
nonblock = 0;
}
}
#endif
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
return -EINVAL;
@ -2596,19 +2572,7 @@ snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __u
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
snd_assert(substream->ffile != NULL, return -ENXIO);
nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if (substream->oss.oss) {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
if (setup != NULL) {
if (setup->nonblock)
nonblock = 1;
else if (setup->block)
nonblock = 0;
}
}
#endif
if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);
@ -2665,20 +2629,7 @@ snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,
if (runtime->status->state == SNDRV_PCM_STATE_OPEN)
return -EBADFD;
snd_assert(substream->ffile != NULL, return -ENXIO);
nonblock = !!(substream->ffile->f_flags & O_NONBLOCK);
#if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
if (substream->oss.oss) {
struct snd_pcm_oss_setup *setup = substream->oss.setup;
if (setup != NULL) {
if (setup->nonblock)
nonblock = 1;
else if (setup->block)
nonblock = 0;
}
}
#endif
if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
return -EINVAL;
return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);

View file

@ -1170,7 +1170,7 @@ static int snd_pcm_resume(struct snd_pcm_substream *substream)
int res;
snd_power_lock(card);
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
res = snd_pcm_action_lock_irq(&snd_pcm_action_resume, substream, 0);
snd_power_unlock(card);
return res;
@ -1198,7 +1198,7 @@ static int snd_pcm_xrun(struct snd_pcm_substream *substream)
snd_power_lock(card);
if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (result < 0)
goto _unlock;
}
@ -1313,13 +1313,13 @@ static struct action_ops snd_pcm_action_prepare = {
*
* Prepare the PCM substream to be triggerable.
*/
int snd_pcm_prepare(struct snd_pcm_substream *substream)
static int snd_pcm_prepare(struct snd_pcm_substream *substream)
{
int res;
struct snd_card *card = substream->pcm->card;
snd_power_lock(card);
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile)) >= 0)
if ((res = snd_power_wait(card, SNDRV_CTL_POWER_D0)) >= 0)
res = snd_pcm_action_nonatomic(&snd_pcm_action_prepare, substream, 0);
snd_power_unlock(card);
return res;
@ -1410,7 +1410,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream)
snd_power_lock(card);
if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (result < 0) {
snd_power_unlock(card);
return result;
@ -1533,7 +1533,7 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
snd_power_lock(card);
if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {
result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);
result = snd_power_wait(card, SNDRV_CTL_POWER_D0);
if (result < 0)
goto _unlock;
}
@ -1995,28 +1995,63 @@ static void snd_pcm_remove_file(struct snd_pcm_str *str,
}
}
static int snd_pcm_release_file(struct snd_pcm_file * pcm_file)
static void pcm_release_private(struct snd_pcm_substream *substream)
{
struct snd_pcm_substream *substream;
struct snd_pcm_runtime *runtime;
struct snd_pcm_str * str;
struct snd_pcm_file *pcm_file = substream->file;
snd_assert(pcm_file != NULL, return -ENXIO);
substream = pcm_file->substream;
snd_assert(substream != NULL, return -ENXIO);
runtime = substream->runtime;
str = substream->pstr;
snd_pcm_unlink(substream);
if (substream->ffile != NULL) {
snd_pcm_remove_file(substream->pstr, pcm_file);
kfree(pcm_file);
}
void snd_pcm_release_substream(struct snd_pcm_substream *substream)
{
snd_pcm_drop(substream);
if (substream->pcm_release)
substream->pcm_release(substream);
if (substream->hw_opened) {
if (substream->ops->hw_free != NULL)
substream->ops->hw_free(substream);
substream->ops->close(substream);
substream->ffile = NULL;
substream->hw_opened = 0;
}
snd_pcm_remove_file(str, pcm_file);
snd_pcm_release_substream(substream);
kfree(pcm_file);
snd_pcm_detach_substream(substream);
}
int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
struct file *file,
struct snd_pcm_substream **rsubstream)
{
struct snd_pcm_substream *substream;
int err;
err = snd_pcm_attach_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
substream->no_mmap_ctrl = 0;
err = snd_pcm_hw_constraints_init(substream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_init failed\n");
goto error;
}
if ((err = substream->ops->open(substream)) < 0)
goto error;
substream->hw_opened = 1;
err = snd_pcm_hw_constraints_complete(substream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_complete failed\n");
goto error;
}
*rsubstream = substream;
return 0;
error:
snd_pcm_release_substream(substream);
return err;
}
static int snd_pcm_open_file(struct file *file,
@ -2024,52 +2059,29 @@ static int snd_pcm_open_file(struct file *file,
int stream,
struct snd_pcm_file **rpcm_file)
{
int err = 0;
struct snd_pcm_file *pcm_file;
struct snd_pcm_substream *substream;
struct snd_pcm_str *str;
int err;
snd_assert(rpcm_file != NULL, return -EINVAL);
*rpcm_file = NULL;
err = snd_pcm_open_substream(pcm, stream, file, &substream);
if (err < 0)
return err;
pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
if (pcm_file == NULL) {
snd_pcm_release_substream(substream);
return -ENOMEM;
}
if ((err = snd_pcm_open_substream(pcm, stream, &substream)) < 0) {
kfree(pcm_file);
return err;
}
str = substream->pstr;
substream->file = pcm_file;
substream->no_mmap_ctrl = 0;
substream->pcm_release = pcm_release_private;
pcm_file->substream = substream;
snd_pcm_add_file(str, pcm_file);
err = snd_pcm_hw_constraints_init(substream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_init failed\n");
snd_pcm_release_file(pcm_file);
return err;
}
if ((err = substream->ops->open(substream)) < 0) {
snd_pcm_release_file(pcm_file);
return err;
}
substream->ffile = file;
err = snd_pcm_hw_constraints_complete(substream);
if (err < 0) {
snd_printd("snd_pcm_hw_constraints_complete failed\n");
snd_pcm_release_file(pcm_file);
return err;
}
file->private_data = pcm_file;
*rpcm_file = pcm_file;
return 0;
@ -2158,10 +2170,9 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
snd_assert(substream != NULL, return -ENXIO);
snd_assert(!atomic_read(&substream->runtime->mmap_count), );
pcm = substream->pcm;
snd_pcm_drop(substream);
fasync_helper(-1, file, 0, &substream->runtime->fasync);
mutex_lock(&pcm->open_mutex);
snd_pcm_release_file(pcm_file);
snd_pcm_release_substream(substream);
mutex_unlock(&pcm->open_mutex);
wake_up(&pcm->open_wait);
module_put(pcm->card->module);
@ -2480,11 +2491,6 @@ static int snd_pcm_sync_ptr(struct snd_pcm_substream *substream,
return 0;
}
static int snd_pcm_playback_ioctl1(struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg);
static int snd_pcm_capture_ioctl1(struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg);
static int snd_pcm_common_ioctl1(struct snd_pcm_substream *substream,
unsigned int cmd, void __user *arg)
{
@ -2736,41 +2742,28 @@ static long snd_pcm_capture_ioctl(struct file *file, unsigned int cmd,
return snd_pcm_capture_ioctl1(pcm_file->substream, cmd, (void __user *)arg);
}
int snd_pcm_kernel_playback_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
mm_segment_t fs;
int result;
fs = snd_enter_user();
result = snd_pcm_playback_ioctl1(substream, cmd, (void __user *)arg);
snd_leave_user(fs);
return result;
}
int snd_pcm_kernel_capture_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
mm_segment_t fs;
int result;
fs = snd_enter_user();
result = snd_pcm_capture_ioctl1(substream, cmd, (void __user *)arg);
snd_leave_user(fs);
return result;
}
int snd_pcm_kernel_ioctl(struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
mm_segment_t fs;
int result;
fs = snd_enter_user();
switch (substream->stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
return snd_pcm_kernel_playback_ioctl(substream, cmd, arg);
result = snd_pcm_playback_ioctl1(substream,
cmd, (void __user *)arg);
break;
case SNDRV_PCM_STREAM_CAPTURE:
return snd_pcm_kernel_capture_ioctl(substream, cmd, arg);
result = snd_pcm_capture_ioctl1(substream,
cmd, (void __user *)arg);
break;
default:
return -EINVAL;
result = -EINVAL;
break;
}
snd_leave_user(fs);
return result;
}
static ssize_t snd_pcm_read(struct file *file, char __user *buf, size_t count,

View file

@ -11,6 +11,15 @@ config SND_CS4231_LIB
tristate
select SND_PCM
config SND_ADLIB
tristate "AdLib FM card"
select SND_OPL3_LIB
help
Say Y here to include support for AdLib FM cards.
To compile this driver as a module, choose M here: the module
will be called snd-adlib.
config SND_AD1816A
tristate "Analog Devices SoundPort AD1816A"
depends on SND && PNP && ISA
@ -292,6 +301,20 @@ config SND_OPTI93X
To compile this driver as a module, choose M here: the module
will be called snd-opti93x.
config SND_MIRO
tristate "Miro miroSOUND PCM1pro/PCM12/PCM20radio driver"
depends on SND
select SND_OPL4_LIB
select SND_CS4231_LIB
select SND_MPU401_UART
select SND_PCM
help
Say 'Y' or 'M' to include support for Miro miroSOUND PCM1 pro,
miroSOUND PCM12 and miroSOUND PCM20 Radio soundcards.
To compile this driver as a module, choose M here: the module
will be called snd-miro.
config SND_SB8
tristate "Sound Blaster 1.0/2.0/Pro (8-bit)"
depends on SND

View file

@ -3,6 +3,7 @@
# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
#
snd-adlib-objs := adlib.o
snd-als100-objs := als100.o
snd-azt2320-objs := azt2320.o
snd-cmi8330-objs := cmi8330.o
@ -13,6 +14,7 @@ snd-sgalaxy-objs := sgalaxy.o
snd-sscape-objs := sscape.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
obj-$(CONFIG_SND_ALS100) += snd-als100.o
obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o

161
sound/isa/adlib.c Normal file
View file

@ -0,0 +1,161 @@
/*
* AdLib FM card driver.
*/
#include <sound/driver.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/opl3.h>
#define CRD_NAME "AdLib FM"
#define DRV_NAME "snd_adlib"
MODULE_DESCRIPTION(CRD_NAME);
MODULE_AUTHOR("Rene Herman");
MODULE_LICENSE("GPL");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
module_param_array(index, int, NULL, 0444);
MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
module_param_array(port, long, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
static struct platform_device *devices[SNDRV_CARDS];
static void snd_adlib_free(struct snd_card *card)
{
release_and_free_resource(card->private_data);
}
static int __devinit snd_adlib_probe(struct platform_device *device)
{
struct snd_card *card;
struct snd_opl3 *opl3;
int error;
int i = device->id;
if (port[i] == SNDRV_AUTO_PORT) {
snd_printk(KERN_ERR DRV_NAME ": please specify port\n");
error = -EINVAL;
goto out0;
}
card = snd_card_new(index[i], id[i], THIS_MODULE, 0);
if (!card) {
snd_printk(KERN_ERR DRV_NAME ": could not create card\n");
error = -EINVAL;
goto out0;
}
card->private_data = request_region(port[i], 4, CRD_NAME);
if (!card->private_data) {
snd_printk(KERN_ERR DRV_NAME ": could not grab ports\n");
error = -EBUSY;
goto out1;
}
card->private_free = snd_adlib_free;
error = snd_opl3_create(card, port[i], port[i] + 2, OPL3_HW_AUTO, 1, &opl3);
if (error < 0) {
snd_printk(KERN_ERR DRV_NAME ": could not create OPL\n");
goto out1;
}
error = snd_opl3_hwdep_new(opl3, 0, 0, NULL);
if (error < 0) {
snd_printk(KERN_ERR DRV_NAME ": could not create FM\n");
goto out1;
}
strcpy(card->driver, DRV_NAME);
strcpy(card->shortname, CRD_NAME);
sprintf(card->longname, CRD_NAME " at %#lx", port[i]);
snd_card_set_dev(card, &device->dev);
error = snd_card_register(card);
if (error < 0) {
snd_printk(KERN_ERR DRV_NAME ": could not register card\n");
goto out1;
}
platform_set_drvdata(device, card);
return 0;
out1: snd_card_free(card);
out0: error = -EINVAL; /* FIXME: should be the original error code */
return error;
}
static int __devexit snd_adlib_remove(struct platform_device *device)
{
snd_card_free(platform_get_drvdata(device));
platform_set_drvdata(device, NULL);
return 0;
}
static struct platform_driver snd_adlib_driver = {
.probe = snd_adlib_probe,
.remove = __devexit_p(snd_adlib_remove),
.driver = {
.name = DRV_NAME
}
};
static int __init alsa_card_adlib_init(void)
{
int i, cards;
if (platform_driver_register(&snd_adlib_driver) < 0) {
snd_printk(KERN_ERR DRV_NAME ": could not register driver\n");
return -ENODEV;
}
for (cards = 0, i = 0; i < SNDRV_CARDS; i++) {
struct platform_device *device;
if (!enable[i])
continue;
device = platform_device_register_simple(DRV_NAME, i, NULL, 0);
if (IS_ERR(device))
continue;
devices[i] = device;
cards++;
}
if (!cards) {
#ifdef MODULE
printk(KERN_ERR CRD_NAME " soundcard not found or device busy\n");
#endif
platform_driver_unregister(&snd_adlib_driver);
return -ENODEV;
}
return 0;
}
static void __exit alsa_card_adlib_exit(void)
{
int i;
for (i = 0; i < SNDRV_CARDS; i++)
platform_device_unregister(devices[i]);
platform_driver_unregister(&snd_adlib_driver);
}
module_init(alsa_card_adlib_init);
module_exit(alsa_card_adlib_exit);

View file

@ -693,9 +693,9 @@ static int __init alsa_card_cmi8330_init(void)
if ((err = platform_driver_register(&snd_cmi8330_driver)) < 0)
return err;
for (i = 0; i < SNDRV_CARDS && enable[i]; i++) {
for (i = 0; i < SNDRV_CARDS; i++) {
struct platform_device *device;
if (is_isapnp_selected(i))
if (! enable[i] || is_isapnp_selected(i))
continue;
device = platform_device_register_simple(CMI8330_DRIVER,
i, NULL, 0);

View file

@ -6,8 +6,10 @@
snd-opti92x-ad1848-objs := opti92x-ad1848.o
snd-opti92x-cs4231-objs := opti92x-cs4231.o
snd-opti93x-objs := opti93x.o
snd-miro-objs := miro.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_OPTI92X_AD1848) += snd-opti92x-ad1848.o
obj-$(CONFIG_SND_OPTI92X_CS4231) += snd-opti92x-cs4231.o
obj-$(CONFIG_SND_OPTI93X) += snd-opti93x.o
obj-$(CONFIG_SND_MIRO) += snd-miro.o

1455
sound/isa/opti9xx/miro.c Normal file

File diff suppressed because it is too large Load diff

73
sound/isa/opti9xx/miro.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef _MIRO_H_
#define _MIRO_H_
#define ACI_REG_COMMAND 0 /* write register offset */
#define ACI_REG_STATUS 1 /* read register offset */
#define ACI_REG_BUSY 2 /* busy register offset */
#define ACI_REG_RDS 2 /* PCM20: RDS register offset */
#define ACI_MINTIME 500 /* ACI time out limit */
#define ACI_SET_MUTE 0x0d
#define ACI_SET_POWERAMP 0x0f
#define ACI_SET_TUNERMUTE 0xa3
#define ACI_SET_TUNERMONO 0xa4
#define ACI_SET_IDE 0xd0
#define ACI_SET_WSS 0xd1
#define ACI_SET_SOLOMODE 0xd2
#define ACI_SET_PREAMP 0x03
#define ACI_GET_PREAMP 0x21
#define ACI_WRITE_TUNE 0xa7
#define ACI_READ_TUNERSTEREO 0xa8
#define ACI_READ_TUNERSTATION 0xa9
#define ACI_READ_VERSION 0xf1
#define ACI_READ_IDCODE 0xf2
#define ACI_INIT 0xff
#define ACI_STATUS 0xf0
#define ACI_S_GENERAL 0x00
#define ACI_ERROR_OP 0xdf
/* ACI Mixer */
/* These are the values for the right channel GET registers.
Add an offset of 0x01 for the left channel register.
(left=right+0x01) */
#define ACI_GET_MASTER 0x03
#define ACI_GET_MIC 0x05
#define ACI_GET_LINE 0x07
#define ACI_GET_CD 0x09
#define ACI_GET_SYNTH 0x0b
#define ACI_GET_PCM 0x0d
#define ACI_GET_LINE1 0x10 /* Radio on PCM20 */
#define ACI_GET_LINE2 0x12
#define ACI_GET_EQ1 0x22 /* from Bass ... */
#define ACI_GET_EQ2 0x24
#define ACI_GET_EQ3 0x26
#define ACI_GET_EQ4 0x28
#define ACI_GET_EQ5 0x2a
#define ACI_GET_EQ6 0x2c
#define ACI_GET_EQ7 0x2e /* ... to Treble */
/* And these are the values for the right channel SET registers.
For left channel access you have to add an offset of 0x08.
MASTER is an exception, which needs an offset of 0x01 */
#define ACI_SET_MASTER 0x00
#define ACI_SET_MIC 0x30
#define ACI_SET_LINE 0x31
#define ACI_SET_CD 0x34
#define ACI_SET_SYNTH 0x33
#define ACI_SET_PCM 0x32
#define ACI_SET_LINE1 0x35 /* Radio on PCM20 */
#define ACI_SET_LINE2 0x36
#define ACI_SET_EQ1 0x40 /* from Bass ... */
#define ACI_SET_EQ2 0x41
#define ACI_SET_EQ3 0x42
#define ACI_SET_EQ4 0x43
#define ACI_SET_EQ5 0x44
#define ACI_SET_EQ6 0x45
#define ACI_SET_EQ7 0x46 /* ... to Treble */
#endif /* _MIRO_H_ */

View file

@ -15,6 +15,18 @@ config SND_AD1889
To compile this as a module, choose M here: the module
will be called snd-ad1889.
config SND_ALS300
tristate "Avance Logic ALS300/ALS300+"
depends on SND
select SND_PCM
select SND_AC97_CODEC
select SND_OPL3_LIB
help
Say 'Y' or 'M' to include support for Avance Logic ALS300/ALS300+
To compile this driver as a module, choose M here: the module
will be called snd-als300
config SND_ALS4000
tristate "Avance Logic ALS4000"
depends on SND && ISA_DMA_API
@ -195,8 +207,9 @@ config SND_CS46XX
will be called snd-cs46xx.
config SND_CS46XX_NEW_DSP
bool "Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)"
depends on SND_CS46XX && EXPERIMENTAL
bool "Cirrus Logic (Sound Fusion) New DSP support"
depends on SND_CS46XX
default y
help
Say Y here to use a new DSP image for SPDIF and dual codecs.
@ -466,6 +479,19 @@ config SND_PCXHR
To compile this driver as a module, choose M here: the module
will be called snd-pcxhr.
config SND_RIPTIDE
tristate "Conexant Riptide"
depends on SND
depends on FW_LOADER
select SND_OPL3_LIB
select SND_MPU401_UART
select SND_AC97_CODEC
help
Say 'Y' or 'M' to include support for Conexant Riptide chip.
To compile this driver as a module, choose M here: the module
will be called snd-riptide
config SND_RME32
tristate "RME Digi32, 32/8, 32 PRO"
depends on SND

View file

@ -4,6 +4,7 @@
#
snd-ad1889-objs := ad1889.o
snd-als300-objs := als300.o
snd-als4000-objs := als4000.o
snd-atiixp-objs := atiixp.o
snd-atiixp-modem-objs := atiixp_modem.o
@ -27,6 +28,7 @@ snd-via82xx-modem-objs := via82xx_modem.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AD1889) += snd-ad1889.o
obj-$(CONFIG_SND_ALS300) += snd-als300.o
obj-$(CONFIG_SND_ALS4000) += snd-als4000.o
obj-$(CONFIG_SND_ATIIXP) += snd-atiixp.o
obj-$(CONFIG_SND_ATIIXP_MODEM) += snd-atiixp-modem.o
@ -62,6 +64,7 @@ obj-$(CONFIG_SND) += \
mixart/ \
nm256/ \
pcxhr/ \
riptide/ \
rme9652/ \
trident/ \
ymfpci/ \

866
sound/pci/als300.c Normal file
View file

@ -0,0 +1,866 @@
/*
* als300.c - driver for Avance Logic ALS300/ALS300+ soundcards.
* Copyright (C) 2005 by Ash Willis <ashwillis@programmer.net>
*
* 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
*
* TODO
* 4 channel playback for ALS300+
* gameport
* mpu401
* opl3
*
* NOTES
* The BLOCK_COUNTER registers for the ALS300(+) return a figure related to
* the position in the current period, NOT the whole buffer. It is important
* to know which period we are in so we can calculate the correct pointer.
* This is why we always use 2 periods. We can then use a flip-flop variable
* to keep track of what period we are in.
*/
#include <sound/driver.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <sound/core.h>
#include <sound/control.h>
#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/ac97_codec.h>
#include <sound/opl3.h>
/* snd_als300_set_irq_flag */
#define IRQ_DISABLE 0
#define IRQ_ENABLE 1
/* I/O port layout */
#define AC97_ACCESS 0x00
#define AC97_READ 0x04
#define AC97_STATUS 0x06
#define AC97_DATA_AVAIL (1<<6)
#define AC97_BUSY (1<<7)
#define ALS300_IRQ_STATUS 0x07 /* ALS300 Only */
#define IRQ_PLAYBACK (1<<3)
#define IRQ_CAPTURE (1<<2)
#define GCR_DATA 0x08
#define GCR_INDEX 0x0C
#define ALS300P_DRAM_IRQ_STATUS 0x0D /* ALS300+ Only */
#define MPU_IRQ_STATUS 0x0E /* ALS300 Rev. E+, ALS300+ */
#define ALS300P_IRQ_STATUS 0x0F /* ALS300+ Only */
/* General Control Registers */
#define PLAYBACK_START 0x80
#define PLAYBACK_END 0x81
#define PLAYBACK_CONTROL 0x82
#define TRANSFER_START (1<<16)
#define FIFO_PAUSE (1<<17)
#define RECORD_START 0x83
#define RECORD_END 0x84
#define RECORD_CONTROL 0x85
#define DRAM_WRITE_CONTROL 0x8B
#define WRITE_TRANS_START (1<<16)
#define DRAM_MODE_2 (1<<17)
#define MISC_CONTROL 0x8C
#define IRQ_SET_BIT (1<<15)
#define VMUTE_NORMAL (1<<20)
#define MMUTE_NORMAL (1<<21)
#define MUS_VOC_VOL 0x8E
#define PLAYBACK_BLOCK_COUNTER 0x9A
#define RECORD_BLOCK_COUNTER 0x9B
#define DEBUG_CALLS 1
#define DEBUG_PLAY_REC 1
#if DEBUG_CALLS
#define snd_als300_dbgcalls(format, args...) printk(format, ##args)
#define snd_als300_dbgcallenter() printk(KERN_ERR "--> %s\n", __FUNCTION__)
#define snd_als300_dbgcallleave() printk(KERN_ERR "<-- %s\n", __FUNCTION__)
#else
#define snd_als300_dbgcalls(format, args...)
#define snd_als300_dbgcallenter()
#define snd_als300_dbgcallleave()
#endif
#if DEBUG_PLAY_REC
#define snd_als300_dbgplay(format, args...) printk(KERN_ERR format, ##args)
#else
#define snd_als300_dbgplay(format, args...)
#endif
enum {DEVICE_ALS300, DEVICE_ALS300_PLUS};
MODULE_AUTHOR("Ash Willis <ashwillis@programmer.net>");
MODULE_DESCRIPTION("Avance Logic ALS300");
MODULE_LICENSE("GPL");
MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS300},{Avance Logic,ALS300+}}");
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
struct snd_als300 {
unsigned long port;
spinlock_t reg_lock;
struct snd_card *card;
struct pci_dev *pci;
struct snd_pcm *pcm;
struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream;
struct snd_ac97 *ac97;
struct snd_opl3 *opl3;
struct resource *res_port;
int irq;
int chip_type; /* ALS300 or ALS300+ */
char revision;
};
struct snd_als300_substream_data {
int period_flipflop;
int control_register;
int block_counter_register;
};
static struct pci_device_id snd_als300_ids[] = {
{ 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 },
{ 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, snd_als300_ids);
static inline u32 snd_als300_gcr_read(unsigned long port, unsigned short reg)
{
outb(reg, port+GCR_INDEX);
return inl(port+GCR_DATA);
}
static inline void snd_als300_gcr_write(unsigned long port,
unsigned short reg, u32 val)
{
outb(reg, port+GCR_INDEX);
outl(val, port+GCR_DATA);
}
/* Enable/Disable Interrupts */
static void snd_als300_set_irq_flag(struct snd_als300 *chip, int cmd)
{
u32 tmp = snd_als300_gcr_read(chip->port, MISC_CONTROL);
snd_als300_dbgcallenter();
/* boolean XOR check, since old vs. new hardware have
directly reversed bit setting for ENABLE and DISABLE.
ALS300+ acts like newer versions of ALS300 */
if (((chip->revision > 5 || chip->chip_type == DEVICE_ALS300_PLUS) ^
(cmd == IRQ_ENABLE)) == 0)
tmp |= IRQ_SET_BIT;
else
tmp &= ~IRQ_SET_BIT;
snd_als300_gcr_write(chip->port, MISC_CONTROL, tmp);
snd_als300_dbgcallleave();
}
static int snd_als300_free(struct snd_als300 *chip)
{
snd_als300_dbgcallenter();
snd_als300_set_irq_flag(chip, IRQ_DISABLE);
if (chip->irq >= 0)
free_irq(chip->irq, (void *)chip);
pci_release_regions(chip->pci);
pci_disable_device(chip->pci);
kfree(chip);
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_dev_free(struct snd_device *device)
{
struct snd_als300 *chip = device->device_data;
return snd_als300_free(chip);
}
static irqreturn_t snd_als300_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
u8 status;
struct snd_als300 *chip = dev_id;
struct snd_als300_substream_data *data;
status = inb(chip->port+ALS300_IRQ_STATUS);
if (!status) /* shared IRQ, for different device?? Exit ASAP! */
return IRQ_NONE;
/* ACK everything ASAP */
outb(status, chip->port+ALS300_IRQ_STATUS);
if (status & IRQ_PLAYBACK) {
if (chip->pcm && chip->playback_substream) {
data = chip->playback_substream->runtime->private_data;
data->period_flipflop ^= 1;
snd_pcm_period_elapsed(chip->playback_substream);
snd_als300_dbgplay("IRQ_PLAYBACK\n");
}
}
if (status & IRQ_CAPTURE) {
if (chip->pcm && chip->capture_substream) {
data = chip->capture_substream->runtime->private_data;
data->period_flipflop ^= 1;
snd_pcm_period_elapsed(chip->capture_substream);
snd_als300_dbgplay("IRQ_CAPTURE\n");
}
}
return IRQ_HANDLED;
}
static irqreturn_t snd_als300plus_interrupt(int irq, void *dev_id,
struct pt_regs *regs)
{
u8 general, mpu, dram;
struct snd_als300 *chip = dev_id;
struct snd_als300_substream_data *data;
general = inb(chip->port+ALS300P_IRQ_STATUS);
mpu = inb(chip->port+MPU_IRQ_STATUS);
dram = inb(chip->port+ALS300P_DRAM_IRQ_STATUS);
/* shared IRQ, for different device?? Exit ASAP! */
if ((general == 0) && ((mpu & 0x80) == 0) && ((dram & 0x01) == 0))
return IRQ_NONE;
if (general & IRQ_PLAYBACK) {
if (chip->pcm && chip->playback_substream) {
outb(IRQ_PLAYBACK, chip->port+ALS300P_IRQ_STATUS);
data = chip->playback_substream->runtime->private_data;
data->period_flipflop ^= 1;
snd_pcm_period_elapsed(chip->playback_substream);
snd_als300_dbgplay("IRQ_PLAYBACK\n");
}
}
if (general & IRQ_CAPTURE) {
if (chip->pcm && chip->capture_substream) {
outb(IRQ_CAPTURE, chip->port+ALS300P_IRQ_STATUS);
data = chip->capture_substream->runtime->private_data;
data->period_flipflop ^= 1;
snd_pcm_period_elapsed(chip->capture_substream);
snd_als300_dbgplay("IRQ_CAPTURE\n");
}
}
/* FIXME: Ack other interrupt types. Not important right now as
* those other devices aren't enabled. */
return IRQ_HANDLED;
}
static void __devexit snd_als300_remove(struct pci_dev *pci)
{
snd_als300_dbgcallenter();
snd_card_free(pci_get_drvdata(pci));
pci_set_drvdata(pci, NULL);
snd_als300_dbgcallleave();
}
static unsigned short snd_als300_ac97_read(struct snd_ac97 *ac97,
unsigned short reg)
{
int i;
struct snd_als300 *chip = ac97->private_data;
for (i = 0; i < 1000; i++) {
if ((inb(chip->port+AC97_STATUS) & (AC97_BUSY)) == 0)
break;
udelay(10);
}
outl((reg << 24) | (1 << 31), chip->port+AC97_ACCESS);
for (i = 0; i < 1000; i++) {
if ((inb(chip->port+AC97_STATUS) & (AC97_DATA_AVAIL)) != 0)
break;
udelay(10);
}
return inw(chip->port+AC97_READ);
}
static void snd_als300_ac97_write(struct snd_ac97 *ac97,
unsigned short reg, unsigned short val)
{
int i;
struct snd_als300 *chip = ac97->private_data;
for (i = 0; i < 1000; i++) {
if ((inb(chip->port+AC97_STATUS) & (AC97_BUSY)) == 0)
break;
udelay(10);
}
outl((reg << 24) | val, chip->port+AC97_ACCESS);
}
static int snd_als300_ac97(struct snd_als300 *chip)
{
struct snd_ac97_bus *bus;
struct snd_ac97_template ac97;
int err;
static struct snd_ac97_bus_ops ops = {
.write = snd_als300_ac97_write,
.read = snd_als300_ac97_read,
};
snd_als300_dbgcallenter();
if ((err = snd_ac97_bus(chip->card, 0, &ops, NULL, &bus)) < 0)
return err;
memset(&ac97, 0, sizeof(ac97));
ac97.private_data = chip;
snd_als300_dbgcallleave();
return snd_ac97_mixer(bus, &ac97, &chip->ac97);
}
/* hardware definition
*
* In AC97 mode, we always use 48k/16bit/stereo.
* Any request to change data type is ignored by
* the card when it is running outside of legacy
* mode.
*/
static struct snd_pcm_hardware snd_als300_playback_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 64,
.period_bytes_max = 32 * 1024,
.periods_min = 2,
.periods_max = 2,
};
static struct snd_pcm_hardware snd_als300_capture_hw =
{
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_MMAP_VALID),
.formats = SNDRV_PCM_FMTBIT_S16,
.rates = SNDRV_PCM_RATE_48000,
.rate_min = 48000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 64 * 1024,
.period_bytes_min = 64,
.period_bytes_max = 32 * 1024,
.periods_min = 2,
.periods_max = 2,
};
static int snd_als300_playback_open(struct snd_pcm_substream *substream)
{
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
GFP_KERNEL);
snd_als300_dbgcallenter();
chip->playback_substream = substream;
runtime->hw = snd_als300_playback_hw;
runtime->private_data = data;
data->control_register = PLAYBACK_CONTROL;
data->block_counter_register = PLAYBACK_BLOCK_COUNTER;
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_playback_close(struct snd_pcm_substream *substream)
{
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_als300_substream_data *data;
data = substream->runtime->private_data;
snd_als300_dbgcallenter();
kfree(data);
chip->playback_substream = NULL;
snd_pcm_lib_free_pages(substream);
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_capture_open(struct snd_pcm_substream *substream)
{
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_als300_substream_data *data = kzalloc(sizeof(*data),
GFP_KERNEL);
snd_als300_dbgcallenter();
chip->capture_substream = substream;
runtime->hw = snd_als300_capture_hw;
runtime->private_data = data;
data->control_register = RECORD_CONTROL;
data->block_counter_register = RECORD_BLOCK_COUNTER;
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_capture_close(struct snd_pcm_substream *substream)
{
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_als300_substream_data *data;
data = substream->runtime->private_data;
snd_als300_dbgcallenter();
kfree(data);
chip->capture_substream = NULL;
snd_pcm_lib_free_pages(substream);
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_pcm_hw_params(struct snd_pcm_substream *substream,
snd_pcm_hw_params_t * hw_params)
{
return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
}
static int snd_als300_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
}
static int snd_als300_playback_prepare(struct snd_pcm_substream *substream)
{
u32 tmp;
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
snd_als300_dbgcallenter();
spin_lock_irq(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
tmp &= ~TRANSFER_START;
snd_als300_dbgplay("Period bytes: %d Buffer bytes %d\n",
period_bytes, buffer_bytes);
/* set block size */
tmp &= 0xffff0000;
tmp |= period_bytes - 1;
snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL, tmp);
/* set dma area */
snd_als300_gcr_write(chip->port, PLAYBACK_START,
runtime->dma_addr);
snd_als300_gcr_write(chip->port, PLAYBACK_END,
runtime->dma_addr + buffer_bytes - 1);
spin_unlock_irq(&chip->reg_lock);
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_capture_prepare(struct snd_pcm_substream *substream)
{
u32 tmp;
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
unsigned short period_bytes = snd_pcm_lib_period_bytes(substream);
unsigned short buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
snd_als300_dbgcallenter();
spin_lock_irq(&chip->reg_lock);
tmp = snd_als300_gcr_read(chip->port, RECORD_CONTROL);
tmp &= ~TRANSFER_START;
snd_als300_dbgplay("Period bytes: %d Buffer bytes %d\n", period_bytes,
buffer_bytes);
/* set block size */
tmp &= 0xffff0000;
tmp |= period_bytes - 1;
/* set dma area */
snd_als300_gcr_write(chip->port, RECORD_CONTROL, tmp);
snd_als300_gcr_write(chip->port, RECORD_START,
runtime->dma_addr);
snd_als300_gcr_write(chip->port, RECORD_END,
runtime->dma_addr + buffer_bytes - 1);
spin_unlock_irq(&chip->reg_lock);
snd_als300_dbgcallleave();
return 0;
}
static int snd_als300_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
u32 tmp;
struct snd_als300_substream_data *data;
unsigned short reg;
int ret = 0;
data = substream->runtime->private_data;
reg = data->control_register;
snd_als300_dbgcallenter();
spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
tmp = snd_als300_gcr_read(chip->port, reg);
data->period_flipflop = 1;
snd_als300_gcr_write(chip->port, reg, tmp | TRANSFER_START);
snd_als300_dbgplay("TRIGGER START\n");
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
tmp = snd_als300_gcr_read(chip->port, reg);
snd_als300_gcr_write(chip->port, reg, tmp & ~TRANSFER_START);
snd_als300_dbgplay("TRIGGER STOP\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
tmp = snd_als300_gcr_read(chip->port, reg);
snd_als300_gcr_write(chip->port, reg, tmp | FIFO_PAUSE);
snd_als300_dbgplay("TRIGGER PAUSE\n");
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
tmp = snd_als300_gcr_read(chip->port, reg);
snd_als300_gcr_write(chip->port, reg, tmp & ~FIFO_PAUSE);
snd_als300_dbgplay("TRIGGER RELEASE\n");
break;
default:
snd_als300_dbgplay("TRIGGER INVALID\n");
ret = -EINVAL;
}
spin_unlock(&chip->reg_lock);
snd_als300_dbgcallleave();
return ret;
}
static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
{
u16 current_ptr;
struct snd_als300 *chip = snd_pcm_substream_chip(substream);
struct snd_als300_substream_data *data;
unsigned short period_bytes;
data = substream->runtime->private_data;
period_bytes = snd_pcm_lib_period_bytes(substream);
snd_als300_dbgcallenter();
spin_lock(&chip->reg_lock);
current_ptr = (u16) snd_als300_gcr_read(chip->port,
data->block_counter_register) + 4;
spin_unlock(&chip->reg_lock);
if (current_ptr > period_bytes)
current_ptr = 0;
else
current_ptr = period_bytes - current_ptr;
if (data->period_flipflop == 0)
current_ptr += period_bytes;
snd_als300_dbgplay("Pointer (bytes): %d\n", current_ptr);
snd_als300_dbgcallleave();
return bytes_to_frames(substream->runtime, current_ptr);
}
static struct snd_pcm_ops snd_als300_playback_ops = {
.open = snd_als300_playback_open,
.close = snd_als300_playback_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_als300_pcm_hw_params,
.hw_free = snd_als300_pcm_hw_free,
.prepare = snd_als300_playback_prepare,
.trigger = snd_als300_trigger,
.pointer = snd_als300_pointer,
};
static struct snd_pcm_ops snd_als300_capture_ops = {
.open = snd_als300_capture_open,
.close = snd_als300_capture_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_als300_pcm_hw_params,
.hw_free = snd_als300_pcm_hw_free,
.prepare = snd_als300_capture_prepare,
.trigger = snd_als300_trigger,
.pointer = snd_als300_pointer,
};
static int __devinit snd_als300_new_pcm(struct snd_als300 *chip)
{
struct snd_pcm *pcm;
int err;
snd_als300_dbgcallenter();
err = snd_pcm_new(chip->card, "ALS300", 0, 1, 1, &pcm);
if (err < 0)
return err;
pcm->private_data = chip;
strcpy(pcm->name, "ALS300");
chip->pcm = pcm;
/* set operators */
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
&snd_als300_playback_ops);
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
&snd_als300_capture_ops);
/* pre-allocation of buffers */
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci), 64*1024, 64*1024);
snd_als300_dbgcallleave();
return 0;
}
static void snd_als300_init(struct snd_als300 *chip)
{
unsigned long flags;
u32 tmp;
snd_als300_dbgcallenter();
spin_lock_irqsave(&chip->reg_lock, flags);
chip->revision = (snd_als300_gcr_read(chip->port, MISC_CONTROL) >> 16)
& 0x0000000F;
/* Setup DRAM */
tmp = snd_als300_gcr_read(chip->port, DRAM_WRITE_CONTROL);
snd_als300_gcr_write(chip->port, DRAM_WRITE_CONTROL,
(tmp | DRAM_MODE_2)
& ~WRITE_TRANS_START);
/* Enable IRQ output */
snd_als300_set_irq_flag(chip, IRQ_ENABLE);
/* Unmute hardware devices so their outputs get routed to
* the onboard mixer */
tmp = snd_als300_gcr_read(chip->port, MISC_CONTROL);
snd_als300_gcr_write(chip->port, MISC_CONTROL,
tmp | VMUTE_NORMAL | MMUTE_NORMAL);
/* Reset volumes */
snd_als300_gcr_write(chip->port, MUS_VOC_VOL, 0);
/* Make sure playback transfer is stopped */
tmp = snd_als300_gcr_read(chip->port, PLAYBACK_CONTROL);
snd_als300_gcr_write(chip->port, PLAYBACK_CONTROL,
tmp & ~TRANSFER_START);
spin_unlock_irqrestore(&chip->reg_lock, flags);
snd_als300_dbgcallleave();
}
static int __devinit snd_als300_create(snd_card_t *card,
struct pci_dev *pci, int chip_type,
struct snd_als300 **rchip)
{
struct snd_als300 *chip;
void *irq_handler;
int err;
static snd_device_ops_t ops = {
.dev_free = snd_als300_dev_free,
};
*rchip = NULL;
snd_als300_dbgcallenter();
if ((err = pci_enable_device(pci)) < 0)
return err;
if (pci_set_dma_mask(pci, 0x0fffffff) < 0 ||
pci_set_consistent_dma_mask(pci, 0x0fffffff) < 0) {
printk(KERN_ERR "error setting 28bit DMA mask\n");
pci_disable_device(pci);
return -ENXIO;
}
pci_set_master(pci);
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (chip == NULL) {
pci_disable_device(pci);
return -ENOMEM;
}
chip->card = card;
chip->pci = pci;
chip->irq = -1;
chip->chip_type = chip_type;
spin_lock_init(&chip->reg_lock);
if ((err = pci_request_regions(pci, "ALS300")) < 0) {
kfree(chip);
pci_disable_device(pci);
return err;
}
chip->port = pci_resource_start(pci, 0);
if (chip->chip_type == DEVICE_ALS300_PLUS)
irq_handler = snd_als300plus_interrupt;
else
irq_handler = snd_als300_interrupt;
if (request_irq(pci->irq, irq_handler, SA_INTERRUPT|SA_SHIRQ,
card->shortname, (void *)chip)) {
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
snd_als300_free(chip);
return -EBUSY;
}
chip->irq = pci->irq;
snd_als300_init(chip);
if (snd_als300_ac97(chip) < 0) {
snd_printk(KERN_WARNING "Could not create ac97\n");
snd_als300_free(chip);
return err;
}
if ((err = snd_als300_new_pcm(chip)) < 0) {
snd_printk(KERN_WARNING "Could not create PCM\n");
snd_als300_free(chip);
return err;
}
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL,
chip, &ops)) < 0) {
snd_als300_free(chip);
return err;
}
snd_card_set_dev(card, &pci->dev);
*rchip = chip;
snd_als300_dbgcallleave();
return 0;
}
#ifdef CONFIG_PM
static int snd_als300_suspend(struct pci_dev *pci, pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_als300 *chip = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend_all(chip->pcm);
snd_ac97_suspend(chip->ac97);
pci_set_power_state(pci, PCI_D3hot);
pci_disable_device(pci);
pci_save_state(pci);
return 0;
}
static int snd_als300_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct snd_als300 *chip = card->private_data;
pci_restore_state(pci);
pci_enable_device(pci);
pci_set_power_state(pci, PCI_D0);
pci_set_master(pci);
snd_als300_init(chip);
snd_ac97_resume(chip->ac97);
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif
static int __devinit snd_als300_probe(struct pci_dev *pci,
const struct pci_device_id *pci_id)
{
static int dev;
struct snd_card *card;
struct snd_als300 *chip;
int err, chip_type;
if (dev >= SNDRV_CARDS)
return -ENODEV;
if (!enable[dev]) {
dev++;
return -ENOENT;
}
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
if (card == NULL)
return -ENOMEM;
chip_type = pci_id->driver_data;
if ((err = snd_als300_create(card, pci, chip_type, &chip)) < 0) {
snd_card_free(card);
return err;
}
card->private_data = chip;
strcpy(card->driver, "ALS300");
if (chip->chip_type == DEVICE_ALS300_PLUS)
/* don't know much about ALS300+ yet
* print revision number for now */
sprintf(card->shortname, "ALS300+ (Rev. %d)", chip->revision);
else
sprintf(card->shortname, "ALS300 (Rev. %c)", 'A' +
chip->revision - 1);
sprintf(card->longname, "%s at 0x%lx irq %i",
card->shortname, chip->port, chip->irq);
if ((err = snd_card_register(card)) < 0) {
snd_card_free(card);
return err;
}
pci_set_drvdata(pci, card);
dev++;
return 0;
}
static struct pci_driver driver = {
.name = "ALS300",
.id_table = snd_als300_ids,
.probe = snd_als300_probe,
.remove = __devexit_p(snd_als300_remove),
#ifdef CONFIG_PM
.suspend = snd_als300_suspend,
.resume = snd_als300_resume,
#endif
};
static int __init alsa_card_als300_init(void)
{
return pci_register_driver(&driver);
}
static void __exit alsa_card_als300_exit(void)
{
pci_unregister_driver(&driver);
}
module_init(alsa_card_als300_init)
module_exit(alsa_card_als300_exit)

View file

@ -1046,7 +1046,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
snd_cs4281_pokeBA0(chip, regL, volL);
change = 1;
}
if (ucontrol->value.integer.value[0] != volL) {
if (ucontrol->value.integer.value[1] != volR) {
volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK);
snd_cs4281_pokeBA0(chip, regR, volR);
change = 1;
@ -1416,7 +1416,7 @@ static int __devinit snd_cs4281_create(struct snd_card *card,
static int snd_cs4281_chip_init(struct cs4281 *chip)
{
unsigned int tmp;
int timeout;
unsigned long end_time;
int retry_count = 2;
/* Having EPPMC.FPDN=1 prevent proper chip initialisation */
@ -1496,7 +1496,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
/*
* Wait for the DLL ready signal from the clock logic.
*/
timeout = 100;
end_time = jiffies + HZ;
do {
/*
* Read the AC97 status register to see if we've seen a CODEC
@ -1504,8 +1504,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
*/
if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY)
goto __ok0;
msleep(1);
} while (timeout-- > 0);
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
snd_printk(KERN_ERR "DLLRDY not seen\n");
return -EIO;
@ -1522,7 +1522,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
/*
* Wait for the codec ready signal from the AC97 codec.
*/
timeout = 100;
end_time = jiffies + HZ;
do {
/*
* Read the AC97 status register to see if we've seen a CODEC
@ -1530,20 +1530,20 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
*/
if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY)
goto __ok1;
msleep(1);
} while (timeout-- > 0);
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
return -EIO;
__ok1:
if (chip->dual_codec) {
timeout = 100;
end_time = jiffies + HZ;
do {
if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY)
goto __codec2_ok;
msleep(1);
} while (timeout-- > 0);
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
chip->dual_codec = 0;
__codec2_ok: ;
@ -1561,7 +1561,7 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
* the codec is pumping ADC data across the AC-link.
*/
timeout = 100;
end_time = jiffies + HZ;
do {
/*
* Read the input slot valid register and see if input slots 3
@ -1569,8 +1569,8 @@ static int snd_cs4281_chip_init(struct cs4281 *chip)
*/
if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4)))
goto __ok2;
msleep(1);
} while (timeout-- > 0);
schedule_timeout_uninterruptible(1);
} while (time_after_eq(end_time, jiffies));
if (--retry_count > 0)
goto __retry;

View file

@ -295,7 +295,7 @@ static int init_unsol_queue(struct hda_bus *bus)
snd_printk(KERN_ERR "hda_codec: can't allocate unsolicited queue\n");
return -ENOMEM;
}
unsol->workq = create_workqueue("hda_codec");
unsol->workq = create_singlethread_workqueue("hda_codec");
if (! unsol->workq) {
snd_printk(KERN_ERR "hda_codec: can't create workqueue\n");
kfree(unsol);

View file

@ -81,6 +81,7 @@ MODULE_SUPPORTED_DEVICE("{{Intel, ICH6},"
"{Intel, ESB2},"
"{Intel, ICH8},"
"{ATI, SB450},"
"{ATI, SB600},"
"{VIA, VT8251},"
"{VIA, VT8237A},"
"{SiS, SIS966},"
@ -1619,6 +1620,7 @@ static struct pci_device_id azx_ids[] = {
{ 0x8086, 0x269a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ESB2 */
{ 0x8086, 0x284b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ICH }, /* ICH8 */
{ 0x1002, 0x437b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB450 */
{ 0x1002, 0x4383, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ATI }, /* ATI SB600 */
{ 0x1106, 0x3288, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_VIA }, /* VIA VT8251/VT8237A */
{ 0x1039, 0x7502, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_SIS }, /* SIS966 */
{ 0x10b9, 0x5461, PCI_ANY_ID, PCI_ANY_ID, 0, 0, AZX_DRIVER_ULI }, /* ULI M5461 */

View file

@ -44,6 +44,7 @@ struct ad198x_spec {
* dig_out_nid and hp_nid are optional
*/
unsigned int cur_eapd;
unsigned int need_dac_fix;
/* capture */
unsigned int num_adc_nids;
@ -836,10 +837,14 @@ static int patch_ad1986a(struct hda_codec *codec)
case AD1986A_3STACK:
spec->num_mixers = 2;
spec->mixers[1] = ad1986a_3st_mixers;
spec->num_init_verbs = 2;
spec->num_init_verbs = 3;
spec->init_verbs[1] = ad1986a_3st_init_verbs;
spec->init_verbs[2] = ad1986a_ch2_init;
spec->channel_mode = ad1986a_modes;
spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
spec->need_dac_fix = 1;
spec->multiout.max_channels = 2;
spec->multiout.num_dacs = 1;
break;
case AD1986A_LAPTOP:
spec->mixers[0] = ad1986a_laptop_mixers;
@ -1555,6 +1560,8 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct ad198x_spec *spec = codec->spec;
if (spec->need_dac_fix)
spec->multiout.num_dacs = spec->multiout.max_channels / 2;
return snd_hda_ch_mode_put(codec, ucontrol, spec->channel_mode,
spec->num_channel_mode, &spec->multiout.max_channels);
}

View file

@ -52,6 +52,7 @@ enum {
ALC880_CLEVO,
ALC880_TCL_S700,
ALC880_LG,
ALC880_LG_LW,
#ifdef CONFIG_SND_DEBUG
ALC880_TEST,
#endif
@ -131,6 +132,7 @@ struct alc_spec {
hda_nid_t dig_in_nid; /* digital-in NID; optional */
/* capture source */
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
unsigned int cur_mux[3];
@ -172,6 +174,7 @@ struct alc_config_preset {
hda_nid_t dig_in_nid;
unsigned int num_channel_mode;
const struct hda_channel_mode *channel_mode;
unsigned int num_mux_defs;
const struct hda_input_mux *input_mux;
void (*unsol_event)(struct hda_codec *, unsigned int);
void (*init_hook)(struct hda_codec *);
@ -185,7 +188,10 @@ static int alc_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
return snd_hda_input_mux_info(spec->input_mux, uinfo);
unsigned int mux_idx = snd_ctl_get_ioffidx(kcontrol, &uinfo->id);
if (mux_idx >= spec->num_mux_defs)
mux_idx = 0;
return snd_hda_input_mux_info(&spec->input_mux[mux_idx], uinfo);
}
static int alc_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
@ -203,7 +209,8 @@ static int alc_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct alc_spec *spec = codec->spec;
unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
unsigned int mux_idx = adc_idx >= spec->num_mux_defs ? 0 : adc_idx;
return snd_hda_input_mux_put(codec, &spec->input_mux[mux_idx], ucontrol,
spec->adc_nids[adc_idx], &spec->cur_mux[adc_idx]);
}
@ -245,7 +252,8 @@ static int alc_ch_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_va
* states other than HiZ (eg: PIN_VREFxx) and revert to HiZ if any of these
* are requested. Therefore order this list so that this behaviour will not
* cause problems when mixer clients move through the enum sequentially.
* NIDs 0x0f and 0x10 have been observed to have this behaviour.
* NIDs 0x0f and 0x10 have been observed to have this behaviour as of
* March 2006.
*/
static char *alc_pin_mode_names[] = {
"Mic 50pc bias", "Mic 80pc bias",
@ -255,19 +263,27 @@ static unsigned char alc_pin_mode_values[] = {
PIN_VREF50, PIN_VREF80, PIN_IN, PIN_OUT, PIN_HP,
};
/* The control can present all 5 options, or it can limit the options based
* in the pin being assumed to be exclusively an input or an output pin.
* in the pin being assumed to be exclusively an input or an output pin. In
* addition, "input" pins may or may not process the mic bias option
* depending on actual widget capability (NIDs 0x0f and 0x10 don't seem to
* accept requests for bias as of chip versions up to March 2006) and/or
* wiring in the computer.
*/
#define ALC_PIN_DIR_IN 0x00
#define ALC_PIN_DIR_OUT 0x01
#define ALC_PIN_DIR_INOUT 0x02
#define ALC_PIN_DIR_IN 0x00
#define ALC_PIN_DIR_OUT 0x01
#define ALC_PIN_DIR_INOUT 0x02
#define ALC_PIN_DIR_IN_NOMICBIAS 0x03
#define ALC_PIN_DIR_INOUT_NOMICBIAS 0x04
/* Info about the pin modes supported by the three different pin directions.
/* Info about the pin modes supported by the different pin direction modes.
* For each direction the minimum and maximum values are given.
*/
static signed char alc_pin_mode_dir_info[3][2] = {
static signed char alc_pin_mode_dir_info[5][2] = {
{ 0, 2 }, /* ALC_PIN_DIR_IN */
{ 3, 4 }, /* ALC_PIN_DIR_OUT */
{ 0, 4 }, /* ALC_PIN_DIR_INOUT */
{ 2, 2 }, /* ALC_PIN_DIR_IN_NOMICBIAS */
{ 2, 4 }, /* ALC_PIN_DIR_INOUT_NOMICBIAS */
};
#define alc_pin_mode_min(_dir) (alc_pin_mode_dir_info[_dir][0])
#define alc_pin_mode_max(_dir) (alc_pin_mode_dir_info[_dir][1])
@ -329,9 +345,10 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
* input modes.
*
* Dynamically switching the input/output buffers probably
* reduces noise slightly, particularly on input. However,
* havingboth input and output buffers enabled
* simultaneously doesn't seem to be problematic.
* reduces noise slightly (particularly on input) so we'll
* do it. However, having both input and output buffers
* enabled simultaneously doesn't seem to be problematic if
* this turns out to be necessary in the future.
*/
if (val <= 2) {
snd_hda_codec_write(codec,nid,0,AC_VERB_SET_AMP_GAIN_MUTE,
@ -483,6 +500,9 @@ static void setup_preset(struct alc_spec *spec, const struct alc_config_preset *
spec->multiout.dig_out_nid = preset->dig_out_nid;
spec->multiout.hp_nid = preset->hp_nid;
spec->num_mux_defs = preset->num_mux_defs;
if (! spec->num_mux_defs)
spec->num_mux_defs = 1;
spec->input_mux = preset->input_mux;
spec->num_adc_nids = preset->num_adc_nids;
@ -1426,6 +1446,82 @@ static void alc880_lg_unsol_event(struct hda_codec *codec, unsigned int res)
alc880_lg_automute(codec);
}
/*
* LG LW20
*
* Pin assignment:
* Speaker-out: 0x14
* Mic-In: 0x18
* Built-in Mic-In: 0x19 (?)
* HP-Out: 0x1b
* SPDIF-Out: 0x1e
*/
/* seems analog CD is not working */
static struct hda_input_mux alc880_lg_lw_capture_source = {
.num_items = 2,
.items = {
{ "Mic", 0x0 },
{ "Internal Mic", 0x1 },
},
};
static struct snd_kcontrol_new alc880_lg_lw_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Master Playback Switch", 0x0c, 2, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_MUTE("Mic Playback Switch", 0x0b, 0x0, HDA_INPUT),
HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x0b, 0x01, HDA_INPUT),
HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x0b, 0x01, HDA_INPUT),
{ } /* end */
};
static struct hda_verb alc880_lg_lw_init_verbs[] = {
/* set capture source to mic-in */
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x09, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
{0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(7)},
/* speaker-out */
{0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* HP-out */
{0x13, AC_VERB_SET_CONNECT_SEL, 0x00},
{0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
{0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* mic-in to input */
{0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* built-in mic */
{0x19, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
{0x19, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
/* jack sense */
{0x1b, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | 0x1},
{ }
};
/* toggle speaker-output according to the hp-jack state */
static void alc880_lg_lw_automute(struct hda_codec *codec)
{
unsigned int present;
present = snd_hda_codec_read(codec, 0x1b, 0,
AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
snd_hda_codec_amp_update(codec, 0x14, 0, HDA_OUTPUT, 0,
0x80, present ? 0x80 : 0);
snd_hda_codec_amp_update(codec, 0x14, 1, HDA_OUTPUT, 0,
0x80, present ? 0x80 : 0);
}
static void alc880_lg_lw_unsol_event(struct hda_codec *codec, unsigned int res)
{
/* Looks like the unsol event is incompatible with the standard
* definition. 4bit tag is placed at 28 bit!
*/
if ((res >> 28) == 0x01)
alc880_lg_lw_automute(codec);
}
/*
* Common callbacks
*/
@ -2078,6 +2174,9 @@ static struct hda_board_config alc880_cfg_tbl[] = {
{ .modelname = "lg", .config = ALC880_LG },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x003b, .config = ALC880_LG },
{ .modelname = "lg-lw", .config = ALC880_LG_LW },
{ .pci_subvendor = 0x1854, .pci_subdevice = 0x0018, .config = ALC880_LG_LW },
#ifdef CONFIG_SND_DEBUG
{ .modelname = "test", .config = ALC880_TEST },
#endif
@ -2268,6 +2367,19 @@ static struct alc_config_preset alc880_presets[] = {
.unsol_event = alc880_lg_unsol_event,
.init_hook = alc880_lg_automute,
},
[ALC880_LG_LW] = {
.mixers = { alc880_lg_lw_mixer },
.init_verbs = { alc880_volume_init_verbs,
alc880_lg_lw_init_verbs },
.num_dacs = 1,
.dac_nids = alc880_dac_nids,
.dig_out_nid = ALC880_DIGOUT_NID,
.num_channel_mode = ARRAY_SIZE(alc880_2_jack_modes),
.channel_mode = alc880_2_jack_modes,
.input_mux = &alc880_lg_lw_capture_source,
.unsol_event = alc880_lg_lw_unsol_event,
.init_hook = alc880_lg_lw_automute,
},
#ifdef CONFIG_SND_DEBUG
[ALC880_TEST] = {
.mixers = { alc880_test_mixer },
@ -2593,6 +2705,7 @@ static int alc880_parse_auto_config(struct hda_codec *codec)
spec->init_verbs[spec->num_init_verbs++] = alc880_volume_init_verbs;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
return 1;
@ -2722,30 +2835,56 @@ static struct hda_input_mux alc260_capture_source = {
};
/* On Fujitsu S702x laptops capture only makes sense from Mic/LineIn jack,
* headphone jack and the internal CD lines.
* headphone jack and the internal CD lines since these are the only pins at
* which audio can appear. For flexibility, also allow the option of
* recording the mixer output on the second ADC (ADC0 doesn't have a
* connection to the mixer output).
*/
static struct hda_input_mux alc260_fujitsu_capture_source = {
.num_items = 3,
.items = {
{ "Mic/Line", 0x0 },
{ "CD", 0x4 },
{ "Headphone", 0x2 },
static struct hda_input_mux alc260_fujitsu_capture_sources[2] = {
{
.num_items = 3,
.items = {
{ "Mic/Line", 0x0 },
{ "CD", 0x4 },
{ "Headphone", 0x2 },
},
},
{
.num_items = 4,
.items = {
{ "Mic/Line", 0x0 },
{ "CD", 0x4 },
{ "Headphone", 0x2 },
{ "Mixer", 0x5 },
},
},
};
/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configutation to
* the Fujitsu S702x, but jacks are marked differently. We won't allow
* retasking the Headphone jack, so it won't be available here.
/* Acer TravelMate(/Extensa/Aspire) notebooks have similar configuration to
* the Fujitsu S702x, but jacks are marked differently.
*/
static struct hda_input_mux alc260_acer_capture_source = {
.num_items = 3,
.items = {
{ "Mic", 0x0 },
{ "Line", 0x2 },
{ "CD", 0x4 },
static struct hda_input_mux alc260_acer_capture_sources[2] = {
{
.num_items = 4,
.items = {
{ "Mic", 0x0 },
{ "Line", 0x2 },
{ "CD", 0x4 },
{ "Headphone", 0x5 },
},
},
{
.num_items = 5,
.items = {
{ "Mic", 0x0 },
{ "Line", 0x2 },
{ "CD", 0x4 },
{ "Headphone", 0x6 },
{ "Mixer", 0x5 },
},
},
};
/*
* This is just place-holder, so there's something for alc_build_pcms to look
* at when it calculates the maximum number of channels. ALC260 has no mixer
@ -2806,6 +2945,9 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
{ } /* end */
};
/* Fujitsu S702x series laptops. ALC260 pin usage: Mic/Line jack = 0x12,
* HP jack = 0x14, CD audio = 0x16, internal speaker = 0x10.
*/
static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
HDA_CODEC_VOLUME("Headphone Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Headphone Playback Switch", 0x08, 2, HDA_INPUT),
@ -2822,9 +2964,28 @@ static struct snd_kcontrol_new alc260_fujitsu_mixer[] = {
{ } /* end */
};
/* Mixer for Acer TravelMate(/Extensa/Aspire) notebooks. Note that current
* versions of the ALC260 don't act on requests to enable mic bias from NID
* 0x0f (used to drive the headphone jack in these laptops). The ALC260
* datasheet doesn't mention this restriction. At this stage it's not clear
* whether this behaviour is intentional or is a hardware bug in chip
* revisions available in early 2006. Therefore for now allow the
* "Headphone Jack Mode" control to span all choices, but if it turns out
* that the lack of mic bias for this NID is intentional we could change the
* mode from ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
*
* In addition, Acer TravelMate(/Extensa/Aspire) notebooks in early 2006
* don't appear to make the mic bias available from the "line" jack, even
* though the NID used for this jack (0x14) can supply it. The theory is
* that perhaps Acer have included blocking capacitors between the ALC260
* and the output jack. If this turns out to be the case for all such
* models the "Line Jack Mode" mode could be changed from ALC_PIN_DIR_INOUT
* to ALC_PIN_DIR_INOUT_NOMICBIAS.
*/
static struct snd_kcontrol_new alc260_acer_mixer[] = {
HDA_CODEC_VOLUME("Master Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("Master Playback Switch", 0x08, 2, HDA_INPUT),
ALC_PIN_MODE("Headphone Jack Mode", 0x0f, ALC_PIN_DIR_INOUT),
HDA_CODEC_VOLUME("CD Playback Volume", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_MUTE("CD Playback Switch", 0x07, 0x04, HDA_INPUT),
HDA_CODEC_VOLUME("Mic Playback Volume", 0x07, 0x0, HDA_INPUT),
@ -3038,7 +3199,8 @@ static struct hda_verb alc260_hp_3013_init_verbs[] = {
};
/* Initialisation sequence for ALC260 as configured in Fujitsu S702x
* laptops.
* laptops. ALC260 pin usage: Mic/Line jack = 0x12, HP jack = 0x14, CD
* audio = 0x16, internal speaker = 0x10.
*/
static struct hda_verb alc260_fujitsu_init_verbs[] = {
/* Disable all GPIOs */
@ -3185,10 +3347,10 @@ static struct hda_verb alc260_acer_init_verbs[] = {
{0x04, AC_VERB_SET_CONNECT_SEL, 0x00},
/* Do similar with the second ADC: mute capture input amp and
* set ADC connection to line (on line1 pin)
* set ADC connection to mic to match ALSA's default state.
*/
{0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
{0x05, AC_VERB_SET_CONNECT_SEL, 0x02},
{0x05, AC_VERB_SET_CONNECT_SEL, 0x00},
/* Mute all inputs to mixer widget (even unconnected ones) */
{0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)}, /* mic1 pin */
@ -3213,26 +3375,35 @@ static hda_nid_t alc260_test_dac_nids[1] = {
static hda_nid_t alc260_test_adc_nids[2] = {
0x04, 0x05,
};
/* This is a bit messy since the two input muxes in the ALC260 have slight
* variations in their signal assignments. The ideal way to deal with this
* is to extend alc_spec.input_mux to allow a different input MUX for each
* ADC. For the purposes of the test model it's sufficient to just list
* both options for affected signal indices. The separate input mux
* functionality only needs to be considered if a model comes along which
* actually uses signals 0x5, 0x6 and 0x7 for something which makes sense to
* record.
/* For testing the ALC260, each input MUX needs its own definition since
* the signal assignments are different. This assumes that the first ADC
* is NID 0x04.
*/
static struct hda_input_mux alc260_test_capture_source = {
.num_items = 8,
.items = {
{ "MIC1 pin", 0x0 },
{ "MIC2 pin", 0x1 },
{ "LINE1 pin", 0x2 },
{ "LINE2 pin", 0x3 },
{ "CD pin", 0x4 },
{ "LINE-OUT pin (cap1), Mixer (cap2)", 0x5 },
{ "HP-OUT pin (cap1), LINE-OUT pin (cap2)", 0x6 },
{ "HP-OUT pin (cap2 only)", 0x7 },
static struct hda_input_mux alc260_test_capture_sources[2] = {
{
.num_items = 7,
.items = {
{ "MIC1 pin", 0x0 },
{ "MIC2 pin", 0x1 },
{ "LINE1 pin", 0x2 },
{ "LINE2 pin", 0x3 },
{ "CD pin", 0x4 },
{ "LINE-OUT pin", 0x5 },
{ "HP-OUT pin", 0x6 },
},
},
{
.num_items = 8,
.items = {
{ "MIC1 pin", 0x0 },
{ "MIC2 pin", 0x1 },
{ "LINE1 pin", 0x2 },
{ "LINE2 pin", 0x3 },
{ "CD pin", 0x4 },
{ "Mixer", 0x5 },
{ "LINE-OUT pin", 0x6 },
{ "HP-OUT pin", 0x7 },
},
},
};
static struct snd_kcontrol_new alc260_test_mixer[] = {
@ -3244,7 +3415,17 @@ static struct snd_kcontrol_new alc260_test_mixer[] = {
HDA_CODEC_VOLUME("LOUT1 Playback Volume", 0x08, 0x0, HDA_OUTPUT),
HDA_BIND_MUTE("LOUT1 Playback Switch", 0x08, 2, HDA_INPUT),
/* Modes for retasking pin widgets */
/* Modes for retasking pin widgets
* Note: the ALC260 doesn't seem to act on requests to enable mic
* bias from NIDs 0x0f and 0x10. The ALC260 datasheet doesn't
* mention this restriction. At this stage it's not clear whether
* this behaviour is intentional or is a hardware bug in chip
* revisions available at least up until early 2006. Therefore for
* now allow the "HP-OUT" and "LINE-OUT" Mode controls to span all
* choices, but if it turns out that the lack of mic bias for these
* NIDs is intentional we could change their modes from
* ALC_PIN_DIR_INOUT to ALC_PIN_DIR_INOUT_NOMICBIAS.
*/
ALC_PIN_MODE("HP-OUT pin mode", 0x10, ALC_PIN_DIR_INOUT),
ALC_PIN_MODE("LINE-OUT pin mode", 0x0f, ALC_PIN_DIR_INOUT),
ALC_PIN_MODE("LINE2 pin mode", 0x15, ALC_PIN_DIR_INOUT),
@ -3606,6 +3787,7 @@ static int alc260_parse_auto_config(struct hda_codec *codec)
spec->init_verbs[spec->num_init_verbs++] = alc260_volume_init_verbs;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
/* check whether NID 0x04 is valid */
@ -3711,7 +3893,8 @@ static struct alc_config_preset alc260_presets[] = {
.adc_nids = alc260_dual_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_fujitsu_capture_source,
.num_mux_defs = ARRAY_SIZE(alc260_fujitsu_capture_sources),
.input_mux = alc260_fujitsu_capture_sources,
},
[ALC260_ACER] = {
.mixers = { alc260_acer_mixer,
@ -3723,7 +3906,8 @@ static struct alc_config_preset alc260_presets[] = {
.adc_nids = alc260_dual_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_acer_capture_source,
.num_mux_defs = ARRAY_SIZE(alc260_acer_capture_sources),
.input_mux = alc260_acer_capture_sources,
},
#ifdef CONFIG_SND_DEBUG
[ALC260_TEST] = {
@ -3736,7 +3920,8 @@ static struct alc_config_preset alc260_presets[] = {
.adc_nids = alc260_test_adc_nids,
.num_channel_mode = ARRAY_SIZE(alc260_modes),
.channel_mode = alc260_modes,
.input_mux = &alc260_test_capture_source,
.num_mux_defs = ARRAY_SIZE(alc260_test_capture_sources),
.input_mux = alc260_test_capture_sources,
},
#endif
};
@ -3828,7 +4013,6 @@ static struct hda_input_mux alc882_capture_source = {
{ "CD", 0x4 },
},
};
#define alc882_mux_enum_info alc_mux_enum_info
#define alc882_mux_enum_get alc_mux_enum_get
@ -4730,6 +4914,7 @@ static int alc262_parse_auto_config(struct hda_codec *codec)
spec->mixers[spec->num_mixers++] = spec->kctl_alloc;
spec->init_verbs[spec->num_init_verbs++] = alc262_volume_init_verbs;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
return 1;
@ -5406,6 +5591,7 @@ static int alc861_parse_auto_config(struct hda_codec *codec)
spec->init_verbs[spec->num_init_verbs++] = alc861_auto_init_verbs;
spec->num_mux_defs = 1;
spec->input_mux = &spec->private_imux;
spec->adc_nids = alc861_adc_nids;

View file

@ -534,6 +534,22 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
return 0;
}
static unsigned int stac92xx_get_vref(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int pincap = snd_hda_param_read(codec, nid,
AC_PAR_PIN_CAP);
pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
if (pincap & AC_PINCAP_VREF_100)
return AC_PINCTL_VREF_100;
if (pincap & AC_PINCAP_VREF_80)
return AC_PINCTL_VREF_80;
if (pincap & AC_PINCAP_VREF_50)
return AC_PINCTL_VREF_50;
if (pincap & AC_PINCAP_VREF_GRD)
return AC_PINCTL_VREF_GRD;
return 0;
}
static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
{
@ -571,9 +587,12 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (val)
stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_OUT_EN);
else
stac92xx_auto_set_pinctl(codec, nid, AC_PINCTL_IN_EN);
else {
unsigned int pinctl = AC_PINCTL_IN_EN;
if (io_idx) /* set VREF for mic */
pinctl |= stac92xx_get_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
return 1;
}
@ -767,13 +786,8 @@ static int stac92xx_auto_create_hp_ctls(struct hda_codec *codec, struct auto_pin
return 0;
wid_caps = get_wcaps(codec, pin);
if (wid_caps & AC_WCAP_UNSOL_CAP) {
/* Enable unsolicited responses on the HP widget */
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
STAC_UNSOL_ENABLE);
if (wid_caps & AC_WCAP_UNSOL_CAP)
spec->hp_detect = 1;
}
nid = snd_hda_codec_read(codec, pin, 0, AC_VERB_GET_CONNECT_LIST, 0) & 0xff;
for (i = 0; i < cfg->line_outs; i++) {
@ -896,13 +910,8 @@ static int stac9200_auto_create_hp_ctls(struct hda_codec *codec,
return 0;
wid_caps = get_wcaps(codec, pin);
if (wid_caps & AC_WCAP_UNSOL_CAP) {
/* Enable unsolicited responses on the HP widget */
snd_hda_codec_write(codec, pin, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
STAC_UNSOL_ENABLE);
if (wid_caps & AC_WCAP_UNSOL_CAP)
spec->hp_detect = 1;
}
return 0;
}
@ -944,6 +953,10 @@ static int stac92xx_init(struct hda_codec *codec)
/* set up pins */
if (spec->hp_detect) {
/* Enable unsolicited responses on the HP widget */
snd_hda_codec_write(codec, cfg->hp_pin, 0,
AC_VERB_SET_UNSOLICITED_ENABLE,
STAC_UNSOL_ENABLE);
/* fake event to set up pins */
codec->patch_ops.unsol_event(codec, STAC_HP_EVENT << 26);
} else {
@ -951,9 +964,13 @@ static int stac92xx_init(struct hda_codec *codec)
stac92xx_auto_init_hp_out(codec);
}
for (i = 0; i < AUTO_PIN_LAST; i++) {
if (cfg->input_pins[i])
stac92xx_auto_set_pinctl(codec, cfg->input_pins[i],
AC_PINCTL_IN_EN);
hda_nid_t nid = cfg->input_pins[i];
if (nid) {
unsigned int pinctl = AC_PINCTL_IN_EN;
if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC)
pinctl |= stac92xx_get_vref(codec, nid);
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
}
if (cfg->dig_out_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,

View file

@ -87,7 +87,151 @@
#define CS8415_C_BUFFER 0x20
#define CS8415_ID 0x7F
static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg, unsigned short val) {
/* PCA9554 registers */
#define PCA9554_DEV 0x40 /* I2C device address */
#define PCA9554_IN 0x00 /* input port */
#define PCA9554_OUT 0x01 /* output port */
#define PCA9554_INVERT 0x02 /* input invert */
#define PCA9554_DIR 0x03 /* port directions */
/*
* Aureon Universe additional controls using PCA9554
*/
/*
* Send data to pca9554
*/
static void aureon_pca9554_write(struct snd_ice1712 *ice, unsigned char reg,
unsigned char data)
{
unsigned int tmp;
int i, j;
unsigned char dev = PCA9554_DEV; /* ID 0100000, write */
unsigned char val = 0;
tmp = snd_ice1712_gpio_read(ice);
snd_ice1712_gpio_set_mask(ice, ~(AUREON_SPI_MOSI|AUREON_SPI_CLK|
AUREON_WM_RW|AUREON_WM_CS|
AUREON_CS8415_CS));
tmp |= AUREON_WM_RW;
tmp |= AUREON_CS8415_CS | AUREON_WM_CS; /* disable SPI devices */
tmp &= ~AUREON_SPI_MOSI;
tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(50);
/*
* send i2c stop condition and start condition
* to obtain sane state
*/
tmp |= AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(50);
tmp |= AUREON_SPI_MOSI;
snd_ice1712_gpio_write(ice, tmp);
udelay(100);
tmp &= ~AUREON_SPI_MOSI;
snd_ice1712_gpio_write(ice, tmp);
udelay(50);
tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(100);
/*
* send device address, command and value,
* skipping ack cycles inbetween
*/
for (j = 0; j < 3; j++) {
switch(j) {
case 0: val = dev; break;
case 1: val = reg; break;
case 2: val = data; break;
}
for (i = 7; i >= 0; i--) {
tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
if (val & (1 << i))
tmp |= AUREON_SPI_MOSI;
else
tmp &= ~AUREON_SPI_MOSI;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
tmp |= AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
}
tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
tmp |= AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
}
tmp &= ~AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
tmp &= ~AUREON_SPI_MOSI;
snd_ice1712_gpio_write(ice, tmp);
udelay(40);
tmp |= AUREON_SPI_CLK;
snd_ice1712_gpio_write(ice, tmp);
udelay(50);
tmp |= AUREON_SPI_MOSI;
snd_ice1712_gpio_write(ice, tmp);
udelay(100);
}
static int aureon_universe_inmux_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
char *texts[3] = {"Internal Aux", "Wavetable", "Rear Line-In"};
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
uinfo->count = 1;
uinfo->value.enumerated.items = 3;
if(uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
return 0;
}
static int aureon_universe_inmux_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
ucontrol->value.integer.value[0] = ice->spec.aureon.pca9554_out;
return 0;
}
static int aureon_universe_inmux_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
unsigned char oval, nval;
int change;
snd_ice1712_save_gpio_status(ice);
oval = ice->spec.aureon.pca9554_out;
nval = ucontrol->value.integer.value[0];
if ((change = (oval != nval))) {
aureon_pca9554_write(ice, PCA9554_OUT, nval);
ice->spec.aureon.pca9554_out = nval;
}
snd_ice1712_restore_gpio_status(ice);
return change;
}
static void aureon_ac97_write(struct snd_ice1712 *ice, unsigned short reg,
unsigned short val)
{
unsigned int tmp;
/* Send address to XILINX chip */
@ -146,7 +290,8 @@ static unsigned short aureon_ac97_read(struct snd_ice1712 *ice, unsigned short r
/*
* Initialize STAC9744 chip
*/
static int aureon_ac97_init (struct snd_ice1712 *ice) {
static int aureon_ac97_init (struct snd_ice1712 *ice)
{
int i;
static unsigned short ac97_defaults[] = {
0x00, 0x9640,
@ -1598,7 +1743,15 @@ static struct snd_kcontrol_new universe_ac97_controls[] __devinitdata = {
.get = aureon_ac97_vol_get,
.put = aureon_ac97_vol_put,
.private_value = AC97_VIDEO|AUREON_AC97_STEREO
}
},
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Aux Source",
.info = aureon_universe_inmux_info,
.get = aureon_universe_inmux_get,
.put = aureon_universe_inmux_put
}
};
@ -1856,6 +2009,10 @@ static int __devinit aureon_init(struct snd_ice1712 *ice)
}
snd_ice1712_restore_gpio_status(ice);
/* initialize PCA9554 pin directions & set default input*/
aureon_pca9554_write(ice, PCA9554_DIR, 0x00);
aureon_pca9554_write(ice, PCA9554_OUT, 0x00); /* internal AUX */
ice->spec.aureon.master[0] = WM_VOL_MUTE;
ice->spec.aureon.master[1] = WM_VOL_MUTE;

View file

@ -2402,7 +2402,7 @@ static int __devinit snd_ice1712_chip_init(struct snd_ice1712 *ice)
if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_DMX6FIRE && !ice->dxr_enable) {
/* Limit active ADCs and DACs to 6; */
/* Note: DXR extension not supported */
pci_write_config_byte(ice->pci, 0x60, 0x0a);
pci_write_config_byte(ice->pci, 0x60, 0x2a);
} else {
pci_write_config_byte(ice->pci, 0x60, ice->eeprom.data[ICE_EEP1_CODEC]);
}

View file

@ -373,6 +373,7 @@ struct snd_ice1712 {
unsigned int cs8415_mux;
unsigned short master[2];
unsigned short vol[8];
unsigned char pca9554_out;
} aureon;
/* AC97 register cache for Phase28 */
struct phase28_spec {

View file

@ -831,8 +831,8 @@ struct snd_m3 {
struct snd_pcm *pcm;
struct pci_dev *pci;
struct m3_quirk *quirk;
struct m3_hv_quirk *hv_quirk;
const struct m3_quirk *quirk;
const struct m3_hv_quirk *hv_quirk;
int dacs_active;
int timer_users;
@ -892,7 +892,7 @@ static struct pci_device_id snd_m3_ids[] = {
MODULE_DEVICE_TABLE(pci, snd_m3_ids);
static struct m3_quirk m3_quirk_list[] = {
static const struct m3_quirk m3_quirk_list[] = {
/* panasonic CF-28 "toughbook" */
{
.name = "Panasonic CF-28",
@ -950,7 +950,7 @@ static struct m3_quirk m3_quirk_list[] = {
};
/* These values came from the Windows driver. */
static struct m3_hv_quirk m3_hv_quirk_list[] = {
static const struct m3_hv_quirk m3_hv_quirk_list[] = {
/* Allegro chips */
{ 0x125D, 0x1988, 0x0E11, 0x002E, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
{ 0x125D, 0x1988, 0x0E11, 0x0094, HV_CTRL_ENABLE | HV_BUTTON_FROM_GD, 0 },
@ -1361,7 +1361,7 @@ static void snd_m3_pcm_setup2(struct snd_m3 *chip, struct m3_dma *s,
}
static struct play_vals {
static const struct play_vals {
u16 addr, val;
} pv[] = {
{CDATA_LEFT_VOLUME, ARB_VOLUME},
@ -1428,7 +1428,7 @@ snd_m3_playback_setup(struct snd_m3 *chip, struct m3_dma *s,
/*
* Native record driver
*/
static struct rec_vals {
static const struct rec_vals {
u16 addr, val;
} rv[] = {
{CDATA_LEFT_VOLUME, ARB_VOLUME},
@ -1598,12 +1598,26 @@ static void snd_m3_update_ptr(struct snd_m3 *chip, struct m3_dma *s)
if (! s->running)
return;
hwptr = snd_m3_get_pointer(chip, s, subs) % s->dma_size;
diff = (s->dma_size + hwptr - s->hwptr) % s->dma_size;
hwptr = snd_m3_get_pointer(chip, s, subs);
/* try to avoid expensive modulo divisions */
if (hwptr >= s->dma_size)
hwptr %= s->dma_size;
diff = s->dma_size + hwptr - s->hwptr;
if (diff >= s->dma_size)
diff %= s->dma_size;
s->hwptr = hwptr;
s->count += diff;
if (s->count >= (signed)s->period_size) {
s->count %= s->period_size;
if (s->count < 2 * (signed)s->period_size)
s->count -= (signed)s->period_size;
else
s->count %= s->period_size;
spin_unlock(&chip->reg_lock);
snd_pcm_period_elapsed(subs);
spin_lock(&chip->reg_lock);
@ -1942,6 +1956,7 @@ static int snd_m3_ac97_wait(struct snd_m3 *chip)
do {
if (! (snd_m3_inb(chip, 0x30) & 1))
return 0;
cpu_relax();
} while (i-- > 0);
snd_printk(KERN_ERR "ac97 serial bus busy\n");
@ -1953,16 +1968,18 @@ snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{
struct snd_m3 *chip = ac97->private_data;
unsigned long flags;
unsigned short data;
unsigned short data = 0xffff;
if (snd_m3_ac97_wait(chip))
return 0xffff;
goto fail;
spin_lock_irqsave(&chip->ac97_lock, flags);
snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND);
if (snd_m3_ac97_wait(chip))
return 0xffff;
goto fail_unlock;
data = snd_m3_inw(chip, CODEC_DATA);
fail_unlock:
spin_unlock_irqrestore(&chip->ac97_lock, flags);
fail:
return data;
}
@ -2121,7 +2138,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip)
* DSP Code images
*/
static u16 assp_kernel_image[] __devinitdata = {
static const u16 assp_kernel_image[] __devinitdata = {
0x7980, 0x0030, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x00FB, 0x7980, 0x00DD, 0x7980, 0x03B4,
0x7980, 0x0332, 0x7980, 0x0287, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
0x7980, 0x031A, 0x7980, 0x03B4, 0x7980, 0x022F, 0x7980, 0x03B4, 0x7980, 0x03B4, 0x7980, 0x03B4,
@ -2208,7 +2225,7 @@ static u16 assp_kernel_image[] __devinitdata = {
* Mini sample rate converter code image
* that is to be loaded at 0x400 on the DSP.
*/
static u16 assp_minisrc_image[] __devinitdata = {
static const u16 assp_minisrc_image[] __devinitdata = {
0xBF80, 0x101E, 0x906E, 0x006E, 0x8B88, 0x6980, 0xEF88, 0x906F, 0x0D6F, 0x6900, 0xEB08, 0x0412,
0xBC20, 0x696E, 0xB801, 0x906E, 0x7980, 0x0403, 0xB90E, 0x8807, 0xBE43, 0xBF01, 0xBE47, 0xBE41,
@ -2251,7 +2268,7 @@ static u16 assp_minisrc_image[] __devinitdata = {
*/
#define MINISRC_LPF_LEN 10
static u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
static const u16 minisrc_lpf[MINISRC_LPF_LEN] __devinitdata = {
0X0743, 0X1104, 0X0A4C, 0XF88D, 0X242C,
0X1023, 0X1AA9, 0X0B60, 0XEFDD, 0X186F
};
@ -2358,7 +2375,7 @@ static int __devinit snd_m3_assp_client_init(struct snd_m3 *chip, struct m3_dma
*/
/*
* align instance address to 256 bytes so that it's
* align instance address to 256 bytes so that its
* shifted list address is aligned.
* list address = (mem address >> 1) >> 7;
*/
@ -2647,8 +2664,8 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci,
{
struct snd_m3 *chip;
int i, err;
struct m3_quirk *quirk;
struct m3_hv_quirk *hv_quirk;
const struct m3_quirk *quirk;
const struct m3_hv_quirk *hv_quirk;
static struct snd_device_ops ops = {
.dev_free = snd_m3_dev_free,
};
@ -2843,12 +2860,12 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
}
#if 0 /* TODO: not supported yet */
/* TODO enable midi irq and i/o */
/* TODO enable MIDI IRQ and I/O */
err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
chip->iobase + MPU401_DATA_PORT, 1,
chip->irq, 0, &chip->rmidi);
if (err < 0)
printk(KERN_WARNING "maestro3: no midi support.\n");
printk(KERN_WARNING "maestro3: no MIDI support.\n");
#endif
pci_set_drvdata(pci, card);

View file

@ -274,12 +274,9 @@ int pcxhr_load_xilinx_binary(struct pcxhr_mgr *mgr, const struct firmware *xilin
/* test first xilinx */
chipsc = PCXHR_INPL(mgr, PCXHR_PLX_CHIPSC);
if (!second) {
if (chipsc & PCXHR_CHIPSC_GPI_USERI) {
snd_printdd("no need to load first xilinx\n");
return 0; /* first xilinx is already present and cannot be reset */
}
} else {
/* REV01 cards do not support the PCXHR_CHIPSC_GPI_USERI bit anymore */
/* this bit will always be 1; no possibility to test presence of first xilinx */
if(second) {
if ((chipsc & PCXHR_CHIPSC_GPI_USERI) == 0) {
snd_printk(KERN_ERR "error loading first xilinx\n");
return -EINVAL;

View file

@ -0,0 +1,3 @@
snd-riptide-objs := riptide.o
obj-$(CONFIG_SND_RIPTIDE) += snd-riptide.o

2223
sound/pci/riptide/riptide.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -2373,6 +2373,7 @@ static int __devinit check_dxs_list(struct pci_dev *pci)
{ .subvendor = 0x161f, .subdevice = 0x2032, .action = VIA_DXS_48K }, /* m680x machines */
{ .subvendor = 0x1631, .subdevice = 0xe004, .action = VIA_DXS_ENABLE }, /* Easy Note 3174, Packard Bell */
{ .subvendor = 0x1695, .subdevice = 0x3005, .action = VIA_DXS_ENABLE }, /* EPoX EP-8K9A */
{ .subvendor = 0x1695, .subdevice = 0x300c, .action = VIA_DXS_SRC }, /* EPoX EP-8KRAI */
{ .subvendor = 0x1695, .subdevice = 0x300e, .action = VIA_DXS_SRC }, /* EPoX 9HEAI */
{ .subvendor = 0x16f3, .subdevice = 0x6405, .action = VIA_DXS_SRC }, /* Jetway K8M8MS */
{ .subvendor = 0x1734, .subdevice = 0x1078, .action = VIA_DXS_SRC }, /* FSC Amilo L7300 */

View file

@ -306,8 +306,8 @@ static int get_relative_value(struct usb_mixer_elem_info *cval, int val)
cval->res = 1;
if (val < cval->min)
return 0;
else if (val > cval->max)
return (cval->max - cval->min) / cval->res;
else if (val >= cval->max)
return (cval->max - cval->min + cval->res - 1) / cval->res;
else
return (val - cval->min) / cval->res;
}
@ -670,6 +670,36 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
}
if (cval->res == 0)
cval->res = 1;
/* Additional checks for the proper resolution
*
* Some devices report smaller resolutions than actually
* reacting. They don't return errors but simply clip
* to the lower aligned value.
*/
if (cval->min + cval->res < cval->max) {
int last_valid_res = cval->res;
int saved, test, check;
get_cur_mix_value(cval, minchn, &saved);
for (;;) {
test = saved;
if (test < cval->max)
test += cval->res;
else
test -= cval->res;
if (test < cval->min || test > cval->max ||
set_cur_mix_value(cval, minchn, test) ||
get_cur_mix_value(cval, minchn, &check)) {
cval->res = last_valid_res;
break;
}
if (test == check)
break;
cval->res *= 2;
}
set_cur_mix_value(cval, minchn, saved);
}
cval->initialized = 1;
}
return 0;
@ -695,7 +725,8 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
if (! cval->initialized)
get_min_max(cval, 0);
uinfo->value.integer.min = 0;
uinfo->value.integer.max = (cval->max - cval->min) / cval->res;
uinfo->value.integer.max =
(cval->max - cval->min + cval->res - 1) / cval->res;
}
return 0;
}