2005-04-17 00:20:36 +02:00
|
|
|
/*
|
|
|
|
* ALSA driver for VT1724 ICEnsemble ICE1724 / VIA VT1724 (Envy24HT)
|
|
|
|
* VIA VT1720 (Envy24PT)
|
|
|
|
*
|
2007-10-15 09:50:19 +02:00
|
|
|
* Copyright (c) 2000 Jaroslav Kysela <perex@perex.cz>
|
2005-04-17 00:20:36 +02:00
|
|
|
* 2002 James Stafford <jstafford@ampltd.com>
|
|
|
|
* 2003 Takashi Iwai <tiwai@suse.de>
|
|
|
|
*
|
|
|
|
* 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 <asm/io.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/pci.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/moduleparam.h>
|
2006-01-16 16:34:20 +01:00
|
|
|
#include <linux/mutex.h>
|
2005-04-17 00:20:36 +02:00
|
|
|
#include <sound/core.h>
|
|
|
|
#include <sound/info.h>
|
|
|
|
#include <sound/mpu401.h>
|
|
|
|
#include <sound/initval.h>
|
|
|
|
|
|
|
|
#include <sound/asoundef.h>
|
|
|
|
|
|
|
|
#include "ice1712.h"
|
|
|
|
#include "envy24ht.h"
|
|
|
|
|
|
|
|
/* lowlevel routines */
|
|
|
|
#include "amp.h"
|
|
|
|
#include "revo.h"
|
|
|
|
#include "aureon.h"
|
|
|
|
#include "vt1720_mobo.h"
|
|
|
|
#include "pontis.h"
|
|
|
|
#include "prodigy192.h"
|
2008-01-16 19:50:00 +01:00
|
|
|
#include "prodigy_hifi.h"
|
2005-04-17 00:20:36 +02:00
|
|
|
#include "juli.h"
|
|
|
|
#include "phase.h"
|
2007-01-08 10:48:41 +01:00
|
|
|
#include "wtm.h"
|
2007-10-23 15:08:18 +02:00
|
|
|
#include "se.h"
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-10-15 09:50:19 +02:00
|
|
|
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
|
2005-04-17 00:20:36 +02:00
|
|
|
MODULE_DESCRIPTION("VIA ICEnsemble ICE1724/1720 (Envy24HT/PT)");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_SUPPORTED_DEVICE("{"
|
|
|
|
REVO_DEVICE_DESC
|
|
|
|
AMP_AUDIO2000_DEVICE_DESC
|
|
|
|
AUREON_DEVICE_DESC
|
|
|
|
VT1720_MOBO_DEVICE_DESC
|
|
|
|
PONTIS_DEVICE_DESC
|
|
|
|
PRODIGY192_DEVICE_DESC
|
2008-01-16 19:50:00 +01:00
|
|
|
PRODIGY_HIFI_DEVICE_DESC
|
2005-04-17 00:20:36 +02:00
|
|
|
JULI_DEVICE_DESC
|
|
|
|
PHASE_DEVICE_DESC
|
2007-01-08 10:48:41 +01:00
|
|
|
WTM_DEVICE_DESC
|
2007-10-23 15:08:18 +02:00
|
|
|
SE_DEVICE_DESC
|
2005-04-17 00:20:36 +02:00
|
|
|
"{VIA,VT1720},"
|
|
|
|
"{VIA,VT1724},"
|
|
|
|
"{ICEnsemble,Generic ICE1724},"
|
|
|
|
"{ICEnsemble,Generic Envy24HT}"
|
|
|
|
"{ICEnsemble,Generic Envy24PT}}");
|
|
|
|
|
|
|
|
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 */
|
|
|
|
static char *model[SNDRV_CARDS];
|
|
|
|
|
|
|
|
module_param_array(index, int, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(index, "Index value for ICE1724 soundcard.");
|
|
|
|
module_param_array(id, charp, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(id, "ID string for ICE1724 soundcard.");
|
|
|
|
module_param_array(enable, bool, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(enable, "Enable ICE1724 soundcard.");
|
|
|
|
module_param_array(model, charp, NULL, 0444);
|
|
|
|
MODULE_PARM_DESC(model, "Use the given board model.");
|
|
|
|
|
|
|
|
|
|
|
|
/* Both VT1720 and VT1724 have the same PCI IDs */
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct pci_device_id snd_vt1724_ids[] = {
|
2005-04-17 00:20:36 +02:00
|
|
|
{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_VT1724, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
|
|
|
|
{ 0, }
|
|
|
|
};
|
|
|
|
|
|
|
|
MODULE_DEVICE_TABLE(pci, snd_vt1724_ids);
|
|
|
|
|
|
|
|
|
|
|
|
static int PRO_RATE_LOCKED;
|
|
|
|
static int PRO_RATE_RESET = 1;
|
|
|
|
static unsigned int PRO_RATE_DEFAULT = 44100;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Basic I/O
|
|
|
|
*/
|
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
/*
|
|
|
|
* default rates, default clock routines
|
|
|
|
*/
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
/* check whether the clock mode is spdif-in */
|
2008-03-20 12:10:27 +01:00
|
|
|
static inline int stdclock_is_spdif_master(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
return (inb(ICEMT1724(ice, RATE)) & VT1724_SPDIF_MASTER) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static inline int is_pro_rate_locked(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2008-03-20 12:10:27 +01:00
|
|
|
return ice->is_spdif_master(ice) || PRO_RATE_LOCKED;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ac97 section
|
|
|
|
*/
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static unsigned char snd_vt1724_ac97_ready(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned char old_cmd;
|
|
|
|
int tm;
|
|
|
|
for (tm = 0; tm < 0x10000; tm++) {
|
|
|
|
old_cmd = inb(ICEMT1724(ice, AC97_CMD));
|
|
|
|
if (old_cmd & (VT1724_AC97_WRITE | VT1724_AC97_READ))
|
|
|
|
continue;
|
|
|
|
if (!(old_cmd & VT1724_AC97_READY))
|
|
|
|
continue;
|
|
|
|
return old_cmd;
|
|
|
|
}
|
|
|
|
snd_printd(KERN_ERR "snd_vt1724_ac97_ready: timeout\n");
|
|
|
|
return old_cmd;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_ac97_wait_bit(struct snd_ice1712 *ice, unsigned char bit)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int tm;
|
|
|
|
for (tm = 0; tm < 0x10000; tm++)
|
|
|
|
if ((inb(ICEMT1724(ice, AC97_CMD)) & bit) == 0)
|
|
|
|
return 0;
|
|
|
|
snd_printd(KERN_ERR "snd_vt1724_ac97_wait_bit: timeout\n");
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void snd_vt1724_ac97_write(struct snd_ac97 *ac97,
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned short reg,
|
|
|
|
unsigned short val)
|
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = ac97->private_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char old_cmd;
|
|
|
|
|
|
|
|
old_cmd = snd_vt1724_ac97_ready(ice);
|
|
|
|
old_cmd &= ~VT1724_AC97_ID_MASK;
|
|
|
|
old_cmd |= ac97->num;
|
|
|
|
outb(reg, ICEMT1724(ice, AC97_INDEX));
|
|
|
|
outw(val, ICEMT1724(ice, AC97_DATA));
|
|
|
|
outb(old_cmd | VT1724_AC97_WRITE, ICEMT1724(ice, AC97_CMD));
|
|
|
|
snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_WRITE);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static unsigned short snd_vt1724_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = ac97->private_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char old_cmd;
|
|
|
|
|
|
|
|
old_cmd = snd_vt1724_ac97_ready(ice);
|
|
|
|
old_cmd &= ~VT1724_AC97_ID_MASK;
|
|
|
|
old_cmd |= ac97->num;
|
|
|
|
outb(reg, ICEMT1724(ice, AC97_INDEX));
|
|
|
|
outb(old_cmd | VT1724_AC97_READ, ICEMT1724(ice, AC97_CMD));
|
|
|
|
if (snd_vt1724_ac97_wait_bit(ice, VT1724_AC97_READ) < 0)
|
|
|
|
return ~0;
|
|
|
|
return inw(ICEMT1724(ice, AC97_DATA));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* GPIO operations
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* set gpio direction 0 = read, 1 = write */
|
2005-11-17 15:00:18 +01:00
|
|
|
static void snd_vt1724_set_gpio_dir(struct snd_ice1712 *ice, unsigned int data)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
outl(data, ICEREG1724(ice, GPIO_DIRECTION));
|
|
|
|
inw(ICEREG1724(ice, GPIO_DIRECTION)); /* dummy read for pci-posting */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the gpio mask (0 = writable) */
|
2005-11-17 15:00:18 +01:00
|
|
|
static void snd_vt1724_set_gpio_mask(struct snd_ice1712 *ice, unsigned int data)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
outw(data, ICEREG1724(ice, GPIO_WRITE_MASK));
|
|
|
|
if (! ice->vt1720) /* VT1720 supports only 16 GPIO bits */
|
|
|
|
outb((data >> 16) & 0xff, ICEREG1724(ice, GPIO_WRITE_MASK_22));
|
|
|
|
inw(ICEREG1724(ice, GPIO_WRITE_MASK)); /* dummy read for pci-posting */
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void snd_vt1724_set_gpio_data(struct snd_ice1712 *ice, unsigned int data)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
outw(data, ICEREG1724(ice, GPIO_DATA));
|
|
|
|
if (! ice->vt1720)
|
|
|
|
outb(data >> 16, ICEREG1724(ice, GPIO_DATA_22));
|
|
|
|
inw(ICEREG1724(ice, GPIO_DATA)); /* dummy read for pci-posting */
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static unsigned int snd_vt1724_get_gpio_data(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned int data;
|
|
|
|
if (! ice->vt1720)
|
|
|
|
data = (unsigned int)inb(ICEREG1724(ice, GPIO_DATA_22));
|
|
|
|
else
|
|
|
|
data = 0;
|
|
|
|
data = (data << 16) | inw(ICEREG1724(ice, GPIO_DATA));
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interrupt handler
|
|
|
|
*/
|
|
|
|
|
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 15:55:46 +02:00
|
|
|
static irqreturn_t snd_vt1724_interrupt(int irq, void *dev_id)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = dev_id;
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char status;
|
|
|
|
int handled = 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
status = inb(ICEREG1724(ice, IRQSTAT));
|
|
|
|
if (status == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
handled = 1;
|
2005-11-17 15:00:18 +01:00
|
|
|
/* these should probably be separated at some point,
|
|
|
|
* but as we don't currently have MPU support on the board
|
|
|
|
* I will leave it
|
|
|
|
*/
|
2005-04-17 00:20:36 +02:00
|
|
|
if ((status & VT1724_IRQ_MPU_RX)||(status & VT1724_IRQ_MPU_TX)) {
|
|
|
|
if (ice->rmidi[0])
|
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 15:55:46 +02:00
|
|
|
snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data);
|
2005-04-17 00:20:36 +02:00
|
|
|
outb(status & (VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX), ICEREG1724(ice, IRQSTAT));
|
|
|
|
status &= ~(VT1724_IRQ_MPU_RX|VT1724_IRQ_MPU_TX);
|
|
|
|
}
|
|
|
|
if (status & VT1724_IRQ_MTPCM) {
|
|
|
|
/*
|
|
|
|
* Multi-track PCM
|
|
|
|
* PCM assignment are:
|
|
|
|
* Playback DMA0 (M/C) = playback_pro_substream
|
|
|
|
* Playback DMA1 = playback_con_substream_ds[0]
|
|
|
|
* Playback DMA2 = playback_con_substream_ds[1]
|
|
|
|
* Playback DMA3 = playback_con_substream_ds[2]
|
|
|
|
* Playback DMA4 (SPDIF) = playback_con_substream
|
|
|
|
* Record DMA0 = capture_pro_substream
|
|
|
|
* Record DMA1 = capture_con_substream
|
|
|
|
*/
|
|
|
|
unsigned char mtstat = inb(ICEMT1724(ice, IRQ));
|
|
|
|
if (mtstat & VT1724_MULTI_PDMA0) {
|
|
|
|
if (ice->playback_pro_substream)
|
|
|
|
snd_pcm_period_elapsed(ice->playback_pro_substream);
|
|
|
|
}
|
|
|
|
if (mtstat & VT1724_MULTI_RDMA0) {
|
|
|
|
if (ice->capture_pro_substream)
|
|
|
|
snd_pcm_period_elapsed(ice->capture_pro_substream);
|
|
|
|
}
|
|
|
|
if (mtstat & VT1724_MULTI_PDMA1) {
|
|
|
|
if (ice->playback_con_substream_ds[0])
|
|
|
|
snd_pcm_period_elapsed(ice->playback_con_substream_ds[0]);
|
|
|
|
}
|
|
|
|
if (mtstat & VT1724_MULTI_PDMA2) {
|
|
|
|
if (ice->playback_con_substream_ds[1])
|
|
|
|
snd_pcm_period_elapsed(ice->playback_con_substream_ds[1]);
|
|
|
|
}
|
|
|
|
if (mtstat & VT1724_MULTI_PDMA3) {
|
|
|
|
if (ice->playback_con_substream_ds[2])
|
|
|
|
snd_pcm_period_elapsed(ice->playback_con_substream_ds[2]);
|
|
|
|
}
|
|
|
|
if (mtstat & VT1724_MULTI_PDMA4) {
|
|
|
|
if (ice->playback_con_substream)
|
|
|
|
snd_pcm_period_elapsed(ice->playback_con_substream);
|
|
|
|
}
|
|
|
|
if (mtstat & VT1724_MULTI_RDMA1) {
|
|
|
|
if (ice->capture_con_substream)
|
|
|
|
snd_pcm_period_elapsed(ice->capture_con_substream);
|
|
|
|
}
|
|
|
|
/* ack anyway to avoid freeze */
|
|
|
|
outb(mtstat, ICEMT1724(ice, IRQ));
|
|
|
|
/* ought to really handle this properly */
|
|
|
|
if (mtstat & VT1724_MULTI_FIFO_ERR) {
|
|
|
|
unsigned char fstat = inb(ICEMT1724(ice, DMA_FIFO_ERR));
|
|
|
|
outb(fstat, ICEMT1724(ice, DMA_FIFO_ERR));
|
|
|
|
outb(VT1724_MULTI_FIFO_ERR | inb(ICEMT1724(ice, DMA_INT_MASK)), ICEMT1724(ice, DMA_INT_MASK));
|
|
|
|
/* If I don't do this, I get machine lockup due to continual interrupts */
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return IRQ_RETVAL(handled);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* PCM code - professional part (multitrack)
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned int rates[] = {
|
|
|
|
8000, 9600, 11025, 12000, 16000, 22050, 24000,
|
|
|
|
32000, 44100, 48000, 64000, 88200, 96000,
|
|
|
|
176400, 192000,
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_hw_constraint_list hw_constraints_rates_96 = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.count = ARRAY_SIZE(rates) - 2, /* up to 96000 */
|
|
|
|
.list = rates,
|
|
|
|
.mask = 0,
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_hw_constraint_list hw_constraints_rates_48 = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.count = ARRAY_SIZE(rates) - 5, /* up to 48000 */
|
|
|
|
.list = rates,
|
|
|
|
.mask = 0,
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_hw_constraint_list hw_constraints_rates_192 = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.count = ARRAY_SIZE(rates),
|
|
|
|
.list = rates,
|
|
|
|
.mask = 0,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct vt1724_pcm_reg {
|
|
|
|
unsigned int addr; /* ADDR register offset */
|
|
|
|
unsigned int size; /* SIZE register offset */
|
|
|
|
unsigned int count; /* COUNT register offset */
|
|
|
|
unsigned int start; /* start & pause bit */
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char what;
|
|
|
|
unsigned char old;
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_pcm_substream *s;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
what = 0;
|
2007-02-22 12:52:53 +01:00
|
|
|
snd_pcm_group_for_each_entry(s, substream) {
|
2007-07-30 08:14:31 +02:00
|
|
|
if (snd_pcm_substream_chip(s) == ice) {
|
|
|
|
const struct vt1724_pcm_reg *reg;
|
|
|
|
reg = s->runtime->private_data;
|
|
|
|
what |= reg->start;
|
|
|
|
snd_pcm_trigger_done(s, substream);
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (cmd) {
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
|
|
|
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
|
|
|
spin_lock(&ice->reg_lock);
|
|
|
|
old = inb(ICEMT1724(ice, DMA_PAUSE));
|
|
|
|
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
|
|
|
|
old |= what;
|
|
|
|
else
|
|
|
|
old &= ~what;
|
|
|
|
outb(old, ICEMT1724(ice, DMA_PAUSE));
|
|
|
|
spin_unlock(&ice->reg_lock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SNDRV_PCM_TRIGGER_START:
|
|
|
|
case SNDRV_PCM_TRIGGER_STOP:
|
|
|
|
spin_lock(&ice->reg_lock);
|
|
|
|
old = inb(ICEMT1724(ice, DMA_CONTROL));
|
|
|
|
if (cmd == SNDRV_PCM_TRIGGER_START)
|
|
|
|
old |= what;
|
|
|
|
else
|
|
|
|
old &= ~what;
|
|
|
|
outb(old, ICEMT1724(ice, DMA_CONTROL));
|
|
|
|
spin_unlock(&ice->reg_lock);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DMA_STARTS (VT1724_RDMA0_START|VT1724_PDMA0_START|VT1724_RDMA1_START|\
|
|
|
|
VT1724_PDMA1_START|VT1724_PDMA2_START|VT1724_PDMA3_START|VT1724_PDMA4_START)
|
|
|
|
#define DMA_PAUSES (VT1724_RDMA0_PAUSE|VT1724_PDMA0_PAUSE|VT1724_RDMA1_PAUSE|\
|
|
|
|
VT1724_PDMA1_PAUSE|VT1724_PDMA2_PAUSE|VT1724_PDMA3_PAUSE|VT1724_PDMA4_PAUSE)
|
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
static const unsigned int stdclock_rate_list[16] = {
|
|
|
|
48000, 24000, 12000, 9600, 32000, 16000, 8000, 96000, 44100,
|
|
|
|
22050, 11025, 88200, 176400, 0, 192000, 64000
|
|
|
|
};
|
|
|
|
|
|
|
|
static unsigned int stdclock_get_rate(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2008-03-20 12:10:27 +01:00
|
|
|
unsigned int rate;
|
|
|
|
rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
|
|
|
|
return rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(stdclock_rate_list); i++) {
|
|
|
|
if (stdclock_rate_list[i] == rate) {
|
|
|
|
outb(i, ICEMT1724(ice, RATE));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned char stdclock_set_mclk(struct snd_ice1712 *ice,
|
|
|
|
unsigned int rate)
|
|
|
|
{
|
|
|
|
unsigned char val, old;
|
|
|
|
/* check MT02 */
|
2005-04-17 00:20:36 +02:00
|
|
|
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
|
2008-03-20 12:10:27 +01:00
|
|
|
val = old = inb(ICEMT1724(ice, I2S_FORMAT));
|
|
|
|
if (rate > 96000)
|
|
|
|
val |= VT1724_MT_I2S_MCLK_128X; /* 128x MCLK */
|
2005-04-17 00:20:36 +02:00
|
|
|
else
|
2008-03-20 12:10:27 +01:00
|
|
|
val &= ~VT1724_MT_I2S_MCLK_128X; /* 256x MCLK */
|
|
|
|
if (val != old) {
|
|
|
|
outb(val, ICEMT1724(ice, I2S_FORMAT));
|
|
|
|
/* master clock changed */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* no change in master clock */
|
|
|
|
return 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void snd_vt1724_set_pro_rate(struct snd_ice1712 *ice, unsigned int rate,
|
|
|
|
int force)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
2008-03-20 12:10:27 +01:00
|
|
|
unsigned char mclk_change;
|
|
|
|
unsigned int i, old_rate;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
if (rate > ice->hw_rates->list[ice->hw_rates->count - 1])
|
2005-04-17 00:20:36 +02:00
|
|
|
return;
|
|
|
|
spin_lock_irqsave(&ice->reg_lock, flags);
|
2008-03-20 12:10:27 +01:00
|
|
|
if ((inb(ICEMT1724(ice, DMA_CONTROL)) & DMA_STARTS) ||
|
2005-04-17 00:20:36 +02:00
|
|
|
(inb(ICEMT1724(ice, DMA_PAUSE)) & DMA_PAUSES)) {
|
|
|
|
/* running? we cannot change the rate now... */
|
|
|
|
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!force && is_pro_rate_locked(ice)) {
|
|
|
|
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
old_rate = ice->get_rate(ice);
|
|
|
|
if (force || (old_rate != rate))
|
|
|
|
ice->set_rate(ice, rate);
|
2005-04-17 00:20:36 +02:00
|
|
|
else if (rate == ice->cur_rate) {
|
|
|
|
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ice->cur_rate = rate;
|
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
/* setting master clock */
|
|
|
|
mclk_change = ice->set_mclk(ice, rate);
|
|
|
|
|
2005-04-17 00:20:36 +02:00
|
|
|
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
|
|
|
|
|
|
|
if (mclk_change && ice->gpio.i2s_mclk_changed)
|
|
|
|
ice->gpio.i2s_mclk_changed(ice);
|
|
|
|
if (ice->gpio.set_pro_rate)
|
|
|
|
ice->gpio.set_pro_rate(ice, rate);
|
|
|
|
|
|
|
|
/* set up codecs */
|
|
|
|
for (i = 0; i < ice->akm_codecs; i++) {
|
|
|
|
if (ice->akm[i].ops.set_rate_val)
|
|
|
|
ice->akm[i].ops.set_rate_val(&ice->akm[i], rate);
|
|
|
|
}
|
|
|
|
if (ice->spdif.ops.setup_rate)
|
|
|
|
ice->spdif.ops.setup_rate(ice, rate);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pcm_hw_params(struct snd_pcm_substream *substream,
|
|
|
|
struct snd_pcm_hw_params *hw_params)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
int i, chs;
|
|
|
|
|
|
|
|
chs = params_channels(hw_params);
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_lock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* mark surround channels */
|
|
|
|
if (substream == ice->playback_pro_substream) {
|
|
|
|
/* PDMA0 can be multi-channel up to 8 */
|
|
|
|
chs = chs / 2 - 1;
|
|
|
|
for (i = 0; i < chs; i++) {
|
2005-11-17 15:00:18 +01:00
|
|
|
if (ice->pcm_reserved[i] &&
|
|
|
|
ice->pcm_reserved[i] != substream) {
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
ice->pcm_reserved[i] = substream;
|
|
|
|
}
|
|
|
|
for (; i < 3; i++) {
|
|
|
|
if (ice->pcm_reserved[i] == substream)
|
|
|
|
ice->pcm_reserved[i] = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
|
|
/* check individual playback stream */
|
|
|
|
if (ice->playback_con_substream_ds[i] == substream) {
|
2005-11-17 15:00:18 +01:00
|
|
|
if (ice->pcm_reserved[i] &&
|
|
|
|
ice->pcm_reserved[i] != substream) {
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
ice->pcm_reserved[i] = substream;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
snd_vt1724_set_pro_rate(ice, params_rate(hw_params), 0);
|
|
|
|
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pcm_hw_free(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
int i;
|
|
|
|
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_lock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* unmark surround channels */
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
if (ice->pcm_reserved[i] == substream)
|
|
|
|
ice->pcm_reserved[i] = NULL;
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
return snd_pcm_lib_free_pages(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_pro_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char val;
|
|
|
|
unsigned int size;
|
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
val = (8 - substream->runtime->channels) >> 1;
|
|
|
|
outb(val, ICEMT1724(ice, BURST));
|
|
|
|
|
|
|
|
outl(substream->runtime->dma_addr, ICEMT1724(ice, PLAYBACK_ADDR));
|
|
|
|
|
|
|
|
size = (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1;
|
|
|
|
// outl(size, ICEMT1724(ice, PLAYBACK_SIZE));
|
|
|
|
outw(size, ICEMT1724(ice, PLAYBACK_SIZE));
|
|
|
|
outb(size >> 16, ICEMT1724(ice, PLAYBACK_SIZE) + 2);
|
|
|
|
size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
|
|
|
|
// outl(size, ICEMT1724(ice, PLAYBACK_COUNT));
|
|
|
|
outw(size, ICEMT1724(ice, PLAYBACK_COUNT));
|
|
|
|
outb(size >> 16, ICEMT1724(ice, PLAYBACK_COUNT) + 2);
|
|
|
|
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
|
|
|
|
// printk("pro prepare: ch = %d, addr = 0x%x, buffer = 0x%x, period = 0x%x\n", substream->runtime->channels, (unsigned int)substream->runtime->dma_addr, snd_pcm_lib_buffer_bytes(substream), snd_pcm_lib_period_bytes(substream));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static snd_pcm_uframes_t snd_vt1724_playback_pro_pointer(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
size_t ptr;
|
|
|
|
|
|
|
|
if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & VT1724_PDMA0_START))
|
|
|
|
return 0;
|
|
|
|
#if 0 /* read PLAYBACK_ADDR */
|
|
|
|
ptr = inl(ICEMT1724(ice, PLAYBACK_ADDR));
|
|
|
|
if (ptr < substream->runtime->dma_addr) {
|
|
|
|
snd_printd("ice1724: invalid negative ptr\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
ptr -= substream->runtime->dma_addr;
|
|
|
|
ptr = bytes_to_frames(substream->runtime, ptr);
|
|
|
|
if (ptr >= substream->runtime->buffer_size) {
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_printd("ice1724: invalid ptr %d (size=%d)\n",
|
|
|
|
(int)ptr, (int)substream->runtime->period_size);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else /* read PLAYBACK_SIZE */
|
|
|
|
ptr = inl(ICEMT1724(ice, PLAYBACK_SIZE)) & 0xffffff;
|
|
|
|
ptr = (ptr + 1) << 2;
|
|
|
|
ptr = bytes_to_frames(substream->runtime, ptr);
|
|
|
|
if (! ptr)
|
|
|
|
;
|
|
|
|
else if (ptr <= substream->runtime->buffer_size)
|
|
|
|
ptr = substream->runtime->buffer_size - ptr;
|
|
|
|
else {
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_printd("ice1724: invalid ptr %d (size=%d)\n",
|
|
|
|
(int)ptr, (int)substream->runtime->buffer_size);
|
2005-04-17 00:20:36 +02:00
|
|
|
ptr = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pcm_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2007-01-29 15:26:36 +01:00
|
|
|
const struct vt1724_pcm_reg *reg = substream->runtime->private_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
outl(substream->runtime->dma_addr, ice->profi_port + reg->addr);
|
2005-11-17 15:00:18 +01:00
|
|
|
outw((snd_pcm_lib_buffer_bytes(substream) >> 2) - 1,
|
|
|
|
ice->profi_port + reg->size);
|
|
|
|
outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1,
|
|
|
|
ice->profi_port + reg->count);
|
2005-04-17 00:20:36 +02:00
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static snd_pcm_uframes_t snd_vt1724_pcm_pointer(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2007-01-29 15:26:36 +01:00
|
|
|
const struct vt1724_pcm_reg *reg = substream->runtime->private_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
size_t ptr;
|
|
|
|
|
|
|
|
if (!(inl(ICEMT1724(ice, DMA_CONTROL)) & reg->start))
|
|
|
|
return 0;
|
|
|
|
#if 0 /* use ADDR register */
|
|
|
|
ptr = inl(ice->profi_port + reg->addr);
|
|
|
|
ptr -= substream->runtime->dma_addr;
|
|
|
|
return bytes_to_frames(substream->runtime, ptr);
|
|
|
|
#else /* use SIZE register */
|
|
|
|
ptr = inw(ice->profi_port + reg->size);
|
|
|
|
ptr = (ptr + 1) << 2;
|
|
|
|
ptr = bytes_to_frames(substream->runtime, ptr);
|
|
|
|
if (! ptr)
|
|
|
|
;
|
|
|
|
else if (ptr <= substream->runtime->buffer_size)
|
|
|
|
ptr = substream->runtime->buffer_size - ptr;
|
|
|
|
else {
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_printd("ice1724: invalid ptr %d (size=%d)\n",
|
|
|
|
(int)ptr, (int)substream->runtime->buffer_size);
|
2005-04-17 00:20:36 +02:00
|
|
|
ptr = 0;
|
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct vt1724_pcm_reg vt1724_playback_pro_reg = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.addr = VT1724_MT_PLAYBACK_ADDR,
|
|
|
|
.size = VT1724_MT_PLAYBACK_SIZE,
|
|
|
|
.count = VT1724_MT_PLAYBACK_COUNT,
|
|
|
|
.start = VT1724_PDMA0_START,
|
|
|
|
};
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct vt1724_pcm_reg vt1724_capture_pro_reg = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.addr = VT1724_MT_CAPTURE_ADDR,
|
|
|
|
.size = VT1724_MT_CAPTURE_SIZE,
|
|
|
|
.count = VT1724_MT_CAPTURE_COUNT,
|
|
|
|
.start = VT1724_RDMA0_START,
|
|
|
|
};
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct snd_pcm_hardware snd_vt1724_playback_pro =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
|
|
|
|
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
|
|
|
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
|
|
|
|
.rate_min = 8000,
|
|
|
|
.rate_max = 192000,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 8,
|
|
|
|
.buffer_bytes_max = (1UL << 21), /* 19bits dword */
|
|
|
|
.period_bytes_min = 8 * 4 * 2, /* FIXME: constraints needed */
|
|
|
|
.period_bytes_max = (1UL << 21),
|
|
|
|
.periods_min = 2,
|
|
|
|
.periods_max = 1024,
|
|
|
|
};
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct snd_pcm_hardware snd_vt1724_spdif =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
|
|
|
|
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
2005-10-12 10:04:42 +02:00
|
|
|
.rates = (SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|
|
|
|
|
SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|
|
|
|
|
SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_176400|
|
|
|
|
SNDRV_PCM_RATE_192000),
|
2005-04-17 00:20:36 +02:00
|
|
|
.rate_min = 32000,
|
2005-10-12 10:04:42 +02:00
|
|
|
.rate_max = 192000,
|
2005-04-17 00:20:36 +02:00
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.buffer_bytes_max = (1UL << 18), /* 16bits dword */
|
|
|
|
.period_bytes_min = 2 * 4 * 2,
|
|
|
|
.period_bytes_max = (1UL << 18),
|
|
|
|
.periods_min = 2,
|
|
|
|
.periods_max = 1024,
|
|
|
|
};
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct snd_pcm_hardware snd_vt1724_2ch_stereo =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
|
|
|
|
SNDRV_PCM_INFO_BLOCK_TRANSFER |
|
|
|
|
SNDRV_PCM_INFO_MMAP_VALID |
|
|
|
|
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
|
|
|
|
.formats = SNDRV_PCM_FMTBIT_S32_LE,
|
|
|
|
.rates = SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_192000,
|
|
|
|
.rate_min = 8000,
|
|
|
|
.rate_max = 192000,
|
|
|
|
.channels_min = 2,
|
|
|
|
.channels_max = 2,
|
|
|
|
.buffer_bytes_max = (1UL << 18), /* 16bits dword */
|
|
|
|
.period_bytes_min = 2 * 4 * 2,
|
|
|
|
.period_bytes_max = (1UL << 18),
|
|
|
|
.periods_min = 2,
|
|
|
|
.periods_max = 1024,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* set rate constraints
|
|
|
|
*/
|
2008-03-20 12:10:27 +01:00
|
|
|
static void set_std_hw_rates(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
if (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S) {
|
|
|
|
/* I2S */
|
|
|
|
/* VT1720 doesn't support more than 96kHz */
|
|
|
|
if ((ice->eeprom.data[ICE_EEP2_I2S] & 0x08) && !ice->vt1720)
|
2008-03-20 12:10:27 +01:00
|
|
|
ice->hw_rates = &hw_constraints_rates_192;
|
|
|
|
else
|
|
|
|
ice->hw_rates = &hw_constraints_rates_96;
|
|
|
|
} else {
|
2005-04-17 00:20:36 +02:00
|
|
|
/* ACLINK */
|
2008-03-20 12:10:27 +01:00
|
|
|
ice->hw_rates = &hw_constraints_rates_48;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
2008-03-20 12:10:27 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int set_rate_constraints(struct snd_ice1712 *ice,
|
|
|
|
struct snd_pcm_substream *substream)
|
|
|
|
{
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
|
|
|
|
runtime->hw.rate_min = ice->hw_rates->list[0];
|
|
|
|
runtime->hw.rate_max = ice->hw_rates->list[ice->hw_rates->count - 1];
|
|
|
|
runtime->hw.rates = SNDRV_PCM_RATE_KNOT;
|
|
|
|
return snd_pcm_hw_constraint_list(runtime, 0,
|
|
|
|
SNDRV_PCM_HW_PARAM_RATE,
|
|
|
|
ice->hw_rates);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* multi-channel playback needs alignment 8x32bit regardless of the channels
|
|
|
|
* actually used
|
|
|
|
*/
|
|
|
|
#define VT1724_BUFFER_ALIGN 0x20
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_pro_open(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
int chs;
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
runtime->private_data = (void *)&vt1724_playback_pro_reg;
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->playback_pro_substream = substream;
|
|
|
|
runtime->hw = snd_vt1724_playback_pro;
|
|
|
|
snd_pcm_set_sync(substream);
|
|
|
|
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
|
|
|
set_rate_constraints(ice, substream);
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_lock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* calculate the currently available channels */
|
|
|
|
for (chs = 0; chs < 3; chs++) {
|
|
|
|
if (ice->pcm_reserved[chs])
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
chs = (chs + 1) * 2;
|
|
|
|
runtime->hw.channels_max = chs;
|
|
|
|
if (chs > 2) /* channels must be even */
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_capture_pro_open(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
runtime->private_data = (void *)&vt1724_capture_pro_reg;
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->capture_pro_substream = substream;
|
|
|
|
runtime->hw = snd_vt1724_2ch_stereo;
|
|
|
|
snd_pcm_set_sync(substream);
|
|
|
|
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
|
|
|
set_rate_constraints(ice, substream);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_pro_close(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (PRO_RATE_RESET)
|
2008-03-20 12:10:27 +01:00
|
|
|
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->playback_pro_substream = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (PRO_RATE_RESET)
|
2008-03-20 12:10:27 +01:00
|
|
|
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->capture_pro_substream = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.open = snd_vt1724_playback_pro_open,
|
|
|
|
.close = snd_vt1724_playback_pro_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_vt1724_pcm_hw_params,
|
|
|
|
.hw_free = snd_vt1724_pcm_hw_free,
|
|
|
|
.prepare = snd_vt1724_playback_pro_prepare,
|
|
|
|
.trigger = snd_vt1724_pcm_trigger,
|
|
|
|
.pointer = snd_vt1724_playback_pro_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.open = snd_vt1724_capture_pro_open,
|
|
|
|
.close = snd_vt1724_capture_pro_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_vt1724_pcm_hw_params,
|
|
|
|
.hw_free = snd_vt1724_pcm_hw_free,
|
|
|
|
.prepare = snd_vt1724_pcm_prepare,
|
|
|
|
.trigger = snd_vt1724_pcm_trigger,
|
|
|
|
.pointer = snd_vt1724_pcm_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_pcm_profi(struct snd_ice1712 * ice, int device)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 00:20:36 +02:00
|
|
|
int err;
|
|
|
|
|
|
|
|
err = snd_pcm_new(ice->card, "ICE1724", device, 1, 1, &pcm);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_vt1724_playback_pro_ops);
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_vt1724_capture_pro_ops);
|
|
|
|
|
|
|
|
pcm->private_data = ice;
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
strcpy(pcm->name, "ICE1724");
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_dma_pci_data(ice->pci),
|
|
|
|
256*1024, 256*1024);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
ice->pcm_pro = pcm;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SPDIF PCM
|
|
|
|
*/
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct vt1724_pcm_reg vt1724_playback_spdif_reg = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.addr = VT1724_MT_PDMA4_ADDR,
|
|
|
|
.size = VT1724_MT_PDMA4_SIZE,
|
|
|
|
.count = VT1724_MT_PDMA4_COUNT,
|
|
|
|
.start = VT1724_PDMA4_START,
|
|
|
|
};
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct vt1724_pcm_reg vt1724_capture_spdif_reg = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.addr = VT1724_MT_RDMA1_ADDR,
|
|
|
|
.size = VT1724_MT_RDMA1_SIZE,
|
|
|
|
.count = VT1724_MT_RDMA1_COUNT,
|
|
|
|
.start = VT1724_RDMA1_START,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* update spdif control bits; call with reg_lock */
|
2005-11-17 15:00:18 +01:00
|
|
|
static void update_spdif_bits(struct snd_ice1712 *ice, unsigned int val)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned char cbit, disabled;
|
|
|
|
|
|
|
|
cbit = inb(ICEREG1724(ice, SPDIF_CFG));
|
|
|
|
disabled = cbit & ~VT1724_CFG_SPDIF_OUT_EN;
|
|
|
|
if (cbit != disabled)
|
|
|
|
outb(disabled, ICEREG1724(ice, SPDIF_CFG));
|
|
|
|
outw(val, ICEMT1724(ice, SPDIF_CTRL));
|
|
|
|
if (cbit != disabled)
|
|
|
|
outb(cbit, ICEREG1724(ice, SPDIF_CFG));
|
|
|
|
outw(val, ICEMT1724(ice, SPDIF_CTRL));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* update SPDIF control bits according to the given rate */
|
2005-11-17 15:00:18 +01:00
|
|
|
static void update_spdif_rate(struct snd_ice1712 *ice, unsigned int rate)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned int val, nval;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&ice->reg_lock, flags);
|
|
|
|
nval = val = inw(ICEMT1724(ice, SPDIF_CTRL));
|
|
|
|
nval &= ~(7 << 12);
|
|
|
|
switch (rate) {
|
|
|
|
case 44100: break;
|
|
|
|
case 48000: nval |= 2 << 12; break;
|
|
|
|
case 32000: nval |= 3 << 12; break;
|
2005-10-12 10:04:42 +02:00
|
|
|
case 88200: nval |= 4 << 12; break;
|
|
|
|
case 96000: nval |= 5 << 12; break;
|
|
|
|
case 192000: nval |= 6 << 12; break;
|
|
|
|
case 176400: nval |= 7 << 12; break;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
if (val != nval)
|
|
|
|
update_spdif_bits(ice, nval);
|
|
|
|
spin_unlock_irqrestore(&ice->reg_lock, flags);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_spdif_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (! ice->force_pdma4)
|
|
|
|
update_spdif_rate(ice, substream->runtime->rate);
|
|
|
|
return snd_vt1724_pcm_prepare(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_spdif_open(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
runtime->private_data = (void *)&vt1724_playback_spdif_reg;
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->playback_con_substream = substream;
|
|
|
|
if (ice->force_pdma4) {
|
|
|
|
runtime->hw = snd_vt1724_2ch_stereo;
|
|
|
|
set_rate_constraints(ice, substream);
|
|
|
|
} else
|
|
|
|
runtime->hw = snd_vt1724_spdif;
|
|
|
|
snd_pcm_set_sync(substream);
|
|
|
|
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
2008-03-14 17:17:09 +01:00
|
|
|
if (ice->spdif.ops.open)
|
|
|
|
ice->spdif.ops.open(ice, substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_spdif_close(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (PRO_RATE_RESET)
|
2008-03-20 12:10:27 +01:00
|
|
|
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->playback_con_substream = NULL;
|
2008-03-14 17:17:09 +01:00
|
|
|
if (ice->spdif.ops.close)
|
|
|
|
ice->spdif.ops.close(ice, substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_capture_spdif_open(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
runtime->private_data = (void *)&vt1724_capture_spdif_reg;
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->capture_con_substream = substream;
|
|
|
|
if (ice->force_rdma1) {
|
|
|
|
runtime->hw = snd_vt1724_2ch_stereo;
|
|
|
|
set_rate_constraints(ice, substream);
|
|
|
|
} else
|
|
|
|
runtime->hw = snd_vt1724_spdif;
|
|
|
|
snd_pcm_set_sync(substream);
|
|
|
|
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
|
|
|
snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
|
|
|
|
VT1724_BUFFER_ALIGN);
|
2008-03-14 17:17:09 +01:00
|
|
|
if (ice->spdif.ops.open)
|
|
|
|
ice->spdif.ops.open(ice, substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (PRO_RATE_RESET)
|
2008-03-20 12:10:27 +01:00
|
|
|
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->capture_con_substream = NULL;
|
2008-03-14 17:17:09 +01:00
|
|
|
if (ice->spdif.ops.close)
|
|
|
|
ice->spdif.ops.close(ice, substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.open = snd_vt1724_playback_spdif_open,
|
|
|
|
.close = snd_vt1724_playback_spdif_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_vt1724_pcm_hw_params,
|
|
|
|
.hw_free = snd_vt1724_pcm_hw_free,
|
|
|
|
.prepare = snd_vt1724_playback_spdif_prepare,
|
|
|
|
.trigger = snd_vt1724_pcm_trigger,
|
|
|
|
.pointer = snd_vt1724_pcm_pointer,
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.open = snd_vt1724_capture_spdif_open,
|
|
|
|
.close = snd_vt1724_capture_spdif_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_vt1724_pcm_hw_params,
|
|
|
|
.hw_free = snd_vt1724_pcm_hw_free,
|
|
|
|
.prepare = snd_vt1724_pcm_prepare,
|
|
|
|
.trigger = snd_vt1724_pcm_trigger,
|
|
|
|
.pointer = snd_vt1724_pcm_pointer,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_pcm_spdif(struct snd_ice1712 * ice, int device)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
char *name;
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 00:20:36 +02:00
|
|
|
int play, capt;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (ice->force_pdma4 ||
|
|
|
|
(ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_OUT_INT)) {
|
|
|
|
play = 1;
|
|
|
|
ice->has_spdif = 1;
|
|
|
|
} else
|
|
|
|
play = 0;
|
|
|
|
if (ice->force_rdma1 ||
|
|
|
|
(ice->eeprom.data[ICE_EEP2_SPDIF] & VT1724_CFG_SPDIF_IN)) {
|
|
|
|
capt = 1;
|
|
|
|
ice->has_spdif = 1;
|
|
|
|
} else
|
|
|
|
capt = 0;
|
|
|
|
if (! play && ! capt)
|
|
|
|
return 0; /* no spdif device */
|
|
|
|
|
|
|
|
if (ice->force_pdma4 || ice->force_rdma1)
|
|
|
|
name = "ICE1724 Secondary";
|
|
|
|
else
|
|
|
|
name = "IEC1724 IEC958";
|
|
|
|
err = snd_pcm_new(ice->card, name, device, play, capt, &pcm);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (play)
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
|
|
|
&snd_vt1724_playback_spdif_ops);
|
|
|
|
if (capt)
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
|
|
|
|
&snd_vt1724_capture_spdif_ops);
|
|
|
|
|
|
|
|
pcm->private_data = ice;
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
strcpy(pcm->name, name);
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_dma_pci_data(ice->pci),
|
|
|
|
64*1024, 64*1024);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
ice->pcm = pcm;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* independent surround PCMs
|
|
|
|
*/
|
|
|
|
|
2007-01-29 15:26:36 +01:00
|
|
|
static const struct vt1724_pcm_reg vt1724_playback_dma_regs[3] = {
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.addr = VT1724_MT_PDMA1_ADDR,
|
|
|
|
.size = VT1724_MT_PDMA1_SIZE,
|
|
|
|
.count = VT1724_MT_PDMA1_COUNT,
|
|
|
|
.start = VT1724_PDMA1_START,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.addr = VT1724_MT_PDMA2_ADDR,
|
|
|
|
.size = VT1724_MT_PDMA2_SIZE,
|
|
|
|
.count = VT1724_MT_PDMA2_COUNT,
|
|
|
|
.start = VT1724_PDMA2_START,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.addr = VT1724_MT_PDMA3_ADDR,
|
|
|
|
.size = VT1724_MT_PDMA3_SIZE,
|
|
|
|
.count = VT1724_MT_PDMA3_COUNT,
|
|
|
|
.start = VT1724_PDMA3_START,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_indep_prepare(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char val;
|
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
val = 3 - substream->number;
|
|
|
|
if (inb(ICEMT1724(ice, BURST)) < val)
|
|
|
|
outb(val, ICEMT1724(ice, BURST));
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return snd_vt1724_pcm_prepare(substream);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_indep_open(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
|
|
|
struct snd_pcm_runtime *runtime = substream->runtime;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_lock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
/* already used by PDMA0? */
|
|
|
|
if (ice->pcm_reserved[substream->number]) {
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EBUSY; /* FIXME: should handle blocking mode properly */
|
|
|
|
}
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->open_mutex);
|
2007-01-29 15:26:36 +01:00
|
|
|
runtime->private_data = (void *)&vt1724_playback_dma_regs[substream->number];
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->playback_con_substream_ds[substream->number] = substream;
|
|
|
|
runtime->hw = snd_vt1724_2ch_stereo;
|
|
|
|
snd_pcm_set_sync(substream);
|
|
|
|
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
|
|
|
set_rate_constraints(ice, substream);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_pcm_substream_chip(substream);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (PRO_RATE_RESET)
|
2008-03-20 12:10:27 +01:00
|
|
|
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->playback_con_substream_ds[substream->number] = NULL;
|
|
|
|
ice->pcm_reserved[substream->number] = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.open = snd_vt1724_playback_indep_open,
|
|
|
|
.close = snd_vt1724_playback_indep_close,
|
|
|
|
.ioctl = snd_pcm_lib_ioctl,
|
|
|
|
.hw_params = snd_vt1724_pcm_hw_params,
|
|
|
|
.hw_free = snd_vt1724_pcm_hw_free,
|
|
|
|
.prepare = snd_vt1724_playback_indep_prepare,
|
|
|
|
.trigger = snd_vt1724_pcm_trigger,
|
|
|
|
.pointer = snd_vt1724_pcm_pointer,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_pcm_indep(struct snd_ice1712 * ice, int device)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_pcm *pcm;
|
2005-04-17 00:20:36 +02:00
|
|
|
int play;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
play = ice->num_total_dacs / 2 - 1;
|
|
|
|
if (play <= 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err = snd_pcm_new(ice->card, "ICE1724 Surrounds", device, play, 0, &pcm);
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
|
|
|
|
&snd_vt1724_playback_indep_ops);
|
|
|
|
|
|
|
|
pcm->private_data = ice;
|
|
|
|
pcm->info_flags = 0;
|
|
|
|
strcpy(pcm->name, "ICE1724 Surround PCM");
|
|
|
|
|
|
|
|
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_dma_pci_data(ice->pci),
|
|
|
|
64*1024, 64*1024);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
ice->pcm_ds = pcm;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mixer section
|
|
|
|
*/
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_ac97_mixer(struct snd_ice1712 * ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (! (ice->eeprom.data[ICE_EEP2_ACLINK] & VT1724_CFG_PRO_I2S)) {
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ac97_bus *pbus;
|
|
|
|
struct snd_ac97_template ac97;
|
|
|
|
static struct snd_ac97_bus_ops ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.write = snd_vt1724_ac97_write,
|
|
|
|
.read = snd_vt1724_ac97_read,
|
|
|
|
};
|
|
|
|
|
|
|
|
/* cold reset */
|
|
|
|
outb(inb(ICEMT1724(ice, AC97_CMD)) | 0x80, ICEMT1724(ice, AC97_CMD));
|
|
|
|
mdelay(5); /* FIXME */
|
|
|
|
outb(inb(ICEMT1724(ice, AC97_CMD)) & ~0x80, ICEMT1724(ice, AC97_CMD));
|
|
|
|
|
|
|
|
if ((err = snd_ac97_bus(ice->card, 0, &ops, NULL, &pbus)) < 0)
|
|
|
|
return err;
|
|
|
|
memset(&ac97, 0, sizeof(ac97));
|
|
|
|
ac97.private_data = ice;
|
|
|
|
if ((err = snd_ac97_mixer(pbus, &ac97, &ice->ac97)) < 0)
|
|
|
|
printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* I2S mixer only */
|
|
|
|
strcat(ice->card->mixername, "ICE1724 - multitrack");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static inline unsigned int eeprom_triple(struct snd_ice1712 *ice, int idx)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
return (unsigned int)ice->eeprom.data[idx] | \
|
|
|
|
((unsigned int)ice->eeprom.data[idx + 1] << 8) | \
|
|
|
|
((unsigned int)ice->eeprom.data[idx + 2] << 16);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void snd_vt1724_proc_read(struct snd_info_entry *entry,
|
|
|
|
struct snd_info_buffer *buffer)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = entry->private_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned int idx;
|
|
|
|
|
|
|
|
snd_iprintf(buffer, "%s\n\n", ice->card->longname);
|
|
|
|
snd_iprintf(buffer, "EEPROM:\n");
|
|
|
|
|
|
|
|
snd_iprintf(buffer, " Subvendor : 0x%x\n", ice->eeprom.subvendor);
|
|
|
|
snd_iprintf(buffer, " Size : %i bytes\n", ice->eeprom.size);
|
|
|
|
snd_iprintf(buffer, " Version : %i\n", ice->eeprom.version);
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_iprintf(buffer, " System Config : 0x%x\n",
|
|
|
|
ice->eeprom.data[ICE_EEP2_SYSCONF]);
|
|
|
|
snd_iprintf(buffer, " ACLink : 0x%x\n",
|
|
|
|
ice->eeprom.data[ICE_EEP2_ACLINK]);
|
|
|
|
snd_iprintf(buffer, " I2S : 0x%x\n",
|
|
|
|
ice->eeprom.data[ICE_EEP2_I2S]);
|
|
|
|
snd_iprintf(buffer, " S/PDIF : 0x%x\n",
|
|
|
|
ice->eeprom.data[ICE_EEP2_SPDIF]);
|
|
|
|
snd_iprintf(buffer, " GPIO direction : 0x%x\n",
|
|
|
|
ice->eeprom.gpiodir);
|
|
|
|
snd_iprintf(buffer, " GPIO mask : 0x%x\n",
|
|
|
|
ice->eeprom.gpiomask);
|
|
|
|
snd_iprintf(buffer, " GPIO state : 0x%x\n",
|
|
|
|
ice->eeprom.gpiostate);
|
2005-04-17 00:20:36 +02:00
|
|
|
for (idx = 0x12; idx < ice->eeprom.size; idx++)
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_iprintf(buffer, " Extra #%02i : 0x%x\n",
|
|
|
|
idx, ice->eeprom.data[idx]);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
snd_iprintf(buffer, "\nRegisters:\n");
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_iprintf(buffer, " PSDOUT03 : 0x%08x\n",
|
|
|
|
(unsigned)inl(ICEMT1724(ice, ROUTE_PLAYBACK)));
|
2005-04-17 00:20:36 +02:00
|
|
|
for (idx = 0x0; idx < 0x20 ; idx++)
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_iprintf(buffer, " CCS%02x : 0x%02x\n",
|
|
|
|
idx, inb(ice->port+idx));
|
2005-04-17 00:20:36 +02:00
|
|
|
for (idx = 0x0; idx < 0x30 ; idx++)
|
2005-11-17 15:00:18 +01:00
|
|
|
snd_iprintf(buffer, " MT%02x : 0x%02x\n",
|
|
|
|
idx, inb(ice->profi_port+idx));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void __devinit snd_vt1724_proc_init(struct snd_ice1712 * ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_info_entry *entry;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (! snd_card_proc_new(ice->card, "ice1724", &entry))
|
2006-04-28 15:13:41 +02:00
|
|
|
snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_eeprom_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
2005-11-17 15:00:18 +01:00
|
|
|
uinfo->count = sizeof(struct snd_ice1712_eeprom);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
memcpy(ucontrol->value.bytes.data, &ice->eeprom, sizeof(ice->eeprom));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_eeprom __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
|
|
|
|
.name = "ICE1724 EEPROM",
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ,
|
|
|
|
.info = snd_vt1724_eeprom_info,
|
|
|
|
.get = snd_vt1724_eeprom_get
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
|
|
|
|
uinfo->count = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static unsigned int encode_spdif_bits(struct snd_aes_iec958 *diga)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-10-12 10:04:42 +02:00
|
|
|
unsigned int val, rbits;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
val = diga->status[0] & 0x03; /* professional, non-audio */
|
|
|
|
if (val & 0x01) {
|
|
|
|
/* professional */
|
2005-11-17 15:00:18 +01:00
|
|
|
if ((diga->status[0] & IEC958_AES0_PRO_EMPHASIS) ==
|
|
|
|
IEC958_AES0_PRO_EMPHASIS_5015)
|
2005-04-17 00:20:36 +02:00
|
|
|
val |= 1U << 3;
|
2005-10-12 10:04:42 +02:00
|
|
|
rbits = (diga->status[4] >> 3) & 0x0f;
|
|
|
|
if (rbits) {
|
|
|
|
switch (rbits) {
|
|
|
|
case 2: val |= 5 << 12; break; /* 96k */
|
|
|
|
case 3: val |= 6 << 12; break; /* 192k */
|
|
|
|
case 10: val |= 4 << 12; break; /* 88.2k */
|
|
|
|
case 11: val |= 7 << 12; break; /* 176.4k */
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
switch (diga->status[0] & IEC958_AES0_PRO_FS) {
|
|
|
|
case IEC958_AES0_PRO_FS_44100:
|
|
|
|
break;
|
|
|
|
case IEC958_AES0_PRO_FS_32000:
|
|
|
|
val |= 3U << 12;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
val |= 2U << 12;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* consumer */
|
|
|
|
val |= diga->status[1] & 0x04; /* copyright */
|
2005-11-17 15:00:18 +01:00
|
|
|
if ((diga->status[0] & IEC958_AES0_CON_EMPHASIS) ==
|
|
|
|
IEC958_AES0_CON_EMPHASIS_5015)
|
2005-04-17 00:20:36 +02:00
|
|
|
val |= 1U << 3;
|
|
|
|
val |= (unsigned int)(diga->status[1] & 0x3f) << 4; /* category */
|
|
|
|
val |= (unsigned int)(diga->status[3] & IEC958_AES3_CON_FS) << 12; /* fs */
|
|
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void decode_spdif_bits(struct snd_aes_iec958 *diga, unsigned int val)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
memset(diga->status, 0, sizeof(diga->status));
|
|
|
|
diga->status[0] = val & 0x03; /* professional, non-audio */
|
|
|
|
if (val & 0x01) {
|
|
|
|
/* professional */
|
|
|
|
if (val & (1U << 3))
|
|
|
|
diga->status[0] |= IEC958_AES0_PRO_EMPHASIS_5015;
|
|
|
|
switch ((val >> 12) & 0x7) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
diga->status[0] |= IEC958_AES0_PRO_FS_32000;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
diga->status[0] |= IEC958_AES0_PRO_FS_48000;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* consumer */
|
|
|
|
diga->status[0] |= val & (1U << 2); /* copyright */
|
|
|
|
if (val & (1U << 3))
|
|
|
|
diga->status[0] |= IEC958_AES0_CON_EMPHASIS_5015;
|
|
|
|
diga->status[1] |= (val >> 4) & 0x3f; /* category */
|
|
|
|
diga->status[3] |= (val >> 12) & 0x07; /* fs */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_default_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned int val;
|
|
|
|
val = inw(ICEMT1724(ice, SPDIF_CTRL));
|
|
|
|
decode_spdif_bits(&ucontrol->value.iec958, val);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned int val, old;
|
|
|
|
|
|
|
|
val = encode_spdif_bits(&ucontrol->value.iec958);
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
old = inw(ICEMT1724(ice, SPDIF_CTRL));
|
|
|
|
if (val != old)
|
|
|
|
update_spdif_bits(ice, val);
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return (val != old);
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_spdif_default __devinitdata =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
|
|
|
|
.info = snd_vt1724_spdif_info,
|
|
|
|
.get = snd_vt1724_spdif_default_get,
|
|
|
|
.put = snd_vt1724_spdif_default_put
|
|
|
|
};
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_maskc_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
|
|
|
|
IEC958_AES0_PROFESSIONAL |
|
|
|
|
IEC958_AES0_CON_NOT_COPYRIGHT |
|
|
|
|
IEC958_AES0_CON_EMPHASIS;
|
|
|
|
ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
|
|
|
|
IEC958_AES1_CON_CATEGORY;
|
|
|
|
ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
|
|
|
|
IEC958_AES0_PROFESSIONAL |
|
|
|
|
IEC958_AES0_PRO_FS |
|
|
|
|
IEC958_AES0_PRO_EMPHASIS;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_spdif_maskc __devinitdata =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.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 15:32:58 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
2005-04-17 00:20:36 +02:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
|
|
|
|
.info = snd_vt1724_spdif_info,
|
|
|
|
.get = snd_vt1724_spdif_maskc_get,
|
|
|
|
};
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_spdif_maskp __devinitdata =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.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 15:32:58 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
2005-04-17 00:20:36 +02:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
|
|
|
|
.info = snd_vt1724_spdif_info,
|
|
|
|
.get = snd_vt1724_spdif_maskp_get,
|
|
|
|
};
|
|
|
|
|
2007-07-23 15:42:26 +02:00
|
|
|
#define snd_vt1724_spdif_sw_info snd_ctl_boolean_mono_info
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_sw_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
|
|
|
ucontrol->value.integer.value[0] = inb(ICEREG1724(ice, SPDIF_CFG)) &
|
|
|
|
VT1724_CFG_SPDIF_OUT_EN ? 1 : 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
unsigned char old, val;
|
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
old = val = inb(ICEREG1724(ice, SPDIF_CFG));
|
|
|
|
val &= ~VT1724_CFG_SPDIF_OUT_EN;
|
|
|
|
if (ucontrol->value.integer.value[0])
|
|
|
|
val |= VT1724_CFG_SPDIF_OUT_EN;
|
|
|
|
if (old != val)
|
|
|
|
outb(val, ICEREG1724(ice, SPDIF_CFG));
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return old != val;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_spdif_switch __devinitdata =
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
/* FIXME: the following conflict with IEC958 Playback Route */
|
|
|
|
// .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
|
2005-08-03 13:40:08 +02:00
|
|
|
.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
|
2005-04-17 00:20:36 +02:00
|
|
|
.info = snd_vt1724_spdif_sw_info,
|
|
|
|
.get = snd_vt1724_spdif_sw_get,
|
|
|
|
.put = snd_vt1724_spdif_sw_put
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#if 0 /* NOT USED YET */
|
|
|
|
/*
|
|
|
|
* GPIO access from extern
|
|
|
|
*/
|
|
|
|
|
2007-07-23 15:42:26 +02:00
|
|
|
#define snd_vt1724_gpio_info snd_ctl_boolean_mono_info
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
int snd_vt1724_gpio_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int shift = kcontrol->private_value & 0xff;
|
|
|
|
int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
|
|
|
|
|
|
|
|
snd_ice1712_save_gpio_status(ice);
|
2005-11-17 15:00:18 +01:00
|
|
|
ucontrol->value.integer.value[0] =
|
|
|
|
(snd_ice1712_gpio_read(ice) & (1 << shift) ? 1 : 0) ^ invert;
|
2005-04-17 00:20:36 +02:00
|
|
|
snd_ice1712_restore_gpio_status(ice);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
int snd_ice1712_gpio_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int shift = kcontrol->private_value & 0xff;
|
|
|
|
int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
|
|
|
|
unsigned int val, nval;
|
|
|
|
|
|
|
|
if (kcontrol->private_value & (1 << 31))
|
|
|
|
return -EPERM;
|
|
|
|
nval = (ucontrol->value.integer.value[0] ? (1 << shift) : 0) ^ invert;
|
|
|
|
snd_ice1712_save_gpio_status(ice);
|
|
|
|
val = snd_ice1712_gpio_read(ice);
|
|
|
|
nval |= val & ~(1 << shift);
|
|
|
|
if (val != nval)
|
|
|
|
snd_ice1712_gpio_write(ice, nval);
|
|
|
|
snd_ice1712_restore_gpio_status(ice);
|
|
|
|
return val != nval;
|
|
|
|
}
|
|
|
|
#endif /* NOT USED YET */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* rate
|
|
|
|
*/
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_internal_clock_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
2008-03-20 12:10:27 +01:00
|
|
|
uinfo->value.enumerated.items = ice->hw_rates->count + 1;
|
2005-04-17 00:20:36 +02:00
|
|
|
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
|
|
|
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
2008-03-20 12:10:27 +01:00
|
|
|
if (uinfo->value.enumerated.item == uinfo->value.enumerated.items - 1)
|
|
|
|
strcpy(uinfo->value.enumerated.name, "IEC958 Input");
|
|
|
|
else
|
|
|
|
sprintf(uinfo->value.enumerated.name, "%d",
|
|
|
|
ice->hw_rates->list[uinfo->value.enumerated.item]);
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_internal_clock_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2008-03-20 12:10:27 +01:00
|
|
|
unsigned int i, rate;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
2008-03-20 12:10:27 +01:00
|
|
|
if (ice->is_spdif_master(ice)) {
|
|
|
|
ucontrol->value.enumerated.item[0] = ice->hw_rates->count;
|
2005-04-17 00:20:36 +02:00
|
|
|
} else {
|
2008-03-20 12:10:27 +01:00
|
|
|
rate = ice->get_rate(ice);
|
|
|
|
ucontrol->value.enumerated.item[0] = 0;
|
|
|
|
for (i = 0; i < ice->hw_rates->count; i++) {
|
|
|
|
if (ice->hw_rates->list[i] == rate) {
|
|
|
|
ucontrol->value.enumerated.item[0] = i;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
/* setting clock to external - SPDIF */
|
|
|
|
static void stdclock_set_spdif_clock(struct snd_ice1712 *ice)
|
|
|
|
{
|
|
|
|
unsigned char oval;
|
|
|
|
unsigned char i2s_oval;
|
|
|
|
oval = inb(ICEMT1724(ice, RATE));
|
|
|
|
outb(oval | VT1724_SPDIF_MASTER, ICEMT1724(ice, RATE));
|
|
|
|
/* setting 256fs */
|
|
|
|
i2s_oval = inb(ICEMT1724(ice, I2S_FORMAT));
|
|
|
|
outb(i2s_oval & ~VT1724_MT_I2S_MCLK_128X, ICEMT1724(ice, I2S_FORMAT));
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2008-03-20 12:10:27 +01:00
|
|
|
unsigned int old_rate, new_rate;
|
|
|
|
unsigned int item = ucontrol->value.enumerated.item[0];
|
|
|
|
unsigned int spdif = ice->hw_rates->count;
|
|
|
|
|
|
|
|
if (item > spdif)
|
|
|
|
return -EINVAL;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
2008-03-20 12:10:27 +01:00
|
|
|
if (ice->is_spdif_master(ice))
|
|
|
|
old_rate = 0;
|
|
|
|
else
|
|
|
|
old_rate = ice->get_rate(ice);
|
|
|
|
if (item == spdif) {
|
|
|
|
/* switching to external clock via SPDIF */
|
|
|
|
ice->set_spdif_clock(ice);
|
|
|
|
new_rate = 0;
|
2005-04-17 00:20:36 +02:00
|
|
|
} else {
|
2008-03-20 12:10:27 +01:00
|
|
|
/* internal on-card clock */
|
|
|
|
new_rate = ice->hw_rates->list[item];
|
|
|
|
ice->pro_rate_default = new_rate;
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
snd_vt1724_set_pro_rate(ice, ice->pro_rate_default, 1);
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
|
2008-03-20 12:10:27 +01:00
|
|
|
/* the first reset to the SPDIF master mode? */
|
|
|
|
if (old_rate != new_rate && !new_rate) {
|
2005-04-17 00:20:36 +02:00
|
|
|
/* notify akm chips as well */
|
2008-03-20 12:10:27 +01:00
|
|
|
unsigned int i;
|
|
|
|
if (ice->gpio.set_pro_rate)
|
|
|
|
ice->gpio.set_pro_rate(ice, 0);
|
|
|
|
for (i = 0; i < ice->akm_codecs; i++) {
|
|
|
|
if (ice->akm[i].ops.set_rate_val)
|
|
|
|
ice->akm[i].ops.set_rate_val(&ice->akm[i], 0);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
}
|
2008-03-20 12:10:27 +01:00
|
|
|
return old_rate != new_rate;
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_pro_internal_clock __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Multi Track Internal Clock",
|
|
|
|
.info = snd_vt1724_pro_internal_clock_info,
|
|
|
|
.get = snd_vt1724_pro_internal_clock_get,
|
|
|
|
.put = snd_vt1724_pro_internal_clock_put
|
|
|
|
};
|
|
|
|
|
2007-07-23 15:42:26 +02:00
|
|
|
#define snd_vt1724_pro_rate_locking_info snd_ctl_boolean_mono_info
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_rate_locking_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
ucontrol->value.integer.value[0] = PRO_RATE_LOCKED;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int change = 0, nval;
|
|
|
|
|
|
|
|
nval = ucontrol->value.integer.value[0] ? 1 : 0;
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
change = PRO_RATE_LOCKED != nval;
|
|
|
|
PRO_RATE_LOCKED = nval;
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_pro_rate_locking __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Multi Track Rate Locking",
|
|
|
|
.info = snd_vt1724_pro_rate_locking_info,
|
|
|
|
.get = snd_vt1724_pro_rate_locking_get,
|
|
|
|
.put = snd_vt1724_pro_rate_locking_put
|
|
|
|
};
|
|
|
|
|
2007-07-23 15:42:26 +02:00
|
|
|
#define snd_vt1724_pro_rate_reset_info snd_ctl_boolean_mono_info
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_rate_reset_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
ucontrol->value.integer.value[0] = PRO_RATE_RESET ? 1 : 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int change = 0, nval;
|
|
|
|
|
|
|
|
nval = ucontrol->value.integer.value[0] ? 1 : 0;
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
change = PRO_RATE_RESET != nval;
|
|
|
|
PRO_RATE_RESET = nval;
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_pro_rate_reset __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Multi Track Rate Reset",
|
|
|
|
.info = snd_vt1724_pro_rate_reset_info,
|
|
|
|
.get = snd_vt1724_pro_rate_reset_get,
|
|
|
|
.put = snd_vt1724_pro_rate_reset_put
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* routing
|
|
|
|
*/
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_route_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
static char *texts[] = {
|
|
|
|
"PCM Out", /* 0 */
|
|
|
|
"H/W In 0", "H/W In 1", /* 1-2 */
|
|
|
|
"IEC958 In L", "IEC958 In R", /* 3-4 */
|
|
|
|
};
|
|
|
|
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
|
|
|
|
uinfo->count = 1;
|
|
|
|
uinfo->value.enumerated.items = 5;
|
|
|
|
if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
|
|
|
|
uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
|
|
|
|
strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int analog_route_shift(int idx)
|
|
|
|
{
|
|
|
|
return (idx % 2) * 12 + ((idx / 2) * 3) + 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int digital_route_shift(int idx)
|
|
|
|
{
|
|
|
|
return idx * 3;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int get_route_val(struct snd_ice1712 *ice, int shift)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned long val;
|
|
|
|
unsigned char eitem;
|
2007-01-29 15:26:36 +01:00
|
|
|
static const unsigned char xlate[8] = {
|
2005-04-17 00:20:36 +02:00
|
|
|
0, 255, 1, 2, 255, 255, 3, 4,
|
|
|
|
};
|
|
|
|
|
|
|
|
val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
|
|
|
|
val >>= shift;
|
|
|
|
val &= 7; //we now have 3 bits per output
|
|
|
|
eitem = xlate[val];
|
|
|
|
if (eitem == 255) {
|
|
|
|
snd_BUG();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return eitem;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int put_route_val(struct snd_ice1712 *ice, unsigned int val, int shift)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned int old_val, nval;
|
|
|
|
int change;
|
2007-01-29 15:26:36 +01:00
|
|
|
static const unsigned char xroute[8] = {
|
2005-04-17 00:20:36 +02:00
|
|
|
0, /* PCM */
|
|
|
|
2, /* PSDIN0 Left */
|
|
|
|
3, /* PSDIN0 Right */
|
|
|
|
6, /* SPDIN Left */
|
|
|
|
7, /* SPDIN Right */
|
|
|
|
};
|
|
|
|
|
|
|
|
nval = xroute[val % 5];
|
|
|
|
val = old_val = inl(ICEMT1724(ice, ROUTE_PLAYBACK));
|
|
|
|
val &= ~(0x07 << shift);
|
|
|
|
val |= nval << shift;
|
|
|
|
change = val != old_val;
|
|
|
|
if (change)
|
|
|
|
outl(val, ICEMT1724(ice, ROUTE_PLAYBACK));
|
|
|
|
return change;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_route_analog_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
2005-11-17 15:00:18 +01:00
|
|
|
ucontrol->value.enumerated.item[0] =
|
|
|
|
get_route_val(ice, analog_route_shift(idx));
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_route_analog_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
|
|
return put_route_val(ice, ucontrol->value.enumerated.item[0],
|
|
|
|
analog_route_shift(idx));
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_route_spdif_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
2005-11-17 15:00:18 +01:00
|
|
|
ucontrol->value.enumerated.item[0] =
|
|
|
|
get_route_val(ice, digital_route_shift(idx));
|
2005-04-17 00:20:36 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_route_spdif_put(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
|
|
|
|
return put_route_val(ice, ucontrol->value.enumerated.item[0],
|
|
|
|
digital_route_shift(idx));
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "H/W Playback Route",
|
|
|
|
.info = snd_vt1724_pro_route_info,
|
|
|
|
.get = snd_vt1724_pro_route_analog_get,
|
|
|
|
.put = snd_vt1724_pro_route_analog_put,
|
|
|
|
};
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Route",
|
|
|
|
.info = snd_vt1724_pro_route_info,
|
|
|
|
.get = snd_vt1724_pro_route_spdif_get,
|
|
|
|
.put = snd_vt1724_pro_route_spdif_put,
|
|
|
|
.count = 2,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_peak_info(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_info *uinfo)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
|
|
|
|
uinfo->count = 22; /* FIXME: for compatibility with ice1712... */
|
|
|
|
uinfo->value.integer.min = 0;
|
|
|
|
uinfo->value.integer.max = 255;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
|
|
|
|
struct snd_ctl_elem_value *ucontrol)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = snd_kcontrol_chip(kcontrol);
|
2005-04-17 00:20:36 +02:00
|
|
|
int idx;
|
|
|
|
|
|
|
|
spin_lock_irq(&ice->reg_lock);
|
|
|
|
for (idx = 0; idx < 22; idx++) {
|
|
|
|
outb(idx, ICEMT1724(ice, MONITOR_PEAKINDEX));
|
2005-11-17 15:00:18 +01:00
|
|
|
ucontrol->value.integer.value[idx] =
|
|
|
|
inb(ICEMT1724(ice, MONITOR_PEAKDATA));
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
spin_unlock_irq(&ice->reg_lock);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
|
|
|
|
.name = "Multi Track Peak",
|
|
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
|
|
.info = snd_vt1724_pro_peak_info,
|
|
|
|
.get = snd_vt1724_pro_peak_get
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_ice1712_card_info no_matched __devinitdata;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
2007-03-13 22:13:47 +01:00
|
|
|
static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
|
2005-04-17 00:20:36 +02:00
|
|
|
snd_vt1724_revo_cards,
|
|
|
|
snd_vt1724_amp_cards,
|
|
|
|
snd_vt1724_aureon_cards,
|
|
|
|
snd_vt1720_mobo_cards,
|
|
|
|
snd_vt1720_pontis_cards,
|
2008-01-16 19:50:00 +01:00
|
|
|
snd_vt1724_prodigy_hifi_cards,
|
2005-04-17 00:20:36 +02:00
|
|
|
snd_vt1724_prodigy192_cards,
|
|
|
|
snd_vt1724_juli_cards,
|
|
|
|
snd_vt1724_phase_cards,
|
2007-01-08 10:48:41 +01:00
|
|
|
snd_vt1724_wtm_cards,
|
2007-10-23 15:08:18 +02:00
|
|
|
snd_vt1724_se_cards,
|
2005-04-17 00:20:36 +02:00
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*/
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static void wait_i2c_busy(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int t = 0x10000;
|
|
|
|
while ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_BUSY) && t--)
|
|
|
|
;
|
|
|
|
if (t == -1)
|
|
|
|
printk(KERN_ERR "ice1724: i2c busy timeout\n");
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
unsigned char snd_vt1724_read_i2c(struct snd_ice1712 *ice,
|
|
|
|
unsigned char dev, unsigned char addr)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
unsigned char val;
|
|
|
|
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_lock(&ice->i2c_mutex);
|
2007-12-03 12:37:17 +01:00
|
|
|
wait_i2c_busy(ice);
|
2005-04-17 00:20:36 +02:00
|
|
|
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
|
|
|
|
outb(dev & ~VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
|
|
|
|
wait_i2c_busy(ice);
|
|
|
|
val = inb(ICEREG1724(ice, I2C_DATA));
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->i2c_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
//printk("i2c_read: [0x%x,0x%x] = 0x%x\n", dev, addr, val);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
void snd_vt1724_write_i2c(struct snd_ice1712 *ice,
|
|
|
|
unsigned char dev, unsigned char addr, unsigned char data)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_lock(&ice->i2c_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
wait_i2c_busy(ice);
|
|
|
|
//printk("i2c_write: [0x%x,0x%x] = 0x%x\n", dev, addr, data);
|
|
|
|
outb(addr, ICEREG1724(ice, I2C_BYTE_ADDR));
|
|
|
|
outb(data, ICEREG1724(ice, I2C_DATA));
|
|
|
|
outb(dev | VT1724_I2C_WRITE, ICEREG1724(ice, I2C_DEV_ADDR));
|
|
|
|
wait_i2c_busy(ice);
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_unlock(&ice->i2c_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_read_eeprom(struct snd_ice1712 *ice,
|
|
|
|
const char *modelname)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
const int dev = 0xa0; /* EEPROM device address */
|
|
|
|
unsigned int i, size;
|
2007-03-13 22:13:47 +01:00
|
|
|
struct snd_ice1712_card_info * const *tbl, *c;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (! modelname || ! *modelname) {
|
|
|
|
ice->eeprom.subvendor = 0;
|
|
|
|
if ((inb(ICEREG1724(ice, I2C_CTRL)) & VT1724_I2C_EEPROM) != 0)
|
|
|
|
ice->eeprom.subvendor =
|
|
|
|
(snd_vt1724_read_i2c(ice, dev, 0x00) << 0) |
|
|
|
|
(snd_vt1724_read_i2c(ice, dev, 0x01) << 8) |
|
|
|
|
(snd_vt1724_read_i2c(ice, dev, 0x02) << 16) |
|
|
|
|
(snd_vt1724_read_i2c(ice, dev, 0x03) << 24);
|
2005-11-17 15:00:18 +01:00
|
|
|
if (ice->eeprom.subvendor == 0 ||
|
|
|
|
ice->eeprom.subvendor == (unsigned int)-1) {
|
|
|
|
/* invalid subvendor from EEPROM, try the PCI
|
|
|
|
* subststem ID instead
|
|
|
|
*/
|
2005-04-17 00:20:36 +02:00
|
|
|
u16 vendor, device;
|
2005-11-17 15:00:18 +01:00
|
|
|
pci_read_config_word(ice->pci, PCI_SUBSYSTEM_VENDOR_ID,
|
|
|
|
&vendor);
|
2005-04-17 00:20:36 +02:00
|
|
|
pci_read_config_word(ice->pci, PCI_SUBSYSTEM_ID, &device);
|
2005-11-17 15:00:18 +01:00
|
|
|
ice->eeprom.subvendor =
|
|
|
|
((unsigned int)swab16(vendor) << 16) | swab16(device);
|
|
|
|
if (ice->eeprom.subvendor == 0 ||
|
|
|
|
ice->eeprom.subvendor == (unsigned int)-1) {
|
2005-04-17 00:20:36 +02:00
|
|
|
printk(KERN_ERR "ice1724: No valid ID is found\n");
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (tbl = card_tables; *tbl; tbl++) {
|
|
|
|
for (c = *tbl; c->subvendor; c++) {
|
2005-11-17 15:00:18 +01:00
|
|
|
if (modelname && c->model &&
|
|
|
|
! strcmp(modelname, c->model)) {
|
|
|
|
printk(KERN_INFO "ice1724: Using board model %s\n",
|
|
|
|
c->name);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->eeprom.subvendor = c->subvendor;
|
|
|
|
} else if (c->subvendor != ice->eeprom.subvendor)
|
|
|
|
continue;
|
|
|
|
if (! c->eeprom_size || ! c->eeprom_data)
|
|
|
|
goto found;
|
|
|
|
/* if the EEPROM is given by the driver, use it */
|
|
|
|
snd_printdd("using the defined eeprom..\n");
|
|
|
|
ice->eeprom.version = 2;
|
|
|
|
ice->eeprom.size = c->eeprom_size + 6;
|
|
|
|
memcpy(ice->eeprom.data, c->eeprom_data, c->eeprom_size);
|
|
|
|
goto read_skipped;
|
|
|
|
}
|
|
|
|
}
|
2005-11-17 15:00:18 +01:00
|
|
|
printk(KERN_WARNING "ice1724: No matching model found for ID 0x%x\n",
|
|
|
|
ice->eeprom.subvendor);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
found:
|
|
|
|
ice->eeprom.size = snd_vt1724_read_i2c(ice, dev, 0x04);
|
|
|
|
if (ice->eeprom.size < 6)
|
|
|
|
ice->eeprom.size = 32;
|
|
|
|
else if (ice->eeprom.size > 32) {
|
2005-11-17 15:00:18 +01:00
|
|
|
printk(KERN_ERR "ice1724: Invalid EEPROM (size = %i)\n",
|
|
|
|
ice->eeprom.size);
|
2005-04-17 00:20:36 +02:00
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
ice->eeprom.version = snd_vt1724_read_i2c(ice, dev, 0x05);
|
|
|
|
if (ice->eeprom.version != 2)
|
2005-11-17 15:00:18 +01:00
|
|
|
printk(KERN_WARNING "ice1724: Invalid EEPROM version %i\n",
|
|
|
|
ice->eeprom.version);
|
2005-04-17 00:20:36 +02:00
|
|
|
size = ice->eeprom.size - 6;
|
|
|
|
for (i = 0; i < size; i++)
|
|
|
|
ice->eeprom.data[i] = snd_vt1724_read_i2c(ice, dev, i + 6);
|
|
|
|
|
|
|
|
read_skipped:
|
|
|
|
ice->eeprom.gpiomask = eeprom_triple(ice, ICE_EEP2_GPIO_MASK);
|
|
|
|
ice->eeprom.gpiostate = eeprom_triple(ice, ICE_EEP2_GPIO_STATE);
|
|
|
|
ice->eeprom.gpiodir = eeprom_triple(ice, ICE_EEP2_GPIO_DIR);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2008-04-22 12:52:15 +02:00
|
|
|
static void __devinit snd_vt1724_chip_reset(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
outb(VT1724_RESET , ICEREG1724(ice, CONTROL));
|
2008-04-22 12:52:15 +02:00
|
|
|
msleep(10);
|
2005-04-17 00:20:36 +02:00
|
|
|
outb(0, ICEREG1724(ice, CONTROL));
|
2008-04-22 12:52:15 +02:00
|
|
|
msleep(10);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __devinit snd_vt1724_chip_init(struct snd_ice1712 *ice)
|
|
|
|
{
|
2005-04-17 00:20:36 +02:00
|
|
|
outb(ice->eeprom.data[ICE_EEP2_SYSCONF], ICEREG1724(ice, SYS_CFG));
|
|
|
|
outb(ice->eeprom.data[ICE_EEP2_ACLINK], ICEREG1724(ice, AC97_CFG));
|
|
|
|
outb(ice->eeprom.data[ICE_EEP2_I2S], ICEREG1724(ice, I2S_FEATURES));
|
|
|
|
outb(ice->eeprom.data[ICE_EEP2_SPDIF], ICEREG1724(ice, SPDIF_CFG));
|
|
|
|
|
|
|
|
ice->gpio.write_mask = ice->eeprom.gpiomask;
|
|
|
|
ice->gpio.direction = ice->eeprom.gpiodir;
|
|
|
|
snd_vt1724_set_gpio_mask(ice, ice->eeprom.gpiomask);
|
|
|
|
snd_vt1724_set_gpio_dir(ice, ice->eeprom.gpiodir);
|
|
|
|
snd_vt1724_set_gpio_data(ice, ice->eeprom.gpiostate);
|
|
|
|
|
|
|
|
outb(0, ICEREG1724(ice, POWERDOWN));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_spdif_build_controls(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int err;
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_kcontrol *kctl;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
snd_assert(ice->pcm != NULL, return -EIO);
|
|
|
|
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_spdif_route, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_spdif_switch, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_default, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = ice->pcm->device;
|
|
|
|
err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskc, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = ice->pcm->device;
|
|
|
|
err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_maskp, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = ice->pcm->device;
|
|
|
|
#if 0 /* use default only */
|
|
|
|
err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_vt1724_spdif_stream, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
kctl->id.device = ice->pcm->device;
|
|
|
|
ice->spdif.stream_ctl = kctl;
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_build_controls(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_eeprom, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_internal_clock, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_rate_locking, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_pro_rate_reset, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (ice->num_total_dacs > 0) {
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_kcontrol_new tmp = snd_vt1724_mixer_pro_analog_route;
|
2005-04-17 00:20:36 +02:00
|
|
|
tmp.count = ice->num_total_dacs;
|
|
|
|
if (ice->vt1720 && tmp.count > 2)
|
|
|
|
tmp.count = 2;
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&tmp, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_vt1724_mixer_pro_peak, ice));
|
|
|
|
if (err < 0)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_free(struct snd_ice1712 *ice)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
|
|
|
if (! ice->port)
|
|
|
|
goto __hw_end;
|
|
|
|
/* mask all interrupts */
|
|
|
|
outb(0xff, ICEMT1724(ice, DMA_INT_MASK));
|
|
|
|
outb(0xff, ICEREG1724(ice, IRQMASK));
|
|
|
|
/* --- */
|
|
|
|
__hw_end:
|
2008-04-22 13:50:34 +02:00
|
|
|
if (ice->irq >= 0)
|
2005-11-17 15:00:18 +01:00
|
|
|
free_irq(ice->irq, ice);
|
2005-04-17 00:20:36 +02:00
|
|
|
pci_release_regions(ice->pci);
|
|
|
|
snd_ice1712_akm4xxx_free(ice);
|
|
|
|
pci_disable_device(ice->pci);
|
2008-01-18 13:36:07 +01:00
|
|
|
kfree(ice->spec);
|
2005-04-17 00:20:36 +02:00
|
|
|
kfree(ice);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int snd_vt1724_dev_free(struct snd_device *device)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice = device->device_data;
|
2005-04-17 00:20:36 +02:00
|
|
|
return snd_vt1724_free(ice);
|
|
|
|
}
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
static int __devinit snd_vt1724_create(struct snd_card *card,
|
2005-04-17 00:20:36 +02:00
|
|
|
struct pci_dev *pci,
|
|
|
|
const char *modelname,
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 ** r_ice1712)
|
2005-04-17 00:20:36 +02:00
|
|
|
{
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_ice1712 *ice;
|
2005-04-17 00:20:36 +02:00
|
|
|
int err;
|
|
|
|
unsigned char mask;
|
2005-11-17 15:00:18 +01:00
|
|
|
static struct snd_device_ops ops = {
|
2005-04-17 00:20:36 +02:00
|
|
|
.dev_free = snd_vt1724_dev_free,
|
|
|
|
};
|
|
|
|
|
|
|
|
*r_ice1712 = NULL;
|
|
|
|
|
|
|
|
/* enable PCI device */
|
|
|
|
if ((err = pci_enable_device(pci)) < 0)
|
|
|
|
return err;
|
|
|
|
|
[ALSA] Replace with kzalloc() - pci stuff
AD1889 driver,ATIIXP driver,ATIIXP-modem driver,AZT3328 driver
BT87x driver,CMIPCI driver,CS4281 driver,ENS1370/1+ driver
ES1938 driver,ES1968 driver,FM801 driver,Intel8x0 driver
Intel8x0-modem driver,Maestro3 driver,SonicVibes driver,VIA82xx driver
VIA82xx-modem driver,AC97 Codec,AK4531 codec,au88x0 driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,HDA Codec driver
HDA generic driver,HDA Intel driver,ICE1712 driver,ICE1724 driver
KORG1212 driver,MIXART driver,NM256 driver,Trident driver,YMFPCI driver
Replace kcalloc(1,..) with kzalloc().
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-09-09 14:21:46 +02:00
|
|
|
ice = kzalloc(sizeof(*ice), GFP_KERNEL);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (ice == NULL) {
|
|
|
|
pci_disable_device(pci);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
ice->vt1724 = 1;
|
|
|
|
spin_lock_init(&ice->reg_lock);
|
2006-01-16 16:34:20 +01:00
|
|
|
mutex_init(&ice->gpio_mutex);
|
|
|
|
mutex_init(&ice->open_mutex);
|
|
|
|
mutex_init(&ice->i2c_mutex);
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->gpio.set_mask = snd_vt1724_set_gpio_mask;
|
|
|
|
ice->gpio.set_dir = snd_vt1724_set_gpio_dir;
|
|
|
|
ice->gpio.set_data = snd_vt1724_set_gpio_data;
|
|
|
|
ice->gpio.get_data = snd_vt1724_get_gpio_data;
|
|
|
|
ice->card = card;
|
|
|
|
ice->pci = pci;
|
|
|
|
ice->irq = -1;
|
|
|
|
pci_set_master(pci);
|
|
|
|
snd_vt1724_proc_init(ice);
|
|
|
|
synchronize_irq(pci->irq);
|
|
|
|
|
|
|
|
if ((err = pci_request_regions(pci, "ICE1724")) < 0) {
|
|
|
|
kfree(ice);
|
|
|
|
pci_disable_device(pci);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ice->port = pci_resource_start(pci, 0);
|
|
|
|
ice->profi_port = pci_resource_start(pci, 1);
|
|
|
|
|
2005-11-17 15:00:18 +01:00
|
|
|
if (request_irq(pci->irq, snd_vt1724_interrupt,
|
2006-11-21 12:14:23 +01:00
|
|
|
IRQF_SHARED, "ICE1724", ice)) {
|
2005-10-20 18:26:44 +02:00
|
|
|
snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
|
2005-04-17 00:20:36 +02:00
|
|
|
snd_vt1724_free(ice);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
ice->irq = pci->irq;
|
|
|
|
|
2008-04-22 12:52:15 +02:00
|
|
|
snd_vt1724_chip_reset(ice);
|
2005-04-17 00:20:36 +02:00
|
|
|
if (snd_vt1724_read_eeprom(ice, modelname) < 0) {
|
|
|
|
snd_vt1724_free(ice);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
if (snd_vt1724_chip_init(ice) < 0) {
|
|
|
|
snd_vt1724_free(ice);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unmask used interrupts */
|
|
|
|
if (! (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401))
|
|
|
|
mask = VT1724_IRQ_MPU_RX | VT1724_IRQ_MPU_TX;
|
|
|
|
else
|
|
|
|
mask = 0;
|
|
|
|
outb(mask, ICEREG1724(ice, IRQMASK));
|
2005-11-17 15:00:18 +01:00
|
|
|
/* don't handle FIFO overrun/underruns (just yet),
|
|
|
|
* since they cause machine lockups
|
|
|
|
*/
|
2005-04-17 00:20:36 +02:00
|
|
|
outb(VT1724_MULTI_FIFO_ERR, ICEMT1724(ice, DMA_INT_MASK));
|
|
|
|
|
|
|
|
if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
|
|
|
|
snd_vt1724_free(ice);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
snd_card_set_dev(card, &pci->dev);
|
|
|
|
|
|
|
|
*r_ice1712 = ice;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Registration
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int __devinit snd_vt1724_probe(struct pci_dev *pci,
|
|
|
|
const struct pci_device_id *pci_id)
|
|
|
|
{
|
|
|
|
static int dev;
|
2005-11-17 15:00:18 +01:00
|
|
|
struct snd_card *card;
|
|
|
|
struct snd_ice1712 *ice;
|
2005-04-17 00:20:36 +02:00
|
|
|
int pcm_dev = 0, err;
|
2007-03-13 22:13:47 +01:00
|
|
|
struct snd_ice1712_card_info * const *tbl, *c;
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if (dev >= SNDRV_CARDS)
|
|
|
|
return -ENODEV;
|
|
|
|
if (!enable[dev]) {
|
|
|
|
dev++;
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
|
|
|
|
if (card == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
strcpy(card->driver, "ICE1724");
|
|
|
|
strcpy(card->shortname, "ICEnsemble ICE1724");
|
|
|
|
|
|
|
|
if ((err = snd_vt1724_create(card, pci, model[dev], &ice)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (tbl = card_tables; *tbl; tbl++) {
|
|
|
|
for (c = *tbl; c->subvendor; c++) {
|
|
|
|
if (c->subvendor == ice->eeprom.subvendor) {
|
|
|
|
strcpy(card->shortname, c->name);
|
|
|
|
if (c->driver) /* specific driver? */
|
|
|
|
strcpy(card->driver, c->driver);
|
|
|
|
if (c->chip_init) {
|
|
|
|
if ((err = c->chip_init(ice)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
goto __found;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c = &no_matched;
|
|
|
|
__found:
|
2007-04-10 11:15:34 +02:00
|
|
|
/*
|
|
|
|
* VT1724 has separate DMAs for the analog and the SPDIF streams while
|
|
|
|
* ICE1712 has only one for both (mixed up).
|
|
|
|
*
|
|
|
|
* Confusingly the analog PCM is named "professional" here because it
|
|
|
|
* was called so in ice1712 driver, and vt1724 driver is derived from
|
|
|
|
* ice1712 driver.
|
|
|
|
*/
|
2008-03-20 12:10:27 +01:00
|
|
|
ice->pro_rate_default = PRO_RATE_DEFAULT;
|
|
|
|
if (!ice->is_spdif_master)
|
|
|
|
ice->is_spdif_master = stdclock_is_spdif_master;
|
|
|
|
if (!ice->get_rate)
|
|
|
|
ice->get_rate = stdclock_get_rate;
|
|
|
|
if (!ice->set_rate)
|
|
|
|
ice->set_rate = stdclock_set_rate;
|
|
|
|
if (!ice->set_mclk)
|
|
|
|
ice->set_mclk = stdclock_set_mclk;
|
|
|
|
if (!ice->set_spdif_clock)
|
|
|
|
ice->set_spdif_clock = stdclock_set_spdif_clock;
|
|
|
|
if (!ice->hw_rates)
|
|
|
|
set_std_hw_rates(ice);
|
2005-04-17 00:20:36 +02:00
|
|
|
|
|
|
|
if ((err = snd_vt1724_pcm_profi(ice, pcm_dev++)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = snd_vt1724_pcm_spdif(ice, pcm_dev++)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = snd_vt1724_pcm_indep(ice, pcm_dev++)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = snd_vt1724_ac97_mixer(ice)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((err = snd_vt1724_build_controls(ice)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ice->pcm && ice->has_spdif) { /* has SPDIF I/O */
|
|
|
|
if ((err = snd_vt1724_spdif_build_controls(ice)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->build_controls) {
|
|
|
|
if ((err = c->build_controls(ice)) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! c->no_mpu401) {
|
|
|
|
if (ice->eeprom.data[ICE_EEP2_SYSCONF] & VT1724_CFG_MPU401) {
|
|
|
|
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
|
2006-05-23 13:24:30 +02:00
|
|
|
ICEREG1724(ice, MPU_CTRL),
|
|
|
|
MPU401_INFO_INTEGRATED,
|
2005-04-17 00:20:36 +02:00
|
|
|
ice->irq, 0,
|
|
|
|
&ice->rmidi[0])) < 0) {
|
|
|
|
snd_card_free(card);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sprintf(card->longname, "%s at 0x%lx, irq %i",
|
|
|
|
card->shortname, ice->port, ice->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_vt1724_remove(struct pci_dev *pci)
|
|
|
|
{
|
|
|
|
snd_card_free(pci_get_drvdata(pci));
|
|
|
|
pci_set_drvdata(pci, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pci_driver driver = {
|
|
|
|
.name = "ICE1724",
|
|
|
|
.id_table = snd_vt1724_ids,
|
|
|
|
.probe = snd_vt1724_probe,
|
|
|
|
.remove = __devexit_p(snd_vt1724_remove),
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init alsa_card_ice1724_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 16:58:24 +02:00
|
|
|
return pci_register_driver(&driver);
|
2005-04-17 00:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit alsa_card_ice1724_exit(void)
|
|
|
|
{
|
|
|
|
pci_unregister_driver(&driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(alsa_card_ice1724_init)
|
|
|
|
module_exit(alsa_card_ice1724_exit)
|