mirror of
https://github.com/torvalds/linux.git
synced 2024-12-28 22:02:28 +00:00
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:
commit
f27f0a045b
@ -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
|
||||
----------------
|
||||
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
161
sound/isa/adlib.c
Normal 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);
|
@ -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);
|
||||
|
@ -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
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
73
sound/isa/opti9xx/miro.h
Normal 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_ */
|
@ -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
|
||||
|
@ -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
866
sound/pci/als300.c
Normal 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)
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
3
sound/pci/riptide/Makefile
Normal file
3
sound/pci/riptide/Makefile
Normal 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
2223
sound/pci/riptide/riptide.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user