2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) by Jaroslav Kysela <perex@suse.cz>
|
|
|
|
* Routines for control of YMF724/740/744/754 chips
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sound/driver.h>
|
|
|
|
#include <linux/delay.h>
|
2006-10-11 18:05:59 +08:00
|
|
|
#include <linux/firmware.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/vmalloc.h>
|
|
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/control.h>
|
|
|
|
#include <sound/info.h>
|
2006-08-28 19:29:42 +08:00
|
|
|
#include <sound/tlv.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <sound/ymfpci.h>
|
|
|
|
#include <sound/asoundef.h>
|
|
|
|
#include <sound/mpu401.h>
|
|
|
|
|
|
|
|
#include <asm/io.h>
|
2006-10-11 18:05:59 +08:00
|
|
|
#include <asm/byteorder.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* common I/O routines
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static inline u8 snd_ymfpci_readb(struct snd_ymfpci *chip, u32 offset)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return readb(chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static inline void snd_ymfpci_writeb(struct snd_ymfpci *chip, u32 offset, u8 val)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
writeb(val, chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static inline u16 snd_ymfpci_readw(struct snd_ymfpci *chip, u32 offset)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return readw(chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static inline void snd_ymfpci_writew(struct snd_ymfpci *chip, u32 offset, u16 val)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
writew(val, chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static inline u32 snd_ymfpci_readl(struct snd_ymfpci *chip, u32 offset)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return readl(chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static inline void snd_ymfpci_writel(struct snd_ymfpci *chip, u32 offset, u32 val)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
writel(val, chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_codec_ready(struct snd_ymfpci *chip, int secondary)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
[ALSA] sound/pci: fix-up sleeping paths
ENS1370/1+ driver,ES1968 driver,Intel8x0 driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,ALI5451 driver,CS46xx driver
MIXART driver,RME HDSP driver,Trident driver,YMFPCI driver
Description: Fix-up sleeping in sound/pci. These changes fall under the
following two categories:
1) Replace schedule_timeout() with msleep() to guarantee the
task delays as expected. This also involved replacing/removing
custom sleep functions.
2) Do not assume jiffies will only increment by one if you
request a 1 jiffy sleep, i.e. use time_after/time_before in
while loops.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
2005-07-09 16:13:22 +08:00
|
|
|
unsigned long end_time;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
|
|
|
|
|
[ALSA] sound/pci: fix-up sleeping paths
ENS1370/1+ driver,ES1968 driver,Intel8x0 driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,ALI5451 driver,CS46xx driver
MIXART driver,RME HDSP driver,Trident driver,YMFPCI driver
Description: Fix-up sleeping in sound/pci. These changes fall under the
following two categories:
1) Replace schedule_timeout() with msleep() to guarantee the
task delays as expected. This also involved replacing/removing
custom sleep functions.
2) Do not assume jiffies will only increment by one if you
request a 1 jiffy sleep, i.e. use time_after/time_before in
while loops.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
2005-07-09 16:13:22 +08:00
|
|
|
end_time = jiffies + msecs_to_jiffies(750);
|
2005-04-17 06:20:36 +08:00
|
|
|
do {
|
|
|
|
if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
|
|
|
|
return 0;
|
|
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
2005-10-24 21:02:37 +08:00
|
|
|
schedule_timeout_uninterruptible(1);
|
[ALSA] sound/pci: fix-up sleeping paths
ENS1370/1+ driver,ES1968 driver,Intel8x0 driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,ALI5451 driver,CS46xx driver
MIXART driver,RME HDSP driver,Trident driver,YMFPCI driver
Description: Fix-up sleeping in sound/pci. These changes fall under the
following two categories:
1) Replace schedule_timeout() with msleep() to guarantee the
task delays as expected. This also involved replacing/removing
custom sleep functions.
2) Do not assume jiffies will only increment by one if you
request a 1 jiffy sleep, i.e. use time_after/time_before in
while loops.
Signed-off-by: Nishanth Aravamudan <nacc@us.ibm.com>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
2005-07-09 16:13:22 +08:00
|
|
|
} while (time_before(jiffies, end_time));
|
2005-10-21 00:26:44 +08:00
|
|
|
snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_codec_write(struct snd_ac97 *ac97, u16 reg, u16 val)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 cmd;
|
|
|
|
|
|
|
|
snd_ymfpci_codec_ready(chip, 0);
|
|
|
|
cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static u16 snd_ymfpci_codec_read(struct snd_ac97 *ac97, u16 reg)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (snd_ymfpci_codec_ready(chip, 0))
|
|
|
|
return ~0;
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_AC97CMDADR, YDSXG_AC97READCMD | reg);
|
|
|
|
if (snd_ymfpci_codec_ready(chip, 0))
|
|
|
|
return ~0;
|
|
|
|
if (chip->device_id == PCI_DEVICE_ID_YAMAHA_744 && chip->rev < 2) {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < 600; i++)
|
|
|
|
snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
|
|
|
|
}
|
|
|
|
return snd_ymfpci_readw(chip, YDSXGR_PRISTATUSDATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Misc routines
|
|
|
|
*/
|
|
|
|
|
|
|
|
static u32 snd_ymfpci_calc_delta(u32 rate)
|
|
|
|
{
|
|
|
|
switch (rate) {
|
|
|
|
case 8000: return 0x02aaab00;
|
|
|
|
case 11025: return 0x03accd00;
|
|
|
|
case 16000: return 0x05555500;
|
|
|
|
case 22050: return 0x07599a00;
|
|
|
|
case 32000: return 0x0aaaab00;
|
|
|
|
case 44100: return 0x0eb33300;
|
|
|
|
default: return ((rate << 16) / 375) << 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 def_rate[8] = {
|
|
|
|
100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
|
|
|
|
};
|
|
|
|
|
|
|
|
static u32 snd_ymfpci_calc_lpfK(u32 rate)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
static u32 val[8] = {
|
|
|
|
0x00570000, 0x06AA0000, 0x18B20000, 0x20930000,
|
|
|
|
0x2B9A0000, 0x35A10000, 0x3EAA0000, 0x40000000
|
|
|
|
};
|
|
|
|
|
|
|
|
if (rate == 44100)
|
|
|
|
return 0x40000000; /* FIXME: What's the right value? */
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
if (rate <= def_rate[i])
|
|
|
|
return val[i];
|
|
|
|
return val[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 snd_ymfpci_calc_lpfQ(u32 rate)
|
|
|
|
{
|
|
|
|
u32 i;
|
|
|
|
static u32 val[8] = {
|
|
|
|
0x35280000, 0x34A70000, 0x32020000, 0x31770000,
|
|
|
|
0x31390000, 0x31C90000, 0x33D00000, 0x40000000
|
|
|
|
};
|
|
|
|
|
|
|
|
if (rate == 44100)
|
|
|
|
return 0x370A0000;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
if (rate <= def_rate[i])
|
|
|
|
return val[i];
|
|
|
|
return val[0];
|
|
|
|
}
|
|
|
|
|
2006-12-05 01:03:53 +08:00
|
|
|
static void snd_ymfpci_pcm_441_volume_set(struct snd_ymfpci_pcm *ypcm)
|
|
|
|
{
|
|
|
|
unsigned int value;
|
|
|
|
struct snd_ymfpci_pcm_mixer *mixer;
|
|
|
|
|
|
|
|
mixer = &ypcm->chip->pcm_mixer[ypcm->substream->number];
|
|
|
|
value = min_t(unsigned int, mixer->left, 0x7fff) >> 1;
|
|
|
|
value |= (min_t(unsigned int, mixer->right, 0x7fff) >> 1) << 16;
|
|
|
|
snd_ymfpci_writel(ypcm->chip, YDSXGR_BUF441OUTVOL, value);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Hardware start management
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_hw_start(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
|
|
if (chip->start_count++ > 0)
|
|
|
|
goto __end;
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE,
|
|
|
|
snd_ymfpci_readl(chip, YDSXGR_MODE) | 3);
|
|
|
|
chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
|
|
|
|
__end:
|
|
|
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_hw_stop(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
long timeout = 1000;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
|
|
if (--chip->start_count > 0)
|
|
|
|
goto __end;
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE,
|
|
|
|
snd_ymfpci_readl(chip, YDSXGR_MODE) & ~3);
|
|
|
|
while (timeout-- > 0) {
|
|
|
|
if ((snd_ymfpci_readl(chip, YDSXGR_STATUS) & 2) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (atomic_read(&chip->interrupt_sleep_count)) {
|
|
|
|
atomic_set(&chip->interrupt_sleep_count, 0);
|
|
|
|
wake_up(&chip->interrupt_sleep);
|
|
|
|
}
|
|
|
|
__end:
|
|
|
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Playback voice management
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int voice_alloc(struct snd_ymfpci *chip,
|
|
|
|
enum snd_ymfpci_voice_type type, int pair,
|
|
|
|
struct snd_ymfpci_voice **rvoice)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_voice *voice, *voice2;
|
2005-04-17 06:20:36 +08:00
|
|
|
int idx;
|
|
|
|
|
|
|
|
*rvoice = NULL;
|
|
|
|
for (idx = 0; idx < YDSXG_PLAYBACK_VOICES; idx += pair ? 2 : 1) {
|
|
|
|
voice = &chip->voices[idx];
|
|
|
|
voice2 = pair ? &chip->voices[idx+1] : NULL;
|
|
|
|
if (voice->use || (voice2 && voice2->use))
|
|
|
|
continue;
|
|
|
|
voice->use = 1;
|
|
|
|
if (voice2)
|
|
|
|
voice2->use = 1;
|
|
|
|
switch (type) {
|
|
|
|
case YMFPCI_PCM:
|
|
|
|
voice->pcm = 1;
|
|
|
|
if (voice2)
|
|
|
|
voice2->pcm = 1;
|
|
|
|
break;
|
|
|
|
case YMFPCI_SYNTH:
|
|
|
|
voice->synth = 1;
|
|
|
|
break;
|
|
|
|
case YMFPCI_MIDI:
|
|
|
|
voice->midi = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
snd_ymfpci_hw_start(chip);
|
|
|
|
if (voice2)
|
|
|
|
snd_ymfpci_hw_start(chip);
|
|
|
|
*rvoice = voice;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,
|
|
|
|
enum snd_ymfpci_voice_type type, int pair,
|
|
|
|
struct snd_ymfpci_voice **rvoice)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
snd_assert(rvoice != NULL, return -EINVAL);
|
|
|
|
snd_assert(!pair || type == YMFPCI_PCM, return -EINVAL);
|
|
|
|
|
|
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
|
|
|
for (;;) {
|
|
|
|
result = voice_alloc(chip, type, pair, rvoice);
|
|
|
|
if (result == 0 || type != YMFPCI_PCM)
|
|
|
|
break;
|
|
|
|
/* TODO: synth/midi voice deallocation */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&chip->voice_lock, flags);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voice *pvoice)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
snd_assert(pvoice != NULL, return -EINVAL);
|
|
|
|
snd_ymfpci_hw_stop(chip);
|
|
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
2006-12-05 01:03:53 +08:00
|
|
|
if (pvoice->number == chip->src441_used) {
|
|
|
|
chip->src441_used = -1;
|
|
|
|
pvoice->ypcm->use_441_slot = 0;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
pvoice->use = pvoice->pcm = pvoice->synth = pvoice->midi = 0;
|
|
|
|
pvoice->ypcm = NULL;
|
|
|
|
pvoice->interrupt = NULL;
|
|
|
|
spin_unlock_irqrestore(&chip->voice_lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PCM part
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 pos, delta;
|
|
|
|
|
|
|
|
if ((ypcm = voice->ypcm) == NULL)
|
|
|
|
return;
|
|
|
|
if (ypcm->substream == NULL)
|
|
|
|
return;
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
if (ypcm->running) {
|
|
|
|
pos = le32_to_cpu(voice->bank[chip->active_bank].start);
|
|
|
|
if (pos < ypcm->last_pos)
|
|
|
|
delta = pos + (ypcm->buffer_size - ypcm->last_pos);
|
|
|
|
else
|
|
|
|
delta = pos - ypcm->last_pos;
|
|
|
|
ypcm->period_pos += delta;
|
|
|
|
ypcm->last_pos = pos;
|
|
|
|
if (ypcm->period_pos >= ypcm->period_size) {
|
|
|
|
// printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
|
|
|
|
ypcm->period_pos %= ypcm->period_size;
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
snd_pcm_period_elapsed(ypcm->substream);
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
}
|
2005-08-10 16:21:43 +08:00
|
|
|
|
|
|
|
if (unlikely(ypcm->update_pcm_vol)) {
|
|
|
|
unsigned int subs = ypcm->substream->number;
|
|
|
|
unsigned int next_bank = 1 - chip->active_bank;
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_playback_bank *bank;
|
2005-08-10 16:21:43 +08:00
|
|
|
u32 volume;
|
|
|
|
|
|
|
|
bank = &voice->bank[next_bank];
|
|
|
|
volume = cpu_to_le32(chip->pcm_mixer[subs].left << 15);
|
|
|
|
bank->left_gain_end = volume;
|
|
|
|
if (ypcm->output_rear)
|
|
|
|
bank->eff2_gain_end = volume;
|
|
|
|
if (ypcm->voices[1])
|
|
|
|
bank = &ypcm->voices[1]->bank[next_bank];
|
|
|
|
volume = cpu_to_le32(chip->pcm_mixer[subs].right << 15);
|
|
|
|
bank->right_gain_end = volume;
|
|
|
|
if (ypcm->output_rear)
|
|
|
|
bank->eff3_gain_end = volume;
|
|
|
|
ypcm->update_pcm_vol--;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
|
|
struct snd_ymfpci *chip = ypcm->chip;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 pos, delta;
|
|
|
|
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
if (ypcm->running) {
|
|
|
|
pos = le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
|
|
|
|
if (pos < ypcm->last_pos)
|
|
|
|
delta = pos + (ypcm->buffer_size - ypcm->last_pos);
|
|
|
|
else
|
|
|
|
delta = pos - ypcm->last_pos;
|
|
|
|
ypcm->period_pos += delta;
|
|
|
|
ypcm->last_pos = pos;
|
|
|
|
if (ypcm->period_pos >= ypcm->period_size) {
|
|
|
|
ypcm->period_pos %= ypcm->period_size;
|
|
|
|
// printk("done - active_bank = 0x%x, start = 0x%x\n", chip->active_bank, voice->bank[chip->active_bank].start);
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
snd_pcm_period_elapsed(substream);
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
|
2005-04-17 06:20:36 +08:00
|
|
|
int cmd)
|
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
if (ypcm->voices[0] == NULL) {
|
|
|
|
result = -EINVAL;
|
|
|
|
goto __unlock;
|
|
|
|
}
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
|
|
chip->ctrl_playback[ypcm->voices[0]->number + 1] = cpu_to_le32(ypcm->voices[0]->bank_addr);
|
2006-12-05 01:03:53 +08:00
|
|
|
if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->ctrl_playback[ypcm->voices[1]->number + 1] = cpu_to_le32(ypcm->voices[1]->bank_addr);
|
|
|
|
ypcm->running = 1;
|
|
|
|
break;
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
|
|
chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
|
2006-12-05 01:03:53 +08:00
|
|
|
if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
|
|
|
|
ypcm->running = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
result = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__unlock:
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
return result;
|
|
|
|
}
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream,
|
2005-04-17 06:20:36 +08:00
|
|
|
int cmd)
|
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int result = 0;
|
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
|
case SNDRV_PCM_TRIGGER_RESUME:
|
|
|
|
tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) | (1 << ypcm->capture_bank_number);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
|
|
|
|
ypcm->running = 1;
|
|
|
|
break;
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
|
|
tmp = snd_ymfpci_readl(chip, YDSXGR_MAPOFREC) & ~(1 << ypcm->capture_bank_number);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, tmp);
|
|
|
|
ypcm->running = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
result = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_pcm_voice_alloc(struct snd_ymfpci_pcm *ypcm, int voices)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (ypcm->voices[1] != NULL && voices < 2) {
|
|
|
|
snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[1]);
|
|
|
|
ypcm->voices[1] = NULL;
|
|
|
|
}
|
|
|
|
if (voices == 1 && ypcm->voices[0] != NULL)
|
|
|
|
return 0; /* already allocated */
|
|
|
|
if (voices == 2 && ypcm->voices[0] != NULL && ypcm->voices[1] != NULL)
|
|
|
|
return 0; /* already allocated */
|
|
|
|
if (voices > 1) {
|
|
|
|
if (ypcm->voices[0] != NULL && ypcm->voices[1] == NULL) {
|
|
|
|
snd_ymfpci_voice_free(ypcm->chip, ypcm->voices[0]);
|
|
|
|
ypcm->voices[0] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
err = snd_ymfpci_voice_alloc(ypcm->chip, YMFPCI_PCM, voices > 1, &ypcm->voices[0]);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
ypcm->voices[0]->ypcm = ypcm;
|
|
|
|
ypcm->voices[0]->interrupt = snd_ymfpci_pcm_interrupt;
|
|
|
|
if (voices > 1) {
|
|
|
|
ypcm->voices[1] = &ypcm->chip->voices[ypcm->voices[0]->number + 1];
|
|
|
|
ypcm->voices[1]->ypcm = ypcm;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int voiceidx,
|
|
|
|
struct snd_pcm_runtime *runtime,
|
2005-08-10 16:21:43 +08:00
|
|
|
int has_pcm_volume)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_voice *voice = ypcm->voices[voiceidx];
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 format;
|
2005-08-10 16:21:43 +08:00
|
|
|
u32 delta = snd_ymfpci_calc_delta(runtime->rate);
|
|
|
|
u32 lpfQ = snd_ymfpci_calc_lpfQ(runtime->rate);
|
|
|
|
u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate);
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_playback_bank *bank;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int nbank;
|
2005-08-10 16:21:43 +08:00
|
|
|
u32 vol_left, vol_right;
|
|
|
|
u8 use_left, use_right;
|
2006-12-05 01:03:53 +08:00
|
|
|
unsigned long flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
snd_assert(voice != NULL, return);
|
2005-08-10 16:21:43 +08:00
|
|
|
if (runtime->channels == 1) {
|
|
|
|
use_left = 1;
|
|
|
|
use_right = 1;
|
|
|
|
} else {
|
|
|
|
use_left = (voiceidx & 1) == 0;
|
|
|
|
use_right = !use_left;
|
|
|
|
}
|
|
|
|
if (has_pcm_volume) {
|
|
|
|
vol_left = cpu_to_le32(ypcm->chip->pcm_mixer
|
|
|
|
[ypcm->substream->number].left << 15);
|
|
|
|
vol_right = cpu_to_le32(ypcm->chip->pcm_mixer
|
|
|
|
[ypcm->substream->number].right << 15);
|
|
|
|
} else {
|
|
|
|
vol_left = cpu_to_le32(0x40000000);
|
|
|
|
vol_right = cpu_to_le32(0x40000000);
|
|
|
|
}
|
2006-12-05 01:03:53 +08:00
|
|
|
spin_lock_irqsave(&ypcm->chip->voice_lock, flags);
|
2005-08-10 16:21:43 +08:00
|
|
|
format = runtime->channels == 2 ? 0x00010000 : 0;
|
|
|
|
if (snd_pcm_format_width(runtime->format) == 8)
|
|
|
|
format |= 0x80000000;
|
2006-12-05 01:03:53 +08:00
|
|
|
else if (ypcm->chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
|
|
|
|
runtime->rate == 44100 && runtime->channels == 2 &&
|
|
|
|
voiceidx == 0 && (ypcm->chip->src441_used == -1 ||
|
|
|
|
ypcm->chip->src441_used == voice->number)) {
|
|
|
|
ypcm->chip->src441_used = voice->number;
|
|
|
|
ypcm->use_441_slot = 1;
|
|
|
|
format |= 0x10000000;
|
|
|
|
snd_ymfpci_pcm_441_volume_set(ypcm);
|
|
|
|
}
|
|
|
|
if (ypcm->chip->src441_used == voice->number &&
|
|
|
|
(format & 0x10000000) == 0) {
|
|
|
|
ypcm->chip->src441_used = -1;
|
|
|
|
ypcm->use_441_slot = 0;
|
|
|
|
}
|
2005-08-10 16:21:43 +08:00
|
|
|
if (runtime->channels == 2 && (voiceidx & 1) != 0)
|
|
|
|
format |= 1;
|
2006-12-05 01:03:53 +08:00
|
|
|
spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
for (nbank = 0; nbank < 2; nbank++) {
|
|
|
|
bank = &voice->bank[nbank];
|
2005-08-10 16:21:43 +08:00
|
|
|
memset(bank, 0, sizeof(*bank));
|
2005-04-17 06:20:36 +08:00
|
|
|
bank->format = cpu_to_le32(format);
|
2005-08-10 16:21:43 +08:00
|
|
|
bank->base = cpu_to_le32(runtime->dma_addr);
|
|
|
|
bank->loop_end = cpu_to_le32(ypcm->buffer_size);
|
2005-04-17 06:20:36 +08:00
|
|
|
bank->lpfQ = cpu_to_le32(lpfQ);
|
|
|
|
bank->delta =
|
|
|
|
bank->delta_end = cpu_to_le32(delta);
|
|
|
|
bank->lpfK =
|
|
|
|
bank->lpfK_end = cpu_to_le32(lpfK);
|
2005-08-10 16:21:43 +08:00
|
|
|
bank->eg_gain =
|
|
|
|
bank->eg_gain_end = cpu_to_le32(0x40000000);
|
|
|
|
|
|
|
|
if (ypcm->output_front) {
|
|
|
|
if (use_left) {
|
|
|
|
bank->left_gain =
|
|
|
|
bank->left_gain_end = vol_left;
|
|
|
|
}
|
|
|
|
if (use_right) {
|
2005-04-17 06:20:36 +08:00
|
|
|
bank->right_gain =
|
2005-08-10 16:21:43 +08:00
|
|
|
bank->right_gain_end = vol_right;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2005-08-10 16:21:43 +08:00
|
|
|
}
|
|
|
|
if (ypcm->output_rear) {
|
2006-01-18 15:02:24 +08:00
|
|
|
if (!ypcm->swap_rear) {
|
|
|
|
if (use_left) {
|
|
|
|
bank->eff2_gain =
|
|
|
|
bank->eff2_gain_end = vol_left;
|
|
|
|
}
|
|
|
|
if (use_right) {
|
|
|
|
bank->eff3_gain =
|
|
|
|
bank->eff3_gain_end = vol_right;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* The SPDIF out channels seem to be swapped, so we have
|
|
|
|
* to swap them here, too. The rear analog out channels
|
|
|
|
* will be wrong, but otherwise AC3 would not work.
|
|
|
|
*/
|
|
|
|
if (use_left) {
|
|
|
|
bank->eff3_gain =
|
|
|
|
bank->eff3_gain_end = vol_left;
|
|
|
|
}
|
|
|
|
if (use_right) {
|
|
|
|
bank->eff2_gain =
|
|
|
|
bank->eff2_gain_end = vol_right;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int __devinit snd_ymfpci_ac3_init(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
|
|
|
|
4096, &chip->ac3_tmp_base) < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
chip->bank_effect[3][0]->base =
|
|
|
|
chip->bank_effect[3][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr);
|
|
|
|
chip->bank_effect[3][0]->loop_end =
|
|
|
|
chip->bank_effect[3][1]->loop_end = cpu_to_le32(1024);
|
|
|
|
chip->bank_effect[4][0]->base =
|
|
|
|
chip->bank_effect[4][1]->base = cpu_to_le32(chip->ac3_tmp_base.addr + 2048);
|
|
|
|
chip->bank_effect[4][0]->loop_end =
|
|
|
|
chip->bank_effect[4][1]->loop_end = cpu_to_le32(1024);
|
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
|
|
|
|
snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) | 3 << 3);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_ac3_done(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT,
|
|
|
|
snd_ymfpci_readl(chip, YDSXGR_MAPOFEFFECT) & ~(3 << 3));
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
// snd_ymfpci_irq_wait(chip);
|
|
|
|
if (chip->ac3_tmp_base.area) {
|
|
|
|
snd_dma_free_pages(&chip->ac3_tmp_base);
|
|
|
|
chip->ac3_tmp_base.area = NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *hw_params)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if ((err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params))) < 0)
|
|
|
|
return err;
|
|
|
|
if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0)
|
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_hw_free(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (runtime->private_data == NULL)
|
|
|
|
return 0;
|
|
|
|
ypcm = runtime->private_data;
|
|
|
|
|
|
|
|
/* wait, until the PCI operations are not finished */
|
|
|
|
snd_ymfpci_irq_wait(chip);
|
|
|
|
snd_pcm_lib_free_pages(substream);
|
|
|
|
if (ypcm->voices[1]) {
|
|
|
|
snd_ymfpci_voice_free(chip, ypcm->voices[1]);
|
|
|
|
ypcm->voices[1] = NULL;
|
|
|
|
}
|
|
|
|
if (ypcm->voices[0]) {
|
|
|
|
snd_ymfpci_voice_free(chip, ypcm->voices[0]);
|
|
|
|
ypcm->voices[0] = NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int nvoice;
|
|
|
|
|
|
|
|
ypcm->period_size = runtime->period_size;
|
|
|
|
ypcm->buffer_size = runtime->buffer_size;
|
|
|
|
ypcm->period_pos = 0;
|
|
|
|
ypcm->last_pos = 0;
|
|
|
|
for (nvoice = 0; nvoice < runtime->channels; nvoice++)
|
2005-08-10 16:21:43 +08:00
|
|
|
snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime,
|
|
|
|
substream->pcm == chip->pcm);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *hw_params)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_hw_free(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* wait, until the PCI operations are not finished */
|
|
|
|
snd_ymfpci_irq_wait(chip);
|
|
|
|
return snd_pcm_lib_free_pages(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
|
|
struct snd_ymfpci_capture_bank * bank;
|
2005-04-17 06:20:36 +08:00
|
|
|
int nbank;
|
|
|
|
u32 rate, format;
|
|
|
|
|
|
|
|
ypcm->period_size = runtime->period_size;
|
|
|
|
ypcm->buffer_size = runtime->buffer_size;
|
|
|
|
ypcm->period_pos = 0;
|
|
|
|
ypcm->last_pos = 0;
|
|
|
|
ypcm->shift = 0;
|
|
|
|
rate = ((48000 * 4096) / runtime->rate) - 1;
|
|
|
|
format = 0;
|
|
|
|
if (runtime->channels == 2) {
|
|
|
|
format |= 2;
|
|
|
|
ypcm->shift++;
|
|
|
|
}
|
|
|
|
if (snd_pcm_format_width(runtime->format) == 8)
|
|
|
|
format |= 1;
|
|
|
|
else
|
|
|
|
ypcm->shift++;
|
|
|
|
switch (ypcm->capture_bank_number) {
|
|
|
|
case 0:
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_RECFORMAT, format);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_RECSLOTSR, rate);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_ADCFORMAT, format);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_ADCSLOTSR, rate);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
for (nbank = 0; nbank < 2; nbank++) {
|
|
|
|
bank = chip->bank_capture[ypcm->capture_bank_number][nbank];
|
|
|
|
bank->base = cpu_to_le32(runtime->dma_addr);
|
|
|
|
bank->loop_end = cpu_to_le32(ypcm->buffer_size << ypcm->shift);
|
|
|
|
bank->start = 0;
|
|
|
|
bank->num_of_loops = 0;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static snd_pcm_uframes_t snd_ymfpci_playback_pointer(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
|
|
struct snd_ymfpci_voice *voice = ypcm->voices[0];
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!(ypcm->running && voice))
|
|
|
|
return 0;
|
|
|
|
return le32_to_cpu(voice->bank[chip->active_bank].start);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static snd_pcm_uframes_t snd_ymfpci_capture_pointer(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (!ypcm->running)
|
|
|
|
return 0;
|
|
|
|
return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
wait_queue_t wait;
|
|
|
|
int loops = 4;
|
|
|
|
|
|
|
|
while (loops-- > 0) {
|
|
|
|
if ((snd_ymfpci_readl(chip, YDSXGR_MODE) & 3) == 0)
|
|
|
|
continue;
|
|
|
|
init_waitqueue_entry(&wait, current);
|
|
|
|
add_wait_queue(&chip->interrupt_sleep, &wait);
|
|
|
|
atomic_inc(&chip->interrupt_sleep_count);
|
2005-10-24 21:02:37 +08:00
|
|
|
schedule_timeout_uninterruptible(msecs_to_jiffies(50));
|
2005-04-17 06:20:36 +08:00
|
|
|
remove_wait_queue(&chip->interrupt_sleep, &wait);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = dev_id;
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 status, nvoice, mode;
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_voice *voice;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
status = snd_ymfpci_readl(chip, YDSXGR_STATUS);
|
|
|
|
if (status & 0x80000000) {
|
|
|
|
chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT) & 1;
|
|
|
|
spin_lock(&chip->voice_lock);
|
|
|
|
for (nvoice = 0; nvoice < YDSXG_PLAYBACK_VOICES; nvoice++) {
|
|
|
|
voice = &chip->voices[nvoice];
|
|
|
|
if (voice->interrupt)
|
|
|
|
voice->interrupt(chip, voice);
|
|
|
|
}
|
|
|
|
for (nvoice = 0; nvoice < YDSXG_CAPTURE_VOICES; nvoice++) {
|
|
|
|
if (chip->capture_substream[nvoice])
|
|
|
|
snd_ymfpci_pcm_capture_interrupt(chip->capture_substream[nvoice]);
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
for (nvoice = 0; nvoice < YDSXG_EFFECT_VOICES; nvoice++) {
|
|
|
|
if (chip->effect_substream[nvoice])
|
|
|
|
snd_ymfpci_pcm_effect_interrupt(chip->effect_substream[nvoice]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
spin_unlock(&chip->voice_lock);
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_STATUS, 0x80000000);
|
|
|
|
mode = snd_ymfpci_readl(chip, YDSXGR_MODE) | 2;
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE, mode);
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
|
|
|
|
if (atomic_read(&chip->interrupt_sleep_count)) {
|
|
|
|
atomic_set(&chip->interrupt_sleep_count, 0);
|
|
|
|
wake_up(&chip->interrupt_sleep);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
status = snd_ymfpci_readw(chip, YDSXGR_INTFLAG);
|
|
|
|
if (status & 1) {
|
|
|
|
if (chip->timer)
|
|
|
|
snd_timer_interrupt(chip->timer, chip->timer->sticks);
|
|
|
|
}
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_INTFLAG, status);
|
|
|
|
|
|
|
|
if (chip->rawmidi)
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data);
|
2005-04-17 06:20:36 +08:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_hardware snd_ymfpci_playback =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
|
|
|
SNDRV_PCM_INFO_PAUSE |
|
|
|
|
SNDRV_PCM_INFO_RESUME),
|
|
|
|
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
|
|
|
|
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
|
|
|
|
.rate_min = 8000,
|
|
|
|
.rate_max = 48000,
|
|
|
|
.channels_min = 1,
|
|
|
|
.channels_max = 2,
|
|
|
|
.buffer_bytes_max = 256 * 1024, /* FIXME: enough? */
|
|
|
|
.period_bytes_min = 64,
|
|
|
|
.period_bytes_max = 256 * 1024, /* FIXME: enough? */
|
|
|
|
.periods_min = 3,
|
|
|
|
.periods_max = 1024,
|
|
|
|
.fifo_size = 0,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_hardware snd_ymfpci_capture =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
|
|
|
SNDRV_PCM_INFO_PAUSE |
|
|
|
|
SNDRV_PCM_INFO_RESUME),
|
|
|
|
.formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
|
|
|
|
.rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
|
|
|
|
.rate_min = 8000,
|
|
|
|
.rate_max = 48000,
|
|
|
|
.channels_min = 1,
|
|
|
|
.channels_max = 2,
|
|
|
|
.buffer_bytes_max = 256 * 1024, /* FIXME: enough? */
|
|
|
|
.period_bytes_min = 64,
|
|
|
|
.period_bytes_max = 256 * 1024, /* FIXME: enough? */
|
|
|
|
.periods_min = 3,
|
|
|
|
.periods_max = 1024,
|
|
|
|
.fifo_size = 0,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_pcm_free_substream(struct snd_pcm_runtime *runtime)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
[ALSA] Remove redundant NULL checks before kfree
Timer Midlevel,ALSA sequencer,ALSA<-OSS sequencer,Digigram VX core
I2C tea6330t,GUS Library,VIA82xx driver,VIA82xx-modem driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,YMFPCI driver
Digigram VX Pocket driver,Common EMU synth,USB generic driver,USB USX2Y
Checking a pointer for NULL before calling kfree() on it is redundant,
kfree() deals with NULL pointers just fine.
This patch removes such checks from sound/
This patch also makes another, but closely related, change.
It avoids casting pointers about to be kfree()'ed.
Signed-off-by: Jesper Juhl <juhl-lkml@dif.dk>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-05-30 23:30:32 +08:00
|
|
|
kfree(runtime->private_data);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 20:21:46 +08:00
|
|
|
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ypcm == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ypcm->chip = chip;
|
|
|
|
ypcm->type = PLAYBACK_VOICE;
|
|
|
|
ypcm->substream = substream;
|
|
|
|
runtime->hw = snd_ymfpci_playback;
|
|
|
|
runtime->private_data = ypcm;
|
|
|
|
runtime->private_free = snd_ymfpci_pcm_free_substream;
|
|
|
|
/* FIXME? True value is 256/48 = 5.33333 ms */
|
|
|
|
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call with spinlock held */
|
2005-11-17 21:53:41 +08:00
|
|
|
static void ymfpci_open_extension(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (! chip->rear_opened) {
|
|
|
|
if (! chip->spdif_opened) /* set AC3 */
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE,
|
|
|
|
snd_ymfpci_readl(chip, YDSXGR_MODE) | (1 << 30));
|
|
|
|
/* enable second codec (4CHEN) */
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
|
|
|
|
(snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) | 0x0010);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call with spinlock held */
|
2005-11-17 21:53:41 +08:00
|
|
|
static void ymfpci_close_extension(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
if (! chip->rear_opened) {
|
|
|
|
if (! chip->spdif_opened)
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE,
|
|
|
|
snd_ymfpci_readl(chip, YDSXGR_MODE) & ~(1 << 30));
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SECCONFIG,
|
|
|
|
(snd_ymfpci_readw(chip, YDSXGR_SECCONFIG) & ~0x0330) & ~0x0010);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_open(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
|
|
struct snd_kcontrol *kctl;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
|
|
|
|
return err;
|
|
|
|
ypcm = runtime->private_data;
|
|
|
|
ypcm->output_front = 1;
|
|
|
|
ypcm->output_rear = chip->mode_dup4ch ? 1 : 0;
|
2006-10-10 15:27:19 +08:00
|
|
|
ypcm->swap_rear = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
if (ypcm->output_rear) {
|
|
|
|
ymfpci_open_extension(chip);
|
|
|
|
chip->rear_opened++;
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
2005-08-10 16:21:43 +08:00
|
|
|
|
|
|
|
kctl = chip->pcm_mixer[substream->number].ctl;
|
|
|
|
kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_spdif_open(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
|
|
|
|
return err;
|
|
|
|
ypcm = runtime->private_data;
|
|
|
|
ypcm->output_front = 0;
|
|
|
|
ypcm->output_rear = 1;
|
2006-10-10 15:27:19 +08:00
|
|
|
ypcm->swap_rear = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
|
|
|
|
snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) | 2);
|
|
|
|
ymfpci_open_extension(chip);
|
|
|
|
chip->spdif_pcm_bits = chip->spdif_bits;
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
|
|
|
|
chip->spdif_opened++;
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
|
|
|
|
chip->spdif_pcm_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
|
|
|
SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_4ch_open(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if ((err = snd_ymfpci_playback_open_1(substream)) < 0)
|
|
|
|
return err;
|
|
|
|
ypcm = runtime->private_data;
|
|
|
|
ypcm->output_front = 0;
|
|
|
|
ypcm->output_rear = 1;
|
2006-10-10 15:27:19 +08:00
|
|
|
ypcm->swap_rear = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
ymfpci_open_extension(chip);
|
|
|
|
chip->rear_opened++;
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
|
2005-04-17 06:20:36 +08:00
|
|
|
u32 capture_bank_number)
|
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 20:21:46 +08:00
|
|
|
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (ypcm == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ypcm->chip = chip;
|
|
|
|
ypcm->type = capture_bank_number + CAPTURE_REC;
|
|
|
|
ypcm->substream = substream;
|
|
|
|
ypcm->capture_bank_number = capture_bank_number;
|
|
|
|
chip->capture_substream[capture_bank_number] = substream;
|
|
|
|
runtime->hw = snd_ymfpci_capture;
|
|
|
|
/* FIXME? True value is 256/48 = 5.33333 ms */
|
|
|
|
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
|
|
|
|
runtime->private_data = ypcm;
|
|
|
|
runtime->private_free = snd_ymfpci_pcm_free_substream;
|
|
|
|
snd_ymfpci_hw_start(chip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_rec_open(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return snd_ymfpci_capture_open(substream, 0);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_ac97_open(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return snd_ymfpci_capture_open(substream, 1);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_close_1(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_close(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
|
|
|
struct snd_kcontrol *kctl;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
if (ypcm->output_rear && chip->rear_opened > 0) {
|
|
|
|
chip->rear_opened--;
|
|
|
|
ymfpci_close_extension(chip);
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
2005-08-10 16:21:43 +08:00
|
|
|
kctl = chip->pcm_mixer[substream->number].ctl;
|
|
|
|
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
|
2005-04-17 06:20:36 +08:00
|
|
|
return snd_ymfpci_playback_close_1(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_spdif_close(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
chip->spdif_opened = 0;
|
|
|
|
ymfpci_close_extension(chip);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL,
|
|
|
|
snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & ~2);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
chip->spdif_pcm_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
|
|
|
SNDRV_CTL_EVENT_MASK_INFO, &chip->spdif_pcm_ctl->id);
|
|
|
|
return snd_ymfpci_playback_close_1(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_playback_4ch_close(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
if (chip->rear_opened > 0) {
|
|
|
|
chip->rear_opened--;
|
|
|
|
ymfpci_close_extension(chip);
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return snd_ymfpci_playback_close_1(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (ypcm != NULL) {
|
|
|
|
chip->capture_substream[ypcm->capture_bank_number] = NULL;
|
|
|
|
snd_ymfpci_hw_stop(chip);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_ops snd_ymfpci_playback_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = snd_ymfpci_playback_open,
|
|
|
|
.close = snd_ymfpci_playback_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_ymfpci_playback_hw_params,
|
|
|
|
.hw_free = snd_ymfpci_playback_hw_free,
|
|
|
|
.prepare = snd_ymfpci_playback_prepare,
|
|
|
|
.trigger = snd_ymfpci_playback_trigger,
|
|
|
|
.pointer = snd_ymfpci_playback_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = snd_ymfpci_capture_rec_open,
|
|
|
|
.close = snd_ymfpci_capture_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_ymfpci_capture_hw_params,
|
|
|
|
.hw_free = snd_ymfpci_capture_hw_free,
|
|
|
|
.prepare = snd_ymfpci_capture_prepare,
|
|
|
|
.trigger = snd_ymfpci_capture_trigger,
|
|
|
|
.pointer = snd_ymfpci_capture_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
int __devinit snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = NULL;
|
|
|
|
if ((err = snd_pcm_new(chip->card, "YMFPCI", device, 32, 1, &pcm)) < 0)
|
|
|
|
return err;
|
|
|
|
pcm->private_data = chip;
|
|
|
|
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_ops);
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_rec_ops);
|
|
|
|
|
|
|
|
/* global setup */
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
strcpy(pcm->name, "YMFPCI");
|
|
|
|
chip->pcm = pcm;
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = pcm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = snd_ymfpci_capture_ac97_open,
|
|
|
|
.close = snd_ymfpci_capture_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_ymfpci_capture_hw_params,
|
|
|
|
.hw_free = snd_ymfpci_capture_hw_free,
|
|
|
|
.prepare = snd_ymfpci_capture_prepare,
|
|
|
|
.trigger = snd_ymfpci_capture_trigger,
|
|
|
|
.pointer = snd_ymfpci_capture_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
int __devinit snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = NULL;
|
|
|
|
if ((err = snd_pcm_new(chip->card, "YMFPCI - PCM2", device, 0, 1, &pcm)) < 0)
|
|
|
|
return err;
|
|
|
|
pcm->private_data = chip;
|
|
|
|
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ymfpci_capture_ac97_ops);
|
|
|
|
|
|
|
|
/* global setup */
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
sprintf(pcm->name, "YMFPCI - %s",
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_754 ? "Direct Recording" : "AC'97");
|
|
|
|
chip->pcm2 = pcm;
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = pcm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = snd_ymfpci_playback_spdif_open,
|
|
|
|
.close = snd_ymfpci_playback_spdif_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_ymfpci_playback_hw_params,
|
|
|
|
.hw_free = snd_ymfpci_playback_hw_free,
|
|
|
|
.prepare = snd_ymfpci_playback_prepare,
|
|
|
|
.trigger = snd_ymfpci_playback_trigger,
|
|
|
|
.pointer = snd_ymfpci_playback_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
int __devinit snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = NULL;
|
|
|
|
if ((err = snd_pcm_new(chip->card, "YMFPCI - IEC958", device, 1, 0, &pcm)) < 0)
|
|
|
|
return err;
|
|
|
|
pcm->private_data = chip;
|
|
|
|
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_spdif_ops);
|
|
|
|
|
|
|
|
/* global setup */
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
strcpy(pcm->name, "YMFPCI - IEC958");
|
|
|
|
chip->pcm_spdif = pcm;
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = pcm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.open = snd_ymfpci_playback_4ch_open,
|
|
|
|
.close = snd_ymfpci_playback_4ch_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_ymfpci_playback_hw_params,
|
|
|
|
.hw_free = snd_ymfpci_playback_hw_free,
|
|
|
|
.prepare = snd_ymfpci_playback_prepare,
|
|
|
|
.trigger = snd_ymfpci_playback_trigger,
|
|
|
|
.pointer = snd_ymfpci_playback_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = NULL;
|
|
|
|
if ((err = snd_pcm_new(chip->card, "YMFPCI - Rear", device, 1, 0, &pcm)) < 0)
|
|
|
|
return err;
|
|
|
|
pcm->private_data = chip;
|
|
|
|
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ymfpci_playback_4ch_ops);
|
|
|
|
|
|
|
|
/* global setup */
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
strcpy(pcm->name, "YMFPCI - Rear PCM");
|
|
|
|
chip->pcm_4ch = pcm;
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
|
|
|
|
|
|
|
|
if (rpcm)
|
|
|
|
*rpcm = pcm;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_default_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
ucontrol->value.iec958.status[0] = (chip->spdif_bits >> 0) & 0xff;
|
|
|
|
ucontrol->value.iec958.status[1] = (chip->spdif_bits >> 8) & 0xff;
|
2006-01-13 14:41:45 +08:00
|
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int val;
|
|
|
|
int change;
|
|
|
|
|
|
|
|
val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
|
|
|
|
(ucontrol->value.iec958.status[1] << 8);
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
change = chip->spdif_bits != val;
|
|
|
|
chip->spdif_bits = val;
|
|
|
|
if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 1) && chip->pcm_spdif == NULL)
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_spdif_default __devinitdata =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
|
|
|
.info = snd_ymfpci_spdif_default_info,
|
|
|
|
.get = snd_ymfpci_spdif_default_get,
|
|
|
|
.put = snd_ymfpci_spdif_default_put
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
ucontrol->value.iec958.status[0] = 0x3e;
|
|
|
|
ucontrol->value.iec958.status[1] = 0xff;
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_spdif_mask __devinitdata =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
|
|
|
|
.info = snd_ymfpci_spdif_mask_info,
|
|
|
|
.get = snd_ymfpci_spdif_mask_get,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_stream_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_stream_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
ucontrol->value.iec958.status[0] = (chip->spdif_pcm_bits >> 0) & 0xff;
|
|
|
|
ucontrol->value.iec958.status[1] = (chip->spdif_pcm_bits >> 8) & 0xff;
|
2006-01-13 14:41:45 +08:00
|
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int val;
|
|
|
|
int change;
|
|
|
|
|
|
|
|
val = ((ucontrol->value.iec958.status[0] & 0x3e) << 0) |
|
|
|
|
(ucontrol->value.iec958.status[1] << 8);
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
change = chip->spdif_pcm_bits != val;
|
|
|
|
chip->spdif_pcm_bits = val;
|
|
|
|
if ((snd_ymfpci_readw(chip, YDSXGR_SPDIFOUTCTRL) & 2))
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_pcm_bits);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM),
|
|
|
|
.info = snd_ymfpci_spdif_stream_info,
|
|
|
|
.get = snd_ymfpci_spdif_stream_get,
|
|
|
|
.put = snd_ymfpci_spdif_stream_put
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
static char *texts[3] = {"AC'97", "IEC958", "ZV Port"};
|
|
|
|
|
|
|
|
info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
info->count = 1;
|
|
|
|
info->value.enumerated.items = 3;
|
|
|
|
if (info->value.enumerated.item > 2)
|
|
|
|
info->value.enumerated.item = 2;
|
|
|
|
strcpy(info->value.enumerated.name, texts[info->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
u16 reg;
|
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
if (!(reg & 0x100))
|
|
|
|
value->value.enumerated.item[0] = 0;
|
|
|
|
else
|
|
|
|
value->value.enumerated.item[0] = 1 + ((reg & 0x200) != 0);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
u16 reg, old_reg;
|
|
|
|
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
old_reg = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
|
|
|
|
if (value->value.enumerated.item[0] == 0)
|
|
|
|
reg = old_reg & ~0x100;
|
|
|
|
else
|
|
|
|
reg = (old_reg & ~0x300) | 0x100 | ((value->value.enumerated.item[0] == 2) << 9);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, reg);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return reg != old_reg;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_drec_source __devinitdata = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Direct Recording Source",
|
|
|
|
.info = snd_ymfpci_drec_source_info,
|
|
|
|
.get = snd_ymfpci_drec_source_get,
|
|
|
|
.put = snd_ymfpci_drec_source_put
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mixer controls
|
|
|
|
*/
|
|
|
|
|
2005-09-28 14:19:05 +08:00
|
|
|
#define YMFPCI_SINGLE(xname, xindex, reg, shift) \
|
2005-04-17 06:20:36 +08:00
|
|
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
|
|
|
|
.info = snd_ymfpci_info_single, \
|
|
|
|
.get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \
|
2005-09-28 14:19:05 +08:00
|
|
|
.private_value = ((reg) | ((shift) << 16)) }
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_info_single(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-09-28 14:19:05 +08:00
|
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
|
|
|
|
|
|
switch (reg) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case YDSXGR_SPDIFOUTCTRL: break;
|
|
|
|
case YDSXGR_SPDIFINCTRL: break;
|
|
|
|
default: return -EINVAL;
|
|
|
|
}
|
2005-04-11 20:11:00 +08:00
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
2005-04-17 06:20:36 +08:00
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.integer.min = 0;
|
2005-04-11 20:11:00 +08:00
|
|
|
uinfo->value.integer.max = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_get_single(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-09-28 14:19:05 +08:00
|
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
|
|
unsigned int shift = (kcontrol->private_value >> 16) & 0xff;
|
|
|
|
unsigned int mask = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-09-28 14:19:05 +08:00
|
|
|
switch (reg) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case YDSXGR_SPDIFOUTCTRL: break;
|
|
|
|
case YDSXGR_SPDIFINCTRL: break;
|
|
|
|
default: return -EINVAL;
|
|
|
|
}
|
2005-09-28 14:19:05 +08:00
|
|
|
ucontrol->value.integer.value[0] =
|
|
|
|
(snd_ymfpci_readl(chip, reg) >> shift) & mask;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-09-28 14:19:05 +08:00
|
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
|
|
unsigned int shift = (kcontrol->private_value >> 16) & 0xff;
|
|
|
|
unsigned int mask = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
int change;
|
|
|
|
unsigned int val, oval;
|
|
|
|
|
2005-09-28 14:19:05 +08:00
|
|
|
switch (reg) {
|
2005-04-17 06:20:36 +08:00
|
|
|
case YDSXGR_SPDIFOUTCTRL: break;
|
|
|
|
case YDSXGR_SPDIFINCTRL: break;
|
|
|
|
default: return -EINVAL;
|
|
|
|
}
|
|
|
|
val = (ucontrol->value.integer.value[0] & mask);
|
|
|
|
val <<= shift;
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
oval = snd_ymfpci_readl(chip, reg);
|
|
|
|
val = (oval & ~(mask << shift)) | val;
|
|
|
|
change = val != oval;
|
|
|
|
snd_ymfpci_writel(chip, reg, val);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2007-01-29 22:33:49 +08:00
|
|
|
static const DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
|
2006-08-28 19:29:42 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#define YMFPCI_DOUBLE(xname, xindex, reg) \
|
|
|
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
|
2006-08-28 19:29:42 +08:00
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
2005-04-17 06:20:36 +08:00
|
|
|
.info = snd_ymfpci_info_double, \
|
|
|
|
.get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
|
2006-08-28 19:29:42 +08:00
|
|
|
.private_value = reg, \
|
|
|
|
.tlv = { .p = db_scale_native } }
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned int reg = kcontrol->private_value;
|
|
|
|
|
|
|
|
if (reg < 0x80 || reg >= 0xc0)
|
|
|
|
return -EINVAL;
|
2005-04-11 20:11:00 +08:00
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
2005-04-17 06:20:36 +08:00
|
|
|
uinfo->count = 2;
|
|
|
|
uinfo->value.integer.min = 0;
|
2005-04-11 20:11:00 +08:00
|
|
|
uinfo->value.integer.max = 16383;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int reg = kcontrol->private_value;
|
2005-04-11 20:11:00 +08:00
|
|
|
unsigned int shift_left = 0, shift_right = 16, mask = 16383;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
if (reg < 0x80 || reg >= 0xc0)
|
|
|
|
return -EINVAL;
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
val = snd_ymfpci_readl(chip, reg);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
ucontrol->value.integer.value[0] = (val >> shift_left) & mask;
|
|
|
|
ucontrol->value.integer.value[1] = (val >> shift_right) & mask;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int reg = kcontrol->private_value;
|
2005-04-11 20:11:00 +08:00
|
|
|
unsigned int shift_left = 0, shift_right = 16, mask = 16383;
|
2005-04-17 06:20:36 +08:00
|
|
|
int change;
|
|
|
|
unsigned int val1, val2, oval;
|
|
|
|
|
|
|
|
if (reg < 0x80 || reg >= 0xc0)
|
|
|
|
return -EINVAL;
|
|
|
|
val1 = ucontrol->value.integer.value[0] & mask;
|
|
|
|
val2 = ucontrol->value.integer.value[1] & mask;
|
|
|
|
val1 <<= shift_left;
|
|
|
|
val2 <<= shift_right;
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
oval = snd_ymfpci_readl(chip, reg);
|
|
|
|
val1 = (oval & ~((mask << shift_left) | (mask << shift_right))) | val1 | val2;
|
|
|
|
change = val1 != oval;
|
|
|
|
snd_ymfpci_writel(chip, reg, val1);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 4ch duplication
|
|
|
|
*/
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_info_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.integer.min = 0;
|
|
|
|
uinfo->value.integer.max = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_get_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
ucontrol->value.integer.value[0] = chip->mode_dup4ch;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
int change;
|
|
|
|
change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch);
|
|
|
|
if (change)
|
|
|
|
chip->mode_dup4ch = !!ucontrol->value.integer.value[0];
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = {
|
2005-04-17 06:20:36 +08:00
|
|
|
YMFPCI_DOUBLE("Wave Playback Volume", 0, YDSXGR_NATIVEDACOUTVOL),
|
|
|
|
YMFPCI_DOUBLE("Wave Capture Volume", 0, YDSXGR_NATIVEDACLOOPVOL),
|
|
|
|
YMFPCI_DOUBLE("Digital Capture Volume", 0, YDSXGR_NATIVEDACINVOL),
|
|
|
|
YMFPCI_DOUBLE("Digital Capture Volume", 1, YDSXGR_NATIVEADCINVOL),
|
|
|
|
YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL),
|
|
|
|
YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL),
|
|
|
|
YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL),
|
|
|
|
YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL),
|
|
|
|
YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL),
|
|
|
|
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL),
|
|
|
|
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL),
|
|
|
|
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL),
|
|
|
|
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,VOLUME), 1, YDSXGR_SPDIFLOOPVOL),
|
2005-09-28 14:19:05 +08:00
|
|
|
YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL, 0),
|
|
|
|
YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL, 0),
|
|
|
|
YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("Loop",NONE,NONE), 0, YDSXGR_SPDIFINCTRL, 4),
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "4ch Duplication",
|
|
|
|
.info = snd_ymfpci_info_dup4ch,
|
|
|
|
.get = snd_ymfpci_get_dup4ch,
|
|
|
|
.put = snd_ymfpci_put_dup4ch,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GPIO
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_get_gpio_out(struct snd_ymfpci *chip, int pin)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u16 reg, mode;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
|
|
reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
|
|
|
|
reg &= ~(1 << (pin + 8));
|
|
|
|
reg |= (1 << pin);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
|
|
|
|
/* set the level mode for input line */
|
|
|
|
mode = snd_ymfpci_readw(chip, YDSXGR_GPIOTYPECONFIG);
|
|
|
|
mode &= ~(3 << (pin * 2));
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GPIOTYPECONFIG, mode);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
|
|
|
|
mode = snd_ymfpci_readw(chip, YDSXGR_GPIOINSTATUS);
|
|
|
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
|
|
return (mode >> pin) & 1;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_set_gpio_out(struct snd_ymfpci *chip, int pin, int enable)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u16 reg;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
|
|
reg = snd_ymfpci_readw(chip, YDSXGR_GPIOFUNCENABLE);
|
|
|
|
reg &= ~(1 << pin);
|
|
|
|
reg &= ~(1 << (pin + 8));
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GPIOOUTCTRL, enable << pin);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GPIOFUNCENABLE, reg | (1 << (pin + 8)));
|
|
|
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_gpio_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.integer.min = 0;
|
|
|
|
uinfo->value.integer.max = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_gpio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
int pin = (int)kcontrol->private_value;
|
|
|
|
ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 06:20:36 +08:00
|
|
|
int pin = (int)kcontrol->private_value;
|
|
|
|
|
|
|
|
if (snd_ymfpci_get_gpio_out(chip, pin) != ucontrol->value.integer.value[0]) {
|
|
|
|
snd_ymfpci_set_gpio_out(chip, pin, !!ucontrol->value.integer.value[0]);
|
|
|
|
ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_rear_shared __devinitdata = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.name = "Shared Rear/Line-In Switch",
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.info = snd_ymfpci_gpio_sw_info,
|
|
|
|
.get = snd_ymfpci_gpio_sw_get,
|
|
|
|
.put = snd_ymfpci_gpio_sw_put,
|
|
|
|
.private_value = 2,
|
|
|
|
};
|
|
|
|
|
2005-08-10 16:21:43 +08:00
|
|
|
/*
|
|
|
|
* PCM voice volume
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_pcm_vol_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-08-10 16:21:43 +08:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
|
|
uinfo->count = 2;
|
|
|
|
uinfo->value.integer.min = 0;
|
|
|
|
uinfo->value.integer.max = 0x8000;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_pcm_vol_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-08-10 16:21:43 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-08-10 16:21:43 +08:00
|
|
|
unsigned int subs = kcontrol->id.subdevice;
|
|
|
|
|
|
|
|
ucontrol->value.integer.value[0] = chip->pcm_mixer[subs].left;
|
|
|
|
ucontrol->value.integer.value[1] = chip->pcm_mixer[subs].right;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-08-10 16:21:43 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-08-10 16:21:43 +08:00
|
|
|
unsigned int subs = kcontrol->id.subdevice;
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_pcm_substream *substream;
|
2005-08-10 16:21:43 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
if (ucontrol->value.integer.value[0] != chip->pcm_mixer[subs].left ||
|
|
|
|
ucontrol->value.integer.value[1] != chip->pcm_mixer[subs].right) {
|
|
|
|
chip->pcm_mixer[subs].left = ucontrol->value.integer.value[0];
|
|
|
|
chip->pcm_mixer[subs].right = ucontrol->value.integer.value[1];
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
substream = (struct snd_pcm_substream *)kcontrol->private_value;
|
2005-08-10 16:21:43 +08:00
|
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
|
|
|
if (substream->runtime && substream->runtime->private_data) {
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2006-12-05 01:03:53 +08:00
|
|
|
if (!ypcm->use_441_slot)
|
|
|
|
ypcm->update_pcm_vol = 2;
|
|
|
|
else
|
|
|
|
snd_ymfpci_pcm_441_volume_set(ypcm);
|
2005-08-10 16:21:43 +08:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&chip->voice_lock, flags);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_kcontrol_new snd_ymfpci_pcm_volume __devinitdata = {
|
2005-08-10 16:21:43 +08:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
|
.name = "PCM Playback Volume",
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
|
|
|
|
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
|
|
|
|
.info = snd_ymfpci_pcm_vol_info,
|
|
|
|
.get = snd_ymfpci_pcm_vol_get,
|
|
|
|
.put = snd_ymfpci_pcm_vol_put,
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mixer routines
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = bus->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->ac97_bus = NULL;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_mixer_free_ac97(struct snd_ac97 *ac97)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->ac97 = NULL;
|
|
|
|
}
|
|
|
|
|
2006-10-10 15:27:19 +08:00
|
|
|
int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ac97_template ac97;
|
|
|
|
struct snd_kcontrol *kctl;
|
|
|
|
struct snd_pcm_substream *substream;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int idx;
|
|
|
|
int err;
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_ac97_bus_ops ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.write = snd_ymfpci_codec_write,
|
|
|
|
.read = snd_ymfpci_codec_read,
|
|
|
|
};
|
|
|
|
|
|
|
|
if ((err = snd_ac97_bus(chip->card, 0, &ops, chip, &chip->ac97_bus)) < 0)
|
|
|
|
return err;
|
|
|
|
chip->ac97_bus->private_free = snd_ymfpci_mixer_free_ac97_bus;
|
|
|
|
chip->ac97_bus->no_vra = 1; /* YMFPCI doesn't need VRA */
|
|
|
|
|
|
|
|
memset(&ac97, 0, sizeof(ac97));
|
|
|
|
ac97.private_data = chip;
|
|
|
|
ac97.private_free = snd_ymfpci_mixer_free_ac97;
|
|
|
|
if ((err = snd_ac97_mixer(chip->ac97_bus, &ac97, &chip->ac97)) < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/* to be sure */
|
|
|
|
snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS,
|
|
|
|
AC97_EA_VRA|AC97_EA_VRM, 0);
|
|
|
|
|
|
|
|
for (idx = 0; idx < ARRAY_SIZE(snd_ymfpci_controls); idx++) {
|
|
|
|
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_controls[idx], chip))) < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add S/PDIF control */
|
|
|
|
snd_assert(chip->pcm_spdif != NULL, return -EIO);
|
|
|
|
if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_default, chip))) < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = chip->pcm_spdif->device;
|
|
|
|
if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_mask, chip))) < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = chip->pcm_spdif->device;
|
|
|
|
if ((err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_spdif_stream, chip))) < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = chip->pcm_spdif->device;
|
|
|
|
chip->spdif_pcm_ctl = kctl;
|
|
|
|
|
|
|
|
/* direct recording source */
|
|
|
|
if (chip->device_id == PCI_DEVICE_ID_YAMAHA_754 &&
|
|
|
|
(err = snd_ctl_add(chip->card, kctl = snd_ctl_new1(&snd_ymfpci_drec_source, chip))) < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* shared rear/line-in
|
|
|
|
*/
|
|
|
|
if (rear_switch) {
|
|
|
|
if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_ymfpci_rear_shared, chip))) < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2005-08-10 16:21:43 +08:00
|
|
|
/* per-voice volume */
|
|
|
|
substream = chip->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
|
|
|
for (idx = 0; idx < 32; ++idx) {
|
|
|
|
kctl = snd_ctl_new1(&snd_ymfpci_pcm_volume, chip);
|
|
|
|
if (!kctl)
|
|
|
|
return -ENOMEM;
|
|
|
|
kctl->id.device = chip->pcm->device;
|
|
|
|
kctl->id.subdevice = idx;
|
|
|
|
kctl->private_value = (unsigned long)substream;
|
|
|
|
if ((err = snd_ctl_add(chip->card, kctl)) < 0)
|
|
|
|
return err;
|
|
|
|
chip->pcm_mixer[idx].left = 0x8000;
|
|
|
|
chip->pcm_mixer[idx].right = 0x8000;
|
|
|
|
chip->pcm_mixer[idx].ctl = kctl;
|
|
|
|
substream = substream->next;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* timer
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_timer_start(struct snd_timer *timer)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
unsigned int count;
|
|
|
|
|
|
|
|
chip = snd_timer_chip(timer);
|
2005-10-19 20:39:48 +08:00
|
|
|
count = (timer->sticks << 1) - 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_TIMERCOUNT, count);
|
|
|
|
snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x03);
|
|
|
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_timer_stop(struct snd_timer *timer)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
chip = snd_timer_chip(timer);
|
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
|
|
|
snd_ymfpci_writeb(chip, YDSXGR_TIMERCTRL, 0x00);
|
|
|
|
spin_unlock_irqrestore(&chip->reg_lock, flags);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer,
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long *num, unsigned long *den)
|
|
|
|
{
|
|
|
|
*num = 1;
|
2005-10-19 20:39:48 +08:00
|
|
|
*den = 48000;
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_timer_hardware snd_ymfpci_timer_hw = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.flags = SNDRV_TIMER_HW_AUTO,
|
2005-10-19 20:39:48 +08:00
|
|
|
.resolution = 20833, /* 1/fs = 20.8333...us */
|
|
|
|
.ticks = 0x8000,
|
2005-04-17 06:20:36 +08:00
|
|
|
.start = snd_ymfpci_timer_start,
|
|
|
|
.stop = snd_ymfpci_timer_stop,
|
|
|
|
.precise_resolution = snd_ymfpci_timer_precise_resolution,
|
|
|
|
};
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
int __devinit snd_ymfpci_timer(struct snd_ymfpci *chip, int device)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_timer *timer = NULL;
|
|
|
|
struct snd_timer_id tid;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
tid.dev_class = SNDRV_TIMER_CLASS_CARD;
|
|
|
|
tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
|
|
|
|
tid.card = chip->card->number;
|
|
|
|
tid.device = device;
|
|
|
|
tid.subdevice = 0;
|
|
|
|
if ((err = snd_timer_new(chip->card, "YMFPCI", &tid, &timer)) >= 0) {
|
|
|
|
strcpy(timer->name, "YMFPCI timer");
|
|
|
|
timer->private_data = chip;
|
|
|
|
timer->hw = snd_ymfpci_timer_hw;
|
|
|
|
}
|
|
|
|
chip->timer = timer;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* proc interface
|
|
|
|
*/
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_proc_read(struct snd_info_entry *entry,
|
|
|
|
struct snd_info_buffer *buffer)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = entry->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
snd_iprintf(buffer, "YMFPCI\n\n");
|
|
|
|
for (i = 0; i <= YDSXGR_WORKBASE; i += 4)
|
|
|
|
snd_iprintf(buffer, "%04x: %04x\n", i, snd_ymfpci_readl(chip, i));
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int __devinit snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_info_entry *entry;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (! snd_card_proc_new(card, "ymfpci", &entry))
|
2006-04-28 21:13:41 +08:00
|
|
|
snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* initialization routines
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void snd_ymfpci_aclink_reset(struct pci_dev * pci)
|
|
|
|
{
|
|
|
|
u8 cmd;
|
|
|
|
|
|
|
|
pci_read_config_byte(pci, PCIR_DSXG_CTRL, &cmd);
|
|
|
|
#if 0 // force to reset
|
|
|
|
if (cmd & 0x03) {
|
|
|
|
#endif
|
|
|
|
pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
|
|
|
|
pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd | 0x03);
|
|
|
|
pci_write_config_byte(pci, PCIR_DSXG_CTRL, cmd & 0xfc);
|
|
|
|
pci_write_config_word(pci, PCIR_DSXG_PWRCTRL1, 0);
|
|
|
|
pci_write_config_word(pci, PCIR_DSXG_PWRCTRL2, 0);
|
|
|
|
#if 0
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_enable_dsp(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
int timeout = 1000;
|
|
|
|
|
|
|
|
val = snd_ymfpci_readl(chip, YDSXGR_CONFIG);
|
|
|
|
if (val)
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000000);
|
|
|
|
while (timeout-- > 0) {
|
|
|
|
val = snd_ymfpci_readl(chip, YDSXGR_STATUS);
|
|
|
|
if ((val & 0x00000002) == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-26 22:55:43 +08:00
|
|
|
#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
|
2006-10-11 18:05:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
#include "ymfpci_image.h"
|
|
|
|
|
2006-10-11 18:05:59 +08:00
|
|
|
static struct firmware snd_ymfpci_dsp_microcode = {
|
|
|
|
.size = YDSXG_DSPLENGTH,
|
|
|
|
.data = (u8 *)DspInst,
|
|
|
|
};
|
|
|
|
static struct firmware snd_ymfpci_controller_microcode = {
|
|
|
|
.size = YDSXG_CTRLLENGTH,
|
|
|
|
.data = (u8 *)CntrlInst,
|
|
|
|
};
|
|
|
|
static struct firmware snd_ymfpci_controller_1e_microcode = {
|
|
|
|
.size = YDSXG_CTRLLENGTH,
|
|
|
|
.data = (u8 *)CntrlInst1E,
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2007-04-26 20:13:44 +08:00
|
|
|
#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
|
|
|
|
static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
|
|
|
|
{
|
|
|
|
chip->dsp_microcode = &snd_ymfpci_dsp_microcode;
|
|
|
|
if (chip->device_id == PCI_DEVICE_ID_YAMAHA_724F ||
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_740C ||
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_744 ||
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_754)
|
|
|
|
chip->controller_microcode =
|
|
|
|
&snd_ymfpci_controller_1e_microcode;
|
|
|
|
else
|
|
|
|
chip->controller_microcode =
|
|
|
|
&snd_ymfpci_controller_microcode;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* use fw_loader */
|
|
|
|
|
2006-10-11 18:05:59 +08:00
|
|
|
#ifdef __LITTLE_ENDIAN
|
|
|
|
static inline void snd_ymfpci_convert_from_le(const struct firmware *fw) { }
|
|
|
|
#else
|
|
|
|
static void snd_ymfpci_convert_from_le(const struct firmware *fw)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u32 *data = (u32 *)fw->data;
|
|
|
|
|
|
|
|
for (i = 0; i < fw->size / 4; ++i)
|
|
|
|
le32_to_cpus(&data[i]);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static int snd_ymfpci_request_firmware(struct snd_ymfpci *chip)
|
|
|
|
{
|
|
|
|
int err, is_1e;
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
err = request_firmware(&chip->dsp_microcode, "yamaha/ds1_dsp.fw",
|
|
|
|
&chip->pci->dev);
|
|
|
|
if (err >= 0) {
|
|
|
|
if (chip->dsp_microcode->size == YDSXG_DSPLENGTH)
|
|
|
|
snd_ymfpci_convert_from_le(chip->dsp_microcode);
|
|
|
|
else {
|
|
|
|
snd_printk(KERN_ERR "DSP microcode has wrong size\n");
|
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2007-04-26 20:13:44 +08:00
|
|
|
if (err < 0)
|
2006-10-11 18:05:59 +08:00
|
|
|
return err;
|
|
|
|
is_1e = chip->device_id == PCI_DEVICE_ID_YAMAHA_724F ||
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_740C ||
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_744 ||
|
|
|
|
chip->device_id == PCI_DEVICE_ID_YAMAHA_754;
|
|
|
|
name = is_1e ? "yamaha/ds1e_ctrl.fw" : "yamaha/ds1_ctrl.fw";
|
|
|
|
err = request_firmware(&chip->controller_microcode, name,
|
|
|
|
&chip->pci->dev);
|
|
|
|
if (err >= 0) {
|
|
|
|
if (chip->controller_microcode->size == YDSXG_CTRLLENGTH)
|
|
|
|
snd_ymfpci_convert_from_le(chip->controller_microcode);
|
|
|
|
else {
|
|
|
|
snd_printk(KERN_ERR "controller microcode"
|
|
|
|
" has wrong size\n");
|
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2007-04-26 20:13:44 +08:00
|
|
|
if (err < 0)
|
2006-10-11 18:05:59 +08:00
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-05-03 23:59:54 +08:00
|
|
|
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1_dsp.fw");
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1_ctrl.fw");
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1e_ctrl.fw");
|
|
|
|
|
2007-04-26 20:13:44 +08:00
|
|
|
#endif
|
2006-10-11 18:05:59 +08:00
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u16 ctrl;
|
2006-10-11 18:05:59 +08:00
|
|
|
u32 *inst;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x00000000);
|
|
|
|
snd_ymfpci_disable_dsp(chip);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00010000);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE, 0x00000000);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MAPOFREC, 0x00000000);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MAPOFEFFECT, 0x00000000);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0x00000000);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0x00000000);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0x00000000);
|
|
|
|
ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
|
|
|
|
|
|
|
|
/* setup DSP instruction code */
|
2006-10-11 18:05:59 +08:00
|
|
|
inst = (u32 *)chip->dsp_microcode->data;
|
2005-04-17 06:20:36 +08:00
|
|
|
for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
|
2006-10-11 18:05:59 +08:00
|
|
|
snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* setup control instruction code */
|
2006-10-11 18:05:59 +08:00
|
|
|
inst = (u32 *)chip->controller_microcode->data;
|
2005-04-17 06:20:36 +08:00
|
|
|
for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
|
|
|
|
|
|
|
|
snd_ymfpci_enable_dsp(chip);
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
long size, playback_ctrl_size;
|
|
|
|
int voice, bank, reg;
|
|
|
|
u8 *ptr;
|
|
|
|
dma_addr_t ptr_addr;
|
|
|
|
|
|
|
|
playback_ctrl_size = 4 + 4 * YDSXG_PLAYBACK_VOICES;
|
|
|
|
chip->bank_size_playback = snd_ymfpci_readl(chip, YDSXGR_PLAYCTRLSIZE) << 2;
|
|
|
|
chip->bank_size_capture = snd_ymfpci_readl(chip, YDSXGR_RECCTRLSIZE) << 2;
|
|
|
|
chip->bank_size_effect = snd_ymfpci_readl(chip, YDSXGR_EFFCTRLSIZE) << 2;
|
|
|
|
chip->work_size = YDSXG_DEFAULT_WORK_SIZE;
|
|
|
|
|
2006-10-09 14:13:32 +08:00
|
|
|
size = ALIGN(playback_ctrl_size, 0x100) +
|
|
|
|
ALIGN(chip->bank_size_playback * 2 * YDSXG_PLAYBACK_VOICES, 0x100) +
|
|
|
|
ALIGN(chip->bank_size_capture * 2 * YDSXG_CAPTURE_VOICES, 0x100) +
|
|
|
|
ALIGN(chip->bank_size_effect * 2 * YDSXG_EFFECT_VOICES, 0x100) +
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->work_size;
|
|
|
|
/* work_ptr must be aligned to 256 bytes, but it's already
|
|
|
|
covered with the kernel page allocation mechanism */
|
|
|
|
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
|
|
|
|
size, &chip->work_ptr) < 0)
|
|
|
|
return -ENOMEM;
|
|
|
|
ptr = chip->work_ptr.area;
|
|
|
|
ptr_addr = chip->work_ptr.addr;
|
|
|
|
memset(ptr, 0, size); /* for sure */
|
|
|
|
|
|
|
|
chip->bank_base_playback = ptr;
|
|
|
|
chip->bank_base_playback_addr = ptr_addr;
|
|
|
|
chip->ctrl_playback = (u32 *)ptr;
|
|
|
|
chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
|
2006-10-09 14:13:32 +08:00
|
|
|
ptr += ALIGN(playback_ctrl_size, 0x100);
|
|
|
|
ptr_addr += ALIGN(playback_ctrl_size, 0x100);
|
2005-04-17 06:20:36 +08:00
|
|
|
for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
|
|
|
|
chip->voices[voice].number = voice;
|
2005-11-17 21:53:41 +08:00
|
|
|
chip->voices[voice].bank = (struct snd_ymfpci_playback_bank *)ptr;
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->voices[voice].bank_addr = ptr_addr;
|
|
|
|
for (bank = 0; bank < 2; bank++) {
|
2005-11-17 21:53:41 +08:00
|
|
|
chip->bank_playback[voice][bank] = (struct snd_ymfpci_playback_bank *)ptr;
|
2005-04-17 06:20:36 +08:00
|
|
|
ptr += chip->bank_size_playback;
|
|
|
|
ptr_addr += chip->bank_size_playback;
|
|
|
|
}
|
|
|
|
}
|
2006-10-09 14:13:32 +08:00
|
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->bank_base_capture = ptr;
|
|
|
|
chip->bank_base_capture_addr = ptr_addr;
|
|
|
|
for (voice = 0; voice < YDSXG_CAPTURE_VOICES; voice++)
|
|
|
|
for (bank = 0; bank < 2; bank++) {
|
2005-11-17 21:53:41 +08:00
|
|
|
chip->bank_capture[voice][bank] = (struct snd_ymfpci_capture_bank *)ptr;
|
2005-04-17 06:20:36 +08:00
|
|
|
ptr += chip->bank_size_capture;
|
|
|
|
ptr_addr += chip->bank_size_capture;
|
|
|
|
}
|
2006-10-09 14:13:32 +08:00
|
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->bank_base_effect = ptr;
|
|
|
|
chip->bank_base_effect_addr = ptr_addr;
|
|
|
|
for (voice = 0; voice < YDSXG_EFFECT_VOICES; voice++)
|
|
|
|
for (bank = 0; bank < 2; bank++) {
|
2005-11-17 21:53:41 +08:00
|
|
|
chip->bank_effect[voice][bank] = (struct snd_ymfpci_effect_bank *)ptr;
|
2005-04-17 06:20:36 +08:00
|
|
|
ptr += chip->bank_size_effect;
|
|
|
|
ptr_addr += chip->bank_size_effect;
|
|
|
|
}
|
2006-10-09 14:13:32 +08:00
|
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->work_base = ptr;
|
|
|
|
chip->work_base_addr = ptr_addr;
|
|
|
|
|
|
|
|
snd_assert(ptr + chip->work_size == chip->work_ptr.area + chip->work_ptr.bytes, );
|
|
|
|
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, chip->bank_base_playback_addr);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, chip->bank_base_capture_addr);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, chip->bank_base_effect_addr);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_WORKBASE, chip->work_base_addr);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, chip->work_size >> 2);
|
|
|
|
|
|
|
|
/* S/PDIF output initialization */
|
|
|
|
chip->spdif_bits = chip->spdif_pcm_bits = SNDRV_PCM_DEFAULT_CON_SPDIF & 0xffff;
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTCTRL, 0);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFOUTSTATUS, chip->spdif_bits);
|
|
|
|
|
|
|
|
/* S/PDIF input initialization */
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_SPDIFINCTRL, 0);
|
|
|
|
|
|
|
|
/* digital mixer setup */
|
|
|
|
for (reg = 0x80; reg < 0xc0; reg += 4)
|
|
|
|
snd_ymfpci_writel(chip, reg, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0x3fff3fff);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_ZVOUTVOL, 0x3fff3fff);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_SPDIFOUTVOL, 0x3fff3fff);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEADCINVOL, 0x3fff3fff);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACINVOL, 0x3fff3fff);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_PRIADCLOOPVOL, 0x3fff3fff);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0x3fff3fff);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_free(struct snd_ymfpci *chip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
u16 ctrl;
|
|
|
|
|
|
|
|
snd_assert(chip != NULL, return -EINVAL);
|
|
|
|
|
|
|
|
if (chip->res_reg_area) { /* don't touch busy hardware */
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_LEGACYOUTVOL, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_STATUS, ~0);
|
|
|
|
snd_ymfpci_disable_dsp(chip);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_PLAYCTRLBASE, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_RECCTRLBASE, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_EFFCTRLBASE, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_WORKBASE, 0);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_WORKSIZE, 0);
|
|
|
|
ctrl = snd_ymfpci_readw(chip, YDSXGR_GLOBALCTRL);
|
|
|
|
snd_ymfpci_writew(chip, YDSXGR_GLOBALCTRL, ctrl & ~0x0007);
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_ymfpci_ac3_done(chip);
|
|
|
|
|
|
|
|
/* Set PCI device to D3 state */
|
|
|
|
#if 0
|
|
|
|
/* FIXME: temporarily disabled, otherwise we cannot fire up
|
|
|
|
* the chip again unless reboot. ACPI bug?
|
|
|
|
*/
|
|
|
|
pci_set_power_state(chip->pci, 3);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
vfree(chip->saved_regs);
|
|
|
|
#endif
|
2005-10-10 17:56:31 +08:00
|
|
|
release_and_free_resource(chip->mpu_res);
|
|
|
|
release_and_free_resource(chip->fm_res);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_ymfpci_free_gameport(chip);
|
|
|
|
if (chip->reg_area_virt)
|
|
|
|
iounmap(chip->reg_area_virt);
|
|
|
|
if (chip->work_ptr.area)
|
|
|
|
snd_dma_free_pages(&chip->work_ptr);
|
|
|
|
|
|
|
|
if (chip->irq >= 0)
|
2006-11-21 19:14:23 +08:00
|
|
|
free_irq(chip->irq, chip);
|
2005-10-10 17:56:31 +08:00
|
|
|
release_and_free_resource(chip->res_reg_area);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
|
|
|
|
|
|
|
|
pci_disable_device(chip->pci);
|
2007-04-26 20:13:44 +08:00
|
|
|
#ifndef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
|
|
|
|
release_firmware(chip->dsp_microcode);
|
|
|
|
release_firmware(chip->controller_microcode);
|
2006-10-11 18:05:59 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
kfree(chip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
static int snd_ymfpci_dev_free(struct snd_device *device)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip = device->device_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
return snd_ymfpci_free(chip);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
static int saved_regs_index[] = {
|
|
|
|
/* spdif */
|
|
|
|
YDSXGR_SPDIFOUTCTRL,
|
|
|
|
YDSXGR_SPDIFOUTSTATUS,
|
|
|
|
YDSXGR_SPDIFINCTRL,
|
|
|
|
/* volumes */
|
|
|
|
YDSXGR_PRIADCLOOPVOL,
|
|
|
|
YDSXGR_NATIVEDACINVOL,
|
|
|
|
YDSXGR_NATIVEDACOUTVOL,
|
2006-12-05 01:03:53 +08:00
|
|
|
YDSXGR_BUF441OUTVOL,
|
2005-04-17 06:20:36 +08:00
|
|
|
YDSXGR_NATIVEADCINVOL,
|
|
|
|
YDSXGR_SPDIFLOOPVOL,
|
|
|
|
YDSXGR_SPDIFOUTVOL,
|
|
|
|
YDSXGR_ZVOUTVOL,
|
|
|
|
YDSXGR_LEGACYOUTVOL,
|
|
|
|
/* address bases */
|
|
|
|
YDSXGR_PLAYCTRLBASE,
|
|
|
|
YDSXGR_RECCTRLBASE,
|
|
|
|
YDSXGR_EFFCTRLBASE,
|
|
|
|
YDSXGR_WORKBASE,
|
|
|
|
/* capture set up */
|
|
|
|
YDSXGR_MAPOFREC,
|
|
|
|
YDSXGR_RECFORMAT,
|
|
|
|
YDSXGR_RECSLOTSR,
|
|
|
|
YDSXGR_ADCFORMAT,
|
|
|
|
YDSXGR_ADCSLOTSR,
|
|
|
|
};
|
|
|
|
#define YDSXGR_NUM_SAVED_REGS ARRAY_SIZE(saved_regs_index)
|
|
|
|
|
2005-11-17 23:09:43 +08:00
|
|
|
int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 23:09:43 +08:00
|
|
|
struct snd_card *card = pci_get_drvdata(pci);
|
|
|
|
struct snd_ymfpci *chip = card->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int i;
|
|
|
|
|
2005-11-17 23:09:43 +08:00
|
|
|
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_pcm_suspend_all(chip->pcm);
|
|
|
|
snd_pcm_suspend_all(chip->pcm2);
|
|
|
|
snd_pcm_suspend_all(chip->pcm_spdif);
|
|
|
|
snd_pcm_suspend_all(chip->pcm_4ch);
|
|
|
|
snd_ac97_suspend(chip->ac97);
|
|
|
|
for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
|
|
|
|
chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]);
|
|
|
|
chip->saved_ydsxgr_mode = snd_ymfpci_readl(chip, YDSXGR_MODE);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
|
|
|
|
snd_ymfpci_disable_dsp(chip);
|
2005-11-17 23:09:43 +08:00
|
|
|
pci_disable_device(pci);
|
|
|
|
pci_save_state(pci);
|
2006-10-12 00:52:53 +08:00
|
|
|
pci_set_power_state(pci, pci_choose_state(pci, state));
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 23:09:43 +08:00
|
|
|
int snd_ymfpci_resume(struct pci_dev *pci)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 23:09:43 +08:00
|
|
|
struct snd_card *card = pci_get_drvdata(pci);
|
|
|
|
struct snd_ymfpci *chip = card->private_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned int i;
|
|
|
|
|
2006-10-12 00:52:53 +08:00
|
|
|
pci_set_power_state(pci, PCI_D0);
|
2005-11-17 23:09:43 +08:00
|
|
|
pci_restore_state(pci);
|
2006-10-12 00:52:53 +08:00
|
|
|
if (pci_enable_device(pci) < 0) {
|
|
|
|
printk(KERN_ERR "ymfpci: pci_enable_device failed, "
|
|
|
|
"disabling device\n");
|
|
|
|
snd_card_disconnect(card);
|
|
|
|
return -EIO;
|
|
|
|
}
|
2005-11-17 23:09:43 +08:00
|
|
|
pci_set_master(pci);
|
|
|
|
snd_ymfpci_aclink_reset(pci);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_ymfpci_codec_ready(chip, 0);
|
|
|
|
snd_ymfpci_download_image(chip);
|
|
|
|
udelay(100);
|
|
|
|
|
|
|
|
for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++)
|
|
|
|
snd_ymfpci_writel(chip, saved_regs_index[i], chip->saved_regs[i]);
|
|
|
|
|
|
|
|
snd_ac97_resume(chip->ac97);
|
|
|
|
|
|
|
|
/* start hw again */
|
|
|
|
if (chip->start_count > 0) {
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_MODE, chip->saved_ydsxgr_mode);
|
|
|
|
chip->active_bank = snd_ymfpci_readl(chip, YDSXGR_CTRLSELECT);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
}
|
2005-11-17 23:09:43 +08:00
|
|
|
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_PM */
|
|
|
|
|
2005-11-17 21:53:41 +08:00
|
|
|
int __devinit snd_ymfpci_create(struct snd_card *card,
|
2005-04-17 06:20:36 +08:00
|
|
|
struct pci_dev * pci,
|
|
|
|
unsigned short old_legacy_ctrl,
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci ** rchip)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-17 21:53:41 +08:00
|
|
|
struct snd_ymfpci *chip;
|
2005-04-17 06:20:36 +08:00
|
|
|
int err;
|
2005-11-17 21:53:41 +08:00
|
|
|
static struct snd_device_ops ops = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.dev_free = snd_ymfpci_dev_free,
|
|
|
|
};
|
|
|
|
|
|
|
|
*rchip = NULL;
|
|
|
|
|
|
|
|
/* enable PCI device */
|
|
|
|
if ((err = pci_enable_device(pci)) < 0)
|
|
|
|
return err;
|
|
|
|
|
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 20:21:46 +08:00
|
|
|
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (chip == NULL) {
|
|
|
|
pci_disable_device(pci);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
chip->old_legacy_ctrl = old_legacy_ctrl;
|
|
|
|
spin_lock_init(&chip->reg_lock);
|
|
|
|
spin_lock_init(&chip->voice_lock);
|
|
|
|
init_waitqueue_head(&chip->interrupt_sleep);
|
|
|
|
atomic_set(&chip->interrupt_sleep_count, 0);
|
|
|
|
chip->card = card;
|
|
|
|
chip->pci = pci;
|
|
|
|
chip->irq = -1;
|
|
|
|
chip->device_id = pci->device;
|
2007-06-09 06:46:36 +08:00
|
|
|
chip->rev = pci->revision;
|
2005-04-17 06:20:36 +08:00
|
|
|
chip->reg_area_phys = pci_resource_start(pci, 0);
|
|
|
|
chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000);
|
|
|
|
pci_set_master(pci);
|
2006-12-05 01:03:53 +08:00
|
|
|
chip->src441_used = -1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
|
2005-10-21 00:26:44 +08:00
|
|
|
snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
2006-11-21 19:14:23 +08:00
|
|
|
if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
|
|
|
|
"YMFPCI", chip)) {
|
2005-10-21 00:26:44 +08:00
|
|
|
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
chip->irq = pci->irq;
|
|
|
|
|
|
|
|
snd_ymfpci_aclink_reset(pci);
|
|
|
|
if (snd_ymfpci_codec_ready(chip, 0) < 0) {
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2006-10-11 18:05:59 +08:00
|
|
|
err = snd_ymfpci_request_firmware(chip);
|
|
|
|
if (err < 0) {
|
|
|
|
snd_printk(KERN_ERR "firmware request failed: %d\n", err);
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
snd_ymfpci_download_image(chip);
|
|
|
|
|
|
|
|
udelay(100); /* seems we need a delay after downloading image.. */
|
|
|
|
|
|
|
|
if (snd_ymfpci_memalloc(chip) < 0) {
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = snd_ymfpci_ac3_init(chip)) < 0) {
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef CONFIG_PM
|
|
|
|
chip->saved_regs = vmalloc(YDSXGR_NUM_SAVED_REGS * sizeof(u32));
|
|
|
|
if (chip->saved_regs == NULL) {
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_ymfpci_proc_init(card, chip);
|
|
|
|
|
|
|
|
snd_card_set_dev(card, &pci->dev);
|
|
|
|
|
|
|
|
*rchip = chip;
|
|
|
|
return 0;
|
|
|
|
}
|