44c10138fd
Instead of all drivers reading pci config space to get the revision ID, they can now use the pci_device->revision member. This exposes some issues where drivers where reading a word or a dword for the revision number, and adding useless error-handling around the read. Some drivers even just read it for no purpose of all. In devices where the revision ID is being copied over and used in what appears to be the equivalent of hotpath, I have left the copy code and the cached copy as not to influence the driver's performance. Compile tested with make all{yes,mod}config on x86_64 and i386. Signed-off-by: Auke Kok <auke-jan.h.kok@intel.com> Acked-by: Dave Jones <davej@redhat.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2472 lines
72 KiB
C
2472 lines
72 KiB
C
/*
|
|
* 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>
|
|
#include <linux/firmware.h>
|
|
#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>
|
|
#include <sound/tlv.h>
|
|
#include <sound/ymfpci.h>
|
|
#include <sound/asoundef.h>
|
|
#include <sound/mpu401.h>
|
|
|
|
#include <asm/io.h>
|
|
#include <asm/byteorder.h>
|
|
|
|
/*
|
|
* common I/O routines
|
|
*/
|
|
|
|
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip);
|
|
|
|
static inline u8 snd_ymfpci_readb(struct snd_ymfpci *chip, u32 offset)
|
|
{
|
|
return readb(chip->reg_area_virt + offset);
|
|
}
|
|
|
|
static inline void snd_ymfpci_writeb(struct snd_ymfpci *chip, u32 offset, u8 val)
|
|
{
|
|
writeb(val, chip->reg_area_virt + offset);
|
|
}
|
|
|
|
static inline u16 snd_ymfpci_readw(struct snd_ymfpci *chip, u32 offset)
|
|
{
|
|
return readw(chip->reg_area_virt + offset);
|
|
}
|
|
|
|
static inline void snd_ymfpci_writew(struct snd_ymfpci *chip, u32 offset, u16 val)
|
|
{
|
|
writew(val, chip->reg_area_virt + offset);
|
|
}
|
|
|
|
static inline u32 snd_ymfpci_readl(struct snd_ymfpci *chip, u32 offset)
|
|
{
|
|
return readl(chip->reg_area_virt + offset);
|
|
}
|
|
|
|
static inline void snd_ymfpci_writel(struct snd_ymfpci *chip, u32 offset, u32 val)
|
|
{
|
|
writel(val, chip->reg_area_virt + offset);
|
|
}
|
|
|
|
static int snd_ymfpci_codec_ready(struct snd_ymfpci *chip, int secondary)
|
|
{
|
|
unsigned long end_time;
|
|
u32 reg = secondary ? YDSXGR_SECSTATUSADR : YDSXGR_PRISTATUSADR;
|
|
|
|
end_time = jiffies + msecs_to_jiffies(750);
|
|
do {
|
|
if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
|
|
return 0;
|
|
set_current_state(TASK_UNINTERRUPTIBLE);
|
|
schedule_timeout_uninterruptible(1);
|
|
} while (time_before(jiffies, end_time));
|
|
snd_printk(KERN_ERR "codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_ymfpci_readw(chip, reg));
|
|
return -EBUSY;
|
|
}
|
|
|
|
static void snd_ymfpci_codec_write(struct snd_ac97 *ac97, u16 reg, u16 val)
|
|
{
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
|
u32 cmd;
|
|
|
|
snd_ymfpci_codec_ready(chip, 0);
|
|
cmd = ((YDSXG_AC97WRITECMD | reg) << 16) | val;
|
|
snd_ymfpci_writel(chip, YDSXGR_AC97CMDDATA, cmd);
|
|
}
|
|
|
|
static u16 snd_ymfpci_codec_read(struct snd_ac97 *ac97, u16 reg)
|
|
{
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
|
|
|
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];
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Hardware start management
|
|
*/
|
|
|
|
static void snd_ymfpci_hw_start(struct snd_ymfpci *chip)
|
|
{
|
|
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);
|
|
}
|
|
|
|
static void snd_ymfpci_hw_stop(struct snd_ymfpci *chip)
|
|
{
|
|
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
|
|
*/
|
|
|
|
static int voice_alloc(struct snd_ymfpci *chip,
|
|
enum snd_ymfpci_voice_type type, int pair,
|
|
struct snd_ymfpci_voice **rvoice)
|
|
{
|
|
struct snd_ymfpci_voice *voice, *voice2;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_voice_alloc(struct snd_ymfpci *chip,
|
|
enum snd_ymfpci_voice_type type, int pair,
|
|
struct snd_ymfpci_voice **rvoice)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voice *pvoice)
|
|
{
|
|
unsigned long flags;
|
|
|
|
snd_assert(pvoice != NULL, return -EINVAL);
|
|
snd_ymfpci_hw_stop(chip);
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
|
if (pvoice->number == chip->src441_used) {
|
|
chip->src441_used = -1;
|
|
pvoice->ypcm->use_441_slot = 0;
|
|
}
|
|
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
|
|
*/
|
|
|
|
static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice)
|
|
{
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
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);
|
|
}
|
|
|
|
if (unlikely(ypcm->update_pcm_vol)) {
|
|
unsigned int subs = ypcm->substream->number;
|
|
unsigned int next_bank = 1 - chip->active_bank;
|
|
struct snd_ymfpci_playback_bank *bank;
|
|
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--;
|
|
}
|
|
}
|
|
spin_unlock(&chip->reg_lock);
|
|
}
|
|
|
|
static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
struct snd_ymfpci *chip = ypcm->chip;
|
|
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);
|
|
}
|
|
|
|
static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
|
|
int cmd)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
|
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);
|
|
if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
|
|
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;
|
|
if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
|
|
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;
|
|
}
|
|
static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream,
|
|
int cmd)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_pcm_voice_alloc(struct snd_ymfpci_pcm *ypcm, int voices)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int voiceidx,
|
|
struct snd_pcm_runtime *runtime,
|
|
int has_pcm_volume)
|
|
{
|
|
struct snd_ymfpci_voice *voice = ypcm->voices[voiceidx];
|
|
u32 format;
|
|
u32 delta = snd_ymfpci_calc_delta(runtime->rate);
|
|
u32 lpfQ = snd_ymfpci_calc_lpfQ(runtime->rate);
|
|
u32 lpfK = snd_ymfpci_calc_lpfK(runtime->rate);
|
|
struct snd_ymfpci_playback_bank *bank;
|
|
unsigned int nbank;
|
|
u32 vol_left, vol_right;
|
|
u8 use_left, use_right;
|
|
unsigned long flags;
|
|
|
|
snd_assert(voice != NULL, return);
|
|
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);
|
|
}
|
|
spin_lock_irqsave(&ypcm->chip->voice_lock, flags);
|
|
format = runtime->channels == 2 ? 0x00010000 : 0;
|
|
if (snd_pcm_format_width(runtime->format) == 8)
|
|
format |= 0x80000000;
|
|
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;
|
|
}
|
|
if (runtime->channels == 2 && (voiceidx & 1) != 0)
|
|
format |= 1;
|
|
spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags);
|
|
for (nbank = 0; nbank < 2; nbank++) {
|
|
bank = &voice->bank[nbank];
|
|
memset(bank, 0, sizeof(*bank));
|
|
bank->format = cpu_to_le32(format);
|
|
bank->base = cpu_to_le32(runtime->dma_addr);
|
|
bank->loop_end = cpu_to_le32(ypcm->buffer_size);
|
|
bank->lpfQ = cpu_to_le32(lpfQ);
|
|
bank->delta =
|
|
bank->delta_end = cpu_to_le32(delta);
|
|
bank->lpfK =
|
|
bank->lpfK_end = cpu_to_le32(lpfK);
|
|
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) {
|
|
bank->right_gain =
|
|
bank->right_gain_end = vol_right;
|
|
}
|
|
}
|
|
if (ypcm->output_rear) {
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int __devinit snd_ymfpci_ac3_init(struct snd_ymfpci *chip)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_ac3_done(struct snd_ymfpci *chip)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_playback_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *hw_params)
|
|
{
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_playback_hw_free(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
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++)
|
|
snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime,
|
|
substream->pcm == chip->pcm);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_capture_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *hw_params)
|
|
{
|
|
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
|
}
|
|
|
|
static int snd_ymfpci_capture_hw_free(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
/* wait, until the PCI operations are not finished */
|
|
snd_ymfpci_irq_wait(chip);
|
|
return snd_pcm_lib_free_pages(substream);
|
|
}
|
|
|
|
static int snd_ymfpci_capture_prepare(struct snd_pcm_substream *substream)
|
|
{
|
|
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;
|
|
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;
|
|
}
|
|
|
|
static snd_pcm_uframes_t snd_ymfpci_playback_pointer(struct snd_pcm_substream *substream)
|
|
{
|
|
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];
|
|
|
|
if (!(ypcm->running && voice))
|
|
return 0;
|
|
return le32_to_cpu(voice->bank[chip->active_bank].start);
|
|
}
|
|
|
|
static snd_pcm_uframes_t snd_ymfpci_capture_pointer(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
|
|
if (!ypcm->running)
|
|
return 0;
|
|
return le32_to_cpu(chip->bank_capture[ypcm->capture_bank_number][chip->active_bank]->start) >> ypcm->shift;
|
|
}
|
|
|
|
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip)
|
|
{
|
|
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);
|
|
schedule_timeout_uninterruptible(msecs_to_jiffies(50));
|
|
remove_wait_queue(&chip->interrupt_sleep, &wait);
|
|
}
|
|
}
|
|
|
|
static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
|
|
{
|
|
struct snd_ymfpci *chip = dev_id;
|
|
u32 status, nvoice, mode;
|
|
struct snd_ymfpci_voice *voice;
|
|
|
|
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)
|
|
snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static struct snd_pcm_hardware snd_ymfpci_playback =
|
|
{
|
|
.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,
|
|
};
|
|
|
|
static struct snd_pcm_hardware snd_ymfpci_capture =
|
|
{
|
|
.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,
|
|
};
|
|
|
|
static void snd_ymfpci_pcm_free_substream(struct snd_pcm_runtime *runtime)
|
|
{
|
|
kfree(runtime->private_data);
|
|
}
|
|
|
|
static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
|
|
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
|
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 */
|
|
static void ymfpci_open_extension(struct snd_ymfpci *chip)
|
|
{
|
|
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 */
|
|
static void ymfpci_close_extension(struct snd_ymfpci *chip)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
static int snd_ymfpci_playback_open(struct snd_pcm_substream *substream)
|
|
{
|
|
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;
|
|
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;
|
|
ypcm->swap_rear = 0;
|
|
spin_lock_irq(&chip->reg_lock);
|
|
if (ypcm->output_rear) {
|
|
ymfpci_open_extension(chip);
|
|
chip->rear_opened++;
|
|
}
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
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);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_playback_spdif_open(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
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;
|
|
ypcm->swap_rear = 1;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_playback_4ch_open(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
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;
|
|
ypcm->swap_rear = 0;
|
|
spin_lock_irq(&chip->reg_lock);
|
|
ymfpci_open_extension(chip);
|
|
chip->rear_opened++;
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
|
|
u32 capture_bank_number)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm;
|
|
|
|
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_capture_rec_open(struct snd_pcm_substream *substream)
|
|
{
|
|
return snd_ymfpci_capture_open(substream, 0);
|
|
}
|
|
|
|
static int snd_ymfpci_capture_ac97_open(struct snd_pcm_substream *substream)
|
|
{
|
|
return snd_ymfpci_capture_open(substream, 1);
|
|
}
|
|
|
|
static int snd_ymfpci_playback_close_1(struct snd_pcm_substream *substream)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_playback_close(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
|
struct snd_kcontrol *kctl;
|
|
|
|
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);
|
|
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);
|
|
return snd_ymfpci_playback_close_1(substream);
|
|
}
|
|
|
|
static int snd_ymfpci_playback_spdif_close(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
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);
|
|
}
|
|
|
|
static int snd_ymfpci_playback_4ch_close(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
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);
|
|
}
|
|
|
|
static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream)
|
|
{
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
|
|
|
if (ypcm != NULL) {
|
|
chip->capture_substream[ypcm->capture_bank_number] = NULL;
|
|
snd_ymfpci_hw_stop(chip);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct snd_pcm_ops snd_ymfpci_playback_ops = {
|
|
.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,
|
|
};
|
|
|
|
static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
|
|
.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,
|
|
};
|
|
|
|
int __devinit snd_ymfpci_pcm(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
|
{
|
|
struct snd_pcm *pcm;
|
|
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;
|
|
}
|
|
|
|
static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
|
|
.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,
|
|
};
|
|
|
|
int __devinit snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
|
{
|
|
struct snd_pcm *pcm;
|
|
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;
|
|
}
|
|
|
|
static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
|
|
.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,
|
|
};
|
|
|
|
int __devinit snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
|
{
|
|
struct snd_pcm *pcm;
|
|
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;
|
|
}
|
|
|
|
static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
|
|
.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,
|
|
};
|
|
|
|
int __devinit snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device, struct snd_pcm ** rpcm)
|
|
{
|
|
struct snd_pcm *pcm;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_spdif_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
uinfo->count = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_spdif_default_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
|
|
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;
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
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;
|
|
}
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_spdif_default __devinitdata =
|
|
{
|
|
.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
|
|
};
|
|
|
|
static int snd_ymfpci_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
uinfo->count = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
|
|
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;
|
|
}
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_spdif_mask __devinitdata =
|
|
{
|
|
.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,
|
|
};
|
|
|
|
static int snd_ymfpci_spdif_stream_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
uinfo->count = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_spdif_stream_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
|
|
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;
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
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;
|
|
}
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_spdif_stream __devinitdata =
|
|
{
|
|
.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
|
|
};
|
|
|
|
static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
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;
|
|
}
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_drec_source __devinitdata = {
|
|
.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
|
|
*/
|
|
|
|
#define YMFPCI_SINGLE(xname, xindex, reg, shift) \
|
|
{ .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, \
|
|
.private_value = ((reg) | ((shift) << 16)) }
|
|
|
|
static int snd_ymfpci_info_single(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
|
|
switch (reg) {
|
|
case YDSXGR_SPDIFOUTCTRL: break;
|
|
case YDSXGR_SPDIFINCTRL: break;
|
|
default: return -EINVAL;
|
|
}
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
|
uinfo->count = 1;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_get_single(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
unsigned int shift = (kcontrol->private_value >> 16) & 0xff;
|
|
unsigned int mask = 1;
|
|
|
|
switch (reg) {
|
|
case YDSXGR_SPDIFOUTCTRL: break;
|
|
case YDSXGR_SPDIFINCTRL: break;
|
|
default: return -EINVAL;
|
|
}
|
|
ucontrol->value.integer.value[0] =
|
|
(snd_ymfpci_readl(chip, reg) >> shift) & mask;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
unsigned int shift = (kcontrol->private_value >> 16) & 0xff;
|
|
unsigned int mask = 1;
|
|
int change;
|
|
unsigned int val, oval;
|
|
|
|
switch (reg) {
|
|
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;
|
|
}
|
|
|
|
static const DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
|
|
|
|
#define YMFPCI_DOUBLE(xname, xindex, reg) \
|
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
|
.info = snd_ymfpci_info_double, \
|
|
.get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
|
|
.private_value = reg, \
|
|
.tlv = { .p = db_scale_native } }
|
|
|
|
static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
unsigned int reg = kcontrol->private_value;
|
|
|
|
if (reg < 0x80 || reg >= 0xc0)
|
|
return -EINVAL;
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = 2;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 16383;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
unsigned int reg = kcontrol->private_value;
|
|
unsigned int shift_left = 0, shift_right = 16, mask = 16383;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
unsigned int reg = kcontrol->private_value;
|
|
unsigned int shift_left = 0, shift_right = 16, mask = 16383;
|
|
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
|
|
*/
|
|
static int snd_ymfpci_info_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
|
uinfo->count = 1;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_get_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
ucontrol->value.integer.value[0] = chip->mode_dup4ch;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
int change;
|
|
change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch);
|
|
if (change)
|
|
chip->mode_dup4ch = !!ucontrol->value.integer.value[0];
|
|
return change;
|
|
}
|
|
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_controls[] __devinitdata = {
|
|
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),
|
|
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),
|
|
{
|
|
.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
|
|
*/
|
|
|
|
static int snd_ymfpci_get_gpio_out(struct snd_ymfpci *chip, int pin)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_set_gpio_out(struct snd_ymfpci *chip, int pin, int enable)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_gpio_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
|
|
uinfo->count = 1;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 1;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_gpio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
int pin = (int)kcontrol->private_value;
|
|
ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
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;
|
|
}
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_rear_shared __devinitdata = {
|
|
.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,
|
|
};
|
|
|
|
/*
|
|
* PCM voice volume
|
|
*/
|
|
|
|
static int snd_ymfpci_pcm_vol_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
uinfo->count = 2;
|
|
uinfo->value.integer.min = 0;
|
|
uinfo->value.integer.max = 0x8000;
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_pcm_vol_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
unsigned int subs = kcontrol->id.subdevice;
|
|
struct snd_pcm_substream *substream;
|
|
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];
|
|
|
|
substream = (struct snd_pcm_substream *)kcontrol->private_value;
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
|
if (substream->runtime && substream->runtime->private_data) {
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
|
if (!ypcm->use_441_slot)
|
|
ypcm->update_pcm_vol = 2;
|
|
else
|
|
snd_ymfpci_pcm_441_volume_set(ypcm);
|
|
}
|
|
spin_unlock_irqrestore(&chip->voice_lock, flags);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static struct snd_kcontrol_new snd_ymfpci_pcm_volume __devinitdata = {
|
|
.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,
|
|
};
|
|
|
|
|
|
/*
|
|
* Mixer routines
|
|
*/
|
|
|
|
static void snd_ymfpci_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
|
|
{
|
|
struct snd_ymfpci *chip = bus->private_data;
|
|
chip->ac97_bus = NULL;
|
|
}
|
|
|
|
static void snd_ymfpci_mixer_free_ac97(struct snd_ac97 *ac97)
|
|
{
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
|
chip->ac97 = NULL;
|
|
}
|
|
|
|
int __devinit snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
|
|
{
|
|
struct snd_ac97_template ac97;
|
|
struct snd_kcontrol *kctl;
|
|
struct snd_pcm_substream *substream;
|
|
unsigned int idx;
|
|
int err;
|
|
static struct snd_ac97_bus_ops ops = {
|
|
.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;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* timer
|
|
*/
|
|
|
|
static int snd_ymfpci_timer_start(struct snd_timer *timer)
|
|
{
|
|
struct snd_ymfpci *chip;
|
|
unsigned long flags;
|
|
unsigned int count;
|
|
|
|
chip = snd_timer_chip(timer);
|
|
count = (timer->sticks << 1) - 1;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_timer_stop(struct snd_timer *timer)
|
|
{
|
|
struct snd_ymfpci *chip;
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer,
|
|
unsigned long *num, unsigned long *den)
|
|
{
|
|
*num = 1;
|
|
*den = 48000;
|
|
return 0;
|
|
}
|
|
|
|
static struct snd_timer_hardware snd_ymfpci_timer_hw = {
|
|
.flags = SNDRV_TIMER_HW_AUTO,
|
|
.resolution = 20833, /* 1/fs = 20.8333...us */
|
|
.ticks = 0x8000,
|
|
.start = snd_ymfpci_timer_start,
|
|
.stop = snd_ymfpci_timer_stop,
|
|
.precise_resolution = snd_ymfpci_timer_precise_resolution,
|
|
};
|
|
|
|
int __devinit snd_ymfpci_timer(struct snd_ymfpci *chip, int device)
|
|
{
|
|
struct snd_timer *timer = NULL;
|
|
struct snd_timer_id tid;
|
|
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
|
|
*/
|
|
|
|
static void snd_ymfpci_proc_read(struct snd_info_entry *entry,
|
|
struct snd_info_buffer *buffer)
|
|
{
|
|
struct snd_ymfpci *chip = entry->private_data;
|
|
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));
|
|
}
|
|
|
|
static int __devinit snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip)
|
|
{
|
|
struct snd_info_entry *entry;
|
|
|
|
if (! snd_card_proc_new(card, "ymfpci", &entry))
|
|
snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read);
|
|
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
|
|
}
|
|
|
|
static void snd_ymfpci_enable_dsp(struct snd_ymfpci *chip)
|
|
{
|
|
snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001);
|
|
}
|
|
|
|
static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
|
|
|
|
#include "ymfpci_image.h"
|
|
|
|
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
|
|
|
|
#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 */
|
|
|
|
#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;
|
|
}
|
|
}
|
|
if (err < 0)
|
|
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;
|
|
}
|
|
}
|
|
if (err < 0)
|
|
return err;
|
|
return 0;
|
|
}
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1_dsp.fw");
|
|
MODULE_FIRMWARE("yamaha/ds1_ctrl.fw");
|
|
MODULE_FIRMWARE("yamaha/ds1e_ctrl.fw");
|
|
|
|
#endif
|
|
|
|
static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
|
|
{
|
|
int i;
|
|
u16 ctrl;
|
|
u32 *inst;
|
|
|
|
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 */
|
|
inst = (u32 *)chip->dsp_microcode->data;
|
|
for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
|
|
snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2), inst[i]);
|
|
|
|
/* setup control instruction code */
|
|
inst = (u32 *)chip->controller_microcode->data;
|
|
for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
|
|
snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2), inst[i]);
|
|
|
|
snd_ymfpci_enable_dsp(chip);
|
|
}
|
|
|
|
static int __devinit snd_ymfpci_memalloc(struct snd_ymfpci *chip)
|
|
{
|
|
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;
|
|
|
|
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) +
|
|
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);
|
|
ptr += ALIGN(playback_ctrl_size, 0x100);
|
|
ptr_addr += ALIGN(playback_ctrl_size, 0x100);
|
|
for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
|
|
chip->voices[voice].number = voice;
|
|
chip->voices[voice].bank = (struct snd_ymfpci_playback_bank *)ptr;
|
|
chip->voices[voice].bank_addr = ptr_addr;
|
|
for (bank = 0; bank < 2; bank++) {
|
|
chip->bank_playback[voice][bank] = (struct snd_ymfpci_playback_bank *)ptr;
|
|
ptr += chip->bank_size_playback;
|
|
ptr_addr += chip->bank_size_playback;
|
|
}
|
|
}
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
|
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++) {
|
|
chip->bank_capture[voice][bank] = (struct snd_ymfpci_capture_bank *)ptr;
|
|
ptr += chip->bank_size_capture;
|
|
ptr_addr += chip->bank_size_capture;
|
|
}
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
|
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++) {
|
|
chip->bank_effect[voice][bank] = (struct snd_ymfpci_effect_bank *)ptr;
|
|
ptr += chip->bank_size_effect;
|
|
ptr_addr += chip->bank_size_effect;
|
|
}
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
|
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;
|
|
}
|
|
|
|
static int snd_ymfpci_free(struct snd_ymfpci *chip)
|
|
{
|
|
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
|
|
release_and_free_resource(chip->mpu_res);
|
|
release_and_free_resource(chip->fm_res);
|
|
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)
|
|
free_irq(chip->irq, chip);
|
|
release_and_free_resource(chip->res_reg_area);
|
|
|
|
pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
|
|
|
|
pci_disable_device(chip->pci);
|
|
#ifndef CONFIG_SND_YMFPCI_FIRMWARE_IN_KERNEL
|
|
release_firmware(chip->dsp_microcode);
|
|
release_firmware(chip->controller_microcode);
|
|
#endif
|
|
kfree(chip);
|
|
return 0;
|
|
}
|
|
|
|
static int snd_ymfpci_dev_free(struct snd_device *device)
|
|
{
|
|
struct snd_ymfpci *chip = device->device_data;
|
|
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,
|
|
YDSXGR_BUF441OUTVOL,
|
|
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)
|
|
|
|
int snd_ymfpci_suspend(struct pci_dev *pci, pm_message_t state)
|
|
{
|
|
struct snd_card *card = pci_get_drvdata(pci);
|
|
struct snd_ymfpci *chip = card->private_data;
|
|
unsigned int i;
|
|
|
|
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
|
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);
|
|
pci_disable_device(pci);
|
|
pci_save_state(pci);
|
|
pci_set_power_state(pci, pci_choose_state(pci, state));
|
|
return 0;
|
|
}
|
|
|
|
int snd_ymfpci_resume(struct pci_dev *pci)
|
|
{
|
|
struct snd_card *card = pci_get_drvdata(pci);
|
|
struct snd_ymfpci *chip = card->private_data;
|
|
unsigned int i;
|
|
|
|
pci_set_power_state(pci, PCI_D0);
|
|
pci_restore_state(pci);
|
|
if (pci_enable_device(pci) < 0) {
|
|
printk(KERN_ERR "ymfpci: pci_enable_device failed, "
|
|
"disabling device\n");
|
|
snd_card_disconnect(card);
|
|
return -EIO;
|
|
}
|
|
pci_set_master(pci);
|
|
snd_ymfpci_aclink_reset(pci);
|
|
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);
|
|
}
|
|
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
|
return 0;
|
|
}
|
|
#endif /* CONFIG_PM */
|
|
|
|
int __devinit snd_ymfpci_create(struct snd_card *card,
|
|
struct pci_dev * pci,
|
|
unsigned short old_legacy_ctrl,
|
|
struct snd_ymfpci ** rchip)
|
|
{
|
|
struct snd_ymfpci *chip;
|
|
int err;
|
|
static struct snd_device_ops ops = {
|
|
.dev_free = snd_ymfpci_dev_free,
|
|
};
|
|
|
|
*rchip = NULL;
|
|
|
|
/* enable PCI device */
|
|
if ((err = pci_enable_device(pci)) < 0)
|
|
return err;
|
|
|
|
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
|
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;
|
|
chip->rev = pci->revision;
|
|
chip->reg_area_phys = pci_resource_start(pci, 0);
|
|
chip->reg_area_virt = ioremap_nocache(chip->reg_area_phys, 0x8000);
|
|
pci_set_master(pci);
|
|
chip->src441_used = -1;
|
|
|
|
if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
|
|
snd_printk(KERN_ERR "unable to grab memory region 0x%lx-0x%lx\n", chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
|
|
snd_ymfpci_free(chip);
|
|
return -EBUSY;
|
|
}
|
|
if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
|
|
"YMFPCI", chip)) {
|
|
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
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;
|
|
}
|