2005-04-16 15:20:36 -07:00
|
|
|
/*
|
|
|
|
* ALSA driver for RME Digi96, Digi96/8 and Digi96/8 PRO/PAD/PST audio
|
|
|
|
* interfaces
|
|
|
|
*
|
|
|
|
* Copyright (c) 2000, 2001 Anders Torger <torger@ludd.luth.se>
|
|
|
|
*
|
|
|
|
* Thanks to Henk Hesselink <henk@anda.nl> for the analog volume control
|
|
|
|
* code.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/moduleparam.h>
|
|
|
|
|
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/info.h>
|
|
|
|
#include <sound/control.h>
|
|
|
|
#include <sound/pcm.h>
|
|
|
|
#include <sound/pcm_params.h>
|
|
|
|
#include <sound/asoundef.h>
|
|
|
|
#include <sound/initval.h>
|
|
|
|
|
|
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
/* note, two last pcis should be equal, it is not a bug */
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Anders Torger <torger@ludd.luth.se>");
|
|
|
|
MODULE_DESCRIPTION("RME Digi96, Digi96/8, Digi96/8 PRO, Digi96/8 PST, "
|
|
|
|
"Digi96/8 PAD");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_SUPPORTED_DEVICE("{{RME,Digi96},"
|
|
|
|
"{RME,Digi96/8},"
|
|
|
|
"{RME,Digi96/8 PRO},"
|
|
|
|
"{RME,Digi96/8 PST},"
|
|
|
|
"{RME,Digi96/8 PAD}}");
|
|
|
|
|
|
|
|
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */
|
|
|
|
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */
|
|
|
|
static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; /* Enable this card */
|
|
|
|
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(index, "Index value for RME Digi96 soundcard.");
|
|
|
|
module_param_array(id, charp, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(id, "ID string for RME Digi96 soundcard.");
|
|
|
|
module_param_array(enable, bool, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Defines for RME Digi96 series, from internal RME reference documents
|
|
|
|
* dated 12.01.00
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define RME96_SPDIF_NCHANNELS 2
|
|
|
|
|
|
|
|
/* Playback and capture buffer size */
|
|
|
|
#define RME96_BUFFER_SIZE 0x10000
|
|
|
|
|
|
|
|
/* IO area size */
|
|
|
|
#define RME96_IO_SIZE 0x60000
|
|
|
|
|
|
|
|
/* IO area offsets */
|
|
|
|
#define RME96_IO_PLAY_BUFFER 0x0
|
|
|
|
#define RME96_IO_REC_BUFFER 0x10000
|
|
|
|
#define RME96_IO_CONTROL_REGISTER 0x20000
|
|
|
|
#define RME96_IO_ADDITIONAL_REG 0x20004
|
|
|
|
#define RME96_IO_CONFIRM_PLAY_IRQ 0x20008
|
|
|
|
#define RME96_IO_CONFIRM_REC_IRQ 0x2000C
|
|
|
|
#define RME96_IO_SET_PLAY_POS 0x40000
|
|
|
|
#define RME96_IO_RESET_PLAY_POS 0x4FFFC
|
|
|
|
#define RME96_IO_SET_REC_POS 0x50000
|
|
|
|
#define RME96_IO_RESET_REC_POS 0x5FFFC
|
|
|
|
#define RME96_IO_GET_PLAY_POS 0x20000
|
|
|
|
#define RME96_IO_GET_REC_POS 0x30000
|
|
|
|
|
|
|
|
/* Write control register bits */
|
|
|
|
#define RME96_WCR_START (1 << 0)
|
|
|
|
#define RME96_WCR_START_2 (1 << 1)
|
|
|
|
#define RME96_WCR_GAIN_0 (1 << 2)
|
|
|
|
#define RME96_WCR_GAIN_1 (1 << 3)
|
|
|
|
#define RME96_WCR_MODE24 (1 << 4)
|
|
|
|
#define RME96_WCR_MODE24_2 (1 << 5)
|
|
|
|
#define RME96_WCR_BM (1 << 6)
|
|
|
|
#define RME96_WCR_BM_2 (1 << 7)
|
|
|
|
#define RME96_WCR_ADAT (1 << 8)
|
|
|
|
#define RME96_WCR_FREQ_0 (1 << 9)
|
|
|
|
#define RME96_WCR_FREQ_1 (1 << 10)
|
|
|
|
#define RME96_WCR_DS (1 << 11)
|
|
|
|
#define RME96_WCR_PRO (1 << 12)
|
|
|
|
#define RME96_WCR_EMP (1 << 13)
|
|
|
|
#define RME96_WCR_SEL (1 << 14)
|
|
|
|
#define RME96_WCR_MASTER (1 << 15)
|
|
|
|
#define RME96_WCR_PD (1 << 16)
|
|
|
|
#define RME96_WCR_INP_0 (1 << 17)
|
|
|
|
#define RME96_WCR_INP_1 (1 << 18)
|
|
|
|
#define RME96_WCR_THRU_0 (1 << 19)
|
|
|
|
#define RME96_WCR_THRU_1 (1 << 20)
|
|
|
|
#define RME96_WCR_THRU_2 (1 << 21)
|
|
|
|
#define RME96_WCR_THRU_3 (1 << 22)
|
|
|
|
#define RME96_WCR_THRU_4 (1 << 23)
|
|
|
|
#define RME96_WCR_THRU_5 (1 << 24)
|
|
|
|
#define RME96_WCR_THRU_6 (1 << 25)
|
|
|
|
#define RME96_WCR_THRU_7 (1 << 26)
|
|
|
|
#define RME96_WCR_DOLBY (1 << 27)
|
|
|
|
#define RME96_WCR_MONITOR_0 (1 << 28)
|
|
|
|
#define RME96_WCR_MONITOR_1 (1 << 29)
|
|
|
|
#define RME96_WCR_ISEL (1 << 30)
|
|
|
|
#define RME96_WCR_IDIS (1 << 31)
|
|
|
|
|
|
|
|
#define RME96_WCR_BITPOS_GAIN_0 2
|
|
|
|
#define RME96_WCR_BITPOS_GAIN_1 3
|
|
|
|
#define RME96_WCR_BITPOS_FREQ_0 9
|
|
|
|
#define RME96_WCR_BITPOS_FREQ_1 10
|
|
|
|
#define RME96_WCR_BITPOS_INP_0 17
|
|
|
|
#define RME96_WCR_BITPOS_INP_1 18
|
|
|
|
#define RME96_WCR_BITPOS_MONITOR_0 28
|
|
|
|
#define RME96_WCR_BITPOS_MONITOR_1 29
|
|
|
|
|
|
|
|
/* Read control register bits */
|
|
|
|
#define RME96_RCR_AUDIO_ADDR_MASK 0xFFFF
|
|
|
|
#define RME96_RCR_IRQ_2 (1 << 16)
|
|
|
|
#define RME96_RCR_T_OUT (1 << 17)
|
|
|
|
#define RME96_RCR_DEV_ID_0 (1 << 21)
|
|
|
|
#define RME96_RCR_DEV_ID_1 (1 << 22)
|
|
|
|
#define RME96_RCR_LOCK (1 << 23)
|
|
|
|
#define RME96_RCR_VERF (1 << 26)
|
|
|
|
#define RME96_RCR_F0 (1 << 27)
|
|
|
|
#define RME96_RCR_F1 (1 << 28)
|
|
|
|
#define RME96_RCR_F2 (1 << 29)
|
|
|
|
#define RME96_RCR_AUTOSYNC (1 << 30)
|
|
|
|
#define RME96_RCR_IRQ (1 << 31)
|
|
|
|
|
|
|
|
#define RME96_RCR_BITPOS_F0 27
|
|
|
|
#define RME96_RCR_BITPOS_F1 28
|
|
|
|
#define RME96_RCR_BITPOS_F2 29
|
|
|
|
|
|
|
|
/* Additonal register bits */
|
|
|
|
#define RME96_AR_WSEL (1 << 0)
|
|
|
|
#define RME96_AR_ANALOG (1 << 1)
|
|
|
|
#define RME96_AR_FREQPAD_0 (1 << 2)
|
|
|
|
#define RME96_AR_FREQPAD_1 (1 << 3)
|
|
|
|
#define RME96_AR_FREQPAD_2 (1 << 4)
|
|
|
|
#define RME96_AR_PD2 (1 << 5)
|
|
|
|
#define RME96_AR_DAC_EN (1 << 6)
|
|
|
|
#define RME96_AR_CLATCH (1 << 7)
|
|
|
|
#define RME96_AR_CCLK (1 << 8)
|
|
|
|
#define RME96_AR_CDATA (1 << 9)
|
|
|
|
|
|
|
|
#define RME96_AR_BITPOS_F0 2
|
|
|
|
#define RME96_AR_BITPOS_F1 3
|
|
|
|
#define RME96_AR_BITPOS_F2 4
|
|
|
|
|
|
|
|
/* Monitor tracks */
|
|
|
|
#define RME96_MONITOR_TRACKS_1_2 0
|
|
|
|
#define RME96_MONITOR_TRACKS_3_4 1
|
|
|
|
#define RME96_MONITOR_TRACKS_5_6 2
|
|
|
|
#define RME96_MONITOR_TRACKS_7_8 3
|
|
|
|
|
|
|
|
/* Attenuation */
|
|
|
|
#define RME96_ATTENUATION_0 0
|
|
|
|
#define RME96_ATTENUATION_6 1
|
|
|
|
#define RME96_ATTENUATION_12 2
|
|
|
|
#define RME96_ATTENUATION_18 3
|
|
|
|
|
|
|
|
/* Input types */
|
|
|
|
#define RME96_INPUT_OPTICAL 0
|
|
|
|
#define RME96_INPUT_COAXIAL 1
|
|
|
|
#define RME96_INPUT_INTERNAL 2
|
|
|
|
#define RME96_INPUT_XLR 3
|
|
|
|
#define RME96_INPUT_ANALOG 4
|
|
|
|
|
|
|
|
/* Clock modes */
|
|
|
|
#define RME96_CLOCKMODE_SLAVE 0
|
|
|
|
#define RME96_CLOCKMODE_MASTER 1
|
|
|
|
#define RME96_CLOCKMODE_WORDCLOCK 2
|
|
|
|
|
|
|
|
/* Block sizes in bytes */
|
|
|
|
#define RME96_SMALL_BLOCK_SIZE 2048
|
|
|
|
#define RME96_LARGE_BLOCK_SIZE 8192
|
|
|
|
|
|
|
|
/* Volume control */
|
|
|
|
#define RME96_AD1852_VOL_BITS 14
|
|
|
|
#define RME96_AD1855_VOL_BITS 10
|
|
|
|
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 {
|
2005-04-16 15:20:36 -07:00
|
|
|
spinlock_t lock;
|
|
|
|
int irq;
|
|
|
|
unsigned long port;
|
|
|
|
void __iomem *iobase;
|
|
|
|
|
|
|
|
u32 wcreg; /* cached write control register value */
|
|
|
|
u32 wcreg_spdif; /* S/PDIF setup */
|
|
|
|
u32 wcreg_spdif_stream; /* S/PDIF setup (temporary) */
|
|
|
|
u32 rcreg; /* cached read control register value */
|
|
|
|
u32 areg; /* cached additional register value */
|
|
|
|
u16 vol[2]; /* cached volume of analog output */
|
|
|
|
|
|
|
|
u8 rev; /* card revision number */
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
struct snd_pcm_substream *playback_substream;
|
|
|
|
struct snd_pcm_substream *capture_substream;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
int playback_frlog; /* log2 of framesize */
|
|
|
|
int capture_frlog;
|
|
|
|
|
|
|
|
size_t playback_periodsize; /* in bytes, zero if not used */
|
|
|
|
size_t capture_periodsize; /* in bytes, zero if not used */
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
struct snd_card *card;
|
|
|
|
struct snd_pcm *spdif_pcm;
|
|
|
|
struct snd_pcm *adat_pcm;
|
2005-04-16 15:20:36 -07:00
|
|
|
struct pci_dev *pci;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct snd_kcontrol *spdif_ctl;
|
|
|
|
};
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2010-02-05 15:21:03 -07:00
|
|
|
static DEFINE_PCI_DEVICE_TABLE(snd_rme96_ids) = {
|
2009-06-24 22:13:35 -07:00
|
|
|
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96), 0, },
|
|
|
|
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8), 0, },
|
|
|
|
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PRO), 0, },
|
|
|
|
{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST), 0, },
|
2005-04-16 15:20:36 -07:00
|
|
|
{ 0, }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, snd_rme96_ids);
|
|
|
|
|
|
|
|
#define RME96_ISPLAYING(rme96) ((rme96)->wcreg & RME96_WCR_START)
|
|
|
|
#define RME96_ISRECORDING(rme96) ((rme96)->wcreg & RME96_WCR_START_2)
|
2005-09-14 14:19:17 -07:00
|
|
|
#define RME96_HAS_ANALOG_IN(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST)
|
|
|
|
#define RME96_HAS_ANALOG_OUT(rme96) ((rme96)->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PRO || \
|
|
|
|
(rme96)->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST)
|
2005-04-16 15:20:36 -07:00
|
|
|
#define RME96_DAC_IS_1852(rme96) (RME96_HAS_ANALOG_OUT(rme96) && (rme96)->rev >= 4)
|
2005-09-14 14:19:17 -07:00
|
|
|
#define RME96_DAC_IS_1855(rme96) (((rme96)->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST && (rme96)->rev < 4) || \
|
|
|
|
((rme96)->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PRO && (rme96)->rev == 2))
|
2005-04-16 15:20:36 -07:00
|
|
|
#define RME96_185X_MAX_OUT(rme96) ((1 << (RME96_DAC_IS_1852(rme96) ? RME96_AD1852_VOL_BITS : RME96_AD1855_VOL_BITS)) - 1)
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_prepare(struct snd_pcm_substream *substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_prepare(struct snd_pcm_substream *substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int cmd);
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int cmd);
|
|
|
|
|
|
|
|
static snd_pcm_uframes_t
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_pointer(struct snd_pcm_substream *substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static snd_pcm_uframes_t
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_pointer(struct snd_pcm_substream *substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static void __devinit
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_proc_init(struct rme96 *rme96);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_create_switches(struct snd_card *card,
|
|
|
|
struct rme96 *rme96);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_getinputtype(struct rme96 *rme96);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
static inline unsigned int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_ptr(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
return (readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
|
|
|
|
& RME96_RCR_AUDIO_ADDR_MASK) >> rme96->playback_frlog;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_ptr(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
return (readl(rme96->iobase + RME96_IO_GET_REC_POS)
|
|
|
|
& RME96_RCR_AUDIO_ADDR_MASK) >> rme96->capture_frlog;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_silence(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int channel, /* not used (interleaved data) */
|
|
|
|
snd_pcm_uframes_t pos,
|
|
|
|
snd_pcm_uframes_t count)
|
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
count <<= rme96->playback_frlog;
|
|
|
|
pos <<= rme96->playback_frlog;
|
|
|
|
memset_io(rme96->iobase + RME96_IO_PLAY_BUFFER + pos,
|
|
|
|
0, count);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_copy(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int channel, /* not used (interleaved data) */
|
|
|
|
snd_pcm_uframes_t pos,
|
|
|
|
void __user *src,
|
|
|
|
snd_pcm_uframes_t count)
|
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
count <<= rme96->playback_frlog;
|
|
|
|
pos <<= rme96->playback_frlog;
|
|
|
|
copy_from_user_toio(rme96->iobase + RME96_IO_PLAY_BUFFER + pos, src,
|
|
|
|
count);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_copy(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int channel, /* not used (interleaved data) */
|
|
|
|
snd_pcm_uframes_t pos,
|
|
|
|
void __user *dst,
|
|
|
|
snd_pcm_uframes_t count)
|
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
count <<= rme96->capture_frlog;
|
|
|
|
pos <<= rme96->capture_frlog;
|
|
|
|
copy_to_user_fromio(dst, rme96->iobase + RME96_IO_REC_BUFFER + pos,
|
|
|
|
count);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2006-03-28 02:56:53 -07:00
|
|
|
* Digital output capabilities (S/PDIF)
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_PAUSE),
|
|
|
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
|
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
|
|
.rates = (SNDRV_PCM_RATE_32000 |
|
|
|
|
SNDRV_PCM_RATE_44100 |
|
|
|
|
SNDRV_PCM_RATE_48000 |
|
|
|
|
SNDRV_PCM_RATE_64000 |
|
|
|
|
SNDRV_PCM_RATE_88200 |
|
|
|
|
SNDRV_PCM_RATE_96000),
|
|
|
|
.rate_min = 32000,
|
|
|
|
.rate_max = 96000,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.buffer_bytes_max = RME96_BUFFER_SIZE,
|
|
|
|
.period_bytes_min = RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.period_bytes_max = RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.fifo_size = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2006-03-28 02:56:53 -07:00
|
|
|
* Digital input capabilities (S/PDIF)
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_hardware snd_rme96_capture_spdif_info =
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_PAUSE),
|
|
|
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
|
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
|
|
.rates = (SNDRV_PCM_RATE_32000 |
|
|
|
|
SNDRV_PCM_RATE_44100 |
|
|
|
|
SNDRV_PCM_RATE_48000 |
|
|
|
|
SNDRV_PCM_RATE_64000 |
|
|
|
|
SNDRV_PCM_RATE_88200 |
|
|
|
|
SNDRV_PCM_RATE_96000),
|
|
|
|
.rate_min = 32000,
|
|
|
|
.rate_max = 96000,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.buffer_bytes_max = RME96_BUFFER_SIZE,
|
|
|
|
.period_bytes_min = RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.period_bytes_max = RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.fifo_size = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2006-03-28 02:56:53 -07:00
|
|
|
* Digital output capabilities (ADAT)
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_hardware snd_rme96_playback_adat_info =
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_PAUSE),
|
|
|
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
|
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
|
|
.rates = (SNDRV_PCM_RATE_44100 |
|
|
|
|
SNDRV_PCM_RATE_48000),
|
|
|
|
.rate_min = 44100,
|
|
|
|
.rate_max = 48000,
|
|
|
|
.channels_min = 8,
|
|
|
|
.channels_max = 8,
|
|
|
|
.buffer_bytes_max = RME96_BUFFER_SIZE,
|
|
|
|
.period_bytes_min = RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.period_bytes_max = RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.fifo_size = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
2006-03-28 02:56:53 -07:00
|
|
|
* Digital input capabilities (ADAT)
|
2005-04-16 15:20:36 -07:00
|
|
|
*/
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_hardware snd_rme96_capture_adat_info =
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_PAUSE),
|
|
|
|
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
|
|
|
SNDRV_PCM_FMTBIT_S32_LE),
|
|
|
|
.rates = (SNDRV_PCM_RATE_44100 |
|
|
|
|
SNDRV_PCM_RATE_48000),
|
|
|
|
.rate_min = 44100,
|
|
|
|
.rate_max = 48000,
|
|
|
|
.channels_min = 8,
|
|
|
|
.channels_max = 8,
|
|
|
|
.buffer_bytes_max = RME96_BUFFER_SIZE,
|
|
|
|
.period_bytes_min = RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.period_bytes_max = RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_min = RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
|
|
|
|
.periods_max = RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
|
|
|
|
.fifo_size = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The CDATA, CCLK and CLATCH bits can be used to write to the SPI interface
|
|
|
|
* of the AD1852 or AD1852 D/A converter on the board. CDATA must be set up
|
|
|
|
* on the falling edge of CCLK and be stable on the rising edge. The rising
|
|
|
|
* edge of CLATCH after the last data bit clocks in the whole data word.
|
|
|
|
* A fast processor could probably drive the SPI interface faster than the
|
|
|
|
* DAC can handle (3MHz for the 1855, unknown for the 1852). The udelay(1)
|
|
|
|
* limits the data rate to 500KHz and only causes a delay of 33 microsecs.
|
|
|
|
*
|
|
|
|
* NOTE: increased delay from 1 to 10, since there where problems setting
|
|
|
|
* the volume.
|
|
|
|
*/
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_write_SPI(struct rme96 *rme96, u16 val)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
|
|
if (val & 0x8000) {
|
|
|
|
rme96->areg |= RME96_AR_CDATA;
|
|
|
|
} else {
|
|
|
|
rme96->areg &= ~RME96_AR_CDATA;
|
|
|
|
}
|
|
|
|
rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CLATCH);
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
udelay(10);
|
|
|
|
rme96->areg |= RME96_AR_CCLK;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
udelay(10);
|
|
|
|
val <<= 1;
|
|
|
|
}
|
|
|
|
rme96->areg &= ~(RME96_AR_CCLK | RME96_AR_CDATA);
|
|
|
|
rme96->areg |= RME96_AR_CLATCH;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
udelay(10);
|
|
|
|
rme96->areg &= ~RME96_AR_CLATCH;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_apply_dac_volume(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
if (RME96_DAC_IS_1852(rme96)) {
|
|
|
|
snd_rme96_write_SPI(rme96, (rme96->vol[0] << 2) | 0x0);
|
|
|
|
snd_rme96_write_SPI(rme96, (rme96->vol[1] << 2) | 0x2);
|
|
|
|
} else if (RME96_DAC_IS_1855(rme96)) {
|
|
|
|
snd_rme96_write_SPI(rme96, (rme96->vol[0] & 0x3FF) | 0x000);
|
|
|
|
snd_rme96_write_SPI(rme96, (rme96->vol[1] & 0x3FF) | 0x400);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_reset_dac(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
writel(rme96->wcreg | RME96_WCR_PD,
|
|
|
|
rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_getmontracks(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
return ((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_0) & 1) +
|
|
|
|
(((rme96->wcreg >> RME96_WCR_BITPOS_MONITOR_1) & 1) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_setmontracks(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int montracks)
|
|
|
|
{
|
|
|
|
if (montracks & 1) {
|
|
|
|
rme96->wcreg |= RME96_WCR_MONITOR_0;
|
|
|
|
} else {
|
|
|
|
rme96->wcreg &= ~RME96_WCR_MONITOR_0;
|
|
|
|
}
|
|
|
|
if (montracks & 2) {
|
|
|
|
rme96->wcreg |= RME96_WCR_MONITOR_1;
|
|
|
|
} else {
|
|
|
|
rme96->wcreg &= ~RME96_WCR_MONITOR_1;
|
|
|
|
}
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_getattenuation(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
return ((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_0) & 1) +
|
|
|
|
(((rme96->wcreg >> RME96_WCR_BITPOS_GAIN_1) & 1) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_setattenuation(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int attenuation)
|
|
|
|
{
|
|
|
|
switch (attenuation) {
|
|
|
|
case 0:
|
|
|
|
rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) &
|
|
|
|
~RME96_WCR_GAIN_1;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) &
|
|
|
|
~RME96_WCR_GAIN_1;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rme96->wcreg = (rme96->wcreg & ~RME96_WCR_GAIN_0) |
|
|
|
|
RME96_WCR_GAIN_1;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_GAIN_0) |
|
|
|
|
RME96_WCR_GAIN_1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_getrate(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int *is_adat)
|
|
|
|
{
|
|
|
|
int n, rate;
|
|
|
|
|
|
|
|
*is_adat = 0;
|
|
|
|
if (rme96->areg & RME96_AR_ANALOG) {
|
|
|
|
/* Analog input, overrides S/PDIF setting */
|
|
|
|
n = ((rme96->areg >> RME96_AR_BITPOS_F0) & 1) +
|
|
|
|
(((rme96->areg >> RME96_AR_BITPOS_F1) & 1) << 1);
|
|
|
|
switch (n) {
|
|
|
|
case 1:
|
|
|
|
rate = 32000;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rate = 44100;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
rate = 48000;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return (rme96->areg & RME96_AR_BITPOS_F2) ? rate << 1 : rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
if (rme96->rcreg & RME96_RCR_LOCK) {
|
|
|
|
/* ADAT rate */
|
|
|
|
*is_adat = 1;
|
|
|
|
if (rme96->rcreg & RME96_RCR_T_OUT) {
|
|
|
|
return 48000;
|
|
|
|
}
|
|
|
|
return 44100;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rme96->rcreg & RME96_RCR_VERF) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* S/PDIF rate */
|
|
|
|
n = ((rme96->rcreg >> RME96_RCR_BITPOS_F0) & 1) +
|
|
|
|
(((rme96->rcreg >> RME96_RCR_BITPOS_F1) & 1) << 1) +
|
|
|
|
(((rme96->rcreg >> RME96_RCR_BITPOS_F2) & 1) << 2);
|
|
|
|
|
|
|
|
switch (n) {
|
|
|
|
case 0:
|
|
|
|
if (rme96->rcreg & RME96_RCR_T_OUT) {
|
|
|
|
return 64000;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
case 3: return 96000;
|
|
|
|
case 4: return 88200;
|
|
|
|
case 5: return 48000;
|
|
|
|
case 6: return 44100;
|
|
|
|
case 7: return 32000;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_getrate(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int rate, dummy;
|
|
|
|
|
|
|
|
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
|
|
|
|
snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
|
|
|
|
(rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
|
|
|
|
{
|
|
|
|
/* slave clock */
|
|
|
|
return rate;
|
|
|
|
}
|
|
|
|
rate = ((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_0) & 1) +
|
|
|
|
(((rme96->wcreg >> RME96_WCR_BITPOS_FREQ_1) & 1) << 1);
|
|
|
|
switch (rate) {
|
|
|
|
case 1:
|
|
|
|
rate = 32000;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
rate = 44100;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
rate = 48000;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return (rme96->wcreg & RME96_WCR_DS) ? rate << 1 : rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_setrate(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int rate)
|
|
|
|
{
|
|
|
|
int ds;
|
|
|
|
|
|
|
|
ds = rme96->wcreg & RME96_WCR_DS;
|
|
|
|
switch (rate) {
|
|
|
|
case 32000:
|
|
|
|
rme96->wcreg &= ~RME96_WCR_DS;
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
|
|
|
|
~RME96_WCR_FREQ_1;
|
|
|
|
break;
|
|
|
|
case 44100:
|
|
|
|
rme96->wcreg &= ~RME96_WCR_DS;
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
|
|
|
|
~RME96_WCR_FREQ_0;
|
|
|
|
break;
|
|
|
|
case 48000:
|
|
|
|
rme96->wcreg &= ~RME96_WCR_DS;
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
|
|
|
|
RME96_WCR_FREQ_1;
|
|
|
|
break;
|
|
|
|
case 64000:
|
|
|
|
rme96->wcreg |= RME96_WCR_DS;
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) &
|
|
|
|
~RME96_WCR_FREQ_1;
|
|
|
|
break;
|
|
|
|
case 88200:
|
|
|
|
rme96->wcreg |= RME96_WCR_DS;
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_1) &
|
|
|
|
~RME96_WCR_FREQ_0;
|
|
|
|
break;
|
|
|
|
case 96000:
|
|
|
|
rme96->wcreg |= RME96_WCR_DS;
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_FREQ_0) |
|
|
|
|
RME96_WCR_FREQ_1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if ((!ds && rme96->wcreg & RME96_WCR_DS) ||
|
|
|
|
(ds && !(rme96->wcreg & RME96_WCR_DS)))
|
|
|
|
{
|
|
|
|
/* change to/from double-speed: reset the DAC (if available) */
|
|
|
|
snd_rme96_reset_dac(rme96);
|
|
|
|
} else {
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_analog_setrate(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int rate)
|
|
|
|
{
|
|
|
|
switch (rate) {
|
|
|
|
case 32000:
|
|
|
|
rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
|
|
|
|
~RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
|
|
|
|
break;
|
|
|
|
case 44100:
|
|
|
|
rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
|
|
|
|
RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
|
|
|
|
break;
|
|
|
|
case 48000:
|
|
|
|
rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
|
|
|
|
RME96_AR_FREQPAD_1) & ~RME96_AR_FREQPAD_2;
|
|
|
|
break;
|
|
|
|
case 64000:
|
|
|
|
if (rme96->rev < 4) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) &
|
|
|
|
~RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
|
|
|
|
break;
|
|
|
|
case 88200:
|
|
|
|
if (rme96->rev < 4) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
rme96->areg = ((rme96->areg & ~RME96_AR_FREQPAD_0) |
|
|
|
|
RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
|
|
|
|
break;
|
|
|
|
case 96000:
|
|
|
|
rme96->areg = ((rme96->areg | RME96_AR_FREQPAD_0) |
|
|
|
|
RME96_AR_FREQPAD_1) | RME96_AR_FREQPAD_2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_setclockmode(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int mode)
|
|
|
|
{
|
|
|
|
switch (mode) {
|
|
|
|
case RME96_CLOCKMODE_SLAVE:
|
|
|
|
/* AutoSync */
|
|
|
|
rme96->wcreg &= ~RME96_WCR_MASTER;
|
|
|
|
rme96->areg &= ~RME96_AR_WSEL;
|
|
|
|
break;
|
|
|
|
case RME96_CLOCKMODE_MASTER:
|
|
|
|
/* Internal */
|
|
|
|
rme96->wcreg |= RME96_WCR_MASTER;
|
|
|
|
rme96->areg &= ~RME96_AR_WSEL;
|
|
|
|
break;
|
|
|
|
case RME96_CLOCKMODE_WORDCLOCK:
|
|
|
|
/* Word clock is a master mode */
|
|
|
|
rme96->wcreg |= RME96_WCR_MASTER;
|
|
|
|
rme96->areg |= RME96_AR_WSEL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_getclockmode(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
if (rme96->areg & RME96_AR_WSEL) {
|
|
|
|
return RME96_CLOCKMODE_WORDCLOCK;
|
|
|
|
}
|
|
|
|
return (rme96->wcreg & RME96_WCR_MASTER) ? RME96_CLOCKMODE_MASTER :
|
|
|
|
RME96_CLOCKMODE_SLAVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_setinputtype(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int type)
|
|
|
|
{
|
|
|
|
int n;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case RME96_INPUT_OPTICAL:
|
|
|
|
rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) &
|
|
|
|
~RME96_WCR_INP_1;
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_COAXIAL:
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) &
|
|
|
|
~RME96_WCR_INP_1;
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_INTERNAL:
|
|
|
|
rme96->wcreg = (rme96->wcreg & ~RME96_WCR_INP_0) |
|
|
|
|
RME96_WCR_INP_1;
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_XLR:
|
2005-09-14 14:19:17 -07:00
|
|
|
if ((rme96->pci->device != PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST &&
|
|
|
|
rme96->pci->device != PCI_DEVICE_ID_RME_DIGI96_8_PRO) ||
|
|
|
|
(rme96->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST &&
|
2005-04-16 15:20:36 -07:00
|
|
|
rme96->rev > 4))
|
|
|
|
{
|
|
|
|
/* Only Digi96/8 PRO and Digi96/8 PAD supports XLR */
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
rme96->wcreg = (rme96->wcreg | RME96_WCR_INP_0) |
|
|
|
|
RME96_WCR_INP_1;
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_ANALOG:
|
|
|
|
if (!RME96_HAS_ANALOG_IN(rme96)) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
rme96->areg |= RME96_AR_ANALOG;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
if (rme96->rev < 4) {
|
|
|
|
/*
|
|
|
|
* Revision less than 004 does not support 64 and
|
|
|
|
* 88.2 kHz
|
|
|
|
*/
|
|
|
|
if (snd_rme96_capture_getrate(rme96, &n) == 88200) {
|
|
|
|
snd_rme96_capture_analog_setrate(rme96, 44100);
|
|
|
|
}
|
|
|
|
if (snd_rme96_capture_getrate(rme96, &n) == 64000) {
|
|
|
|
snd_rme96_capture_analog_setrate(rme96, 32000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (type != RME96_INPUT_ANALOG && RME96_HAS_ANALOG_IN(rme96)) {
|
|
|
|
rme96->areg &= ~RME96_AR_ANALOG;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
}
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_getinputtype(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
if (rme96->areg & RME96_AR_ANALOG) {
|
|
|
|
return RME96_INPUT_ANALOG;
|
|
|
|
}
|
|
|
|
return ((rme96->wcreg >> RME96_WCR_BITPOS_INP_0) & 1) +
|
|
|
|
(((rme96->wcreg >> RME96_WCR_BITPOS_INP_1) & 1) << 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_setframelog(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int n_channels,
|
|
|
|
int is_playback)
|
|
|
|
{
|
|
|
|
int frlog;
|
|
|
|
|
|
|
|
if (n_channels == 2) {
|
|
|
|
frlog = 1;
|
|
|
|
} else {
|
|
|
|
/* assume 8 channels */
|
|
|
|
frlog = 3;
|
|
|
|
}
|
|
|
|
if (is_playback) {
|
|
|
|
frlog += (rme96->wcreg & RME96_WCR_MODE24) ? 2 : 1;
|
|
|
|
rme96->playback_frlog = frlog;
|
|
|
|
} else {
|
|
|
|
frlog += (rme96->wcreg & RME96_WCR_MODE24_2) ? 2 : 1;
|
|
|
|
rme96->capture_frlog = frlog;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_setformat(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
|
|
rme96->wcreg &= ~RME96_WCR_MODE24;
|
|
|
|
break;
|
|
|
|
case SNDRV_PCM_FORMAT_S32_LE:
|
|
|
|
rme96->wcreg |= RME96_WCR_MODE24;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_setformat(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
case SNDRV_PCM_FORMAT_S16_LE:
|
|
|
|
rme96->wcreg &= ~RME96_WCR_MODE24_2;
|
|
|
|
break;
|
|
|
|
case SNDRV_PCM_FORMAT_S32_LE:
|
|
|
|
rme96->wcreg |= RME96_WCR_MODE24_2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_set_period_properties(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
size_t period_bytes)
|
|
|
|
{
|
|
|
|
switch (period_bytes) {
|
|
|
|
case RME96_LARGE_BLOCK_SIZE:
|
|
|
|
rme96->wcreg &= ~RME96_WCR_ISEL;
|
|
|
|
break;
|
|
|
|
case RME96_SMALL_BLOCK_SIZE:
|
|
|
|
rme96->wcreg |= RME96_WCR_ISEL;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
snd_BUG();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
rme96->wcreg &= ~RME96_WCR_IDIS;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *params)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-16 15:20:36 -07:00
|
|
|
int err, rate, dummy;
|
|
|
|
|
2005-09-05 01:35:20 -07:00
|
|
|
runtime->dma_area = (void __force *)(rme96->iobase +
|
|
|
|
RME96_IO_PLAY_BUFFER);
|
2005-04-16 15:20:36 -07:00
|
|
|
runtime->dma_addr = rme96->port + RME96_IO_PLAY_BUFFER;
|
|
|
|
runtime->dma_bytes = RME96_BUFFER_SIZE;
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
|
|
|
|
snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
|
|
|
|
(rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
|
|
|
|
{
|
|
|
|
/* slave clock */
|
|
|
|
if ((int)params_rate(params) != rate) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
} else if ((err = snd_rme96_playback_setrate(rme96, params_rate(params))) < 0) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
if ((err = snd_rme96_playback_setformat(rme96, params_format(params))) < 0) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
snd_rme96_setframelog(rme96, params_channels(params), 1);
|
|
|
|
if (rme96->capture_periodsize != 0) {
|
|
|
|
if (params_period_size(params) << rme96->playback_frlog !=
|
|
|
|
rme96->capture_periodsize)
|
|
|
|
{
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rme96->playback_periodsize =
|
|
|
|
params_period_size(params) << rme96->playback_frlog;
|
|
|
|
snd_rme96_set_period_properties(rme96, rme96->playback_periodsize);
|
|
|
|
/* S/PDIF setup */
|
|
|
|
if ((rme96->wcreg & RME96_WCR_ADAT) == 0) {
|
|
|
|
rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
|
|
|
|
writel(rme96->wcreg |= rme96->wcreg_spdif_stream, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *params)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-16 15:20:36 -07:00
|
|
|
int err, isadat, rate;
|
|
|
|
|
2005-09-05 01:35:20 -07:00
|
|
|
runtime->dma_area = (void __force *)(rme96->iobase +
|
|
|
|
RME96_IO_REC_BUFFER);
|
2005-04-16 15:20:36 -07:00
|
|
|
runtime->dma_addr = rme96->port + RME96_IO_REC_BUFFER;
|
|
|
|
runtime->dma_bytes = RME96_BUFFER_SIZE;
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if ((err = snd_rme96_capture_setformat(rme96, params_format(params))) < 0) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
|
|
|
|
if ((err = snd_rme96_capture_analog_setrate(rme96,
|
|
|
|
params_rate(params))) < 0)
|
|
|
|
{
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
} else if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) {
|
|
|
|
if ((int)params_rate(params) != rate) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
if ((isadat && runtime->hw.channels_min == 2) ||
|
|
|
|
(!isadat && runtime->hw.channels_min == 8))
|
|
|
|
{
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
snd_rme96_setframelog(rme96, params_channels(params), 0);
|
|
|
|
if (rme96->playback_periodsize != 0) {
|
|
|
|
if (params_period_size(params) << rme96->capture_frlog !=
|
|
|
|
rme96->playback_periodsize)
|
|
|
|
{
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rme96->capture_periodsize =
|
|
|
|
params_period_size(params) << rme96->capture_frlog;
|
|
|
|
snd_rme96_set_period_properties(rme96, rme96->capture_periodsize);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_start(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int from_pause)
|
|
|
|
{
|
|
|
|
if (!from_pause) {
|
|
|
|
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
|
|
|
|
}
|
|
|
|
|
|
|
|
rme96->wcreg |= RME96_WCR_START;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_start(struct rme96 *rme96,
|
2005-04-16 15:20:36 -07:00
|
|
|
int from_pause)
|
|
|
|
{
|
|
|
|
if (!from_pause) {
|
|
|
|
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
|
|
|
|
}
|
|
|
|
|
|
|
|
rme96->wcreg |= RME96_WCR_START_2;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_stop(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Check if there is an unconfirmed IRQ, if so confirm it, or else
|
|
|
|
* the hardware will not stop generating interrupts
|
|
|
|
*/
|
|
|
|
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
if (rme96->rcreg & RME96_RCR_IRQ) {
|
|
|
|
writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
|
|
|
|
}
|
|
|
|
rme96->wcreg &= ~RME96_WCR_START;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_stop(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
if (rme96->rcreg & RME96_RCR_IRQ_2) {
|
|
|
|
writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
|
|
|
|
}
|
|
|
|
rme96->wcreg &= ~RME96_WCR_START_2;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t
|
|
|
|
snd_rme96_interrupt(int irq,
|
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 06:55:46 -07:00
|
|
|
void *dev_id)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = (struct rme96 *)dev_id;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
/* fastpath out, to ease interrupt sharing */
|
|
|
|
if (!((rme96->rcreg & RME96_RCR_IRQ) ||
|
|
|
|
(rme96->rcreg & RME96_RCR_IRQ_2)))
|
|
|
|
{
|
|
|
|
return IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rme96->rcreg & RME96_RCR_IRQ) {
|
|
|
|
/* playback */
|
|
|
|
snd_pcm_period_elapsed(rme96->playback_substream);
|
|
|
|
writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
|
|
|
|
}
|
|
|
|
if (rme96->rcreg & RME96_RCR_IRQ_2) {
|
|
|
|
/* capture */
|
|
|
|
snd_pcm_period_elapsed(rme96->capture_substream);
|
|
|
|
writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
|
|
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int period_bytes[] = { RME96_SMALL_BLOCK_SIZE, RME96_LARGE_BLOCK_SIZE };
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
|
2005-04-16 15:20:36 -07:00
|
|
|
.count = ARRAY_SIZE(period_bytes),
|
|
|
|
.list = period_bytes,
|
|
|
|
.mask = 0
|
|
|
|
};
|
|
|
|
|
2006-05-10 07:24:42 -07:00
|
|
|
static void
|
|
|
|
rme96_set_buffer_size_constraint(struct rme96 *rme96,
|
|
|
|
struct snd_pcm_runtime *runtime)
|
|
|
|
{
|
|
|
|
unsigned int size;
|
|
|
|
|
|
|
|
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
|
|
|
RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
|
|
|
|
if ((size = rme96->playback_periodsize) != 0 ||
|
|
|
|
(size = rme96->capture_periodsize) != 0)
|
|
|
|
snd_pcm_hw_constraint_minmax(runtime,
|
|
|
|
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
|
|
|
size, size);
|
|
|
|
else
|
|
|
|
snd_pcm_hw_constraint_list(runtime, 0,
|
|
|
|
SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
|
|
|
&hw_constraints_period_bytes);
|
|
|
|
}
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int rate, dummy;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (rme96->playback_substream != NULL) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
rme96->wcreg &= ~RME96_WCR_ADAT;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
rme96->playback_substream = substream;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
runtime->hw = snd_rme96_playback_spdif_info;
|
|
|
|
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
|
|
|
|
snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
|
|
|
|
(rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
|
|
|
|
{
|
|
|
|
/* slave clock */
|
2007-08-13 08:40:54 -07:00
|
|
|
runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
|
2005-04-16 15:20:36 -07:00
|
|
|
runtime->hw.rate_min = rate;
|
|
|
|
runtime->hw.rate_max = rate;
|
|
|
|
}
|
2006-05-10 07:24:42 -07:00
|
|
|
rme96_set_buffer_size_constraint(rme96, runtime);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
rme96->wcreg_spdif_stream = rme96->wcreg_spdif;
|
|
|
|
rme96->spdif_ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
|
|
|
SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int isadat, rate;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
runtime->hw = snd_rme96_capture_spdif_info;
|
|
|
|
if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
|
|
|
|
(rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0)
|
|
|
|
{
|
|
|
|
if (isadat) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
2007-08-13 08:40:54 -07:00
|
|
|
runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
|
2005-04-16 15:20:36 -07:00
|
|
|
runtime->hw.rate_min = rate;
|
|
|
|
runtime->hw.rate_max = rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (rme96->capture_substream != NULL) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
rme96->capture_substream = substream;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
2006-05-10 07:24:42 -07:00
|
|
|
rme96_set_buffer_size_constraint(rme96, runtime);
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int rate, dummy;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (rme96->playback_substream != NULL) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
rme96->wcreg |= RME96_WCR_ADAT;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
rme96->playback_substream = substream;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
runtime->hw = snd_rme96_playback_adat_info;
|
|
|
|
if (!(rme96->wcreg & RME96_WCR_MASTER) &&
|
|
|
|
snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
|
|
|
|
(rate = snd_rme96_capture_getrate(rme96, &dummy)) > 0)
|
|
|
|
{
|
|
|
|
/* slave clock */
|
2007-08-13 08:40:54 -07:00
|
|
|
runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
|
2005-04-16 15:20:36 -07:00
|
|
|
runtime->hw.rate_min = rate;
|
|
|
|
runtime->hw.rate_max = rate;
|
|
|
|
}
|
2006-05-10 07:24:42 -07:00
|
|
|
rme96_set_buffer_size_constraint(rme96, runtime);
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int isadat, rate;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
runtime->hw = snd_rme96_capture_adat_info;
|
|
|
|
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
|
|
|
|
/* makes no sense to use analog input. Note that analog
|
|
|
|
expension cards AEB4/8-I are RME96_INPUT_INTERNAL */
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
if ((rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) {
|
|
|
|
if (!isadat) {
|
|
|
|
return -EIO;
|
|
|
|
}
|
2007-08-13 08:40:54 -07:00
|
|
|
runtime->hw.rates = snd_pcm_rate_to_rate_bit(rate);
|
2005-04-16 15:20:36 -07:00
|
|
|
runtime->hw.rate_min = rate;
|
|
|
|
runtime->hw.rate_max = rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (rme96->capture_substream != NULL) {
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
rme96->capture_substream = substream;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
2006-05-10 07:24:42 -07:00
|
|
|
rme96_set_buffer_size_constraint(rme96, runtime);
|
2005-04-16 15:20:36 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_close(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
int spdif = 0;
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (RME96_ISPLAYING(rme96)) {
|
|
|
|
snd_rme96_playback_stop(rme96);
|
|
|
|
}
|
|
|
|
rme96->playback_substream = NULL;
|
|
|
|
rme96->playback_periodsize = 0;
|
|
|
|
spdif = (rme96->wcreg & RME96_WCR_ADAT) == 0;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
if (spdif) {
|
|
|
|
rme96->spdif_ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
|
|
|
|
snd_ctl_notify(rme96->card, SNDRV_CTL_EVENT_MASK_VALUE |
|
|
|
|
SNDRV_CTL_EVENT_MASK_INFO, &rme96->spdif_ctl->id);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_close(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (RME96_ISRECORDING(rme96)) {
|
|
|
|
snd_rme96_capture_stop(rme96);
|
|
|
|
}
|
|
|
|
rme96->capture_substream = NULL;
|
|
|
|
rme96->capture_periodsize = 0;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (RME96_ISPLAYING(rme96)) {
|
|
|
|
snd_rme96_playback_stop(rme96);
|
|
|
|
}
|
|
|
|
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
if (RME96_ISRECORDING(rme96)) {
|
|
|
|
snd_rme96_capture_stop(rme96);
|
|
|
|
}
|
|
|
|
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int cmd)
|
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
|
|
if (!RME96_ISPLAYING(rme96)) {
|
|
|
|
if (substream != rme96->playback_substream) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
snd_rme96_playback_start(rme96, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
|
if (RME96_ISPLAYING(rme96)) {
|
|
|
|
if (substream != rme96->playback_substream) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
snd_rme96_playback_stop(rme96);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
|
if (RME96_ISPLAYING(rme96)) {
|
|
|
|
snd_rme96_playback_stop(rme96);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
|
if (!RME96_ISPLAYING(rme96)) {
|
|
|
|
snd_rme96_playback_start(rme96, 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
|
2005-04-16 15:20:36 -07:00
|
|
|
int cmd)
|
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
|
|
if (!RME96_ISRECORDING(rme96)) {
|
|
|
|
if (substream != rme96->capture_substream) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
snd_rme96_capture_start(rme96, 0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
|
if (RME96_ISRECORDING(rme96)) {
|
|
|
|
if (substream != rme96->capture_substream) {
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
snd_rme96_capture_stop(rme96);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
|
if (RME96_ISRECORDING(rme96)) {
|
|
|
|
snd_rme96_capture_stop(rme96);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
|
if (!RME96_ISRECORDING(rme96)) {
|
|
|
|
snd_rme96_capture_start(rme96, 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static snd_pcm_uframes_t
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_playback_pointer(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
return snd_rme96_playback_ptr(rme96);
|
|
|
|
}
|
|
|
|
|
|
|
|
static snd_pcm_uframes_t
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_capture_pointer(struct snd_pcm_substream *substream)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_pcm_substream_chip(substream);
|
2005-04-16 15:20:36 -07:00
|
|
|
return snd_rme96_capture_ptr(rme96);
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
|
2005-04-16 15:20:36 -07:00
|
|
|
.open = snd_rme96_playback_spdif_open,
|
|
|
|
.close = snd_rme96_playback_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_rme96_playback_hw_params,
|
|
|
|
.prepare = snd_rme96_playback_prepare,
|
|
|
|
.trigger = snd_rme96_playback_trigger,
|
|
|
|
.pointer = snd_rme96_playback_pointer,
|
|
|
|
.copy = snd_rme96_playback_copy,
|
|
|
|
.silence = snd_rme96_playback_silence,
|
|
|
|
.mmap = snd_pcm_lib_mmap_iomem,
|
|
|
|
};
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
|
2005-04-16 15:20:36 -07:00
|
|
|
.open = snd_rme96_capture_spdif_open,
|
|
|
|
.close = snd_rme96_capture_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_rme96_capture_hw_params,
|
|
|
|
.prepare = snd_rme96_capture_prepare,
|
|
|
|
.trigger = snd_rme96_capture_trigger,
|
|
|
|
.pointer = snd_rme96_capture_pointer,
|
|
|
|
.copy = snd_rme96_capture_copy,
|
|
|
|
.mmap = snd_pcm_lib_mmap_iomem,
|
|
|
|
};
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_ops snd_rme96_playback_adat_ops = {
|
2005-04-16 15:20:36 -07:00
|
|
|
.open = snd_rme96_playback_adat_open,
|
|
|
|
.close = snd_rme96_playback_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_rme96_playback_hw_params,
|
|
|
|
.prepare = snd_rme96_playback_prepare,
|
|
|
|
.trigger = snd_rme96_playback_trigger,
|
|
|
|
.pointer = snd_rme96_playback_pointer,
|
|
|
|
.copy = snd_rme96_playback_copy,
|
|
|
|
.silence = snd_rme96_playback_silence,
|
|
|
|
.mmap = snd_pcm_lib_mmap_iomem,
|
|
|
|
};
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_pcm_ops snd_rme96_capture_adat_ops = {
|
2005-04-16 15:20:36 -07:00
|
|
|
.open = snd_rme96_capture_adat_open,
|
|
|
|
.close = snd_rme96_capture_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_rme96_capture_hw_params,
|
|
|
|
.prepare = snd_rme96_capture_prepare,
|
|
|
|
.trigger = snd_rme96_capture_trigger,
|
|
|
|
.pointer = snd_rme96_capture_pointer,
|
|
|
|
.copy = snd_rme96_capture_copy,
|
|
|
|
.mmap = snd_pcm_lib_mmap_iomem,
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
snd_rme96_free(void *private_data)
|
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = (struct rme96 *)private_data;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
if (rme96 == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (rme96->irq >= 0) {
|
|
|
|
snd_rme96_playback_stop(rme96);
|
|
|
|
snd_rme96_capture_stop(rme96);
|
|
|
|
rme96->areg &= ~RME96_AR_DAC_EN;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
free_irq(rme96->irq, (void *)rme96);
|
|
|
|
rme96->irq = -1;
|
|
|
|
}
|
|
|
|
if (rme96->iobase) {
|
|
|
|
iounmap(rme96->iobase);
|
|
|
|
rme96->iobase = NULL;
|
|
|
|
}
|
|
|
|
if (rme96->port) {
|
|
|
|
pci_release_regions(rme96->pci);
|
|
|
|
rme96->port = 0;
|
|
|
|
}
|
|
|
|
pci_disable_device(rme96->pci);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_free_spdif_pcm(struct snd_pcm *pcm)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
|
2005-04-16 15:20:36 -07:00
|
|
|
rme96->spdif_pcm = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_free_adat_pcm(struct snd_pcm *pcm)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = (struct rme96 *) pcm->private_data;
|
2005-04-16 15:20:36 -07:00
|
|
|
rme96->adat_pcm = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __devinit
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_create(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
struct pci_dev *pci = rme96->pci;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
rme96->irq = -1;
|
|
|
|
spin_lock_init(&rme96->lock);
|
|
|
|
|
|
|
|
if ((err = pci_enable_device(pci)) < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if ((err = pci_request_regions(pci, "RME96")) < 0)
|
|
|
|
return err;
|
|
|
|
rme96->port = pci_resource_start(rme96->pci, 0);
|
|
|
|
|
2008-02-28 03:57:47 -07:00
|
|
|
rme96->iobase = ioremap_nocache(rme96->port, RME96_IO_SIZE);
|
|
|
|
if (!rme96->iobase) {
|
2006-06-06 06:44:34 -07:00
|
|
|
snd_printk(KERN_ERR "unable to remap memory region 0x%lx-0x%lx\n", rme96->port, rme96->port + RME96_IO_SIZE - 1);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2006-11-21 04:14:23 -07:00
|
|
|
if (request_irq(pci->irq, snd_rme96_interrupt, IRQF_SHARED,
|
|
|
|
"RME96", rme96)) {
|
2005-10-20 09:26:44 -07:00
|
|
|
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
|
2005-04-16 15:20:36 -07:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
rme96->irq = pci->irq;
|
|
|
|
|
|
|
|
/* read the card's revision number */
|
|
|
|
pci_read_config_byte(pci, 8, &rme96->rev);
|
|
|
|
|
|
|
|
/* set up ALSA pcm device for S/PDIF */
|
|
|
|
if ((err = snd_pcm_new(rme96->card, "Digi96 IEC958", 0,
|
|
|
|
1, 1, &rme96->spdif_pcm)) < 0)
|
|
|
|
{
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
rme96->spdif_pcm->private_data = rme96;
|
|
|
|
rme96->spdif_pcm->private_free = snd_rme96_free_spdif_pcm;
|
|
|
|
strcpy(rme96->spdif_pcm->name, "Digi96 IEC958");
|
|
|
|
snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_spdif_ops);
|
|
|
|
snd_pcm_set_ops(rme96->spdif_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_spdif_ops);
|
|
|
|
|
|
|
|
rme96->spdif_pcm->info_flags = 0;
|
|
|
|
|
|
|
|
/* set up ALSA pcm device for ADAT */
|
2005-09-14 14:19:17 -07:00
|
|
|
if (pci->device == PCI_DEVICE_ID_RME_DIGI96) {
|
2005-04-16 15:20:36 -07:00
|
|
|
/* ADAT is not available on the base model */
|
|
|
|
rme96->adat_pcm = NULL;
|
|
|
|
} else {
|
|
|
|
if ((err = snd_pcm_new(rme96->card, "Digi96 ADAT", 1,
|
|
|
|
1, 1, &rme96->adat_pcm)) < 0)
|
|
|
|
{
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
rme96->adat_pcm->private_data = rme96;
|
|
|
|
rme96->adat_pcm->private_free = snd_rme96_free_adat_pcm;
|
|
|
|
strcpy(rme96->adat_pcm->name, "Digi96 ADAT");
|
|
|
|
snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_rme96_playback_adat_ops);
|
|
|
|
snd_pcm_set_ops(rme96->adat_pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_rme96_capture_adat_ops);
|
|
|
|
|
|
|
|
rme96->adat_pcm->info_flags = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
rme96->playback_periodsize = 0;
|
|
|
|
rme96->capture_periodsize = 0;
|
|
|
|
|
|
|
|
/* make sure playback/capture is stopped, if by some reason active */
|
|
|
|
snd_rme96_playback_stop(rme96);
|
|
|
|
snd_rme96_capture_stop(rme96);
|
|
|
|
|
|
|
|
/* set default values in registers */
|
|
|
|
rme96->wcreg =
|
|
|
|
RME96_WCR_FREQ_1 | /* set 44.1 kHz playback */
|
|
|
|
RME96_WCR_SEL | /* normal playback */
|
|
|
|
RME96_WCR_MASTER | /* set to master clock mode */
|
|
|
|
RME96_WCR_INP_0; /* set coaxial input */
|
|
|
|
|
|
|
|
rme96->areg = RME96_AR_FREQPAD_1; /* set 44.1 kHz analog capture */
|
|
|
|
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
|
|
|
|
/* reset the ADC */
|
|
|
|
writel(rme96->areg | RME96_AR_PD2,
|
|
|
|
rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
|
|
|
|
/* reset and enable the DAC (order is important). */
|
|
|
|
snd_rme96_reset_dac(rme96);
|
|
|
|
rme96->areg |= RME96_AR_DAC_EN;
|
|
|
|
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
|
|
|
|
|
|
|
/* reset playback and record buffer pointers */
|
|
|
|
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
|
|
|
|
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
|
|
|
|
|
|
|
|
/* reset volume */
|
|
|
|
rme96->vol[0] = rme96->vol[1] = 0;
|
|
|
|
if (RME96_HAS_ANALOG_OUT(rme96)) {
|
|
|
|
snd_rme96_apply_dac_volume(rme96);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init switch interface */
|
|
|
|
if ((err = snd_rme96_create_switches(rme96->card, rme96)) < 0) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* init proc interface */
|
|
|
|
snd_rme96_proc_init(rme96);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* proc interface
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int n;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = (struct rme96 *)entry->private_data;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
|
|
|
|
snd_iprintf(buffer, rme96->card->longname);
|
|
|
|
snd_iprintf(buffer, " (index #%d)\n", rme96->card->number + 1);
|
|
|
|
|
|
|
|
snd_iprintf(buffer, "\nGeneral settings\n");
|
|
|
|
if (rme96->wcreg & RME96_WCR_IDIS) {
|
|
|
|
snd_iprintf(buffer, " period size: N/A (interrupts "
|
|
|
|
"disabled)\n");
|
|
|
|
} else if (rme96->wcreg & RME96_WCR_ISEL) {
|
|
|
|
snd_iprintf(buffer, " period size: 2048 bytes\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " period size: 8192 bytes\n");
|
|
|
|
}
|
|
|
|
snd_iprintf(buffer, "\nInput settings\n");
|
|
|
|
switch (snd_rme96_getinputtype(rme96)) {
|
|
|
|
case RME96_INPUT_OPTICAL:
|
|
|
|
snd_iprintf(buffer, " input: optical");
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_COAXIAL:
|
|
|
|
snd_iprintf(buffer, " input: coaxial");
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_INTERNAL:
|
|
|
|
snd_iprintf(buffer, " input: internal");
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_XLR:
|
|
|
|
snd_iprintf(buffer, " input: XLR");
|
|
|
|
break;
|
|
|
|
case RME96_INPUT_ANALOG:
|
|
|
|
snd_iprintf(buffer, " input: analog");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (snd_rme96_capture_getrate(rme96, &n) < 0) {
|
|
|
|
snd_iprintf(buffer, "\n sample rate: no valid signal\n");
|
|
|
|
} else {
|
|
|
|
if (n) {
|
|
|
|
snd_iprintf(buffer, " (8 channels)\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " (2 channels)\n");
|
|
|
|
}
|
|
|
|
snd_iprintf(buffer, " sample rate: %d Hz\n",
|
|
|
|
snd_rme96_capture_getrate(rme96, &n));
|
|
|
|
}
|
|
|
|
if (rme96->wcreg & RME96_WCR_MODE24_2) {
|
|
|
|
snd_iprintf(buffer, " sample format: 24 bit\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " sample format: 16 bit\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_iprintf(buffer, "\nOutput settings\n");
|
|
|
|
if (rme96->wcreg & RME96_WCR_SEL) {
|
|
|
|
snd_iprintf(buffer, " output signal: normal playback\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " output signal: same as input\n");
|
|
|
|
}
|
|
|
|
snd_iprintf(buffer, " sample rate: %d Hz\n",
|
|
|
|
snd_rme96_playback_getrate(rme96));
|
|
|
|
if (rme96->wcreg & RME96_WCR_MODE24) {
|
|
|
|
snd_iprintf(buffer, " sample format: 24 bit\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " sample format: 16 bit\n");
|
|
|
|
}
|
|
|
|
if (rme96->areg & RME96_AR_WSEL) {
|
|
|
|
snd_iprintf(buffer, " sample clock source: word clock\n");
|
|
|
|
} else if (rme96->wcreg & RME96_WCR_MASTER) {
|
|
|
|
snd_iprintf(buffer, " sample clock source: internal\n");
|
|
|
|
} else if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
|
|
|
|
snd_iprintf(buffer, " sample clock source: autosync (internal anyway due to analog input setting)\n");
|
|
|
|
} else if (snd_rme96_capture_getrate(rme96, &n) < 0) {
|
|
|
|
snd_iprintf(buffer, " sample clock source: autosync (internal anyway due to no valid signal)\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " sample clock source: autosync\n");
|
|
|
|
}
|
|
|
|
if (rme96->wcreg & RME96_WCR_PRO) {
|
|
|
|
snd_iprintf(buffer, " format: AES/EBU (professional)\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " format: IEC958 (consumer)\n");
|
|
|
|
}
|
|
|
|
if (rme96->wcreg & RME96_WCR_EMP) {
|
|
|
|
snd_iprintf(buffer, " emphasis: on\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " emphasis: off\n");
|
|
|
|
}
|
|
|
|
if (rme96->wcreg & RME96_WCR_DOLBY) {
|
|
|
|
snd_iprintf(buffer, " non-audio (dolby): on\n");
|
|
|
|
} else {
|
|
|
|
snd_iprintf(buffer, " non-audio (dolby): off\n");
|
|
|
|
}
|
|
|
|
if (RME96_HAS_ANALOG_IN(rme96)) {
|
|
|
|
snd_iprintf(buffer, "\nAnalog output settings\n");
|
|
|
|
switch (snd_rme96_getmontracks(rme96)) {
|
|
|
|
case RME96_MONITOR_TRACKS_1_2:
|
|
|
|
snd_iprintf(buffer, " monitored ADAT tracks: 1+2\n");
|
|
|
|
break;
|
|
|
|
case RME96_MONITOR_TRACKS_3_4:
|
|
|
|
snd_iprintf(buffer, " monitored ADAT tracks: 3+4\n");
|
|
|
|
break;
|
|
|
|
case RME96_MONITOR_TRACKS_5_6:
|
|
|
|
snd_iprintf(buffer, " monitored ADAT tracks: 5+6\n");
|
|
|
|
break;
|
|
|
|
case RME96_MONITOR_TRACKS_7_8:
|
|
|
|
snd_iprintf(buffer, " monitored ADAT tracks: 7+8\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
switch (snd_rme96_getattenuation(rme96)) {
|
|
|
|
case RME96_ATTENUATION_0:
|
|
|
|
snd_iprintf(buffer, " attenuation: 0 dB\n");
|
|
|
|
break;
|
|
|
|
case RME96_ATTENUATION_6:
|
|
|
|
snd_iprintf(buffer, " attenuation: -6 dB\n");
|
|
|
|
break;
|
|
|
|
case RME96_ATTENUATION_12:
|
|
|
|
snd_iprintf(buffer, " attenuation: -12 dB\n");
|
|
|
|
break;
|
|
|
|
case RME96_ATTENUATION_18:
|
|
|
|
snd_iprintf(buffer, " attenuation: -18 dB\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
snd_iprintf(buffer, " volume left: %u\n", rme96->vol[0]);
|
|
|
|
snd_iprintf(buffer, " volume right: %u\n", rme96->vol[1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __devinit
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_proc_init(struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct snd_info_entry *entry;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
if (! snd_card_proc_new(rme96->card, "rme96", &entry))
|
2006-04-28 06:13:41 -07:00
|
|
|
snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* control interface
|
|
|
|
*/
|
|
|
|
|
2007-07-23 06:42:26 -07:00
|
|
|
#define snd_rme96_info_loopback_control snd_ctl_boolean_mono_info
|
|
|
|
|
2005-04-16 15:20:36 -07:00
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_get_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
ucontrol->value.integer.value[0] = rme96->wcreg & RME96_WCR_SEL ? 0 : 1;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_put_loopback_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned int val;
|
|
|
|
int change;
|
|
|
|
|
|
|
|
val = ucontrol->value.integer.value[0] ? 0 : RME96_WCR_SEL;
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
val = (rme96->wcreg & ~RME96_WCR_SEL) | val;
|
|
|
|
change = val != rme96->wcreg;
|
|
|
|
rme96->wcreg = val;
|
|
|
|
writel(val, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_info_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
static char *_texts[5] = { "Optical", "Coaxial", "Internal", "XLR", "Analog" };
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
char *texts[5] = { _texts[0], _texts[1], _texts[2], _texts[3], _texts[4] };
|
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
switch (rme96->pci->device) {
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96:
|
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8:
|
2005-04-16 15:20:36 -07:00
|
|
|
uinfo->value.enumerated.items = 3;
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PRO:
|
2005-04-16 15:20:36 -07:00
|
|
|
uinfo->value.enumerated.items = 4;
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST:
|
2005-04-16 15:20:36 -07:00
|
|
|
if (rme96->rev > 4) {
|
|
|
|
/* PST */
|
|
|
|
uinfo->value.enumerated.items = 4;
|
|
|
|
texts[3] = _texts[4]; /* Analog instead of XLR */
|
|
|
|
} else {
|
|
|
|
/* PAD */
|
|
|
|
uinfo->value.enumerated.items = 5;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
snd_BUG();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (uinfo->value.enumerated.item > uinfo->value.enumerated.items - 1) {
|
|
|
|
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
|
|
|
}
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_get_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned int items = 3;
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
ucontrol->value.enumerated.item[0] = snd_rme96_getinputtype(rme96);
|
|
|
|
|
|
|
|
switch (rme96->pci->device) {
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96:
|
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8:
|
2005-04-16 15:20:36 -07:00
|
|
|
items = 3;
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PRO:
|
2005-04-16 15:20:36 -07:00
|
|
|
items = 4;
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST:
|
2005-04-16 15:20:36 -07:00
|
|
|
if (rme96->rev > 4) {
|
|
|
|
/* for handling PST case, (INPUT_ANALOG is moved to INPUT_XLR */
|
|
|
|
if (ucontrol->value.enumerated.item[0] == RME96_INPUT_ANALOG) {
|
|
|
|
ucontrol->value.enumerated.item[0] = RME96_INPUT_XLR;
|
|
|
|
}
|
|
|
|
items = 4;
|
|
|
|
} else {
|
|
|
|
items = 5;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
snd_BUG();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (ucontrol->value.enumerated.item[0] >= items) {
|
|
|
|
ucontrol->value.enumerated.item[0] = items - 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_put_inputtype_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned int val;
|
|
|
|
int change, items = 3;
|
|
|
|
|
|
|
|
switch (rme96->pci->device) {
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96:
|
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8:
|
2005-04-16 15:20:36 -07:00
|
|
|
items = 3;
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PRO:
|
2005-04-16 15:20:36 -07:00
|
|
|
items = 4;
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST:
|
2005-04-16 15:20:36 -07:00
|
|
|
if (rme96->rev > 4) {
|
|
|
|
items = 4;
|
|
|
|
} else {
|
|
|
|
items = 5;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
snd_BUG();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
val = ucontrol->value.enumerated.item[0] % items;
|
|
|
|
|
|
|
|
/* special case for PST */
|
2005-09-14 14:19:17 -07:00
|
|
|
if (rme96->pci->device == PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST && rme96->rev > 4) {
|
2005-04-16 15:20:36 -07:00
|
|
|
if (val == RME96_INPUT_XLR) {
|
|
|
|
val = RME96_INPUT_ANALOG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
change = (int)val != snd_rme96_getinputtype(rme96);
|
|
|
|
snd_rme96_setinputtype(rme96, val);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_info_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
static char *texts[3] = { "AutoSync", "Internal", "Word" };
|
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.enumerated.items = 3;
|
|
|
|
if (uinfo->value.enumerated.item > 2) {
|
|
|
|
uinfo->value.enumerated.item = 2;
|
|
|
|
}
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_get_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
ucontrol->value.enumerated.item[0] = snd_rme96_getclockmode(rme96);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_put_clockmode_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned int val;
|
|
|
|
int change;
|
|
|
|
|
|
|
|
val = ucontrol->value.enumerated.item[0] % 3;
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
change = (int)val != snd_rme96_getclockmode(rme96);
|
|
|
|
snd_rme96_setclockmode(rme96, val);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_info_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
static char *texts[4] = { "0 dB", "-6 dB", "-12 dB", "-18 dB" };
|
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.enumerated.items = 4;
|
|
|
|
if (uinfo->value.enumerated.item > 3) {
|
|
|
|
uinfo->value.enumerated.item = 3;
|
|
|
|
}
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_get_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
ucontrol->value.enumerated.item[0] = snd_rme96_getattenuation(rme96);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_put_attenuation_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned int val;
|
|
|
|
int change;
|
|
|
|
|
|
|
|
val = ucontrol->value.enumerated.item[0] % 4;
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
change = (int)val != snd_rme96_getattenuation(rme96);
|
|
|
|
snd_rme96_setattenuation(rme96, val);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_info_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
static char *texts[4] = { "1+2", "3+4", "5+6", "7+8" };
|
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.enumerated.items = 4;
|
|
|
|
if (uinfo->value.enumerated.item > 3) {
|
|
|
|
uinfo->value.enumerated.item = 3;
|
|
|
|
}
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_get_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
ucontrol->value.enumerated.item[0] = snd_rme96_getmontracks(rme96);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_put_montracks_control(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
unsigned int val;
|
|
|
|
int change;
|
|
|
|
|
|
|
|
val = ucontrol->value.enumerated.item[0] % 4;
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
change = (int)val != snd_rme96_getmontracks(rme96);
|
|
|
|
snd_rme96_setmontracks(rme96, val);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static u32 snd_rme96_convert_from_aes(struct snd_aes_iec958 *aes)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
u32 val = 0;
|
|
|
|
val |= (aes->status[0] & IEC958_AES0_PROFESSIONAL) ? RME96_WCR_PRO : 0;
|
|
|
|
val |= (aes->status[0] & IEC958_AES0_NONAUDIO) ? RME96_WCR_DOLBY : 0;
|
|
|
|
if (val & RME96_WCR_PRO)
|
|
|
|
val |= (aes->status[0] & IEC958_AES0_PRO_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
|
|
|
|
else
|
|
|
|
val |= (aes->status[0] & IEC958_AES0_CON_EMPHASIS_5015) ? RME96_WCR_EMP : 0;
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static void snd_rme96_convert_to_aes(struct snd_aes_iec958 *aes, u32 val)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
aes->status[0] = ((val & RME96_WCR_PRO) ? IEC958_AES0_PROFESSIONAL : 0) |
|
|
|
|
((val & RME96_WCR_DOLBY) ? IEC958_AES0_NONAUDIO : 0);
|
|
|
|
if (val & RME96_WCR_PRO)
|
|
|
|
aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_PRO_EMPHASIS_5015 : 0;
|
|
|
|
else
|
|
|
|
aes->status[0] |= (val & RME96_WCR_EMP) ? IEC958_AES0_CON_EMPHASIS_5015 : 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
int change;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
change = val != rme96->wcreg_spdif;
|
|
|
|
rme96->wcreg_spdif = val;
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_stream_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_stream_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
snd_rme96_convert_to_aes(&ucontrol->value.iec958, rme96->wcreg_spdif_stream);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_stream_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
int change;
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
val = snd_rme96_convert_from_aes(&ucontrol->value.iec958);
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
change = val != rme96->wcreg_spdif_stream;
|
|
|
|
rme96->wcreg_spdif_stream = val;
|
|
|
|
rme96->wcreg &= ~(RME96_WCR_PRO | RME96_WCR_DOLBY | RME96_WCR_EMP);
|
|
|
|
rme96->wcreg |= val;
|
|
|
|
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_mask_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static int snd_rme96_control_spdif_mask_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
ucontrol->value.iec958.status[0] = kcontrol->private_value;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_dac_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
|
|
uinfo->count = 2;
|
|
|
|
uinfo->value.integer.min = 0;
|
|
|
|
uinfo->value.integer.max = RME96_185X_MAX_OUT(rme96);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_dac_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
spin_lock_irq(&rme96->lock);
|
|
|
|
u->value.integer.value[0] = rme96->vol[0];
|
|
|
|
u->value.integer.value[1] = rme96->vol[1];
|
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_dac_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96 = snd_kcontrol_chip(kcontrol);
|
2005-04-16 15:20:36 -07:00
|
|
|
int change = 0;
|
2007-11-15 07:58:13 -07:00
|
|
|
unsigned int vol, maxvol;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
2007-11-15 07:58:13 -07:00
|
|
|
|
|
|
|
if (!RME96_HAS_ANALOG_OUT(rme96))
|
2005-04-16 15:20:36 -07:00
|
|
|
return -EINVAL;
|
2007-11-15 07:58:13 -07:00
|
|
|
maxvol = RME96_185X_MAX_OUT(rme96);
|
2005-04-16 15:20:36 -07:00
|
|
|
spin_lock_irq(&rme96->lock);
|
2007-11-15 07:58:13 -07:00
|
|
|
vol = u->value.integer.value[0];
|
|
|
|
if (vol != rme96->vol[0] && vol <= maxvol) {
|
|
|
|
rme96->vol[0] = vol;
|
|
|
|
change = 1;
|
|
|
|
}
|
|
|
|
vol = u->value.integer.value[1];
|
|
|
|
if (vol != rme96->vol[1] && vol <= maxvol) {
|
|
|
|
rme96->vol[1] = vol;
|
|
|
|
change = 1;
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
2007-11-15 07:58:13 -07:00
|
|
|
if (change)
|
|
|
|
snd_rme96_apply_dac_volume(rme96);
|
2005-04-16 15:20:36 -07:00
|
|
|
spin_unlock_irq(&rme96->lock);
|
|
|
|
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static struct snd_kcontrol_new snd_rme96_controls[] = {
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
|
|
|
.info = snd_rme96_control_spdif_info,
|
|
|
|
.get = snd_rme96_control_spdif_get,
|
|
|
|
.put = snd_rme96_control_spdif_put
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.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_rme96_control_spdif_stream_info,
|
|
|
|
.get = snd_rme96_control_spdif_stream_get,
|
|
|
|
.put = snd_rme96_control_spdif_stream_put
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
[ALSA] sound - fix .iface field of mixer control elements
Documentation,CS46xx driver,EMU10K1/EMU10K2 driver,AD1848 driver
SB16/AWE driver,CMIPCI driver,ENS1370/1+ driver,RME32 driver
RME96 driver,ICE1712 driver,ICE1724 driver,KORG1212 driver
RME HDSP driver,RME9652 driver
This patch changes .iface to SNDRV_CTL_ELEM_IFACE_MIXER whre _PCM or
_HWDEP was used in controls that are not associated with a specific PCM
(sub)stream or hwdep device, and changes some controls that got
inconsitent .iface values due to copy+paste errors. Furthermore, it
makes sure that all control that do use _PCM or _HWDEP use the correct
number in the .device field.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
2005-07-29 06:32:58 -07:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
2005-04-16 15:20:36 -07:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
|
|
|
|
.info = snd_rme96_control_spdif_mask_info,
|
|
|
|
.get = snd_rme96_control_spdif_mask_get,
|
|
|
|
.private_value = IEC958_AES0_NONAUDIO |
|
|
|
|
IEC958_AES0_PROFESSIONAL |
|
|
|
|
IEC958_AES0_CON_EMPHASIS
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
[ALSA] sound - fix .iface field of mixer control elements
Documentation,CS46xx driver,EMU10K1/EMU10K2 driver,AD1848 driver
SB16/AWE driver,CMIPCI driver,ENS1370/1+ driver,RME32 driver
RME96 driver,ICE1712 driver,ICE1724 driver,KORG1212 driver
RME HDSP driver,RME9652 driver
This patch changes .iface to SNDRV_CTL_ELEM_IFACE_MIXER whre _PCM or
_HWDEP was used in controls that are not associated with a specific PCM
(sub)stream or hwdep device, and changes some controls that got
inconsitent .iface values due to copy+paste errors. Furthermore, it
makes sure that all control that do use _PCM or _HWDEP use the correct
number in the .device field.
Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
2005-07-29 06:32:58 -07:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
2005-04-16 15:20:36 -07:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
|
|
|
|
.info = snd_rme96_control_spdif_mask_info,
|
|
|
|
.get = snd_rme96_control_spdif_mask_get,
|
|
|
|
.private_value = IEC958_AES0_NONAUDIO |
|
|
|
|
IEC958_AES0_PROFESSIONAL |
|
|
|
|
IEC958_AES0_PRO_EMPHASIS
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Input Connector",
|
|
|
|
.info = snd_rme96_info_inputtype_control,
|
|
|
|
.get = snd_rme96_get_inputtype_control,
|
|
|
|
.put = snd_rme96_put_inputtype_control
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Loopback Input",
|
|
|
|
.info = snd_rme96_info_loopback_control,
|
|
|
|
.get = snd_rme96_get_loopback_control,
|
|
|
|
.put = snd_rme96_put_loopback_control
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Sample Clock Source",
|
|
|
|
.info = snd_rme96_info_clockmode_control,
|
|
|
|
.get = snd_rme96_get_clockmode_control,
|
|
|
|
.put = snd_rme96_put_clockmode_control
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Monitor Tracks",
|
|
|
|
.info = snd_rme96_info_montracks_control,
|
|
|
|
.get = snd_rme96_get_montracks_control,
|
|
|
|
.put = snd_rme96_put_montracks_control
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Attenuation",
|
|
|
|
.info = snd_rme96_info_attenuation_control,
|
|
|
|
.get = snd_rme96_get_attenuation_control,
|
|
|
|
.put = snd_rme96_put_attenuation_control
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "DAC Playback Volume",
|
|
|
|
.info = snd_rme96_dac_volume_info,
|
|
|
|
.get = snd_rme96_dac_volume_get,
|
|
|
|
.put = snd_rme96_dac_volume_put
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static int
|
2005-11-17 07:05:37 -07:00
|
|
|
snd_rme96_create_switches(struct snd_card *card,
|
|
|
|
struct rme96 *rme96)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
int idx, err;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct snd_kcontrol *kctl;
|
2005-04-16 15:20:36 -07:00
|
|
|
|
|
|
|
for (idx = 0; idx < 7; idx++) {
|
|
|
|
if ((err = snd_ctl_add(card, kctl = snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
|
|
|
|
return err;
|
|
|
|
if (idx == 1) /* IEC958 (S/PDIF) Stream */
|
|
|
|
rme96->spdif_ctl = kctl;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (RME96_HAS_ANALOG_OUT(rme96)) {
|
|
|
|
for (idx = 7; idx < 10; idx++)
|
|
|
|
if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_rme96_controls[idx], rme96))) < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Card initialisation
|
|
|
|
*/
|
|
|
|
|
2005-11-17 07:05:37 -07:00
|
|
|
static void snd_rme96_card_free(struct snd_card *card)
|
2005-04-16 15:20:36 -07:00
|
|
|
{
|
|
|
|
snd_rme96_free(card->private_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __devinit
|
|
|
|
snd_rme96_probe(struct pci_dev *pci,
|
|
|
|
const struct pci_device_id *pci_id)
|
|
|
|
{
|
|
|
|
static int dev;
|
2005-11-17 07:05:37 -07:00
|
|
|
struct rme96 *rme96;
|
|
|
|
struct snd_card *card;
|
2005-04-16 15:20:36 -07:00
|
|
|
int err;
|
|
|
|
u8 val;
|
|
|
|
|
|
|
|
if (dev >= SNDRV_CARDS) {
|
|
|
|
return -ENODEV;
|
|
|
|
}
|
|
|
|
if (!enable[dev]) {
|
|
|
|
dev++;
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2008-12-28 08:44:30 -07:00
|
|
|
err = snd_card_create(index[dev], id[dev], THIS_MODULE,
|
|
|
|
sizeof(struct rme96), &card);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
2005-04-16 15:20:36 -07:00
|
|
|
card->private_free = snd_rme96_card_free;
|
2005-11-17 07:05:37 -07:00
|
|
|
rme96 = (struct rme96 *)card->private_data;
|
2005-04-16 15:20:36 -07:00
|
|
|
rme96->card = card;
|
|
|
|
rme96->pci = pci;
|
|
|
|
snd_card_set_dev(card, &pci->dev);
|
|
|
|
if ((err = snd_rme96_create(rme96)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(card->driver, "Digi96");
|
|
|
|
switch (rme96->pci->device) {
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96:
|
2005-04-16 15:20:36 -07:00
|
|
|
strcpy(card->shortname, "RME Digi96");
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8:
|
2005-04-16 15:20:36 -07:00
|
|
|
strcpy(card->shortname, "RME Digi96/8");
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PRO:
|
2005-04-16 15:20:36 -07:00
|
|
|
strcpy(card->shortname, "RME Digi96/8 PRO");
|
|
|
|
break;
|
2005-09-14 14:19:17 -07:00
|
|
|
case PCI_DEVICE_ID_RME_DIGI96_8_PAD_OR_PST:
|
2005-04-16 15:20:36 -07:00
|
|
|
pci_read_config_byte(rme96->pci, 8, &val);
|
|
|
|
if (val < 5) {
|
|
|
|
strcpy(card->shortname, "RME Digi96/8 PAD");
|
|
|
|
} else {
|
|
|
|
strcpy(card->shortname, "RME Digi96/8 PST");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sprintf(card->longname, "%s at 0x%lx, irq %d", card->shortname,
|
|
|
|
rme96->port, rme96->irq);
|
|
|
|
|
|
|
|
if ((err = snd_card_register(card)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
pci_set_drvdata(pci, card);
|
|
|
|
dev++;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __devexit snd_rme96_remove(struct pci_dev *pci)
|
|
|
|
{
|
|
|
|
snd_card_free(pci_get_drvdata(pci));
|
|
|
|
pci_set_drvdata(pci, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_driver driver = {
|
|
|
|
.name = "RME Digi96",
|
|
|
|
.id_table = snd_rme96_ids,
|
|
|
|
.probe = snd_rme96_probe,
|
|
|
|
.remove = __devexit_p(snd_rme96_remove),
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init alsa_card_rme96_init(void)
|
|
|
|
{
|
[ALSA] Replace pci_module_init() with pci_register_driver()
Documentation,ALS4000 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,RME32 driver
RME96 driver,SonicVibes driver,VIA82xx driver,VIA82xx-modem driver
ALI5451 driver,au88x0 driver,CA0106 driver,CS46xx driver
EMU10K1/EMU10K2 driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,RME HDSP driver
RME9652 driver,Trident driver,Digigram VX222 driver,YMFPCI driver
Replace the obsolete pci_module_init() with pci_register_driver().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-04-11 07:58:24 -07:00
|
|
|
return pci_register_driver(&driver);
|
2005-04-16 15:20:36 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit alsa_card_rme96_exit(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(alsa_card_rme96_init)
|
|
|
|
module_exit(alsa_card_rme96_exit)
|