2019-05-27 13:55:05 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
2007-10-15 14:50:19 +07:00
|
|
|
* Copyright (c) by Jaroslav Kysela <perex@perex.cz>
|
2005-04-17 05:20:36 +07:00
|
|
|
* Routines for control of YMF724/740/744/754 chips
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/delay.h>
|
2006-10-11 17:05:59 +07:00
|
|
|
#include <linux/firmware.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/slab.h>
|
2008-05-29 18:40:00 +07:00
|
|
|
#include <linux/mutex.h>
|
2011-07-15 23:38:28 +07:00
|
|
|
#include <linux/module.h>
|
2015-01-28 22:49:33 +07:00
|
|
|
#include <linux/io.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/control.h>
|
|
|
|
#include <sound/info.h>
|
2006-08-28 18:29:42 +07:00
|
|
|
#include <sound/tlv.h>
|
2012-07-02 21:37:05 +07:00
|
|
|
#include "ymfpci.h"
|
2005-04-17 05:20:36 +07:00
|
|
|
#include <sound/asoundef.h>
|
|
|
|
#include <sound/mpu401.h>
|
|
|
|
|
2006-10-11 17:05:59 +07:00
|
|
|
#include <asm/byteorder.h>
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* common I/O routines
|
|
|
|
*/
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static inline u8 snd_ymfpci_readb(struct snd_ymfpci *chip, u32 offset)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return readb(chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static inline void snd_ymfpci_writeb(struct snd_ymfpci *chip, u32 offset, u8 val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
writeb(val, chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static inline u16 snd_ymfpci_readw(struct snd_ymfpci *chip, u32 offset)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return readw(chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static inline void snd_ymfpci_writew(struct snd_ymfpci *chip, u32 offset, u16 val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
writew(val, chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static inline u32 snd_ymfpci_readl(struct snd_ymfpci *chip, u32 offset)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return readl(chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static inline void snd_ymfpci_writel(struct snd_ymfpci *chip, u32 offset, u32 val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
writel(val, chip->reg_area_virt + offset);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_codec_ready(struct snd_ymfpci *chip, int secondary)
|
2005-04-17 05:20:36 +07: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 15:13:22 +07:00
|
|
|
unsigned long end_time;
|
2005-04-17 05:20:36 +07: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 15:13:22 +07:00
|
|
|
end_time = jiffies + msecs_to_jiffies(750);
|
2005-04-17 05:20:36 +07:00
|
|
|
do {
|
|
|
|
if ((snd_ymfpci_readw(chip, reg) & 0x8000) == 0)
|
|
|
|
return 0;
|
2005-10-24 20:02:37 +07: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 15:13:22 +07:00
|
|
|
} while (time_before(jiffies, end_time));
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_err(chip->card->dev,
|
|
|
|
"codec_ready: codec %i is not ready [0x%x]\n",
|
|
|
|
secondary, snd_ymfpci_readw(chip, reg));
|
2005-04-17 05:20:36 +07:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_codec_write(struct snd_ac97 *ac97, u16 reg, u16 val)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static u16 snd_ymfpci_codec_read(struct snd_ac97 *ac97, u16 reg)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-05 21:47:40 +07:00
|
|
|
static const u32 def_rate[8] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
100, 2000, 8000, 11025, 16000, 22050, 32000, 48000
|
|
|
|
};
|
|
|
|
|
|
|
|
static u32 snd_ymfpci_calc_lpfK(u32 rate)
|
|
|
|
{
|
|
|
|
u32 i;
|
2020-01-05 21:47:40 +07:00
|
|
|
static const u32 val[8] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
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;
|
2020-01-05 21:47:40 +07:00
|
|
|
static const u32 val[8] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hardware start management
|
|
|
|
*/
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_hw_start(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_hw_stop(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07: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 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci_voice *voice, *voice2;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07: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 05:20:36 +07:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
int result;
|
|
|
|
|
2008-08-08 22:12:14 +07:00
|
|
|
if (snd_BUG_ON(!rvoice))
|
|
|
|
return -EINVAL;
|
|
|
|
if (snd_BUG_ON(pair && type != YMFPCI_PCM))
|
|
|
|
return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_voice_free(struct snd_ymfpci *chip, struct snd_ymfpci_voice *pvoice)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
2008-08-08 22:12:14 +07:00
|
|
|
if (snd_BUG_ON(!pvoice))
|
|
|
|
return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
snd_ymfpci_hw_stop(chip);
|
|
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
2006-12-05 00:03:53 +07:00
|
|
|
if (pvoice->number == chip->src441_used) {
|
|
|
|
chip->src441_used = -1;
|
|
|
|
pvoice->ypcm->use_441_slot = 0;
|
|
|
|
}
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_pcm_interrupt(struct snd_ymfpci *chip, struct snd_ymfpci_voice *voice)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2005-04-17 05:20:36 +07: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) {
|
2009-02-05 22:11:31 +07:00
|
|
|
/*
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_dbg(chip->card->dev,
|
2009-02-05 22:11:31 +07:00
|
|
|
"done - active_bank = 0x%x, start = 0x%x\n",
|
|
|
|
chip->active_bank,
|
|
|
|
voice->bank[chip->active_bank].start);
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
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 15:21:43 +07:00
|
|
|
|
|
|
|
if (unlikely(ypcm->update_pcm_vol)) {
|
|
|
|
unsigned int subs = ypcm->substream->number;
|
|
|
|
unsigned int next_bank = 1 - chip->active_bank;
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci_playback_bank *bank;
|
2018-07-26 04:24:01 +07:00
|
|
|
__le32 volume;
|
2005-08-10 15:21:43 +07:00
|
|
|
|
|
|
|
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 05:20:36 +07:00
|
|
|
}
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_pcm_capture_interrupt(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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;
|
2009-02-05 22:11:31 +07:00
|
|
|
/*
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_dbg(chip->card->dev,
|
2009-02-05 22:11:31 +07:00
|
|
|
"done - active_bank = 0x%x, start = 0x%x\n",
|
|
|
|
chip->active_bank,
|
|
|
|
voice->bank[chip->active_bank].start);
|
|
|
|
*/
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
snd_pcm_period_elapsed(substream);
|
|
|
|
spin_lock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock(&chip->reg_lock);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_trigger(struct snd_pcm_substream *substream,
|
2005-04-17 05:20:36 +07:00
|
|
|
int cmd)
|
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2007-07-23 22:38:44 +07:00
|
|
|
struct snd_kcontrol *kctl = NULL;
|
2005-04-17 05:20:36 +07: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 00:03:53 +07:00
|
|
|
if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
|
2005-04-17 05:20:36 +07: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:
|
2007-07-23 22:38:44 +07:00
|
|
|
if (substream->pcm == chip->pcm && !ypcm->use_441_slot) {
|
|
|
|
kctl = chip->pcm_mixer[substream->number].ctl;
|
|
|
|
kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
}
|
2020-07-09 03:32:36 +07:00
|
|
|
fallthrough;
|
2005-04-17 05:20:36 +07:00
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
|
case SNDRV_PCM_TRIGGER_SUSPEND:
|
|
|
|
chip->ctrl_playback[ypcm->voices[0]->number + 1] = 0;
|
2006-12-05 00:03:53 +07:00
|
|
|
if (ypcm->voices[1] != NULL && !ypcm->use_441_slot)
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->ctrl_playback[ypcm->voices[1]->number + 1] = 0;
|
|
|
|
ypcm->running = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
result = -EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
__unlock:
|
|
|
|
spin_unlock(&chip->reg_lock);
|
2007-07-23 22:38:44 +07:00
|
|
|
if (kctl)
|
|
|
|
snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &kctl->id);
|
2005-04-17 05:20:36 +07:00
|
|
|
return result;
|
|
|
|
}
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_trigger(struct snd_pcm_substream *substream,
|
2005-04-17 05:20:36 +07:00
|
|
|
int cmd)
|
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_pcm_voice_alloc(struct snd_ymfpci_pcm *ypcm, int voices)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_pcm_init_voice(struct snd_ymfpci_pcm *ypcm, unsigned int voiceidx,
|
|
|
|
struct snd_pcm_runtime *runtime,
|
2005-08-10 15:21:43 +07:00
|
|
|
int has_pcm_volume)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci_voice *voice = ypcm->voices[voiceidx];
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 format;
|
2005-08-10 15:21:43 +07: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 20:53:41 +07:00
|
|
|
struct snd_ymfpci_playback_bank *bank;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int nbank;
|
2018-07-26 04:24:01 +07:00
|
|
|
__le32 vol_left, vol_right;
|
2005-08-10 15:21:43 +07:00
|
|
|
u8 use_left, use_right;
|
2006-12-05 00:03:53 +07:00
|
|
|
unsigned long flags;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2008-08-08 22:12:14 +07:00
|
|
|
if (snd_BUG_ON(!voice))
|
|
|
|
return;
|
2005-08-10 15:21:43 +07: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 00:03:53 +07:00
|
|
|
spin_lock_irqsave(&ypcm->chip->voice_lock, flags);
|
2005-08-10 15:21:43 +07:00
|
|
|
format = runtime->channels == 2 ? 0x00010000 : 0;
|
|
|
|
if (snd_pcm_format_width(runtime->format) == 8)
|
|
|
|
format |= 0x80000000;
|
2006-12-05 00:03:53 +07: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;
|
|
|
|
}
|
|
|
|
if (ypcm->chip->src441_used == voice->number &&
|
|
|
|
(format & 0x10000000) == 0) {
|
|
|
|
ypcm->chip->src441_used = -1;
|
|
|
|
ypcm->use_441_slot = 0;
|
|
|
|
}
|
2005-08-10 15:21:43 +07:00
|
|
|
if (runtime->channels == 2 && (voiceidx & 1) != 0)
|
|
|
|
format |= 1;
|
2006-12-05 00:03:53 +07:00
|
|
|
spin_unlock_irqrestore(&ypcm->chip->voice_lock, flags);
|
2005-04-17 05:20:36 +07:00
|
|
|
for (nbank = 0; nbank < 2; nbank++) {
|
|
|
|
bank = &voice->bank[nbank];
|
2005-08-10 15:21:43 +07:00
|
|
|
memset(bank, 0, sizeof(*bank));
|
2005-04-17 05:20:36 +07:00
|
|
|
bank->format = cpu_to_le32(format);
|
2005-08-10 15:21:43 +07:00
|
|
|
bank->base = cpu_to_le32(runtime->dma_addr);
|
|
|
|
bank->loop_end = cpu_to_le32(ypcm->buffer_size);
|
2005-04-17 05:20:36 +07: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 15:21:43 +07: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 05:20:36 +07:00
|
|
|
bank->right_gain =
|
2005-08-10 15:21:43 +07:00
|
|
|
bank->right_gain_end = vol_right;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2005-08-10 15:21:43 +07:00
|
|
|
}
|
|
|
|
if (ypcm->output_rear) {
|
2006-01-18 14:02:24 +07: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 05:20:36 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-12-07 00:35:10 +07:00
|
|
|
static int snd_ymfpci_ac3_init(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2019-11-05 22:18:55 +07:00
|
|
|
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
|
2005-04-17 05:20:36 +07:00
|
|
|
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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_ac3_done(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *hw_params)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm = runtime->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
if ((err = snd_ymfpci_pcm_voice_alloc(ypcm, params_channels(hw_params))) < 0)
|
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_hw_free(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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);
|
|
|
|
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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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;
|
2007-07-23 22:38:44 +07:00
|
|
|
struct snd_kcontrol *kctl;
|
2005-04-17 05:20:36 +07: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 15:21:43 +07:00
|
|
|
snd_ymfpci_pcm_init_voice(ypcm, nvoice, runtime,
|
|
|
|
substream->pcm == chip->pcm);
|
2007-07-23 22:38:44 +07:00
|
|
|
|
|
|
|
if (substream->pcm == chip->pcm && !ypcm->use_441_slot) {
|
|
|
|
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 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_hw_free(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* wait, until the PCI operations are not finished */
|
|
|
|
snd_ymfpci_irq_wait(chip);
|
2019-12-09 16:49:28 +07:00
|
|
|
return 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static snd_pcm_uframes_t snd_ymfpci_playback_pointer(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07:00
|
|
|
|
|
|
|
if (!(ypcm->running && voice))
|
|
|
|
return 0;
|
|
|
|
return le32_to_cpu(voice->bank[chip->active_bank].start);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static snd_pcm_uframes_t snd_ymfpci_capture_pointer(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_irq_wait(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2017-06-20 17:06:13 +07:00
|
|
|
wait_queue_entry_t wait;
|
2005-04-17 05:20:36 +07:00
|
|
|
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 20:02:37 +07:00
|
|
|
schedule_timeout_uninterruptible(msecs_to_jiffies(50));
|
2005-04-17 05:20:36 +07: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 20:55:46 +07:00
|
|
|
static irqreturn_t snd_ymfpci_interrupt(int irq, void *dev_id)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = dev_id;
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 status, nvoice, mode;
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci_voice *voice;
|
2005-04-17 05:20:36 +07: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)
|
2009-08-10 15:06:53 +07:00
|
|
|
snd_timer_interrupt(chip->timer, chip->timer_ticks);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
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 20:55:46 +07:00
|
|
|
snd_mpu401_uart_interrupt(irq, chip->rawmidi->private_data);
|
2005-04-17 05:20:36 +07:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2017-08-12 22:31:27 +07:00
|
|
|
static const struct snd_pcm_hardware snd_ymfpci_playback =
|
2005-04-17 05:20:36 +07: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,
|
|
|
|
};
|
|
|
|
|
2017-08-12 22:31:27 +07:00
|
|
|
static const struct snd_pcm_hardware snd_ymfpci_capture =
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_pcm_free_substream(struct snd_pcm_runtime *runtime)
|
2005-04-17 05:20:36 +07: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 22:30:32 +07:00
|
|
|
kfree(runtime->private_data);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2011-09-17 03:52:48 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
runtime->hw = snd_ymfpci_playback;
|
|
|
|
/* FIXME? True value is 256/48 = 5.33333 ms */
|
|
|
|
err = snd_pcm_hw_constraint_minmax(runtime,
|
|
|
|
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
|
|
|
|
5334, UINT_MAX);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2011-09-17 04:08:28 +07:00
|
|
|
err = snd_pcm_hw_rule_noresample(runtime, 48000);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07: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 19:21:46 +07:00
|
|
|
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (ypcm == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
ypcm->chip = chip;
|
|
|
|
ypcm->type = PLAYBACK_VOICE;
|
|
|
|
ypcm->substream = substream;
|
|
|
|
runtime->private_data = ypcm;
|
|
|
|
runtime->private_free = snd_ymfpci_pcm_free_substream;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call with spinlock held */
|
2005-11-17 20:53:41 +07:00
|
|
|
static void ymfpci_open_extension(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void ymfpci_close_extension(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_open(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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 14:27:19 +07:00
|
|
|
ypcm->swap_rear = 0;
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
if (ypcm->output_rear) {
|
|
|
|
ymfpci_open_extension(chip);
|
|
|
|
chip->rear_opened++;
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_spdif_open(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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 14:27:19 +07:00
|
|
|
ypcm->swap_rear = 1;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_4ch_open(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07: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 14:27:19 +07:00
|
|
|
ypcm->swap_rear = 0;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
|
2005-04-17 05:20:36 +07:00
|
|
|
u32 capture_bank_number)
|
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ymfpci_pcm *ypcm;
|
2011-09-17 03:52:48 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
runtime->hw = snd_ymfpci_capture;
|
|
|
|
/* FIXME? True value is 256/48 = 5.33333 ms */
|
|
|
|
err = snd_pcm_hw_constraint_minmax(runtime,
|
|
|
|
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
|
|
|
|
5334, UINT_MAX);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2011-09-17 04:08:28 +07:00
|
|
|
err = snd_pcm_hw_rule_noresample(runtime, 48000);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07: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 19:21:46 +07:00
|
|
|
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
2005-04-17 05:20:36 +07: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->private_data = ypcm;
|
|
|
|
runtime->private_free = snd_ymfpci_pcm_free_substream;
|
|
|
|
snd_ymfpci_hw_start(chip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_rec_open(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return snd_ymfpci_capture_open(substream, 0);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_ac97_open(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return snd_ymfpci_capture_open(substream, 1);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_close_1(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_close(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2005-04-17 05:20:36 +07: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);
|
|
|
|
return snd_ymfpci_playback_close_1(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_spdif_close(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_playback_4ch_close(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07: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 05:20:36 +07:00
|
|
|
|
|
|
|
if (ypcm != NULL) {
|
|
|
|
chip->capture_substream[ypcm->capture_bank_number] = NULL;
|
|
|
|
snd_ymfpci_hw_stop(chip);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-02 05:13:10 +07:00
|
|
|
static const struct snd_pcm_ops snd_ymfpci_playback_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = snd_ymfpci_playback_open,
|
|
|
|
.close = snd_ymfpci_playback_close,
|
|
|
|
.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,
|
|
|
|
};
|
|
|
|
|
2016-09-02 05:13:10 +07:00
|
|
|
static const struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = snd_ymfpci_capture_rec_open,
|
|
|
|
.close = snd_ymfpci_capture_close,
|
|
|
|
.hw_free = snd_ymfpci_capture_hw_free,
|
|
|
|
.prepare = snd_ymfpci_capture_prepare,
|
|
|
|
.trigger = snd_ymfpci_capture_trigger,
|
|
|
|
.pointer = snd_ymfpci_capture_pointer,
|
|
|
|
};
|
|
|
|
|
2015-01-02 18:24:55 +07:00
|
|
|
int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-12-09 16:49:28 +07:00
|
|
|
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
&chip->pci->dev, 64*1024, 256*1024);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2015-01-02 18:24:55 +07:00
|
|
|
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
2012-09-12 21:14:37 +07:00
|
|
|
snd_pcm_std_chmaps, 2, 0, NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2016-09-02 05:13:10 +07:00
|
|
|
static const struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = snd_ymfpci_capture_ac97_open,
|
|
|
|
.close = snd_ymfpci_capture_close,
|
|
|
|
.hw_free = snd_ymfpci_capture_hw_free,
|
|
|
|
.prepare = snd_ymfpci_capture_prepare,
|
|
|
|
.trigger = snd_ymfpci_capture_trigger,
|
|
|
|
.pointer = snd_ymfpci_capture_pointer,
|
|
|
|
};
|
|
|
|
|
2015-01-02 18:24:55 +07:00
|
|
|
int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-12-09 16:49:28 +07:00
|
|
|
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
&chip->pci->dev, 64*1024, 256*1024);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-02 05:13:10 +07:00
|
|
|
static const struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = snd_ymfpci_playback_spdif_open,
|
|
|
|
.close = snd_ymfpci_playback_spdif_close,
|
|
|
|
.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,
|
|
|
|
};
|
|
|
|
|
2015-01-02 18:24:55 +07:00
|
|
|
int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-12-09 16:49:28 +07:00
|
|
|
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
&chip->pci->dev, 64*1024, 256*1024);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-02 05:13:10 +07:00
|
|
|
static const struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.open = snd_ymfpci_playback_4ch_open,
|
|
|
|
.close = snd_ymfpci_playback_4ch_close,
|
|
|
|
.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,
|
|
|
|
};
|
|
|
|
|
2012-09-12 21:14:37 +07:00
|
|
|
static const struct snd_pcm_chmap_elem surround_map[] = {
|
|
|
|
{ .channels = 1,
|
2012-09-13 19:48:46 +07:00
|
|
|
.map = { SNDRV_CHMAP_MONO } },
|
2012-09-12 21:14:37 +07:00
|
|
|
{ .channels = 2,
|
|
|
|
.map = { SNDRV_CHMAP_RL, SNDRV_CHMAP_RR } },
|
|
|
|
{ }
|
|
|
|
};
|
|
|
|
|
2015-01-02 18:24:55 +07:00
|
|
|
int snd_ymfpci_pcm_4ch(struct snd_ymfpci *chip, int device)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
2019-12-09 16:49:28 +07:00
|
|
|
snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV,
|
|
|
|
&chip->pci->dev, 64*1024, 256*1024);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2015-01-02 18:24:55 +07:00
|
|
|
return snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
2012-09-12 21:14:37 +07:00
|
|
|
surround_map, 2, 0, NULL);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_default_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_default_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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 13:41:45 +07:00
|
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_spdif_default =
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_spdif_mask =
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_stream_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_stream_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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 13:41:45 +07:00
|
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS_48000;
|
2005-04-17 05:20:36 +07:00
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_spdif_stream =
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_drec_source_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *info)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2011-01-10 22:29:06 +07:00
|
|
|
static const char *const texts[3] = {"AC'97", "IEC958", "ZV Port"};
|
|
|
|
|
|
|
|
return snd_ctl_enum_info(info, 1, 3, texts);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_drec_source_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *value)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_drec_source = {
|
2005-04-17 05:20:36 +07: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 13:19:05 +07:00
|
|
|
#define YMFPCI_SINGLE(xname, xindex, reg, shift) \
|
2005-04-17 05:20:36 +07: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 13:19:05 +07:00
|
|
|
.private_value = ((reg) | ((shift) << 16)) }
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2007-07-23 20:42:26 +07:00
|
|
|
#define snd_ymfpci_info_single snd_ctl_boolean_mono_info
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_get_single(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-09-28 13:19:05 +07:00
|
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
|
|
unsigned int shift = (kcontrol->private_value >> 16) & 0xff;
|
|
|
|
unsigned int mask = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-09-28 13:19:05 +07:00
|
|
|
switch (reg) {
|
2005-04-17 05:20:36 +07:00
|
|
|
case YDSXGR_SPDIFOUTCTRL: break;
|
|
|
|
case YDSXGR_SPDIFINCTRL: break;
|
|
|
|
default: return -EINVAL;
|
|
|
|
}
|
2005-09-28 13:19:05 +07:00
|
|
|
ucontrol->value.integer.value[0] =
|
|
|
|
(snd_ymfpci_readl(chip, reg) >> shift) & mask;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_put_single(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-09-28 13:19:05 +07:00
|
|
|
int reg = kcontrol->private_value & 0xffff;
|
|
|
|
unsigned int shift = (kcontrol->private_value >> 16) & 0xff;
|
|
|
|
unsigned int mask = 1;
|
2005-04-17 05:20:36 +07:00
|
|
|
int change;
|
|
|
|
unsigned int val, oval;
|
|
|
|
|
2005-09-28 13:19:05 +07:00
|
|
|
switch (reg) {
|
2005-04-17 05:20:36 +07: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 21:33:49 +07:00
|
|
|
static const DECLARE_TLV_DB_LINEAR(db_scale_native, TLV_DB_GAIN_MUTE, 0);
|
2006-08-28 18:29:42 +07:00
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
#define YMFPCI_DOUBLE(xname, xindex, reg) \
|
|
|
|
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
|
2006-08-28 18:29:42 +07:00
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
|
2005-04-17 05:20:36 +07:00
|
|
|
.info = snd_ymfpci_info_double, \
|
|
|
|
.get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
|
2006-08-28 18:29:42 +07:00
|
|
|
.private_value = reg, \
|
|
|
|
.tlv = { .p = db_scale_native } }
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
unsigned int reg = kcontrol->private_value;
|
|
|
|
|
|
|
|
if (reg < 0x80 || reg >= 0xc0)
|
|
|
|
return -EINVAL;
|
2005-04-11 19:11:00 +07:00
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
2005-04-17 05:20:36 +07:00
|
|
|
uinfo->count = 2;
|
|
|
|
uinfo->value.integer.min = 0;
|
2005-04-11 19:11:00 +07:00
|
|
|
uinfo->value.integer.max = 16383;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int reg = kcontrol->private_value;
|
2005-04-11 19:11:00 +07:00
|
|
|
unsigned int shift_left = 0, shift_right = 16, mask = 16383;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int reg = kcontrol->private_value;
|
2005-04-11 19:11:00 +07:00
|
|
|
unsigned int shift_left = 0, shift_right = 16, mask = 16383;
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2007-07-23 22:38:44 +07:00
|
|
|
static int snd_ymfpci_put_nativedacvol(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
|
|
{
|
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
|
|
|
unsigned int reg = YDSXGR_NATIVEDACOUTVOL;
|
|
|
|
unsigned int reg2 = YDSXGR_BUF441OUTVOL;
|
|
|
|
int change;
|
|
|
|
unsigned int value, oval;
|
|
|
|
|
|
|
|
value = ucontrol->value.integer.value[0] & 0x3fff;
|
|
|
|
value |= (ucontrol->value.integer.value[1] & 0x3fff) << 16;
|
|
|
|
spin_lock_irq(&chip->reg_lock);
|
|
|
|
oval = snd_ymfpci_readl(chip, reg);
|
|
|
|
change = value != oval;
|
|
|
|
snd_ymfpci_writel(chip, reg, value);
|
|
|
|
snd_ymfpci_writel(chip, reg2, value);
|
|
|
|
spin_unlock_irq(&chip->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/*
|
|
|
|
* 4ch duplication
|
|
|
|
*/
|
2007-07-23 20:42:26 +07:00
|
|
|
#define snd_ymfpci_info_dup4ch snd_ctl_boolean_mono_info
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_get_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07:00
|
|
|
ucontrol->value.integer.value[0] = chip->mode_dup4ch;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07:00
|
|
|
int change;
|
|
|
|
change = (ucontrol->value.integer.value[0] != chip->mode_dup4ch);
|
|
|
|
if (change)
|
|
|
|
chip->mode_dup4ch = !!ucontrol->value.integer.value[0];
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_dup4ch = {
|
2012-01-17 10:41:47 +07:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "4ch Duplication",
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
|
|
|
|
.info = snd_ymfpci_info_dup4ch,
|
|
|
|
.get = snd_ymfpci_get_dup4ch,
|
|
|
|
.put = snd_ymfpci_put_dup4ch,
|
|
|
|
};
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2020-01-03 15:16:53 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_controls[] = {
|
2007-07-23 22:38:44 +07:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Wave Playback Volume",
|
|
|
|
.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_nativedacvol,
|
|
|
|
.private_value = YDSXGR_NATIVEDACOUTVOL,
|
|
|
|
.tlv = { .p = db_scale_native },
|
|
|
|
},
|
2005-04-17 05:20:36 +07:00
|
|
|
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),
|
2011-09-09 18:15:01 +07:00
|
|
|
YMFPCI_DOUBLE("FM Legacy Playback Volume", 0, YDSXGR_LEGACYOUTVOL),
|
2005-04-17 05:20:36 +07:00
|
|
|
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 13:19:05 +07: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 05:20:36 +07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GPIO
|
|
|
|
*/
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_get_gpio_out(struct snd_ymfpci *chip, int pin)
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_set_gpio_out(struct snd_ymfpci *chip, int pin, int enable)
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2007-07-23 20:42:26 +07:00
|
|
|
#define snd_ymfpci_gpio_sw_info snd_ctl_boolean_mono_info
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_gpio_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07:00
|
|
|
int pin = (int)kcontrol->private_value;
|
|
|
|
ucontrol->value.integer.value[0] = snd_ymfpci_get_gpio_out(chip, pin);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_rear_shared = {
|
2005-04-17 05:20:36 +07: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 15:21:43 +07:00
|
|
|
/*
|
|
|
|
* PCM voice volume
|
|
|
|
*/
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_pcm_vol_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-08-10 15:21:43 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_pcm_vol_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-08-10 15:21:43 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-08-10 15:21:43 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-08-10 15:21:43 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = snd_kcontrol_chip(kcontrol);
|
2005-08-10 15:21:43 +07:00
|
|
|
unsigned int subs = kcontrol->id.subdevice;
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_pcm_substream *substream;
|
2005-08-10 15:21:43 +07: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];
|
2007-11-15 21:58:13 +07:00
|
|
|
if (chip->pcm_mixer[subs].left > 0x8000)
|
|
|
|
chip->pcm_mixer[subs].left = 0x8000;
|
|
|
|
if (chip->pcm_mixer[subs].right > 0x8000)
|
|
|
|
chip->pcm_mixer[subs].right = 0x8000;
|
2005-08-10 15:21:43 +07:00
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
substream = (struct snd_pcm_substream *)kcontrol->private_value;
|
2005-08-10 15:21:43 +07:00
|
|
|
spin_lock_irqsave(&chip->voice_lock, flags);
|
|
|
|
if (substream->runtime && substream->runtime->private_data) {
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci_pcm *ypcm = substream->runtime->private_data;
|
2006-12-05 00:03:53 +07:00
|
|
|
if (!ypcm->use_441_slot)
|
|
|
|
ypcm->update_pcm_vol = 2;
|
2005-08-10 15:21:43 +07:00
|
|
|
}
|
|
|
|
spin_unlock_irqrestore(&chip->voice_lock, flags);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-20 01:48:09 +07:00
|
|
|
static const struct snd_kcontrol_new snd_ymfpci_pcm_volume = {
|
2005-08-10 15:21:43 +07: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 05:20:36 +07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Mixer routines
|
|
|
|
*/
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_mixer_free_ac97_bus(struct snd_ac97_bus *bus)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = bus->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->ac97_bus = NULL;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_mixer_free_ac97(struct snd_ac97 *ac97)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = ac97->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->ac97 = NULL;
|
|
|
|
}
|
|
|
|
|
2012-12-07 00:35:10 +07:00
|
|
|
int snd_ymfpci_mixer(struct snd_ymfpci *chip, int rear_switch)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ac97_template ac97;
|
|
|
|
struct snd_kcontrol *kctl;
|
|
|
|
struct snd_pcm_substream *substream;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int idx;
|
|
|
|
int err;
|
2020-01-03 15:16:43 +07:00
|
|
|
static const struct snd_ac97_bus_ops ops = {
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
2012-01-17 10:41:47 +07:00
|
|
|
if (chip->ac97->ext_id & AC97_EI_SDAC) {
|
|
|
|
kctl = snd_ctl_new1(&snd_ymfpci_dup4ch, chip);
|
|
|
|
err = snd_ctl_add(chip->card, kctl);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* add S/PDIF control */
|
2008-08-08 22:12:14 +07:00
|
|
|
if (snd_BUG_ON(!chip->pcm_spdif))
|
|
|
|
return -ENXIO;
|
2005-04-17 05:20:36 +07:00
|
|
|
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 15:21:43 +07: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 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* timer
|
|
|
|
*/
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_timer_start(struct snd_timer *timer)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long flags;
|
|
|
|
unsigned int count;
|
|
|
|
|
|
|
|
chip = snd_timer_chip(timer);
|
|
|
|
spin_lock_irqsave(&chip->reg_lock, flags);
|
2009-08-10 15:06:53 +07:00
|
|
|
if (timer->sticks > 1) {
|
|
|
|
chip->timer_ticks = timer->sticks;
|
|
|
|
count = timer->sticks - 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Divisor 1 is not allowed; fake it by using divisor 2 and
|
|
|
|
* counting two ticks for each interrupt.
|
|
|
|
*/
|
|
|
|
chip->timer_ticks = 2;
|
|
|
|
count = 2 - 1;
|
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_timer_stop(struct snd_timer *timer)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_timer_precise_resolution(struct snd_timer *timer,
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned long *num, unsigned long *den)
|
|
|
|
{
|
|
|
|
*num = 1;
|
2009-08-10 15:06:53 +07:00
|
|
|
*den = 96000;
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-01-03 15:16:37 +07:00
|
|
|
static const struct snd_timer_hardware snd_ymfpci_timer_hw = {
|
2005-04-17 05:20:36 +07:00
|
|
|
.flags = SNDRV_TIMER_HW_AUTO,
|
2009-08-10 15:06:53 +07:00
|
|
|
.resolution = 10417, /* 1 / 96 kHz = 10.41666...us */
|
|
|
|
.ticks = 0x10000,
|
2005-04-17 05:20:36 +07:00
|
|
|
.start = snd_ymfpci_timer_start,
|
|
|
|
.stop = snd_ymfpci_timer_stop,
|
|
|
|
.precise_resolution = snd_ymfpci_timer_precise_resolution,
|
|
|
|
};
|
|
|
|
|
2012-12-07 00:35:10 +07:00
|
|
|
int snd_ymfpci_timer(struct snd_ymfpci *chip, int device)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_timer *timer = NULL;
|
|
|
|
struct snd_timer_id tid;
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_proc_read(struct snd_info_entry *entry,
|
|
|
|
struct snd_info_buffer *buffer)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = entry->private_data;
|
2005-04-17 05:20:36 +07: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));
|
|
|
|
}
|
|
|
|
|
2012-12-07 00:35:10 +07:00
|
|
|
static int snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2019-02-04 22:01:39 +07:00
|
|
|
return snd_card_ro_proc_new(card, "ymfpci", chip, snd_ymfpci_proc_read);
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 20:53:41 +07:00
|
|
|
static void snd_ymfpci_enable_dsp(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
snd_ymfpci_writel(chip, YDSXGR_CONFIG, 0x00000001);
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_disable_dsp(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-10-11 17:05:59 +07:00
|
|
|
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) {
|
2008-05-29 18:40:00 +07:00
|
|
|
if (chip->dsp_microcode->size != YDSXG_DSPLENGTH) {
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_err(chip->card->dev,
|
|
|
|
"DSP microcode has wrong size\n");
|
2006-10-11 17:05:59 +07:00
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2007-04-26 19:13:44 +07:00
|
|
|
if (err < 0)
|
2006-10-11 17:05:59 +07: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) {
|
2008-05-29 18:40:00 +07:00
|
|
|
if (chip->controller_microcode->size != YDSXG_CTRLLENGTH) {
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_err(chip->card->dev,
|
|
|
|
"controller microcode has wrong size\n");
|
2006-10-11 17:05:59 +07:00
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2007-04-26 19:13:44 +07:00
|
|
|
if (err < 0)
|
2006-10-11 17:05:59 +07:00
|
|
|
return err;
|
|
|
|
return 0;
|
|
|
|
}
|
2007-05-03 22:59:54 +07:00
|
|
|
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1_dsp.fw");
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1_ctrl.fw");
|
|
|
|
MODULE_FIRMWARE("yamaha/ds1e_ctrl.fw");
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static void snd_ymfpci_download_image(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
u16 ctrl;
|
2008-05-29 18:40:00 +07:00
|
|
|
const __le32 *inst;
|
2005-04-17 05:20:36 +07: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 */
|
2008-05-29 18:40:00 +07:00
|
|
|
inst = (const __le32 *)chip->dsp_microcode->data;
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; i < YDSXG_DSPLENGTH / 4; i++)
|
2008-05-29 18:40:00 +07:00
|
|
|
snd_ymfpci_writel(chip, YDSXGR_DSPINSTRAM + (i << 2),
|
|
|
|
le32_to_cpu(inst[i]));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
/* setup control instruction code */
|
2008-05-29 18:40:00 +07:00
|
|
|
inst = (const __le32 *)chip->controller_microcode->data;
|
2005-04-17 05:20:36 +07:00
|
|
|
for (i = 0; i < YDSXG_CTRLLENGTH / 4; i++)
|
2008-05-29 18:40:00 +07:00
|
|
|
snd_ymfpci_writel(chip, YDSXGR_CTRLINSTRAM + (i << 2),
|
|
|
|
le32_to_cpu(inst[i]));
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
snd_ymfpci_enable_dsp(chip);
|
|
|
|
}
|
|
|
|
|
2012-12-07 00:35:10 +07:00
|
|
|
static int snd_ymfpci_memalloc(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07: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 13:13:32 +07: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 05:20:36 +07:00
|
|
|
chip->work_size;
|
|
|
|
/* work_ptr must be aligned to 256 bytes, but it's already
|
|
|
|
covered with the kernel page allocation mechanism */
|
2019-11-05 22:18:55 +07:00
|
|
|
if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, &chip->pci->dev,
|
2005-04-17 05:20:36 +07:00
|
|
|
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;
|
2018-07-26 04:24:01 +07:00
|
|
|
chip->ctrl_playback = (__le32 *)ptr;
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->ctrl_playback[0] = cpu_to_le32(YDSXG_PLAYBACK_VOICES);
|
2006-10-09 13:13:32 +07:00
|
|
|
ptr += ALIGN(playback_ctrl_size, 0x100);
|
|
|
|
ptr_addr += ALIGN(playback_ctrl_size, 0x100);
|
2005-04-17 05:20:36 +07:00
|
|
|
for (voice = 0; voice < YDSXG_PLAYBACK_VOICES; voice++) {
|
|
|
|
chip->voices[voice].number = voice;
|
2005-11-17 20:53:41 +07:00
|
|
|
chip->voices[voice].bank = (struct snd_ymfpci_playback_bank *)ptr;
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->voices[voice].bank_addr = ptr_addr;
|
|
|
|
for (bank = 0; bank < 2; bank++) {
|
2005-11-17 20:53:41 +07:00
|
|
|
chip->bank_playback[voice][bank] = (struct snd_ymfpci_playback_bank *)ptr;
|
2005-04-17 05:20:36 +07:00
|
|
|
ptr += chip->bank_size_playback;
|
|
|
|
ptr_addr += chip->bank_size_playback;
|
|
|
|
}
|
|
|
|
}
|
2006-10-09 13:13:32 +07:00
|
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
chip->bank_capture[voice][bank] = (struct snd_ymfpci_capture_bank *)ptr;
|
2005-04-17 05:20:36 +07:00
|
|
|
ptr += chip->bank_size_capture;
|
|
|
|
ptr_addr += chip->bank_size_capture;
|
|
|
|
}
|
2006-10-09 13:13:32 +07:00
|
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
2005-04-17 05:20:36 +07: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 20:53:41 +07:00
|
|
|
chip->bank_effect[voice][bank] = (struct snd_ymfpci_effect_bank *)ptr;
|
2005-04-17 05:20:36 +07:00
|
|
|
ptr += chip->bank_size_effect;
|
|
|
|
ptr_addr += chip->bank_size_effect;
|
|
|
|
}
|
2006-10-09 13:13:32 +07:00
|
|
|
ptr = (char *)ALIGN((unsigned long)ptr, 0x100);
|
|
|
|
ptr_addr = ALIGN(ptr_addr, 0x100);
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->work_base = ptr;
|
|
|
|
chip->work_base_addr = ptr_addr;
|
|
|
|
|
2008-08-08 22:12:14 +07:00
|
|
|
snd_BUG_ON(ptr + chip->work_size !=
|
|
|
|
chip->work_ptr.area + chip->work_ptr.bytes);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
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);
|
2008-06-25 22:17:00 +07:00
|
|
|
snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0x3fff3fff);
|
2005-04-17 05:20:36 +07:00
|
|
|
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 20:53:41 +07:00
|
|
|
static int snd_ymfpci_free(struct snd_ymfpci *chip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
|
|
|
u16 ctrl;
|
|
|
|
|
2008-08-08 22:12:14 +07:00
|
|
|
if (snd_BUG_ON(!chip))
|
|
|
|
return -EINVAL;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
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?
|
|
|
|
*/
|
2013-06-27 19:55:11 +07:00
|
|
|
pci_set_power_state(chip->pci, PCI_D3hot);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
|
|
|
|
2012-08-14 23:12:04 +07:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2012-11-22 22:23:22 +07:00
|
|
|
kfree(chip->saved_regs);
|
2005-04-17 05:20:36 +07:00
|
|
|
#endif
|
2008-03-22 16:11:08 +07:00
|
|
|
if (chip->irq >= 0)
|
|
|
|
free_irq(chip->irq, chip);
|
2005-10-10 16:56:31 +07:00
|
|
|
release_and_free_resource(chip->mpu_res);
|
|
|
|
release_and_free_resource(chip->fm_res);
|
2005-04-17 05:20:36 +07:00
|
|
|
snd_ymfpci_free_gameport(chip);
|
2015-01-04 04:55:54 +07:00
|
|
|
iounmap(chip->reg_area_virt);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (chip->work_ptr.area)
|
|
|
|
snd_dma_free_pages(&chip->work_ptr);
|
|
|
|
|
2005-10-10 16:56:31 +07:00
|
|
|
release_and_free_resource(chip->res_reg_area);
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
pci_write_config_word(chip->pci, 0x40, chip->old_legacy_ctrl);
|
|
|
|
|
|
|
|
pci_disable_device(chip->pci);
|
2007-04-26 19:13:44 +07:00
|
|
|
release_firmware(chip->dsp_microcode);
|
|
|
|
release_firmware(chip->controller_microcode);
|
2005-04-17 05:20:36 +07:00
|
|
|
kfree(chip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 20:53:41 +07:00
|
|
|
static int snd_ymfpci_dev_free(struct snd_device *device)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip = device->device_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
return snd_ymfpci_free(chip);
|
|
|
|
}
|
|
|
|
|
2012-08-14 23:12:04 +07:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
2020-01-05 21:47:40 +07:00
|
|
|
static const int saved_regs_index[] = {
|
2005-04-17 05:20:36 +07:00
|
|
|
/* spdif */
|
|
|
|
YDSXGR_SPDIFOUTCTRL,
|
|
|
|
YDSXGR_SPDIFOUTSTATUS,
|
|
|
|
YDSXGR_SPDIFINCTRL,
|
|
|
|
/* volumes */
|
|
|
|
YDSXGR_PRIADCLOOPVOL,
|
|
|
|
YDSXGR_NATIVEDACINVOL,
|
|
|
|
YDSXGR_NATIVEDACOUTVOL,
|
2006-12-05 00:03:53 +07:00
|
|
|
YDSXGR_BUF441OUTVOL,
|
2005-04-17 05:20:36 +07: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)
|
|
|
|
|
2012-07-02 20:20:37 +07:00
|
|
|
static int snd_ymfpci_suspend(struct device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-07-02 20:20:37 +07:00
|
|
|
struct snd_card *card = dev_get_drvdata(dev);
|
2005-11-17 22:09:43 +07:00
|
|
|
struct snd_ymfpci *chip = card->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int i;
|
|
|
|
|
2005-11-17 22:09:43 +07:00
|
|
|
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
|
2005-04-17 05:20:36 +07:00
|
|
|
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);
|
2012-03-13 14:07:41 +07:00
|
|
|
pci_read_config_word(chip->pci, PCIR_DSXG_LEGACY,
|
|
|
|
&chip->saved_dsxg_legacy);
|
|
|
|
pci_read_config_word(chip->pci, PCIR_DSXG_ELEGACY,
|
|
|
|
&chip->saved_dsxg_elegacy);
|
2005-04-17 05:20:36 +07:00
|
|
|
snd_ymfpci_writel(chip, YDSXGR_NATIVEDACOUTVOL, 0);
|
2008-06-25 22:17:00 +07:00
|
|
|
snd_ymfpci_writel(chip, YDSXGR_BUF441OUTVOL, 0);
|
2005-04-17 05:20:36 +07:00
|
|
|
snd_ymfpci_disable_dsp(chip);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-07-02 20:20:37 +07:00
|
|
|
static int snd_ymfpci_resume(struct device *dev)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2012-07-02 20:20:37 +07:00
|
|
|
struct pci_dev *pci = to_pci_dev(dev);
|
|
|
|
struct snd_card *card = dev_get_drvdata(dev);
|
2005-11-17 22:09:43 +07:00
|
|
|
struct snd_ymfpci *chip = card->private_data;
|
2005-04-17 05:20:36 +07:00
|
|
|
unsigned int i;
|
|
|
|
|
2005-11-17 22:09:43 +07:00
|
|
|
snd_ymfpci_aclink_reset(pci);
|
2005-04-17 05:20:36 +07: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);
|
|
|
|
|
2012-03-13 14:07:41 +07:00
|
|
|
pci_write_config_word(chip->pci, PCIR_DSXG_LEGACY,
|
|
|
|
chip->saved_dsxg_legacy);
|
|
|
|
pci_write_config_word(chip->pci, PCIR_DSXG_ELEGACY,
|
|
|
|
chip->saved_dsxg_elegacy);
|
|
|
|
|
2005-04-17 05:20:36 +07:00
|
|
|
/* 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 22:09:43 +07:00
|
|
|
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
|
2005-04-17 05:20:36 +07:00
|
|
|
return 0;
|
|
|
|
}
|
2012-07-02 20:20:37 +07:00
|
|
|
|
|
|
|
SIMPLE_DEV_PM_OPS(snd_ymfpci_pm, snd_ymfpci_suspend, snd_ymfpci_resume);
|
2012-08-14 23:12:04 +07:00
|
|
|
#endif /* CONFIG_PM_SLEEP */
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-12-07 00:35:10 +07:00
|
|
|
int snd_ymfpci_create(struct snd_card *card,
|
|
|
|
struct pci_dev *pci,
|
|
|
|
unsigned short old_legacy_ctrl,
|
|
|
|
struct snd_ymfpci **rchip)
|
2005-04-17 05:20:36 +07:00
|
|
|
{
|
2005-11-17 20:53:41 +07:00
|
|
|
struct snd_ymfpci *chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
int err;
|
2020-01-03 15:16:25 +07:00
|
|
|
static const struct snd_device_ops ops = {
|
2005-04-17 05:20:36 +07: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 19:21:46 +07:00
|
|
|
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
|
2005-04-17 05:20:36 +07: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 05:46:36 +07:00
|
|
|
chip->rev = pci->revision;
|
2005-04-17 05:20:36 +07:00
|
|
|
chip->reg_area_phys = pci_resource_start(pci, 0);
|
2020-01-06 15:43:50 +07:00
|
|
|
chip->reg_area_virt = ioremap(chip->reg_area_phys, 0x8000);
|
2005-04-17 05:20:36 +07:00
|
|
|
pci_set_master(pci);
|
2006-12-05 00:03:53 +07:00
|
|
|
chip->src441_used = -1;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
if ((chip->res_reg_area = request_mem_region(chip->reg_area_phys, 0x8000, "YMFPCI")) == NULL) {
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_err(chip->card->dev,
|
|
|
|
"unable to grab memory region 0x%lx-0x%lx\n",
|
|
|
|
chip->reg_area_phys, chip->reg_area_phys + 0x8000 - 1);
|
2017-09-07 02:12:51 +07:00
|
|
|
err = -EBUSY;
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
2006-11-21 18:14:23 +07:00
|
|
|
if (request_irq(pci->irq, snd_ymfpci_interrupt, IRQF_SHARED,
|
2011-06-10 21:36:37 +07:00
|
|
|
KBUILD_MODNAME, chip)) {
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_err(chip->card->dev, "unable to grab IRQ %d\n", pci->irq);
|
2017-09-07 02:12:51 +07:00
|
|
|
err = -EBUSY;
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
chip->irq = pci->irq;
|
2019-12-10 13:34:37 +07:00
|
|
|
card->sync_irq = chip->irq;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
snd_ymfpci_aclink_reset(pci);
|
|
|
|
if (snd_ymfpci_codec_ready(chip, 0) < 0) {
|
2017-09-07 02:12:51 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2006-10-11 17:05:59 +07:00
|
|
|
err = snd_ymfpci_request_firmware(chip);
|
|
|
|
if (err < 0) {
|
2014-02-26 18:18:27 +07:00
|
|
|
dev_err(chip->card->dev, "firmware request failed: %d\n", err);
|
2017-09-07 02:12:51 +07:00
|
|
|
goto free_chip;
|
2006-10-11 17:05:59 +07:00
|
|
|
}
|
2005-04-17 05:20:36 +07:00
|
|
|
snd_ymfpci_download_image(chip);
|
|
|
|
|
|
|
|
udelay(100); /* seems we need a delay after downloading image.. */
|
|
|
|
|
|
|
|
if (snd_ymfpci_memalloc(chip) < 0) {
|
2017-09-07 02:12:51 +07:00
|
|
|
err = -EIO;
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
|
2017-09-07 02:12:51 +07:00
|
|
|
err = snd_ymfpci_ac3_init(chip);
|
|
|
|
if (err < 0)
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
2012-08-14 23:12:04 +07:00
|
|
|
#ifdef CONFIG_PM_SLEEP
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 03:55:00 +07:00
|
|
|
chip->saved_regs = kmalloc_array(YDSXGR_NUM_SAVED_REGS, sizeof(u32),
|
|
|
|
GFP_KERNEL);
|
2005-04-17 05:20:36 +07:00
|
|
|
if (chip->saved_regs == NULL) {
|
2017-09-07 02:12:51 +07:00
|
|
|
err = -ENOMEM;
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-09-07 02:12:51 +07:00
|
|
|
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
|
|
|
|
if (err < 0)
|
|
|
|
goto free_chip;
|
2005-04-17 05:20:36 +07:00
|
|
|
|
|
|
|
snd_ymfpci_proc_init(card, chip);
|
|
|
|
|
|
|
|
*rchip = chip;
|
|
|
|
return 0;
|
2017-09-07 02:12:51 +07:00
|
|
|
|
|
|
|
free_chip:
|
|
|
|
snd_ymfpci_free(chip);
|
|
|
|
return err;
|
2005-04-17 05:20:36 +07:00
|
|
|
}
|